bagger 0.0.1

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,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