bagger 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
6
+ *.rbc
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 1.8.7
5
+ - ree
6
+ - rbx-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in bagger.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # bagger
2
+
3
+ A framework agnostic packaging solution to speed up client side
4
+ rendering by using the following techniques:
5
+
6
+ * Generating versioned assets to maximize cache efficiency
7
+ * Combine, minify and lint javascript and css files to reduce bandwidth
8
+ * Zip assets to further reduce bandwidth consumption
9
+ * Generating a file manifest to lookup an asset's location
10
+ * Rewrite urls in css files with the versioned file name
11
+ * Bundle up the assets as a zip file
12
+
13
+ ## Similar projects
14
+
15
+ * [jammit](https://github.com/documentcloud/jammit)
16
+ * [Rails 3 asset pipeline](http://blog.nodeta.com/2011/06/14/rails-3-1-asset-pipeline-in-the-real-world/)
17
+ * [assets.io](http://www.assets.io/)
18
+
19
+ ## Roadmap
20
+
21
+ ### Version 0.0.1 (Minimal Viable Product)
22
+
23
+ * Versioned assets
24
+ * Manifest files
25
+ * Combine javascript
26
+ * Combine css
27
+ * Rewrite css urls
28
+ * CLI binary
29
+ * Solid test suite
30
+ * Support for the following Ruby versions (MRI 1.8.7, 1.9.2, REE 1.8.7)
31
+ * Pass all tests on travisci.org
32
+ * Examples on how to integrate it into the development workflow using
33
+ tools like watch or supervisor
34
+
35
+ ## Tests
36
+
37
+ check the build status on [travis.ci](http://travis-ci.org/wooga/bagger)
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+
5
+ task :default => ['test:units']
6
+
7
+ def rubies
8
+ require 'yaml'
9
+ rubies = YAML.load_file('.travis.yml')['rvm']
10
+ end
11
+
12
+ namespace :test do
13
+ Rake::TestTask.new(:units) do |t|
14
+ t.libs << "test"
15
+ t.test_files = FileList['test/*_test.rb']
16
+
17
+ t.verbose = true
18
+ end
19
+
20
+ desc 'run test suite with all ruby versions'
21
+ task :multi_ruby do
22
+ rubies.each do |ruby_version|
23
+ puts `rvm use #{ruby_version} && rake`
24
+ end
25
+ end
26
+ end
27
+
28
+ desc 'run bundle install for all rubies'
29
+ task :prepare_rubies do
30
+ rubies.each do |ruby_version|
31
+ puts `rvm use #{ruby_version} && gem install bundler && bundle`
32
+ end
33
+ end
data/bagger.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "bagger/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "bagger"
7
+ s.version = Bagger::VERSION
8
+ s.authors = ["Patrick Huesler"]
9
+ s.email = ["patrick.huesler@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = "Asset packaging for the rest of us"
12
+ s.description = "A framework agnostic packaging solution for your assets: version files, combine them, minify them and create a manifest"
13
+
14
+ s.rubyforge_project = "bagger"
15
+
16
+ s.add_dependency "json"
17
+ s.add_dependency "addressable"
18
+ s.add_dependency "uglifier"
19
+ s.add_dependency "rainpress"
20
+
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "shoulda-context"
23
+ s.add_development_dependency "mocha"
24
+
25
+ s.files = `git ls-files`.split("\n")
26
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
+ s.require_paths = ["lib"]
29
+ end
@@ -0,0 +1,148 @@
1
+ # encoding: UTF-8
2
+ require 'json'
3
+ require 'digest/md5'
4
+ require 'addressable/uri'
5
+ require 'uglifier'
6
+ require 'rainpress'
7
+
8
+ module Bagger
9
+ class Packager
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ @manifest_name = 'manifest.json'
14
+ @stylesheets = (@options[:combine] || {})[:stylesheets] || []
15
+ @javascripts = (@options[:combine] || {})[:javascripts] || []
16
+ @source_dir = @options[:source_dir]
17
+ @target_dir = @options[:target_dir]
18
+ @stylesheet_path = (@options[:combine] || {})[:stylesheet_path] || 'combined.css'
19
+ @javascript_path = (@options[:combine] || {})[:javascript_path] || 'combined.js'
20
+ @path_prefix = @options[:path_prefix] || ''
21
+ @manifest = {}
22
+ end
23
+
24
+ def to_manifest(path, keep_original = true)
25
+ content = File.open(File.join(@target_dir, path)) { |f| f.read }
26
+ extension = File.extname(path)
27
+ basename = File.basename(path, extension)
28
+ dirname = File.dirname(path)
29
+ FileUtils.mkdir_p(File.join(@target_dir, dirname))
30
+ md5 = Digest::MD5.hexdigest(content)
31
+ new_file_name = "#{basename}.#{md5}#{extension}"
32
+ new_file_path = File.join(@target_dir, dirname, new_file_name)
33
+ File.open(new_file_path, 'w') { |f| f.write content }
34
+ FileUtils.rm(File.join(@target_dir, path)) unless keep_original
35
+ manifest_key_path = File.expand_path("/#{dirname}/#{basename}#{extension}")
36
+ effective_path = File.expand_path(@path_prefix + "/" + File.join(dirname, new_file_name))
37
+ @manifest[manifest_key_path] = effective_path
38
+ end
39
+
40
+ def manifest_path
41
+ File.join(@options[:source_dir], @manifest_name)
42
+ end
43
+
44
+ def run
45
+ combine_css
46
+ combine_js
47
+ version_files
48
+ rewrite_urls_in_css
49
+ compress_css
50
+ to_manifest(@stylesheet_path, false)
51
+ compress_js
52
+ to_manifest(@javascript_path, false)
53
+ generate_and_version_cache_manifest
54
+ write_manifest
55
+ end
56
+
57
+ def write_manifest
58
+ File.open(manifest_path, 'w') do |f|
59
+ f.write JSON.pretty_generate(@manifest)
60
+ end
61
+ end
62
+
63
+ def version_files
64
+ FileUtils.cd(@source_dir) do
65
+ Dir["**/*"].reject{ |f| f =~ /\.(css|js)$/ }.each do |path|
66
+ if File.directory? path
67
+ FileUtils.mkdir_p(File.join(@target_dir, path))
68
+ next
69
+ end
70
+ FileUtils.cp(path, File.join(@target_dir, path))
71
+ to_manifest(path, false)
72
+ end
73
+ end
74
+ end
75
+
76
+ def combine_css
77
+ combine_files(@stylesheets, @stylesheet_path)
78
+ end
79
+
80
+ def rewrite_urls_in_css
81
+ url_regex = /(^|[{;])(.*?url\(\s*['"]?)(.*?)(['"]?\s*\).*?)([;}]|$)/ui
82
+ behavior_regex = /behavior:\s*url/ui
83
+ data_regex = /^\s*data:/ui
84
+ input = File.open(File.join(@target_dir, @stylesheet_path)){|f| f.read}
85
+ output = input.gsub(url_regex) do |full_match|
86
+ pre, url_match, post = ($1 + $2), $3, ($4 + $5)
87
+ if behavior_regex.match(pre) || data_regex.match(url_match)
88
+ full_match
89
+ else
90
+ path = Addressable::URI.parse("/") + url_match
91
+ target_url = @manifest[path.to_s]
92
+ if target_url
93
+ pre + target_url + post
94
+ else
95
+ full_match
96
+ end
97
+ end
98
+ end
99
+ File.open(File.join(@target_dir, @stylesheet_path), 'w') do |f|
100
+ f.write output
101
+ end
102
+ end
103
+
104
+ def compress_css
105
+ css = File.open(File.join(@target_dir, @stylesheet_path)){|f| f.read}
106
+ compressed = Rainpress.compress(css)
107
+ File.open(File.join(@target_dir, @stylesheet_path), 'w') do |f|
108
+ f.write compressed
109
+ end
110
+ end
111
+
112
+ def combine_js
113
+ combine_files(@javascripts, @javascript_path)
114
+ end
115
+
116
+ def compress_js
117
+ javascript = File.open(File.join(@target_dir, @javascript_path)){|f| f.read}
118
+ compressed = Uglifier.compile(javascript)
119
+ File.open(File.join(@target_dir, @javascript_path), 'w'){|f| f.write compressed}
120
+ end
121
+
122
+ def generate_and_version_cache_manifest
123
+ File.open(File.join(@target_dir, 'cache.manifest'), 'w') do |f|
124
+ f.puts 'CACHE MANIFEST'
125
+ f.puts ''
126
+ f.puts '# Explicitely cached entries'
127
+ f.puts @manifest.values.join("\n")
128
+ f.puts ''
129
+ f.puts 'NETWORK:'
130
+ f.puts '*'
131
+ end
132
+ to_manifest('cache.manifest')
133
+ end
134
+
135
+ private
136
+
137
+ def combine_files(files, path)
138
+ output = ''
139
+ FileUtils.mkdir_p(File.join(@target_dir, File.dirname(path)))
140
+ target_path = File.join(@target_dir, path)
141
+ files.each do |file|
142
+ output << File.open(File.join(@source_dir, file)) { |f| f.read }
143
+ output << "\n"
144
+ end
145
+ File.open(target_path, "w") { |f| f.write(output) }
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,3 @@
1
+ module Bagger
2
+ VERSION = "0.0.1"
3
+ end
data/lib/bagger.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "rubygems"
2
+ require 'json'
3
+ require "bagger/packager"
4
+
5
+ module Bagger
6
+ def self.bagit!(options)
7
+ Bagger::Packager.new(options).run
8
+ end
9
+ end
@@ -0,0 +1,265 @@
1
+ # encoding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class BaggerTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @source_dir = Dir.mktmpdir
8
+ @target_dir = Dir.mktmpdir
9
+ Uglifier.stubs(:compile).returns('//minfied js');
10
+ end
11
+
12
+ def teardown
13
+ FileUtils.remove_entry_secure @source_dir
14
+ FileUtils.remove_entry_secure @target_dir
15
+ end
16
+
17
+ def write_file(path, content)
18
+ File.open(path, 'w') do |f|
19
+ f.write content
20
+ end
21
+ end
22
+
23
+ def default_options
24
+ {
25
+ :source_dir => @source_dir,
26
+ :target_dir => @target_dir,
27
+ }
28
+ end
29
+
30
+ def manifest
31
+ path = File.join(@source_dir, 'manifest.json')
32
+ json = File.open(path){|f| f.read}
33
+ JSON.parse(json)
34
+ end
35
+
36
+ context 'manifest' do
37
+ should 'generate one' do
38
+ Bagger.bagit!(default_options)
39
+ assert File.exists?(File.join(@source_dir, 'manifest.json')), 'manifest was not created'
40
+ end
41
+
42
+ should 'version files with md5' do
43
+ test_content = 'testcontent'
44
+ write_file(File.join(@source_dir, 'test.txt'), test_content)
45
+ Bagger.bagit!(default_options)
46
+ md5 = Digest::MD5.hexdigest(test_content)
47
+ assert_equal md5, manifest['/test.txt'].split('.')[1]
48
+ end
49
+
50
+ should 'copy over the versioned files' do
51
+ test_content = 'testcontent'
52
+ write_file(File.join(@source_dir, 'test.txt'), test_content)
53
+ Bagger.bagit!(default_options)
54
+ assert File.exists?(File.join(@target_dir, manifest['/test.txt']))
55
+ end
56
+
57
+ should 'support a path prefix' do
58
+ test_content = 'testcontent'
59
+ write_file(File.join(@source_dir, 'test.txt'), test_content)
60
+ Bagger.bagit!(default_options.merge(:path_prefix => '/path_prefix'))
61
+ assert_match /\/path_prefix\/test\..*\.txt/, manifest['/test.txt']
62
+ end
63
+ end
64
+
65
+ context 'html 5 cache manifest' do
66
+ should 'generate one' do
67
+ Bagger.bagit!(default_options)
68
+ expected_path = File.join(@target_dir, 'cache.manifest')
69
+ assert File.exists?(expected_path), 'cache manifest not found'
70
+ end
71
+
72
+ should 'add the cache manifest to the manifest' do
73
+ Bagger.bagit!(default_options)
74
+ assert_match /\/cache\..*\.manifest/, manifest['/cache.manifest']
75
+ end
76
+
77
+ should 'create a versioned cache manifest' do
78
+ Bagger.bagit!(default_options)
79
+ expected_path = File.join(@target_dir, manifest['/cache.manifest'])
80
+ assert File.exists?(expected_path), 'versioned cache manifest not found'
81
+ end
82
+ end
83
+
84
+ context 'css files' do
85
+ setup do
86
+ @config = {
87
+ :stylesheets => [],
88
+ :stylesheet_path => 'css/combined.css'
89
+ }
90
+ @css_dir = File.join(@source_dir, 'css')
91
+ FileUtils.mkdir_p(@css_dir)
92
+ %w(one two).each do |file|
93
+ write_file(
94
+ File.join(@css_dir, "#{file}.css"),
95
+ ".#{file}{}"
96
+ )
97
+ @config[:stylesheets] << "css/#{file}.css"
98
+ end
99
+ end
100
+
101
+ should 'add it to the manifest' do
102
+ Bagger.bagit!(
103
+ :source_dir => @source_dir,
104
+ :target_dir => @target_dir,
105
+ :combine => @config
106
+ )
107
+
108
+ assert manifest['/css/combined.css']
109
+ end
110
+
111
+ should 'combine it' do
112
+ Bagger.bagit!(
113
+ :source_dir => @source_dir,
114
+ :target_dir => @target_dir,
115
+ :combine => @config
116
+ )
117
+ expected_file_path = File.join(@target_dir, manifest['/css/combined.css'])
118
+ assert File.exists?(expected_file_path), 'combined css not found'
119
+ end
120
+
121
+ should 'only copy over the generate files' do
122
+ Bagger.bagit!(
123
+ :source_dir => @source_dir,
124
+ :target_dir => @target_dir,
125
+ :combine => @config
126
+ )
127
+ assert !File.exists?(File.join(@target_dir, 'css', 'one.css'))
128
+ end
129
+
130
+ should 'compress it' do
131
+ Rainpress.stubs(:compress).returns('//super minified css');
132
+ Bagger.bagit!(
133
+ :source_dir => @source_dir,
134
+ :target_dir => @target_dir,
135
+ :combine => @config
136
+ )
137
+ expected_file_path = File.join(@target_dir, manifest['/css/combined.css'])
138
+ compressed_content = File.open(expected_file_path){|f| f.read}
139
+ assert_equal '//super minified css', compressed_content , 'combined css not found'
140
+ end
141
+
142
+ context 'url rewriting' do
143
+ setup do
144
+ css = <<-EOF
145
+ #documentRootBasedUrl {
146
+ background: url("/images/root.png") top center;
147
+ }
148
+ #relativeUrl {
149
+ background: url("../images/relative.png") top center;
150
+ }
151
+ #absoluteUrl {
152
+ background: url('http://localhost/absolute.png') top center;
153
+ }
154
+ EOF
155
+ write_file(File.join(@css_dir, "urled.css"), css)
156
+ @config[:stylesheets] << 'css/urled.css'
157
+ FileUtils.mkdir_p(File.join(@source_dir, 'images'))
158
+ %w(root relative absolute).each do |type|
159
+ FileUtils.touch(File.join(@source_dir, 'images', "#{type}.png"))
160
+ end
161
+ end
162
+
163
+ should 'rewrite document root based urls' do
164
+ Bagger.bagit!(
165
+ :source_dir => @source_dir,
166
+ :target_dir => @target_dir,
167
+ :combine => @config
168
+ )
169
+ combined_css = File.open(File.join(@target_dir, manifest['/css/combined.css'])){|f| f.read}
170
+ assert combined_css.include?(manifest['/images/root.png'])
171
+ end
172
+
173
+ should 'rewrite relative (anchored by the css file) urls to absolute paths' do
174
+ Bagger.bagit!(
175
+ :source_dir => @source_dir,
176
+ :target_dir => @target_dir,
177
+ :combine => @config
178
+ )
179
+ combined_css = File.open(File.join(@target_dir, manifest['/css/combined.css'])){|f| f.read}
180
+ assert combined_css.include?(manifest['/images/relative.png'])
181
+ end
182
+
183
+ should 'not rewrite absolute urls' do
184
+ Bagger.bagit!(
185
+ :source_dir => @source_dir,
186
+ :target_dir => @target_dir,
187
+ :combine => @config
188
+ )
189
+ combined_css = File.open(File.join(@target_dir, manifest['/css/combined.css'])){|f| f.read}
190
+ assert combined_css.include?('http://localhost/absolute.png')
191
+ end
192
+
193
+ should 'support a path prefix' do
194
+ Bagger.bagit!(
195
+ :source_dir => @source_dir,
196
+ :target_dir => @target_dir,
197
+ :path_prefix => '/path_prefix',
198
+ :combine => @config
199
+ )
200
+ combined_css_path = manifest['/css/combined.css'].gsub(/path_prefix/,'')
201
+ combined_css = File.open(File.join(@target_dir, combined_css_path)){|f| f.read}
202
+ assert_match /\/path_prefix\/images\/relative\..*\.png/, combined_css
203
+ end
204
+ end
205
+ end
206
+
207
+ context 'combine javascript' do
208
+ setup do
209
+ @config = {
210
+ :javascripts => [],
211
+ :javascript_path => 'js/combined.js'
212
+ }
213
+ @js_dir = File.join(@source_dir, 'js')
214
+ FileUtils.mkdir_p(@js_dir)
215
+ %w(one two).each do |file|
216
+ write_file(
217
+ File.join(@js_dir, "#{file}.js"),
218
+ "var #{file} = 1;"
219
+ )
220
+ @config[:javascripts] << "js/#{file}.js"
221
+ end
222
+ end
223
+
224
+ should 'add it to the manifest' do
225
+ Bagger.bagit!(
226
+ :source_dir => @source_dir,
227
+ :target_dir => @target_dir,
228
+ :combine => @config
229
+ )
230
+ assert manifest['/js/combined.js']
231
+ end
232
+
233
+ should 'combine it' do
234
+ Bagger.bagit!(
235
+ :source_dir => @source_dir,
236
+ :target_dir => @target_dir,
237
+ :combine => @config
238
+ )
239
+ expected_file_path = File.join(@target_dir, manifest['/js/combined.js'])
240
+ assert File.exists?(expected_file_path), 'combined js not found'
241
+ end
242
+
243
+ should 'only copy over the generate files' do
244
+ Bagger.bagit!(
245
+ :source_dir => @source_dir,
246
+ :target_dir => @target_dir,
247
+ :combine => @config
248
+ )
249
+ assert !File.exists?(File.join(@target_dir, 'js', 'one.js'))
250
+ end
251
+
252
+ should 'compress the javascript' do
253
+ expected_path = File.join(@target_dir, 'js', 'combined.js')
254
+ Uglifier.expects(:compile).returns('//minified javascript')
255
+
256
+ Bagger.bagit!(
257
+ :source_dir => @source_dir,
258
+ :target_dir => @target_dir,
259
+ :combine => @config
260
+ )
261
+ expected_file_path = File.join(@target_dir, manifest['/js/combined.js'])
262
+ assert_equal '//minified javascript', File.open(expected_file_path){|f| f.read}
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift( File.join( File.dirname(__FILE__), "..", "lib") )
2
+
3
+ require "rubygems"
4
+ require 'test/unit'
5
+ require 'mocha'
6
+ require 'shoulda-context'
7
+ require 'bagger'
8
+ require 'fileutils'
9
+
10
+ TEST_TEMP_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp'))
11
+
data/tests.watchr ADDED
@@ -0,0 +1,38 @@
1
+ # Run me with:
2
+ # $ watchr specs.watchr
3
+
4
+ # --------------------------------------------------
5
+ # Rules
6
+ # --------------------------------------------------
7
+ watch( '^test.*/test_.*\.rb' ) { |m| ruby m[0] }
8
+ watch( '^lib/(.*)\.rb' ) { |m| ruby "test/#{m[1]}_test.rb" }
9
+ watch( '^test/test_helper\.rb' ) { ruby tests }
10
+ watch( '^test/bagger_test\.rb' ) { ruby tests }
11
+ watch( '^lib/bagger\.rb' ) { ruby tests }
12
+
13
+
14
+ # --------------------------------------------------
15
+ # Signal Handling
16
+ # --------------------------------------------------
17
+ Signal.trap('QUIT') { ruby tests } # Ctrl-\
18
+ Signal.trap('INT' ) { abort("\n") } # Ctrl-C
19
+
20
+ # --------------------------------------------------
21
+ # Helpers
22
+ # --------------------------------------------------
23
+ def ruby(*paths)
24
+ run "ruby #{gem_opt} -I.:lib:test -e'%w( #{paths.flatten.join(' ')} ).each {|p| require p }'"
25
+ end
26
+
27
+ def tests
28
+ Dir['test/**/test_*.rb'] - ['test/test_helper.rb']
29
+ end
30
+
31
+ def run( cmd )
32
+ puts cmd
33
+ system cmd
34
+ end
35
+
36
+ def gem_opt
37
+ defined?(Gem) ? "-rubygems" : ""
38
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bagger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Patrick Huesler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &70307486940240 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70307486940240
25
+ - !ruby/object:Gem::Dependency
26
+ name: addressable
27
+ requirement: &70307486939720 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70307486939720
36
+ - !ruby/object:Gem::Dependency
37
+ name: uglifier
38
+ requirement: &70307486939180 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70307486939180
47
+ - !ruby/object:Gem::Dependency
48
+ name: rainpress
49
+ requirement: &70307486938540 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70307486938540
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: &70307486938000 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70307486938000
69
+ - !ruby/object:Gem::Dependency
70
+ name: shoulda-context
71
+ requirement: &70307486937460 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70307486937460
80
+ - !ruby/object:Gem::Dependency
81
+ name: mocha
82
+ requirement: &70307486936940 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70307486936940
91
+ description: ! 'A framework agnostic packaging solution for your assets: version files,
92
+ combine them, minify them and create a manifest'
93
+ email:
94
+ - patrick.huesler@gmail.com
95
+ executables: []
96
+ extensions: []
97
+ extra_rdoc_files: []
98
+ files:
99
+ - .gitignore
100
+ - .travis.yml
101
+ - Gemfile
102
+ - README.md
103
+ - Rakefile
104
+ - bagger.gemspec
105
+ - lib/bagger.rb
106
+ - lib/bagger/packager.rb
107
+ - lib/bagger/version.rb
108
+ - test/bagger_test.rb
109
+ - test/test_helper.rb
110
+ - tests.watchr
111
+ homepage: ''
112
+ licenses: []
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project: bagger
131
+ rubygems_version: 1.8.6
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Asset packaging for the rest of us
135
+ test_files:
136
+ - test/bagger_test.rb
137
+ - test/test_helper.rb