hanami-assets 0.0.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +426 -9
  5. data/bin/hanami-assets +22 -0
  6. data/hanami-assets.gemspec +26 -12
  7. data/lib/hanami/assets.rb +153 -2
  8. data/lib/hanami/assets/bundler.rb +173 -0
  9. data/lib/hanami/assets/cache.rb +58 -0
  10. data/lib/hanami/assets/compiler.rb +212 -0
  11. data/lib/hanami/assets/compressors/abstract.rb +119 -0
  12. data/lib/hanami/assets/compressors/builtin_javascript.rb +36 -0
  13. data/lib/hanami/assets/compressors/builtin_stylesheet.rb +57 -0
  14. data/lib/hanami/assets/compressors/closure_javascript.rb +25 -0
  15. data/lib/hanami/assets/compressors/javascript.rb +77 -0
  16. data/lib/hanami/assets/compressors/jsmin.rb +283 -0
  17. data/lib/hanami/assets/compressors/null_compressor.rb +19 -0
  18. data/lib/hanami/assets/compressors/sass_stylesheet.rb +38 -0
  19. data/lib/hanami/assets/compressors/stylesheet.rb +77 -0
  20. data/lib/hanami/assets/compressors/uglifier_javascript.rb +25 -0
  21. data/lib/hanami/assets/compressors/yui_javascript.rb +25 -0
  22. data/lib/hanami/assets/compressors/yui_stylesheet.rb +25 -0
  23. data/lib/hanami/assets/config/global_sources.rb +50 -0
  24. data/lib/hanami/assets/config/manifest.rb +112 -0
  25. data/lib/hanami/assets/config/sources.rb +77 -0
  26. data/lib/hanami/assets/configuration.rb +539 -0
  27. data/lib/hanami/assets/helpers.rb +733 -0
  28. data/lib/hanami/assets/precompiler.rb +67 -0
  29. data/lib/hanami/assets/version.rb +4 -1
  30. metadata +189 -17
  31. data/.gitignore +0 -9
  32. data/Gemfile +0 -4
  33. data/Rakefile +0 -2
  34. data/bin/console +0 -14
  35. data/bin/setup +0 -8
@@ -0,0 +1,25 @@
1
+ require 'hanami/assets/compressors/javascript'
2
+ require 'uglifier'
3
+
4
+ module Hanami
5
+ module Assets
6
+ module Compressors
7
+ # Uglifier compressor for JavaScript
8
+ #
9
+ # It depends on <tt>uglifier</tt> gem
10
+ #
11
+ # @since 0.1.0
12
+ # @api private
13
+ #
14
+ # @see http://lisperator.net/uglifyjs
15
+ # @see https://rubygems.org/gems/uglifier
16
+ class UglifierJavascript < Javascript
17
+ # @since 0.1.0
18
+ # @api private
19
+ def initialize
20
+ @compressor = Uglifier.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'hanami/assets/compressors/javascript'
2
+ require 'yui/compressor'
3
+
4
+ module Hanami
5
+ module Assets
6
+ module Compressors
7
+ # YUI Compressor for JavaScript
8
+ #
9
+ # It depends on <tt>yui-compressor</tt> gem
10
+ #
11
+ # @since 0.1.0
12
+ # @api private
13
+ #
14
+ # @see http://yui.github.io/yuicompressor
15
+ # @see https://rubygems.org/gems/yui-compressor
16
+ class YuiJavascript < Javascript
17
+ # @since 0.1.0
18
+ # @api private
19
+ def initialize
20
+ @compressor = YUI::JavaScriptCompressor.new(munge: true)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'hanami/assets/compressors/stylesheet'
2
+ require 'yui/compressor'
3
+
4
+ module Hanami
5
+ module Assets
6
+ module Compressors
7
+ # YUI Compressor for stylesheet
8
+ #
9
+ # It depends on <tt>yui-compressor</tt> gem
10
+ #
11
+ # @since 0.1.0
12
+ # @api private
13
+ #
14
+ # @see http://yui.github.io/yuicompressor
15
+ # @see https://rubygems.org/gems/yui-compressor
16
+ class YuiStylesheet < Stylesheet
17
+ # @since 0.1.0
18
+ # @api private
19
+ def initialize
20
+ @compressor = YUI::CssCompressor.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ require 'hanami/utils/load_paths'
2
+
3
+ module Hanami
4
+ module Assets
5
+ # Configuration settings
6
+ #
7
+ # @since 0.1.0
8
+ # @api private
9
+ module Config
10
+ # Global asset sources across all the duplicated <tt>Hanami::Assets</tt>
11
+ # instances.
12
+ #
13
+ # @since 0.1.0
14
+ # @api private
15
+ #
16
+ # @see Hanami::Assets.duplicate
17
+ # @see http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/LoadPaths
18
+ class GlobalSources < Utils::LoadPaths
19
+ # @since 0.1.0
20
+ # @api private
21
+ def push(*paths)
22
+ super
23
+
24
+ sync_configuration
25
+ sync_duplicated_frameworks
26
+ end
27
+
28
+ # @since 0.1.0
29
+ # @api private
30
+ alias_method :<<, :push
31
+
32
+ private
33
+
34
+ # @since 0.1.0
35
+ # @api private
36
+ def sync_configuration
37
+ Hanami::Assets.configuration.sources << @paths
38
+ end
39
+
40
+ # @since 0.1.0
41
+ # @api private
42
+ def sync_duplicated_frameworks
43
+ Hanami::Assets.duplicates.each do |duplicate|
44
+ duplicate.configuration.sources << @paths
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,112 @@
1
+ module Hanami
2
+ module Assets
3
+ # This error is raised when the application starts but can't be load the
4
+ # digest manifest.
5
+ #
6
+ # @since 0.1.0
7
+ # @api private
8
+ class MissingDigestManifestError < Error
9
+ def initialize(path)
10
+ super("Can't read manifest: #{ path }")
11
+ end
12
+ end
13
+
14
+ # This error is raised when an asset is referenced from the DOM, but it's
15
+ # not present in the digest manifest
16
+ #
17
+ # @since 0.1.0
18
+ # @api private
19
+ class MissingDigestAssetError < Error
20
+ def initialize(asset, manifest_path)
21
+ super("Can't find asset `#{ asset }' in manifest (#{ manifest_path })")
22
+ end
23
+ end
24
+
25
+ # Configuration settings
26
+ #
27
+ # @since 0.1.0
28
+ # @api private
29
+ module Config
30
+ # Default value for configuration's digest manifest.
31
+ #
32
+ # It indicates that the digest manifest wasn't loaded yet.
33
+ #
34
+ # At the load time, this should be replaced by an instance of
35
+ # <tt>Hanami::Assets::Config::Manifest</tt>.
36
+ #
37
+ # If for some reason that won't happen, the instance of this class is
38
+ # still referenced by the configuration and all the method invocations
39
+ # will raise a <tt>Hanami::Assets::MissingDigestManifestError</tt>.
40
+ #
41
+ # @since 0.1.0
42
+ # @api private
43
+ #
44
+ # @see Hanami::Assets::Configuration#manifest
45
+ # @see Hanami::Assets::Configuration#manifest_path
46
+ # @see Hanami::Assets::Configuration#digest
47
+ class NullDigestManifest < Utils::BasicObject
48
+ # Return a new instance
49
+ #
50
+ # @param configuration [Hanami::Assets::Configuration]
51
+ #
52
+ # @return [Hanami::Assets::Config::NullDigestManifest] a new instance
53
+ #
54
+ # @since 0.1.0
55
+ # @api private
56
+ def initialize(configuration)
57
+ @configuration = configuration
58
+ end
59
+
60
+ # @raise [Hanami::Assets::MissingDigestManifestError]
61
+ #
62
+ # @since 0.1.0
63
+ # @api private
64
+ def method_missing(*)
65
+ ::Kernel.raise(
66
+ ::Hanami::Assets::MissingDigestManifestError.new(@configuration.manifest_path)
67
+ )
68
+ end
69
+ end
70
+
71
+ # Digest manifest
72
+ #
73
+ # @since 0.1.0
74
+ # @api private
75
+ class DigestManifest
76
+ # Return a new instance
77
+ #
78
+ # @param assets [Hash] the content of the digest manifest
79
+ # @param manifest_path [Pathname] the path to the digest manifest
80
+ #
81
+ # @return [Hanami::Assets::Config::Manifest] a new instance
82
+ #
83
+ # @since 0.1.0
84
+ # @api private
85
+ #
86
+ # @see Hanami::Assets::Configuration#manifest
87
+ # @see Hanami::Assets::Configuration#manifest_path
88
+ def initialize(assets, manifest_path)
89
+ @assets = assets
90
+ @manifest_path = manifest_path
91
+ end
92
+
93
+ # Resolve the given asset into a digest path
94
+ #
95
+ # For a given path <tt>/assets/application.js</tt> it will return
96
+ # <tt>/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js</tt>
97
+ #
98
+ # @param asset [#to_s] the relateive asset path
99
+ #
100
+ # @return [String] the digest path
101
+ #
102
+ # @raise [Hanami::Assets::MissingDigestAssetError] when the asset can't be
103
+ # found in manifest
104
+ def resolve(asset)
105
+ @assets.fetch(asset.to_s) do
106
+ raise Hanami::Assets::MissingDigestAssetError.new(asset, @manifest_path)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,77 @@
1
+ require 'hanami/utils/load_paths'
2
+
3
+ module Hanami
4
+ module Assets
5
+ # Configuration settings
6
+ #
7
+ # @since 0.1.0
8
+ # @api private
9
+ module Config
10
+ # Source directories for a specific application
11
+ #
12
+ # @since 0.1.0
13
+ # @api private
14
+ #
15
+ # @see Hanami::Assets.duplicate
16
+ # @see http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/LoadPaths
17
+ #
18
+ # TODO The perf of this class is poor, consider to improve it.
19
+ class Sources < Utils::LoadPaths
20
+ # @since 0.1.0
21
+ # @api private
22
+ attr_writer :root
23
+
24
+ # @since 0.1.0
25
+ # @api private
26
+ def initialize(root)
27
+ super()
28
+ @root = root
29
+ end
30
+
31
+ # @since 0.1.0
32
+ # @api private
33
+ def map
34
+ Array.new.tap do |result|
35
+ each do |source|
36
+ result << yield(source)
37
+ end
38
+ end
39
+ end
40
+
41
+ # @since 0.1.0
42
+ # @api private
43
+ def find(filename)
44
+ result = files(filename).first
45
+ result = Pathname.new(result) unless result.nil?
46
+ result
47
+ end
48
+
49
+ # @since 0.1.0
50
+ # @api private
51
+ def files(name = nil)
52
+ result = []
53
+
54
+ Dir.glob(map {|source| "#{ source }#{ ::File::SEPARATOR }**#{ ::File::SEPARATOR }#{ name }*"}).each do |file|
55
+ next if ::File.directory?(file) || ::File.basename(file).match(/\A\_/)
56
+ result << file
57
+ end
58
+
59
+ result
60
+ end
61
+
62
+ # @since 0.1.0
63
+ # @api private
64
+ def to_a
65
+ map {|s| s }
66
+ end
67
+
68
+ private
69
+ # @since 0.1.0
70
+ # @api private
71
+ def realpath(path)
72
+ @root.join(path).realpath
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,539 @@
1
+ require 'pathname'
2
+ require 'json'
3
+ require 'hanami/utils/string'
4
+ require 'hanami/utils/class'
5
+ require 'hanami/utils/path_prefix'
6
+ require 'hanami/utils/basic_object'
7
+ require 'hanami/assets/config/manifest'
8
+ require 'hanami/assets/config/sources'
9
+
10
+ module Hanami
11
+ module Assets
12
+ # Framework configuration
13
+ #
14
+ # @since 0.1.0
15
+ class Configuration
16
+ # @since 0.1.0
17
+ # @api private
18
+ DEFAULT_SCHEME = 'http'.freeze
19
+
20
+ # @since 0.1.0
21
+ # @api private
22
+ DEFAULT_HOST = 'localhost'.freeze
23
+
24
+ # @since 0.1.0
25
+ # @api private
26
+ DEFAULT_PORT = '2300'.freeze
27
+
28
+ # @since 0.1.0
29
+ # @api private
30
+ DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
31
+
32
+ # @since 0.1.0
33
+ # @api private
34
+ DEFAULT_MANIFEST = 'assets.json'.freeze
35
+
36
+ # @since 0.1.0
37
+ # @api private
38
+ DEFAULT_PREFIX = '/assets'.freeze
39
+
40
+ # @since 0.1.0
41
+ # @api private
42
+ URL_SEPARATOR = '/'.freeze
43
+
44
+ # @since 0.1.0
45
+ # @api private
46
+ HTTP_SCHEME = 'http'.freeze
47
+
48
+ # @since 0.1.0
49
+ # @api private
50
+ HTTP_PORT = '80'.freeze
51
+
52
+ # @since 0.1.0
53
+ # @api private
54
+ HTTPS_SCHEME = 'https'.freeze
55
+
56
+ # @since 0.1.0
57
+ # @api private
58
+ HTTPS_PORT = '443'.freeze
59
+
60
+ # Return a copy of the configuration of the framework instance associated
61
+ # with the given class.
62
+ #
63
+ # When multiple instances of Hanami::Assets are used in the same
64
+ # application, we want to make sure that a controller or an action will
65
+ # receive the expected configuration.
66
+ #
67
+ # @param base [Class, Module] a controller or an action
68
+ #
69
+ # @return [Hanami::Assets::Configuration] the configuration associated
70
+ # to the given class.
71
+ #
72
+ # @since 0.1.0
73
+ # @api private
74
+ def self.for(base)
75
+ # TODO this implementation is similar to Hanami::Controller::Configuration consider to extract it into Hanami::Utils
76
+ namespace = Utils::String.new(base).namespace
77
+ framework = Utils::Class.load_from_pattern!("(#{namespace}|Hanami)::Assets")
78
+ framework.configuration
79
+ end
80
+
81
+ # @since 0.1.0
82
+ # @api private
83
+ attr_reader :digest_manifest
84
+
85
+ # Return a new instance
86
+ #
87
+ # @return [Hanami::Assets::Configuration] a new instance
88
+ #
89
+ # @since 0.1.0
90
+ # @api private
91
+ def initialize
92
+ reset!
93
+ end
94
+
95
+ # Compile mode
96
+ #
97
+ # Determine if compile assets from sources to destination.
98
+ # Usually this is turned off in production mode.
99
+ #
100
+ # @since 0.1.0
101
+ def compile(value = nil)
102
+ if value.nil?
103
+ @compile
104
+ else
105
+ @compile = value
106
+ end
107
+ end
108
+
109
+ # Digest mode
110
+ #
111
+ # Determine if the helpers should generate the digest path for an asset.
112
+ # Usually this is turned on in production mode.
113
+ #
114
+ # @since 0.1.0
115
+ def digest(value = nil)
116
+ if value.nil?
117
+ @digest
118
+ else
119
+ @digest = value
120
+ end
121
+ end
122
+
123
+ # CDN mode
124
+ #
125
+ # Determine if the helpers should always generate absolute URL.
126
+ # This is useful in production mode.
127
+ #
128
+ # @since 0.1.0
129
+ def cdn(value = nil)
130
+ if value.nil?
131
+ @cdn
132
+ else
133
+ @cdn = !!value
134
+ end
135
+ end
136
+
137
+ # JavaScript compressor
138
+ #
139
+ # Determine which compressor to use for JavaScript files during deploy.
140
+ #
141
+ # By default it's <tt>nil</tt>, that means it doesn't compress JavaScripts at deploy time.
142
+ #
143
+ # It accepts a <tt>Symbol</tt> or an object that respond to <tt>#compress(file)</tt>.
144
+ #
145
+ # The following symbols are accepted:
146
+ #
147
+ # * <tt>:builtin</tt> - Ruby based implementation of jsmin. It doesn't require any external gem.
148
+ # * <tt>:yui</tt> - YUI Compressor, it depends on <tt>yui-compressor</tt> gem and it requires Java 1.4+
149
+ # * <tt>:uglifier</tt> - UglifyJS, it depends on <tt>uglifier</tt> gem and it requires Node.js
150
+ # * <tt>:closure</tt> - Google Closure Compiler, it depends on <tt>closure-compiler</tt> gem and it requires Java
151
+ #
152
+ # @param value [Symbol,#compress] the compressor
153
+ #
154
+ # @since 0.1.0
155
+ #
156
+ # @see http://yui.github.io/yuicompressor
157
+ # @see https://rubygems.org/gems/yui-compressor
158
+ #
159
+ # @see http://lisperator.net/uglifyjs
160
+ # @see https://rubygems.org/gems/uglifier
161
+ #
162
+ # @see https://developers.google.com/closure/compiler
163
+ # @see https://rubygems.org/gems/closure-compiler
164
+ #
165
+ # @example YUI Compressor
166
+ # require 'hanami/assets'
167
+ #
168
+ # Hanami::Assets.configure do
169
+ # # ...
170
+ # javascript_compressor :yui
171
+ # end.load!
172
+ #
173
+ # @example Custom Compressor
174
+ # require 'hanami/assets'
175
+ #
176
+ # Hanami::Assets.configure do
177
+ # # ...
178
+ # javascript_compressor MyCustomJavascriptCompressor.new
179
+ # end.load!
180
+ def javascript_compressor(value = nil)
181
+ if value.nil?
182
+ @javascript_compressor
183
+ else
184
+ @javascript_compressor = value
185
+ end
186
+ end
187
+
188
+ # Stylesheet compressor
189
+ #
190
+ # Determine which compressor to use for Stylesheet files during deploy.
191
+ #
192
+ # By default it's <tt>nil</tt>, that means it doesn't compress Stylesheets at deploy time.
193
+ #
194
+ # It accepts a <tt>Symbol</tt> or an object that respond to <tt>#compress(file)</tt>.
195
+ #
196
+ # The following symbols are accepted:
197
+ #
198
+ # * <tt>:builtin</tt> - Ruby based compressor. It doesn't require any external gem. It's fast, but not an efficient compressor.
199
+ # * <tt>:yui</tt> - YUI-Compressor, it depends on <tt>yui-compressor</tt> gem and requires Java 1.4+
200
+ # * <tt>:sass</tt> - Sass, it depends on <tt>sass</tt> gem
201
+ #
202
+ # @param value [Symbol,#compress] the compressor
203
+ #
204
+ # @since 0.1.0
205
+ #
206
+ # @see http://yui.github.io/yuicompressor
207
+ # @see https://rubygems.org/gems/yui-compressor
208
+ #
209
+ # @see http://sass-lang.com
210
+ # @see https://rubygems.org/gems/sass
211
+ #
212
+ # @example YUI Compressor
213
+ # require 'hanami/assets'
214
+ #
215
+ # Hanami::Assets.configure do
216
+ # # ...
217
+ # stylesheet_compressor :yui
218
+ # end.load!
219
+ #
220
+ # @example Custom Compressor
221
+ # require 'hanami/assets'
222
+ #
223
+ # Hanami::Assets.configure do
224
+ # # ...
225
+ # stylesheet_compressor MyCustomStylesheetCompressor.new
226
+ # end.load!
227
+ def stylesheet_compressor(value = nil)
228
+ if value.nil?
229
+ @stylesheet_compressor
230
+ else
231
+ @stylesheet_compressor = value
232
+ end
233
+ end
234
+
235
+ # URL scheme for the application
236
+ #
237
+ # This is used to generate absolute URL from helpers.
238
+ #
239
+ # @since 0.1.0
240
+ def scheme(value = nil)
241
+ if value.nil?
242
+ @scheme
243
+ else
244
+ @scheme = value
245
+ end
246
+ end
247
+
248
+ # URL host for the application
249
+ #
250
+ # This is used to generate absolute URL from helpers.
251
+ #
252
+ # @since 0.1.0
253
+ def host(value = nil)
254
+ if value.nil?
255
+ @host
256
+ else
257
+ @host = value
258
+ end
259
+ end
260
+
261
+ # URL port for the application
262
+ #
263
+ # This is used to generate absolute URL from helpers.
264
+ #
265
+ # @since 0.1.0
266
+ def port(value = nil)
267
+ if value.nil?
268
+ @port
269
+ else
270
+ @port = value.to_s
271
+ end
272
+ end
273
+
274
+ # URL port for the application
275
+ #
276
+ # This is used to generate absolute or relative URL from helpers.
277
+ #
278
+ # @since 0.1.0
279
+ def prefix(value = nil)
280
+ if value.nil?
281
+ @prefix
282
+ else
283
+ @prefix = Utils::PathPrefix.new(value)
284
+ end
285
+ end
286
+
287
+ # Sources root
288
+ #
289
+ # @since 0.1.0
290
+ def root(value = nil)
291
+ if value.nil?
292
+ @root
293
+ else
294
+ @root = Pathname.new(value).realpath
295
+ sources.root = @root
296
+ end
297
+ end
298
+
299
+ # Application public directory
300
+ #
301
+ # @since 0.1.0
302
+ def public_directory(value = nil)
303
+ if value.nil?
304
+ @public_directory
305
+ else
306
+ @public_directory = Pathname.new(::File.expand_path(value))
307
+ end
308
+ end
309
+
310
+ # Destination directory
311
+ #
312
+ # It's the combination of <tt>public_directory</tt> and <tt>prefix</tt>.
313
+ #
314
+ # @since 0.1.0
315
+ # @api private
316
+ def destination_directory
317
+ @destination_directory ||= public_directory.join(*prefix.split(URL_SEPARATOR))
318
+ end
319
+
320
+ # Manifest path from public directory
321
+ #
322
+ # @since 0.1.0
323
+ def manifest(value = nil)
324
+ if value.nil?
325
+ @manifest
326
+ else
327
+ @manifest = value.to_s
328
+ end
329
+ end
330
+
331
+ # Absolute manifest path
332
+ #
333
+ # @since 0.1.0
334
+ # @api private
335
+ def manifest_path
336
+ public_directory.join(manifest)
337
+ end
338
+
339
+ # Application's assets sources
340
+ #
341
+ # @since 0.1.0
342
+ # @api private
343
+ def sources
344
+ @sources ||= Hanami::Assets::Config::Sources.new(root)
345
+ end
346
+
347
+ # Application's assets
348
+ #
349
+ # @since 0.1.0
350
+ # @api private
351
+ def files
352
+ sources.files
353
+ end
354
+
355
+ # Find a file from sources
356
+ #
357
+ # @since 0.1.0
358
+ # @api private
359
+ def find(file)
360
+ @sources.find(file)
361
+ end
362
+
363
+ # Relative URL
364
+ #
365
+ # @since 0.1.0
366
+ # @api private
367
+ def asset_path(source)
368
+ cdn ?
369
+ asset_url(source) :
370
+ compile_path(source)
371
+ end
372
+
373
+ # Absolute URL
374
+ #
375
+ # @since 0.1.0
376
+ # @api private
377
+ def asset_url(source)
378
+ "#{ @base_url }#{ compile_path(source) }"
379
+ end
380
+
381
+ # Load Javascript compressor
382
+ #
383
+ # @return [Hanami::Assets::Compressors::Javascript] a compressor
384
+ #
385
+ # @raise [Hanami::Assets::Compressors::UnknownCompressorError] when the
386
+ # given name refers to an unknown compressor engine
387
+ #
388
+ # @since 0.1.0
389
+ # @api private
390
+ #
391
+ # @see Hanami::Assets::Configuration#javascript_compressor
392
+ # @see Hanami::Assets::Compressors::Javascript#for
393
+ def js_compressor
394
+ require 'hanami/assets/compressors/javascript'
395
+ Hanami::Assets::Compressors::Javascript.for(javascript_compressor)
396
+ end
397
+
398
+ # Load Stylesheet compressor
399
+ #
400
+ # @return [Hanami::Assets::Compressors::Stylesheet] a compressor
401
+ #
402
+ # @raise [Hanami::Assets::Compressors::UnknownCompressorError] when the
403
+ # given name refers to an unknown compressor engine
404
+ #
405
+ # @since 0.1.0
406
+ # @api private
407
+ #
408
+ # @see Hanami::Assets::Configuration#stylesheet_compressor
409
+ # @see Hanami::Assets::Compressors::Stylesheet#for
410
+ def css_compressor
411
+ require 'hanami/assets/compressors/stylesheet'
412
+ Hanami::Assets::Compressors::Stylesheet.for(stylesheet_compressor)
413
+ end
414
+
415
+ # @since 0.1.0
416
+ # @api private
417
+ def duplicate
418
+ Configuration.new.tap do |c|
419
+ c.root = root
420
+ c.scheme = scheme
421
+ c.host = host
422
+ c.port = port
423
+ c.prefix = prefix
424
+ c.cdn = cdn
425
+ c.compile = compile
426
+ c.public_directory = public_directory
427
+ c.manifest = manifest
428
+ c.sources = sources.dup
429
+ c.javascript_compressor = javascript_compressor
430
+ c.stylesheet_compressor = stylesheet_compressor
431
+ end
432
+ end
433
+
434
+ # @since 0.1.0
435
+ # @api private
436
+ def reset!
437
+ @scheme = DEFAULT_SCHEME
438
+ @host = DEFAULT_HOST
439
+ @port = DEFAULT_PORT
440
+
441
+ @prefix = Utils::PathPrefix.new(DEFAULT_PREFIX)
442
+ @cdn = false
443
+ @compile = false
444
+ @destination_directory = nil
445
+ @digest_manifest = Config::NullDigestManifest.new(self)
446
+
447
+ @javascript_compressor = nil
448
+ @stylesheet_compressor = nil
449
+
450
+ root Dir.pwd
451
+ public_directory root.join(DEFAULT_PUBLIC_DIRECTORY)
452
+ manifest DEFAULT_MANIFEST
453
+ end
454
+
455
+ # Load the configuration
456
+ #
457
+ # This MUST be executed before to accept the first HTTP request
458
+ #
459
+ # @since 0.1.0
460
+ def load!
461
+ if digest && manifest_path.exist?
462
+ @digest_manifest = Config::DigestManifest.new(
463
+ JSON.load(manifest_path.read),
464
+ manifest_path
465
+ )
466
+ end
467
+
468
+ @base_url = URI::Generic.build(scheme: scheme, host: host, port: url_port).to_s
469
+ end
470
+
471
+ protected
472
+
473
+ # @since 0.1.0
474
+ # @api private
475
+ attr_writer :cdn
476
+
477
+ # @since 0.1.0
478
+ # @api private
479
+ attr_writer :compile
480
+
481
+ # @since 0.1.0
482
+ # @api private
483
+ attr_writer :scheme
484
+
485
+ # @since 0.1.0
486
+ # @api private
487
+ attr_writer :host
488
+
489
+ # @since 0.1.0
490
+ # @api private
491
+ attr_writer :port
492
+
493
+ # @since 0.1.0
494
+ # @api private
495
+ attr_writer :prefix
496
+
497
+ # @since 0.1.0
498
+ # @api private
499
+ attr_writer :root
500
+
501
+ # @since 0.1.0
502
+ # @api private
503
+ attr_writer :public_directory
504
+
505
+ # @since 0.1.0
506
+ # @api private
507
+ attr_writer :manifest
508
+
509
+ # @since 0.1.0
510
+ # @api private
511
+ attr_writer :sources
512
+
513
+ # @since 0.1.0
514
+ # @api private
515
+ attr_writer :javascript_compressor
516
+
517
+ # @since 0.1.0
518
+ # @api private
519
+ attr_writer :stylesheet_compressor
520
+
521
+ private
522
+
523
+ # @since 0.1.0
524
+ # @api private
525
+ def compile_path(source)
526
+ result = prefix.join(source)
527
+ result = digest_manifest.resolve(result) if digest
528
+ result.to_s
529
+ end
530
+
531
+ # @since 0.1.0
532
+ # @api private
533
+ def url_port
534
+ ( (scheme == HTTP_SCHEME && port == HTTP_PORT ) ||
535
+ (scheme == HTTPS_SCHEME && port == HTTPS_PORT ) ) ? nil : port.to_i
536
+ end
537
+ end
538
+ end
539
+ end