ballonizer 0.1.0 → 0.2.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/examples/ballonizer_app/config.ru +59 -0
  3. data/examples/ballonizer_app/index.html +159 -0
  4. data/examples/ballonizer_js_module/index.html +196 -0
  5. data/lib/assets/javascripts/ballonizer.js +482 -0
  6. data/lib/assets/stylesheets/ballonizer.css +78 -0
  7. data/lib/ballonizer.rb +201 -36
  8. data/spec/ballonizer_spec.rb +153 -2
  9. data/spec/javascripts/ballonizer_spec.js +568 -0
  10. data/spec/javascripts/fixtures/ballonized-xkcd-with-anchor-in-image.html +163 -0
  11. data/spec/javascripts/fixtures/ballonized-xkcd-with-ballons.html +163 -0
  12. data/spec/javascripts/fixtures/ballonized-xkcd-without-ballons.html +163 -0
  13. data/spec/javascripts/fixtures/xkcd.css +191 -0
  14. data/spec/javascripts/helpers/jasmine-jquery.js +660 -0
  15. data/spec/javascripts/helpers/jquery.simulate-ext.js +32 -0
  16. data/spec/javascripts/helpers/jquery.simulate.drag-n-drop.js +583 -0
  17. data/spec/javascripts/helpers/jquery.simulate.js +328 -0
  18. data/spec/javascripts/support/jasmine.yml +99 -0
  19. data/vendor/assets/javascripts/jquery-2.0.1.js +8837 -0
  20. data/vendor/assets/javascripts/jquery-ui-1.10.3.custom.min.js +6 -0
  21. data/vendor/assets/javascripts/jquery.json-2.4.min.js +24 -0
  22. data/vendor/assets/stylesheets/ui-lightness/images/animated-overlay.gif +0 -0
  23. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  24. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  25. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  26. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  27. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  28. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  29. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  30. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  31. data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  32. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  33. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  34. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  35. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  36. data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  37. data/vendor/assets/stylesheets/ui-lightness/jquery-ui-1.10.3.custom.min.css +5 -0
  38. metadata +51 -3
data/lib/ballonizer.rb CHANGED
@@ -4,6 +4,7 @@ require 'sequel'
4
4
  require 'json'
5
5
  require 'htmlentities'
6
6
  require 'rack'
7
+ require 'sprockets'
7
8
 
8
9
  # This gem provides mechanisms to allow ballons (or speech bubbles) to be
9
10
  # added/removed/edited over images of a HTML or XHTML document and to be
@@ -16,7 +17,7 @@ require 'rack'
16
17
  #
17
18
  # This class lacks a lot of features like: access to an abstraction of the
18
19
  # ballons, images and their relationship; control over users who edit the
19
- # ballons; access to the old versions of the ballon set of a image (who
20
+ # ballons; access to the old versions of the ballon set of a image (that
20
21
  # are stored in the database, but only can be accessed directly by the
21
22
  # Sequel::Database object). It's a work in progress, be warned to use
22
23
  # carefully and motivated to contribute.
@@ -40,12 +41,10 @@ require 'rack'
40
41
  # To use this class with your (rack isn't?) app you need to: create the
41
42
  # necessary tables in a Sequel::Database object with Ballonizer.create_tables;
42
43
  # create a ballonizer instance with the url where you gonna handle the ballon
43
- # change requests. Handle the ballon changes request in that url with
44
- # process_submit. Call instance.ballonize_page over the html documents who can
45
- # have the images to be ballonized. Include the jquery, jquery-json, jquery-ui,
46
- # and ballonizer javascript libraries in the page (provided by the gem); Include
47
- # the ui-lightness and ballonizer css in the page (provided by the gem).
48
- # Check if the image match the css selector :img_to_ballonize_css_selector.
44
+ # change requests and where provide the assets. Handle the ballon changes request
45
+ # in that url with process_submit. Call instance.ballonize_page over the html
46
+ # documents that can have the images to be ballonized. Check if the image match
47
+ # the css selector :img_to_ballonize_css_selector.
49
48
  #
50
49
  # What's explained above is basically the example you can access with
51
50
  # 'rake example' and is in the examples/ballonizer_app/config.ru file.
@@ -64,23 +63,93 @@ class Ballonizer
64
63
 
65
64
  attr_accessor :db, :settings
66
65
 
66
+ # @api private Don't use the methods of this module. They are for internal use only.
67
+ module Workaround
68
+ def self.join_uris(base, relative)
69
+ base = base.end_with?('/') ? base : base + '/'
70
+ Addressable::URI.parse(base).join(relative).to_s
71
+ end
72
+
73
+ def self.deep_freeze(e)
74
+ e.each { | v | deep_freeze(v) } if e.is_a?(Enumerable)
75
+ e.freeze
76
+ end
77
+
78
+ def self.parse_html_or_xhtml(doc)
79
+ # If you parse XHTML as HTML with Nokogiri and use to_s after the markup can be messed up
80
+ #
81
+ # Example: <meta name="description" content="not important" />
82
+ # becomes <meta name="description" content="not important" >
83
+ # To avoid this we parse a document that is XML valid as XML, and, otherwise as HTML
84
+ parsed_doc = nil
85
+ begin
86
+ # this also isn't a great way to do this
87
+ # the Nokogiri don't have exception classes, this way any StandardError will be silenced
88
+ options = Nokogiri::XML::ParseOptions::DEFAULT_XML &
89
+ Nokogiri::XML::ParseOptions::STRICT &
90
+ Nokogiri::XML::ParseOptions::NONET
91
+ parsed_doc = Nokogiri::XML::Document.parse(doc, nil, nil, options)
92
+ rescue
93
+ parsed_doc = Nokogiri::HTML(doc)
94
+ end
95
+
96
+ parsed_doc
97
+ end
98
+ end
99
+
100
+ private_constant :Workaround
101
+
102
+ # The load paths of assets inside the gem and the files inside each path,
103
+ # in the order they need to be included (the files of the first path need
104
+ # to be included before the files in the second path, and the files in the
105
+ # same path need to be included in the specified order).
106
+ # Give preference to the asset(s)_* and *_html_links methods over this constant.
107
+ ASSETS = Workaround.deep_freeze([
108
+ ['vendor/assets/javascripts', [
109
+ 'jquery-2.0.1.js',
110
+ 'jquery.json-2.4.min.js',
111
+ 'jquery-ui-1.10.3.custom.min.js']],
112
+ ['lib/assets/javascripts', [
113
+ 'ballonizer.js']],
114
+ ['vendor/assets/stylesheets', [
115
+ 'ui-lightness/jquery-ui-1.10.3.custom.min.css']],
116
+ ['lib/assets/stylesheets', [
117
+ 'ballonizer.css']]
118
+ ])
119
+
67
120
  # The default #settings
68
121
  DEFAULT_SETTINGS = {
69
122
  # The css selector used to define the elements to ballonize.
70
123
  img_to_ballonize_css_selector: 'img.to_ballonize',
71
124
  # A url to be used in the client-side action attribute of the form for
72
- # ballon submition. The value will be used in the javascript snippet who
125
+ # ballon submition. The value will be used in the javascript snippet that
73
126
  # initialize the ballonizer client javascript allowing ballon edition
74
127
  # (and consequently creating the form).
75
128
  form_handler_url: '#',
76
- # Define if the javascript code who allow edition will be added to the page.
129
+ # Define if the javascript code that allow edition will be added to the page.
77
130
  # (this don't refer to the jquery-* libs and the ballonizer.js only the
78
131
  # snippet to execute when the page is ready)
79
- add_js_for_edition: true
132
+ add_js_for_edition: true,
133
+ # A path string to prefix each href of the css stylesheet links generated
134
+ # by the js_libs_html_links, and, possibly, added by the ballonize_page
135
+ # object. Example: if you use Ballonizer.assets_app mapped to '/assets'
136
+ # then use '/assets' here. This is used with the :add_required_css setting.
137
+ css_asset_path_for_link: nil,
138
+ # If the ballonize_page method will add or not the html generated by
139
+ # #css_html_links (require the :css_asset_path_for_link to be defined).
140
+ add_required_css: false,
141
+ # A path string to prefix each js source src generated by the
142
+ # object. Example: if you use Ballonizer.assets_app mapped to '/assets'
143
+ # then use '/assets' here. This is used with the
144
+ # :add_required_js_libs_for_edition setting.
145
+ js_asset_path_for_link: nil,
146
+ # If the ballonize_page method will add or not the html generated by
147
+ # #js_libs_html_links (require the :js_asset_path_for_link to be defined).
148
+ add_required_js_libs_for_edition: false
80
149
  }.freeze.each { | _, v| v.freeze }
