hanami-assets 1.1.1 → 1.2.0.beta1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d71de2dd9edf00f8046d10907f60b555b0391d3079bc81b58bd88ec68d7eaf4
4
- data.tar.gz: 823426740462f9e3d5a5357de6d6fe15d761dec2130e4e0363fe51f8b4ffebd7
3
+ metadata.gz: dee96814cdbdc9c4b0bf4519052da244585da7ce22f1ee9d002ef2bfde88344d
4
+ data.tar.gz: 7a0607dac9d21de73b5052c5b02efb7e1cf316fb278cc19fee6a197780dfb890
5
5
  SHA512:
6
- metadata.gz: c6de7ae4f3605ea0ea0a823f14699ba8b6c4358652fb377e70af78fb5147ae33554352de2d40ba168c8039836f359c8769f44107d107cd1aa4cda7e04c6fb06b
7
- data.tar.gz: fd1f5db6af660bfd1893495ce5059f915d8855c8b404fc648515dbadbcfc55d800f83867f9417f32a88399a86887d2a6b1da1cac07bd5b7e0af3233c697437c7
6
+ metadata.gz: 8a984c83ee52c5d75c9175376090b1f00bad151ca63d354d2dd626ae955defeebd685999726be12f5b884bbd6fcb5b3c49304fa8ed2425f9de1cb74708b39270
7
+ data.tar.gz: 13217797668cc3bba5369fbabd88f69796a16752193f0fbf57f270f990e1e332ec71616dc0c751a25691e1517845dc621ca5a3038032b4b2fac38e929fcf2a4f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Hanami::Assets
2
2
  Assets management for Ruby web applications
3
3
 
4
+ ## v1.2.0.beta1 - 2018-02-28
5
+ ### Added
6
+ - [Luca Guidi] Collect assets informations for Early Hints (103)
7
+ - [Luca Guidi] Send automatically javascripts and stylesheets via Push Promise / Early Hints
8
+ - [Luca Guidi] Add the ability to send audio, video, and generic assets for Push Promise / Early Hints
9
+
4
10
  ## v1.1.1 - 2018-02-27
5
11
  ### Added
6
12
  - [Luca Guidi] Official support for Ruby: MRI 2.5
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ['lib']
21
21
  spec.required_ruby_version = '>= 2.3.0'
22
22
 
23
- spec.add_runtime_dependency 'hanami-utils', '~> 1.1'
24
- spec.add_runtime_dependency 'hanami-helpers', '~> 1.1'
23
+ spec.add_runtime_dependency 'hanami-utils', '1.2.0.beta1'
24
+ spec.add_runtime_dependency 'hanami-helpers', '1.2.0.beta1'
25
25
  spec.add_runtime_dependency 'tilt', '~> 2.0', '>= 2.0.2'
26
26
 
27
27
  spec.add_development_dependency 'bundler'
@@ -412,6 +412,18 @@ module Hanami
412
412
  "#{@base_url}#{compile_path(source)}"
413
413
  end
414
414
 
415
+ # Check if the given source is linked via Cross-Origin policy.
416
+ # In other words, the given source, doesn't satisfy the Same-Origin policy.
417
+ #
418
+ # @see https://en.wikipedia.org/wiki/Same-origin_policy#Origin_determination_rules
419
+ # @see https://en.wikipedia.org/wiki/Same-origin_policy#document.domain_property
420
+ #
421
+ # @since 1.2.0
422
+ # @api private
423
+ def crossorigin?(source)
424
+ !source.start_with?(@base_url)
425
+ end
426
+
415
427
  # An array of crypographically secure hashing algorithms to use for
416
428
  # generating asset subresource integrity checks
417
429
  #
@@ -1,5 +1,4 @@
1
1
  require 'uri'
2
- require 'set'
3
2
  require 'hanami/helpers/html_helper'
4
3
  require 'hanami/utils/escape'
5
4
 
@@ -97,7 +96,11 @@ module Hanami
97
96
  # name of the algorithm, then a hyphen, then the hash value of the file.
98
97
  # If more than one algorithm is used, they'll be separated by a space.
99
98
  #
99
+ # It makes the script(s) eligible for HTTP/2 Push Promise/Early Hints.
100
+ # You can opt-out with inline option: `push: false`.
101
+ #
100
102
  # @param sources [Array<String>] one or more assets by name or absolute URL
103
+ # @param push [TrueClass, FalseClass] HTTP/2 Push Promise/Early Hints flag
101
104
  #
102
105
  # @return [Hanami::Utils::Escape::SafeString] the markup
