asset_hat 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +37 -0
  3. data/HISTORY +10 -0
  4. data/README.rdoc +117 -75
  5. data/Rakefile +4 -4
  6. data/VERSION.yml +2 -2
  7. data/asset_hat.gemspec +69 -7
  8. data/config/assets.yml +46 -24
  9. data/doc/classes/AssetHat.html +183 -118
  10. data/doc/classes/AssetHat/CSS.html +21 -21
  11. data/doc/classes/AssetHat/CSS/Engines.html +10 -10
  12. data/doc/classes/AssetHat/JS.html +13 -13
  13. data/doc/classes/AssetHat/JS/Engines.html +10 -10
  14. data/doc/classes/AssetHat/JS/Vendors.html +85 -22
  15. data/doc/classes/AssetHatHelper.html +94 -19
  16. data/doc/created.rid +1 -1
  17. data/doc/files/HISTORY.html +20 -2
  18. data/doc/files/LICENSE.html +1 -1
  19. data/doc/files/README_rdoc.html +87 -31
  20. data/doc/files/lib/asset_hat/capistrano_rb.html +1 -1
  21. data/doc/files/lib/asset_hat/css_rb.html +1 -1
  22. data/doc/files/lib/asset_hat/initializers/action_view_rb.html +49 -0
  23. data/doc/files/lib/asset_hat/initializers/cache_last_commit_ids_rb.html +49 -0
  24. data/doc/files/lib/asset_hat/js/vendors_rb.html +1 -1
  25. data/doc/files/lib/asset_hat/js_rb.html +1 -1
  26. data/doc/files/lib/asset_hat/railtie_rb.html +61 -0
  27. data/doc/files/lib/asset_hat/tasks/css_rb.html +1 -1
  28. data/doc/files/lib/asset_hat/tasks/js_rb.html +1 -1
  29. data/doc/files/lib/asset_hat/tasks_rb.html +2 -1
  30. data/doc/files/lib/asset_hat/unicorn_rb.html +58 -0
  31. data/doc/files/lib/asset_hat/vcs_rb.html +1 -1
  32. data/doc/files/lib/asset_hat/version_rb.html +1 -1
  33. data/doc/files/{app/helpers → lib}/asset_hat_helper_rb.html +3 -3
  34. data/doc/files/lib/asset_hat_rb.html +9 -1
  35. data/doc/files/lib/tasks/asset_hat_rake.html +54 -0
  36. data/doc/fr_file_index.html +6 -1
  37. data/doc/fr_method_index.html +60 -52
  38. data/lib/asset_hat.rb +104 -59
  39. data/lib/asset_hat/initializers/action_view.rb +1 -0
  40. data/lib/asset_hat/initializers/cache_last_commit_ids.rb +1 -0
  41. data/lib/asset_hat/js/vendors.rb +198 -72
  42. data/lib/asset_hat/railtie.rb +19 -0
  43. data/lib/asset_hat/tasks.rb +5 -2
  44. data/lib/asset_hat/unicorn.rb +9 -0
  45. data/lib/asset_hat/vcs.rb +3 -2
  46. data/{app/helpers → lib}/asset_hat_helper.rb +184 -29
  47. data/{tasks → lib/tasks}/asset_hat.rake +0 -0
  48. data/rails/init.rb +2 -2
  49. data/test/asset_hat_helper_test.rb +731 -148
  50. data/test/asset_hat_test.rb +23 -2
  51. data/test/test_helper.rb +4 -8
  52. metadata +311 -30
@@ -0,0 +1,19 @@
1
+ require 'asset_hat'
2
+ require 'asset_hat_helper'
3
+ require 'rails'
4
+
5
+ module AssetHat
6
+ class Railtie < Rails::Railtie #:nodoc:
7
+ initializer 'asset_hat.action_view' do |app|
8
+ require 'asset_hat/initializers/action_view'
9
+ end
10
+
11
+ initializer 'asset_hat.cache_last_commit_ids' do |app|
12
+ require 'asset_hat/initializers/cache_last_commit_ids'
13
+ end
14
+
15
+ rake_tasks do
16
+ load 'tasks/asset_hat.rake'
17
+ end
18
+ end
19
+ end
@@ -26,7 +26,9 @@ namespace :asset_hat do
26
26
  end
27
27
 
28
28
  desc 'Prepare configuration file'
29
- task :config, :needs => :environment do
29
+ task :config do
30
+ require 'asset_hat'
31
+
30
32
  template_filepath = File.join(File.dirname(__FILE__), '..', '..',
31
33
  AssetHat::RELATIVE_CONFIG_FILEPATH)
32
34
  target_filepath = AssetHat::CONFIG_FILEPATH
@@ -40,7 +42,8 @@ namespace :asset_hat do
40
42
  end
41
43
 
42
44
  FileUtils.cp(template_filepath, target_filepath)
43
- puts "Wrote to #{target_filepath}"
45
+ puts "\nWrote to #{target_filepath}. Next, open this file in your editor"
46
+ puts 'and set up your CSS/JS bundles.'
44
47
  end
45
48
 
46
49
  end # namespace :asset_hat
@@ -0,0 +1,9 @@
1
+ # Add the code below to your app's unicorn.conf.rb or similar so that assets'
2
+ # commit IDs are precached only once by the Unicorn master, then propagated
3
+ # out to workers. (Otherwise, each worker will precache commit IDs, which
4
+ # wastes resources.) More on configuring Unicorn:
5
+ # http://unicorn.bogomips.org/Unicorn/Configurator.html
6
+
7
+ before_fork do |server, worker|
8
+ AssetHat.cache_last_commit_ids if defined?(AssetHat)
9
+ end
data/lib/asset_hat/vcs.rb CHANGED
@@ -46,7 +46,7 @@ module AssetHat
46
46
  # Process arguments
47
47
  type = type.to_sym
48
48
  unless TYPES.include?(type)
49
- raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}."
49
+ raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.}
50
50
  return
51
51
  end
52
52
 
@@ -58,7 +58,8 @@ module AssetHat
58
58
  dir = self.assets_dir(type)
59
59
  filepaths = self.bundle_filepaths(bundle, type)
60
60
  if filepaths.present?
61
- @last_bundle_commit_ids[type][bundle] = self.last_commit_id(*filepaths)
61
+ @last_bundle_commit_ids[type][bundle] =
62
+ self.last_commit_id(*filepaths)
62
63
  end
63
64
  end
64
65
 
@@ -20,33 +20,34 @@ module AssetHatHelper
20
20
  end
21
21
 
22
22
  options = args.extract_options!.symbolize_keys
23
- filenames = []
23
+ filenames = [] # May or may not have proper extensions
24
24
  sources = [] # The URLs that are ultimately included via HTML
25
25
  source_commit_ids = {} # Last commit ID for each source
26
26
 
27
- # Set to `true` to use bundles and minified code:
27
+ # If `use_caching` is `true`, bundles and minified code will be used:
28
28
  use_caching = AssetHat.cache?
29
29
  use_caching = options[:cache] unless options[:cache].nil?
30
30
  options.delete :cache # Completely avoid Rails' built-in caching
31
31
 
32
+ # Gather list of filenames, which may not have proper extensions yet
33
+ filenames = args.dup
32
34
  if options[:bundle].present? || options[:bundles].present?
33
35
  bundles = [options.delete(:bundle), options.delete(:bundles)].
34
36
  flatten.reject(&:blank?)
35
37
  if use_caching
36
- sources += bundles.map do |bundle|
38
+ filenames += bundles.map do |bundle|
37
39
  File.join(AssetHat.bundles_dir(options.slice(:ssl)),
38
40
  "#{bundle}.min.#{type}")
39
41
  end
40
42
  else
41
43
  config = AssetHat.config
42
- filenames = bundles.map { |b| AssetHat.bundle_filenames(b, type) }.
44
+ filenames += bundles.map { |b| AssetHat.bundle_filenames(b, type) }.
43
45
  flatten.reject(&:blank?)
44
46
  end
45
- else
46
- filenames = args
47
47
  end
48
48
 
49
- # Add extensions if needed, using minified file if it already exists
49
+ # Build `sources`, adding extensions if needed, using minified file if it
50
+ # already exists
50
51
  filenames.each do |filename|
