hanami-assets 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +49 -25
- data/bin/hanami-assets +4 -4
- data/hanami-assets.gemspec +7 -7
- data/lib/hanami/assets/bundler/asset.rb +98 -0
- data/lib/hanami/assets/bundler/compressor.rb +61 -0
- data/lib/hanami/assets/bundler/manifest_entry.rb +62 -0
- data/lib/hanami/assets/bundler.rb +35 -62
- data/lib/hanami/assets/cache.rb +58 -15
- data/lib/hanami/assets/compiler.rb +66 -45
- data/lib/hanami/assets/compilers/less.rb +29 -0
- data/lib/hanami/assets/compilers/sass.rb +46 -0
- data/lib/hanami/assets/compressors/sass_stylesheet.rb +2 -2
- data/lib/hanami/assets/config/global_sources.rb +1 -1
- data/lib/hanami/assets/config/manifest.rb +23 -3
- data/lib/hanami/assets/config/sources.rb +8 -3
- data/lib/hanami/assets/configuration.rb +90 -23
- data/lib/hanami/assets/helpers.rb +81 -9
- data/lib/hanami/assets/precompiler.rb +31 -8
- data/lib/hanami/assets/version.rb +1 -1
- data/lib/hanami/assets.rb +12 -10
- metadata +15 -11
@@ -12,50 +12,58 @@ module Hanami
|
|
12
12
|
# Framework configuration
|
13
13
|
#
|
14
14
|
# @since 0.1.0
|
15
|
-
class Configuration
|
15
|
+
class Configuration # rubocop:disable Metrics/ClassLength
|
16
16
|
# @since 0.1.0
|
17
17
|
# @api private
|
18
|
-
DEFAULT_SCHEME
|
18
|
+
DEFAULT_SCHEME = 'http'.freeze
|
19
19
|
|
20
20
|
# @since 0.1.0
|
21
21
|
# @api private
|
22
|
-
DEFAULT_HOST
|
22
|
+
DEFAULT_HOST = 'localhost'.freeze
|
23
23
|
|
24
24
|
# @since 0.1.0
|
25
25
|
# @api private
|
26
|
-
DEFAULT_PORT
|
26
|
+
DEFAULT_PORT = '2300'.freeze
|
27
27
|
|
28
28
|
# @since 0.1.0
|
29
29
|
# @api private
|
30
|
-
DEFAULT_PUBLIC_DIRECTORY
|
30
|
+
DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
|
31
31
|
|
32
32
|
# @since 0.1.0
|
33
33
|
# @api private
|
34
|
-
DEFAULT_MANIFEST
|
34
|
+
DEFAULT_MANIFEST = 'assets.json'.freeze
|
35
35
|
|
36
36
|
# @since 0.1.0
|
37
37
|
# @api private
|
38
|
-
DEFAULT_PREFIX
|
38
|
+
DEFAULT_PREFIX = '/assets'.freeze
|
39
39
|
|
40
40
|
# @since 0.1.0
|
41
41
|
# @api private
|
42
|
-
URL_SEPARATOR
|
42
|
+
URL_SEPARATOR = '/'.freeze
|
43
43
|
|
44
44
|
# @since 0.1.0
|
45
45
|
# @api private
|
46
|
-
HTTP_SCHEME
|
46
|
+
HTTP_SCHEME = 'http'.freeze
|
47
47
|
|
48
48
|
# @since 0.1.0
|
49
49
|
# @api private
|
50
|
-
HTTP_PORT
|
50
|
+
HTTP_PORT = '80'.freeze
|
51
51
|
|
52
52
|
# @since 0.1.0
|
53
53
|
# @api private
|
54
|
-
HTTPS_SCHEME
|
54
|
+
HTTPS_SCHEME = 'https'.freeze
|
55
55
|
|
56
56
|
# @since 0.1.0
|
57
57
|
# @api private
|
58
|
-
HTTPS_PORT
|
58
|
+
HTTPS_PORT = '443'.freeze
|
59
|
+
|
60
|
+
# @since 0.3.0
|
61
|
+
# @api private
|
62
|
+
DEFAULT_SUBRESOURCE_INTEGRITY_ALGORITHM = :sha256
|
63
|
+
|
64
|
+
# @since 0.3.0
|
65
|
+
# @api private
|
66
|
+
SUBRESOURCE_INTEGRITY_SEPARATOR = ' '.freeze
|
59
67
|
|
60
68
|
# Return a copy of the configuration of the framework instance associated
|
61
69
|
# with the given class.
|
@@ -72,7 +80,7 @@ module Hanami
|
|
72
80
|
# @since 0.1.0
|
73
81
|
# @api private
|
74
82
|
def self.for(base)
|
75
|
-
# TODO this implementation is similar to Hanami::Controller::Configuration consider to extract it into Hanami::Utils
|
83
|
+
# TODO: this implementation is similar to Hanami::Controller::Configuration consider to extract it into Hanami::Utils
|
76
84
|
namespace = Utils::String.new(base).namespace
|
77
85
|
framework = Utils::Class.load_from_pattern!("(#{namespace}|Hanami)::Assets")
|
78
86
|
framework.configuration
|
@@ -120,6 +128,22 @@ module Hanami
|
|
120
128
|
end
|
121
129
|
end
|
122
130
|
|
131
|
+
# Subresource integrity mode
|
132
|
+
#
|
133
|
+
# Determine if the helpers should generate the integrity attribute for an
|
134
|
+
# asset. Usually this is turned on in production mode.
|
135
|
+
#
|
136
|
+
# @since 0.3.0
|
137
|
+
def subresource_integrity(*values)
|
138
|
+
if values.empty?
|
139
|
+
@subresource_integrity
|
140
|
+
elsif values.length == 1
|
141
|
+
@subresource_integrity = values.first
|
142
|
+
else
|
143
|
+
@subresource_integrity = values
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
123
147
|
# CDN mode
|
124
148
|
#
|
125
149
|
# Determine if the helpers should always generate absolute URL.
|
@@ -130,7 +154,7 @@ module Hanami
|
|
130
154
|
if value.nil?
|
131
155
|
@cdn
|
132
156
|
else
|
133
|
-
@cdn = !!value
|
157
|
+
@cdn = !!value # rubocop:disable Style/DoubleNegation
|
134
158
|
end
|
135
159
|
end
|
136
160
|
|
@@ -352,6 +376,13 @@ module Hanami
|
|
352
376
|
sources.files
|
353
377
|
end
|
354
378
|
|
379
|
+
# @since 0.3.0
|
380
|
+
# @api private
|
381
|
+
def source(file)
|
382
|
+
pathname = Pathname.new(file)
|
383
|
+
pathname.absolute? ? pathname : find(file)
|
384
|
+
end
|
385
|
+
|
355
386
|
# Find a file from sources
|
356
387
|
#
|
357
388
|
# @since 0.1.0
|
@@ -365,9 +396,11 @@ module Hanami
|
|
365
396
|
# @since 0.1.0
|
366
397
|
# @api private
|
367
398
|
def asset_path(source)
|
368
|
-
cdn
|
369
|
-
asset_url(source)
|
399
|
+
if cdn
|
400
|
+
asset_url(source)
|
401
|
+
else
|
370
402
|
compile_path(source)
|
403
|
+
end
|
371
404
|
end
|
372
405
|
|
373
406
|
# Absolute URL
|
@@ -375,7 +408,33 @@ module Hanami
|
|
375
408
|
# @since 0.1.0
|
376
409
|
# @api private
|
377
410
|
def asset_url(source)
|
378
|
-
"#{
|
411
|
+
"#{@base_url}#{compile_path(source)}"
|
412
|
+
end
|
413
|
+
|
414
|
+
# An array of digest algorithms to use for generating asset subresource
|
415
|
+
# integrity checks
|
416
|
+
#
|
417
|
+
# @since 0.3.0
|
418
|
+
def subresource_integrity_algorithms
|
419
|
+
if @subresource_integrity == true
|
420
|
+
[DEFAULT_SUBRESOURCE_INTEGRITY_ALGORITHM]
|
421
|
+
else
|
422
|
+
# Using Array() allows us to accept Array or Symbol, and '|| nil' lets
|
423
|
+
# us return an empty array when @subresource_integrity is `false`
|
424
|
+
Array(@subresource_integrity || nil)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# Subresource integrity attribute
|
429
|
+
#
|
430
|
+
# @since 0.3.0
|
431
|
+
# @api private
|
432
|
+
def subresource_integrity_value(source)
|
433
|
+
if subresource_integrity
|
434
|
+
digest_manifest.subresource_integrity_values(
|
435
|
+
prefix.join(source)
|
436
|
+
).join(SUBRESOURCE_INTEGRITY_SEPARATOR)
|
437
|
+
end
|
379
438
|
end
|
380
439
|
|
381
440
|
# Load Javascript compressor
|
@@ -414,13 +473,14 @@ module Hanami
|
|
414
473
|
|
415
474
|
# @since 0.1.0
|
416
475
|
# @api private
|
417
|
-
def duplicate
|
476
|
+
def duplicate # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
418
477
|
Configuration.new.tap do |c|
|
419
478
|
c.root = root
|
420
479
|
c.scheme = scheme
|
421
480
|
c.host = host
|
422
481
|
c.port = port
|
423
482
|
c.prefix = prefix
|
483
|
+
c.subresource_integrity = subresource_integrity
|
424
484
|
c.cdn = cdn
|
425
485
|
c.compile = compile
|
426
486
|
c.public_directory = public_directory
|
@@ -433,14 +493,17 @@ module Hanami
|
|
433
493
|
|
434
494
|
# @since 0.1.0
|
435
495
|
# @api private
|
436
|
-
def reset!
|
496
|
+
def reset! # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
437
497
|
@scheme = DEFAULT_SCHEME
|
438
498
|
@host = DEFAULT_HOST
|
439
499
|
@port = DEFAULT_PORT
|
440
500
|
|
441
501
|
@prefix = Utils::PathPrefix.new(DEFAULT_PREFIX)
|
502
|
+
@subresource_integrity = false
|
442
503
|
@cdn = false
|
504
|
+
@digest = false
|
443
505
|
@compile = false
|
506
|
+
@base_url = nil
|
444
507
|
@destination_directory = nil
|
445
508
|
@digest_manifest = Config::NullDigestManifest.new(self)
|
446
509
|
|
@@ -458,7 +521,7 @@ module Hanami
|
|
458
521
|
#
|
459
522
|
# @since 0.1.0
|
460
523
|
def load!
|
461
|
-
if digest && manifest_path.exist?
|
524
|
+
if (digest || subresource_integrity) && manifest_path.exist?
|
462
525
|
@digest_manifest = Config::DigestManifest.new(
|
463
526
|
JSON.load(manifest_path.read),
|
464
527
|
manifest_path
|
@@ -470,6 +533,10 @@ module Hanami
|
|
470
533
|
|
471
534
|
protected
|
472
535
|
|
536
|
+
# @since 0.3.0
|
537
|
+
# @api private
|
538
|
+
attr_writer :subresource_integrity
|
539
|
+
|
473
540
|
# @since 0.1.0
|
474
541
|
# @api private
|
475
542
|
attr_writer :cdn
|
@@ -524,15 +591,15 @@ module Hanami
|
|
524
591
|
# @api private
|
525
592
|
def compile_path(source)
|
526
593
|
result = prefix.join(source)
|
527
|
-
result = digest_manifest.
|
594
|
+
result = digest_manifest.target(result) if digest
|
528
595
|
result.to_s
|
529
596
|
end
|
530
597
|
|
531
598
|
# @since 0.1.0
|
532
599
|
# @api private
|
533
600
|
def url_port
|
534
|
-
(
|
535
|
-
(scheme == HTTPS_SCHEME && port == HTTPS_PORT
|
601
|
+
((scheme == HTTP_SCHEME && port == HTTP_PORT) || # rubocop:disable Style/MultilineTernaryOperator
|
602
|
+
(scheme == HTTPS_SCHEME && port == HTTPS_PORT)) ? nil : port.to_i
|
536
603
|
end
|
537
604
|
end
|
538
605
|
end
|
@@ -13,7 +13,7 @@ module Hanami
|
|
13
13
|
# @since 0.1.0
|
14
14
|
#
|
15
15
|
# @see http://www.rubydoc.info/gems/hanami-helpers/Hanami/Helpers/HtmlHelper
|
16
|
-
module Helpers
|
16
|
+
module Helpers # rubocop:disable Metrics/ModuleLength
|
17
17
|
# @since 0.1.0
|
18
18
|
# @api private
|
19
19
|
NEW_LINE_SEPARATOR = "\n".freeze
|
@@ -54,6 +54,14 @@ module Hanami
|
|
54
54
|
# @api private
|
55
55
|
DEFAULT_FAVICON = 'favicon.ico'.freeze
|
56
56
|
|
57
|
+
# @since 0.3.0
|
58
|
+
# @api private
|
59
|
+
CROSSORIGIN_ANONYMOUS = 'anonymous'.freeze
|
60
|
+
|
61
|
+
# @since 0.3.0
|
62
|
+
# @api private
|
63
|
+
ABSOLUTE_URL_MATCHER = URI::Parser.new.make_regexp
|
64
|
+
|
57
65
|
include Hanami::Helpers::HtmlHelper
|
58
66
|
|
59
67
|
# Inject helpers into the given class
|
@@ -108,6 +116,30 @@ module Hanami
|
|
108
116
|
# # <script src="/assets/application.js" type="text/javascript"></script>
|
109
117
|
# # <script src="/assets/dashboard.js" type="text/javascript"></script>
|
110
118
|
#
|
119
|
+
# @example Asynchronous Execution
|
120
|
+
#
|
121
|
+
# <%= javascript 'application', async: true %>
|
122
|
+
#
|
123
|
+
# # <script src="/assets/application.js" type="text/javascript" async="async"></script>
|
124
|
+
#
|
125
|
+
# @example Subresource Integrity
|
126
|
+
#
|
127
|
+
# <%= javascript 'application' %>
|
128
|
+
#
|
129
|
+
# # <script src="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
|
130
|
+
#
|
131
|
+
# @example Subresource Integrity for 3rd Party Scripts
|
132
|
+
#
|
133
|
+
# <%= javascript 'https://example.com/assets/example.js', integrity: 'sha384-oqVu...Y8wC' %>
|
134
|
+
#
|
135
|
+
# # <script src="https://example.com/assets/example.js" type="text/javascript" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
|
136
|
+
#
|
137
|
+
# @example Deferred Execution
|
138
|
+
#
|
139
|
+
# <%= javascript 'application', defer: true %>
|
140
|
+
#
|
141
|
+
# # <script src="/assets/application.js" type="text/javascript" defer="defer"></script>
|
142
|
+
#
|
111
143
|
# @example Absolute URL
|
112
144
|
#
|
113
145
|
# <%= javascript 'https://code.jquery.com/jquery-2.1.4.min.js' %>
|
@@ -125,9 +157,18 @@ module Hanami
|
|
125
157
|
# <%= javascript 'application' %>
|
126
158
|
#
|
127
159
|
# # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
|
128
|
-
def javascript(*sources)
|
160
|
+
def javascript(*sources, **options)
|
129
161
|
_safe_tags(*sources) do |source|
|
130
|
-
|
162
|
+
tag_options = options.dup
|
163
|
+
tag_options[:src] ||= _typed_asset_path(source, JAVASCRIPT_EXT)
|
164
|
+
tag_options[:type] ||= JAVASCRIPT_MIME_TYPE
|
165
|
+
|
166
|
+
if _subresource_integrity? || tag_options.include?(:integrity)
|
167
|
+
tag_options[:integrity] ||= _subresource_integrity_value(source, JAVASCRIPT_EXT)
|
168
|
+
tag_options[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
|
169
|
+
end
|
170
|
+
|
171
|
+
html.script(**tag_options).to_s
|
131
172
|
end
|
132
173
|
end
|
133
174
|
|
@@ -169,6 +210,18 @@ module Hanami
|
|
169
210
|
# # <link href="/assets/application.css" type="text/css" rel="stylesheet">
|
170
211
|
# # <link href="/assets/dashboard.css" type="text/css" rel="stylesheet">
|
171
212
|
#
|
213
|
+
# @example Subresource Integrity
|
214
|
+
#
|
215
|
+
# <%= stylesheet 'application' %>
|
216
|
+
#
|
217
|
+
# # <link href="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
|
218
|
+
#
|
219
|
+
# @example Subresource Integrity for 3rd Party Assets
|
220
|
+
#
|
221
|
+
# <%= stylesheet 'https://example.com/assets/example.css', integrity: 'sha384-oqVu...Y8wC' %>
|
222
|
+
#
|
223
|
+
# # <link href="https://example.com/assets/example.css" type="text/css" rel="stylesheet" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
|
224
|
+
#
|
172
225
|
# @example Absolute URL
|
173
226
|
#
|
174
227
|
# <%= stylesheet 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' %>
|
@@ -186,9 +239,19 @@ module Hanami
|
|
186
239
|
# <%= stylesheet 'application' %>
|
187
240
|
#
|
188
241
|
# # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
|
189
|
-
def stylesheet(*sources)
|
242
|
+
def stylesheet(*sources, **options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
190
243
|
_safe_tags(*sources) do |source|
|
191
|
-
|
244
|
+
tag_options = options.dup
|
245
|
+
tag_options[:href] ||= _typed_asset_path(source, STYLESHEET_EXT)
|
246
|
+
tag_options[:type] ||= STYLESHEET_MIME_TYPE
|
247
|
+
tag_options[:rel] ||= STYLESHEET_REL
|
248
|
+
|
249
|
+
if _subresource_integrity? || tag_options.include?(:integrity)
|
250
|
+
tag_options[:integrity] ||= _subresource_integrity_value(source, STYLESHEET_EXT)
|
251
|
+
tag_options[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
|
252
|
+
end
|
253
|
+
|
254
|
+
html.link(**tag_options).to_s
|
192
255
|
end
|
193
256
|
end
|
194
257
|
|
@@ -670,7 +733,7 @@ module Hanami
|
|
670
733
|
# @api private
|
671
734
|
def _asset_url(source)
|
672
735
|
_push_promise(
|
673
|
-
_absolute_url?(source) ?
|
736
|
+
_absolute_url?(source) ? # rubocop:disable Style/MultilineTernaryOperator
|
674
737
|
source : yield
|
675
738
|
)
|
676
739
|
end
|
@@ -678,14 +741,23 @@ module Hanami
|
|
678
741
|
# @since 0.1.0
|
679
742
|
# @api private
|
680
743
|
def _typed_asset_path(source, ext)
|
681
|
-
source = "#{
|
744
|
+
source = "#{source}#{ext}" unless source =~ /#{Regexp.escape(ext)}\z/
|
682
745
|
asset_path(source)
|
683
746
|
end
|
684
747
|
|
748
|
+
def _subresource_integrity?
|
749
|
+
!!self.class.assets_configuration.subresource_integrity # rubocop:disable Style/DoubleNegation
|
750
|
+
end
|
751
|
+
|
752
|
+
def _subresource_integrity_value(source, ext)
|
753
|
+
source = "#{source}#{ext}" unless source =~ /#{Regexp.escape(ext)}\z/
|
754
|
+
self.class.assets_configuration.subresource_integrity_value(source) unless _absolute_url?(source)
|
755
|
+
end
|
756
|
+
|
685
757
|
# @since 0.1.0
|
686
758
|
# @api private
|
687
759
|
def _absolute_url?(source)
|
688
|
-
|
760
|
+
ABSOLUTE_URL_MATCHER.match(source)
|
689
761
|
end
|
690
762
|
|
691
763
|
# @since 0.1.0
|
@@ -702,7 +774,7 @@ module Hanami
|
|
702
774
|
|
703
775
|
# @since 0.1.0
|
704
776
|
# @api private
|
705
|
-
def _source_options(src, options, &
|
777
|
+
def _source_options(src, options, &_blk)
|
706
778
|
options ||= {}
|
707
779
|
|
708
780
|
if src.respond_to?(:to_hash)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'fileutils'
|
1
2
|
require 'hanami/assets/compiler'
|
2
3
|
|
3
4
|
module Hanami
|
@@ -30,22 +31,35 @@ module Hanami
|
|
30
31
|
# @since 0.1.0
|
31
32
|
# @api private
|
32
33
|
def run
|
33
|
-
|
34
|
+
clear_assets_directory
|
34
35
|
precompile
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
39
|
|
39
|
-
# @since 0.
|
40
|
+
# @since 0.3.0
|
40
41
|
# @api private
|
41
|
-
def
|
42
|
-
|
43
|
-
|
42
|
+
def clear_assets_directory
|
43
|
+
delete @configuration.manifest_path
|
44
|
+
delete @configuration.destination_directory
|
45
|
+
end
|
46
|
+
|
47
|
+
# @since 0.3.0
|
48
|
+
# @api private
|
49
|
+
def clear_manifest(manifest)
|
50
|
+
JSON.load(manifest).each do |_, asset_hash|
|
51
|
+
asset_file_name = @configuration.public_directory.join(asset_hash['target'])
|
52
|
+
asset_file_name.unlink if asset_file_name.exist?
|
53
|
+
end
|
54
|
+
rescue JSON::ParserError
|
55
|
+
$stderr.puts 'Non JSON manifest found and unlinked.'
|
56
|
+
ensure
|
57
|
+
manifest.unlink
|
44
58
|
end
|
45
59
|
|
46
60
|
# @since 0.1.0
|
47
61
|
# @api private
|
48
|
-
def precompile
|
62
|
+
def precompile # rubocop:disable Metrics/MethodLength
|
49
63
|
applications.each do |duplicate|
|
50
64
|
config = if duplicate.respond_to?(:configuration)
|
51
65
|
duplicate.configuration
|
@@ -64,8 +78,17 @@ module Hanami
|
|
64
78
|
# @since 0.1.0
|
65
79
|
# @api private
|
66
80
|
def applications
|
67
|
-
@duplicates.empty?
|
68
|
-
[@configuration]
|
81
|
+
if @duplicates.empty?
|
82
|
+
[@configuration]
|
83
|
+
else
|
84
|
+
@duplicates
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# @since 0.3.0
|
89
|
+
# @api private
|
90
|
+
def delete(path)
|
91
|
+
FileUtils.rm_rf(path) if path.exist?
|
69
92
|
end
|
70
93
|
end
|
71
94
|
end
|
data/lib/hanami/assets.rb
CHANGED
@@ -93,22 +93,22 @@ module Hanami
|
|
93
93
|
# Hanami::Assets.sources << '/path/to/emberjs/assets'
|
94
94
|
def self.sources
|
95
95
|
synchronize do
|
96
|
-
@@sources ||= Config::GlobalSources.new
|
96
|
+
@@sources ||= Config::GlobalSources.new # rubocop:disable Style/ClassVars
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
100
|
# Duplicate the framework and generate modules for the target application
|
101
101
|
#
|
102
|
-
# @param
|
102
|
+
# @param _mod [Module] the Ruby namespace of the application
|
103
103
|
# @param blk [Proc] an optional block to configure the framework
|
104
104
|
#
|
105
105
|
# @return [Module] a copy of Hanami::Assets
|
106
106
|
#
|
107
|
-
#
|
107
|
+
# @since 0.1.0
|
108
108
|
#
|
109
109
|
# @see Hanami::Assets#dupe
|
110
110
|
# @see Hanami::Assets::Configuration
|
111
|
-
def self.duplicate(
|
111
|
+
def self.duplicate(_mod, &blk)
|
112
112
|
dupe.tap do |duplicated|
|
113
113
|
duplicated.configure(&blk) if block_given?
|
114
114
|
duplicates << duplicated
|
@@ -143,16 +143,18 @@ module Hanami
|
|
143
143
|
# @see Hanami::Assets#dupe
|
144
144
|
def self.duplicates
|
145
145
|
synchronize do
|
146
|
-
@@duplicates ||=
|
146
|
+
@@duplicates ||= [] # rubocop:disable Style/ClassVars
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
-
|
150
|
+
class << self
|
151
|
+
private
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
153
|
+
# @since 0.1.0
|
154
|
+
# @api private
|
155
|
+
def synchronize(&blk)
|
156
|
+
Mutex.new.synchronize(&blk)
|
157
|
+
end
|
156
158
|
end
|
157
159
|
end
|
158
160
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hanami-assets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-07-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: hanami-utils
|
@@ -18,28 +18,28 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '0.
|
21
|
+
version: '0.8'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: '0.
|
28
|
+
version: '0.8'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: hanami-helpers
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
33
|
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version: '0.
|
35
|
+
version: '0.4'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
40
|
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '0.
|
42
|
+
version: '0.4'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: tilt
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,14 +80,14 @@ dependencies:
|
|
80
80
|
requirements:
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '11'
|
84
84
|
type: :development
|
85
85
|
prerelease: false
|
86
86
|
version_requirements: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
88
|
- - "~>"
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '
|
90
|
+
version: '11'
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: minitest
|
93
93
|
requirement: !ruby/object:Gem::Requirement
|
@@ -203,8 +203,13 @@ files:
|
|
203
203
|
- hanami-assets.gemspec
|
204
204
|
- lib/hanami/assets.rb
|
205
205
|
- lib/hanami/assets/bundler.rb
|
206
|
+
- lib/hanami/assets/bundler/asset.rb
|
207
|
+
- lib/hanami/assets/bundler/compressor.rb
|
208
|
+
- lib/hanami/assets/bundler/manifest_entry.rb
|
206
209
|
- lib/hanami/assets/cache.rb
|
207
210
|
- lib/hanami/assets/compiler.rb
|
211
|
+
- lib/hanami/assets/compilers/less.rb
|
212
|
+
- lib/hanami/assets/compilers/sass.rb
|
208
213
|
- lib/hanami/assets/compressors/abstract.rb
|
209
214
|
- lib/hanami/assets/compressors/builtin_javascript.rb
|
210
215
|
- lib/hanami/assets/compressors/builtin_stylesheet.rb
|
@@ -236,7 +241,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
236
241
|
requirements:
|
237
242
|
- - ">="
|
238
243
|
- !ruby/object:Gem::Version
|
239
|
-
version: 2.
|
244
|
+
version: 2.2.0
|
240
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
241
246
|
requirements:
|
242
247
|
- - ">="
|
@@ -244,9 +249,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
244
249
|
version: '0'
|
245
250
|
requirements: []
|
246
251
|
rubyforge_project:
|
247
|
-
rubygems_version: 2.
|
252
|
+
rubygems_version: 2.6.4
|
248
253
|
signing_key:
|
249
254
|
specification_version: 4
|
250
255
|
summary: Assets management
|
251
256
|
test_files: []
|
252
|
-
has_rdoc:
|