103
106
  #
@@ -165,12 +168,17 @@ module Hanami
165
168
  # <%= javascript 'application' %>
166
169
  #
167
170
  # # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
168
- def javascript(*sources, **options) # rubocop:disable Metrics/MethodLength
171
+ #
172
+ # @example Disable Push Promise/Early Hints
173
+ #
174
+ # <%= javascript 'application', push: false %>
175
+ # <%= javascript 'http://cdn.example.test/jquery.js', 'dashboard', push: false %>
176
+ def javascript(*sources, push: true, **options) # rubocop:disable Metrics/MethodLength
169
177
  options = options.reject { |k, _| k.to_sym == :src }
170
178
 
171
179
  _safe_tags(*sources) do |source|
172
180
  attributes = {
173
- src: _typed_asset_path(source, JAVASCRIPT_EXT),
181
+ src: _typed_asset_path(source, JAVASCRIPT_EXT, push: push, as: :script),
174
182
  type: JAVASCRIPT_MIME_TYPE
175
183
  }
176
184
  attributes.merge!(options)
@@ -199,7 +207,12 @@ module Hanami
199
207
  # If the "subresource integrity mode" is on, <tt>integriy</tt> is the
200
208
  # name of the algorithm, then a hyphen, then the hashed value of the file.
201
209
  # If more than one algorithm is used, they'll be separated by a space.
210
+ #
211
+ # It makes the script(s) eligible for HTTP/2 Push Promise/Early Hints.
212
+ # You can opt-out with inline option: `push: false`.
213
+ #
202
214
  # @param sources [Array<String>] one or more assets by name or absolute URL
215
+ # @param push [TrueClass, FalseClass] HTTP/2 Push Promise/Early Hints flag
203
216
  #
204
217
  # @return [Hanami::Utils::Escape::SafeString] the markup
205
218
  #
@@ -256,12 +269,17 @@ module Hanami
256
269
  # <%= stylesheet 'application' %>
257
270
  #
258
271
  # # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
259
- def stylesheet(*sources, **options) # rubocop:disable Metrics/MethodLength
272
+ #
273
+ # @example Disable Push Promise/Early Hints
274
+ #
275
+ # <%= stylesheet 'application', push: false %>
276
+ # <%= stylesheet 'http://cdn.example.test/bootstrap.css', 'dashboard', push: false %>
277
+ def stylesheet(*sources, push: true, **options) # rubocop:disable Metrics/MethodLength
260
278
  options = options.reject { |k, _| k.to_sym == :href }
261
279
 
262
280
  _safe_tags(*sources) do |source|
263
281
  attributes = {
264
- href: _typed_asset_path(source, STYLESHEET_EXT),
282
+ href: _typed_asset_path(source, STYLESHEET_EXT, push: push, as: :style),
265
283
  type: STYLESHEET_MIME_TYPE,
266
284
  rel: STYLESHEET_REL
267
285
  }
@@ -292,6 +310,8 @@ module Hanami
292
310
  # application CDN.
293
311
  #
294
312
  # @param source [String] asset name or absolute URL
313
+ # @param options [Hash] HTML 5 attributes
314
+ # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
295
315
  #
296
316
  # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
297
317
  #
@@ -341,11 +361,14 @@ module Hanami
341
361
  # <%= image 'logo.png' %>
342
362
  #
343
363
  # # <img src="https://assets.bookshelf.org/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
364
+ #
365
+ # @example Enable Push Promise/Early Hints
366
+ #
367
+ # <%= image 'logo.png', push: true %>
344
368
  def image(source, options = {})
345
369
  options = options.reject { |k, _| k.to_sym == :src }
346
-
347
370
  attributes = {
348
- src: asset_path(source),
371
+ src: asset_path(source, push: options.delete(:push) || false, as: :image),
349
372
  alt: Utils::String.titleize(::File.basename(source, WILDCARD_EXT))
350
373
  }
351
374
  attributes.merge!(options)
@@ -366,6 +389,8 @@ module Hanami
366
389
  # application CDN.
367
390
  #
368
391
  # @param source [String] asset name
392
+ # @param options [Hash] HTML 5 attributes
393
+ # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
369
394
  #
370
395
  # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
371
396
  #
@@ -393,7 +418,7 @@ module Hanami
393
418
  #
394
419
  # @example Custom HTML Attributes
395
420
  #
396
- # <%= favicon id: 'fav' %>
421
+ # <%= favicon "favicon.ico", id: "fav" %>
397
422
  #
398
423
  # # <link id: "fav" href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
399
424
  #
