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.
@@ -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 = 'http'.freeze
18
+ DEFAULT_SCHEME = 'http'.freeze
19
19
 
20
20
  # @since 0.1.0
21
21
  # @api private
22
- DEFAULT_HOST = 'localhost'.freeze
22
+ DEFAULT_HOST = 'localhost'.freeze
23
23
 
24
24
  # @since 0.1.0
25
25
  # @api private
26
- DEFAULT_PORT = '2300'.freeze
26
+ DEFAULT_PORT = '2300'.freeze
27
27
 
28
28
  # @since 0.1.0
29
29
  # @api private
30
- DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
30
+ DEFAULT_PUBLIC_DIRECTORY = 'public'.freeze
31
31
 
32
32
  # @since 0.1.0
33
33
  # @api private
34
- DEFAULT_MANIFEST = 'assets.json'.freeze
34
+ DEFAULT_MANIFEST = 'assets.json'.freeze
35
35
 
36
36
  # @since 0.1.0
37
37
  # @api private
38
- DEFAULT_PREFIX = '/assets'.freeze
38
+ DEFAULT_PREFIX = '/assets'.freeze
39
39
 
40
40
  # @since 0.1.0
41
41
  # @api private
42
- URL_SEPARATOR = '/'.freeze
42
+ URL_SEPARATOR = '/'.freeze
43
43
 
44
44
  # @since 0.1.0
45
45
  # @api private
46
- HTTP_SCHEME = 'http'.freeze
46
+ HTTP_SCHEME = 'http'.freeze
47
47
 
48
48
  # @since 0.1.0
49
49
  # @api private
50
- HTTP_PORT = '80'.freeze
50
+ HTTP_PORT = '80'.freeze
51
51
 
52
52
  # @since 0.1.0
53
53
  # @api private
54
- HTTPS_SCHEME = 'https'.freeze
54
+ HTTPS_SCHEME = 'https'.freeze
55
55
 
56
56
  # @since 0.1.0
57
57
  # @api private
58
- HTTPS_PORT = '443'.freeze
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
- "#{ @base_url }#{ compile_path(source) }"
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.resolve(result) if digest
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
- ( (scheme == HTTP_SCHEME && port == HTTP_PORT ) ||
535
- (scheme == HTTPS_SCHEME && port == HTTPS_PORT ) ) ? nil : port.to_i
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
- html.script(src: _typed_asset_path(source, JAVASCRIPT_EXT), type: JAVASCRIPT_MIME_TYPE).to_s
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
- html.link(href: _typed_asset_path(source, STYLESHEET_EXT), type: STYLESHEET_MIME_TYPE, rel: STYLESHEET_REL).to_s
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 = "#{ source }#{ ext }" unless source.match(/#{ Regexp.escape(ext) }\z/)
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
- URI.regexp.match(source)
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, &blk)
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
- clear_public_directory
34
+ clear_assets_directory
34
35
  precompile
35
36
  end
36
37
 
37
38
  private
38
39
 
39
- # @since 0.1.0
40
+ # @since 0.3.0
40
41
  # @api private
41
- def clear_public_directory
42
- public_directory = @configuration.public_directory
43
- public_directory.rmtree if public_directory.exist?
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] : @duplicates
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
@@ -3,6 +3,6 @@ module Hanami
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '0.2.1'.freeze
6
+ VERSION = '0.3.0'.freeze
7
7
  end
8
8
  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 mod [Module] the Ruby namespace of the application
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
- # @since 0.1.0
107
+ # @since 0.1.0
108
108
  #
109
109
  # @see Hanami::Assets#dupe
110
110
  # @see Hanami::Assets::Configuration
111
- def self.duplicate(mod, &blk)
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 ||= Array.new
146
+ @@duplicates ||= [] # rubocop:disable Style/ClassVars
147
147
  end
148
148
  end
149
149
 
150
- private
150
+ class << self
151
+ private
151
152
 
152
- # @since 0.1.0
153
- # @api private
154
- def self.synchronize(&blk)
155
- Mutex.new.synchronize(&blk)
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.2.1
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-02-05 00:00:00.000000000 Z
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.7'
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.7'
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.3'
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.3'
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: '10'
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: '10'
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.0.0
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.5.1
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: