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,733 @@
1
+ require 'uri'
2
+ require 'set'
3
+ require 'thread'
4
+ require 'hanami/helpers/html_helper'
5
+ require 'hanami/utils/escape'
6
+
7
+ module Hanami
8
+ module Assets
9
+ # HTML assets helpers
10
+ #
11
+ # Include this helper in a view
12
+ #
13
+ # @since 0.1.0
14
+ #
15
+ # @see http://www.rubydoc.info/gems/hanami-helpers/Hanami/Helpers/HtmlHelper
16
+ module Helpers
17
+ # @since 0.1.0
18
+ # @api private
19
+ NEW_LINE_SEPARATOR = "\n".freeze
20
+
21
+ # @since 0.1.0
22
+ # @api private
23
+ WILDCARD_EXT = '.*'.freeze
24
+
25
+ # @since 0.1.0
26
+ # @api private
27
+ JAVASCRIPT_EXT = '.js'.freeze
28
+
29
+ # @since 0.1.0
30
+ # @api private
31
+ STYLESHEET_EXT = '.css'.freeze
32
+
33
+ # @since 0.1.0
34
+ # @api private
35
+ JAVASCRIPT_MIME_TYPE = 'text/javascript'.freeze
36
+
37
+ # @since 0.1.0
38
+ # @api private
39
+ STYLESHEET_MIME_TYPE = 'text/css'.freeze
40
+
41
+ # @since 0.1.0
42
+ # @api private
43
+ FAVICON_MIME_TYPE = 'image/x-icon'.freeze
44
+
45
+ # @since 0.1.0
46
+ # @api private
47
+ STYLESHEET_REL = 'stylesheet'.freeze
48
+
49
+ # @since 0.1.0
50
+ # @api private
51
+ FAVICON_REL = 'shortcut icon'.freeze
52
+
53
+ # @since 0.1.0
54
+ # @api private
55
+ DEFAULT_FAVICON = 'favicon.ico'.freeze
56
+
57
+ include Hanami::Helpers::HtmlHelper
58
+
59
+ # Inject helpers into the given class
60
+ #
61
+ # @since 0.1.0
62
+ # @api private
63
+ def self.included(base)
64
+ conf = ::Hanami::Assets::Configuration.for(base)
65
+ base.class_eval do
66
+ include Utils::ClassAttribute
67
+
68
+ class_attribute :assets_configuration
69
+ self.assets_configuration = conf
70
+ end
71
+ end
72
+
73
+ # Generate <tt>script</tt> tag for given source(s)
74
+ #
75
+ # It accepts one or more strings representing the name of the asset, if it
76
+ # comes from the application or third party gems. It also accepts strings
77
+ # representing absolute URLs in case of public CDN (eg. jQuery CDN).
78
+ #
79
+ # If the "digest mode" is on, <tt>src</tt> is the digest version of the
80
+ # relative URL.
81
+ #
82
+ # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
83
+ # application CDN.
84
+ #
85
+ # @param sources [Array<String>] one or more assets by name or absolute URL
86
+ #
87
+ # @return [Hanami::Utils::Escape::SafeString] the markup
88
+ #
89
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
90
+ # at least one of the given sources is missing from the manifest
91
+ #
92
+ # @since 0.1.0
93
+ #
94
+ # @see Hanami::Assets::Configuration#digest
95
+ # @see Hanami::Assets::Configuration#cdn
96
+ # @see Hanami::Assets::Helpers#asset_path
97
+ #
98
+ # @example Single Asset
99
+ #
100
+ # <%= javascript 'application' %>
101
+ #
102
+ # # <script src="/assets/application.js" type="text/javascript"></script>
103
+ #
104
+ # @example Multiple Assets
105
+ #
106
+ # <%= javascript 'application', 'dashboard' %>
107
+ #
108
+ # # <script src="/assets/application.js" type="text/javascript"></script>
109
+ # # <script src="/assets/dashboard.js" type="text/javascript"></script>
110
+ #
111
+ # @example Absolute URL
112
+ #
113
+ # <%= javascript 'https://code.jquery.com/jquery-2.1.4.min.js' %>
114
+ #
115
+ # # <script src="https://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script>
116
+ #
117
+ # @example Digest Mode
118
+ #
119
+ # <%= javascript 'application' %>
120
+ #
121
+ # # <script src="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
122
+ #
123
+ # @example CDN Mode
124
+ #
125
+ # <%= javascript 'application' %>
126
+ #
127
+ # # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
128
+ def javascript(*sources)
129
+ _safe_tags(*sources) do |source|
130
+ html.script(src: _typed_asset_path(source, JAVASCRIPT_EXT), type: JAVASCRIPT_MIME_TYPE).to_s
131
+ end
132
+ end
133
+
134
+ # Generate <tt>link</tt> tag for given source(s)
135
+ #
136
+ # It accepts one or more strings representing the name of the asset, if it
137
+ # comes from the application or third party gems. It also accepts strings
138
+ # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
139
+ #
140
+ # If the "digest mode" is on, <tt>href</tt> is the digest version of the
141
+ # relative URL.
142
+ #
143
+ # If the "CDN mode" is on, the <tt>href</tt> is an absolute URL of the
144
+ # application CDN.
145
+ #
146
+ # @param sources [Array<String>] one or more assets by name or absolute URL
147
+ #
148
+ # @return [Hanami::Utils::Escape::SafeString] the markup
149
+ #
150
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
151
+ # at least one of the given sources is missing from the manifest
152
+ #
153
+ # @since 0.1.0
154
+ #
155
+ # @see Hanami::Assets::Configuration#digest
156
+ # @see Hanami::Assets::Configuration#cdn
157
+ # @see Hanami::Assets::Helpers#asset_path
158
+ #
159
+ # @example Single Asset
160
+ #
161
+ # <%= stylesheet 'application' %>
162
+ #
163
+ # # <link href="/assets/application.css" type="text/css" rel="stylesheet">
164
+ #
165
+ # @example Multiple Assets
166
+ #
167
+ # <%= stylesheet 'application', 'dashboard' %>
168
+ #
169
+ # # <link href="/assets/application.css" type="text/css" rel="stylesheet">
170
+ # # <link href="/assets/dashboard.css" type="text/css" rel="stylesheet">
171
+ #
172
+ # @example Absolute URL
173
+ #
174
+ # <%= stylesheet 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' %>
175
+ #
176
+ # # <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" rel="stylesheet">
177
+ #
178
+ # @example Digest Mode
179
+ #
180
+ # <%= stylesheet 'application' %>
181
+ #
182
+ # # <link href="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
183
+ #
184
+ # @example CDN Mode
185
+ #
186
+ # <%= stylesheet 'application' %>
187
+ #
188
+ # # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
189
+ def stylesheet(*sources)
190
+ _safe_tags(*sources) do |source|
191
+ html.link(href: _typed_asset_path(source, STYLESHEET_EXT), type: STYLESHEET_MIME_TYPE, rel: STYLESHEET_REL).to_s
192
+ end
193
+ end
194
+
195
+ # Generate <tt>img</tt> tag for given source
196
+ #
197
+ # It accepts one string representing the name of the asset, if it comes
198
+ # from the application or third party gems. It also accepts string
199
+ # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
200
+ #
201
+ # <tt>alt</tt> Attribute is auto generated from <tt>src</tt>.
202
+ # You can specify a different value, by passing the <tt>:src</tt> option.
203
+ #
204
+ # If the "digest mode" is on, <tt>src</tt> is the digest version of the
205
+ # relative URL.
206
+ #
207
+ # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
208
+ # application CDN.
209
+ #
210
+ # @param source [String] asset name or absolute URL
211
+ #
212
+ # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
213
+ #
214
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
215
+ # the image is missing from the manifest
216
+ #
217
+ # @since 0.1.0
218
+ #
219
+ # @see Hanami::Assets::Configuration#digest
220
+ # @see Hanami::Assets::Configuration#cdn
221
+ # @see Hanami::Assets::Helpers#asset_path
222
+ #
223
+ # @example Basic Usage
224
+ #
225
+ # <%= image 'logo.png' %>
226
+ #
227
+ # # <img src="/assets/logo.png" alt="Logo">
228
+ #
229
+ # @example Custom alt Attribute
230
+ #
231
+ # <%= image 'logo.png', alt: 'Application Logo' %>
232
+ #
233
+ # # <img src="/assets/logo.png" alt="Application Logo">
234
+ #
235
+ # @example Custom HTML Attributes
236
+ #
237
+ # <%= image 'logo.png', id: 'logo', class: 'image' %>
238
+ #
239
+ # # <img src="/assets/logo.png" alt="Logo" id="logo" class="image">
240
+ #
241
+ # @example Absolute URL
242
+ #
243
+ # <%= image 'https://example-cdn.com/images/logo.png' %>
244
+ #
245
+ # # <img src="https://example-cdn.com/images/logo.png" alt="Logo">
246
+ #
247
+ # @example Digest Mode
248
+ #
249
+ # <%= image 'logo.png' %>
250
+ #
251
+ # # <img src="/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
252
+ #
253
+ # @example CDN Mode
254
+ #
255
+ # <%= image 'logo.png' %>
256
+ #
257
+ # # <img src="https://assets.bookshelf.org/assets/logo-28a6b886de2372ee3922fcaf3f78f2d8.png" alt="Logo">
258
+ def image(source, options = {})
259
+ options[:src] = asset_path(source)
260
+ options[:alt] ||= Utils::String.new(::File.basename(source, WILDCARD_EXT)).titleize
261
+
262
+ html.img(options)
263
+ end
264
+
265
+ # Generate <tt>link</tt> tag application favicon.
266
+ #
267
+ # If no argument is given, it assumes <tt>favico.ico</tt> from the application.
268
+ #
269
+ # It accepts one string representing the name of the asset.
270
+ #
271
+ # If the "digest mode" is on, <tt>href</tt> is the digest version of the
272
+ # relative URL.
273
+ #
274
+ # If the "CDN mode" is on, the <tt>href</tt> is an absolute URL of the
275
+ # application CDN.
276
+ #
277
+ # @param source [String] asset name
278
+ #
279
+ # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
280
+ #
281
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
282
+ # the favicon is missing from the manifest
283
+ #
284
+ # @since 0.1.0
285
+ #
286
+ # @see Hanami::Assets::Configuration#digest
287
+ # @see Hanami::Assets::Configuration#cdn
288
+ # @see Hanami::Assets::Helpers#asset_path
289
+ #
290
+ # @example Basic Usage
291
+ #
292
+ # <%= favicon %>
293
+ #
294
+ # # <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
295
+ #
296
+ # @example Custom Path
297
+ #
298
+ # <%= favicon 'fav.ico' %>
299
+ #
300
+ # # <link href="/assets/fav.ico" rel="shortcut icon" type="image/x-icon">
301
+ #
302
+ # @example Custom HTML Attributes
303
+ #
304
+ # <%= favicon id: 'fav' %>
305
+ #
306
+ # # <link id: "fav" href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">
307
+ #
308
+ # @example Digest Mode
309
+ #
310
+ # <%= favicon %>
311
+ #
312
+ # # <link href="/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico" rel="shortcut icon" type="image/x-icon">
313
+ #
314
+ # @example CDN Mode
315
+ #
316
+ # <%= favicon %>
317
+ #
318
+ # # <link href="https://assets.bookshelf.org/assets/favicon-28a6b886de2372ee3922fcaf3f78f2d8.ico" rel="shortcut icon" type="image/x-icon">
319
+ def favicon(source = DEFAULT_FAVICON, options = {})
320
+ options[:href] = asset_path(source)
321
+ options[:rel] ||= FAVICON_REL
322
+ options[:type] ||= FAVICON_MIME_TYPE
323
+
324
+ html.link(options)
325
+ end
326
+
327
+ # Generate <tt>video</tt> tag for given source
328
+ #
329
+ # It accepts one string representing the name of the asset, if it comes
330
+ # from the application or third party gems. It also accepts string
331
+ # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
332
+ #
333
+ # Alternatively, it accepts a block that allows to specify one or more
334
+ # sources via the <tt>source</tt> tag.
335
+ #
336
+ # If the "digest mode" is on, <tt>src</tt> is the digest version of the
337
+ # relative URL.
338
+ #
339
+ # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
340
+ # application CDN.
341
+ #
342
+ # @param source [String] asset name or absolute URL
343
+ #
344
+ # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
345
+ #
346
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
347
+ # the image is missing from the manifest
348
+ #
349
+ # @raise [ArgumentError] if source isn't specified both as argument or
350
+ # tag inside the given block
351
+ #
352
+ # @since 0.1.0
353
+ #
354
+ # @see Hanami::Assets::Configuration#digest
355
+ # @see Hanami::Assets::Configuration#cdn
356
+ # @see Hanami::Assets::Helpers#asset_path
357
+ #
358
+ # @example Basic Usage
359
+ #
360
+ # <%= video 'movie.mp4' %>
361
+ #
362
+ # # <video src="/assets/movie.mp4"></video>
363
+ #
364
+ # @example Absolute URL
365
+ #
366
+ # <%= video 'https://example-cdn.com/assets/movie.mp4' %>
367
+ #
368
+ # # <video src="https://example-cdn.com/assets/movie.mp4"></video>
369
+ #
370
+ # @example Custom HTML Attributes
371
+ #
372
+ # <%= video('movie.mp4', autoplay: true, controls: true) %>
373
+ #
374
+ # # <video src="/assets/movie.mp4" autoplay="autoplay" controls="controls"></video>
375
+ #
376
+ # @example Fallback Content
377
+ #
378
+ # <%=
379
+ # video('movie.mp4') do
380
+ # "Your browser does not support the video tag"
381
+ # end
382
+ # %>
383
+ #
384
+ # # <video src="/assets/movie.mp4">
385
+ # # Your browser does not support the video tag
386
+ # # </video>
387
+ #
388
+ # @example Tracks
389
+ #
390
+ # <%=
391
+ # video('movie.mp4') do
392
+ # track(kind: 'captions', src: asset_path('movie.en.vtt'),
393
+ # srclang: 'en', label: 'English')
394
+ # end
395
+ # %>
396
+ #
397
+ # # <video src="/assets/movie.mp4">
398
+ # # <track kind="captions" src="/assets/movie.en.vtt" srclang="en" label="English">
399
+ # # </video>
400
+ #
401
+ # @example Sources
402
+ #
403
+ # <%=
404
+ # video do
405
+ # text "Your browser does not support the video tag"
406
+ # source(src: asset_path('movie.mp4'), type: 'video/mp4')
407
+ # source(src: asset_path('movie.ogg'), type: 'video/ogg')
408
+ # end
409
+ # %>
410
+ #
411
+ # # <video>
412
+ # # Your browser does not support the video tag
413
+ # # <source src="/assets/movie.mp4" type="video/mp4">
414
+ # # <source src="/assets/movie.ogg" type="video/ogg">
415
+ # # </video>
416
+ #
417
+ # @example Without Any Argument
418
+ #
419
+ # <%= video %>
420
+ #
421
+ # # ArgumentError
422
+ #
423
+ # @example Without src And Without Block
424
+ #
425
+ # <%= video(content: true) %>
426
+ #
427
+ # # ArgumentError
428
+ #
429
+ # @example Digest Mode
430
+ #
431
+ # <%= video 'movie.mp4' %>
432
+ #
433
+ # # <video src="/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
434
+ #
435
+ # @example CDN Mode
436
+ #
437
+ # <%= video 'movie.mp4' %>
438
+ #
439
+ # # <video src="https://assets.bookshelf.org/assets/movie-28a6b886de2372ee3922fcaf3f78f2d8.mp4"></video>
440
+ def video(source = nil, options = {}, &blk)
441
+ options = _source_options(source, options, &blk)
442
+ html.video(blk, options)
443
+ end
444
+
445
+ # Generate <tt>audio</tt> tag for given source
446
+ #
447
+ # It accepts one string representing the name of the asset, if it comes
448
+ # from the application or third party gems. It also accepts string
449
+ # representing absolute URLs in case of public CDN (eg. Bootstrap CDN).
450
+ #
451
+ # Alternatively, it accepts a block that allows to specify one or more
452
+ # sources via the <tt>source</tt> tag.
453
+ #
454
+ # If the "digest mode" is on, <tt>src</tt> is the digest version of the
455
+ # relative URL.
456
+ #
457
+ # If the "CDN mode" is on, the <tt>src</tt> is an absolute URL of the
458
+ # application CDN.
459
+ #
460
+ # @param source [String] asset name or absolute URL
461
+ #
462
+ # @return [Hanami::Utils::Helpers::HtmlBuilder] the builder
463
+ #
464
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
465
+ # the image is missing from the manifest
466
+ #
467
+ # @raise [ArgumentError] if source isn't specified both as argument or
468
+ # tag inside the given block
469
+ #
470
+ # @since 0.1.0
471
+ #
472
+ # @see Hanami::Assets::Configuration#digest
473
+ # @see Hanami::Assets::Configuration#cdn
474
+ # @see Hanami::Assets::Helpers#asset_path
475
+ #
476
+ # @example Basic Usage
477
+ #
478
+ # <%= audio 'song.ogg' %>
479
+ #
480
+ # # <audio src="/assets/song.ogg"></audio>
481
+ #
482
+ # @example Absolute URL
483
+ #
484
+ # <%= audio 'https://example-cdn.com/assets/song.ogg' %>
485
+ #
486
+ # # <audio src="https://example-cdn.com/assets/song.ogg"></audio>
487
+ #
488
+ # @example Custom HTML Attributes
489
+ #
490
+ # <%= audio('song.ogg', autoplay: true, controls: true) %>
491
+ #
492
+ # # <audio src="/assets/song.ogg" autoplay="autoplay" controls="controls"></audio>
493
+ #
494
+ # @example Fallback Content
495
+ #
496
+ # <%=
497
+ # audio('song.ogg') do
498
+ # "Your browser does not support the audio tag"
499
+ # end
500
+ # %>
501
+ #
502
+ # # <audio src="/assets/song.ogg">
503
+ # # Your browser does not support the audio tag
504
+ # # </audio>
505
+ #
506
+ # @example Tracks
507
+ #
508
+ # <%=
509
+ # audio('song.ogg') do
510
+ # track(kind: 'captions', src: asset_path('song.pt-BR.vtt'),
511
+ # srclang: 'pt-BR', label: 'Portuguese')
512
+ # end
513
+ # %>
514
+ #
515
+ # # <audio src="/assets/song.ogg">
516
+ # # <track kind="captions" src="/assets/song.pt-BR.vtt" srclang="pt-BR" label="Portuguese">
517
+ # # </audio>
518
+ #
519
+ # @example Sources
520
+ #
521
+ # <%=
522
+ # audio do
523
+ # text "Your browser does not support the audio tag"
524
+ # source(src: asset_path('song.ogg'), type: 'audio/ogg')
525
+ # source(src: asset_path('song.wav'), type: 'auido/wav')
526
+ # end
527
+ # %>
528
+ #
529
+ # # <audio>
530
+ # # Your browser does not support the audio tag
531
+ # # <source src="/assets/song.ogg" type="audio/ogg">
532
+ # # <source src="/assets/song.wav" type="auido/wav">
533
+ # # </audio>
534
+ #
535
+ # @example Without Any Argument
536
+ #
537
+ # <%= audio %>
538
+ #
539
+ # # ArgumentError
540
+ #
541
+ # @example Without src And Without Block
542
+ #
543
+ # <%= audio(controls: true) %>
544
+ #
545
+ # # ArgumentError
546
+ #
547
+ # @example Digest Mode
548
+ #
549
+ # <%= audio 'song.ogg' %>
550
+ #
551
+ # # <audio src="/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
552
+ #
553
+ # @example CDN Mode
554
+ #
555
+ # <%= audio 'song.ogg' %>
556
+ #
557
+ # # <audio src="https://assets.bookshelf.org/assets/song-28a6b886de2372ee3922fcaf3f78f2d8.ogg"></audio>
558
+ def audio(source = nil, options = {}, &blk)
559
+ options = _source_options(source, options, &blk)
560
+ html.audio(blk, options)
561
+ end
562
+
563
+ # It generates the relative URL for the given source.
564
+ #
565
+ # It can be the name of the asset, coming from the sources or third party
566
+ # gems.
567
+ #
568
+ # Absolute URLs are returned as they are.
569
+ #
570
+ # If Digest mode is on, it returns the digest path of the source
571
+ #
572
+ # If CDN mode is on, it returns the absolute URL of the asset.
573
+ #
574
+ # @param source [String] the asset name
575
+ #
576
+ # @return [String] the asset path
577
+ #
578
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
579
+ # the asset is missing from the manifest
580
+ #
581
+ # @since 0.1.0
582
+ #
583
+ # @example Basic Usage
584
+ #
585
+ # <%= asset_path 'application.js' %>
586
+ #
587
+ # # "/assets/application.js"
588
+ #
589
+ # @example Absolute URL
590
+ #
591
+ # <%= asset_path 'https://code.jquery.com/jquery-2.1.4.min.js' %>
592
+ #
593
+ # # "https://code.jquery.com/jquery-2.1.4.min.js"
594
+ #
595
+ # @example Digest Mode
596
+ #
597
+ # <%= asset_path 'application.js' %>
598
+ #
599
+ # # "/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
600
+ #
601
+ # @example CDN Mode
602
+ #
603
+ # <%= asset_path 'application.js' %>
604
+ #
605
+ # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
606
+ def asset_path(source)
607
+ _asset_url(source) { _relative_url(source) }
608
+ end
609
+
610
+ # It generates the absolute URL for the given source.
611
+ #
612
+ # It can be the name of the asset, coming from the sources or third party
613
+ # gems.
614
+ #
615
+ # Absolute URLs are returned as they are.
616
+ #
617
+ # If Digest mode is on, it returns the digest URL of the source
618
+ #
619
+ # If CDN mode is on, it returns the absolute URL of the asset.
620
+ #
621
+ # @param source [String] the asset name
622
+ #
623
+ # @return [String] the asset URL
624
+ #
625
+ # @raise [Hanami::Assets::MissingDigestAssetError] if digest mode is on and
626
+ # the asset is missing from the manifest
627
+ #
628
+ # @since 0.1.0
629
+ #
630
+ # @example Basic Usage
631
+ #
632
+ # <%= asset_url 'application.js' %>
633
+ #
634
+ # # "https://bookshelf.org/assets/application.js"
635
+ #
636
+ # @example Absolute URL
637
+ #
638
+ # <%= asset_url 'https://code.jquery.com/jquery-2.1.4.min.js' %>
639
+ #
640
+ # # "https://code.jquery.com/jquery-2.1.4.min.js"
641
+ #
642
+ # @example Digest Mode
643
+ #
644
+ # <%= asset_url 'application.js' %>
645
+ #
646
+ # # "https://bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
647
+ #
648
+ # @example CDN Mode
649
+ #
650
+ # <%= asset_url 'application.js' %>
651
+ #
652
+ # # "https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js"
653
+ def asset_url(source)
654
+ _asset_url(source) { _absolute_url(source) }
655
+ end
656
+
657
+ private
658
+
659
+ # @since 0.1.0
660
+ # @api private
661
+ def _safe_tags(*sources)
662
+ ::Hanami::Utils::Escape::SafeString.new(
663
+ sources.map do |source|
664
+ yield source
665
+ end.join(NEW_LINE_SEPARATOR)
666
+ )
667
+ end
668
+
669
+ # @since 0.1.0
670
+ # @api private
671
+ def _asset_url(source)
672
+ _push_promise(
673
+ _absolute_url?(source) ?
674
+ source : yield
675
+ )
676
+ end
677
+
678
+ # @since 0.1.0
679
+ # @api private
680
+ def _typed_asset_path(source, ext)
681
+ source = "#{ source }#{ ext }" unless source.match(/#{ Regexp.escape(ext) }\z/)
682
+ asset_path(source)
683
+ end
684
+
685
+ # @since 0.1.0
686
+ # @api private
687
+ def _absolute_url?(source)
688
+ URI.regexp.match(source)
689
+ end
690
+
691
+ # @since 0.1.0
692
+ # @api private
693
+ def _relative_url(source)
694
+ self.class.assets_configuration.asset_path(source)
695
+ end
696
+
697
+ # @since 0.1.0
698
+ # @api private
699
+ def _absolute_url(source)
700
+ self.class.assets_configuration.asset_url(source)
701
+ end
702
+
703
+ # @since 0.1.0
704
+ # @api private
705
+ def _source_options(src, options, &blk)
706
+ options ||= {}
707
+
708
+ if src.respond_to?(:to_hash)
709
+ options = src.to_hash
710
+ elsif src
711
+ options[:src] = asset_path(src)
712
+ end
713
+
714
+ if !options[:src] && !block_given?
715
+ raise ArgumentError.new('You should provide a source via `src` option or with a `source` HTML tag')
716
+ end
717
+
718
+ options
719
+ end
720
+
721
+ # @since 0.1.0
722
+ # @api private
723
+ def _push_promise(url)
724
+ Mutex.new.synchronize do
725
+ Thread.current[:__hanami_assets] ||= Set.new
726
+ Thread.current[:__hanami_assets].add(url.to_s)
727
+ end
728
+
729
+ url
730
+ end
731
+ end
732
+ end
733
+ end