51
52
  if filename.match(/\.#{type}$/)
52
53
  sources << filename
@@ -88,26 +89,34 @@ module AssetHatHelper
88
89
  end
89
90
  end
90
91
 
91
- # Build output HTML
92
+ # Prepare return value
92
93
  options.delete :ssl
93
- sources.map do |src|
94
- case type
95
- when :css ; stylesheet_link_tag(src, options)
96
- when :js ; javascript_include_tag(src, options)
97
- else nil
98
- end
99
- end.join("\n")
94
+ if options[:only_url]
95
+ # Return only asset URLs, not HTML inclusions
96
+ source_urls = sources.map { |source| asset_path(type, source) }
97
+ source_urls.size == 1 ? source_urls.first : source_urls
98
+ else
99
+ html = sources.map do |src|
100
+ case type
101
+ when :css then stylesheet_link_tag(src, options)
102
+ when :js then javascript_include_tag(src, options)
103
+ else nil
104
+ end
105
+ end.join("\n")
106
+ html.respond_to?(:html_safe) ? html.html_safe : html
107
+ end
108
+
100
109
  end # def include_assets
101
110
 
102
111
  # <code>include_css</code> is a smart wrapper for Rails'
103
112
  # <code>stylesheet_link_tag</code>. The two can be used together while
104
113
  # migrating to AssetHat.
105
114
  #
106
- # Include a single stylesheet:
115
+ # Include a single, minified stylesheet:
107
116
  # include_css 'diagnostics'
108
117
  # => <link href="/stylesheets/diagnostics.min.css" media="screen,projection" rel="stylesheet" type="text/css" />
109
118
  #
110
- # Include a single unminified stylesheet:
119
+ # Include a single, unminified stylesheet:
111
120
  # include_css 'diagnostics.css'
112
121
  # => <link href="/stylesheets/diagnostics.css" media="screen,projection" rel="stylesheet" type="text/css" />
113
122
  #
@@ -117,15 +126,36 @@ module AssetHatHelper
117
126
  # => <link href="/stylesheets/bundles/application.min.css" ... />
118
127
  #
119
128
  # Include multiple stylesheets separately (not as cool):
120
- # include_css 'reset', 'application', 'clearfix'
129
+ # include_css 'reset', 'application'
121
130
  # => <link href="/stylesheets/reset.min.css" ... />
122
131
  # <link href="/stylesheets/application.min.css" ... />
123
- # <link href="/stylesheets/clearfix.min.css" ... />
124
132
  #
125
133
  # Include a stylesheet with extra media types:
126
134
  # include_css 'mobile', :media => 'handheld,screen,projection'
127
135
  # => <link href="/stylesheets/mobile.min.css"
128
136
  # media="handheld,screen,projection" ... />
137
+ #
138
+ # Get the URL for a single, minified stylesheet:
139
+ # include_css 'diagnostics', :only_url => true
140
+ # => '/stylesheets/diagnostics.min.css'
141
+ #
142
+ # Get the URL for a single, unminified stylesheet:
143
+ # include_css 'diagnostics.css', :only_url => true
144
+ # => '/stylesheets/diagnostics.css'
145
+ #
146
+ # Get the URL for a bundle of stylesheets when environment *enables* caching
147
+ # (e.g., staging, production):
148
+ # include_css :bundle => 'application', :only_url => true
149
+ # => '/stylesheets/bundles/application.min.css'
150
+ #
151
+ # Get URLs for a bundle of stylesheets when environment *disables* caching
152
+ # (e.g., development, test):
153
+ # include_css :bundle => 'application', :only_url => true
154
+ # => ['/stylesheets/reset.css', '/stylesheets/common.css', ...]
155
+ #
156
+ # Get URLs for multiple stylesheets manually:
157
+ # include_css 'reset', 'application', :only_url => true
158
+ # => ['/stylesheets/reset.css', '/stylesheets/application.css']
129
159
  def include_css(*args)
130
160
  return if args.blank?
131
161
 
@@ -145,18 +175,18 @@ module AssetHatHelper
145
175
  end
146
176
 
147
177
  html ||= AssetHat.html_cache[:css][cache_key]
148
- html
178
+ html.respond_to?(:html_safe) ? html.html_safe : html
149
179
  end
150
180
 
151
181
  # <code>include_js</code> is a smart wrapper for Rails'
152
182
  # <code>javascript_include_tag</code>. The two can be used together while
153
183
  # migrating to AssetHat.
154
184
  #
155
- # Include a single JS file:
185
+ # Include a single, minified JS file:
156
186
  # include_js 'application'
157
187
  # => <script src="/javascripts/application.min.js" type="text/javascript"></script>
158
188
  #
159
- # Include a single JS unminified file:
189
+ # Include a single, unminified JS file:
160
190
  # include_js 'application.js'
161
191
  # => <script src="/javascripts/application.js" type="text/javascript"></script>
162
192
  #
@@ -169,7 +199,7 @@ module AssetHatHelper
169
199
  # include_js :jquery
170
200
  # => <script src="http://ajax.googleapis.com/.../jquery.min.js" ...></script>
171
201
  # # Set jQuery versions either in `config/assets.yml`, or by using
172
- # # `include_js :jquery, :version => '1.4'`.
202
+ # # `include_js :jquery, :version => '1.6.0'`.
173
203
  #
174
204
  # Include a bundle of JS files (i.e., a concatenated set of files;
175
205
  # configure in <code>config/assets.yml</code>):
@@ -186,6 +216,82 @@ module AssetHatHelper
186
216
  # => <script src="/javascripts/bloombox.min.js" ...></script>
187
217
  # <script src="/javascripts/jquery.cookie.min.js" ...></script>
188
218
  # <script src="/javascripts/jquery.json.min.js" ...></script>
219
+ #
220
+ # Get the URL for a single, minified JS file:
221
+ # include_js 'application', :only_url => true
222
+ # => '/javascripts/application.min.js'
223
+ #
224
+ # Get the URL for a single, unminified JS file:
225
+ # include_js 'application.js', :only_url => true
226
+ # => '/javascripts/application.js', :only_url => true
227
+ #
228
+ # Get the URL for jQuery:
229
+ # # Development/test environment:
230
+ # include_js :jquery, :only_url => true
231
+ # => '/javascripts/jquery-VERSION.min.js'
232
+ #
233
+ # # Staging/production environment:
234
+ # include_js :jquery, :only_url => true
235
+ # => 'http://ajax.googleapis.com/.../jquery.min.js'
236
+ #
237
+ # Get the URL for a bundle of JS files when environment *enables* caching
238
+ # (e.g., staging, production):
239
+ # include_js :bundle => 'application', :only_url => true
240
+ # => '/javascripts/bundles/application.min.js'
241
+ #
242
+ # Get URLs for a bundle of JS files when environment *disables* caching
243
+ # (e.g., development, test):
244
+ # include_js :bundle => 'application', :only_url => true
245
+ # => ['/javascripts/jquery.plugin-foo.js',
246
+ # '/javascripts/jquery.plugin-bar.min.js',
247
+ # '/javascripts/json2.js',
248
+ # ...]
249
+ #
250
+ # Get URLs for multiple JS files manually:
251
+ # include_js 'json2', 'application', :only_url => true
252
+ # => ['/javascripts/json2.js', '/javascripts/application.js']
253
+ #
254
+ # Load JS files with {LABjs}[http://labjs.com] (hosted either from cdnjs or
255
+ # your own web server, if found in <code>public/javascripts/</code>):
256
+ #
257
+ # # config/assets.yml:
258
+ # js:
259
+ # vendors:
260
+ # lab_js:
261
+ # version: 1.x.x
262
+ #
263
+ # # Usage:
264
+ # include_js :jquery, :bundle => 'application', :loader => :lab_js
265
+ # => <script src="http://ajax.cdnjs.com/.../1.x.x/LAB.min.js" ...></script>
266
+ # <script type="text/javascript">
267
+ # window.$LABinst=$LAB.
268
+ # script('http://ajax.googleapis.com/.../jquery.min.js').wait().
269
+ # script('/javascripts/bundles/application.min.js').wait();
270
+ # </script>
271
+ #
272
+ # # For advanced fine-tuning, build the LABjs calls manually (based on
273
+ # # example from http://labjs.com/documentation.php ):
274
+ # <script>
275
+ # window.$LABinst = $LAB.
276
+ # script('<%= include_js 'framework', :only_url => true %>').wait().
277
+ # script('<%= include_js 'plugin.framework.js',
278
+ # :only_url => true %>').
279
+ # script('<%= include_js 'myplugin.framework.js',
280
+ # :only_url => true %>').wait().
281
+ # script('<%= include_js 'init.js', :only_url => true %>').wait();
282
+ # </script>
283
+ #
284
+ # # If you want to execute an inline <script> block that relies on any
285
+ # # of these dependencies, use the JS variable `window.$LABinst`.
286
+ # # Example (using jQuery to handle when DOM is ready):
287
+ # <script>
288
+ # window.$LABinst(function(){
289
+ # console.log('JS dependencies are ready');
290
+ # $(function(){
291
+ # console.log('DOM is ready');
292
+ # });
293
+ # });
294
+ # </script>
189
295
  def include_js(*args)
190
296
  return if args.blank?
191
297
 
@@ -199,24 +305,73 @@ module AssetHatHelper
199
305
  if !AssetHat.cache? || AssetHat.html_cache[:js][cache_key].blank?
200
306
  # Generate HTML and write to cache
201
307
 
202
- html = []
308
+ htmls = []
309
+ include_assets_options = options.except(:ssl, :version)
310
+ loader = nil
311
+
312
+ if options[:loader].present?
313
+ loader = options.delete(:loader)
314
+ include_assets_options.merge!(:only_url => true)
315
+ end
316
+
317
+ # Get vendor HTML/URLs
203
318
  included_vendors = (args & AssetHat::JS::VENDORS)
319
+
320
+ # Add HTML inclusions for vendors
204
321
  included_vendors.each do |vendor|
205
322
  args.delete vendor
206
323
  src = AssetHat::JS::Vendors.source_for(
207
324
  vendor, options.slice(:ssl, :version))
208
- html << include_assets(:js, src, :cache => true)
325
+ htmls << include_assets(:js, src,
326
+ include_assets_options.merge(:cache => true).
327
+ except(:bundle, :bundles))
209
328
  end
210
329
 
211
- options.except! :ssl, :version
330
+ # Get non-vendor HTML/URLs
331
+ htmls << include_assets(:js, *(args + [include_assets_options]))
332
+ htmls.reject!(&:blank?)
212
333
 
213
- html << include_assets(:js, *(args + [options]))
214
- html = html.join("\n").strip
334
+ if loader
335
+ # `htmls` actually contains URLs; convert to an HTML/JS block
336
+ urls = htmls.dup.flatten
337
+ htmls = []
338
+
339
+ case loader
340
+ when :lab_js
341
+ htmls << include_js(:lab_js)
342
+ htmls << '<script type="text/javascript">'
343
+ htmls << AssetHat::JS::Vendors.loader_js(:lab_js, :urls => urls)
344
+ htmls << '</script>'
345
+ end
346
+ end
347
+
348
+ # Convert to a URL (string), array of URLs, or one long HTML string
349
+ html = if options[:only_url]
350
+ # Return one URL (string) or multiple (array of strings).
351
+ # Not actually HTML.
352
+ htmls.flatten!
353
+ htmls.size == 1 ? htmls.first : htmls
354
+ else
355
+ # Return one long string of HTML
356
+ htmls.join("\n").strip
357
+ end
215
358
  AssetHat.html_cache[:js][cache_key] = html
216
359
  end
217
360
 
218
361
  html ||= AssetHat.html_cache[:js][cache_key]
219
- html
362
+ html.respond_to?(:html_safe) ? html.html_safe : html
363
+ end
364
+
365
+ # Returns the public URL path to the given source file.
366
+ #
367
+ # <code>type</code> argument: <code>:css</code> or <code>:js</code>
368
+ def asset_path(type, source)
369
+ case type.to_sym
370
+ when :css ; stylesheet_path(source)
371
+ when :js ; javascript_path(source)
372
+ else
373
+ raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.}
374
+ end
220
375
  end
221
376
 
222
377
  end
File without changes
data/rails/init.rb CHANGED
@@ -1,2 +1,2 @@
1
- ::ActionView::Base.send(:include, AssetHatHelper)
2
- AssetHat.cache_last_commit_ids unless defined?(::IRB)
1
+ require 'asset_hat/initializers/action_view'
2
+ require 'asset_hat/initializers/cache_last_commit_ids'
@@ -4,71 +4,116 @@ class AssetHatHelperTest < ActionView::TestCase
4
4
  RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5
5
 
6
6
  context 'include_css' do
7
+ setup { flexmock_rails_app }
8
+
7
9
  context 'with caching enabled' do
8
10
  context 'with minified versions' do
9
11
  setup do
10
12
  @commit_id = '111'
11
- flexmock(AssetHat, :last_commit_id => @commit_id)
13
+ flexmock(AssetHat).should_receive(:last_commit_id => @commit_id).
14
+ by_default
12
15
  end
13
16
 
14
17
  should 'include one file by name, and ' +
15
18
  'automatically use minified version' do
16
19
  flexmock(AssetHat, :asset_exists? => true)
17
- output = include_css('foo', :cache => true)
18
- assert_equal css_tag("foo.min.css?#{@commit_id}"), output
20
+ expected_html = css_tag("foo.min.css?#{@commit_id}")
21
+ expected_path =
22
+ AssetHat.assets_path(:css) + "/foo.min.css?#{@commit_id}"
23
+
24
+ assert_equal expected_html, include_css('foo', :cache => true)
25
+ assert_equal expected_path, include_css('foo', :cache => true,
26
+ :only_url => true)
19
27
  end
20
28
 
21
29
  should 'include one unminified file by name and extension' do
22
- output = include_css('foo.css', :cache => true)
23
- assert_equal css_tag("foo.css?#{@commit_id}"), output
30
+ expected_html = css_tag("foo.css?#{@commit_id}")
31
+ expected_path =
32
+ AssetHat.assets_path(:css) + "/foo.css?#{@commit_id}"
33
+
34
+ assert_equal expected_html, include_css('foo.css', :cache => true)
35
+ assert_equal expected_path, include_css('foo.css', :cache => true,
36
+ :only_url => true)
24
37
  end
25
38
 
26
39
  should 'include one minified file by name and extension' do
27
- output = include_css('foo.min.css', :cache => true)
28
- assert_equal css_tag("foo.min.css?#{@commit_id}"), output
40
+ expected_html = css_tag("foo.min.css?#{@commit_id}")
41
+ expected_path =
42
+ AssetHat.assets_path(:css) + "/foo.min.css?#{@commit_id}"
43
+
44
+ assert_equal expected_html,
45
+ include_css('foo.min.css', :cache => true)
46
+ assert_equal expected_path,
47
+ include_css('foo.min.css', :cache => true, :only_url => true)
29
48
  end
30
49
 
31
50
  should 'include multiple files by name' do
32
51
  flexmock(AssetHat, :asset_exists? => true)
33
- expected = %w[foo bar].map do |source|
52
+
53
+ sources = %w[foo bar]
54
+ expected_html = sources.map do |source|
34
55
  css_tag("#{source}.min.css?#{@commit_id}")
35
56
  end.join("\n")
36
- output = include_css('foo', 'bar', :cache => true)
37
- assert_equal expected, output
57
+ expected_paths = sources.map do |source|
58
+ AssetHat.assets_path(:css) + "/#{source}.min.css?#{@commit_id}"
59
+ end
60
+
61
+ assert_equal expected_html,
62
+ include_css('foo', 'bar', :cache => true)
63
+ assert_equal expected_paths,
64
+ include_css('foo', 'bar', :cache => true, :only_url => true)
38
65
  end
39
66
 
40
67
  should 'include multiple files as a bundle' do
41
68
  bundle = 'css-bundle-1'
42
- output = include_css(:bundle => bundle, :cache => true)
43
- assert_equal(
44
- css_tag("bundles/#{bundle}.min.css?#{@commit_id}"), output)
69
+ expected_html = css_tag("bundles/#{bundle}.min.css?#{@commit_id}")
70
+ expected_path =
71
+ AssetHat.bundles_path(:css) + "/#{bundle}.min.css?#{@commit_id}"
72
+
73
+ assert_equal expected_html,
74
+ include_css(:bundle => bundle, :cache => true)
75
+ assert_equal expected_path,
76
+ include_css(:bundle => bundle, :cache => true, :only_url => true)
45
77
  end
46
78
 
47
79
  context 'via SSL' do
48
80
  setup do
49
- @request = ActionController::TestRequest.new
50
- flexmock(@controller, :request => @request)
51
- flexmock(@controller.request, :ssl? => true)
81
+ @request = test_request
82
+ flexmock(@controller).should_receive(:request => @request).
83
+ by_default
84
+ flexmock(@controller.request).should_receive(:ssl? => true).
85
+ by_default
52
86
  assert @controller.request.ssl?,
53
87
  'Precondition: Request should use SSL'
54
88
  end
55
89
 
56
90
  should 'include multiple files as a SSL bundle' do
57
91
  flexmock(AssetHat, :ssl_asset_host_differs? => true)
58
-
59
92
  bundle = 'css-bundle-1'
60
- output = include_css(:bundle => bundle, :cache => true)
61
- assert_equal(
62
- css_tag("bundles/ssl/#{bundle}.min.css?#{@commit_id}"), output)
93
+ expected_html =
94
+ css_tag("bundles/ssl/#{bundle}.min.css?#{@commit_id}")
95
+ expected_path = AssetHat.bundles_path(:css, :ssl => true) +
96
+ "/#{bundle}.min.css?#{@commit_id}"
97
+
98
+ assert_equal expected_html,
99
+ include_css(:bundle => bundle, :cache => true)
100
+ assert_equal expected_path,
101
+ include_css(:bundle => bundle, :cache => true,
102
+ :only_url => true)
63
103
  end
64
104
 
65
105
  should 'use non-SSL CSS if SSL/non-SSL asset hosts are the same' do
66
106
  flexmock(AssetHat, :ssl_asset_host_differs? => false)
67
-
68
107
  bundle = 'css-bundle-1'
69
- output = include_css(:bundle => bundle, :cache => true)
70
- assert_equal(
71
- css_tag("bundles/#{bundle}.min.css?#{@commit_id}"), output)
108
+ expected_html = css_tag("bundles/#{bundle}.min.css?#{@commit_id}")
109
+ expected_path = AssetHat.bundles_path(:css) +
110
+ "/#{bundle}.min.css?#{@commit_id}"
111
+
112
+ assert_equal expected_html,
113
+ include_css(:bundle => bundle, :cache => true)
114
+ assert_equal expected_path,
115
+ include_css(:bundle => bundle, :cache => true,
116
+ :only_url => true)
72
117
  end
73
118
  end # context 'via SSL'
74
119
 
@@ -77,8 +122,11 @@ class AssetHatHelperTest < ActionView::TestCase
77
122
  context 'without minified versions' do
78
123
  should 'include one file by name, and ' +
79
124
  'automatically use original version' do
80
- output = include_css('foo')
81
- assert_equal css_tag('foo.css'), output
125
+ expected_html = css_tag('foo.css')
126
+ expected_path = AssetHat.assets_path(:css) + '/foo.css'
127
+
128
+ assert_equal expected_html, include_css('foo')
129
+ assert_equal expected_path, include_css('foo', :only_url => true)
82
130
  end
83
131
  end # context 'without minified versions'
84
132
  end # context 'with caching enabled'
@@ -86,20 +134,35 @@ class AssetHatHelperTest < ActionView::TestCase
86
134
  context 'with caching disabled' do
87
135
  should 'include one file by name, and ' +
88
136
  'automatically use original version' do
89
- output = include_css('foo', :cache => false)
90
- assert_equal css_tag('foo.css'), output
137
+ expected_html = css_tag('foo.css')
138
+ expected_path = AssetHat.assets_path(:css) + '/foo.css'
139
+
140
+ assert_equal expected_html, include_css('foo', :cache => false)
141
+ assert_equal expected_path, include_css('foo', :cache => false,
142
+ :only_url => true)
91
143
  end
92
144
 
93
145
  should 'include one unminified file by name and extension' do
94
- output = include_css('foo.css', :cache => false)
95
- assert_equal css_tag('foo.css'), output
146
+ expected_html = css_tag('foo.css')
147
+ expected_path = AssetHat.assets_path(:css) + '/foo.css'
148
+
149
+ assert_equal expected_html, include_css('foo.css', :cache => false)
150
+ assert_equal expected_path, include_css('foo.css', :cache => false,
151
+ :only_url => true)
96
152
  end
97
153
 
98
154
  should 'include multiple files by name' do
99
- expected = %w[foo bar.min].
100
- map { |src| css_tag("#{src}.css") }.join("\n")
101
- output = include_css('foo', 'bar.min', :cache => false)
102
- assert_equal expected, output
155
+ sources = %w[foo bar.min]
156
+ expected_html =
157
+ sources.map { |source| css_tag("#{source}.css") }.join("\n")
158
+ expected_paths = sources.map do |source|
159
+ AssetHat.assets_path(:css) + "/#{source}.css"
160
+ end
161
+
162
+ assert_equal expected_html,
163
+ include_css('foo', 'bar.min', :cache => false)
164
+ assert_equal expected_paths,
165
+ include_css('foo', 'bar.min', :cache => false, :only_url => true)
103
166
  end
104
167
 
105
168
  context 'with real bundle files' do
@@ -111,31 +174,70 @@ class AssetHatHelperTest < ActionView::TestCase
111
174
 
112
175
  should 'include a bundle as separate files' do
113
176
  bundle = 'css-bundle-1'
114
- expected = @config['css']['bundles'][bundle].map do |source|
177
+ bundle_filenames = @config['css']['bundles'][bundle]
178
+ expected_html = bundle_filenames.map do |source|
115
179
  css_tag("#{source}.css?#{@asset_id}")
116
180
  end.join("\n")
117
- output = include_css(:bundle => bundle, :cache => false)
118
- assert_equal expected, output
181
+ expected_paths = bundle_filenames.map do |source|
182
+ AssetHat.assets_path(:css) + "/#{source}.css?#{@asset_id}"
183
+ end
184
+
185
+ assert_equal expected_html,
186
+ include_css(:bundle => bundle, :cache => false)
187
+ assert_equal expected_paths,
188
+ include_css(:bundle => bundle, :cache => false, :only_url => true)
119
189
  end
120
190
 
121
191
  should 'include a bundle as separate files ' +
122
192
  'with a symbol bundle name' do
123
193
  bundle = 'css-bundle-1'
124
- expected = @config['css']['bundles'][bundle].map do |source|
194
+ expected = @config['css']['bundles'][bundle].map { |source|
125
195
  css_tag("#{source}.css?#{@asset_id}")
126
- end.join("\n")
196
+ }.join("\n")
127
197
  output = include_css(:bundle => bundle.to_sym, :cache => false)
128
198
  assert_equal expected, output
129
199
  end
130
200
 
131
201
  should 'include multiple bundles as separate files' do
132
- bundles = [1,2,3].map { |i| "css-bundle-#{i}" }
133
- expected = bundles.map do |bundle|
202
+ bundles = [1,2].map { |i| "css-bundle-#{i}" }
203
+ expected_html = bundles.map { |bundle|
134
204
  sources = @config['css']['bundles'][bundle]
135
205
  sources.map { |src| css_tag("#{src}.css?#{@asset_id}") }
136
- end.flatten.uniq.join("\n")
137
- output = include_css(:bundles => bundles, :cache => false)
138
- assert_equal expected, output
206
+ }.flatten.uniq.join("\n")
207
+ expected_paths = bundles.map do |bundle|
208
+ sources = @config['css']['bundles'][bundle]
209
+ sources.map do |src|
210
+ AssetHat.assets_path(:css) + "/#{src}.css?#{@asset_id}"
211
+ end
212
+ end.flatten.uniq
213
+
214
+ assert_equal expected_html,
215
+ include_css(:bundles => bundles, :cache => false)
216
+ assert_equal expected_paths,
217
+ include_css(:bundles => bundles, :cache => false,
218
+ :only_url => true)
219
+ end
220
+
221
+ should 'include named files and bundles together' do
222
+ bundles = ['css-bundle-2']
223
+ expected_html = css_tag("css-file-1-1.css?#{@asset_id}") + "\n" +
224
+ bundles.map do |bundle|
225
+ sources = @config['css']['bundles'][bundle]
226
+ sources.map { |src| css_tag("#{src}.css?#{@asset_id}") }
227
+ end.flatten.uniq.join("\n")
228
+ expected_paths = ["/stylesheets/css-file-1-1.css?#{@asset_id}"] +
229
+ bundles.map do |bundle|
230
+ sources = @config['css']['bundles'][bundle]
231
+ sources.map do |src|
232
+ AssetHat.assets_path(:css) + "/#{src}.css?#{@asset_id}"
233
+ end
234
+ end.flatten.uniq
235
+
236
+ assert_equal expected_html,
237
+ include_css('css-file-1-1', :bundles => bundles, :cache => false)
238
+ assert_equal expected_paths,
239
+ include_css('css-file-1-1', :bundles => bundles, :cache => false,
240
+ :only_url => true)
139
241
  end
140
242
  end # context 'with real bundle files'
141
243
  end # context 'with caching disabled'
@@ -143,78 +245,123 @@ class AssetHatHelperTest < ActionView::TestCase
143
245
 
144
246
  context 'include_js' do
145
247
  setup do
146
- @request = ActionController::TestRequest.new
147
- flexmock(@controller, :request => @request)
248
+ flexmock_rails_app
249
+ @request = test_request
250
+ flexmock(@controller).should_receive(:request => @request).by_default
148
251
  end
149
252
 
150
253
  context 'with caching enabled' do
151
254
  context 'with minified versions' do
152
255
  setup do
153
256
  @commit_id = '111'
154
- flexmock(AssetHat,
155
- :last_commit_id => @commit_id,
257
+ flexmock(AssetHat).should_receive(
258
+ :last_commit_id => @commit_id,
156
259
  :last_bundle_commit_id => @commit_id
157
- )
260
+ ).by_default
158
261
  end
159
262
 
160
263
  should 'include one file by name, and ' +
161
264
  'automatically use minified version' do
162
265
  flexmock(AssetHat, :asset_exists? => true)
163
- output = include_js('jquery.some-plugin', :cache => true)
164
- assert_equal js_tag("jquery.some-plugin.min.js?#{@commit_id}"),
165
- output
266
+ expected_html = js_tag("jquery.some-plugin.min.js?#{@commit_id}")
267
+ expected_path = AssetHat.assets_path(:js) +
268
+ "/jquery.some-plugin.min.js?#{@commit_id}"
269
+
270
+ assert_equal expected_html,
271
+ include_js('jquery.some-plugin', :cache => true)
272
+ assert_equal expected_path,
273
+ include_js('jquery.some-plugin', :cache => true,
274
+ :only_url => true)
166
275
  end
167
276
 
168
277
  should 'include one unminified file by name and extension' do
169
- output = include_js('jquery.some-plugin.js', :cache => true)
170
- assert_equal js_tag("jquery.some-plugin.js?#{@commit_id}"), output
278
+ filename = 'jquery.some-plugin.js'
279
+ expected_html = js_tag("#{filename}?#{@commit_id}")
280
+ expected_path =
281
+ AssetHat.assets_path(:js) + "/#{filename}?#{@commit_id}"
282
+
283
+ assert_equal expected_html, include_js(filename, :cache => true)
284
+ assert_equal expected_path, include_js(filename, :cache => true,
285
+ :only_url => true)
171
286
  end
172
287
 
173
288
  should 'include one minified file by name and extension' do
174
- output = include_js('jquery.some-plugin.min.js', :cache => true)
175
- assert_equal(
176
- js_tag("jquery.some-plugin.min.js?#{@commit_id}"), output)
289
+ filename = 'jquery.some-plugin.min.js'
290
+ expected_html = js_tag("#{filename}?#{@commit_id}")
291
+ expected_path =
292
+ AssetHat.assets_path(:js) + "/#{filename}?#{@commit_id}"
293
+
294
+ assert_equal expected_html, include_js(filename, :cache => true)
295
+ assert_equal expected_path, include_js(filename, :cache => true,
296
+ :only_url => true)
177
297
  end
178
298
 
179
299
  context 'with vendors' do
180
300
  should 'know where to find each vendor file' do
181
301
  AssetHat::JS::VENDORS.each do |vendor|
182
302
  assert include_js(vendor, :cache => true).present?
