juicer 0.2.0
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/History.txt +10 -0
- data/Manifest.txt +58 -0
- data/Rakefile +44 -0
- data/Readme.rdoc +143 -0
- data/bin/juicer +8 -0
- data/lib/juicer.rb +70 -0
- data/lib/juicer/binary.rb +173 -0
- data/lib/juicer/cache_buster.rb +45 -0
- data/lib/juicer/chainable.rb +106 -0
- data/lib/juicer/cli.rb +56 -0
- data/lib/juicer/command/install.rb +59 -0
- data/lib/juicer/command/list.rb +50 -0
- data/lib/juicer/command/merge.rb +185 -0
- data/lib/juicer/command/util.rb +32 -0
- data/lib/juicer/command/verify.rb +60 -0
- data/lib/juicer/core.rb +59 -0
- data/lib/juicer/css_cache_buster.rb +99 -0
- data/lib/juicer/install/base.rb +186 -0
- data/lib/juicer/install/jslint_installer.rb +51 -0
- data/lib/juicer/install/rhino_installer.rb +52 -0
- data/lib/juicer/install/yui_compressor_installer.rb +66 -0
- data/lib/juicer/jslint.rb +90 -0
- data/lib/juicer/merger/base.rb +74 -0
- data/lib/juicer/merger/css_dependency_resolver.rb +25 -0
- data/lib/juicer/merger/dependency_resolver.rb +82 -0
- data/lib/juicer/merger/javascript_dependency_resolver.rb +21 -0
- data/lib/juicer/merger/javascript_merger.rb +30 -0
- data/lib/juicer/merger/stylesheet_merger.rb +112 -0
- data/lib/juicer/minifyer/yui_compressor.rb +129 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +300 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/test/setup.rake +35 -0
- data/test/bin/jslint.js +474 -0
- data/test/bin/rhino1_7R1.zip +0 -0
- data/test/bin/rhino1_7R2-RC1.zip +0 -0
- data/test/bin/yuicompressor +238 -0
- data/test/bin/yuicompressor-2.3.5.zip +0 -0
- data/test/bin/yuicompressor-2.4.2.zip +0 -0
- data/test/juicer/command/test_install.rb +53 -0
- data/test/juicer/command/test_list.rb +69 -0
- data/test/juicer/command/test_merge.rb +155 -0
- data/test/juicer/command/test_util.rb +54 -0
- data/test/juicer/command/test_verify.rb +33 -0
- data/test/juicer/install/test_installer_base.rb +195 -0
- data/test/juicer/install/test_jslint_installer.rb +54 -0
- data/test/juicer/install/test_rhino_installer.rb +57 -0
- data/test/juicer/install/test_yui_compressor_installer.rb +56 -0
- data/test/juicer/merger/test_base.rb +122 -0
- data/test/juicer/merger/test_css_dependency_resolver.rb +36 -0
- data/test/juicer/merger/test_javascript_dependency_resolver.rb +39 -0
- data/test/juicer/merger/test_javascript_merger.rb +74 -0
- data/test/juicer/merger/test_stylesheet_merger.rb +178 -0
- data/test/juicer/minifyer/test_yui_compressor.rb +159 -0
- data/test/juicer/test_cache_buster.rb +58 -0
- data/test/juicer/test_chainable.rb +94 -0
- data/test/juicer/test_core.rb +47 -0
- data/test/juicer/test_css_cache_buster.rb +72 -0
- data/test/juicer/test_jslint.rb +33 -0
- data/test/test_helper.rb +146 -0
- data/test/test_juicer.rb +4 -0
- metadata +194 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Juicer
|
4
|
+
module Command
|
5
|
+
# Utilities for Juicer command objects
|
6
|
+
#
|
7
|
+
module Util
|
8
|
+
# Returns an array of files from a variety of input. Input may be a single
|
9
|
+
# file, a single glob pattern or multiple files and/or patterns. It may
|
10
|
+
# even be an array of mixed input.
|
11
|
+
#
|
12
|
+
def files(*args)
|
13
|
+
args.flatten.collect { |file| Dir.glob(file) }.flatten
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Uses Pathname to calculate the shortest relative path from +path+ to
|
18
|
+
# +reference_path+ (default is +Dir.cwd+)
|
19
|
+
#
|
20
|
+
def relative(paths, reference_path = Dir.pwd)
|
21
|
+
paths = [paths].flatten.collect do |path|
|
22
|
+
path = Pathname.new(File.expand_path(path))
|
23
|
+
reference_path = Pathname.new(File.expand_path(reference_path))
|
24
|
+
path.relative_path_from(reference_path).to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
paths.length == 1 ? paths.first : paths
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "util")
|
2
|
+
require "rubygems"
|
3
|
+
require "cmdparse"
|
4
|
+
require "pathname"
|
5
|
+
|
6
|
+
module Juicer
|
7
|
+
module Command
|
8
|
+
# Verifies problem-free-ness of source code (JavaScript and CSS)
|
9
|
+
#
|
10
|
+
class Verify < CmdParse::Command
|
11
|
+
include Juicer::Command::Util
|
12
|
+
|
13
|
+
# Initializes command
|
14
|
+
#
|
15
|
+
def initialize(log = nil)
|
16
|
+
super('verify', false, true)
|
17
|
+
@log = log || Logger.new($STDIO)
|
18
|
+
self.short_desc = "Verifies that the given JavaScript/CSS file is problem free"
|
19
|
+
self.description = <<-EOF
|
20
|
+
Uses JsLint (http://www.jslint.com) to check that code adheres to good coding
|
21
|
+
practices to avoid potential bugs, and protect against introducing bugs by
|
22
|
+
minifying.
|
23
|
+
EOF
|
24
|
+
end
|
25
|
+
|
26
|
+
# Execute command
|
27
|
+
#
|
28
|
+
def execute(args)
|
29
|
+
# Need atleast one file
|
30
|
+
raise ArgumentError.new('Please provide atleast one input file/pattern') if args.length == 0
|
31
|
+
Juicer::Command::Verify.check_all(files(args), @log)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.check_all(files, log = nil)
|
35
|
+
log ||= Logger.new($stdio)
|
36
|
+
jslint = Juicer::JsLint.new(:bin_path => Juicer.home)
|
37
|
+
problems = false
|
38
|
+
|
39
|
+
# Check that JsLint is installed
|
40
|
+
raise FileNotFoundError.new("Missing 3rd party library JsLint, install with\njuicer install jslint") if jslint.locate_lib.nil?
|
41
|
+
|
42
|
+
# Verify all files
|
43
|
+
files.each do |file|
|
44
|
+
log.info "Verifying #{file} with JsLint"
|
45
|
+
report = jslint.check(file)
|
46
|
+
|
47
|
+
if report.ok?
|
48
|
+
log.info " OK!"
|
49
|
+
else
|
50
|
+
problems = true
|
51
|
+
log.warn " Problems detected"
|
52
|
+
log.warn " #{report.errors.join("\n").gsub(/\n/, "\n ")}\n"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
!problems
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/juicer/core.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Additions to core Ruby objects
|
3
|
+
#
|
4
|
+
|
5
|
+
class String
|
6
|
+
#
|
7
|
+
# Turn an underscored string into camel case, ie this_becomes -> ThisBecomes
|
8
|
+
#
|
9
|
+
def camel_case
|
10
|
+
self.split("_").inject("") { |str, piece| str + piece.capitalize }
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Treat a string as a class name and return the class. Optionally provide a
|
15
|
+
# module to look up the class in.
|
16
|
+
#
|
17
|
+
def to_class(mod = nil)
|
18
|
+
res = "#{mod}::#{self}".sub(/^::/, "").split("::").inject(Object) do |mod, obj|
|
19
|
+
raise "No such class/module" unless mod.const_defined?(obj)
|
20
|
+
mod = mod.const_get(obj)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Turn a string in either underscore or camel case form into a class directly
|
26
|
+
#
|
27
|
+
def classify(mod = nil)
|
28
|
+
self.camel_case.to_class(mod)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Turn a camelcase string into underscore string
|
33
|
+
#
|
34
|
+
def underscore
|
35
|
+
self.split(/([A-Z][^A-Z]*)/).find_all { |str| str != "" }.join("_").downcase
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Symbol
|
40
|
+
#
|
41
|
+
# Converts symbol to string and calls String#camel_case
|
42
|
+
#
|
43
|
+
def camel_case
|
44
|
+
self.to_s.camel_case
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Converts symbol to string and calls String#classify
|
49
|
+
#
|
50
|
+
def classify(mod = nil)
|
51
|
+
self.to_s.classify(mod)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Logger
|
56
|
+
def format_message(severity, datetime, progname, msg)
|
57
|
+
"#{msg}\n"
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "chainable"))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "cache_buster"))
|
3
|
+
|
4
|
+
module Juicer
|
5
|
+
#
|
6
|
+
# The CssCacheBuster is a tool that can parse a CSS file and substitute all
|
7
|
+
# referenced URLs by a URL appended with a timestamp denoting it's last change.
|
8
|
+
# This causes the URLs to be unique every time they've been modified, thus
|
9
|
+
# facilitating using a far future expires header on your web server.
|
10
|
+
#
|
11
|
+
# See Juicer::CacheBuster for more information on how the cache buster URLs
|
12
|
+
# work.
|
13
|
+
#
|
14
|
+
# When dealing with CSS files that reference absolute URLs like /images/1.png
|
15
|
+
# you must specify the :web_root option that these URLs should be resolved
|
16
|
+
# against.
|
17
|
+
#
|
18
|
+
# When dealing with full URLs (ie including hosts) you can optionally specify
|
19
|
+
# an array of hosts to recognize as "local", meaning they serve assets from
|
20
|
+
# the :web_root directory. This way even asset host cycling can benefit from
|
21
|
+
# cache busters.
|
22
|
+
#
|
23
|
+
class CssCacheBuster
|
24
|
+
include Juicer::Chainable
|
25
|
+
|
26
|
+
def initialize(options = {})
|
27
|
+
@web_root = options[:web_root]
|
28
|
+
@web_root.sub!(%r{/?$}, "") if @web_root # Remove trailing slash
|
29
|
+
@type = options[:type] || :soft
|
30
|
+
@hosts = (options[:hosts] || []).collect { |h| h.sub!(%r{/?$}, "") } # Remove trailing slashes
|
31
|
+
@contents = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Update file. If no +output+ is provided, the input file is overwritten
|
36
|
+
#
|
37
|
+
def save(file, output = nil)
|
38
|
+
@contents = File.read(file)
|
39
|
+
|
40
|
+
urls(file).each do |url|
|
41
|
+
path = resolve(url, file)
|
42
|
+
|
43
|
+
if path != url
|
44
|
+
basename = File.basename(Juicer::CacheBuster.path(path))
|
45
|
+
@contents.sub!(url, File.join(File.dirname(url), basename))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
File.open(output || file, "w") { |f| f.puts @contents }
|
50
|
+
@contents = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
chain_method :save
|
54
|
+
|
55
|
+
#
|
56
|
+
# Returns all referenced URLs in +file+. Returned paths are absolute (ie,
|
57
|
+
# they're resolved relative to the +file+ path.
|
58
|
+
#
|
59
|
+
def urls(file)
|
60
|
+
@contents = File.read(file) unless @contents
|
61
|
+
|
62
|
+
@contents.scan(/url\(([^\)]*)\)/m).collect do |match|
|
63
|
+
match.first
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Resolve full path from URL
|
69
|
+
#
|
70
|
+
def resolve(target, from)
|
71
|
+
# If URL is external, check known hosts to see if URL can be treated
|
72
|
+
# like a local one (ie so we can add cache buster)
|
73
|
+
catch(:continue) do
|
74
|
+
if target =~ %r{^[a-z]+\://}
|
75
|
+
# This could've been a one-liner, but I prefer to be
|
76
|
+
# able to read my own code ;)
|
77
|
+
@hosts.each do |host|
|
78
|
+
if target =~ /^#{host}/
|
79
|
+
target.sub!(/^#{host}/, "")
|
80
|
+
throw :continue
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# No known hosts matched, return
|
85
|
+
return target
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Simply add web root to absolute URLs
|
90
|
+
if target =~ %r{^/}
|
91
|
+
raise FileNotFoundError.new("Unable to resolve absolute path #{target} without :web_root option") unless @web_root
|
92
|
+
return File.expand_path(File.join(@web_root, target))
|
93
|
+
end
|
94
|
+
|
95
|
+
# Resolve relative URLs to full paths
|
96
|
+
File.expand_path(File.join(File.dirname(File.expand_path(from)), target))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'fileutils'
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. juicer])) unless defined?(Juicer)
|
5
|
+
|
6
|
+
module Juicer
|
7
|
+
module Install
|
8
|
+
#
|
9
|
+
# Installer skeleton. Provides basic functionality like figuring out where
|
10
|
+
# to install, create base directories, remove unneeded directories and more
|
11
|
+
# housekeeping.
|
12
|
+
#
|
13
|
+
class Base
|
14
|
+
attr_reader :install_dir
|
15
|
+
|
16
|
+
#
|
17
|
+
# Create new installer
|
18
|
+
#
|
19
|
+
def initialize(install_dir = Juicer.home)
|
20
|
+
@install_dir = install_dir
|
21
|
+
@path = nil
|
22
|
+
@bin_path = nil
|
23
|
+
@name = nil
|
24
|
+
@dependencies = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Returns the latest available version number. Must be implemented in
|
29
|
+
# subclasses. Raises an exception when called directly.
|
30
|
+
#
|
31
|
+
def latest
|
32
|
+
raise NotImplementedError.new("Implement in subclasses")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the path relative to installation path this installer will
|
36
|
+
# install to
|
37
|
+
def path
|
38
|
+
return @path if @path
|
39
|
+
@path = "lib/" + self.class.to_s.split("::").pop.sub(/Installer$/, "").underscore
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the path to search for binaries from
|
43
|
+
#
|
44
|
+
def bin_path
|
45
|
+
return @bin_path if @bin_path
|
46
|
+
@bin_path = File.join(path, "bin")
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Returns name of component. Default implementation returns class name
|
51
|
+
# with "Installer" removed
|
52
|
+
#
|
53
|
+
def name
|
54
|
+
return @name if @name
|
55
|
+
@name = File.basename(path).split("_").inject("") { |str, word| (str + " #{word.capitalize}").strip }
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Checks if the component is currently installed.
|
60
|
+
#
|
61
|
+
# If no version is provided the most recent version is assumed.
|
62
|
+
#
|
63
|
+
def installed?(version = nil)
|
64
|
+
installed = File.exists?(File.join(@install_dir, path, "#{version || latest}"))
|
65
|
+
deps = @dependencies.length == 0 || dependencies.all? { |d, v| d.installed?(v) }
|
66
|
+
installed && deps
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Install the component. Creates basic directory structure.
|
71
|
+
#
|
72
|
+
def install(version = nil)
|
73
|
+
raise "#{name} #{version} is already installed in #{File.join(@install_dir, path)}" if installed?(version)
|
74
|
+
version ||= latest
|
75
|
+
log "Installing #{name} #{version} in #{File.join(@install_dir, path)}"
|
76
|
+
|
77
|
+
if @dependencies.length > 0
|
78
|
+
log "Installing dependencies"
|
79
|
+
dependencies { |dependency, ver| dependency.install(ver) unless dependency.installed?(ver) }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Create directories
|
83
|
+
FileUtils.mkdir_p(File.join(@install_dir, path, "bin"))
|
84
|
+
FileUtils.mkdir_p(File.join(@install_dir, path, version))
|
85
|
+
|
86
|
+
# Return resolved version for subclass to use
|
87
|
+
version
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Uninstalls the given version of the component.
|
92
|
+
#
|
93
|
+
# If no version is provided the most recent version is assumed.
|
94
|
+
#
|
95
|
+
# If there are no more files left in INSTALLATION_PATH/<path>, the
|
96
|
+
# whole directory is removed.
|
97
|
+
#
|
98
|
+
# This method takes a block and can be used from subclasses like so:
|
99
|
+
#
|
100
|
+
# def self.uninstall(install_dir = nil, version = nil)
|
101
|
+
# super do |home_dir, version|
|
102
|
+
# # Custom uninstall logic
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
#
|
107
|
+
def uninstall(version = nil)
|
108
|
+
version ||= self.latest
|
109
|
+
install_dir = File.join(@install_dir, path, version)
|
110
|
+
raise "#{name} #{version} is not installed" if !File.exists?(install_dir)
|
111
|
+
|
112
|
+
FileUtils.rm_rf(install_dir)
|
113
|
+
|
114
|
+
yield(File.join(@install_dir, path), version) if block_given?
|
115
|
+
|
116
|
+
files = Dir.glob(File.join(@install_dir, path, "**", "*")).find_all { |f| File.file?(f) }
|
117
|
+
FileUtils.rm_rf(File.join(@install_dir, path)) if files.length == 0
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Download a file to Juicer temporary directory. The file will be kept
|
122
|
+
# until #purge is called to wipe it. If the installer receives a request
|
123
|
+
# to download the same file again, the disk cache will be used unless the
|
124
|
+
# force argument is true (default false)
|
125
|
+
#
|
126
|
+
def download(url, force = false)
|
127
|
+
filename = File.join(@install_dir, "download", path.sub("lib/", ""), File.basename(url))
|
128
|
+
return filename if File.exists?(filename) && !force
|
129
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
130
|
+
File.delete(filename) if File.exists?(filename) && force
|
131
|
+
|
132
|
+
log "Downloading #{url}"
|
133
|
+
File.open(filename, "wb") do |file|
|
134
|
+
webpage = open(url)
|
135
|
+
file.write(webpage.read)
|
136
|
+
webpage.close
|
137
|
+
end
|
138
|
+
|
139
|
+
filename
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Display a message to the user through Juicer::LOGGER
|
144
|
+
#
|
145
|
+
def log(str)
|
146
|
+
Juicer::LOGGER.info str
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Add a dependency. Dependency should be a Juicer::Install::Base installer
|
151
|
+
# class (not instance) OR a symbol/string like :rhino/"rhino" (which will
|
152
|
+
# be expanded unto Juicer::Install::RhinoInstaller). Version is optional
|
153
|
+
# and defaults to latest and greatest.
|
154
|
+
#
|
155
|
+
def dependency(dependency, version = nil)
|
156
|
+
dependency = Juicer::Install.get(dependency) if [String, Symbol].include?(dependency.class)
|
157
|
+
|
158
|
+
@dependencies[dependency.to_s + (version || "")] = [dependency, version]
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Yields depencies one at a time: class and version and returns an array
|
163
|
+
# of arrays: [dependency, version] where dependency is an instance and
|
164
|
+
# version a string.
|
165
|
+
#
|
166
|
+
def dependencies(&block)
|
167
|
+
@dependencies.collect do |name, dependency|
|
168
|
+
version = dependency[1]
|
169
|
+
dependency = dependency[0].new(@install_dir)
|
170
|
+
block.call(dependency, version) if block
|
171
|
+
[dependency, version]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Returns the installer. Accepts installer classes (which are returned
|
178
|
+
# directly), strings or symbols. Strings and symbols may be on the form
|
179
|
+
# :my_module which is expanded to Juicer::Install::MyModuleInstaller
|
180
|
+
#
|
181
|
+
def self.get(nameOrClass)
|
182
|
+
return nameOrClass if nameOrClass.is_a? Class
|
183
|
+
(nameOrClass.to_s + "_installer").classify(Juicer::Install)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), %w[.. .. juicer])) unless defined?(Juicer)
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "base"))
|
3
|
+
require "zip/zip"
|
4
|
+
|
5
|
+
module Juicer
|
6
|
+
module Install
|
7
|
+
#
|
8
|
+
# Install and uninstall routines for the JSLint library by Douglas Crockford.
|
9
|
+
# Installation downloads the jslintfull.js and rhino.js files and stores
|
10
|
+
# them in the Juicer installation directory.
|
11
|
+
#
|
12
|
+
class JSLintInstaller < Base
|
13
|
+
attr_reader :latest
|
14
|
+
|
15
|
+
def initialize(install_dir = Juicer.home)
|
16
|
+
super(install_dir)
|
17
|
+
@latest = "1.0"
|
18
|
+
@website = "http://www.jslint.com/"
|
19
|
+
@path = "lib/jslint"
|
20
|
+
@name = "JsLint"
|
21
|
+
dependency :rhino
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Install JSLint. Downloads the two js files and stores them in the
|
26
|
+
# installation directory.
|
27
|
+
#
|
28
|
+
def install(version = nil)
|
29
|
+
version = super(version)
|
30
|
+
filename = download(File.join(@website, "rhino/jslint.js"))
|
31
|
+
File.copy(filename, File.join(@install_dir, path, "bin", "jslint-#{version}.js"))
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Uninstalls JSLint
|
36
|
+
#
|
37
|
+
def uninstall(version = nil)
|
38
|
+
super(version) do |dir, version|
|
39
|
+
File.delete(File.join(dir, "bin", "jslint-#{version}.js"))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# This class makes it possible to do Juicer.install("jslint") instead of
|
46
|
+
# Juicer.install("j_s_lint"). Sugar, sugar...
|
47
|
+
#
|
48
|
+
class JslintInstaller < JSLintInstaller
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|