asset_bundler 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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ public/*
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ AssetBundler
2
+ ============
3
+
4
+ A simple asset bundling solution for Sinatra/Rack apps. Adapted from asset bundling functionality in ActionPack.
5
+
6
+ Allows you to cache multiple javascripts or stylesheets into one file, so that scripts/styles can be downloaded
7
+ with fewer HTTP connections. Bundling will only happen when ENV['RACK_ENV'] is "production" or "staging" (other
8
+ bundling environments can be added via AssetBundler.bundle_environments).
9
+
10
+ Last modified timestamps are added as a param to outputted script/style sources, so that far future expires headers
11
+ can be leveraged. The [asset_timestamps_cache](http://github.com/gbuesing/asset_timestamps_cache) gem is a required dependency.
12
+
13
+
14
+ ERB Examples:
15
+
16
+ <= javascript_include_tag "/scripts/one.js", "/scripts/two.js", :cache => "/scripts/all.js" %>
17
+
18
+ <= stylesheet_link_tag "/styles/one.css", "/styles/two.css", :cache => "/styles/all.css" %>
19
+
20
+
21
+ output the following tags:
22
+
23
+ <script type="text/javascript" src="/scripts/all.js?1267564623"></script>
24
+
25
+ <link rel="stylesheet" href="/styles/all.css?1267564678" type="text/css">
26
+
27
+
28
+ and creates the following files on first invocation (in the project root):
29
+
30
+ "public/scripts/all.js"
31
+
32
+ "public/styles/all.css"
33
+
34
+
35
+ Sinatra setup example:
36
+
37
+ class MyApp < Sinatra::Base
38
+ helper AssetBundler::ViewHelper
39
+
40
+ # etc...
41
+ end
42
+
43
+
44
+ Adding additional bundling environments:
45
+
46
+ AssetBundler.bundle_environments << "staging2"
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/test*.rb']
6
+ t.verbose = true
7
+ end
8
+ task :default => :test
9
+
10
+ begin
11
+ require 'jeweler'
12
+ Jeweler::Tasks.new do |gemspec|
13
+ gemspec.name = "asset_bundler"
14
+ gemspec.summary = "A simple asset bundling solution."
15
+ gemspec.description = "Adapted from asset bundling functionality in ActionPack."
16
+ gemspec.email = "gbuesing@gmail.com"
17
+ gemspec.homepage = "http://github.com/gbuesing/asset_bundler"
18
+ gemspec.authors = ["Geoff Buesing"]
19
+ gemspec.add_dependency('asset_timestamps_cache', '>= 0.1.1')
20
+ end
21
+ rescue LoadError
22
+ puts "Jeweler not available. Install it with: gem install jeweler"
23
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,73 @@
1
+ require 'asset_timestamps_cache'
2
+ require 'fileutils'
3
+
4
+ module AssetBundler
5
+
6
+ # Mix this in to view context for convenient access
7
+ module ViewHelper
8
+ def javascript_include_tag(*args)
9
+ AssetBundler.javascript_include_tag(*args)
10
+ end
11
+
12
+ def stylesheet_link_tag(*args)
13
+ AssetBundler.stylesheet_link_tag(*args)
14
+ end
15
+ end
16
+
17
+ @@bundle_environments = ['production', 'staging']
18
+
19
+ # Expose @@bundle_environments via class method so that you can add other bundle enviroments:
20
+ #
21
+ # AssetBundler.bundle_environments << "staging2"
22
+ def self.bundle_environments
23
+ @@bundle_environments
24
+ end
25
+
26
+ def self.javascript_include_tag(*args)
27
+ bundled_asset_include_tag(*args) do |cache|
28
+ %{<script type="text/javascript" src="#{AssetTimestampsCache.timestamped_asset_path(cache)}"></script>}
29
+ end
30
+ end
31
+
32
+ def self.stylesheet_link_tag(*args)
33
+ bundled_asset_include_tag(*args) do |cache|
34
+ %{<link rel="stylesheet" href="#{AssetTimestampsCache.timestamped_asset_path(cache)}" type="text/css">}
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def self.bundled_asset_include_tag(*args)
41
+ opts = args.last.is_a?(Hash) ? args.pop : {}
42
+ cache = opts.delete(:cache)
43
+
44
+ if cache && @@bundle_environments.include?(ENV['RACK_ENV'])
45
+ file_path = asset_file_path(cache)
46
+ unless File.exist?(file_path)
47
+ write_asset_file_contents(file_path, args)
48
+ end
49
+ yield cache
50
+ else
51
+ args.map {|f| yield f}.join("\n")
52
+ end
53
+ end
54
+
55
+ def self.join_asset_file_contents(paths)
56
+ paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
57
+ end
58
+
59
+ def self.write_asset_file_contents(joined_asset_path, asset_paths)
60
+ FileUtils.mkdir_p(File.dirname(joined_asset_path))
61
+ File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
62
+
63
+ # Set mtime to the latest of the combined files to allow for
64
+ # consistent ETag without a shared filesystem.
65
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
66
+ File.utime(mt, mt, joined_asset_path)
67
+ end
68
+
69
+ def self.asset_file_path(path)
70
+ File.join('public', path.to_s)
71
+ end
72
+
73
+ end
Binary file
@@ -0,0 +1,2 @@
1
+ // script one
2
+ function one() {};
@@ -0,0 +1,2 @@
1
+ // script three
2
+ function three() {};
@@ -0,0 +1,2 @@
1
+ // script two
2
+ function two() {};
@@ -0,0 +1,2 @@
1
+ /* style one */
2
+ #one {}
@@ -0,0 +1,2 @@
1
+ /* style three */
2
+ #three {}
@@ -0,0 +1,2 @@
1
+ /* style two */
2
+ #two {}
@@ -0,0 +1,111 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'asset_bundler'
4
+
5
+ FileUtils.mkdir_p 'public'
6
+ FileUtils.cp_r 'test/fixtures/public/scripts', 'public'
7
+ FileUtils.cp_r 'test/fixtures/public/styles', 'public'
8
+
9
+ class TestAssetBundler < Test::Unit::TestCase
10
+
11
+ class Foo; include(AssetBundler::ViewHelper); end
12
+
13
+ def setup
14
+ @old_rack_env, ENV['RACK_ENV'] = ENV['RACK_ENV'], 'production'
15
+ AssetTimestampsCache.clear
16
+ FileUtils.mkdir_p 'public'
17
+ @scope = Foo.new
18
+ end
19
+
20
+ def teardown
21
+ ENV['RACK_ENV'] = @old_rack_env
22
+ end
23
+
24
+ def test_javascript_include_tag_html_output_without_cache
25
+ html = @scope.javascript_include_tag('/scripts/one.js', '/scripts/two.js', '/scripts/three.js')
26
+ expected = ['one', 'two', 'three'].map do |name|
27
+ mtime = File.mtime("public/scripts/#{name}.js").to_i
28
+ %(<script type="text/javascript" src="/scripts/#{name}.js?#{mtime}"></script>)
29
+ end.join("\n")
30
+ assert_equal expected, html
31
+ end
32
+
33
+ def test_stylesheet_link_tag_html_output_without_cache
34
+ html = @scope.stylesheet_link_tag('/styles/one.css', '/styles/two.css', '/styles/three.css')
35
+ expected = ['one', 'two', 'three'].map do |name|
36
+ mtime = File.mtime("public/styles/#{name}.css").to_i
37
+ %{<link rel="stylesheet" href="/styles/#{name}.css?#{mtime}" type="text/css">}
38
+ end.join("\n")
39
+ assert_equal expected, html
40
+ end
41
+
42
+ def test_javascript_include_tag_html_output_with_cache
43
+ html = @scope.javascript_include_tag('/scripts/one.js', '/scripts/two.js', '/scripts/three.js', :cache => '/scripts/all.js')
44
+ mtime = File.mtime("public/scripts/all.js").to_i
45
+ expected = %(<script type="text/javascript" src="/scripts/all.js?#{mtime}"></script>)
46
+ assert_equal expected, html
47
+ ensure
48
+ File.delete("public/scripts/all.js")
49
+ end
50
+
51
+ def test_stylesheet_link_tag_html_output_with_cache
52
+ html = @scope.stylesheet_link_tag('/styles/one.css', '/styles/two.css', '/styles/three.css', :cache => '/styles/all.css')
53
+ mtime = File.mtime("public/styles/all.css").to_i
54
+ expected = %{<link rel="stylesheet" href="/styles/all.css?#{mtime}" type="text/css">}
55
+ assert_equal expected, html
56
+ ensure
57
+ File.delete("public/styles/all.css")
58
+ end
59
+
60
+ def test_javascript_include_tag_html_output_with_cache_does_not_bundle_in_development
61
+ with_rack_env 'development' do
62
+ html = @scope.javascript_include_tag('/scripts/one.js', '/scripts/two.js', '/scripts/three.js', :cache => '/scripts/all.js')
63
+ expected = ['one', 'two', 'three'].map do |name|
64
+ mtime = File.mtime("public/scripts/#{name}.js").to_i
65
+ %(<script type="text/javascript" src="/scripts/#{name}.js?#{mtime}"></script>)
66
+ end.join("\n")
67
+ assert_equal expected, html
68
+ end
69
+ end
70
+
71
+ def test_stylesheet_link_tag_html_output_with_cache_does_not_bundle_in_development
72
+ with_rack_env 'development' do
73
+ html = @scope.stylesheet_link_tag('/styles/one.css', '/styles/two.css', '/styles/three.css', :cache => '/styles/all.css')
74
+ expected = ['one', 'two', 'three'].map do |name|
75
+ mtime = File.mtime("public/styles/#{name}.css").to_i
76
+ %{<link rel="stylesheet" href="/styles/#{name}.css?#{mtime}" type="text/css">}
77
+ end.join("\n")
78
+ assert_equal expected, html
79
+ end
80
+ end
81
+
82
+ def test_javascript_include_tag_html_output_with_cache_uses_latest_mtime
83
+ sleep 1
84
+ FileUtils.touch 'public/scripts/two.js'
85
+ @scope.javascript_include_tag('/scripts/one.js', '/scripts/two.js', '/scripts/three.js', :cache => '/scripts/all.js')
86
+ assert_equal File.mtime('public/scripts/two.js'), File.mtime('public/scripts/all.js')
87
+ assert File.mtime('public/scripts/one.js') < File.mtime('public/scripts/all.js')
88
+ assert File.mtime('public/scripts/three.js') < File.mtime('public/scripts/all.js')
89
+ ensure
90
+ File.delete("public/scripts/all.js")
91
+ end
92
+
93
+ def test_cached_javascript
94
+ @scope.javascript_include_tag('/scripts/one.js', '/scripts/two.js', '/scripts/three.js', :cache => '/scripts/all.js')
95
+ actual = File.read("public/scripts/all.js")
96
+ expected = ['one', 'two', 'three'].map do |name|
97
+ "// script #{name}\nfunction #{name}() {};"
98
+ end.join("\n\n")
99
+ assert_equal expected, actual
100
+ ensure
101
+ File.delete("public/scripts/all.js")
102
+ end
103
+
104
+ private
105
+ def with_rack_env(val)
106
+ old_env, ENV['RACK_ENV'] = ENV['RACK_ENV'], val
107
+ yield
108
+ ensure
109
+ ENV['RACK_ENV'] = old_env
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asset_bundler
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Geoff Buesing
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-05 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: asset_timestamps_cache
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 1
30
+ - 1
31
+ version: 0.1.1
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Adapted from asset bundling functionality in ActionPack.
35
+ email: gbuesing@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.md
42
+ files:
43
+ - .gitignore
44
+ - README.md
45
+ - Rakefile
46
+ - VERSION
47
+ - lib/asset_bundler.rb
48
+ - test/fixtures/.DS_Store
49
+ - test/fixtures/public/scripts/one.js
50
+ - test/fixtures/public/scripts/three.js
51
+ - test/fixtures/public/scripts/two.js
52
+ - test/fixtures/public/styles/one.css
53
+ - test/fixtures/public/styles/three.css
54
+ - test/fixtures/public/styles/two.css
55
+ - test/test_asset_bundler.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/gbuesing/asset_bundler
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.6
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A simple asset bundling solution.
86
+ test_files:
87
+ - test/test_asset_bundler.rb