303
+ assert include_js(vendor, :cache => true,
304
+ :only_url => true).present?
183
305
  end
184
306
  end
185
307
 
186
- should 'include jQuery and jQuery UI' do
187
- flexmock(AssetHat, :config => @original_config)
308
+ should 'include jQuery and jQuery UI via local vendor files' do
188
309
  [:jquery, :jquery_ui].each do |vendor|
189
- output = include_js(vendor, :cache => true)
190
- assert_equal(
191
- js_tag("#{vendor.to_s.dasherize}.min.js?#{@commit_id}"),
192
- output)
310
+ vendor_filename = "#{vendor.to_s.dasherize}.min.js"
311
+ flexmock(AssetHat).should_receive(:asset_exists?).
312
+ with(vendor_filename, :js).and_return(true)
313
+
314
+ expected_html = js_tag("#{vendor_filename}?#{@commit_id}")
315
+ expected_path = AssetHat.assets_path(:js) +
316
+ "/#{vendor_filename}?#{@commit_id}"
317
+
318
+ assert_equal expected_html, include_js(vendor, :cache => true)
319
+ assert_equal expected_path, include_js(vendor, :cache => true,
320
+ :only_url => true)
193
321
  end
194
322
  end
195
323
 
196
- should 'include Prototype and script.aculo.us' do
324
+ should 'include Prototype and script.aculo.us ' +
325
+ 'via local vendor files' do
197
326
  [:prototype, :scriptaculous].each do |vendor|
198
- output = include_js(vendor, :cache => true)
199
- assert_equal js_tag("#{vendor}.js?#{@commit_id}"), output
327
+ vendor_filename = "#{vendor}.js"
328
+ flexmock(AssetHat).should_receive(:asset_exists?).
329
+ with(vendor_filename, :js).and_return(true)
330
+
331
+ expected_html = js_tag("#{vendor_filename}?#{@commit_id}")
200
332
  # N.B.: Including only the regular, not minified, version
333
+ expected_path = AssetHat.assets_path(:js) +
334
+ "/#{vendor_filename}?#{@commit_id}"
335
+
336
+ assert_equal expected_html, include_js(vendor, :cache => true)
337
+ assert_equal expected_path, include_js(vendor, :cache => true,
338
+ :only_url => true)
201
339
  end
202
340
  end
203
341
 
342
+ should 'not use a remote URL fallback if version is unknown' do
343
+ output = include_js(:jquery, :cache => true)
344
+ assert_equal js_tag("jquery.min.js?#{@commit_id}"), output
345
+ end
346
+
204
347
  context 'with remote requests via SSL' do
205
348
  should 'include vendor JS via Google CDN' do
206
- AssetHat::JS::VENDORS.each do |vendor|
349
+ AssetHat::JS::Vendors::VENDORS_ON_GOOGLE_CDN.each do |vendor|
207
350
  AssetHat.html_cache[:js] = {}
208
351
  helper_opts = {:version => '1', :cache => true}
209
352
 
353
+ # Setup
210
354
  flexmock_teardown
211
- flexmock(AssetHat, :cache? => true)
212
- flexmock(ActionController::Base,
213
- :consider_all_requests_local => false)
355
+ flexmock_rails_app
356
+ flexmock(AssetHat,
357
+ :cache? => true,
358
+ :consider_all_requests_local? => false
359
+ )
360
+
361
+ # Test inclusion via SSL
214
362
  flexmock(@controller.request, :ssl? => true)
215
363
  assert @controller.request.ssl?,
216
364
  'Precondition: Request should use SSL'
217
-
218
365
  https_html = include_js(vendor, helper_opts.dup)