81
150
 
82
151
  # Create a new Ballonizer object from a Sequel Database (with the expected
83
- # tables, who can be created with Ballonizer.create_tables) and a optional
152
+ # tables, that can be created with Ballonizer.create_tables) and a optional
84
153
  # hash of settings.
85
154
  # @param db [Sequel::Database] A Sequel::Database with tables as described
86
155
  # @param settings [Hash{Symbol => String}] A optional hash of settings. The
@@ -256,7 +325,9 @@ class Ballonizer
256
325
  end
257
326
 
258
327
  # Wrap each image to ballonize with a container, add its ballons to the
259
- # container and add the js snippet for the edition initialization.
328
+ # container and, possibly, add the css and js libs and snippet for the
329
+ # edition initialization. Don't make any change if the page has no images
330
+ # to ballonize.
260
331
  # @param page [String] The (X)HTML page.
261
332
  # @param page_url [String] The url of the page to be ballonized, necessary
262
333
  # to make absolute the src attribute of img (if it's relative).
@@ -284,9 +355,15 @@ class Ballonizer
284
355
  end
285
356
  end
286
357
 
358
+ head = parsed_page.at_css('head')
359
+ if settings[:add_required_css]
360
+ head.children.last.add_next_sibling(self.css_html_links)
361
+ end
362
+ if settings[:add_required_js_libs_for_edition]
363
+ head.children.last.add_next_sibling(self.js_libs_html_links)
364
+ end
287
365
  if settings[:add_js_for_edition]
288
- parsed_page.at_css('head').children.last
289
- .add_next_sibling(self.js_load_snippet)
366
+ head.children.last.add_next_sibling(self.js_load_snippet)
290
367
  end
291
368
  end
292
369
 
@@ -310,7 +387,7 @@ class Ballonizer
310
387
  # Don't use this method. It is for internal use only.
311
388
  # @note This method don't make distinction between a image in the database
312
389
  # without any ballons (removed in the last version, by example) or a image
313
- # who isn't in the database (both return a empty array).
390
+ # that isn't in the database (both return a empty array).
314
391
  def last_ballon_set_of_image(img_src)
315
392
  db_image = self.db[:images].first({img_src: img_src})
316
393
  if db_image
@@ -372,30 +449,118 @@ class Ballonizer
372
449
  end
373
450
  end
374
451
 
375
- # @api private Don't use the methods of this module. They are for internal use only.
376
- module Workaround
377
- def self.parse_html_or_xhtml(doc)
378
- # If you parse XHTML as HTML with Nokogiri and use to_s after the markup can be messed up
379
- #
380
- # Example: <meta name="description" content="not important" />
381
- # becomes <meta name="description" content="not important" >
382
- # To avoid this we parse a document who is XML valid as XML, and, otherwise as HTML
383
- parsed_doc = nil
384
- begin
385
- # this also isn't a great way to do this
386
- # the Nokogiri don't have exception classes, this way any StandardError will be silenced
387
- options = Nokogiri::XML::ParseOptions::DEFAULT_XML &
388
- Nokogiri::XML::ParseOptions::STRICT &
389
- Nokogiri::XML::ParseOptions::NONET
390
- parsed_doc = Nokogiri::XML::Document.parse(doc, nil, nil, options)
391
- rescue
392
- parsed_doc = Nokogiri::HTML(doc)
393
- end
452
+ # The (X)HTML fragment with the link tags that are added to the page by
453
+ # ballonize_page if the :add_required_css setting is true (the default
454
+ # is false). If the :css_asset_path_for_link isn't set (what is the
455
+ # default) this method return nil.
456
+ # @return [String,NilClass] A frozen string.
457
+ def css_html_links
458
+ return nil unless settings[:css_asset_path_for_link]
459
+ return @css_html_links if @css_html_links
460
+
461
+ link_template = '<link rel="stylesheet" type="text/css" href="PATH" />'
462
+ css_paths = self.class.asset_logical_paths.select do | p |
463
+ /^.+\.css$/.match(p)
464
+ end
394
465
 
