@@ -12,9 +12,18 @@ defmodule Scenic.Cache.Base do
12
12
13
13
| Asset Class | Module |
14
14
| ------------- | -----|
15
- | Fonts | [Scenic.Cache.Font](Scenic.Cache.Font.html) |
16
- | Font Metrics | [Scenic.Cache.FontMetrics](Scenic.Cache.FontMetrics.html) |
17
- | Textures (images in a fill) | [Scenic.Cache.Texture](Scenic.Cache.Texture.html) |
15
+ | Fonts | `Scenic.Cache.Static.Font` |
16
+ | Font Metrics | `Scenic.Cache.Static.FontMetrics` |
17
+ | Textures (images in a fill) | `Scenic.Cache.Static.Texture` |
18
+ | Raw Pixel Maps | `Scenic.Cache.Dynamic.Texture` |
19
+
20
+ Some of the Cache support modules have moved
21
+
22
+ | Old Module | New Module |
23
+ | ------------- | -----|
24
+ | `Scenic.Cache.Hash` | `Scenic.Cache.Support.Hash` |
25
+ | `Scenic.Cache.File` | `Scenic.Cache.Support.File` |
26
+ | `Scenic.Cache.Supervisor` | `Scenic.Cache.Support.Supervisor` |
18
27
19
28
## Overview
20
29
@@ -182,129 +191,6 @@ defmodule Scenic.Cache.Base do
182
191
@ behaviour Scenic.Cache.Base
183
192
end
184
193
185
- @ moduledoc """
186
- In memory cache for static #{ unquote ( using_opts ) [ :name ] } assets.
187
-
188
- Assets such as #{ unquote ( using_opts ) [ :name ] } tend to be relatively large compared to
189
- other data. These assets are often used across multiple scenes and may need to be shared
190
- with multiple drivers.
191
-
192
- These assets also tend to have a significant load cost. Fonts need to be rendered. Images
193
- interpreted into their final binary form, etc.
194
-
195
- ## Goals
196
-
197
- Given this situation, the Cache module has multiple goals.
198
- * __Reuse__ - assets used by multiple scenes should only be stored in memory once
199
- * __Load Time__- loading cost should only be paid once
200
- * __Copy time__ - assets are stored in ETS, so they don't need to be copied as they are used
201
- * __Pub/Sub__ - Consumers of static assets (drivers...) should be notified when an asset is
202
- loaded or changed. They should not poll the system.
203
- * __Security__ - Base assets can become an attack vector. Helper modules are provided
204
- to assist in verifying these files.
205
-
206
- ## Scope
207
-
208
- When a #{ unquote ( using_opts ) [ :name ] } is loaded into the cache, it is assigned a scope.
209
- The scope is used to
210
- determine how long to hold the asset in memory before it is unloaded. Scope is either
211
- the atom `:global`, or a `pid`.
212
-
213
- The typical flow is that a scene will load a #{ unquote ( using_opts ) [ :name ] } into the cache.
214
- A scope is automatically
215
- defined that tracks the asset against the pid of the scene that loaded it. When the scene
216
- is closed, the scope becomes empty and the asset is unloaded.
217
-
218
- If, while that scene is loaded, another scene (or any process...) attempts to load
219
- the same asset into the cache, a second scope is added and the duplicate load is
220
- skipped. When the first scene closes, the asset stays in memory as long as the second
221
- scope remains valid.
222
-
223
- When a scene closes, it's scope stays valid for a short time in order to give the next
224
- scene a chance to load its assets (or claim a scope) and possibly re-use the already
225
- loaded assets.
226
-
227
- This is also useful in the event of a scene crashing and being restarted. The delay
228
- in unloading the scope means that the replacement scene will use already loaded
229
- assets instead of loading the same files again for no real benefit.
230
-
231
- When you load assets you can alternately provide your own scope instead of taking the
232
- default, which is your processes pid. If you provide `:global`, then the asset will
233
- stay in memory until you explicitly release it.
234
-
235
- ## Hashes
236
-
237
- At its simplest, accessing the cache is a key-value store. This cache is meant to be
238
- static in nature, so the key is be a hash of the data.
239
-
240
- Why? Read below...
241
-
242
- ## Security
243
-
244
- A lesson learned the hard way is that static assets (fonts, images, etc) that your app
245
- loads out of storage can easily become attack vectors.
246
-
247
- These formats are complicated! There is no guarantee (on any system) that a malformed
248
- asset will not cause an error in the C code that interprets it. Again - these are complicated
249
- and the renderers need to be fast...
250
-
251
- The solution is to compute a SHA hash of these files during build-time of your
252
- and to store the result in your applications code itself. Then during run time, you
253
- compare then pre-computed hash against the run-time of the asset being loaded.
254
-
255
- Please take advantage of the helper modules [`Cache.File`](Scenic.Cache.File.html),
256
- [`Cache.Term`](Scenic.Cache.Term.html), and [`Cache.Hash`](Scenic.Cache.Hash.html) to
257
- do this for you. These modules load files and insert them into the cache while checking
258
- a precomputed hash.
259
-
260
- These scheme is much stronger when the application code itself is also signed and
261
- verified, but that is an exercise for the packaging tools.
262
-
263
- Full Example:
264
-
265
- defmodule MyApp.MyScene do
266
- use Scenic.Scene
267
- import Scenic.Primitives
268
-
269
- # build the path to the static asset file (compile time)
270
- @asset_path :code.priv_dir(:my_app) |> Path.join("/static/images/asset.jpg")
271
-
272
- # pre-compute the hash (compile time)
273
- @asset_hash Scenic.Cache.Hash.file!( @asset_path, :sha )
274
-
275
- # build a graph that uses the asset (compile time)
276
- @graph Scenic.Graph.build()
277
- |> rect( {100, 100}, fill: {:image, @asset_hash} )
278
-
279
-
280
- def init( _, _ ) do
281
- # load the asset into the cache (run time)
282
- Scenic.Cache.File.load(@asset_path, @asset_hash)
283
-
284
- {:ok, :some_state, push: @graph}
285
- end
286
-
287
- end
288
-
289
- When assets are loaded this way, the `@asset_hash` term is also used as the key in
290
- the cache. This has the additional benefit of allowing you to pre-compute
291
- the graph itself, using the correct keys for the correct assets.
292
-
293
- ## Pub/Sub
294
-
295
- Drivers (or any process...) listen to the #{ unquote ( using_opts ) [ :name ] }
296
- Cache via a simple pub/sub api.
297
-
298
- Because the graph, may be computed during compile time and pushed at some
299
- other time than the assets are loaded, the drivers need to know when the assets
300
- become available.
301
-
302
- Whenever any asset is loaded into the cache, messages are sent to any
303
- subscribing processes along with the affected keys. This allows them to react in a
304
- loosely-coupled way to how the assets are managed in your scene.
305
-
306
- """
307
-
308
194
unless unquote ( using_opts ) [ :name ] do
309
195
raise "You must supply a :name option to the \" use Scenic.Cache.Base\" macro."
310
196
end
@@ -373,8 +259,7 @@ defmodule Scenic.Cache.Base do
373
259
* `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
374
260
but is usually nil, which defaults to the pid of the calling process.
375
261
376
- Returns:
377
- {:ok, hash}
262
+ Returns: `{:ok, hash}`
378
263
"""
379
264
@ spec put (
380
265
hash :: Scenic.Cache.Base . hash ( ) ,
@@ -400,8 +285,7 @@ defmodule Scenic.Cache.Base do
400
285
* `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
401
286
but is usually nil, which defaults to the pid of the calling process.
402
287
403
- Returns:
404
- {:ok, hash}
288
+ Returns: `{:ok, hash}`
405
289
"""
406
290
@ spec put_new (
407
291
hash :: Scenic.Cache.Base . hash ( ) ,
@@ -692,46 +576,6 @@ defmodule Scenic.Cache.Base do
692
576
GenServer . call ( service , { :put , normalize_scope ( scope ) , key , data } )
693
577
end
694
578
695
- # # --------------------------------------------------------
696
- # @doc """
697
- # Insert an item into the Cache. If it is already in the cache, then it
698
- # does nothing and returns {:ok, hash}
699
-
700
- # Parameters:
701
- # * `key` - term to use as the retrieval key. Typically a hash of the data itself.
702
- # * `data` - term to use as the stored data
703
- # * `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
704
- # but is usually nil, which defaults to the pid of the calling process.
705
-
706
- # ## Examples
707
- # iex> Scenic.Cache.get("test_key")
708
- # nil
709
-
710
- # iex> :ets.insert(:scenic_cache_key_table, {"test_key", 1, :test_data})
711
- # ...> true
712
- # ...> Scenic.Cache.get("test_key")
713
- # :test_data
714
- # """
715
- # @spec put_new(
716
- # service :: atom,
717
- # hash :: Scenic.Cache.Base.hash(),
718
- # data :: term(),
719
- # scope :: :global | nil | GenServer.server()
720
- # ) :: term()
721
-
722
- # def put_new(service, hash, data, scope \\ nil)
723
- # when service != nil and (is_atom(service) or is_pid(service)) do
724
- # scope = normalize_scope(scope)
725
-
726
- # case :ets.member(service, hash) do
727
- # true ->
728
- # {:ok, hash}
729
-
730
- # false ->
731
- # GenServer.call(service, {:put_new, scope, hash, data})
732
- # end
733
- # end
734
-
735
579
# --------------------------------------------------------
736
580
@ doc """
737
581
Add a scope to an existing asset in the cache.
@@ -776,7 +620,7 @@ defmodule Scenic.Cache.Base do
776
620
processes a chance to claim a scope before it is unloaded.
777
621
"""
778
622
779
- # returns :ok
623
+ # returns ` :ok`
780
624
@ spec release (
781
625
service :: atom ,
782
626
hash :: Scenic.Cache.Base . hash ( ) ,
@@ -815,9 +659,11 @@ defmodule Scenic.Cache.Base do
815
659
Pass in the service, hash, and a scope.
816
660
817
661
returns one of:
818
- {:ok, hash} # it is claimed by the given scope
819
- {:ok, :global} # it is NOT claimed by the given scope, but is :global
820
- {:error, :not_found} # it is not in the cache at all
662
+ ```elixir
663
+ {:ok, hash} # it is claimed by the given scope
664
+ {:ok, :global} # it is NOT claimed by the given scope, but is :global
665
+ {:error, :not_found} # it is not in the cache at all
666
+ ```
821
667
"""
822
668
@ spec status (
823
669
service :: atom ,
@@ -855,7 +701,7 @@ defmodule Scenic.Cache.Base do
855
701
856
702
Pass in the service and a hash.
857
703
858
- returns true or false.
704
+ returns ` true` or ` false` .
859
705
"""
860
706
@ spec member? (
861
707
service :: atom ,
@@ -870,7 +716,7 @@ defmodule Scenic.Cache.Base do
870
716
871
717
Pass in the service, hash, and scope.
872
718
873
- returns true or false.
719
+ returns ` true` or ` false` .
874
720
"""
875
721
@ spec claimed? (
876
722
service :: atom ,
0 commit comments