compressible 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Lance Pollard and David Crockett
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,125 @@
1
+ # Compressible
2
+
3
+ Ready-to-go Asset Compression for Ruby, using the YUICompressor.
4
+
5
+ sudo gem install compressible
6
+
7
+ # Usage
8
+
9
+ ## Configuration
10
+
11
+ There are a few ways you can configure this
12
+
13
+ ### 1. Initializers
14
+
15
+ Inside an initializer such as `config/compressible.rb`:
16
+
17
+ Compressible.stylesheets(
18
+ :production_cache => %w(reset background footer list),
19
+ :typography => %w(forms headers basic_text)
20
+ )
21
+ Compressible.javascripts(
22
+ :production_cache => %w(animations effects dragdrop)
23
+ )
24
+
25
+ # or
26
+ Compressible.assets(
27
+ :stylesheets => {
28
+ :production_cache => %w(reset background footer list),
29
+ :typography => %w(forms headers basic_text)
30
+ }
31
+ :javascripts => {
32
+ :production_cache => %w(animations effects dragdrop)
33
+ }
34
+ )
35
+
36
+ # or one at a time
37
+ Compressible.stylesheet(
38
+ :production_cache => %w(reset background footer list)
39
+ )
40
+ Compressible.stylesheet(
41
+ :typography => %w(forms headers basic_text)
42
+ )
43
+
44
+ Each of those methods have plenty of aliases to be more descriptive and more concise, respectively:
45
+
46
+ - `stylesheets`: `add_stylesheets`
47
+ - `javascripts`: `add_javascripts`
48
+ - `stylesheet`: `add_stylesheet`, `css`
49
+ - `javascript`: `add_javascript`, `js`
50
+
51
+ Or you can call them generically:
52
+
53
+ Compressible.assets(:stylesheet)
54
+ Compressible.assets(:javascript)
55
+
56
+ ### 2. Yaml
57
+
58
+ You can specify these same configuration options using Yaml:
59
+
60
+ js:
61
+ paths:
62
+ - animations
63
+ - effects
64
+ - dragdrop
65
+ to: production_cache
66
+ munge: true
67
+ css:
68
+ -
69
+ paths:
70
+ - reset
71
+ - background
72
+ - footer
73
+ - list
74
+ to: production_cache
75
+ -
76
+ paths:
77
+ - forms
78
+ - headers
79
+ - basic_text
80
+ to: typography
81
+
82
+ You can then setup everything with `configure`:
83
+
84
+ Compressible.configure("config/compressible.yml") # or pass it the yaml hash
85
+
86
+ ## Views
87
+
88
+ Add this to your views:
89
+
90
+ compressible_stylesheet_tag "production_cache", "typography"
91
+ compressible_javascript_tag "production_cache"
92
+
93
+ By default, it will use non-compressed stylesheets when `Rails.env == "development"`, and the compressed stylesheets when `Rails.env == "production"`.
94
+
95
+ To tell it you want to use the cached in a different environment, you can specify it like this:
96
+
97
+ compressible_stylesheet_tag "production_cache", :environments => ["production", "staging"]
98
+
99
+ ## Awesome Assets for Heroku
100
+
101
+ Because Heroku is a Read-Only file system, you can't use Rails' built in asset cacher, or libraries like `asset_packager`. They rely on the ability to write to the file system in the production environment.
102
+
103
+ Some libraries solve this by patching `asset_packager` to redirect css/js urls to the `/tmp` directory using Rack middleware. The `/tmp` directory is about the only place Heroku lets you write to the file system in. The main issue with this approach is that all requests for stylesheets and javascripts must pass through Rack, which potentially _doubles_ response time. Take a look at the [asset loading performance comparison with different Ruby stacks](http://www.ridingtheclutch.com/2009/07/13/the-ultimate-ruby-performance-test-part-1.html).
104
+
105
+ As such, Compressible compresses all assets before you push to Heroku, so a) you never write to the Heroku file system, and b) you don't have slow down the request with application or middleware layers. It does this with _git hooks_. Every time you commit, a `pre-commit` hook runs which re-compresses your assets. That means whenever you push, your assets are ready for production.
106
+
107
+ This is configurable. It relies on the [`hookify`](http://github.com/viatropos/hookify) gem.
108
+
109
+ sudo gem install hookify
110
+ cd my-rails-app
111
+ hookify pre-commit
112
+
113
+ That creates a ruby script for you were you can define what you want to run when. In our case, we want to run:
114
+
115
+ Compressible.assets(
116
+ :stylesheets => {
117
+ :production_cache => %w(reset background footer list),
118
+ :typography => %w(forms headers basic_text)
119
+ }
120
+ :javascripts => {
121
+ :production_cache => %w(animations effects dragdrop)
122
+ }
123
+ )
124
+
125
+ Very cool.
@@ -0,0 +1,80 @@
1
+ require 'rake'
2
+ require "rake/rdoctask"
3
+ require 'rake/gempackagetask'
4
+
5
+ # http://docs.rubygems.org/read/chapter/20
6
+ spec = Gem::Specification.new do |s|
7
+ s.name = "compressible"
8
+ s.version = "0.0.2"
9
+ s.author = "Lance Pollard"
10
+ s.summary = "Compressible: Quick asset compression for Ruby"
11
+ s.homepage = "http://github.com/viatropos/compressible"
12
+ s.email = "lancejpollard@gmail.com"
13
+ s.description = "Quick asset compression for Ruby"
14
+ s.has_rdoc = true
15
+ s.rubyforge_project = "compressible"
16
+ s.platform = Gem::Platform::RUBY
17
+ s.files = %w(README.markdown Rakefile init.rb MIT-LICENSE) + Dir["{lib,rails,test}/**/*"] - Dir["test/tmp"]
18
+ s.require_path = "lib"
19
+ s.add_dependency("activesupport", ">= 2.1.2")
20
+ s.add_dependency("yui-compressor")
21
+ end
22
+
23
+ Rake::GemPackageTask.new(spec) do |pkg|
24
+ pkg.gem_spec = spec
25
+ pkg.package_dir = "pkg"
26
+ end
27
+
28
+ desc "Create .gemspec file (useful for github)"
29
+ task :gemspec do
30
+ File.open("pkg/#{spec.name}.gemspec", "w") do |f|
31
+ f.puts spec.to_ruby
32
+ end
33
+ end
34
+
35
+ desc "Build the gem into the current directory"
36
+ task :gem => :gemspec do
37
+ `gem build pkg/#{spec.name}.gemspec`
38
+ end
39
+
40
+ desc "Publish gem to rubygems"
41
+ task :publish => [:package] do
42
+ %x[gem push pkg/#{spec.name}-#{spec.version}.gem]
43
+ end
44
+
45
+ desc "Print a list of the files to be put into the gem"
46
+ task :manifest do
47
+ File.open("Manifest", "w") do |f|
48
+ spec.files.each do |file|
49
+ f.puts file
50
+ end
51
+ end
52
+ end
53
+
54
+ desc "Install the gem locally"
55
+ task :install => [:package] do
56
+ sh %{sudo gem install pkg/#{spec.name}-#{spec.version} --no-ri --no-rdoc}
57
+ end
58
+
59
+ desc "Generate the rdoc"
60
+ Rake::RDocTask.new do |rdoc|
61
+ files = ["README.markdown", "lib/**/*.rb"]
62
+ rdoc.rdoc_files.add(files)
63
+ rdoc.main = "README.markdown"
64
+ rdoc.title = spec.summary
65
+ end
66
+
67
+ task :yank do
68
+ `gem yank #{spec.name} -v #{spec.version}`
69
+ end
70
+
71
+ task :lance do
72
+ puts "LANCE POLARd"
73
+ end
74
+
75
+ namespace :hookify do
76
+
77
+ task :pre_commit do
78
+ end
79
+
80
+ end#
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ File.dirname(__FILE__) + "/rails/init.rb"
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'yui/compressor'
4
+
5
+ namespace :compress do
6
+
7
+ desc "Compress JS"
8
+ task :js do
9
+ Compressible.js(
10
+ ENV["JS"].split(/,?\s+/),
11
+ :to => ENV["TO"],
12
+ :munge => ENV["MUNGE"].blank? || true : ENV["MUNGE"]
13
+ )
14
+ end
15
+
16
+ desc "Compress CSS"
17
+ task :css do
18
+ Compressible.css(
19
+ ENV["CSS"].split(/,?\s+/),
20
+ :to => ENV["TO"]
21
+ )
22
+ end
23
+
24
+ desc "Compress CSS and JS"
25
+ task :all => [:js, :css]
26
+
27
+ end
@@ -0,0 +1,225 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ begin
4
+ gem "activesupport", "= 2.3.5"
5
+ require 'active_support'
6
+ rescue Gem::LoadError => e
7
+ puts e.inspect
8
+ end
9
+ require 'yui/compressor'
10
+ this = File.dirname(__FILE__)
11
+ require File.join(this, "ext.rb")
12
+
13
+ class Compressible
14
+
15
+ KEYS = {
16
+ :css => :stylesheet,
17
+ :stylesheet => :css,
18
+ :js => :javascript,
19
+ :javascript => :js
20
+ }
21
+
22
+ class << self
23
+
24
+ attr_reader :config
25
+
26
+ def configure(value = nil)
27
+ raise "invalid config" unless (value.is_a?(String) || value.is_a?(Hash))
28
+ @config = value.is_a?(String) ? YAML.load_file(value) : value
29
+ @config.recursively_symbolize_keys!
30
+
31
+ @config = defaults.merge(@config)
32
+
33
+ # normalize everything to an array
34
+ [:js, :css].each do |type|
35
+ @config[type] = [@config[type]] unless @config[type].is_a?(Array)
36
+ end
37
+
38
+ @config
39
+ end
40
+
41
+ def defaults
42
+ {
43
+ :js => [],
44
+ :css => [],
45
+ :stylesheet_path => defined?(Rails) ? "#{Rails.root}/public/stylesheets" : nil,
46
+ :javascript_path => defined?(Rails) ? "#{Rails.root}/public/javascripts" : nil
47
+ }
48
+ end
49
+
50
+ def config
51
+ @config ||= defaults
52
+ end
53
+
54
+ def add_to_config(type, key, value)
55
+ item = find_or_create(type, key)
56
+ item[:paths] = value.collect {|i| asset_name(i)}
57
+ item
58
+ end
59
+
60
+ def find_or_create(type, key)
61
+ result = config[type].detect {|i| i[:to].to_s == key.to_s}
62
+ unless result
63
+ result = {:to => key.to_s}
64
+ config[type] << result
65
+ end
66
+ result
67
+ end
68
+
69
+ def reset
70
+ @config = defaults
71
+ end
72
+
73
+ def uncached_stylesheet_paths(*keys)
74
+ uncached_paths_for(:css, *keys)
75
+ end
76
+
77
+ def uncached_javascript_paths(*keys)
78
+ uncached_paths_for(:js, *keys)
79
+ end
80
+
81
+ def uncached_paths_for(type, *keys)
82
+ returning [] do |result|
83
+ config[type].each do |item|
84
+ keys.each do |key|
85
+ result.concat(item[:paths]) if item[:to] == key.to_s
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # called if you gave it a config
92
+ def compress(value = nil)
93
+ configure(value) if value
94
+ raise "set config to yaml file or run 'Compressible.js' or 'Compressible.css' manually" unless @config
95
+
96
+ [:js, :css].each do |k|
97
+ config[k].each do |item|
98
+ compress_from_hash(k, item)
99
+ end
100
+ end
101
+ end
102
+
103
+ def compress_from_hash(k, v)
104
+ args = v.dup.delete(:paths) + [v]
105
+ self.send(k, *args)
106
+ end
107
+
108
+ def javascripts(hash)
109
+ hash.each do |to, paths|
110
+ paths << {:to => to}
111
+ javascript(*paths)
112
+ end
113
+ end
114
+ alias_method :add_javascripts, :javascripts
115
+
116
+ def stylesheets(hash)
117
+ hash.each do |to, paths|
118
+ paths << {:to => to}
119
+ stylesheet(*paths)
120
+ end
121
+ end
122
+ alias_method :add_stylesheets, :stylesheets
123
+
124
+ def javascript(*paths)
125
+ options = paths.extract_options!
126
+ to = asset_name(options[:to])
127
+ raise "Please define a name for the cached javascript using ':to => :my_name'" unless to
128
+ munge = options.has_key?(:munge) ? options[:munge] : true
129
+
130
+ add_to_config(:js, to, paths)
131
+
132
+ compressor = YUI::JavaScriptCompressor.new(:munge => munge)
133
+
134
+ result = paths.collect do |path|
135
+ puts "Compressing #{path}..."
136
+ compressor.compress(read(:javascript, path))
137
+ end.join("\n\n")
138
+
139
+ write(:javascript, to, result) if to
140
+
141
+ result
142
+ end
143
+ alias_method :add_javascript, :javascript
144
+ alias_method :js, :javascript
145
+
146
+ def stylesheet(*paths)
147
+ options = paths.extract_options!
148
+ to = asset_name(options[:to])
149
+
150
+ add_to_config(:css, to, paths)
151
+
152
+ compressor = YUI::CssCompressor.new
153
+
154
+ result = paths.collect do |path|
155
+ puts "Compressing #{path}..."
156
+ compressor.compress(read(:stylesheet, path))
157
+ end.join("\n\n")
158
+
159
+ write(:stylesheet, to, result) if to
160
+
161
+ result
162
+ end
163
+ alias_method :add_stylesheet, :stylesheet
164
+ alias_method :css, :stylesheet
165
+
166
+ def stylesheets_for(*keys)
167
+ assets_for(:stylesheet, *keys)
168
+ end
169
+
170
+ def javascripts_for(*keys)
171
+ assets_for(:javascript, *keys)
172
+ end
173
+
174
+ def assets_for(type, *keys)
175
+ options = keys.extract_options!
176
+ environment = defined?(Rails) ? Rails.env.to_s : (options[:current] || "production")
177
+ environment = environment.to_s
178
+ cache_environments = options[:environments] || "production"
179
+ cache_environments = [cache_environments] unless cache_environments.is_a?(Array)
180
+ cache_environments = cache_environments.collect(&:to_s)
181
+
182
+ assets = cache_environments.include?(environment) ? keys : send("uncached_#{type.to_s}_paths", *keys)
183
+ assets
184
+ end
185
+
186
+ def read(type, from)
187
+ IO.read(path_for(type, from))
188
+ end
189
+
190
+ def write(type, to, result)
191
+ File.open(path_for(type, to), "w") {|f| f.puts result}
192
+ end
193
+
194
+ def asset_name(path)
195
+ result = path.to_s.split(".")
196
+ if result.last =~ /(js|css)/
197
+ result = result[0..-2].join(".")
198
+ else
199
+ result = result.join(".")
200
+ end
201
+ result
202
+ end
203
+
204
+ # ultimately should return global path
205
+ def path_for(type, file)
206
+ key = "#{type.to_s}_path".to_sym
207
+
208
+ if config && config[key]
209
+ path = File.join(config[key], file.to_s)
210
+ elsif defined?(Rails)
211
+ path = File.join(Rails.root.to_s, "public/#{type.to_s.pluralize}", file.to_s)
212
+ else
213
+ path = file.to_s
214
+ end
215
+
216
+ path << ".#{KEYS[type].to_s}" unless path.split(".").last == KEYS[type].to_s
217
+
218
+ path
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+
225
+ Dir["#{this}/compressible/*"].each { |c| require c }