dasch-smurf 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +24 -0
- data/README.markdown +59 -0
- data/Rakefile +22 -0
- data/app/controllers/application.rb +2 -0
- data/config/boot.rb +109 -0
- data/config/database.yml +3 -0
- data/config/environment.rb +74 -0
- data/config/environments/test.rb +19 -0
- data/config/routes.rb +5 -0
- data/init.rb +1 -0
- data/install.rb +0 -0
- data/lib/smurf.rb +38 -0
- data/lib/smurf/javascript.rb +219 -0
- data/lib/smurf/stylesheet.rb +34 -0
- data/public/javascripts/cache/expected.js +2 -0
- data/public/javascripts/testing.js +3 -0
- data/public/stylesheets/bar.css +44 -0
- data/public/stylesheets/cache/expected.css +1 -0
- data/public/stylesheets/foo.css +48 -0
- data/rails/init.rb +1 -0
- data/smurf.gemspec +35 -0
- data/test/smurf_test.rb +64 -0
- data/test/test_helper.rb +31 -0
- data/uninstall.rb +0 -0
- metadata +78 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
MIT License
|
2
|
+
--------------------------------------------------------------------------------
|
3
|
+
Copyright (c) 2008 Justin Knowlden
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person
|
6
|
+
obtaining a copy of this software and associated documentation
|
7
|
+
files (the "Software"), to deal in the Software without
|
8
|
+
restriction, including without limitation the rights to use,
|
9
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the
|
11
|
+
Software is furnished to do so, subject to the following
|
12
|
+
conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
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
|
+
## Contact
|
54
|
+
|
55
|
+
Justin Knowlden <gus@gusg.us>
|
56
|
+
|
57
|
+
## License
|
58
|
+
|
59
|
+
See MIT-LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the load_model plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the smurf plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Smurf'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
data/config/boot.rb
ADDED
@@ -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!
|
data/config/database.yml
ADDED
@@ -0,0 +1,74 @@
|
|
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.2.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
|
+
# Settings in config/environments/* take precedence over those specified here.
|
17
|
+
# Application configuration should go into files in config/initializers
|
18
|
+
# -- all .rb files in that directory are automatically loaded.
|
19
|
+
# See Rails::Configuration for more options.
|
20
|
+
|
21
|
+
# Skip frameworks you're not going to use. To use Rails without a database
|
22
|
+
# you must remove the Active Record framework.
|
23
|
+
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
|
24
|
+
|
25
|
+
# Specify gems that this application depends on.
|
26
|
+
# They can then be installed with "rake gems:install" on new installations.
|
27
|
+
# config.gem "bj"
|
28
|
+
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
|
29
|
+
# config.gem "aws-s3", :lib => "aws/s3"
|
30
|
+
config.gem 'thoughtbot-shoulda', :lib => 'shoulda/rails',
|
31
|
+
:source => 'http://gems.github.com'
|
32
|
+
config.gem 'sqlite3-ruby', :lib => 'sqlite3'
|
33
|
+
|
34
|
+
# Only load the plugins named here, in the order given. By default, all plugins
|
35
|
+
# in vendor/plugins are loaded in alphabetical order.
|
36
|
+
# :all can be used as a placeholder for all plugins not explicitly named
|
37
|
+
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
|
38
|
+
|
39
|
+
# Add additional load paths for your own custom dirs
|
40
|
+
# config.load_paths += %W( #{RAILS_ROOT}/extras )
|
41
|
+
|
42
|
+
# Force all environments to use the same logger level
|
43
|
+
# (by default production uses :info, the others :debug)
|
44
|
+
# config.log_level = :debug
|
45
|
+
|
46
|
+
# Make Time.zone default to the specified zone, and make Active Record store time values
|
47
|
+
# in the database in UTC, and return them converted to the specified local zone.
|
48
|
+
# Run "rake -D time" for a list of tasks for finding time zone names. Comment line to use default local time.
|
49
|
+
# config.time_zone = ENV['TZ'] = 'UTC'
|
50
|
+
|
51
|
+
# Your secret key for verifying cookie session data integrity.
|
52
|
+
# If you change this key, all old sessions will become invalid!
|
53
|
+
# Make sure the secret is at least 30 characters and all random,
|
54
|
+
# no regular words or you'll be exposed to dictionary attacks.
|
55
|
+
# config.action_controller.session = {
|
56
|
+
# :session_key => '_load_model_session',
|
57
|
+
# :secret => '731d6426b38a848657211af3650ea99dc064a830ec15fd20565d71ba62498382132872b2d9b549eeb5a016025c119eb821d8e66794cd380888120aa0b857386d'
|
58
|
+
# }
|
59
|
+
|
60
|
+
# Use the database for sessions instead of the cookie-based default,
|
61
|
+
# which shouldn't be used to store highly confidential information
|
62
|
+
# (create the session table with "rake db:sessions:create")
|
63
|
+
# config.action_controller.session_store = :active_record_store
|
64
|
+
|
65
|
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
66
|
+
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
67
|
+
# like if you have constraints or database-specific column types
|
68
|
+
# config.active_record.schema_format = :sql
|
69
|
+
|
70
|
+
# Activate observers that should always be running
|
71
|
+
# config.active_record.observers = :cacher, :garbage_collector
|
72
|
+
end
|
73
|
+
|
74
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'init'))
|
@@ -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/config/routes.rb
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'smurf'
|
data/install.rb
ADDED
File without changes
|
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,219 @@
|
|
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 == '\\' || c[0] > 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
|
+
@output.write @theA
|
134
|
+
@theA = get
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
if(a==1 || a==2 || a==3)
|
140
|
+
@theB = mynext
|
141
|
+
if (@theB == "/" && (@theA == "(" || @theA == "," || @theA == "=" ||
|
142
|
+
@theA == ":" || @theA == "[" || @theA == "!" ||
|
143
|
+
@theA == "&" || @theA == "|" || @theA == "?" ||
|
144
|
+
@theA == "{" || @theA == "}" || @theA == ";" ||
|
145
|
+
@theA == "\n"))
|
146
|
+
@output.write @theA
|
147
|
+
@output.write @theB
|
148
|
+
while (true)
|
149
|
+
@theA = get
|
150
|
+
if (@theA == "/")
|
151
|
+
break
|
152
|
+
elsif (@theA == "\\")
|
153
|
+
@output.write @theA
|
154
|
+
@theA = get
|
155
|
+
elsif (@theA <= "\n")
|
156
|
+
raise "Unterminated RegExp Literal"
|
157
|
+
end
|
158
|
+
@output.write @theA
|
159
|
+
end
|
160
|
+
@theB = mynext
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# jsmin -- Copy the input to the output, deleting the characters which are
|
166
|
+
# insignificant to JavaScript. Comments will be removed. Tabs will be
|
167
|
+
# replaced with spaces. Carriage returns will be replaced with linefeeds.
|
168
|
+
# Most spaces and linefeeds will be removed.
|
169
|
+
def jsmin
|
170
|
+
@theA = "\n"
|
171
|
+
action(3)
|
172
|
+
while (@theA != EOF)
|
173
|
+
case @theA
|
174
|
+
when " "
|
175
|
+
if (isAlphanum(@theB))
|
176
|
+
action(1)
|
177
|
+
else
|
178
|
+
action(2)
|
179
|
+
end
|
180
|
+
when "\n"
|
181
|
+
case (@theB)
|
182
|
+
when "{","[","(","+","-"
|
183
|
+
action(1)
|
184
|
+
when " "
|
185
|
+
action(3)
|
186
|
+
else
|
187
|
+
if (isAlphanum(@theB))
|
188
|
+
action(1)
|
189
|
+
else
|
190
|
+
action(2)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
else
|
194
|
+
case (@theB)
|
195
|
+
when " "
|
196
|
+
if (isAlphanum(@theA))
|
197
|
+
action(1)
|
198
|
+
else
|
199
|
+
action(3)
|
200
|
+
end
|
201
|
+
when "\n"
|
202
|
+
case (@theA)
|
203
|
+
when "}","]",")","+","-","\"","\\", "'", '"'
|
204
|
+
action(1)
|
205
|
+
else
|
206
|
+
if (isAlphanum(@theA))
|
207
|
+
action(1)
|
208
|
+
else
|
209
|
+
action(3)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
else
|
213
|
+
action(1)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end # Javascript
|
219
|
+
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
|
@@ -0,0 +1,44 @@
|
|
1
|
+
abbr, acronym {
|
2
|
+
border: 0;
|
3
|
+
font-variant: normal;
|
4
|
+
}
|
5
|
+
|
6
|
+
sup {
|
7
|
+
vertical-align: text-top;
|
8
|
+
}
|
9
|
+
/*
|
10
|
+
hello, my name is sam
|
11
|
+
*/
|
12
|
+
sub {
|
13
|
+
vertical-align: text-bottom;
|
14
|
+
}
|
15
|
+
|
16
|
+
input, textarea, select{
|
17
|
+
font-family: inherit;
|
18
|
+
font-size: inherit;
|
19
|
+
font-weight: inherit;
|
20
|
+
}
|
21
|
+
|
22
|
+
input, textarea, select {
|
23
|
+
*font-size: 100%;
|
24
|
+
}
|
25
|
+
|
26
|
+
legend { color:#000; }
|
27
|
+
|
28
|
+
del, ins {
|
29
|
+
text-decoration:none;
|
30
|
+
}
|
31
|
+
|
32
|
+
smurf {
|
33
|
+
content: "pa pa";
|
34
|
+
}
|
35
|
+
|
36
|
+
.smurf :link li {
|
37
|
+
color:blue;
|
38
|
+
}
|
39
|
+
|
40
|
+
smurf #smurf , :smurf .smurf {
|
41
|
+
color : black;
|
42
|
+
}
|
43
|
+
|
44
|
+
smurfdom { papa: smurf }
|
@@ -0,0 +1 @@
|
|
1
|
+
html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}li{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}del,ins{text-decoration:none}smurf{content:"pa pa"}.smurf :link li{color:blue}smurf #smurf,:smurf .smurf{color:black}smurfdom{papa:smurf}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/*
|
2
|
+
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
|
3
|
+
Code licensed under the BSD License:
|
4
|
+
http://developer.yahoo.net/yui/license.txt
|
5
|
+
version: 2.6.0
|
6
|
+
*/
|
7
|
+
|
8
|
+
html {
|
9
|
+
color: #000;
|
10
|
+
background: #FFF;
|
11
|
+
}
|
12
|
+
|
13
|
+
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td {
|
14
|
+
margin: 0;
|
15
|
+
padding: 0;
|
16
|
+
}
|
17
|
+
|
18
|
+
table {
|
19
|
+
border-collapse: collapse;
|
20
|
+
border-spacing: 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
fieldset, img {
|
24
|
+
border: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
address, caption, cite, code, dfn, em, strong, th, var {
|
28
|
+
font-style: normal;
|
29
|
+
font-weight: normal;
|
30
|
+
}
|
31
|
+
|
32
|
+
li {
|
33
|
+
list-style: none;
|
34
|
+
}
|
35
|
+
|
36
|
+
caption, th {
|
37
|
+
text-align: left;
|
38
|
+
}
|
39
|
+
|
40
|
+
h1, h2, h3, h4, h5, h6 {
|
41
|
+
font-size: 100%;
|
42
|
+
font-weight: normal;
|
43
|
+
}
|
44
|
+
|
45
|
+
q:before, q:after {
|
46
|
+
content: '';
|
47
|
+
}
|
48
|
+
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'smurf'
|
data/smurf.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "smurf"
|
4
|
+
s.summary = "Rails plugin to automatically minify JS and CSS when their bundles get cached"
|
5
|
+
s.version = "1.0.0"
|
6
|
+
s.authors = ["Justin Knowlden"]
|
7
|
+
s.homepage = "http://github.com/thumblemonks/smurf"
|
8
|
+
s.files = ["config/boot.rb",
|
9
|
+
"config/routes.rb",
|
10
|
+
"config/environments/test.rb",
|
11
|
+
"config/database.yml",
|
12
|
+
"config/environment.rb",
|
13
|
+
"init.rb",
|
14
|
+
"install.rb",
|
15
|
+
"uninstall.rb",
|
16
|
+
"rails/init.rb",
|
17
|
+
"Rakefile",
|
18
|
+
"README.markdown",
|
19
|
+
"smurf.gemspec",
|
20
|
+
"lib/smurf/stylesheet.rb",
|
21
|
+
"lib/smurf/javascript.rb",
|
22
|
+
"lib/smurf.rb",
|
23
|
+
"MIT-LICENSE"]
|
24
|
+
s.test_files = ["app/controllers/application.rb",
|
25
|
+
"public/stylesheets/bar.css",
|
26
|
+
"public/stylesheets/cache",
|
27
|
+
"public/stylesheets/cache/expected.css",
|
28
|
+
"public/stylesheets/foo.css",
|
29
|
+
"public/javascripts",
|
30
|
+
"public/javascripts/cache",
|
31
|
+
"public/javascripts/cache/expected.js",
|
32
|
+
"public/javascripts/testing.js",
|
33
|
+
"test/smurf_test.rb",
|
34
|
+
"test/test_helper.rb"]
|
35
|
+
end
|
data/test/smurf_test.rb
ADDED
@@ -0,0 +1,64 @@
|
|
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
|
+
context "when caching on for javascript" do
|
8
|
+
setup do
|
9
|
+
ActionController::Base.stubs(:perform_caching).returns(true)
|
10
|
+
javascript_include_tag('testing.js', :cache => 'cache/actual')
|
11
|
+
end
|
12
|
+
|
13
|
+
should_have_same_contents('javascripts/cache/expected.js',
|
14
|
+
'javascripts/cache/actual.js')
|
15
|
+
end # when caching on for javascript
|
16
|
+
|
17
|
+
# Stylesheet
|
18
|
+
|
19
|
+
context "when caching on for stylesheets" do
|
20
|
+
setup do
|
21
|
+
ActionController::Base.stubs(:perform_caching).returns(true)
|
22
|
+
stylesheet_link_tag('foo', 'bar', :cache => 'cache/actual')
|
23
|
+
end
|
24
|
+
|
25
|
+
should_have_same_contents('stylesheets/cache/expected.css',
|
26
|
+
'stylesheets/cache/actual.css')
|
27
|
+
end # when caching on for stylesheets
|
28
|
+
|
29
|
+
context "minifying a non-existent pattern in a stylesheet" do
|
30
|
+
setup {@himom = "hi{mom:super-awesome}"}
|
31
|
+
|
32
|
+
# Thanks to someone named Niko for finding this
|
33
|
+
should "succeed for removing comments" do
|
34
|
+
actual = "hi { mom: super-awesome; } "
|
35
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
36
|
+
end
|
37
|
+
|
38
|
+
should "succeed when no spaces to compress" do
|
39
|
+
actual = @himom
|
40
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
41
|
+
end
|
42
|
+
|
43
|
+
should "succeed when no outside or inside blocks" do
|
44
|
+
# nothing outside, means nothing inside. they are complementary
|
45
|
+
actual = "how-did: this-happen; typo: maybe;}"
|
46
|
+
expected = "how-did: this-happen; typo: maybe}"
|
47
|
+
assert_equal expected, Smurf::Stylesheet.new(actual).minified
|
48
|
+
end
|
49
|
+
|
50
|
+
should "succeed when no last semi-colon in block" do
|
51
|
+
actual = "hi { mom: super-awesome } "
|
52
|
+
assert_equal @himom, Smurf::Stylesheet.new(actual).minified
|
53
|
+
end
|
54
|
+
|
55
|
+
should "succeed across all parsers when no content provided" do
|
56
|
+
actual = ""
|
57
|
+
assert_equal "", Smurf::Stylesheet.new(actual).minified
|
58
|
+
end
|
59
|
+
|
60
|
+
should "succeed if nil provided" do
|
61
|
+
assert_nil Smurf::Stylesheet.new(nil).minified
|
62
|
+
end
|
63
|
+
end # minifying a non-existent pattern
|
64
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
|
3
|
+
# load(File.dirname(__FILE__) + "/../db/schema.rb")
|
4
|
+
|
5
|
+
require 'test_help'
|
6
|
+
require 'ruby-debug'
|
7
|
+
|
8
|
+
class Test::Unit::TestCase
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
artifacts = Dir.glob(File.join(asset_path, '**', 'cache', 'actual.*'))
|
12
|
+
FileUtils.rm(artifacts)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.should_have_same_contents(expected_path, actual_path)
|
16
|
+
should "have the same file contents as #{expected_path}" do
|
17
|
+
expected = read_asset_file(expected_path)
|
18
|
+
actual = read_asset_file(actual_path)
|
19
|
+
assert_equal expected, actual
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def asset_path
|
25
|
+
File.join(File.dirname(__FILE__), '..', 'public')
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_asset_file(relative_path)
|
29
|
+
File.read(File.join(asset_path, relative_path))
|
30
|
+
end
|
31
|
+
end
|
data/uninstall.rb
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dasch-smurf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Justin Knowlden
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-08 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email:
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- config/boot.rb
|
26
|
+
- config/routes.rb
|
27
|
+
- config/environments/test.rb
|
28
|
+
- config/database.yml
|
29
|
+
- config/environment.rb
|
30
|
+
- init.rb
|
31
|
+
- install.rb
|
32
|
+
- uninstall.rb
|
33
|
+
- rails/init.rb
|
34
|
+
- Rakefile
|
35
|
+
- README.markdown
|
36
|
+
- smurf.gemspec
|
37
|
+
- lib/smurf/stylesheet.rb
|
38
|
+
- lib/smurf/javascript.rb
|
39
|
+
- lib/smurf.rb
|
40
|
+
- MIT-LICENSE
|
41
|
+
has_rdoc: false
|
42
|
+
homepage: http://github.com/thumblemonks/smurf
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.2.0
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: Rails plugin to automatically minify JS and CSS when their bundles get cached
|
67
|
+
test_files:
|
68
|
+
- app/controllers/application.rb
|
69
|
+
- public/stylesheets/bar.css
|
70
|
+
- public/stylesheets/cache
|
71
|
+
- public/stylesheets/cache/expected.css
|
72
|
+
- public/stylesheets/foo.css
|
73
|
+
- public/javascripts
|
74
|
+
- public/javascripts/cache
|
75
|
+
- public/javascripts/cache/expected.js
|
76
|
+
- public/javascripts/testing.js
|
77
|
+
- test/smurf_test.rb
|
78
|
+
- test/test_helper.rb
|