giga-lite-hub 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/giga-lite-hub.gemspec +12 -0
  3. data/sprockets-4.2.2/CHANGELOG.md +118 -0
  4. data/sprockets-4.2.2/MIT-LICENSE +21 -0
  5. data/sprockets-4.2.2/README.md +716 -0
  6. data/sprockets-4.2.2/bin/sprockets +93 -0
  7. data/sprockets-4.2.2/lib/rake/sprocketstask.rb +157 -0
  8. data/sprockets-4.2.2/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  9. data/sprockets-4.2.2/lib/sprockets/asset.rb +212 -0
  10. data/sprockets-4.2.2/lib/sprockets/autoload/babel.rb +8 -0
  11. data/sprockets-4.2.2/lib/sprockets/autoload/closure.rb +8 -0
  12. data/sprockets-4.2.2/lib/sprockets/autoload/coffee_script.rb +8 -0
  13. data/sprockets-4.2.2/lib/sprockets/autoload/eco.rb +8 -0
  14. data/sprockets-4.2.2/lib/sprockets/autoload/ejs.rb +8 -0
  15. data/sprockets-4.2.2/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/sprockets-4.2.2/lib/sprockets/autoload/sass.rb +8 -0
  17. data/sprockets-4.2.2/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/sprockets-4.2.2/lib/sprockets/autoload/uglifier.rb +8 -0
  19. data/sprockets-4.2.2/lib/sprockets/autoload/yui.rb +8 -0
  20. data/sprockets-4.2.2/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/sprockets-4.2.2/lib/sprockets/autoload.rb +16 -0
  22. data/sprockets-4.2.2/lib/sprockets/babel_processor.rb +66 -0
  23. data/sprockets-4.2.2/lib/sprockets/base.rb +147 -0
  24. data/sprockets-4.2.2/lib/sprockets/bower.rb +61 -0
  25. data/sprockets-4.2.2/lib/sprockets/bundle.rb +105 -0
  26. data/sprockets-4.2.2/lib/sprockets/cache/file_store.rb +208 -0
  27. data/sprockets-4.2.2/lib/sprockets/cache/memory_store.rb +84 -0
  28. data/sprockets-4.2.2/lib/sprockets/cache/null_store.rb +54 -0
  29. data/sprockets-4.2.2/lib/sprockets/cache.rb +271 -0
  30. data/sprockets-4.2.2/lib/sprockets/cached_environment.rb +64 -0
  31. data/sprockets-4.2.2/lib/sprockets/closure_compressor.rb +48 -0
  32. data/sprockets-4.2.2/lib/sprockets/coffee_script_processor.rb +39 -0
  33. data/sprockets-4.2.2/lib/sprockets/compressing.rb +134 -0
  34. data/sprockets-4.2.2/lib/sprockets/configuration.rb +79 -0
  35. data/sprockets-4.2.2/lib/sprockets/context.rb +305 -0
  36. data/sprockets-4.2.2/lib/sprockets/dependencies.rb +74 -0
  37. data/sprockets-4.2.2/lib/sprockets/digest_utils.rb +197 -0
  38. data/sprockets-4.2.2/lib/sprockets/directive_processor.rb +432 -0
  39. data/sprockets-4.2.2/lib/sprockets/eco_processor.rb +33 -0
  40. data/sprockets-4.2.2/lib/sprockets/ejs_processor.rb +32 -0
  41. data/sprockets-4.2.2/lib/sprockets/encoding_utils.rb +261 -0
  42. data/sprockets-4.2.2/lib/sprockets/environment.rb +46 -0
  43. data/sprockets-4.2.2/lib/sprockets/erb_processor.rb +43 -0
  44. data/sprockets-4.2.2/lib/sprockets/errors.rb +12 -0
  45. data/sprockets-4.2.2/lib/sprockets/exporters/base.rb +71 -0
  46. data/sprockets-4.2.2/lib/sprockets/exporters/file_exporter.rb +24 -0
  47. data/sprockets-4.2.2/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  48. data/sprockets-4.2.2/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  49. data/sprockets-4.2.2/lib/sprockets/exporting.rb +73 -0
  50. data/sprockets-4.2.2/lib/sprockets/file_reader.rb +16 -0
  51. data/sprockets-4.2.2/lib/sprockets/http_utils.rb +135 -0
  52. data/sprockets-4.2.2/lib/sprockets/jsminc_compressor.rb +32 -0
  53. data/sprockets-4.2.2/lib/sprockets/jst_processor.rb +50 -0
  54. data/sprockets-4.2.2/lib/sprockets/loader.rb +347 -0
  55. data/sprockets-4.2.2/lib/sprockets/manifest.rb +339 -0
  56. data/sprockets-4.2.2/lib/sprockets/manifest_utils.rb +48 -0
  57. data/sprockets-4.2.2/lib/sprockets/mime.rb +96 -0
  58. data/sprockets-4.2.2/lib/sprockets/npm.rb +52 -0
  59. data/sprockets-4.2.2/lib/sprockets/path_dependency_utils.rb +77 -0
  60. data/sprockets-4.2.2/lib/sprockets/path_digest_utils.rb +48 -0
  61. data/sprockets-4.2.2/lib/sprockets/path_utils.rb +367 -0
  62. data/sprockets-4.2.2/lib/sprockets/paths.rb +82 -0
  63. data/sprockets-4.2.2/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  64. data/sprockets-4.2.2/lib/sprockets/processing.rb +228 -0
  65. data/sprockets-4.2.2/lib/sprockets/processor_utils.rb +170 -0
  66. data/sprockets-4.2.2/lib/sprockets/resolve.rb +295 -0
  67. data/sprockets-4.2.2/lib/sprockets/sass_cache_store.rb +30 -0
  68. data/sprockets-4.2.2/lib/sprockets/sass_compressor.rb +63 -0
  69. data/sprockets-4.2.2/lib/sprockets/sass_functions.rb +3 -0
  70. data/sprockets-4.2.2/lib/sprockets/sass_importer.rb +3 -0
  71. data/sprockets-4.2.2/lib/sprockets/sass_processor.rb +313 -0
  72. data/sprockets-4.2.2/lib/sprockets/sassc_compressor.rb +56 -0
  73. data/sprockets-4.2.2/lib/sprockets/sassc_processor.rb +297 -0
  74. data/sprockets-4.2.2/lib/sprockets/server.rb +315 -0
  75. data/sprockets-4.2.2/lib/sprockets/source_map_processor.rb +66 -0
  76. data/sprockets-4.2.2/lib/sprockets/source_map_utils.rb +483 -0
  77. data/sprockets-4.2.2/lib/sprockets/transformers.rb +173 -0
  78. data/sprockets-4.2.2/lib/sprockets/uglifier_compressor.rb +66 -0
  79. data/sprockets-4.2.2/lib/sprockets/unloaded_asset.rb +139 -0
  80. data/sprockets-4.2.2/lib/sprockets/uri_tar.rb +99 -0
  81. data/sprockets-4.2.2/lib/sprockets/uri_utils.rb +194 -0
  82. data/sprockets-4.2.2/lib/sprockets/utils/gzip.rb +99 -0
  83. data/sprockets-4.2.2/lib/sprockets/utils.rb +210 -0
  84. data/sprockets-4.2.2/lib/sprockets/version.rb +4 -0
  85. data/sprockets-4.2.2/lib/sprockets/yui_compressor.rb +56 -0
  86. data/sprockets-4.2.2/lib/sprockets.rb +232 -0
  87. metadata +126 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/path_utils'
