asset_hat 0.1.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 (45) hide show
  1. data/.gitignore +1 -0
  2. data/HISTORY +12 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +153 -0
  5. data/Rakefile +22 -0
  6. data/VERSION.yml +5 -0
  7. data/app/helpers/asset_hat_helper.rb +171 -0
  8. data/asset_hat.gemspec +98 -0
  9. data/config/assets.yml +59 -0
  10. data/lib/asset_hat.rb +85 -0
  11. data/lib/asset_hat/css.rb +76 -0
  12. data/lib/asset_hat/js.rb +86 -0
  13. data/lib/asset_hat/tasks.rb +296 -0
  14. data/lib/asset_hat/vcs.rb +65 -0
  15. data/lib/asset_hat/version.rb +11 -0
  16. data/public/javascripts/bundles/js-bundle-1.min.js +6 -0
  17. data/public/javascripts/bundles/js-bundle-2.min.js +6 -0
  18. data/public/javascripts/bundles/js-bundle-3.min.js +6 -0
  19. data/public/javascripts/js-file-1-1.js +1 -0
  20. data/public/javascripts/js-file-1-2.js +1 -0
  21. data/public/javascripts/js-file-1-3.js +1 -0
  22. data/public/javascripts/js-file-2-1.js +1 -0
  23. data/public/javascripts/js-file-2-2.js +1 -0
  24. data/public/javascripts/js-file-2-3.js +1 -0
  25. data/public/javascripts/js-file-3-1.js +1 -0
  26. data/public/javascripts/js-file-3-2.js +1 -0
  27. data/public/javascripts/js-file-3-3.js +1 -0
  28. data/public/stylesheets/bundles/css-bundle-1.min.css +3 -0
  29. data/public/stylesheets/bundles/css-bundle-2.min.css +3 -0
  30. data/public/stylesheets/bundles/css-bundle-3.min.css +3 -0
  31. data/public/stylesheets/css-file-1-1.css +1 -0
  32. data/public/stylesheets/css-file-1-2.css +1 -0
  33. data/public/stylesheets/css-file-1-3.css +1 -0
  34. data/public/stylesheets/css-file-2-1.css +1 -0
  35. data/public/stylesheets/css-file-2-2.css +1 -0
  36. data/public/stylesheets/css-file-2-3.css +1 -0
  37. data/public/stylesheets/css-file-3-1.css +1 -0
  38. data/public/stylesheets/css-file-3-2.css +1 -0
  39. data/public/stylesheets/css-file-3-3.css +1 -0
  40. data/rails/init.rb +17 -0
  41. data/tasks/asset_hat.rake +1 -0
  42. data/test/asset_hat_helper_test.rb +237 -0
  43. data/test/asset_hat_test.rb +46 -0
  44. data/test/test_helper.rb +23 -0
  45. metadata +141 -0