219
366
  assert_match(
220
367
  %r{src="https://ajax\.googleapis\.com/}, https_html)
@@ -223,12 +370,17 @@ class AssetHatHelperTest < ActionView::TestCase
223
370
  AssetHat.html_cache[:js].to_a.first[1],
224
371
  'SSL HTML should be cached'
225
372
 
226
- http_cache_key = AssetHat.html_cache[:js].to_a.first[0]
373
+ # Re-setup
227
374
  flexmock_teardown
228
- flexmock(AssetHat, :cache? => true)
229
- flexmock(ActionController::Base,
230
- :consider_all_requests_local => false)
375
+ flexmock_rails_app
376
+ flexmock(AssetHat,
377
+ :cache? => true,
378
+ :consider_all_requests_local? => false
379
+ )
380
+
381
+ # Test caching of SSL inclusion HTML
231
382
  flexmock(@controller.request, :ssl? => false)
383
+ http_cache_key = AssetHat.html_cache[:js].to_a.first[0]
232
384
  assert !@controller.request.ssl?,
233
385
  'Precondition: Request should not use SSL'
234
386
  assert_equal 1, AssetHat.html_cache[:js].size
@@ -236,7 +388,8 @@ class AssetHatHelperTest < ActionView::TestCase
236
388
  AssetHat.html_cache[:js][http_cache_key],
237
389
  'SSL HTML should be still be cached'
238
390
 
239
- http_html = include_js(vendor, helper_opts)
391
+ # Test inclusion, and caching of inclusion HTML, via non-SSL
392
+ http_html = include_js(vendor, helper_opts.dup)
240
393
  assert_match(
241
394
  %r{src="http://ajax\.googleapis\.com/},
242
395
  http_html,
@@ -251,53 +404,181 @@ class AssetHatHelperTest < ActionView::TestCase
251
404
  'Non-SSL HTML should be cached'
252
405
  end
253
406
  end
407
+
408
+ should 'get vendor URLs pointing to Google CDN' do
409
+ AssetHat::JS::Vendors::VENDORS_ON_GOOGLE_CDN.each do |vendor|
410
+ AssetHat.html_cache[:js] = {}
411
+ helper_opts = {:version => '1', :cache => true}
412
+
413
+ # Setup
414
+ flexmock_teardown
415
+ flexmock_rails_app
416
+ flexmock(AssetHat,
417
+ :cache? => true,
418
+ :consider_all_requests_local? => false
419
+ )
420
+
421
+ # Test SSL URL and URL caching
422
+ flexmock(@controller.request, :ssl? => true)
423
+ assert @controller.request.ssl?,
424
+ 'Precondition: Request should use SSL'
425
+ https_url = include_js(vendor,
426
+ helper_opts.dup.merge(:only_url => true))
427
+ assert_equal 1, AssetHat.html_cache[:js].size
428
+ assert_match %r{^https://ajax\.googleapis\.com/}, https_url
429
+
430
+ # Re-setup
431
+ flexmock_teardown
432
+ flexmock_rails_app
433
+ flexmock(AssetHat,
434
+ :cache? => true,
435
+ :consider_all_requests_local? => false
436
+ )
437
+
438
+ # Test non-SSL URL and URL caching
439
+ flexmock(@controller.request, :ssl? => false)
440
+ assert !@controller.request.ssl?,
441
+ 'Precondition: Request should not use SSL'
442
+ http_url = include_js(vendor,
443
+ helper_opts.dup.merge(:only_url => true))
444
+ assert_equal 2, AssetHat.html_cache[:js].size
445
+ assert_match %r{^http://ajax\.googleapis\.com/}, http_url
446
+ end
447
+ end
254
448
  end # context 'with remote requests via SSL'
255
449
  end # context 'with vendor JS'
256
450
 
257
- should 'include jQuery by version via helper option' do
258
- version = '1.4.1'
259
- output = include_js(:jquery, :version => version, :cache => true)
260
- assert_equal(
261
- js_tag("jquery-#{version}.min.js?#{@commit_id}"), output)
262
- end
263
-
264
- context 'with a mock config' do
451
+ context 'with a mock config containing a version number' do
265
452
  setup do
266
- version = '1.4.1'
453
+ @vendor_version = '1.6.0'
267
454
  config = AssetHat.config
268
455
  config['js']['vendors'] = {
269
- 'jquery' => {
270
- 'version' => version,
271
- 'remote_url' => 'http://example.com/cdn/jquery.min.js',
272
- 'remote_ssl_url' => 'https://secure.example.com/cdn/jquery.min.js'
273
- }
456
+ 'jquery' => {'version' => @vendor_version}
274
457
  }
275
- flexmock(AssetHat, :config => config)
458
+ flexmock(AssetHat).should_receive(:config => config).by_default
276
459
  end
277
460
 
278
- should 'include jQuery by version via config file' do
279
- version = AssetHat.config['js']['vendors']['jquery']['version']
461
+ should 'include local copy of vendor with version in config file' do
462
+ vendor_filename = "jquery-#{@vendor_version}.min.js"
463
+ flexmock(AssetHat).should_receive(:asset_exists?).
464
+ with(vendor_filename, :js).and_return(true)
465
+
466
+ expected_html = js_tag("#{vendor_filename}?#{@commit_id}")
467
+ expected_path =
468
+ AssetHat.assets_path(:js) + "/#{vendor_filename}?#{@commit_id}"
469
+
470
+ assert_equal expected_html, include_js(:jquery, :cache => true)
471
+ assert_equal expected_path, include_js(:jquery, :cache => true,
472
+ :only_url => true)
473
+ end
474
+
475
+ should 'include local copy of vendor with ' +
476
+ 'custom version in helper options' do
477
+ custom_version = '1.3.2'
478
+ vendor_filename = "jquery-#{custom_version}.min.js"
479
+ flexmock(AssetHat).should_receive(:asset_exists?).
480
+ with(vendor_filename, :js).and_return(true)
481
+
280
482
  assert_equal(
281
- js_tag("jquery-#{version}.min.js?#{@commit_id}"),
282
- include_js(:jquery, :cache => true)
283
- )
483
+ js_tag("#{vendor_filename}?#{@commit_id}"),
484
+ include_js(:jquery, :version => custom_version, :cache => true))
284
485
  end
285
486
 
286
- context 'with remote requests' do
487
+ context 'with local requests but no local copy of vendor file' do
287
488
  setup do
288
- flexmock(ActionController::Base,
289
- :consider_all_requests_local => false)
489
+ # Mock for version from config file:
490
+ flexmock(AssetHat).should_receive(:asset_exists?).
491
+ with("jquery-#{@vendor_version}.min.js", :js).
492
+ and_return(false).by_default
493
+
494
+ # Mock for version from helper options:
495
+ @custom_vendor_version = '1.3.2'
496
+ flexmock(AssetHat).should_receive(:asset_exists?).
497
+ with("jquery-#{@custom_vendor_version}.min.js", :js).
498
+ and_return(false).by_default
499
+
500
+ assert AssetHat.consider_all_requests_local?, 'Precondition'
501
+ end
502
+
503
+ should 'fall back to default remote vendor URL ' +
504
+ 'with version in config file' do
505
+ src = "http://ajax.googleapis.com/ajax/libs/jquery/" +
506
+ "#{@vendor_version}/jquery.min.js"
507
+
508
+ assert_equal(
509
+ %{<script src="#{src}" type="text/javascript"></script>},
510
+ include_js(:jquery, :cache => true))
511
+ end
512
+
513
+ should 'fall back to default remote vendor URL ' +
514
+ 'with custom version in helper options' do
515
+ src = "http://ajax.googleapis.com/ajax/libs/jquery/" +
516
+ "#{@custom_vendor_version}/jquery.min.js"
517
+
518
+ assert_equal(
519
+ %{<script src="#{src}" type="text/javascript"></script>},
520
+ include_js(:jquery, :version => @custom_vendor_version,
521
+ :cache => true))
522
+ end
523
+
524
+ should 'fall back to default remote vendor SSL URL ' +
525
+ 'with version in config file' do
526
+ flexmock(@controller.request, :ssl? => true)
527
+ src = "https://ajax.googleapis.com/ajax/libs/jquery/" +
528
+ "#{@vendor_version}/jquery.min.js"
529
+
530
+ assert_equal(
531
+ %{<script src="#{src}" type="text/javascript"></script>},
532
+ include_js(:jquery, :cache => true))
533
+ end
534
+
535
+ should 'fall back to default remote vendor SSL URL ' +
536
+ 'with custom version in helper options' do
537
+ flexmock(@controller.request, :ssl? => true)
538
+ src = "https://ajax.googleapis.com/ajax/libs/jquery/" +
539
+ "#{@custom_vendor_version}/jquery.min.js"
540
+
541
+ assert_equal(
542
+ %{<script src="#{src}" type="text/javascript"></script>},
543
+ include_js(:jquery, :version => @custom_vendor_version,
544
+ :cache => true))
290
545
  end
291
546
 
292
- should 'use specified remote URL for jQuery' do
547
+ end # context 'with local requests but no local copy of vendor file'
548
+ end # context 'with a mock config containing a version number'
549
+
550
+ context 'with a mock config containing custom CDN URLs' do
551
+ setup do
552
+ @vendor_version = '1.6.0'
553
+ config = AssetHat.config
554
+ config['js']['vendors'] = {
555
+ 'jquery' => {
556
+ 'version' => @vendor_version,
557
+ 'remote_url' => 'http://example.com/cdn/' +
558
+ "jquery-#{@vendor_version}.min.js",
559
+ 'remote_ssl_url' => 'https://secure.example.com/cdn/' +
560
+ "jquery-#{@vendor_version}.min.js"
561
+ }
562
+ }
563
+ flexmock(AssetHat).should_receive(:config => config).by_default
564
+ end
565
+
566
+ context 'with local requests but no local copy of vendor file' do
567
+ setup do
568
+ flexmock(AssetHat).should_receive(:asset_exists?).
569
+ with("jquery-#{@vendor_version}.min.js", :js).
570
+ and_return(false).by_default
571
+ assert AssetHat.consider_all_requests_local?, 'Precondition'
572
+ end
573
+
574
+ should 'fall back to configured remote vendor URL' do
293
575
  src = AssetHat.config['js']['vendors']['jquery']['remote_url']
294
576
  assert_equal(
295
577
  %{<script src="#{src}" type="text/javascript"></script>},
296
- include_js(:jquery, :cache => true)
297
- )
578
+ include_js(:jquery, :cache => true))
298
579
  end
299
580
 
300
- should 'use specified remote SSL URL for jQuery' do
581
+ should 'fall back to configured remote vendor SSL URL' do
301
582
  flexmock(@controller.request, :ssl? => true)
302
583
  src =
303
584
  AssetHat.config['js']['vendors']['jquery']['remote_ssl_url']
@@ -307,59 +588,123 @@ class AssetHatHelperTest < ActionView::TestCase
307
588
  include_js(:jquery, :cache => true)
308
589
  )
309
590
  end
591
+ end # context 'with local requests but no local copy of vendor file'
592
+
593
+ context 'with remote requests' do
594
+ setup do
595
+ flexmock(AssetHat).
596
+ should_receive(:consider_all_requests_local? => false)
597
+ assert !AssetHat.consider_all_requests_local?, 'Precondition'
598
+ end
599
+
600
+ should 'use specified remote URL for vendor' do
601
+ src = AssetHat.config['js']['vendors']['jquery']['remote_url']
602
+ expected_html =
603
+ %{<script src="#{src}" type="text/javascript"></script>}
604
+ expected_path = src
605
+
606
+ assert_equal expected_html, include_js(:jquery, :cache => true)
607
+ assert_equal expected_path, include_js(:jquery, :cache => true,
608
+ :only_url => true)
609
+ end
610
+
611
+ should 'use specified remote SSL URL for vendor' do
612
+ flexmock(@controller.request, :ssl? => true)
613
+ src =
614
+ AssetHat.config['js']['vendors']['jquery']['remote_ssl_url']
615
+ expected_html =
616
+ %{<script src="#{src}" type="text/javascript"></script>}
617
+ expected_path = src
618
+
619
+ assert_equal expected_html, include_js(:jquery, :cache => true)
620
+ assert_equal expected_path, include_js(:jquery, :cache => true,
621
+ :only_url => true)
622
+ end
310
623
  end # context 'with remote requests'
311
- end # context 'with a mock config'
624
+
625
+ end # context 'with a mock config containing custom CDN URLs'
312
626
 
313
627
  should 'include multiple files by name' do
314
628
  flexmock(AssetHat, :asset_exists? => true)
315
- expected = %w[foo jquery.bar].map do |source|
629
+ sources = %w[foo jquery.plugin]
630
+ expected_html = sources.map do |source|
316
631
  js_tag("#{source}.min.js?#{@commit_id}")
317
632
  end.join("\n")
318
- output = include_js('foo', 'jquery.bar', :cache => true)
319
- assert_equal expected, output
633
+ expected_paths = sources.map do |source|
634
+ AssetHat.assets_path(:js) + "/#{source}.min.js?#{@commit_id}"
635
+ end
636
+
637
+ assert_equal expected_html,
638
+ include_js('foo', 'jquery.plugin', :cache => true)
639
+ assert_equal expected_paths,
640
+ include_js('foo', 'jquery.plugin', :cache => true,
641
+ :only_url => true)
320
642
  end
321
643
 
322
644
  should 'include multiple files as a bundle' do
323
- bundle = 'js-bundle-1'
324
- output = include_js(:bundle => bundle, :cache => true)
325
- assert_equal(
326
- js_tag("bundles/#{bundle}.min.js?#{@commit_id}"), output)
645
+ bundle = 'js-bundle-1'
646
+ filename = "#{bundle}.min.js"
647
+ expected_html = js_tag("bundles/#{filename}?#{@commit_id}")
648
+ expected_path =
649
+ AssetHat.bundles_path(:js) + "/#{filename}?#{@commit_id}"
650
+
651
+ assert_equal expected_html,
652
+ include_js(:bundle => bundle, :cache => true)
653
+ assert_equal expected_path,
654
+ include_js(:bundle => bundle, :cache => true, :only_url => true)
327
655
  end
328
656
 
329
657
  should 'include multiple bundles' do
330
658
  flexmock(AssetHat, :asset_exists? => true)
331
- expected = %w[foo bar].map do |bundle|
659
+ bundles = %w[foo bar]
660
+ expected_html = bundles.map do |bundle|
332
661
  js_tag("bundles/#{bundle}.min.js?#{@commit_id}")
333
662
  end.join("\n")
334
- output = include_js(:bundles => %w[foo bar], :cache => true)
335
- assert_equal expected, output
663
+ expected_paths = bundles.map do |bundle|
664
+ AssetHat.bundles_path(:js) + "/#{bundle}.min.js?#{@commit_id}"
665
+ end
666
+
667
+ assert_equal expected_html,
668
+ include_js(:bundles => bundles, :cache => true)
669
+ assert_equal expected_paths,
670
+ include_js(:bundles => bundles, :cache => true, :only_url => true)
336
671
  end
337
672
 
338
673
  context 'via SSL' do
339
674
  setup do
340
- @request = ActionController::TestRequest.new
341
- flexmock(@controller, :request => @request)
342
- flexmock(@controller.request, :ssl? => true)
675
+ @request = test_request
676
+ flexmock(@controller).should_receive(:request => @request).
677
+ by_default
678
+ flexmock(@controller.request).should_receive(:ssl? => true).
679
+ by_default
343
680
  assert @controller.request.ssl?,
344
681
  'Precondition: Request should use SSL'
345
682
  end
346
683
 
347
684
  should 'use non-SSL JS if SSL/non-SSL asset hosts differ' do
348
685
  flexmock(AssetHat, :ssl_asset_host_differs? => true)
349
-
350
686
  bundle = 'js-bundle-1'
351
- output = include_js(:bundle => bundle, :cache => true)
352
- assert_equal(
353
- js_tag("bundles/#{bundle}.min.js?#{@commit_id}"), output)
687
+ expected_html = js_tag("bundles/#{bundle}.min.js?#{@commit_id}")
688
+ expected_path =
689
+ AssetHat.bundles_path(:js) + "/#{bundle}.min.js?#{@commit_id}"
690
+
691
+ assert_equal expected_html,
692
+ include_js(:bundle => bundle, :cache => true)
693
+ assert_equal expected_path,
694
+ include_js(:bundle => bundle, :cache => true, :only_url => true)
354
695
  end
355
696
 
356
697
  should 'use non-SSL JS if SSL/non-SSL asset hosts are the same' do
357
698
  flexmock(AssetHat, :ssl_asset_host_differs? => false)
358
-
359
699
  bundle = 'js-bundle-1'
360
- output = include_js(:bundle => bundle, :cache => true)
361
- assert_equal(
362
- js_tag("bundles/#{bundle}.min.js?#{@commit_id}"), output)
700
+ expected_html = js_tag("bundles/#{bundle}.min.js?#{@commit_id}")
701
+ expected_path =
702
+ AssetHat.bundles_path(:js) + "/#{bundle}.min.js?#{@commit_id}"
703
+
704
+ assert_equal expected_html,
705
+ include_js(:bundle => bundle, :cache => true)
706
+ assert_equal expected_path,
707
+ include_js(:bundle => bundle, :cache => true, :only_url => true)
363
708
  end
364
709
  end # context 'via SSL'
365
710
  end # context 'with minified versions'
@@ -367,8 +712,13 @@ class AssetHatHelperTest < ActionView::TestCase
367
712
  context 'without minified versions' do
368
713
  should 'include one file by name, and ' +
369
714
  'automatically use original version' do
370
- output = include_js('jquery.some-plugin', :cache => true)
371
- assert_equal js_tag('jquery.some-plugin.js'), output
715
+ source = 'jquery.some-plugin'
716
+ expected_html = js_tag("#{source}.js")
717
+ expected_path = AssetHat.assets_path(:js) + "/#{source}.js"
718
+
719
+ assert_equal expected_html, include_js(source, :cache => true)
720
+ assert_equal expected_path, include_js(source, :cache => true,
721
+ :only_url => true)
372
722
  end
373
723
  end # context 'without minified versions'
374
724
  end # context 'with caching enabled'
@@ -376,25 +726,48 @@ class AssetHatHelperTest < ActionView::TestCase
376
726
  context 'with caching disabled' do
377
727
  should 'include one file by name, and ' +
378
728
  'automatically use original version' do
379
- output = include_js('foo', :cache => false)
380
- assert_equal js_tag('foo.js'), output
729
+ source = 'foo'
730
+ expected_html = js_tag("#{source}.js")
731
+ expected_path = AssetHat.assets_path(:js) + "/#{source}.js"
732
+
733
+ assert_equal expected_html, include_js(source, :cache => true)
734
+ assert_equal expected_path, include_js(source, :cache => true,
735
+ :only_url => true)
381
736
  end
382
737
 
383
738
  should 'include one unminified file by name and extension' do
384
- output = include_js('foo.js', :cache => false)
385
- assert_equal js_tag('foo.js'), output
739
+ filename = 'foo.js'
740
+ expected_html = js_tag(filename)
741
+ expected_path = AssetHat.assets_path(:js) + "/#{filename}"
742
+
743
+ assert_equal expected_html, include_js(filename, :cache => true)
744
+ assert_equal expected_path, include_js(filename, :cache => true,
745
+ :only_url => true)
386
746
  end
387
747
 
388
748
  should 'include one minified file by name and extension' do
389
- output = include_js('foo.min.js', :cache => false)
390
- assert_equal js_tag('foo.min.js'), output
749
+ filename = 'foo.min.js'
750
+ expected_html = js_tag(filename)
751
+ expected_path = AssetHat.assets_path(:js) + "/#{filename}"
752
+
753
+ assert_equal expected_html, include_js(filename, :cache => true)
754
+ assert_equal expected_path, include_js(filename, :cache => true,
755
+ :only_url => true)
391
756
  end
392
757
 
393
758
  should 'include multiple files by name' do
394
- expected = %w[foo bar.min].
395
- map { |src| js_tag("#{src}.js") }.join("\n")
396
- output = include_js('foo', 'bar.min', :cache => false)
397
- assert_equal expected, output
759
+ sources = %w[foo bar.min]
760
+ expected_html = sources.map do |source|
761
+ js_tag("#{source}.js")
762
+ end.join("\n")
763
+ expected_paths = sources.map do |source|
764
+ AssetHat.assets_path(:js) + "/#{source}.js"
765
+ end
766
+
767
+ assert_equal expected_html,
768
+ include_js('foo', 'bar.min', :cache => false)
769
+ assert_equal expected_paths,
770
+ include_js('foo', 'bar.min', :cache => false, :only_url => true)
398
771
  end
399
772
 
400
773
  context 'with real bundle files' do
@@ -407,16 +780,23 @@ class AssetHatHelperTest < ActionView::TestCase
407
780
  should 'include a bundle as separate files' do
408
781
  bundle = 'js-bundle-1'
409
782
  sources = @config['js']['bundles'][bundle]
410
- expected = sources.
411
- map { |src| js_tag("#{src}.js?#{@asset_id}") }.join("\n")
412
- output = include_js(:bundle => bundle, :cache => false)
413
- assert_equal expected, output
783
+ expected_html = sources.map do |source|
784
+ js_tag("#{source}.js?#{@asset_id}")
785
+ end.join("\n")
786
+ expected_paths = sources.map do |source|
787
+ AssetHat.assets_path(:js) + "/#{source}.js?#{@asset_id}"
788
+ end
789
+
790
+ assert_equal expected_html,
791
+ include_js(:bundle => bundle, :cache => false)
792
+ assert_equal expected_paths,
793
+ include_js(:bundle => bundle, :cache => false, :only_url => true)
414
794
  end
415
795
 
416
796
  should 'include a bundle as separate files ' +
417
797
  'with a symbol bundle name' do
418
- bundle = 'js-bundle-1'
419
- sources = @config['js']['bundles'][bundle]
798
+ bundle = 'js-bundle-1'
799
+ sources = @config['js']['bundles'][bundle]
420
800
  expected = sources.
421
801
  map { |src| js_tag("#{src}.js?#{@asset_id}") }.join("\n")
422
802
  output = include_js(:bundle => bundle.to_sym, :cache => false)
@@ -424,19 +804,194 @@ class AssetHatHelperTest < ActionView::TestCase
424
804
  end
425
805
 
426
806
  should 'include multiple bundles as separate files' do
427
- bundles = [1,2,3].map { |i| "js-bundle-#{i}" }
428
- expected = bundles.map do |bundle|
807
+ bundles = [1,2].map { |i| "js-bundle-#{i}" }
808
+ expected_html = bundles.map { |bundle|
429
809
  sources = @config['js']['bundles'][bundle]
430
810
  sources.map { |src| js_tag("#{src}.js?#{@asset_id}") }
431
- end.flatten.uniq.join("\n")
432
- output = include_js(:bundles => bundles, :cache => false)
433
- assert_equal expected, output
811
+ }.flatten.uniq.join("\n")
812
+ expected_paths = bundles.map do |bundle|
813
+ sources = @config['js']['bundles'][bundle]
814
+ sources.map do |src|
815
+ AssetHat.assets_path(:js) + "/#{src}.js?#{@asset_id}"
816
+ end
817
+ end.flatten.uniq
818
+
819
+ assert_equal expected_html,
820
+ include_js(:bundles => bundles, :cache => false)
821
+ assert_equal expected_paths,
822
+ include_js(:bundles => bundles, :cache => false,
823
+ :only_url => true)
824
+ end
825
+
826
+ should 'include vendors, named files and bundles together' do
827
+ bundles = ['js-bundle-2']
828
+ expected_html =
829
+ js_tag("jquery.min.js?#{@asset_id}") + "\n" +
830
+ js_tag("js-file-1-1.js?#{@asset_id}") + "\n" +
831
+ bundles.map do |bundle|
832
+ sources = @config['js']['bundles'][bundle]
833
+ sources.map { |src| js_tag("#{src}.js?#{@asset_id}") }
834
+ end.flatten.uniq.join("\n")
835
+ expected_paths =
836
+ [ "/javascripts/jquery.min.js?#{@asset_id}",
837
+ "/javascripts/js-file-1-1.js?#{@asset_id}" ] +
838
+ bundles.map do |bundle|
839
+ sources = @config['js']['bundles'][bundle]
840
+ sources.map do |src|
841
+ AssetHat.assets_path(:js) + "/#{src}.js?#{@asset_id}"
842
+ end
843
+ end.flatten.uniq
844
+
845
+ assert_equal expected_html,
846
+ include_js(:jquery, 'js-file-1-1', :bundles => bundles,
847
+ :cache => false)
848
+ assert_equal expected_paths,
849
+ include_js(:jquery, 'js-file-1-1', :bundles => bundles,
850
+ :cache => false, :only_url => true)
434
851
  end
435
852
  end # context 'with real bundle files'
436
853
  end # context 'with caching disabled'
437
854
 
855
+ context 'with LABjs' do
856
+ should 'render with default config and basic URL arguments' do
857
+ urls = [ '/javascripts/foo.js',
858
+ 'http://cdn.example.com/bar.js' ]
859
+ expected = %{<script src="/javascripts/LAB.min.js" } +
860
+ %{type="text/javascript"></script>\n}
861
+ expected << %{<script type="text/javascript">\n}
862
+ expected << %{window.$LABinst=$LAB.\n}
863
+ expected << %{ script('#{urls.first}').wait().\n}
864
+ expected << %{ script('#{urls.second}').wait();\n}
865
+ expected << %{</script>}
866
+
867
+ assert_equal expected,
868
+ include_js('foo', urls.second, :loader => :lab_js)
869
+ end
870
+
871
+ context 'with LABjs version config, vendor, and multiple bundles' do
872
+ setup do
873
+ @config = AssetHat.config
874
+ @config['js']['vendors'] = {
875
+ 'jquery' => {'version' => '1.6.0'},
876
+ 'lab_js' => {'version' => '1.2.0'}
877
+ }
878
+ flexmock(AssetHat).should_receive(:config => @config).by_default
879
+
880
+ @asset_id = ENV['RAILS_ASSET_ID'] = ''
881
+ flexmock(AssetHat).should_receive(
882
+ :asset_exists? => false,
883
+ :last_commit_id => ''
884
+ ).by_default
885
+
886
+ @jquery_version = @config['js']['vendors']['jquery']['version']
887
+ @lab_js_version = @config['js']['vendors']['lab_js']['version']
888
+ end
889
+ teardown { ENV['RAILS_ASSET_ID'] = nil }
890
+
891
+ context 'with local requests' do
892
+ should 'render with caching disabled' do
893
+ loader_filename = "LAB-#{@lab_js_version}.min.js"
894
+ vendor_filename = "jquery-#{@jquery_version}.min.js"
895
+
896
+ flexmock(AssetHat).should_receive(:asset_exists?).
897
+ with(loader_filename, :js).and_return(true)
898
+ flexmock(AssetHat).should_receive(:asset_exists?).
899
+ with(vendor_filename, :js).and_return(true)
900
+ assert AssetHat.asset_exists?(vendor_filename, :js),
901
+ 'Precondition'
902
+
903
+ expected = %{<script src="/javascripts/#{loader_filename}" } +
904
+ %{type="text/javascript"></script>\n}
905
+ expected << %{<script type="text/javascript">\n}
906
+ expected << "window.$LABinst=$LAB.\n"
907
+ expected << " script('/javascripts/#{vendor_filename}').wait().\n"
908
+ expected << " script('/javascripts/foo.js').wait().\n"
909
+ expected << " script('/javascripts/js-file-1-1.js').wait().\n"
910
+ expected << " script('/javascripts/js-file-1-2.js').wait().\n"
911
+ expected << " script('/javascripts/js-file-1-3.js').wait().\n"
912
+ expected << " script('/javascripts/js-file-2-1.js').wait().\n"
913
+ expected << " script('/javascripts/js-file-2-2.js').wait().\n"
914
+ expected << " script('/javascripts/js-file-2-3.js').wait();\n"
915
+ expected << '</script>'
916
+
917
+ assert_equal expected, include_js(:jquery, 'foo',
918
+ :bundles => %w[js-bundle-1 js-bundle-2],
919
+ :loader => :lab_js)
920
+ end
921
+
922
+ should 'render with caching disabled and remote vendor ' +
923
+ 'if local loader vendor is missing' do
924
+ loader_filename = "LAB-#{@lab_js_version}.min.js"
925
+ vendor_filename = "jquery-#{@jquery_version}.min.js"
926
+ loader_url = 'http://ajax.cdnjs.com/ajax/libs/labjs/' +
927
+ @lab_js_version + '/LAB.min.js'
928
+
929
+ flexmock(AssetHat).should_receive(:asset_exists?).
930
+ with(loader_filename, :js).and_return(false)
931
+ flexmock(AssetHat).should_receive(:asset_exists?).
932
+ with(vendor_filename, :js).and_return(true)
933
+ assert AssetHat.asset_exists?(vendor_filename, :js),
934
+ 'Precondition'
935
+
936
+ expected = %{<script src="#{loader_url}" } +
937
+ %{type="text/javascript"></script>\n}
938
+ expected << %{<script type="text/javascript">\n}
939
+ expected << "window.$LABinst=$LAB.\n"
940
+ expected << " script('/javascripts/#{vendor_filename}').wait().\n"
941
+ expected << " script('/javascripts/foo.js').wait();\n"
942
+ expected << '</script>'
943
+
944
+ assert_equal expected,
945
+ include_js(:jquery, 'foo', :loader => :lab_js)
946
+ end
947
+ end # context 'with local requests'
948
+
949
+ context 'with remote requests' do
950
+ setup do
951
+ flexmock(AssetHat, :consider_all_requests_local? => false)
952
+ end
953
+
954
+ should 'render with caching enabled and remote vendors' do
955
+ lab_js_url = 'http://ajax.cdnjs.com/ajax/libs/labjs/' +
956
+ @lab_js_version + '/LAB.min.js'
957
+ jquery_url = 'http://ajax.googleapis.com/ajax/libs/jquery/' +
958
+ @jquery_version + '/jquery.min.js'
959
+
960
+ expected = %{<script src="#{lab_js_url}" } +
961
+ %{type="text/javascript"></script>\n}
962
+ expected << %{<script type="text/javascript">\n}
963
+ expected << "window.$LABinst=$LAB.\n"
964
+ expected << " script('#{jquery_url}').wait().\n"
965
+ expected << " script('/javascripts/foo.js').wait().\n"
966
+ expected << " script('/javascripts/" +
967
+ "bundles/js-bundle-1.min.js').wait().\n"
968
+ expected << " script('/javascripts/" +
969
+ "bundles/js-bundle-2.min.js').wait();\n"
970
+ expected << '</script>'
971
+
972
+ assert_equal expected, include_js(:jquery, 'foo',
973
+ :bundles => %w[js-bundle-1 js-bundle-2],
974
+ :loader => :lab_js,
975
+ :cache => true)
976
+ end
977
+ end # context 'with remote requests'
978
+ end # context 'with LABjs version config, vendor, and multiple bundles'
979
+ end # context 'with LABjs'
980
+
438
981
  end # context 'include_js'
439
982
 
983
+ should 'compute public asset paths' do
984
+ flexmock_rails_app
985
+
986
+ assert_equal '/stylesheets/foo.css', asset_path(:css, 'foo')
987
+ assert_equal '/stylesheets/bundles/foo.min.css',
988
+ asset_path(:css, 'bundles/foo.min.css')
989
+
990
+ assert_equal '/javascripts/foo.js', asset_path(:js, 'foo')
991
+ assert_equal '/javascripts/bundles/foo.min.js',
992
+ asset_path(:js, 'bundles/foo.min.js')
993
+ end
994
+
440
995
 
441
996
 
442
997
  private
@@ -452,4 +1007,32 @@ class AssetHatHelperTest < ActionView::TestCase
452
1007
  %{<script src="/javascripts/#{filename}" type="text/javascript"></script>}
453
1008
  end
454
1009
 
1010
+ def flexmock_rails_app
1011
+ # Creates just enough hooks for a dummy Rails app.
1012
+ flexmock(Rails,
1013
+ :application => flexmock('dummy_app', :env_defaults => {}),
1014
+ :logger => flexmock('dummy_logger', :warn => nil)
1015
+ )
1016
+
1017
+ if defined?(config) # Rails 3.x
1018
+ config.assets_dir = AssetHat::ASSETS_DIR
1019
+ end
1020
+
1021
+ flexmock(AssetHat).should_receive(:consider_all_requests_local? => true).
1022
+ by_default
1023
+ end
1024
+
1025
+ # def flexmock_rails_app_config(opts)
1026
+ # flexmock(Rails.application.config, opts) # Rails 3.x
1027
+ # flexmock(ActionController::Base, opts) # Rails 2.x
1028
+ # end
1029
+
1030
+ def test_request
1031
+ if defined?(ActionDispatch) # Rails 3.x
1032
+ ActionDispatch::TestRequest.new
1033
+ else # Rails 2.x
1034
+ ActionController::TestRequest.new
1035
+ end
1036
+ end
1037
+
455
1038
  end