rack-bundle 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source 'http://rubygems.org'
2
2
  gem 'nokogiri', '>= 1.4.2'
3
3
  gem 'rack', '>= 1.0.0'
4
+ gem 'sequel', '>= 3.12.1'
4
5
  group :test do
5
6
  gem 'rspec', '>= 1.3.0', :require => 'spec'
6
7
  end
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. It also works for stylesheets, grouping them by media type.
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 to your application's public directory"
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
- general pointer, I know it's in _ROOT/config/environment.rb_.
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 your app can write to a directory that's visible to the internet (a.k.a.: the application's public dir). I'm
28
- aware that from a security perspective (and for the sake of this working on Heroku), this is a *bad* idea. Consider this an interim
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
1
+ 0.2.0
@@ -10,4 +10,8 @@ class Rack::Bundle::CSSBundle
10
10
  def extension
11
11
  'css'
12
12
  end
13
+
14
+ def == bundle
15
+ self.class == bundle.class && hash == bundle.hash
16
+ end
13
17
  end
@@ -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 save!
16
- @bundles.each do |bundle|
17
- next if has_bundle? bundle
18
- File.open("#{dir}/rack-bundle-#{bundle.hash}.#{bundle.extension}", 'w') do |file|
19
- file << bundle.contents
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
@@ -10,4 +10,8 @@ class Rack::Bundle::JSBundle
10
10
  def extension
11
11
  'js'
12
12
  end
13
+
14
+ def == bundle
15
+ self.class == bundle.class && hash == bundle.hash
16
+ end
13
17
  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
- raise ArgumentError, ":public needs to be a directory" unless ::File.directory?(@public_dir.to_s)
14
- @storage = FileSystemStore.new @public_dir
15
- yield self if block_given?
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
- status, headers, @response = @app.call(env)
20
- return [status, headers, @response] unless headers['Content-Type'] =~ /html/
21
- parse!
22
- replace_javascripts!
23
- replace_stylesheets!
24
- [status, headers, [@document.to_html]]
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 false unless local_javascript_nodes.count > 1
42
+ return unless @document.css(SELECTORS.js).count > 1
33
43
  bundle = JSBundle.new *scripts
34
- @storage.bundles << bundle and @storage.save! unless @storage.has_bundle? bundle
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
- local_javascript_nodes.first.before(bundle_node)
40
- local_javascript_nodes.slice(0..-1).remove
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 false unless local_css_nodes.count > 1
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.bundles << bundle and @storage.save! unless @storage.has_bundle? bundle
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
- @js_nodes ||= @document.css('head script[src$=".js"]:not([src^="http"])')
75
+ @document.css(SELECTORS.js)
66
76
  end
67
-
77
+
68
78
  def local_css_nodes
69
- @css_nodes ||= @document.css('head link[href$=".css"]:not([href^="http"])')
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
@@ -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
+
@@ -10,6 +10,6 @@ describe Rack::Bundle::CSSBundle do
10
10
  end
11
11
 
12
12
  it 'creates a MD5 hash out of the contents of the bundle' do
13
- @bundle.hash.should == MD5.new(@bundle.contents)
13
+ @bundle.hash.should == MD5.new(@bundle.contents).to_s
14
14
  end
15
15
  end
@@ -10,6 +10,6 @@ describe Rack::Bundle::JSBundle do
10
10
  end
11
11
 
12
12
  it 'creates a MD5 hash out of the contents of the bundle' do
13
- @bundle.hash.should == MD5.new(@bundle.contents)
13
+ @bundle.hash.should == MD5.new(@bundle.contents).to_s
14
14
  end
15
15
  end
@@ -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
- puts h(@bundle.document.to_html)
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('link[rel="stylesheet"]').group_by { |node| node.attribute('media').value rescue nil }
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
- it "stores bundles in a location specified on the argument when instancing" do
5
- Rack::Bundle::FileSystemStore.new(FIXTURES_PATH).dir.should == FIXTURES_PATH
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 "keeps a collection of bundles in #bundles" do
9
- subject.bundles.should be_an Array
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
- context 'storing bundles in the file system' do
17
- before do
18
- @store = Rack::Bundle::FileSystemStore.new FIXTURES_PATH
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
- @store.has_bundle?(@jsbundle).should be_true
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(@store.dir, "rack-bundle-#{@jsbundle.hash}.js")).should > 0
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(@store.dir, "rack-bundle-#{@cssbundle.hash}.css")).should > 0
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.1.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-11 00:00:00 +10:00
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