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 +1 -0
- data/README.md +46 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/lib/asset_bundler.rb +73 -0
- data/test/fixtures/.DS_Store +0 -0
- data/test/fixtures/public/scripts/one.js +2 -0
- data/test/fixtures/public/scripts/three.js +2 -0
- data/test/fixtures/public/scripts/two.js +2 -0
- data/test/fixtures/public/styles/one.css +2 -0
- data/test/fixtures/public/styles/three.css +2 -0
- data/test/fixtures/public/styles/two.css +2 -0
- data/test/test_asset_bundler.rb +111 -0
- metadata +87 -0
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,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
|