sprocketize 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Mato Ilic
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,45 @@
1
+ # Sprocketize: Command-line utility for the [sprockets](http://getsprockets.com) gem [![Build Status](https://secure.travis-ci.org/matoilic/sprocketize.png)](http://travis-ci.org/matoilic/sprocketize)
2
+
3
+ # Usage #
4
+
5
+ Usage: sprocketize [options] output_directory filename [filename ...]
6
+ -a, --asset-root=DIRECTORY Assets root path.
7
+ -I, --include-dir=DIRECTORY Adds the directory to the Sprockets load path.
8
+ -d, --digest Incorporates a MD5 digest into all filenames.
9
+ -m, --manifest [=DIRECTORY] Writes a manifest for the assets. If no directory is
10
+ specified the manifest will be written to the output directory.
11
+ -g, --gzip Also create a compressed version of all Stylesheets and Javascripts.
12
+ -s, --save Add given parameters to .sprocksrc
13
+ -j [=COMPRESSOR], Compress all Javascript using either closure, yui or uglifier. If no
14
+ compiler is specified closure will be used.
15
+ --compress-javascripts
16
+ -c, --compress-stylesheets Compress all Stylesheets with the YUI CSS compressor.
17
+ -h, --help Show this help message.
18
+ -v, --version Show version.
19
+
20
+ The options can also be set through a local options file in the asset root or through a global file in your
21
+ home directory. `target`, `assets` and `manifest_path` are ignored when set in the global file.
22
+
23
+ #.sprocksrc
24
+ ---
25
+ target: target output directory
26
+ paths:
27
+ - include paths
28
+ assets:
29
+ - all files here will be compiled
30
+ manifest: true or false
31
+ manifest_path: output path for the manifest file
32
+ digest: true or false
33
+ gzip: true or false
34
+ js_compressor: either closure, yui or uglifier
35
+ compress_css: true or false
36
+
37
+ Include paths should always be absolute. Asset paths are always relative to the asset root. The other paths can either
38
+ be absolute or relative. Relative paths are always relative to the asset root.
39
+
40
+ # License #
41
+
42
+ Copyright &copy; 2011 Mato Ilic <<info@matoilic.ch>>
43
+
44
+ Sprockets is distributed under an MIT-style license. See LICENSE for
45
+ details.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sprocketize'
4
+
5
+ Sprocketize::CLI.new(*ARGV).run
@@ -0,0 +1,7 @@
1
+ require 'sprockets'
2
+
3
+ module Sprocketize
4
+ autoload :CLI, "sprocketize/cli"
5
+ autoload :Compiler, "sprocketize/compiler"
6
+ autoload :VERSION, "sprocketize/version"
7
+ end
@@ -0,0 +1,265 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'pathname'
4
+ require 'set'
5
+ require 'fileutils'
6
+ require 'yaml'
7
+
8
+ module Sprocketize
9
+ class CLI
10
+ SPROCKETS_FILE = '.sprocksrc'
11
+
12
+ attr_accessor :local_options, :global_options
13
+
14
+ def compile
15
+ env = Sprockets::Environment.new(@root)
16
+ env.js_compressor = expand_js_compressor(@local_options[:js_compressor])
17
+ env.css_compressor = expand_css_compressor(:yui) if @local_options[:compress_css]
18
+ (@paths + (@global_options[:paths] || Set.new)).each {|p| env.append_path(p)}
19
+
20
+ assets = local_options[:assets].map {|a| realpath(a).to_s}
21
+ filter = Proc.new do |asset|
22
+ assets.any? {|a| asset.pathname.to_s.start_with?(a)}
23
+ end
24
+
25
+ options = {
26
+ :manifest => local_options[:manifest] || global_options[:manifest],
27
+ :manifest_path => local_options[:manifest_path],
28
+ :digest => local_options[:digest] || global_options[:digest],
29
+ :gzip => local_options[:gzip] || global_options[:gzip]
30
+ }
31
+
32
+ compiler = Sprocketize::Compiler.new(env, realpath(@local_options[:target], true), [filter], options)
33
+ compiler.compile
34
+ end
35
+
36
+ def initialize(*args)
37
+ @assets = Set.new
38
+ @local_paths = Set.new
39
+ @global_options = load_global_options
40
+ input = parse_input(args)
41
+ @root = input.delete(:root)
42
+ @save = input.delete(:save)
43
+ @local_options = load_local_options(@root)
44
+ @local_options = merge_options(load_local_options(@root), input)
45
+ @local_options.freeze #to preserve exact user input
46
+ end
47
+
48
+ def merge_options(target, source)
49
+ {
50
+ :target => source[:target] || target[:target],
51
+ :paths => (target[:paths] || Set.new).merge((source[:paths] || Set.new)),
52
+ :assets => (target[:assets] || Set.new).merge((source[:assets] || Set.new)),
53
+ :digest => (source[:digest].nil? ? target[:digest] : source[:digest]),
54
+ :manifest => (source[:manifest].nil? ? target[:manifest] : source[:manifest]),
55
+ :manifest_path => (source[:manifest_path] || target[:manifest_path]),
56
+ :gzip => (source[:gzip].nil? ? target[:gzip] : source[:gzip]),
57
+ :js_compressor => source[:js_compressor] || target[:js_compressor],
58
+ :compress_css => source[:compress_css].nil? ? target[:compress_css] : source[:compress_css]
59
+ }
60
+ end
61
+
62
+ def parser
63
+ OptionParser.new do |opts|
64
+ opts.banner = "Usage: sprocketize [options] output_directory filename [filename ...]"
65
+
66
+ opts.on("-a DIRECTORY", "--asset-root=DIRECTORY", "Assets root path.") do |dir|
67
+ exit_if_non_existent(dir)
68
+ @input[:root] = realpath(dir)
69
+ end
70
+
71
+ opts.on("-I DIRECTORY", "--include-dir=DIRECTORY", "Adds the directory to the Sprockets load path.") do |dir|
72
+ exit_if_non_existent(dir)
73
+ @input[:paths] << dir
74
+ end
75
+
76
+ opts.on("-d", "--digest", "Incorporates a MD5 digest into all filenames.") do
77
+ @input[:digest] = true
78
+ end
79
+
80
+ opts.on("-m [DIRECTORY]", "--manifest [=DIRECTORY]", "Writes a manifest for the assets. If no directory is specified the manifest will be written to the output directory.") do |dir|
81
+ @input[:manifest] = true
82
+ @input[:manifest_path] = dir
83
+ end
84
+
85
+ opts.on("-g", "--gzip", "Also create a compressed version of all Stylesheets and Javascripts.") do |dir|
86
+ @input[:gzip] = true
87
+ end
88
+
89
+ opts.on("-s", "--save", "Add given parameters to #{SPROCKETS_FILE}") do |dir|
90
+ @input[:save] = true
91
+ end
92
+
93
+ opts.on("-j [COMPRESSOR]", "--compress-javascripts [=COMPRESSOR]", "Compress all Javascript using either closure, yui or uglifier. If no compiler is specified closure will be used.") do |compressor|
94
+ @input[:js_compressor] = (compressor || 'closure').to_sym
95
+ end
96
+
97
+ opts.on("-c", "--compress-stylesheets", "Compress all Stylesheets with the YUI CSS compressor.") do
98
+ @input[:compress_css] = true
99
+ end
100
+
101
+ opts.on_tail("-h", "--help", "Show this help message.") do
102
+ show_usage
103
+ exit
104
+ end
105
+
106
+ opts.on_tail("-v", "--version", "Show version.") do
107
+ show_version
108
+ exit
109
+ end
110
+ end
111
+ end
112
+
113
+ def puts_error(message)
114
+ puts "\e[31msprocketize: #{message}\e[0m"
115
+ end
116
+
117
+ def run
118
+ if @local_options[:target].to_s.length == 0
119
+ puts_error "no output directory provided"
120
+ show_usage
121
+ elsif @local_options[:assets].length == 0
122
+ puts_error "no assets provided"
123
+ show_usage
124
+ elsif !([:closure, :uglifier, :yui, nil].include?(@local_options[:js_compressor]))
125
+ puts_error "unsupported javascript processor #{@local_options[:js_compressor]}"
126
+ show_usage
127
+ else
128
+ @paths = Set.new
129
+ @local_options[:assets].each do |a|
130
+ a = realpath(a)
131
+ @paths << (a.file? ? a.dirname : a)
132
+ end
133
+ @local_options[:paths].each {|p| @paths << realpath(p)}
134
+
135
+ compile
136
+ save_local_options if @save
137
+ end
138
+ rescue OptionParser::InvalidOption => e
139
+ puts_error e.message
140
+ show_usage
141
+ rescue Sprockets::FileNotFound => e
142
+ puts_error e.message
143
+ end
144
+
145
+ def show_usage
146
+ puts parser
147
+ end
148
+
149
+ def show_version
150
+ puts Sprocketize::VERSION
151
+ end
152
+
153
+ private
154
+
155
+ def exit_if_non_existent(path)
156
+ return if File.exists?(path)
157
+ puts_error "No such file or directory #{path}"
158
+ exit
159
+ end
160
+
161
+ def expand_css_compressor(sym)
162
+ case sym
163
+ when :yui
164
+ load_gem('yui/compressor', 'yui-compressor')
165
+ YUI::CssCompressor.new
166
+ else
167
+ nil
168
+ end
169
+ end
170
+
171
+ def expand_js_compressor(sym)
172
+ case sym
173
+ when :closure
174
+ load_gem('closure-compiler')
175
+ Closure::Compiler.new
176
+ when :uglifier
177
+ load_gem('uglifier')
178
+ Uglifier.new
179
+ when :yui
180
+ load_gem('yui/compressor', 'yui-compressor')
181
+ YUI::JavaScriptCompressor.new
182
+ else
183
+ nil
184
+ end
185
+ end
186
+
187
+ def load_gem(require_path, gem_name = nil)
188
+ begin
189
+ require require_path
190
+ rescue LoadError
191
+ puts_error "required gem #{gem_name || require_path} not installed. please run `gem install #{gem_name || require_path}` to install it."
192
+ exit
193
+ end
194
+ end
195
+
196
+ def load_global_options
197
+ return {} if ENV['HOME'].nil?
198
+ file = Pathname.new(ENV['HOME']).join(SPROCKETS_FILE)
199
+
200
+ load_options(file)
201
+ end
202
+
203
+ def load_local_options(root)
204
+ file = root.join(SPROCKETS_FILE)
205
+
206
+ load_options(file)
207
+ end
208
+
209
+ def load_options(file)
210
+ return {} unless file.exist?
211
+
212
+ raw = YAML.load(File.open(file))
213
+
214
+ options = {}
215
+ raw.each_pair do |key, value|
216
+ options[key.to_sym] = value
217
+ end
218
+
219
+ options[:assets] = options[:assets].to_set if options[:assets].respond_to?(:to_set)
220
+ options[:paths] = options[:paths].to_set if options[:paths].respond_to?(:to_set)
221
+ options[:js_compressor] = options[:js_compressor].to_sym unless options[:js_compressor].nil?
222
+
223
+ options
224
+ end
225
+
226
+ def parse_input(args)
227
+ @input = {
228
+ :root => Pathname.new('.'),
229
+ :paths => Set.new,
230
+ :assets => []
231
+ }
232
+
233
+ parser.order(args) {|a| @input[:assets] << a}
234
+ @input[:target] = @input[:assets].shift
235
+ @input[:assets] = @input[:assets].to_set
236
+
237
+ @input
238
+ end
239
+
240
+ def realpath(path, create = false)
241
+ path = Pathname.new(path)
242
+ path = Pathname.new(@root).join(path) unless path.absolute?
243
+
244
+ FileUtils.mkdir_p(path) if create
245
+
246
+ path.realpath
247
+ end
248
+
249
+ def save_local_options
250
+ File.open(@root.join(SPROCKETS_FILE), 'wb') do |f|
251
+ YAML.dump({
252
+ 'target' => @local_options[:target],
253
+ 'paths' => (@local_options[:paths] || Set.new).to_a.map {|p| p.to_s},
254
+ 'assets' => @local_options[:assets].to_a.map {|a| a.to_s},
255
+ 'manifest' => @local_options[:manifest] || false,
256
+ 'manifest_path' => @local_options[:manifest_path],
257
+ 'digest' => @local_options[:digest] || false,
258
+ 'gzip' => @local_options[:gzip] || false,
259
+ 'js_compressor' => @local_options[:js_compressor],
260
+ 'compress_css' => @local_options[:compress_css] || false
261
+ }, f)
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,61 @@
1
+ require 'fileutils'
2
+
3
+ module Sprocketize
4
+ class Compiler
5
+ attr_accessor :env, :target, :paths
6
+
7
+ def initialize(env, target, paths, options = {})
8
+ @env = env
9
+ @target = target
10
+ @paths = paths
11
+ @gzip = options.key?(:gzip) ? options.delete(:gzip) : false
12
+ @digest = options.key?(:digest) ? options.delete(:digest) : false
13
+ @manifest = options.key?(:manifest) ? options.delete(:manifest) : false
14
+ @manifest_path = options.delete(:manifest_path) || target
15
+ end
16
+
17
+ def compile
18
+ manifest = {}
19
+ env.each_logical_path do |logical_path|
20
+ asset = env.find_asset(logical_path)
21
+ next if asset.nil? || !compile_asset?(asset)
22
+ manifest[logical_path] = write_asset(asset)
23
+ end
24
+ write_manifest(manifest) if @manifest
25
+ end
26
+
27
+ def write_manifest(manifest)
28
+ FileUtils.mkdir_p(@manifest_path)
29
+ File.open("#{@manifest_path}/manifest.yml", 'wb') do |f|
30
+ YAML.dump(manifest, f)
31
+ end
32
+ end
33
+
34
+ def write_asset(asset)
35
+ path_for(asset).tap do |path|
36
+ filename = File.join(target, path)
37
+ FileUtils.mkdir_p File.dirname(filename)
38
+ asset.write_to(filename)
39
+ asset.write_to("#{filename}.gz") if @gzip && filename.to_s =~ /\.(css|js)$/
40
+ end
41
+ end
42
+
43
+ def compile_asset?(asset)
44
+ paths.each do |path|
45
+ case path
46
+ when Regexp
47
+ return true if path.match(asset.pathname.to_s)
48
+ when Proc
49
+ return true if path.call(asset)
50
+ else
51
+ return true if File.fnmatch(path.to_s, asset.pathname.to_s)
52
+ end
53
+ end
54
+ false
55
+ end
56
+
57
+ def path_for(asset)
58
+ @digest ? asset.digest_path : asset.logical_path
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Sprocketize
2
+ VERSION = "0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sprocketize
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mato Ilic
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sprockets
16
+ requirement: &70169800278780 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70169800278780
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70169800278220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70169800278220
36
+ - !ruby/object:Gem::Dependency
37
+ name: closure-compiler
38
+ requirement: &70169800277620 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70169800277620
47
+ description: Sprocketize provides a command-line utility for the sprockets gem so
48
+ it can be used without rails.
49
+ email:
50
+ - info@matoilic.ch
51
+ executables:
52
+ - sprocketize
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - README.md
57
+ - LICENSE
58
+ - lib/sprocketize/cli.rb
59
+ - lib/sprocketize/compiler.rb
60
+ - lib/sprocketize/version.rb
61
+ - lib/sprocketize.rb
62
+ - bin/sprocketize
63
+ homepage: http://github.com/matoilic/sprocketize
64
+ licenses: []
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.12
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Command-line utility for the Sprockets gem
87
+ test_files: []