395
- parsed_doc
466
+ links = css_paths.map do | p |
467
+ p = Workaround.join_uris(settings[:css_asset_path_for_link], p)
468
+ link_template.sub('PATH', p)
396
469
  end
470
+
471
+ @css_html_links = links.join('').freeze
397
472
  end
398
473
 
399
- private_constant :Workaround
474
+ # The (X)HTML fragment with the script tags that are added to the page by
475
+ # ballonize_page if the :add_required_js_libs_for_edition setting is true
476
+ # (the default is false). If the :js_asset_path_for_link isn't set (what
477
+ # is the default) this method return nil.
478
+ # @return [String,NilClass] A frozen string.
479
+ def js_libs_html_links
480
+ return nil unless settings[:js_asset_path_for_link]
481
+ return @js_libs_html_links if @js_libs_html_links
482
+
483
+ link_template = '<script type="text/javascript" src="PATH" />'
484
+ js_libs_paths = self.class.asset_logical_paths.select do | p |
485
+ /^.+\.js$/.match(p)
486
+ end
487
+
488
+ links = js_libs_paths.map do | p |
489
+ p = Workaround.join_uris(settings[:js_asset_path_for_link], p)
490
+ link_template.sub('PATH', p)
491
+ end
492
+
493
+ @js_libs_html_links = links.join('').freeze
494
+ end
495
+
496
+ # List of paths (relative to the gem root directory) to the directories with
497
+ # the css and js provided by the gem.
498
+ # @return [Array<String>] A frozen array of frozen strings.
499
+ def self.asset_load_paths
500
+ return @asset_load_paths if @asset_load_paths
501
+
502
+ absolute_lib_dir = File.dirname(File.realpath(__FILE__))
503
+ ballonizer_gem_root_dir = File.expand_path('../', absolute_lib_dir)
504
+
505
+ @asset_load_paths = ASSETS.map do | load_path_and_files |
506
+ load_path = load_path_and_files.first
507
+ File.expand_path(load_path, ballonizer_gem_root_dir)
508
+ end
509
+
510
+ @asset_load_paths.flatten!
511
+ @asset_load_paths.freeze
512
+ end
513
+
514
+ # List of logical paths to the css and js assets. The assets_app respond to
515
+ # any requisition to one of these paths.
516
+ # @return [Array<String>] A frozen array of frozen strings.
517
+ def self.asset_logical_paths
518
+ return @asset_logical_paths if @asset_logical_paths
519
+
520
+ @asset_logical_paths = ASSETS.map do | load_path_and_files |
521
+ load_path_and_files.last
522
+ end
523
+
524
+ @asset_logical_paths.flatten!
525
+ @asset_logical_paths.freeze
526
+ end
527
+
528
+ # List of absolute filepaths to the css and js files needed by the client
529
+ # counterpart and provided by the gem. To all who not want to use assets_app.
530
+ # @return [Array<String>] A frozen array of frozen strings.
531
+ # @see Ballonizer.assets_app
532
+ def self.asset_absolute_paths
533
+ return @asset_absolute_paths if @asset_absolute_paths
534
+
535
+ absolute_lib_dir = File.dirname(File.realpath(__FILE__))
536
+ ballonizer_gem_root_dir = File.expand_path('../', absolute_lib_dir)
537
+
538
+ @asset_absolute_paths = ASSETS.map do | load_path_and_files |
539
+ relative_load_path, filepaths = *load_path_and_files
540
+ absolute_load_path = File.expand_path(relative_load_path, ballonizer_gem_root_dir)
541
+
542
+ filepaths.map do | filepath |
543
+ File.expand_path(filepath, absolute_load_path)
544
+ end
545
+ end
546
+
547
+ @asset_absolute_paths.flatten!
548
+ @asset_absolute_paths.freeze
549
+ end
550
+
551
+ # A Rack app that provide the gem css and js. Each call to this method return
552
+ # a new object (clone). The Sprockets::Environment isn't frozen because it
553
+ # can't be used with 'run' in a rack app if frozen.
554
+ # @return [Sprockets::Environment]
555
+ # @see Ballonizer.assets_app
556
+ def self.assets_app
557
+ # dont freeze because run don't work in a frozen sprockets env
558
+ return @assets_app.clone if @assets_app
559
+ @assets_app = Sprockets::Environment.new
560
+ asset_load_paths.each do | load_path |
561
+ @assets_app.prepend_path load_path
562
+ end
563
+ @assets_app.clone
564
+ end
400
565
  end
