hanami-assets 0.2.1 → 0.3.0

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.
@@ -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: