rack-bundle 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
- data/README.md +21 -25
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/rack/bundle/css_bundle.rb +4 -0
- data/lib/rack/bundle/database_store.rb +39 -0
- data/lib/rack/bundle/file_system_store.rb +13 -10
- data/lib/rack/bundle/js_bundle.rb +4 -0
- data/lib/rack/bundle.rb +51 -32
- data/rack-bundle.gemspec +85 -0
- data/spec/bundles/css_bundle_spec.rb +1 -1
- data/spec/bundles/js_bundle_spec.rb +1 -1
- data/spec/rack-bundle_spec.rb +18 -3
- data/spec/spec_helper.rb +9 -0
- data/spec/store/database_store_spec.rb +38 -0
- data/spec/store/file_system_store_spec.rb +14 -22
- metadata +6 -2
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# rack-bundle
|
2
2
|
|
3
|
-
A Rack middleware for grouping Javascripts into one single file
|
3
|
+
A Rack middleware for grouping Javascripts and stylesheets into one single file (styles are grouped by media type).
|
4
4
|
|
5
5
|
# Installation
|
6
6
|
|
@@ -10,50 +10,46 @@ A Rack middleware for grouping Javascripts into one single file. It also works f
|
|
10
10
|
|
11
11
|
Rack:
|
12
12
|
|
13
|
-
use Rack::Bundle, :public_dir => "path
|
13
|
+
use Rack::Bundle, :public_dir => "path/to/app/public/dir"
|
14
14
|
run app
|
15
15
|
|
16
16
|
Sinatra it's almost the same as above, except you don't need to explicitly call run as Sinatra will handle that:
|
17
17
|
|
18
18
|
use Rack::Bundle, :public_dir => Sinatra::Application.public
|
19
19
|
|
20
|
-
As for Rails, google around how to add Rack middlewares to the stack. I'm too lazy right now to look it up. But as a
|
21
|
-
|
20
|
+
As for Rails, google around how to add Rack middlewares to the stack. I'm too lazy right now to look it up. But as a general pointer, I know it's in _ROOT/config/environment.rb_.
|
21
|
+
|
22
|
+
By default, this middleware will use the file system for storing bundles. For Heroku and a few other setups where the application doesn't have permission to write to certain directories, you can store and serve bundles directly from a database. Like so:
|
23
|
+
|
24
|
+
use Rack::Bundle, :public_dir => 'path/to/public' do |rack_bundle|
|
25
|
+
rack_bundle.storage = Rack::Bundle::DatabaseStore.new
|
26
|
+
end
|
27
|
+
|
28
|
+
_DatabaseStore_ assumes an environment variable called *DATABASE_URL* exists, which is an URL that the adapter can use to connect to a database ([See examples](http://sequel.rubyforge.org/rdoc/files/doc/opening_databases_rdoc.html)). You can alternatively supply that as parameter. For instance:
|
29
|
+
|
30
|
+
use Rack::Bundle, :public_dir => 'path/to/public' do |rack_bundle|
|
31
|
+
rack_bundle.storage = Rack::Bundle::DatabaseStore.new "sqlite://foo.sqlite3"
|
32
|
+
end
|
33
|
+
|
22
34
|
|
23
35
|
# A few assumptions
|
24
36
|
|
25
37
|
There's a few assumptions that this middleware makes in order to work. Note that *all* of those will change soon:
|
26
38
|
|
27
|
-
* That
|
28
|
-
|
29
|
-
measure so I can get something out quickly.
|
30
|
-
* That external Javascripts (read: not hosted on the same web server as the app itself) come *first* in the DOM. This may or
|
31
|
-
may not be an issue for you, but I've experienced a few. I'll add automatic reordering soon.
|
32
|
-
* That you're linking Javascripts inside the <head> tag. It won't break your app if you don't. But scripts that sit outside
|
33
|
-
will be ignored.
|
39
|
+
* That external Javascripts (read: not hosted on the same web server as the app itself) come *first* in the DOM. This may or may not be an issue for you, but I've experienced a few. I'll add automatic reordering soon.
|
40
|
+
* That you're linking Javascripts inside the <head> tag. It won't break your app if you don't. But scripts that sit outside will be ignored.
|
34
41
|
|
35
42
|
# How does it work
|
36
43
|
|
37
|
-
It parses the response body using [Nokogiri](http://nokogiri.org/), finds every reference to external scripts/stylesheets, locates
|
38
|
-
them in the file system, bundles them, saves the bundle in the application's public directory, and replaces the references in the
|
39
|
-
response for one single reference to the bundle(s).
|
44
|
+
It parses the response body using [Nokogiri](http://nokogiri.org/), finds every reference to external scripts/stylesheets, locates them in the file system, bundles them, saves the bundle in the application's public directory, and replaces the references in the response for one single reference to the bundle(s).
|
40
45
|
|
41
46
|
# Performance
|
42
47
|
|
43
|
-
This project is currently at *very* early stages of development, which in my case means I haven't bothered making it do what it's
|
44
|
-
supposed to do fast. It's quite possible however that your app will still perform a lot better with it as is, depending on how
|
45
|
-
lazy you were when writing your layouts/templates. After the first release I'll be addressing performance almost exclusively.
|
48
|
+
This project is currently at *very* early stages of development, which in my case means I haven't bothered making it do what it's supposed to do fast. It's quite possible however that your app will still perform a lot better with it as is, depending on how lazy you were when writing your layouts/templates. After the first release I'll be addressing performance almost exclusively.
|
46
49
|
|
47
50
|
# Compared to...
|
48
51
|
|
49
|
-
rack-bundle is, as of now, a lot simpler (as in less features) than solutions such as [Jammit](http://documentcloud.github.com/jammit/).
|
50
|
-
But it is plug-and-play: you load up the middleware with a few configuration parameters and you're set. No need to modify templates, no
|
51
|
-
helpers, nothing. Oh also, it's framework agnostic, and that's priceless.
|
52
|
-
|
53
|
-
# Heroku
|
54
|
-
|
55
|
-
Heroku support is in the works. As Heroku doesn't won't allow an application to write to the filesystem, rack-bundle won't work.
|
56
|
-
That will be addressed soon.
|
52
|
+
rack-bundle is, as of now, a lot simpler (as in less features) than solutions such as [Jammit](http://documentcloud.github.com/jammit/). But it is plug-and-play: you load up the middleware with a few configuration parameters and you're set. No need to modify templates, no helpers, nothing. Oh also, it's framework agnostic, and that's priceless.
|
57
53
|
|
58
54
|
# License
|
59
55
|
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), 'spec', 'spec_helper')
|
|
2
2
|
require 'spec/rake/spectask'
|
3
3
|
|
4
4
|
Spec::Rake::SpecTask.new do |t|
|
5
|
-
t.spec_files = FileList['spec/*_spec.rb']
|
5
|
+
t.spec_files = FileList['spec/*_spec.rb', 'spec/*/*_spec.rb']
|
6
6
|
t.spec_opts = ['--colour', '--format nested']
|
7
7
|
end
|
8
8
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
class Rack::Bundle::DatabaseStore
|
4
|
+
attr_accessor :db, :bundles
|
5
|
+
|
6
|
+
def initialize url = ENV['DATABASE_URL']
|
7
|
+
@db = Sequel.connect(url)
|
8
|
+
create_table!
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_bundle_by_hash hash
|
12
|
+
return nil unless result = @db[:rack_bundle].where(:hash => hash).first
|
13
|
+
result[:type] == 'js' ?
|
14
|
+
Rack::Bundle::JSBundle.new(result[:contents]) :
|
15
|
+
Rack::Bundle::CSSBundle.new(result[:contents])
|
16
|
+
end
|
17
|
+
|
18
|
+
def add bundle
|
19
|
+
return false if has_bundle? bundle
|
20
|
+
@db[:rack_bundle].insert :contents => bundle.contents,
|
21
|
+
:hash => bundle.hash,
|
22
|
+
:type => bundle.is_a?(Rack::Bundle::JSBundle) ? 'js' : 'css'
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_bundle? bundle
|
26
|
+
not find_bundle_by_hash(bundle.hash).nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def create_table!
|
31
|
+
@db.create_table! 'rack_bundle' do
|
32
|
+
String :hash
|
33
|
+
String :type
|
34
|
+
Text :contents
|
35
|
+
|
36
|
+
index :hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -5,19 +5,22 @@ class Rack::Bundle::FileSystemStore
|
|
5
5
|
|
6
6
|
def initialize dir = Dir.tmpdir
|
7
7
|
@dir = dir
|
8
|
-
@bundles = []
|
9
8
|
end
|
10
9
|
|
10
|
+
def find_bundle_by_hash hash
|
11
|
+
found = Dir["#{dir}/rack-bundle-#{hash}.*"]
|
12
|
+
return nil unless found.any?
|
13
|
+
type, contents = File.extname(found.first).sub(/^./, ''), File.read(File.join(dir, File.basename(found.first)))
|
14
|
+
type == 'js' ? Rack::Bundle::JSBundle.new(contents) : Rack::Bundle::CSSBundle.new(contents)
|
15
|
+
end
|
16
|
+
|
11
17
|
def has_bundle? bundle
|
12
18
|
File.exists? "#{dir}/rack-bundle-#{bundle.hash}.#{bundle.extension}"
|
13
19
|
end
|
14
|
-
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
20
|
+
|
21
|
+
def add bundle
|
22
|
+
File.open("#{dir}/rack-bundle-#{bundle.hash}.#{bundle.extension}", 'w') do |file|
|
23
|
+
file << bundle.contents
|
24
|
+
end
|
25
|
+
end
|
23
26
|
end
|
data/lib/rack/bundle.rb
CHANGED
@@ -3,54 +3,64 @@ require 'nokogiri'
|
|
3
3
|
|
4
4
|
module Rack
|
5
5
|
class Bundle
|
6
|
+
SELECTORS = Struct.new('Selector', :js, :css).new(
|
7
|
+
'head script[src$=".js"]:not([src^="http"])',
|
8
|
+
'head link[href$=".css"]:not([href^="http"])'
|
9
|
+
)
|
6
10
|
attr_accessor :storage, :document, :public_dir
|
7
11
|
autoload :FileSystemStore, 'rack/bundle/file_system_store'
|
12
|
+
autoload :DatabaseStore, 'rack/bundle/database_store'
|
8
13
|
autoload :JSBundle, 'rack/bundle/js_bundle'
|
9
14
|
autoload :CSSBundle, 'rack/bundle/css_bundle'
|
10
|
-
|
15
|
+
|
11
16
|
def initialize app, options = {}
|
12
17
|
@app, @public_dir = app, options[:public_dir]
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
@storage = options[:storage] || FileSystemStore.new(@public_dir)
|
19
|
+
yield self if block_given?
|
20
|
+
raise ArgumentError, ":public_dir needs to be a directory" unless ::File.directory?(@public_dir.to_s)
|
16
21
|
end
|
17
|
-
|
22
|
+
|
18
23
|
def call env
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
if match = %r(^/rack-bundle-(\w+)).match(env['PATH_INFO'])
|
25
|
+
bundle = @storage.find_bundle_by_hash match[1]
|
26
|
+
bundle ? respond_with(bundle) : not_found
|
27
|
+
else
|
28
|
+
status, headers, @response = @app.call(env)
|
29
|
+
return [status, headers, @response] unless headers['Content-Type'] =~ /html/
|
30
|
+
parse!
|
31
|
+
replace_javascripts!
|
32
|
+
replace_stylesheets!
|
33
|
+
[status, headers, [@document.to_html]]
|
34
|
+
end
|
25
35
|
end
|
26
|
-
|
36
|
+
|
27
37
|
def parse!
|
28
38
|
@document = Nokogiri::HTML(@response.join)
|
29
39
|
end
|
30
|
-
|
40
|
+
|
31
41
|
def replace_javascripts!
|
32
|
-
return
|
42
|
+
return unless @document.css(SELECTORS.js).count > 1
|
33
43
|
bundle = JSBundle.new *scripts
|
34
|
-
@storage.
|
35
|
-
bundle_node = @document.create_element 'script',
|
36
|
-
:type => 'text/javascript',
|
44
|
+
@storage.add bundle unless @storage.has_bundle? bundle
|
45
|
+
bundle_node = @document.create_element 'script',
|
46
|
+
:type => 'text/javascript',
|
37
47
|
:src => bundle_path(bundle),
|
38
48
|
:charset => 'utf-8'
|
39
|
-
|
40
|
-
|
49
|
+
@document.css(SELECTORS.js).first.before(bundle_node)
|
50
|
+
@document.css(SELECTORS.js).slice(1..-1).remove
|
41
51
|
@document
|
42
52
|
end
|
43
|
-
|
53
|
+
|
44
54
|
def replace_stylesheets!
|
45
|
-
return
|
55
|
+
return unless local_css_nodes.count > 1
|
46
56
|
styles = local_css_nodes.group_by { |node| node.attribute('media').value rescue nil }
|
47
57
|
styles.each do |media, nodes|
|
48
58
|
next unless nodes.count > 1
|
49
59
|
stylesheets = stylesheet_contents_for nodes
|
50
60
|
bundle = CSSBundle.new *stylesheets
|
51
|
-
@storage.
|
52
|
-
node = @document.create_element 'link',
|
53
|
-
:rel => 'stylesheet',
|
61
|
+
@storage.add bundle unless @storage.has_bundle? bundle
|
62
|
+
node = @document.create_element 'link',
|
63
|
+
:rel => 'stylesheet',
|
54
64
|
:type => 'text/css',
|
55
65
|
:href => bundle_path(bundle),
|
56
66
|
:media => media
|
@@ -59,16 +69,16 @@ module Rack
|
|
59
69
|
end
|
60
70
|
@document
|
61
71
|
end
|
62
|
-
|
72
|
+
|
63
73
|
private
|
64
74
|
def local_javascript_nodes
|
65
|
-
@
|
75
|
+
@document.css(SELECTORS.js)
|
66
76
|
end
|
67
|
-
|
77
|
+
|
68
78
|
def local_css_nodes
|
69
|
-
@
|
79
|
+
@document.css(SELECTORS.css)
|
70
80
|
end
|
71
|
-
|
81
|
+
|
72
82
|
def scripts
|
73
83
|
local_javascript_nodes.inject([]) do |contents, node|
|
74
84
|
path = ::File.join(@public_dir, node.attribute('src').value)
|
@@ -76,17 +86,26 @@ module Rack
|
|
76
86
|
contents
|
77
87
|
end
|
78
88
|
end
|
79
|
-
|
89
|
+
|
80
90
|
def stylesheet_contents_for nodes
|
81
91
|
nodes.inject([]) do |contents, node|
|
82
92
|
path = ::File.join(@public_dir, node.attribute('href').value)
|
83
93
|
contents << ::File.read(path) if ::File.exists?(path)
|
84
94
|
contents
|
85
|
-
end
|
95
|
+
end
|
86
96
|
end
|
87
|
-
|
97
|
+
|
88
98
|
def bundle_path bundle
|
89
99
|
"/rack-bundle-#{bundle.hash}.#{bundle.extension}"
|
90
100
|
end
|
101
|
+
|
102
|
+
def not_found
|
103
|
+
[404, {'Content-Type' => 'text/plain'}, ['Not Found']]
|
104
|
+
end
|
105
|
+
|
106
|
+
def respond_with bundle
|
107
|
+
content_type = bundle.is_a?(JSBundle) ? 'text/javascript' : 'text/css'
|
108
|
+
[200, {'Content-Type' => content_type}, [bundle.contents]]
|
109
|
+
end
|
91
110
|
end
|
92
111
|
end
|
data/rack-bundle.gemspec
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rack-bundle}
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Julio Cesar Ody"]
|
12
|
+
s.date = %q{2010-06-18}
|
13
|
+
s.description = %q{Javascript and CSS bundling at the Rack level}
|
14
|
+
s.email = %q{julio.ody@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"Gemfile",
|
20
|
+
"README.md",
|
21
|
+
"Rakefile",
|
22
|
+
"TODO.txt",
|
23
|
+
"VERSION",
|
24
|
+
"lib/rack-bundle.rb",
|
25
|
+
"lib/rack/bundle.rb",
|
26
|
+
"lib/rack/bundle/css_bundle.rb",
|
27
|
+
"lib/rack/bundle/database_store.rb",
|
28
|
+
"lib/rack/bundle/file_system_store.rb",
|
29
|
+
"lib/rack/bundle/js_bundle.rb",
|
30
|
+
"rack-bundle.gemspec",
|
31
|
+
"spec/bundles/css_bundle_spec.rb",
|
32
|
+
"spec/bundles/js_bundle_spec.rb",
|
33
|
+
"spec/fixtures/hh-reset.css",
|
34
|
+
"spec/fixtures/index.html",
|
35
|
+
"spec/fixtures/iphone.css",
|
36
|
+
"spec/fixtures/jquery-1.4.1.min.js",
|
37
|
+
"spec/fixtures/medialess1.css",
|
38
|
+
"spec/fixtures/medialess2.css",
|
39
|
+
"spec/fixtures/mylib.js",
|
40
|
+
"spec/fixtures/reset.css",
|
41
|
+
"spec/fixtures/screen.css",
|
42
|
+
"spec/fixtures/simple.html",
|
43
|
+
"spec/rack-bundle_spec.rb",
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/store/database_store_spec.rb",
|
46
|
+
"spec/store/file_system_store_spec.rb",
|
47
|
+
"vendor/jsmin.rb"
|
48
|
+
]
|
49
|
+
s.homepage = %q{http://github.com/juliocesar/rack-bundle}
|
50
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = %q{1.3.5}
|
53
|
+
s.summary = %q{Javascript and CSS bundling at the Rack level}
|
54
|
+
s.test_files = [
|
55
|
+
"spec/bundles/css_bundle_spec.rb",
|
56
|
+
"spec/bundles/js_bundle_spec.rb",
|
57
|
+
"spec/rack-bundle_spec.rb",
|
58
|
+
"spec/spec_helper.rb",
|
59
|
+
"spec/store/database_store_spec.rb",
|
60
|
+
"spec/store/file_system_store_spec.rb"
|
61
|
+
]
|
62
|
+
|
63
|
+
if s.respond_to? :specification_version then
|
64
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
65
|
+
s.specification_version = 3
|
66
|
+
|
67
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
68
|
+
s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
|
69
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.2"])
|
70
|
+
s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
|
71
|
+
s.add_development_dependency(%q<rake>, ["= 0.8.7"])
|
72
|
+
else
|
73
|
+
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
74
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
75
|
+
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
76
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
80
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.2"])
|
81
|
+
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
82
|
+
s.add_dependency(%q<rake>, ["= 0.8.7"])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
data/spec/rack-bundle_spec.rb
CHANGED
@@ -13,6 +13,22 @@ describe Rack::Bundle do
|
|
13
13
|
it 'defaults to FileSystemStore for storage' do
|
14
14
|
Rack::Bundle.new(index_page, :public_dir => '.').storage.is_a? Rack::Bundle::FileSystemStore
|
15
15
|
end
|
16
|
+
|
17
|
+
context 'serving bundles' do
|
18
|
+
before do
|
19
|
+
@jsbundle, @cssbundle = make_js_bundle, make_css_bundle
|
20
|
+
@bundle.storage.add @jsbundle
|
21
|
+
@bundle.storage.add @cssbundle
|
22
|
+
@js_request = Rack::MockRequest.env_for @bundle.send(:bundle_path, @jsbundle)
|
23
|
+
@css_request = Rack::MockRequest.env_for @bundle.send(:bundle_path, @cssbundle)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "fetches a bundle from storage and serves if the request URL matches" do
|
27
|
+
@bundle.storage.should_receive(:find_bundle_by_hash).with(@jsbundle.hash).and_return(@jsbundle)
|
28
|
+
status, headers, response = @bundle.call @js_request
|
29
|
+
response.join.should == @jsbundle.contents
|
30
|
+
end
|
31
|
+
end
|
16
32
|
|
17
33
|
context 'parsing the document' do
|
18
34
|
before do
|
@@ -46,8 +62,7 @@ describe Rack::Bundle do
|
|
46
62
|
|
47
63
|
it "replaces multiple references to Javascrips to one single reference to the bundle" do
|
48
64
|
@bundle.call @env
|
49
|
-
|
50
|
-
@bundle.document.css('head script:not([src^="http"])').count.should == 1
|
65
|
+
@bundle.document.css(Rack::Bundle::SELECTORS.js).count.should == 1
|
51
66
|
end
|
52
67
|
|
53
68
|
it "skips #replace_stylesheets! if there's only one stylesheet being included in" do
|
@@ -57,7 +72,7 @@ describe Rack::Bundle do
|
|
57
72
|
|
58
73
|
it "replaces references to external stylesheets of the same media type to their respective bundle" do
|
59
74
|
@bundle.call @env
|
60
|
-
styles = @bundle.document.css(
|
75
|
+
styles = @bundle.document.css(Rack::Bundle::SELECTORS.css).group_by { |node| node.attribute('media').value rescue nil }
|
61
76
|
styles.each_key do |media|
|
62
77
|
styles[media].count.should == 1
|
63
78
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,7 @@ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
2
|
require 'spec'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'rack/bundle'
|
5
|
+
require 'tmpdir'
|
5
6
|
include Rack::Utils
|
6
7
|
alias :h :escape_html
|
7
8
|
|
@@ -11,6 +12,14 @@ def fixture name
|
|
11
12
|
File.read(File.join(FIXTURES_PATH, name))
|
12
13
|
end
|
13
14
|
|
15
|
+
def make_js_bundle
|
16
|
+
Rack::Bundle::JSBundle.new 'La laaaa'
|
17
|
+
end
|
18
|
+
|
19
|
+
def make_css_bundle
|
20
|
+
Rack::Bundle::CSSBundle.new 'La la laaaaaaa'
|
21
|
+
end
|
22
|
+
|
14
23
|
def index_page
|
15
24
|
lambda { |env| [200, { 'Content-Type' => 'text/html' }, [fixture('index.html')]] }
|
16
25
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe Rack::Bundle::DatabaseStore do
|
4
|
+
before do
|
5
|
+
ENV['DATABASE_URL'] = "sqlite://#{Dir.tmpdir}/foo.db"
|
6
|
+
@db_store = Rack::Bundle::DatabaseStore.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'initializing' do
|
10
|
+
it 'looks for a DATABASE_URL environment variable when a database URL is not specified' do
|
11
|
+
@db_store.db.url.should == "sqlite:/#{Dir.tmpdir}/foo.db"
|
12
|
+
end
|
13
|
+
it 'takes a database url as a parameter' do
|
14
|
+
db_store = Rack::Bundle::DatabaseStore.new "sqlite://#{Dir.tmpdir}/bar.db"
|
15
|
+
db_store.db.url.should == "sqlite:/#{Dir.tmpdir}/bar.db"
|
16
|
+
end
|
17
|
+
it 'creates a table to store bundles in' do
|
18
|
+
@db_store.db.tables.should include(:rack_bundle)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#find_bundle_by_hash' do
|
23
|
+
it 'takes a bundle hash as argument and returns a matching bundle' do
|
24
|
+
jsbundle = Rack::Bundle::JSBundle.new fixture('jquery-1.4.1.min.js'), fixture('mylib.js')
|
25
|
+
@db_store.db[:rack_bundle].insert(:hash => jsbundle.hash, :contents => jsbundle.contents, :type => 'js')
|
26
|
+
@db_store.find_bundle_by_hash(jsbundle.hash).should == jsbundle
|
27
|
+
end
|
28
|
+
it "returns nil when a bundle can't be found with a matching hash" do
|
29
|
+
@db_store.find_bundle_by_hash('non existant').should be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "saves bundles to the database on #add" do
|
34
|
+
jsbundle = make_js_bundle
|
35
|
+
@db_store.add jsbundle
|
36
|
+
@db_store.db[:rack_bundle].where(:hash => jsbundle.hash).first[:hash].should == jsbundle.hash
|
37
|
+
end
|
38
|
+
end
|
@@ -1,48 +1,40 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Rack::Bundle::FileSystemStore do
|
4
|
-
|
5
|
-
|
4
|
+
before do
|
5
|
+
@jsbundle, @cssbundle = make_js_bundle, make_css_bundle
|
6
|
+
@storage = Rack::Bundle::FileSystemStore.new FIXTURES_PATH
|
7
|
+
@storage.add @jsbundle
|
8
|
+
@storage.add @cssbundle
|
6
9
|
end
|
7
10
|
|
8
|
-
it "
|
9
|
-
|
11
|
+
it "stores bundles in a location specified on the argument when instancing" do
|
12
|
+
Rack::Bundle::FileSystemStore.new(FIXTURES_PATH).dir.should == FIXTURES_PATH
|
10
13
|
end
|
11
14
|
|
12
15
|
it "defaults to the system's temporary dir" do
|
13
16
|
subject.dir.should == Dir.tmpdir
|
14
17
|
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@jsbundle = mock Rack::Bundle::JSBundle,
|
20
|
-
:contents => 'All we are saaaaaayin...',
|
21
|
-
:hash => MD5.new($jquery),
|
22
|
-
:extension => 'js'
|
23
|
-
@cssbundle = mock Rack::Bundle::CSSBundle,
|
24
|
-
:contents => '... is give peace a chaaaaance',
|
25
|
-
:hash => MD5.new($screen),
|
26
|
-
:extension => 'css'
|
27
|
-
@store.bundles.concat [@jsbundle, @cssbundle]
|
28
|
-
@store.save!
|
29
|
-
end
|
19
|
+
it "finds a bundle by it's hash on #find_bundle_by_hash" do
|
20
|
+
@storage.find_bundle_by_hash(@jsbundle.hash).should == @jsbundle
|
21
|
+
end
|
30
22
|
|
23
|
+
context 'storing bundles in the file system' do
|
31
24
|
it "checks if a bundle exists with #has_bundle?" do
|
32
|
-
@
|
25
|
+
@storage.has_bundle?(@jsbundle).should be_true
|
33
26
|
end
|
34
27
|
|
35
28
|
it 'skips saving a bundle if one with a matching hash already exists' do
|
36
29
|
File.should_not_receive(:open)
|
37
|
-
@store.save!
|
38
30
|
end
|
39
31
|
|
40
32
|
it 'stores Javascripts in a single Javascript file' do
|
41
|
-
File.size(File.join(@
|
33
|
+
File.size(File.join(@storage.dir, "rack-bundle-#{@jsbundle.hash}.js")).should > 0
|
42
34
|
end
|
43
35
|
|
44
36
|
it 'stores stylesheets in a single CSS file' do
|
45
|
-
File.size(File.join(@
|
37
|
+
File.size(File.join(@storage.dir, "rack-bundle-#{@cssbundle.hash}.css")).should > 0
|
46
38
|
end
|
47
39
|
end
|
48
40
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-bundle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julio Cesar Ody
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-06-
|
12
|
+
date: 2010-06-18 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -69,8 +69,10 @@ files:
|
|
69
69
|
- lib/rack-bundle.rb
|
70
70
|
- lib/rack/bundle.rb
|
71
71
|
- lib/rack/bundle/css_bundle.rb
|
72
|
+
- lib/rack/bundle/database_store.rb
|
72
73
|
- lib/rack/bundle/file_system_store.rb
|
73
74
|
- lib/rack/bundle/js_bundle.rb
|
75
|
+
- rack-bundle.gemspec
|
74
76
|
- spec/bundles/css_bundle_spec.rb
|
75
77
|
- spec/bundles/js_bundle_spec.rb
|
76
78
|
- spec/fixtures/hh-reset.css
|
@@ -85,6 +87,7 @@ files:
|
|
85
87
|
- spec/fixtures/simple.html
|
86
88
|
- spec/rack-bundle_spec.rb
|
87
89
|
- spec/spec_helper.rb
|
90
|
+
- spec/store/database_store_spec.rb
|
88
91
|
- spec/store/file_system_store_spec.rb
|
89
92
|
- vendor/jsmin.rb
|
90
93
|
has_rdoc: true
|
@@ -120,4 +123,5 @@ test_files:
|
|
120
123
|
- spec/bundles/js_bundle_spec.rb
|
121
124
|
- spec/rack-bundle_spec.rb
|
122
125
|
- spec/spec_helper.rb
|
126
|
+
- spec/store/database_store_spec.rb
|
123
127
|
- spec/store/file_system_store_spec.rb
|