perfectline-smurf 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +59 -0
- data/Rakefile +38 -0
- data/VERSION.yml +4 -0
- data/lib/smurf.rb +38 -0
- data/lib/smurf/javascript.rb +225 -0
- data/lib/smurf/stylesheet.rb +34 -0
- data/rails/init.rb +1 -0
- data/test/rails/app/controllers/application.rb +2 -0
- data/test/rails/config/boot.rb +109 -0
- data/test/rails/config/environment.rb +24 -0
- data/test/rails/config/environments/test.rb +19 -0
- data/test/rails/config/routes.rb +5 -0
- data/test/smurf_test.rb +82 -0
- data/test/test_helper.rb +32 -0
- metadata +65 -0
data/README.markdown
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Smurf
|
2
|
+
|
3
|
+
Smurf is a Rails plugin that does Javascript and CSS minification the way you would expect. See, with Rails `2.x` we got this cool new `:cache` option on `javascript_include_tag` and `stylesheet_link_tag`, but no option for minifying the cached file(s).
|
4
|
+
|
5
|
+
Smurf ends that. Smurf - if installed and when caching is enabled for the environment - will nab the concatenated file content from Rails just before it saves it and minifies the content using either JSmin or a custom CSS compressor.
|
6
|
+
|
7
|
+
Some cool things about Smurf, which also allude to the reasons I wrote it:
|
8
|
+
|
9
|
+
* Smurf will only run when Rails needs to cache new files
|
10
|
+
* It will never run on its own
|
11
|
+
* It requires absolutely no configuration
|
12
|
+
* Other than installing it, you don't need to do anything
|
13
|
+
* It just gets out of your way
|
14
|
+
|
15
|
+
Smurf will work with any version of Rails `2.x`; including Rails `2.2.2`.
|
16
|
+
|
17
|
+
### JSmin
|
18
|
+
|
19
|
+
It's really an adaptation of Uladzislau Latynski's [jsmin.rb](http://javascript.crockford.com/jsmin.rb) port of Douglas Crockford's [jsmin.c](http://javascript.crockford.com/jsmin.c) library.
|
20
|
+
|
21
|
+
### Smurf CSS Compressor
|
22
|
+
|
23
|
+
The following are the rules I applied, gathered from various perusals around the Internet*s*
|
24
|
+
|
25
|
+
1. Replace consecutive whitespace characters with a single space
|
26
|
+
2. Remove whitespace around opening brackets
|
27
|
+
3. Remove whitespace in front of closing brackets
|
28
|
+
4. Remove the semi-colon just before the closing bracket
|
29
|
+
5. Remove comments between `/* ... */` - this could be a problem (esp. for CSS hacks)
|
30
|
+
6. Remove spaces around `;`, `:`, and `,` characters
|
31
|
+
7. Ensure whitespace between closing brackets and periods
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
./script/plugin install git://github.com/thumblemonks/smurf.git
|
36
|
+
|
37
|
+
Then, wherever you define `javascript_include_tag` or `stylesheet_link_tag`, make sure to add the standard `:cache => true` or `:cache => 'some_bundle'` options.
|
38
|
+
|
39
|
+
Also make sure to at least have this setting in your production.rb:
|
40
|
+
|
41
|
+
config.action_controller.perform_caching = true
|
42
|
+
|
43
|
+
## Testing and Other Stuff
|
44
|
+
|
45
|
+
If you want to test Smurf and you don't want to test with the latest version of Rails as of this writing (2.2.2), then do something like the following:
|
46
|
+
|
47
|
+
rake RAILS_GEM_VERSION=2.1.2
|
48
|
+
|
49
|
+
This is the mechanism I used for testing that Smurf works for all versions:
|
50
|
+
|
51
|
+
rake && rake RAILS_GEM_VERSION=2.1.2
|
52
|
+
|
53
|
+
## Meta
|
54
|
+
|
55
|
+
Author: Justin Knowlden <gus@thumblemonks.com>
|
56
|
+
|
57
|
+
Contributions from: Lance Ivy, Scott White, Daniel Schierbeck
|
58
|
+
|
59
|
+
See MIT-LICENSE for licensing information
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |jewel|
|
8
|
+
jewel.name = 'smurf'
|
9
|
+
jewel.summary = 'Rails plugin to automatically minify JS and CSS when their bundles get cached'
|
10
|
+
jewel.email = 'gus@thumblemonks.com'
|
11
|
+
jewel.homepage = 'http://github.com/thumblemonks/smurf'
|
12
|
+
jewel.description = 'Rails plugin to automatically minify JS and CSS when their bundles get cached. Send in those patches!'
|
13
|
+
jewel.authors = ["Justin Knowlden"]
|
14
|
+
jewel.files.exclude 'test/**'
|
15
|
+
jewel.test_files.exclude 'test/log/**'
|
16
|
+
end
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Default: run unit tests.'
|
22
|
+
task :default => :test
|
23
|
+
|
24
|
+
desc 'Test the load_model plugin.'
|
25
|
+
Rake::TestTask.new(:test) do |t|
|
26
|
+
t.libs << 'lib'
|
27
|
+
t.pattern = 'test/**/*_test.rb'
|
28
|
+
t.verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Generate documentation for the smurf plugin.'
|
32
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
33
|
+
rdoc.rdoc_dir = 'rdoc'
|
34
|
+
rdoc.title = 'Smurf'
|
35
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
36
|
+
rdoc.rdoc_files.include('README')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
end
|
data/VERSION.yml
ADDED
data/lib/smurf.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'smurf/javascript'
|
2
|
+
require 'smurf/stylesheet'
|
3
|
+
|
4
|
+
if Rails.version =~ /^2\.2\./
|
5
|
+
# Support for Rails >= 2.2.x
|
6
|
+
module Smurf
|
7
|
+
|
8
|
+
module JavaScriptSources
|
9
|
+
private
|
10
|
+
def joined_contents; Smurf::Javascript.new(super).minified; end
|
11
|
+
end # JavaScriptSources
|
12
|
+
|
13
|
+
module StylesheetSources
|
14
|
+
private
|
15
|
+
def joined_contents; Smurf::Stylesheet.new(super).minified; end
|
16
|
+
end # StylesheetSources
|
17
|
+
|
18
|
+
end # ActionView::Helpers::AssetTagHelper::AssetTag
|
19
|
+
ActionView::Helpers::AssetTagHelper::JavaScriptSources.send(
|
20
|
+
:include, Smurf::JavaScriptSources)
|
21
|
+
ActionView::Helpers::AssetTagHelper::StylesheetSources.send(
|
22
|
+
:include, Smurf::StylesheetSources)
|
23
|
+
else
|
24
|
+
# Support for Rails <= 2.1.x
|
25
|
+
module ActionView::Helpers::AssetTagHelper
|
26
|
+
private
|
27
|
+
def join_asset_file_contents_with_minification(files)
|
28
|
+
content = join_asset_file_contents_without_minification(files)
|
29
|
+
if !files.grep(%r[/javascripts]).empty?
|
30
|
+
content = Smurf::Javascript.new(content).minified
|
31
|
+
elsif !files.grep(%r[/stylesheets]).empty?
|
32
|
+
content = Smurf::Stylesheet.new(content).minified
|
33
|
+
end
|
34
|
+
content
|
35
|
+
end
|
36
|
+
alias_method_chain :join_asset_file_contents, :minification
|
37
|
+
end # ActionView::Helpers::AssetTagHelper
|
38
|
+
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
# Author: Justin Knowlden
|
2
|
+
# Adaption of jsmin.rb published by Uladzislau Latynski
|
3
|
+
#
|
4
|
+
# -------------------
|
5
|
+
# jsmin.rb 2007-07-20
|
6
|
+
# Author: Uladzislau Latynski
|
7
|
+
# This work is a translation from C to Ruby of jsmin.c published by
|
8
|
+
# Douglas Crockford. Permission is hereby granted to use the Ruby
|
9
|
+
# version under the same conditions as the jsmin.c on which it is
|
10
|
+
# based.
|
11
|
+
#
|
12
|
+
# /* jsmin.c
|
13
|
+
# 2003-04-21
|
14
|
+
#
|
15
|
+
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
16
|
+
#
|
17
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
18
|
+
# this software and associated documentation files (the "Software"), to deal in
|
19
|
+
# the Software without restriction, including without limitation the rights to
|
20
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
21
|
+
# of the Software, and to permit persons to whom the Software is furnished to do
|
22
|
+
# so, subject to the following conditions:
|
23
|
+
#
|
24
|
+
# The above copyright notice and this permission notice shall be included in all
|
25
|
+
# copies or substantial portions of the Software.
|
26
|
+
#
|
27
|
+
# The Software shall be used for Good, not Evil.
|
28
|
+
#
|
29
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
30
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
31
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
32
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
33
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
34
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
35
|
+
# SOFTWARE.
|
36
|
+
|
37
|
+
require 'stringio'
|
38
|
+
|
39
|
+
module Smurf
|
40
|
+
class Javascript
|
41
|
+
EOF = -1
|
42
|
+
|
43
|
+
@theA = ""
|
44
|
+
@theB = ""
|
45
|
+
|
46
|
+
def initialize(content)
|
47
|
+
@input = StringIO.new(content)
|
48
|
+
@output = StringIO.new
|
49
|
+
jsmin
|
50
|
+
end
|
51
|
+
|
52
|
+
def minified
|
53
|
+
@output.rewind
|
54
|
+
@output.read
|
55
|
+
end
|
56
|
+
|
57
|
+
# isAlphanum -- return true if the character is a letter, digit,
|
58
|
+
# underscore, # dollar sign, or non-ASCII character
|
59
|
+
def isAlphanum(c)
|
60
|
+
return false if !c || c == EOF
|
61
|
+
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
62
|
+
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
|
63
|
+
c == '\\' || Array(c[0].bytes).first > 126)
|
64
|
+
end
|
65
|
+
|
66
|
+
# get -- return the next character from stdin. Watch out for lookahead. If
|
67
|
+
# the character is a control character, translate it to a space or linefeed.
|
68
|
+
def get()
|
69
|
+
c = @input.getc
|
70
|
+
return EOF if(!c)
|
71
|
+
c = c.chr
|
72
|
+
return c if (c >= " " || c == "\n" || c.unpack("c") == EOF)
|
73
|
+
return "\n" if (c == "\r")
|
74
|
+
return " "
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get the next character without getting it.
|
78
|
+
def peek()
|
79
|
+
lookaheadChar = @input.getc
|
80
|
+
@input.ungetc(lookaheadChar)
|
81
|
+
return lookaheadChar.chr
|
82
|
+
end
|
83
|
+
|
84
|
+
# mynext -- get the next character, excluding comments.
|
85
|
+
# peek() is used to see if a '/' is followed by a '/' or '*'.
|
86
|
+
def mynext()
|
87
|
+
c = get
|
88
|
+
if (c == "/")
|
89
|
+
if(peek == "/")
|
90
|
+
while(true)
|
91
|
+
c = get
|
92
|
+
if (c <= "\n")
|
93
|
+
return c
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if(peek == "*")
|
98
|
+
get
|
99
|
+
while(true)
|
100
|
+
case get
|
101
|
+
when "*"
|
102
|
+
if (peek == "/")
|
103
|
+
get
|
104
|
+
return " "
|
105
|
+
end
|
106
|
+
when EOF
|
107
|
+
raise "Unterminated comment"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
return c
|
113
|
+
end
|
114
|
+
|
115
|
+
# action -- do something! What you do is determined by the argument: 1
|
116
|
+
# Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
|
117
|
+
# (Delete A). 3 Get the next B. (Delete B). action treats a string as a
|
118
|
+
# single character. Wow! action recognizes a regular expression if it is
|
119
|
+
# preceded by ( or , or =.
|
120
|
+
def action(a)
|
121
|
+
if(a==1)
|
122
|
+
@output.write @theA
|
123
|
+
end
|
124
|
+
if(a==1 || a==2)
|
125
|
+
@theA = @theB
|
126
|
+
if (@theA == "\'" || @theA == "\"")
|
127
|
+
while (true)
|
128
|
+
@output.write @theA
|
129
|
+
@theA = get
|
130
|
+
break if (@theA == @theB)
|
131
|
+
raise "Unterminated string literal" if (@theA <= "\n")
|
132
|
+
if (@theA == "\\")
|
133
|
+
# allow multi-line strings if each line is terminated by \\n (or \\r\n)
|
134
|
+
if ["\r", "\n"].include? peek
|
135
|
+
get # throw away the line ending
|
136
|
+
get if ["\r", "\n"].include? peek
|
137
|
+
else
|
138
|
+
@output.write @theA
|
139
|
+
end
|
140
|
+
@theA = get
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
if(a==1 || a==2 || a==3)
|
146
|
+
@theB = mynext
|
147
|
+
if (@theB == "/" && (@theA == "(" || @theA == "," || @theA == "=" ||
|
148
|
+
@theA == ":" || @theA == "[" || @theA == "!" ||
|
149
|
+
@theA == "&" || @theA == "|" || @theA == "?" ||
|
150
|
+
@theA == "{" || @theA == "}" || @theA == ";" ||
|
151
|
+
@theA == "\n"))
|
152
|
+
@output.write @theA
|
153
|
+
@output.write @theB
|
154
|
+
while (true)
|
155
|
+
@theA = get
|
156
|
+
if (@theA == "/")
|
157
|
+
break
|
158
|
+
elsif (@theA == "\\")
|
159
|
+
@output.write @theA
|
160
|
+
@theA = get
|
161
|
+
elsif (@theA <= "\n")
|
162
|
+
raise "Unterminated RegExp Literal"
|
163
|
+
end
|
164
|
+
@output.write @theA
|
165
|
+
end
|
166
|
+
@theB = mynext
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# jsmin -- Copy the input to the output, deleting the characters which are
|
172
|
+
# insignificant to JavaScript. Comments will be removed. Tabs will be
|
173
|
+
# replaced with spaces. Carriage returns will be replaced with linefeeds.
|
174
|
+
# Most spaces and linefeeds will be removed.
|
175
|
+
def jsmin
|
176
|
+
@theA = "\n"
|
177
|
+
action(3)
|
178
|
+
while (@theA != EOF)
|
179
|
+
case @theA
|
180
|
+
when " "
|
181
|
+
if (isAlphanum(@theB))
|
182
|
+
action(1)
|
183
|
+
else
|
184
|
+
action(2)
|
185
|
+
end
|
186
|
+
when "\n"
|
187
|
+
case (@theB)
|
188
|
+
when "{","[","(","+","-"
|
189
|
+
action(1)
|
190
|
+
when " "
|
191
|
+
action(3)
|
192
|
+
else
|
193
|
+
if (isAlphanum(@theB))
|
194
|
+
action(1)
|
195
|
+
else
|
196
|
+
action(2)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
else
|
200
|
+
case (@theB)
|
201
|
+
when " "
|
202
|
+
if (isAlphanum(@theA))
|
203
|
+
action(1)
|
204
|
+
else
|
205
|
+
action(3)
|
206
|
+
end
|
207
|
+
when "\n"
|
208
|
+
case (@theA)
|
209
|
+
when "}","]",")","+","-","\"","\\", "'", '"'
|
210
|
+
action(1)
|
211
|
+
else
|
212
|
+
if (isAlphanum(@theA))
|
213
|
+
action(1)
|
214
|
+
else
|
215
|
+
action(3)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
else
|
219
|
+
action(1)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end # Javascript
|
225
|
+
end # Smurf
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Smurf
|
2
|
+
class Stylesheet
|
3
|
+
def initialize(content)
|
4
|
+
@content = content.nil? ? nil : minify(content)
|
5
|
+
end
|
6
|
+
|
7
|
+
def minified; @content; end
|
8
|
+
|
9
|
+
# TODO: deal with string values better (urls, content blocks, etc.)
|
10
|
+
def minify(content)
|
11
|
+
class << content; include Minifier; end
|
12
|
+
content.compress_whitespace.remove_comments.remove_spaces_outside_block.
|
13
|
+
remove_spaces_inside_block.trim_last_semicolon.strip
|
14
|
+
end
|
15
|
+
|
16
|
+
module Minifier
|
17
|
+
# .*? is a non-greedy match on anything
|
18
|
+
def compress_whitespace; compress!(/\s+/, ' '); end
|
19
|
+
def remove_comments; compress!(/\/\*.*?\*\/\s?/, ''); end
|
20
|
+
def remove_spaces_outside_block
|
21
|
+
compress!(/(\A|\})(.*?)\{/) { |m| m.gsub(/\s?([}{,])\s?/, '\1') }
|
22
|
+
end
|
23
|
+
def remove_spaces_inside_block
|
24
|
+
compress!(/\{(.*?)(?=\})/) do |m|
|
25
|
+
# remove spaces in the labels/attributes
|
26
|
+
m.gsub(/(?:\A|\s*;)(.*?)(?::\s*|\z)/) { |n| n.gsub(/\s/, '') }.strip
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def trim_last_semicolon; compress!(/;(?=\})/, ''); end
|
30
|
+
private
|
31
|
+
def compress!(*args, &block) gsub!(*args, &block) || self; end
|
32
|
+
end
|
33
|
+
end # Stylesheet
|
34
|
+
end # Smurf
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'smurf'
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Don't change this file!
|
2
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
3
|
+
|
4
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def boot!
|
9
|
+
unless booted?
|
10
|
+
preinitialize
|
11
|
+
pick_boot.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def booted?
|
16
|
+
defined? Rails::Initializer
|
17
|
+
end
|
18
|
+
|
19
|
+
def pick_boot
|
20
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
21
|
+
end
|
22
|
+
|
23
|
+
def vendor_rails?
|
24
|
+
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
25
|
+
end
|
26
|
+
|
27
|
+
def preinitialize
|
28
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preinitializer_path
|
32
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Boot
|
37
|
+
def run
|
38
|
+
load_initializer
|
39
|
+
Rails::Initializer.run(:set_load_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class VendorBoot < Boot
|
44
|
+
def load_initializer
|
45
|
+
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
46
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class GemBoot < Boot
|
51
|
+
def load_initializer
|
52
|
+
self.class.load_rubygems
|
53
|
+
load_rails_gem
|
54
|
+
require 'initializer'
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_rails_gem
|
58
|
+
if version = self.class.gem_version
|
59
|
+
gem 'rails', version
|
60
|
+
else
|
61
|
+
gem 'rails'
|
62
|
+
end
|
63
|
+
rescue Gem::LoadError => load_error
|
64
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def rubygems_version
|
70
|
+
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
|
71
|
+
end
|
72
|
+
|
73
|
+
def gem_version
|
74
|
+
if defined? RAILS_GEM_VERSION
|
75
|
+
RAILS_GEM_VERSION
|
76
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
77
|
+
ENV['RAILS_GEM_VERSION']
|
78
|
+
else
|
79
|
+
parse_gem_version(read_environment_rb)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_rubygems
|
84
|
+
require 'rubygems'
|
85
|
+
min_version = '1.1.1'
|
86
|
+
unless rubygems_version >= min_version
|
87
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
|
91
|
+
rescue LoadError
|
92
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
93
|
+
exit 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_gem_version(text)
|
97
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def read_environment_rb
|
102
|
+
File.read("#{RAILS_ROOT}/config/environment.rb")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# All that for this:
|
109
|
+
Rails.boot!
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file
|
2
|
+
|
3
|
+
# Uncomment below to force Rails into production mode when
|
4
|
+
# you don't control web/app server and can't set it the proper way
|
5
|
+
# ENV['RAILS_ENV'] ||= 'production'
|
6
|
+
|
7
|
+
# Specifies gem version of Rails to use when vendor/rails is not present
|
8
|
+
unless defined? RAILS_GEM_VERSION
|
9
|
+
RAILS_GEM_VERSION = (ENV['RAILS_GEM_VERSION'] || '2.3.2')
|
10
|
+
end
|
11
|
+
|
12
|
+
# Bootstrap the Rails environment, frameworks, and default configuration
|
13
|
+
require File.join(File.dirname(__FILE__), 'boot')
|
14
|
+
|
15
|
+
Rails::Initializer.run do |config|
|
16
|
+
config.gem 'thoughtbot-shoulda', :lib => 'shoulda/rails',
|
17
|
+
:source => 'http://gems.github.com'
|
18
|
+
config.gem 'sqlite3-ruby', :lib => 'sqlite3'
|
19
|
+
|
20
|
+
config.action_controller.session = {
|
21
|
+
:session_key => '_smurf_session',
|
22
|
+
:secret => '731d6426b731d6426b731d6426b731d6426b731d6426b731d6426b731d6426b'
|
23
|
+
}
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Settings specified here will take precedence over those in config/environment.rb
|
2
|
+
|
3
|
+
# The test environment is used exclusively to run your application's
|
4
|
+
# test suite. You never need to work with it otherwise. Remember that
|
5
|
+
# your test database is "scratch space" for the test suite and is wiped
|
6
|
+
# and recreated between test runs. Don't rely on the data there!
|
7
|
+
config.cache_classes = true
|
8
|
+
|
9
|
+
# Log error messages when you accidentally call methods on nil.
|
10
|
+
config.whiny_nils = true
|
11
|
+
|
12
|
+
# Show full error reports and disable caching
|
13
|
+
config.action_controller.consider_all_requests_local = true
|
14
|
+
config.action_controller.perform_caching = false
|
15
|
+
|
16
|
+
# Tell ActionMailer not to deliver emails to the real world.
|
17
|
+
# The :test delivery method accumulates sent emails in the
|
18
|
+
# ActionMailer::Base.deliveries array.
|
19
|
+
config.action_mailer.delivery_method = :test
|
data/test/smurf_test.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class SmurfTest < Test::Unit::TestCase
|
4
|
+
include ActionView::Helpers::TagHelper
|
5
|
+
include ActionView::Helpers::AssetTagHelper
|
6
|
+
|
7
|
+
# Javascript
|
8
|
+
|
9
|
+
context "when caching on for javascript" do
|
10
|
+
setup do
|
11
|
+
ActionController::Base.stubs(:perform_caching).returns(true)
|
12
|
+
javascript_include_tag('testing.js', :cache => 'cache/actual')
|
13
|
+
end
|
14
|
+
|
15
|
+
should_have_same_contents('javascripts/cache/expected.js',
|
16
|
+
'javascripts/cache/actual.js')
|
17
|
+
end # when caching on for javascript
|
18
|
+
|
19
|
+
context "minifying multi-line strings in javascript" do
|
20
|
+
setup do
|
21
|
+
input = StringIO.new()
|
22
|
+
input.puts("var foo='bar \\")
|
23
|
+
input.puts(" bar \\")
|
24
|
+
input.puts(" baz';")
|
25
|
+
input.rewind
|
26
|
+
@content = input.read
|
27
|
+
end
|
28
|
+
|
29
|
+
should "not affect the string" do
|
30
|
+
expected = "\n" + @content.gsub(/\\?\r?\n/, '')
|
31
|
+
assert_equal expected, Smurf::Javascript.new(@content).minified
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Stylesheet
|
36
|
+
|
37
|
+
context "when caching on for stylesheets" do
|
38
|
+
setup do
|
39
|
+
ActionController::Base.stubs(:perform_caching).returns(true)
|
40
|
+
stylesheet_link_tag('foo', 'bar', :cache => 'cache/actual')
|
41
|
+
end
|
42
|
+
|
43
|
+
should_have_same_contents('stylesheets/cache/expected.css',
|
44
|
+
'stylesheets/cache/actual.css')
|
45
|
+
end # when caching on for stylesheets
|
46
|
+
|
47
|
+
context "minifying a non-existent pattern in a stylesheet" do
|
48
|
+
setup {@himom = "hi{mom:super-awesome}"}
|
49
|
+
|
50
|
+
# Thanks to someone named Niko for finding this
|
51
|
+
should "succeed for removing comments" do
|
52
|
+
actual = "hi { mom: super-awesome; } "
|
53
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
54
|
+
end
|
55
|
+
|
56
|
+
should "succeed when no spaces to compress" do
|
57
|
+
actual = @himom
|
58
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
59
|
+
end
|
60
|
+
|
61
|
+
should "succeed when no outside or inside blocks" do
|
62
|
+
# nothing outside, means nothing inside. they are complementary
|
63
|
+
actual = "how-did: this-happen; typo: maybe;}"
|
64
|
+
expected = "how-did: this-happen; typo: maybe}"
|
65
|
+
assert_equal expected, Smurf::Stylesheet.new(actual).minified
|
66
|
+
end
|
67
|
+
|
68
|
+
should "succeed when no last semi-colon in block" do
|
69
|
+
actual = "hi { mom: super-awesome } "
|
70
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
71
|
+
end
|
72
|
+
|
73
|
+
should "succeed across all parsers when no content provided" do
|
74
|
+
actual = ""
|
75
|
+
assert_equal "", Smurf::Stylesheet.new(actual).minified
|
76
|
+
end
|
77
|
+
|
78
|
+
should "succeed if nil provided" do
|
79
|
+
assert_nil Smurf::Stylesheet.new(nil).minified
|
80
|
+
end
|
81
|
+
end # minifying a non-existent pattern
|
82
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
ENV["RAILS_ROOT"] = File.expand_path(File.join(File.dirname(__FILE__), 'rails'))
|
3
|
+
require File.expand_path(File.join(ENV["RAILS_ROOT"], 'config', 'environment'))
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'init'))
|
5
|
+
|
6
|
+
require 'test_help'
|
7
|
+
require 'ruby-debug'
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
artifacts = Dir.glob(File.join(asset_path, '**', 'cache', 'actual.*'))
|
13
|
+
FileUtils.rm(artifacts)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.should_have_same_contents(expected_path, actual_path)
|
17
|
+
should "have the same file contents as #{expected_path}" do
|
18
|
+
expected = read_asset_file(expected_path)
|
19
|
+
actual = read_asset_file(actual_path)
|
20
|
+
assert_equal expected, actual
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def asset_path
|
26
|
+
File.join(File.join(ENV["RAILS_ROOT"], 'public'))
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_asset_file(relative_path)
|
30
|
+
File.read(File.join(asset_path, relative_path))
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: perfectline-smurf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Knowlden
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-24 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Rails plugin to automatically minify JS and CSS when their bundles get cached. Send in those patches!
|
17
|
+
email: gus@thumblemonks.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- README.markdown
|
26
|
+
- Rakefile
|
27
|
+
- VERSION.yml
|
28
|
+
- lib/smurf.rb
|
29
|
+
- lib/smurf/javascript.rb
|
30
|
+
- lib/smurf/stylesheet.rb
|
31
|
+
- rails/init.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/thumblemonks/smurf
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --charset=UTF-8
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.2.0
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Rails plugin to automatically minify JS and CSS when their bundles get cached
|
58
|
+
test_files:
|
59
|
+
- test/rails/app/controllers/application.rb
|
60
|
+
- test/rails/config/boot.rb
|
61
|
+
- test/rails/config/environment.rb
|
62
|
+
- test/rails/config/environments/test.rb
|
63
|
+
- test/rails/config/routes.rb
|
64
|
+
- test/smurf_test.rb
|
65
|
+
- test/test_helper.rb
|