401
566
 
@@ -3,6 +3,7 @@ require 'rspec-html-matchers'
3
3
  # Avoid to use equivalent-xml, the specs break with cosmetic changes this way
4
4
  require 'equivalent-xml'
5
5
  require 'stringio'
6
+ require 'rexml/document'
6
7
 
7
8
  # make the changes in the BD restricted to the example
8
9
  RSpec.configure do |c|
@@ -11,6 +12,26 @@ RSpec.configure do |c|
11
12
  end
12
13
  end
13
14
 
15
+ RSpec::Matchers.define :exist_in_filesystem do
16
+ match do | actual |
17
+ if actual.respond_to? :all?
18
+ actual.all? { | filename | File.exist?(filename) }
19
+ else
20
+ File.exist? actual
21
+ end
22
+ end
23
+ failure_message_for_should do | actual |
24
+ if actual.respond_to? :all?
25
+ "expected #{actual} to be a list of absolute filepaths for existing files or directories"
26
+ else
27
+ "expected #{actual} to be a absolute filepath for an existing file or directory"
28
+ end
29
+ end
30
+ description do
31
+ "be a list of absolute paths for existing files or directories"
32
+ end
33
+ end
34
+
14
35
  describe Ballonizer do
15
36
 
16
37
  DB = Sequel.sqlite
@@ -63,8 +84,11 @@ describe Ballonizer do
63
84
  # Definitions ending with '_example' are to be cloned and defined in a
64
85
  # context without the sufix. Definitions without the sufix are used in the
65
86
  # specs and may require the definition of some without '_example' counterparts.
87
+ let (:ballonizer_settings_example) do
88
+ {}
89
+ end
66
90
  let (:ballonizer_new_args_example) do
67
- [DB, {}]
91
+ [DB, ballonizer_settings]
68
92
  end
69
93
  # TODO: This isn't a valid Rack env, to turn it in a valid env will be
70
94
  # necessary to add all obrigatory 'rack.' variables described in:
@@ -123,6 +147,7 @@ describe Ballonizer do
123
147
 
124
148
  # definitions to be overriden in specific contexts (by doing a clone of the
125
149
  # '_example' counterpart and changing what is needed)
150
+ let (:ballonizer_settings) { ballonizer_settings_example }
126
151
  let (:ballonizer_new_args) { ballonizer_new_args_example }
127
152
  let (:env) { env_example }
128
153
  let (:original_page) { original_page_example.to_s }
@@ -173,6 +198,35 @@ describe Ballonizer do
173
198
  end)
174
199
  end
175
200
  end
201
+ # TODO: create this specs
202
+ context 'and the settings define to insert css' do
203
+ let (:ballonizer_settings) do
204
+ ballonizer_settings_example.merge({
205
+ add_required_css: true,
206
+ css_asset_path_for_link: 'assets/css'
207
+ })
208
+ end
209
+
210
+ it do
211
+ # the example html don't have any link tag, so if there one
212
+ # it was added by the method
213
+ should have_tag('link', :with => { type: 'text/css' })
214
+ end
215
+ end
216
+ context 'and the settings define to insert js' do
217
+ let (:ballonizer_settings) do
218
+ ballonizer_settings_example.merge({
219
+ # this option is added to avoid a false positive in the test
220
+ add_js_for_edition: false,
221
+ add_required_js_libs_for_edition: true,
222
+ js_asset_path_for_link: 'assets/js'
223
+ })
224
+ end
225
+
226
+ it do
227
+ should have_tag('script', :with => { type: 'text/javascript' })
228
+ end
229
+ end
176
230
  end
177
231
  context 'and more than one of them is to be ballonized' do
178
232
  let (:original_page) do
@@ -488,7 +542,6 @@ describe Ballonizer do
488
542
  end