@@ -0,0 +1,65 @@
1
+ module AssetHat
2
+ def self.last_commit_id(*args)
3
+ # Usage:
4
+ #
5
+ # AssetHat.last_commit_id('public/stylesheets/application.css')
6
+ # AssetHat.last_commit_id('public/stylesheets/ie.css',
7
+ # 'public/stylesheets/ie7.css',
8
+ # 'public/stylesheets/ie6.css')
9
+ #
10
+ # Returns a string of the commit ID for the file with the most recent
11
+ # commit. If the file(s) cannot be found, `nil` is returned.
12
+
13
+ # Process arguments
14
+ options = args.extract_options!
15
+ options = options.symbolize_keys.reverse_merge(:vcs => :git)
16
+ filepaths = args.join(' ')
17
+
18
+ # Validate options
19
+ if options[:vcs] != :git
20
+ raise 'Git is currently the only supported VCS.' and return
21
+ end
22
+
23
+ @@last_commit_ids ||= {}
24
+ if @@last_commit_ids[filepaths].blank?
25
+ h = `git log -1 --pretty=format:%h #{filepaths} 2>/dev/null`
26
+ # `h` has either the abbreviated Git commit hash or an empty string
27
+ @@last_commit_ids[filepaths] = h if h.present?
28
+ end
29
+ @@last_commit_ids[filepaths]
30
+ end
31
+
32
+ def self.last_bundle_commit_id(bundle, type)
33
+ # Usage:
34
+ #
35
+ # AssetHat.last_bundle_commit_id('application', :css)
36
+ #
37
+ # Returns a string of the latest commit ID for the given bundle, based
38
+ # on which of its files were most recently modified in the repository. If
39
+ # no ID can be found, `nil` is returned.
40
+
41
+ # Process arguments
42
+ type = type.to_sym
43
+ unless TYPES.include?(type)
44
+ raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}."
45
+ return
46
+ end
47
+
48
+ # Default to `{:css => {}, :js => {}}`
49
+ @@last_bundle_commit_ids ||=
50
+ TYPES.inject({}) { |hsh, t| hsh.merge(t => {}) }
51
+
52
+ if @@last_bundle_commit_ids[type][bundle].blank?
53
+ dir = self.assets_dir(type)
54
+ filepaths = self.bundle_filepaths(bundle, type)
55
+ if filepaths.present?
56
+ @@last_bundle_commit_ids[type][bundle] = self.last_commit_id(*filepaths)
57
+ end
58
+ end
59
+
60
+ @@last_bundle_commit_ids[type][bundle]
61
+ end
62
+
63
+ def self.last_commit_ids ; @@last_commit_ids ; end
64
+
65
+ end
@@ -0,0 +1,11 @@
1
+ module AssetHat
2
+ def self.version
3
+ data_filepath = File.join(File.dirname(__FILE__), %w[.. .. VERSION.yml])
4
+ data = YAML.load(File.open(data_filepath, 'r'))
5
+ [:major, :minor, :patch, :build].
6
+ map { |x| data[x] }.reject(&:blank?).join('.')
7
+ end
8
+
9
+ VERSION = self.version
10
+
11
+ end
@@ -0,0 +1,6 @@
1
+ /* [placeholder] */
2
+
3
+ /* [placeholder] */
4
+
5
+ /* [placeholder] */
6
+
@@ -0,0 +1,6 @@
1
+ /* [placeholder] */
2
+
3
+ /* [placeholder] */
4
+
5
+ /* [placeholder] */
6
+
@@ -0,0 +1,6 @@
1
+ /* [placeholder] */
2
+
3
+ /* [placeholder] */
4
+
5
+ /* [placeholder] */
6
+
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1,3 @@
1
+ /* [placeholder] */
2
+ /* [placeholder] */
3
+ /* [placeholder] */
@@ -0,0 +1,3 @@
1
+ /* [placeholder] */
2
+ /* [placeholder] */
3
+ /* [placeholder] */
@@ -0,0 +1,3 @@
1
+ /* [placeholder] */
2
+ /* [placeholder] */
3
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1 @@
1
+ /* [placeholder] */
@@ -0,0 +1,17 @@
1
+ ::ActionView::Base.send(:include, AssetHatHelper)
2
+
3
+ # Precalculate (and memoize) asset commit IDs
4
+ AssetHat::TYPES.each do |type|
5
+ next if AssetHat.config[type.to_s].blank? ||
6
+ AssetHat.config[type.to_s]['bundles'].blank?
7
+
8
+ AssetHat.config[type.to_s]['bundles'].keys.each do |bundle|
9
+ # Memoize commit ID for this bundle
10
+ AssetHat.last_bundle_commit_id(bundle, type) if AssetHat.cache?
11
+
12
+ # Memoize commit IDs for each file in this bundle
13
+ AssetHat.bundle_filepaths(bundle, type).each do |filepath|
14
+ AssetHat.last_commit_id(filepath)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. lib asset_hat tasks])
@@ -0,0 +1,237 @@
1
+ require 'test_helper'
2
+
3
+ class AssetHatHelperTest < ActionView::TestCase
4
+ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5
+
6
+ context 'include_css' do
7
+ context 'with caching enabled' do
8
+ context 'with minified versions' do
9
+ setup do
10
+ @commit_id = '111'
11
+ flexmock(AssetHat).should_receive(:last_commit_id => @commit_id)
12
+ end
13
+
14
+ should 'include one file by name, and automatically use minified version' do
15
+ flexmock(AssetHat).should_receive(:asset_exists?).and_return(true)
16
+ output = include_css('foo', :cache => true)
17
+ assert_equal css_tag("foo.min.css?#{@commit_id}"), output
18
+ end
19
+
20
+ should 'include one unminified file by name and extension' do
21
+ output = include_css('foo.css', :cache => true)
22
+ assert_equal css_tag("foo.css?#{@commit_id}"), output
23
+ end
24
+
25
+ should 'include one minified file by name and extension' do
26
+ output = include_css('foo.min.css', :cache => true)
27
+ assert_equal css_tag("foo.min.css?#{@commit_id}"), output
28
+ end
29
+
30
+ should 'include multiple files by name' do
31
+ flexmock(AssetHat).should_receive(:asset_exists?).and_return(true)
32
+ expected = %w[foo bar].map do |source|
33
+ css_tag("#{source}.min.css?#{@commit_id}")
34
+ end.join("\n")
35
+ output = include_css('foo', 'bar', :cache => true)
36
+ assert_equal expected, output
37
+ end
38
+
39
+ should 'include multiple files as a bundle' do
40
+ bundle = 'css-bundle-1'
41
+ output = include_css(:bundle => bundle, :cache => true)
42
+ assert_equal css_tag("bundles/#{bundle}.min.css?#{@commit_id}"), output
43
+ end
44
+ end # context 'with minified versions'
45
+
46
+ context 'without minified versions' do
47
+ should 'include one file by name, and automatically use original version' do
48
+ output = include_css('foo')
49
+ assert_equal css_tag('foo.css'), output
50
+ end
51
+ end # context 'without minified versions'
52
+ end # context 'with caching enabled'
53
+
54
+ context 'with caching disabled' do
55
+ should 'include one file by name, and automatically use original version' do
56
+ output = include_css('foo', :cache => false)
57
+ assert_equal css_tag('foo.css'), output
58
+ end
59
+
60
+ should 'include one unminified file by name and extension' do
61
+ output = include_css('foo.css', :cache => false)
62
+ assert_equal css_tag('foo.css'), output
63
+ end
64
+
65
+ should 'include multiple files by name' do
66
+ expected = %w[foo bar.min].map { |src| css_tag("#{src}.css") }.join("\n")
67
+ output = include_css('foo', 'bar.min', :cache => false)
68
+ assert_equal expected, output
69
+ end
70
+
71
+ context 'with real bundle files' do
72
+ setup do
73
+ @asset_id = ENV['RAILS_ASSET_ID'] = '222'
74
+ @config = AssetHat.config
75
+ end
76
+ teardown { ENV['RAILS_ASSET_ID'] = nil }
77
+
78
+ should 'include a bundle as separate files' do
79
+ bundle = 'css-bundle-1'
80
+ expected = @config['css']['bundles'][bundle].map do |source|
81
+ css_tag("#{source}.css?#{@asset_id}")
82
+ end.join("\n")
83
+ output = include_css(:bundle => bundle, :cache => false)
84
+ assert_equal expected, output
85
+ end
86
+
87
+ should 'include multiple bundles as separate files' do
88
+ bundles = [1,2,3].map { |i| "css-bundle-#{i}" }
89
+ expected = bundles.map do |bundle|
90
+ sources = @config['css']['bundles'][bundle]
91
+ sources.map { |src| css_tag("#{src}.css?#{@asset_id}") }
92
+ end.flatten.uniq.join("\n")
93
+ output = include_css(:bundles => bundles, :cache => false)
94
+ assert_equal expected, output
95
+ end
96
+ end # context 'with real bundle files'
97
+ end # context 'with caching disabled'
98
+ end # context 'include_css'
99
+
100
+ context 'include_js' do
101
+ context 'with caching enabled' do
102
+ context 'with minified versions' do
103
+ setup do
104
+ @commit_id = '111'
105
+ flexmock(AssetHat).should_receive(
106
+ :last_commit_id => @commit_id,
107
+ :last_bundle_commit_id => @commit_id
108
+ )
109
+ end
110
+
111
+ should 'include one file by name, and automatically use minified version' do
112
+ flexmock(AssetHat).should_receive(:asset_exists?).and_return(true)
113
+ output = include_js('jquery.some-plugin', :cache => true)
114
+ assert_equal js_tag("jquery.some-plugin.min.js?#{@commit_id}"), output
115
+ end
116
+
117
+ should 'include one unminified file by name and extension' do
118
+ output = include_js('jquery.some-plugin.js', :cache => true)
119
+ assert_equal js_tag("jquery.some-plugin.js?#{@commit_id}"), output
120
+ end
121
+
122
+ should 'include one minified file by name and extension' do
123
+ output = include_js('jquery.some-plugin.min.js', :cache => true)
124
+ assert_equal js_tag("jquery.some-plugin.min.js?#{@commit_id}"), output
125
+ end
126
+
127
+ should 'include jQuery' do
128
+ version = AssetHat::JS::Vendors::JQUERY_DEFAULT_VERSION
129
+ output = include_js(:jquery, :cache => true)
130
+ assert_equal(
131
+ js_tag("jquery-#{version}.min.js?#{@commit_id}"), output)
132
+ end
133
+
134
+ should 'include jQuery by version' do
135
+ version = '1.3.2'
136
+ output = include_js(:jquery, :version => version, :cache => true)
137
+ assert_equal(
138
+ js_tag("jquery-#{version}.min.js?#{@commit_id}"), output)
139
+ end
140
+
141
+ should 'include multiple files by name' do
142
+ flexmock(AssetHat).should_receive(:asset_exists?).and_return(true)
143
+ expected = %w[foo jquery.bar].map do |source|
144
+ js_tag("#{source}.min.js?#{@commit_id}")
145
+ end.join("\n")
146
+ output = include_js('foo', 'jquery.bar', :cache => true)
147
+ assert_equal expected, output
148
+ end
149
+
150
+ should 'include multiple files as a bundle' do
151
+ bundle = 'js-bundle-1'
152
+ output = include_js(:bundle => bundle, :cache => true)
153
+ assert_equal js_tag("bundles/#{bundle}.min.js?#{@commit_id}"), output
154
+ end
155
+
156
+ should 'include multiple bundles' do
157
+ flexmock(AssetHat).should_receive(:asset_exists?).and_return(true)
158
+ expected = %w[foo bar].map do |bundle|
159
+ js_tag("bundles/#{bundle}.min.js?#{@commit_id}")
160
+ end.join("\n")
161
+ output = include_js(:bundles => %w[foo bar], :cache => true)
162
+ assert_equal expected, output
163
+ end
164
+ end # context 'with minified versions'
165
+
166
+ context 'without minified versions' do
167
+ should 'include one file by name, and automatically use original version' do
168
+ output = include_js('jquery.some-plugin', :cache => true)
169
+ assert_equal js_tag('jquery.some-plugin.js'), output
170
+ end
171
+ end # context 'without minified versions'
172
+ end # context 'with caching enabled'
173
+
174
+ context 'with caching disabled' do
175
+ should 'include one file by name, and automatically use original version' do
176
+ output = include_js('foo', :cache => false)
177
+ assert_equal js_tag('foo.js'), output
178
+ end
179
+
180
+ should 'include one unminified file by name and extension' do
181
+ output = include_js('foo.js', :cache => false)
182
+ assert_equal js_tag('foo.js'), output
183
+ end
184
+
185
+ should 'include one minified file by name and extension' do
186
+ output = include_js('foo.min.js', :cache => false)
187
+ assert_equal js_tag('foo.min.js'), output
188
+ end
189
+
190
+ should 'include multiple files by name' do
191
+ expected = %w[foo bar.min].map { |src| js_tag("#{src}.js") }.join("\n")
192
+ output = include_js('foo', 'bar.min', :cache => false)
193
+ assert_equal expected, output
194
+ end
195
+
196
+ context 'with real bundle files' do
197
+ setup do
198
+ @asset_id = ENV['RAILS_ASSET_ID'] = '222'
199
+ @config = AssetHat.config
200
+ end
201
+ teardown { ENV['RAILS_ASSET_ID'] = nil }
202
+
203
+ should 'include a bundle as separate files' do
204
+ bundle = 'js-bundle-1'
205
+ sources = @config['js']['bundles'][bundle]
206
+ expected = sources.map { |src| js_tag("#{src}.js?#{@asset_id}") }.join("\n")
207
+ output = include_js(:bundle => bundle, :cache => false)
208
+ assert_equal expected, output
209
+ end
210
+
211
+ should 'include multiple bundles as separate files' do
212
+ bundles = [1,2,3].map { |i| "js-bundle-#{i}" }
213
+ expected = bundles.map do |bundle|
214
+ sources = @config['js']['bundles'][bundle]
215
+ sources.map { |src| js_tag("#{src}.js?#{@asset_id}") }
216
+ end.flatten.uniq.join("\n")
217
+ output = include_js(:bundles => bundles, :cache => false)
218
+ assert_equal expected, output
219
+ end
220
+ end # context 'with real bundle files'
221
+ end # context 'with caching disabled'
222
+
223
+ end # context 'include_js'
224
+
225
+
226
+
227
+ private
228
+
229
+ def css_tag(filename)
230
+ %Q{<link href="/stylesheets/#{filename}" media="screen,projection" rel="stylesheet" type="text/css" />}
231
+ end
232
+
233
+ def js_tag(filename)
234
+ %Q{<script src="/javascripts/#{filename}" type="text/javascript"></script>}
235
+ end
236
+
237
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class AssetHatTest < ActiveSupport::TestCase
4
+ context 'AssetHat::CSS' do
5
+ should 'return path to minified file' do
6
+ assert_equal 'foo/bar/baz.min.css',
7
+ AssetHat::CSS.min_filepath('foo/bar/baz.css')
8
+ end
9
+
10
+ should 'add asset commit IDs' do
11
+ commit_id = 111
12
+ flexmock(AssetHat).should_receive(:last_commit_id => commit_id)
13
+ flexmock(Rails).should_receive(:public_path => '')
14
+
15
+ assert_equal "p{background:url(/images/foo.png?#{commit_id})}",
16
+ AssetHat::CSS.add_asset_commit_ids(
17
+ 'p{background:url(/images/foo.png)}')
18
+ end
19
+
20
+ should 'add asset hosts' do
21
+ asset_host = 'http://media%d.example.com'
22
+ assert_match(
23
+ /^p\{background:url\(http:\/\/media[\d]\.example\.com\/images\/foo.png\)\}$/,
24
+ AssetHat::CSS.add_asset_hosts(
25
+ 'p{background:url(/images/foo.png)}', asset_host)
26
+ )
27
+ end
28
+ end # context 'AssetHat::CSS'
29
+
30
+ context 'AssetHat::JS' do
31
+ should 'return path to minified file' do
32
+ assert_equal 'foo/bar/baz.min.js',
33
+ AssetHat::JS.min_filepath('foo/bar/baz.js')
34
+ end
35
+ end # context 'AssetHat::JS'
36
+
37
+ should "return a bundle's filenames" do
38
+ assert_equal %w[css-file-1-1 css-file-1-2 css-file-1-3],
39
+ AssetHat.bundle_filenames('css-bundle-1', :css)
40
+ end
41
+
42
+ should "return a bundle's filepaths" do
43
+ expected = [1,2,3].map { |i| "public/stylesheets/css-file-1-#{i}.css" }
44
+ assert_equal expected, AssetHat.bundle_filepaths('css-bundle-1', :css)
45
+ end
46
+ end