@@ -408,11 +433,15 @@ module Hanami
408
433
  # <%= favicon %>
409
434
  #
410
435
  # # <link href="https://assets.bookshelf.org/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico" rel="shortcut icon" type="image/x-icon">
436
+ #
437
+ # @example Enable Push Promise/Early Hints
438
+ #
439
+ # <%= favicon 'favicon.ico', push: true %>
411
440
  def favicon(source = DEFAULT_FAVICON, options = {})
412
441
  options = options.reject { |k, _| k.to_sym == :href }
413
442
 
414
443
  attributes = {
415
- href: asset_path(source),
444
+ href: asset_path(source, push: options.delete(:push) || false, as: :image),
416
445
  rel: FAVICON_REL,
417
446
  type: FAVICON_MIME_TYPE
418
447
  }
@@ -437,6 +466,8 @@ module Hanami
437
466
  # application CDN.
438
467
  #
439
468
  # @param source [String] asset name or absolute URL
469
+ # @param options [Hash] HTML 5 attributes
470
+ # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
440
471
  #
441
472
  # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
442
473
  #
@@ -535,8 +566,20 @@ module Hanami
535
566
  # <%= video 'movie.mp4' %>
536
567
  #
537
568
  # # <video src="https://assets.bookshelf.org/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
569
+ #
570
+ # @example Enable Push Promise/Early Hints
571
+ #
572
+ # <%= video 'movie.mp4', push: true %>
573
+ #
574
+ # <%=
575
+ # video do
576
+ # text "Your browser does not support the video tag"
577
+ # source src: asset_path("movie.mp4", push: :video), type: "video/mp4"
578
+ # source src: asset_path("movie.ogg"), type: "video/ogg"
579
+ # end
580
+ # %>
538
581
  def video(source = nil, options = {}, &blk)
539
- options = _source_options(source, options, &blk)
582
+ options = _source_options(source, options, as: :video, &blk)
540
583
  html.video(blk, options)
541
584
  end
542
585
 
@@ -556,6 +599,8 @@ module Hanami
556
599
  # application CDN.
557
600
  #
558
601
  # @param source [String] asset name or absolute URL
602
+ # @param options [Hash] HTML 5 attributes
603
+ # @option options [TrueClass, FalseClass] :push HTTP/2 Push Promise/Early Hints flag
559
604
  #
560
605
  # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
561
606
  #
@@ -654,8 +699,20 @@ module Hanami
654
699
  # <%= audio 'song.ogg' %>
655
700
  #
656
701
  # # <audio src="https://assets.bookshelf.org/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
702
+ #
703
+ # @example Enable Push Promise/Early Hints
704
+ #
705
+ # <%= audio 'movie.mp4', push: true %>
706
+ #
707
+ # <%=
708
+ # audio do
709
+ # text "Your browser does not support the audio tag"
710
+ # source src: asset_path("song.ogg", push: :audio), type: "audio/ogg"
711
+ # source src: asset_path("song.wav"), type: "audio/wav"
712
+ # end
713
+ # %>
657
714
  def audio(source = nil, options = {}, &blk)
658
- options = _source_options(source, options, &blk)
715
+ options = _source_options(source, options, as: :audio, &blk)
659
716
  html.audio(blk, options)
660
717
  end
661
718
 
@@ -671,6 +728,8 @@ module Hanami
671
728
  # If CDN mode is on, it returns the absolute URL of the asset.
672
729
  #
673
730
  # @param source [String] the asset name
731
+ # @param push [TrueClass, FalseClass, Symbol] HTTP/2 Push Promise/Early Hints flag, or type
732
+ # @param as [Symbol] HTTP/2 Push Promise / Early Hints flag type
674
733
  #
675
734
  # @return [String] the asset path
676
735
  #
@@ -703,8 +762,12 @@ module Hanami
703
762
  # <%= asset_path 'application.js' %>
704
763
  #
705
764
  # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
706
- def asset_path(source)
707
- _asset_url(source) { _relative_url(source) }
765
+ #
766
+ # @example Enable Push Promise/Early Hints
767
+ #
768
+ # <%= asset_path "application.js", push: :script %>
769
+ def asset_path(source, push: false, as: nil)
770
+ _asset_url(source, push: push, as: as) { _relative_url(source) }
708
771
  end
709
772
 
710
773
  # It generates the absolute URL for the given source.
@@ -719,6 +782,8 @@ module Hanami
719
782
  # If CDN mode is on, it returns the absolute URL of the asset.
720
783
  #
721
784
  # @param source [String] the asset name