489
543
 
490
544
  describe '#process_submit' do
491
-
492
545
  # As process_submit use the #valid_submit_json? and #process_submit_hash
493
546
  # we only make the tests here for example and its not the ideia cover the
494
547
  # cases already covered in the specs of the two methods
@@ -535,5 +588,103 @@ describe Ballonizer do
535
588
  end
536
589
  end
537
590
  end
591
+
592
+ describe '#css_html_links' do
593
+ context "when the path for the css isn't configured" do
594
+ it 'returns nil' do
595
+ expect(instance.css_html_links).to eq nil
596
+ end
597
+ end
598
+ context 'when the path for the css is configured' do
599
+ let (:ballonizer_settings) do
600
+ ballonizer_settings_example.merge({
601
+ css_asset_path_for_link: 'assets/css'
602
+ })
603
+ end
604
+
605
+ it 'return a HTML string with links prefixed by the path' do
606
+ REXML::Document.new("<root>#{instance.css_html_links}</root>")
607
+ .root.children.each do | e |
608
+ expect(e.attributes['href']).to match(/^assets\/css/)
609
+ end
610
+ end
611
+ end
612
+ end
613
+
614
+ describe '#js_libs_html_links' do
615
+ context "when the path for the js libs isn't configured" do
616
+ it 'returns nil' do
617
+ expect(instance.js_libs_html_links).to eq nil
618
+ end
619
+ end
620
+ context 'when the path for the js libs is configured' do
621
+ let (:ballonizer_settings) do
622
+ ballonizer_settings_example.merge({
623
+ js_asset_path_for_link: 'assets/js'
624
+ })
625
+ end
626
+
627
+ it 'return a HTML string with links prefixed by the path' do
628
+ REXML::Document.new("<root>#{instance.js_libs_html_links}</root>")
629
+ .root.children.each do | e |
630
+ expect(e.attributes['src']).to match(/^assets\/js/)
631
+ end
632
+ end
633
+ end
634
+ end
635
+
636
+ describe '.asset_load_paths' do
637
+ it 'return an array of paths who exist in the gem root dir' do
638
+ # TODO: change to __dir__ when the 2.0 become widely adopted
639
+ spec_dir = File.dirname(File.realpath(__FILE__))
640
+ gem_root_dir = File.expand_path('../', spec_dir)
641
+
642
+ absolute_load_paths = described_class.asset_load_paths.map do | path |
643
+ File.expand_path(path, gem_root_dir)
644
+ end
645
+
646
+ expect(absolute_load_paths).to exist_in_filesystem
647
+ end
648
+ end
649
+
650
+ describe '.asset_absolute_paths' do
651
+ it 'return an array of absolute filepaths who exist' do
652
+ expect(described_class.asset_absolute_paths).to exist_in_filesystem
653
+ end
654
+ end
655
+
656
+ describe '.asset_logical_paths' do
657
+ it 'return the last part of the asset_absolute_paths' do
658
+ logical_paths = described_class.asset_logical_paths
659
+ absolute_paths = described_class.asset_absolute_paths
660
+
661
+ are_last_part_of_absolute = logical_paths.all? do | lp |
662
+ absolute_paths.any? { | ap | ap.end_with? lp }
663
+ end
664
+
665
+ expect(are_last_part_of_absolute).to be_true
666
+ end
667
+ end
668
+
669
+ describe '.assets_app' do
670
+ let (:envs) do
671
+ described_class.asset_logical_paths.map do | asset_logical_path |
672
+ env_example.merge({
673
+ 'HTTP_HOST' => 'example.net',
674
+ 'SERVER_NAME' => 'example.net',
675
+ 'PATH_INFO' => asset_logical_path,
676
+ 'REQUEST_METHOD' => 'GET',
677
+ 'rack.input' => nil,
678
+ 'CONTENT_TYPE' => nil,
679
+ 'CONTENT_LENGTH' => nil
680
+ })
681
+ end
682
+ end
683
+ it 'provide the css and js libs required by the gem' do
684
+ envs.each do | env |
685
+ expect(described_class.assets_app.call(env)[0]).to eq 200
686
+ end
687
+ end
688
+ end
538
689
  end
539
690