4
+ require 'sprockets/source_map_utils'
5
+ require 'json'
6
+
7
+ module Sprockets
8
+ class BabelProcessor
9
+ VERSION = '1'
10
+
11
+ def self.instance
12
+ @instance ||= new
13
+ end
14
+
15
+ def self.call(input)
16
+ instance.call(input)
17
+ end
18
+
19
+ def self.cache_key
20
+ instance.cache_key
21
+ end
22
+
23
+ attr_reader :cache_key
24
+
25
+ def initialize(options = {})
26
+ @options = options.merge({
27
+ 'blacklist' => (options['blacklist'] || []) + ['useStrict'],
28
+ 'sourceMap' => true
29
+ }).freeze
30
+
31
+ @cache_key = [
32
+ self.class.name,
33
+ Autoload::Babel::Transpiler::VERSION,
34
+ Autoload::Babel::Source::VERSION,
35
+ VERSION,
36
+ @options
37
+ ].freeze
38
+ end
39
+
40
+ def call(input)
41
+ data = input[:data]
42
+
43
+ result = input[:cache].fetch(@cache_key + [input[:filename]] + [data]) do
44
+ opts = {
45
+ 'moduleRoot' => nil,
46
+ 'filename' => input[:filename],
47
+ 'filenameRelative' => PathUtils.split_subpath(input[:load_path], input[:filename]),
48
+ 'sourceFileName' => File.basename(input[:filename]),
49
+ 'sourceMapTarget' => input[:filename]
50
+ }.merge(@options)
51
+
52
+ if opts['moduleIds'] && opts['moduleRoot']
53
+ opts['moduleId'] ||= File.join(opts['moduleRoot'], input[:name])
54
+ elsif opts['moduleIds']
55
+ opts['moduleId'] ||= input[:name]
56
+ end
57
+ Autoload::Babel::Transpiler.transform(data, opts)
58
+ end
59
+
60
+ map = SourceMapUtils.format_source_map(result["map"], input)
61
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
62
+
63
+ { data: result['code'], map: map }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/asset'
3
+ require 'sprockets/bower'
4
+ require 'sprockets/cache'
5
+ require 'sprockets/configuration'
6
+ require 'sprockets/digest_utils'
7
+ require 'sprockets/errors'
8
+ require 'sprockets/loader'
9
+ require 'sprockets/npm'
10
+ require 'sprockets/path_dependency_utils'
11
+ require 'sprockets/path_digest_utils'
12
+ require 'sprockets/path_utils'
13
+ require 'sprockets/resolve'
14
+ require 'sprockets/server'
15
+ require 'sprockets/source_map_utils'
16
+ require 'sprockets/uri_tar'
17
+
18
+ module Sprockets
19
+
20
+ class DoubleLinkError < Sprockets::Error
21
+ def initialize(parent_filename:, logical_path:, last_filename:, filename:)
22
+ super <<~MSG
23
+ Multiple files with the same output path cannot be linked (#{logical_path.inspect})
24
+ In #{parent_filename.inspect} these files were linked:
25
+ - #{last_filename}
26
+ - #{filename}
27
+ MSG
28
+ end
29
+ end
30
+
31
+ # `Base` class for `Environment` and `CachedEnvironment`.
32
+ class Base
33
+ include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
34
+ include Configuration
35
+ include Server
36
+ include Resolve, Loader
37
+ include Bower
38
+ include Npm
39
+
40
+ # Get persistent cache store
41
+ attr_reader :cache
42
+
43
+ # Set persistent cache store
44
+ #
45
+ # The cache store must implement a pair of getters and
46
+ # setters. Either `get(key)`/`set(key, value)`,
47
+ # `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
48
+ def cache=(cache)
49
+ @cache = Cache.new(cache, logger)
50
+ end
51
+
52
+ # Return an `CachedEnvironment`. Must be implemented by the subclass.
53
+ def cached
54
+ raise NotImplementedError
55
+ end
56
+ alias_method :index, :cached
57
+
58
+ # Internal: Compute digest for path.
59
+ #
60
+ # path - String filename or directory path.
61
+ #
62
+ # Returns a String digest or nil.
63
+ def file_digest(path)
64
+ if stat = self.stat(path)
65
+ # Caveat: Digests are cached by the path's current mtime. Its possible
66
+ # for a files contents to have changed and its mtime to have been
67
+ # negligently reset thus appearing as if the file hasn't changed on
68
+ # disk. Also, the mtime is only read to the nearest second. It's
69
+ # also possible the file was updated more than once in a given second.
70
+ key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i)
71
+ cache.fetch(key) do
72
+ self.stat_digest(path, stat)
73
+ end
74
+ end
75
+ end
76
+
77
+ # Find asset by logical path or expanded path.
78
+ def find_asset(*args, **options)
79
+ uri, _ = resolve(*args, **options)
80
+ if uri
81
+ load(uri)
82
+ end
83
+ end
84
+
85
+ def find_all_linked_assets(*args)
86
+ return to_enum(__method__, *args) unless block_given?
87
+
88
+ parent_asset = asset = find_asset(*args)
89
+ return unless asset
90
+
91
+ yield asset
92
+ stack = asset.links.to_a
93
+ linked_paths = {}
94
+
95
+ while uri = stack.shift
96
+ yield asset = load(uri)
97
+
98
+ last_filename = linked_paths[asset.logical_path]
99
+ if last_filename && last_filename != asset.filename
100
+ raise DoubleLinkError.new(
101
+ parent_filename: parent_asset.filename,
102
+ last_filename: last_filename,
103
+ logical_path: asset.logical_path,
104
+ filename: asset.filename
105
+ )
106
+ end
107
+ linked_paths[asset.logical_path] = asset.filename
108
+ stack = asset.links.to_a + stack
109
+ end
110
+
111
+ nil
112
+ end
113
+
114
+ # Preferred `find_asset` shorthand.
115
+ #
116
+ # environment['application.js']
117
+ #
118
+ def [](*args, **options)
119
+ find_asset(*args, **options)
120
+ end
121
+
122
+ # Find asset by logical path or expanded path.
123
+ #
124
+ # If the asset is not found an error will be raised.
125
+ def find_asset!(*args)
126
+ uri, _ = resolve!(*args)
127
+ if uri
128
+ load(uri)
129
+ end
130
+ end
131
+
132
+ # Pretty inspect
133
+ def inspect
134
+ "#<#{self.class}:0x#{object_id.to_s(16)} " +
135
+ "root=#{root.to_s.inspect}, " +
136
+ "paths=#{paths.inspect}>"
137
+ end
138
+
139
+ def compress_from_root(uri)
140
+ URITar.new(uri, self).compress
141
+ end
142
+
143
+ def expand_from_root(uri)
144
+ URITar.new(uri, self).expand
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Sprockets
5
+ module Bower
6
+ # Internal: All supported bower.json files.
7
+ #
8
+ # https://github.com/bower/json/blob/0.4.0/lib/json.js#L7
9
+ POSSIBLE_BOWER_JSONS = ['bower.json', 'component.json', '.bower.json']
10
+
11
+ # Internal: Override resolve_alternates to install bower.json behavior.
12
+ #
13
+ # load_path - String environment path
14
+ # logical_path - String path relative to base
15
+ #
16
+ # Returns candidate filenames.
17
+ def resolve_alternates(load_path, logical_path)
18
+ candidates, deps = super
19
+
20
+ # bower.json can only be nested one level deep
21
+ if !logical_path.index('/'.freeze)
22
+ dirname = File.join(load_path, logical_path)
23
+
24
+ if directory?(dirname)
25
+ filenames = POSSIBLE_BOWER_JSONS.map { |basename| File.join(dirname, basename) }
26
+ filename = filenames.detect { |fn| self.file?(fn) }
27
+
28
+ if filename
29
+ deps << build_file_digest_uri(filename)
30
+ read_bower_main(dirname, filename) do |path|
31
+ if file?(path)
32
+ candidates << path
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ return candidates, deps
40
+ end
41
+
42
+ # Internal: Read bower.json's main directive.
43
+ #
44
+ # dirname - String path to component directory.
45
+ # filename - String path to bower.json.
46
+ #
47
+ # Returns nothing.
48
+ def read_bower_main(dirname, filename)
49
+ bower = JSON.parse(File.read(filename), create_additions: false)
50
+
51
+ case bower['main']
52
+ when String
53
+ yield File.expand_path(bower['main'], dirname)
54
+ when Array
55
+ bower['main'].each do |name|
56
+ yield File.expand_path(name, dirname)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+ require 'sprockets/utils'
4
+ require 'sprockets/uri_utils'
5
+
6
+ module Sprockets
7
+ # Internal: Bundle processor takes a single file asset and prepends all the
8
+ # `:required` URIs to the contents.
9
+ #
10
+ # Uses pipeline metadata:
11
+ #
12
+ # :required - Ordered Set of asset URIs to prepend
13
+ # :stubbed - Set of asset URIs to subtract from the required set.
14
+ #
15
+ # Also see DirectiveProcessor.
16
+ class Bundle
17
+ def self.call(input)
18
+ env = input[:environment]
19
+ type = input[:content_type]
20
+ input[:links] ||= Set.new
21
+ dependencies = Set.new(input[:metadata][:dependencies])
22
+
23
+ processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self)
24
+ dependencies.merge(deps)
25
+
26
+ # DirectiveProcessor (and any other transformers called here with pipeline=self)
27
+ primary_asset = env.load(processed_uri)
28
+ to_load = primary_asset.metadata.delete(:to_load) || Set.new
29
+ to_link = primary_asset.metadata.delete(:to_link) || Set.new
30
+
31
+ to_load.each do |uri|
32
+ loaded_asset = env.load(uri)
33
+ dependencies.merge(loaded_asset.metadata[:dependencies])
34
+ if to_link.include?(uri)
35
+ primary_metadata = primary_asset.metadata
36
+ input[:links] << loaded_asset.uri
37
+ primary_metadata[:links] << loaded_asset.uri
38
+ end
39
+ end
40
+
41
+ find_required = proc { |uri| env.load(uri).metadata[:required] }
42
+ required = Utils.dfs(processed_uri, &find_required)
43
+ stubbed = Utils.dfs(env.load(processed_uri).metadata[:stubbed], &find_required)
44
+ required.subtract(stubbed)
45
+ dedup(required)
46
+ assets = required.map { |uri| env.load(uri) }
47
+
48
+ (required + stubbed).each do |uri|
49
+ dependencies.merge(env.load(uri).metadata[:dependencies])
50
+ end
51
+
52
+ reducers = Hash[env.match_mime_type_keys(env.config[:bundle_reducers], type).flat_map(&:to_a)]
53
+ process_bundle_reducers(input, assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri))
54
+ end
55
+
56
+ # Internal: Removes uri from required if it's already included as an alias.
57
+ #
58
+ # required - Set of required uris
59
+ #
60
+ # Returns deduped set of uris
61
+ def self.dedup(required)
62
+ dupes = required.reduce([]) do |r, uri|
63
+ path, params = URIUtils.parse_asset_uri(uri)
64
+ if (params.delete(:index_alias))
65
+ r << URIUtils.build_asset_uri(path, params)
66
+ end
67
+ r
68
+ end
69
+ required.subtract(dupes)
70
+ end
71
+
72
+ # Internal: Run bundle reducers on set of Assets producing a reduced
73
+ # metadata Hash.
74
+ #
75
+ # filename - String bundle filename
76
+ # assets - Array of Assets
77
+ # reducers - Array of [initial, reducer_proc] pairs
78
+ #
79
+ # Returns reduced asset metadata Hash.
80
+ def self.process_bundle_reducers(input, assets, reducers)
81
+ initial = {}
82
+ reducers.each do |k, (v, _)|
83
+ if v.respond_to?(:call)
84
+ initial[k] = v.call(input)
85
+ elsif !v.nil?
86
+ initial[k] = v
87
+ end
88
+ end
89
+
90
+ assets.reduce(initial) do |h, asset|
91
+ reducers.each do |k, (_, block)|
92
+ value = k == :data ? asset.source : asset.metadata[k]
93
+ if h.key?(k)
94
+ if !value.nil?
95
+ h[k] = block.call(h[k], value)
96
+ end
97
+ else
98
+ h[k] = value
99
+ end
100
+ end
101
+ h
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+ require 'fileutils'
3
+ require 'logger'
4
+ require 'sprockets/encoding_utils'
5
+ require 'sprockets/path_utils'
6
+ require 'zlib'
7
+
8
+ module Sprockets
9
+ class Cache
10
+ # Public: A file system cache store that automatically cleans up old keys.
11
+ #
12
+ # Assign the instance to the Environment#cache.
13
+ #
14
+ # environment.cache = Sprockets::Cache::FileStore.new("/tmp")
15
+ #
16
+ # See Also
17
+ #
18
+ # ActiveSupport::Cache::FileStore
19
+ #
20
+ class FileStore
21
+ # Internal: Default key limit for store.
22
+ DEFAULT_MAX_SIZE = 25 * 1024 * 1024
23
+ EXCLUDED_DIRS = ['.', '..'].freeze
24
+ GITKEEP_FILES = ['.gitkeep', '.keep'].freeze
25
+
26
+ # Internal: Default standard error fatal logger.
27
+ #
28
+ # Returns a Logger.
29
+ def self.default_logger
30
+ logger = Logger.new($stderr)
31
+ logger.level = Logger::FATAL
32
+ logger
33
+ end
34
+
35
+ # Public: Initialize the cache store.
36
+ #
37
+ # root - A String path to a directory to persist cached values to.
38
+ # max_size - A Integer of the maximum size the store will hold (in bytes).
39
+ # (default: 25MB).
40
+ # logger - The logger to which some info will be printed.
41
+ # (default logger level is FATAL and won't output anything).
42
+ def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger)
43
+ @root = root
44
+ @max_size = max_size
45
+ @gc_size = max_size * 0.75
46
+ @logger = logger
47
+ end
48
+
49
+ # Public: Retrieve value from cache.
50
+ #
51
+ # This API should not be used directly, but via the Cache wrapper API.
52
+ #
53
+ # key - String cache key.
54
+ #
55
+ # Returns Object or nil or the value is not set.
56
+ def get(key)
57
+ path = File.join(@root, "#{key}.cache")
58
+
59
+ value = safe_open(path) do |f|
60
+ begin
61
+ EncodingUtils.unmarshaled_deflated(f.read, Zlib::MAX_WBITS)
62
+ rescue Exception => e
63
+ @logger.error do
64
+ "#{self.class}[#{path}] could not be unmarshaled: " +
65
+ "#{e.class}: #{e.message}"
66
+ end
67
+ nil
68
+ end
69
+ end
70
+
71
+ if value
72
+ FileUtils.touch(path)
73
+ value
74
+ end
75
+ end
76
+
77
+ # Public: Set a key and value in the cache.
78
+ #
79
+ # This API should not be used directly, but via the Cache wrapper API.
80
+ #
81
+ # key - String cache key.
82
+ # value - Object value.
83
+ #
84
+ # Returns Object value.
85
+ def set(key, value)
86
+ path = File.join(@root, "#{key}.cache")
87
+
88
+ # Ensure directory exists
89
+ FileUtils.mkdir_p File.dirname(path)
90
+
91
+ # Check if cache exists before writing
92
+ exists = File.exist?(path)
93
+
94
+ # Serialize value
95
+ marshaled = Marshal.dump(value)
96
+
97
+ # Compress if larger than 4KB
98
+ if marshaled.bytesize > 4 * 1024
99
+ deflater = Zlib::Deflate.new(
100
+ Zlib::BEST_COMPRESSION,
101
+ Zlib::MAX_WBITS,
102
+ Zlib::MAX_MEM_LEVEL,
103
+ Zlib::DEFAULT_STRATEGY
104
+ )
105
+ deflater << marshaled
106
+ raw = deflater.finish
107
+ else
108
+ raw = marshaled
109
+ end
110
+
111
+ # Write data
112
+ PathUtils.atomic_write(path) do |f|
113
+ f.write(raw)
114
+ @size = size + f.size unless exists
115
+ end
116
+
117
+ # GC if necessary
118
+ gc! if size > @max_size
119
+
120
+ value
121
+ end
122
+
123
+ # Public: Pretty inspect
124
+ #
125
+ # Returns String.
126
+ def inspect
127
+ "#<#{self.class} size=#{size}/#{@max_size}>"
128
+ end
129
+
130
+ # Public: Clear the cache
131
+ #
132
+ # adapted from ActiveSupport::Cache::FileStore#clear
133
+ #
134
+ # Deletes all items from the cache. In this case it deletes all the entries in the specified
135
+ # file store directory except for .keep or .gitkeep. Be careful which directory is specified
136
+ # as @root because everything in that directory will be deleted.
137
+ #
138
+ # Returns true
139
+ def clear(options=nil)
140
+ if File.exist?(@root)
141
+ root_dirs = Dir.entries(@root).reject { |f| (EXCLUDED_DIRS + GITKEEP_FILES).include?(f) }
142
+ FileUtils.rm_r(root_dirs.collect{ |f| File.join(@root, f) })
143
+ end
144
+ true
145
+ end
146
+
147
+ private
148
+ # Internal: Get all cache files along with stats.
149
+ #
150
+ # Returns an Array of [String filename, File::Stat] pairs sorted by
151
+ # mtime.
152
+ def find_caches
153
+ Dir.glob(File.join(@root, '**/*.cache')).reduce([]) { |stats, filename|
154
+ stat = safe_stat(filename)
155
+ # stat maybe nil if file was removed between the time we called
156
+ # dir.glob and the next stat
157
+ stats << [filename, stat] if stat
158
+ stats
159
+ }.sort_by { |_, stat| stat.mtime.to_i }
160
+ end
161
+
162
+ def size
163
+ @size ||= compute_size(find_caches)
164
+ end
165
+
166
+ def compute_size(caches)
167
+ caches.inject(0) { |sum, (_, stat)| sum + stat.size }
168
+ end
169
+
170
+ def safe_stat(fn)
171
+ File.stat(fn)
172
+ rescue Errno::ENOENT
173
+ nil
174
+ end
175
+
176
+ def safe_open(path, &block)
177
+ if File.exist?(path)
178
+ File.open(path, 'rb', &block)
179
+ end
180
+ rescue Errno::ENOENT
181
+ end
182
+
183
+ def gc!
184
+ start_time = Time.now
185
+
186
+ caches = find_caches
187
+ size = compute_size(caches)
188
+
189
+ delete_caches, keep_caches = caches.partition { |filename, stat|
190
+ deleted = size > @gc_size
191
+ size -= stat.size
192
+ deleted
193
+ }
194
+
195
+ return if delete_caches.empty?
196
+
197
+ FileUtils.remove(delete_caches.map(&:first), force: true)
198
+ @size = compute_size(keep_caches)
199
+
200
+ @logger.warn do
201
+ secs = Time.now.to_f - start_time.to_f
202
+ "#{self.class}[#{@root}] garbage collected " +
203
+ "#{delete_caches.size} files (#{(secs * 1000).to_i}ms)"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ class Cache
4
+ # Public: Basic in memory LRU cache.
5
+ #
6
+ # Assign the instance to the Environment#cache.
7
+ #
8
+ # environment.cache = Sprockets::Cache::MemoryStore.new(1000)
9
+ #
10
+ # See Also
11
+ #
12
+ # ActiveSupport::Cache::MemoryStore
13
+ #
14
+ class MemoryStore
15
+ # Internal: Default key limit for store.
16
+ DEFAULT_MAX_SIZE = 1000
17
+
18
+ # Public: Initialize the cache store.
19
+ #
20
+ # max_size - A Integer of the maximum number of keys the store will hold.
21
+ # (default: 1000).
22
+ def initialize(max_size = DEFAULT_MAX_SIZE)
23
+ @max_size = max_size
24
+ @cache = {}
25
+ @mutex = Mutex.new
26
+ end
27
+
28
+ # Public: Retrieve value from cache.
29
+ #
30
+ # This API should not be used directly, but via the Cache wrapper API.
31
+ #
32
+ # key - String cache key.
33
+ #
34
+ # Returns Object or nil or the value is not set.
35
+ def get(key)
36
+ @mutex.synchronize do
37
+ exists = true
38
+ value = @cache.delete(key) { exists = false }
39
+ if exists
40
+ @cache[key] = value
41
+ else
42
+ nil
43
+ end
44
+ end
45
+ end
46
+
47
+ # Public: Set a key and value in the cache.
48
+ #
49
+ # This API should not be used directly, but via the Cache wrapper API.
50
+ #
51
+ # key - String cache key.
52
+ # value - Object value.
53
+ #
54
+ # Returns Object value.
55
+ def set(key, value)
56
+ @mutex.synchronize do
57
+ @cache.delete(key)
58
+ @cache[key] = value
59
+ @cache.shift if @cache.size > @max_size
60
+ end
61
+ value
62
+ end
63
+
64
+ # Public: Pretty inspect
65
+ #
66
+ # Returns String.
67
+ def inspect
68
+ @mutex.synchronize do
69
+ "#<#{self.class} size=#{@cache.size}/#{@max_size}>"
70
+ end
71
+ end
72
+
73
+ # Public: Clear the cache
74
+ #
75
+ # Returns true
76
+ def clear(options=nil)
77
+ @mutex.synchronize do
78
+ @cache.clear
79
+ end
80
+ true
81
+ end
82
+ end
83
+ end
84
+ end