785
+ # @param push [TrueClass, FalseClass, Symbol] HTTP/2 Push Promise/Early Hints flag, or type
786
+ # @param as [Symbol] HTTP/2 Push Promise / Early Hints flag type
722
787
  #
723
788
  # @return [String] the asset URL
724
789
  #
@@ -751,8 +816,12 @@ module Hanami
751
816
  # <%= asset_url 'application.js' %>
752
817
  #
753
818
  # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
754
- def asset_url(source)
755
- _asset_url(source) { _absolute_url(source) }
819
+ #
820
+ # @example Enable Push Promise/Early Hints
821
+ #
822
+ # <%= asset_url "application.js", push: :script %>
823
+ def asset_url(source, push: false, as: nil)
824
+ _asset_url(source, push: push, as: as) { _absolute_url(source) }
756
825
  end
757
826
 
758
827
  private
@@ -769,18 +838,24 @@ module Hanami
769
838
 
770
839
  # @since 0.1.0
771
840
  # @api private
772
- def _asset_url(source)
773
- _push_promise(
774
- _absolute_url?(source) ? # rubocop:disable Style/MultilineTernaryOperator
775
- source : yield
776
- )
841
+ def _asset_url(source, push:, as:)
842
+ url = _absolute_url?(source) ? source : yield
843
+
844
+ case push
845
+ when Symbol
846
+ _push_promise(url, as: push)
847
+ when TrueClass
848
+ _push_promise(url, as: as)
849
+ end
850
+
851
+ url
777
852
  end
778
853
 
779
854
  # @since 0.1.0
780
855
  # @api private
781
- def _typed_asset_path(source, ext)
856
+ def _typed_asset_path(source, ext, push: false, as: nil)
782
857
  source = "#{source}#{ext}" if _append_extension?(source, ext)
783
- asset_path(source)
858
+ asset_path(source, push: push, as: as)
784
859
  end
785
860
 
786
861
  # @api private
@@ -800,6 +875,13 @@ module Hanami
800
875
  ABSOLUTE_URL_MATCHER.match(source)
801
876
  end
802
877
 
878
+ # @since 1.2.0
879
+ # @api private
880
+ def _crossorigin?(source)
881
+ return false unless _absolute_url?(source)
882
+ self.class.assets_configuration.crossorigin?(source)
883
+ end
884
+
803
885
  # @since 0.1.0
804
886
  # @api private
805
887
  def _relative_url(source)
@@ -814,13 +896,13 @@ module Hanami
814
896
 
815
897
  # @since 0.1.0
816
898
  # @api private
817
- def _source_options(src, options, &_blk)
899
+ def _source_options(src, options, as:, &_blk)
818
900
  options ||= {}
819
901
 
820
902
  if src.respond_to?(:to_hash)
821
903
  options = src.to_hash
822
904
  elsif src
823
- options[:src] = asset_path(src)
905
+ options[:src] = asset_path(src, push: options.delete(:push) || false, as: as)
824
906
  end
825
907
 
826
908
  if !options[:src] && !block_given?
@@ -832,11 +914,9 @@ module Hanami
832
914
 
833
915
  # @since 0.1.0
834
916
  # @api private
835
- def _push_promise(url)
836
- Mutex.new.synchronize do
837
- Thread.current[:__hanami_assets] ||= Set.new
838
- Thread.current[:__hanami_assets].add(url.to_s)
839
- end
917
+ def _push_promise(url, as: nil)
918
+ Thread.current[:__hanami_assets] ||= {}
919
+ Thread.current[:__hanami_assets][url.to_s] = { as: as, crossorigin: _crossorigin?(url) }
840
920
 
841
921
  url
842
922
  end
@@ -3,6 +3,6 @@ module Hanami
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '1.1.1'.freeze
6
+ VERSION = '1.2.0.beta1'.freeze
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-assets
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-27 00:00:00.000000000 Z
11
+ date: 2018-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hanami-utils
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: 1.2.0.beta1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: 1.2.0.beta1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: hanami-helpers
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: 1.2.0.beta1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: '1.1'
40
+ version: 1.2.0.beta1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: tilt
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -240,9 +240,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
240
240
  version: 2.3.0
241
241
  required_rubygems_version: !ruby/object:Gem::Requirement
242
242
  requirements:
243
- - - ">="
243
+ - - ">"
244
244
  - !ruby/object:Gem::Version
245
- version: '0'
245
+ version: 1.3.1
246
246
  requirements: []
247
247
  rubyforge_project:
248
248
  rubygems_version: 2.7.5