shift 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.yardopts +7 -0
- data/LICENSE +20 -0
- data/README.md +100 -0
- data/Rakefile +17 -0
- data/lib/shift/c/closure_compiler.rb +19 -0
- data/lib/shift/c/coffee_script.rb +23 -0
- data/lib/shift/c/identity.rb +116 -0
- data/lib/shift/c/rdiscount.rb +18 -0
- data/lib/shift/c/redcarpet.rb +18 -0
- data/lib/shift/c/sass.rb +17 -0
- data/lib/shift/c/uglify_js.rb +19 -0
- data/lib/shift/c/yui_compressor.rb +22 -0
- data/lib/shift/errors.rb +20 -0
- data/lib/shift/mappings.rb +32 -0
- data/lib/shift.rb +60 -0
- data/shift.gemspec +23 -0
- data/test/c/closure_compiler_test.rb +33 -0
- data/test/c/coffee_script_test.rb +23 -0
- data/test/c/identity_test.rb +79 -0
- data/test/c/rdiscount_test.rb +18 -0
- data/test/c/redcarpet_test.rb +18 -0
- data/test/c/sass_test.rb +14 -0
- data/test/c/uglify_js_test.rb +33 -0
- data/test/c/yui_compressor_test.rb +33 -0
- data/test/data/letter.echo +6 -0
- data/test/data/piglet.coffee +5 -0
- data/test/helper.rb +37 -0
- data/test/shift_test.rb +65 -0
- data/test/support.rb +37 -0
- metadata +93 -0
data/.yardopts
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jostein Berre Eliassen
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
Shift (draft)
|
3
|
+
=====
|
4
|
+
|
5
|
+
**State and logic-less compiler and compressor interface**
|
6
|
+
|
7
|
+
---
|
8
|
+
|
9
|
+
Shift is a generic Ruby interface to different compilers, compressors, transformers and so on. What the [Tilt](https://github.com/rtomayko/tilt) gem does for template languages, Shift does for stuff that compiles in one step, without stateful template logic.
|
10
|
+
|
11
|
+
* [Documentation](http://rubydoc.info/github/jbe/shift/master/frames)
|
12
|
+
* [File type mappings](http://rubydoc.info/github/jbe/shift/master/Shift)
|
13
|
+
|
14
|
+
### Installation
|
15
|
+
|
16
|
+
gem install tilt
|
17
|
+
|
18
|
+
|
19
|
+
### Usage
|
20
|
+
|
21
|
+
To read and process a file, using the preferred available default component for that filetype:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
|
25
|
+
Shift.read('thefile.js') # => minified js string
|
26
|
+
|
27
|
+
```
|
28
|
+
|
29
|
+
Or to read, process, and then write:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
|
33
|
+
Shift.read('canopy.sass').write('canopy.css')
|
34
|
+
|
35
|
+
```
|
36
|
+
|
37
|
+
The components can also be used directly:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
|
41
|
+
cc = Shift::ClosureCompiler.new(:compilation_level => 'ADVANCED_OPTIMIZATIONS')
|
42
|
+
minified_js_string = cc.read(path)
|
43
|
+
|
44
|
+
```
|
45
|
+
|
46
|
+
To simply process a string, or to process and save a string:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
|
50
|
+
md = Shift::RDiscount.new
|
51
|
+
md.process("hello *there*") # => "<p>hello <em>there</em></p>"
|
52
|
+
md.process("hello *there*").write('message.html')
|
53
|
+
|
54
|
+
```
|
55
|
+
To see if a component is available (check if the gem is installed and so on):
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
|
59
|
+
Shift::YUICompressor.available?.
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
To get the first available preferred default component class for a file:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
|
67
|
+
Shift['somefile.js'] # => Shift::UglifyJS
|
68
|
+
|
69
|
+
```
|
70
|
+
|
71
|
+
You can also do:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
|
75
|
+
Shift[:md] # => Shift::RDiscount
|
76
|
+
|
77
|
+
```
|
78
|
+
|
79
|
+
### Available engines
|
80
|
+
|
81
|
+
* UglifyJS
|
82
|
+
* ClosureCompiler
|
83
|
+
* YUICompressor
|
84
|
+
* CoffeeScript
|
85
|
+
* Sass
|
86
|
+
* RDiscount
|
87
|
+
* Redcarpet
|
88
|
+
|
89
|
+
|
90
|
+
### Why not use or extend Tilt instead?
|
91
|
+
|
92
|
+
I am making a separate library for this rather than extending Tilt, because i would usually only need one of the two in a given context. One and two step compilation are somewhat different things. Shift is more on the build side. Tilt is more on the dynamic side.
|
93
|
+
|
94
|
+
### Bye
|
95
|
+
|
96
|
+
Bye Bye, see you. There are proper [docs](http://rubydoc.info/github/jbe/shift/master/frames) if you want more. And once again, [the mappings are there too](http://rubydoc.info/github/jbe/shift/master/Shift). Contributions and feedback is welcome, of course.
|
97
|
+
|
98
|
+
---
|
99
|
+
|
100
|
+
© 2011 Jostein Berre Eliassen. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
task :test do
|
3
|
+
$LOAD_PATH.unshift './lib'
|
4
|
+
require 'shift'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
begin; require 'turn'; rescue LoadError; end
|
7
|
+
Dir.glob("test/**/*_test.rb").each { |test| require "./#{test}" }
|
8
|
+
end
|
9
|
+
|
10
|
+
task :shell do
|
11
|
+
system 'pry -I lib -r shift'
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Shift
|
4
|
+
class ClosureCompiler < Identity
|
5
|
+
|
6
|
+
def self.gem_dependencies
|
7
|
+
%w{closure-compiler}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.compiler_class
|
11
|
+
Closure::Compiler
|
12
|
+
end
|
13
|
+
|
14
|
+
def process_plain(str)
|
15
|
+
@engine.compile(str)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Shift
|
3
|
+
class CoffeeScript < Identity
|
4
|
+
|
5
|
+
def self.gem_dependencies
|
6
|
+
%w{coffee-script}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.require_libs
|
10
|
+
%w{coffee-script}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(opts={})
|
14
|
+
@opts = opts
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def process_plain(str)
|
19
|
+
::CoffeeScript.compile(str, @opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Shift
|
4
|
+
|
5
|
+
# Returns the input unaltered. The purpose is mainly to inherit
|
6
|
+
# from this when defining other, more useful compilers.
|
7
|
+
#
|
8
|
+
class Identity
|
9
|
+
|
10
|
+
# Mixed into the resulting strings to make them easy to save.
|
11
|
+
#
|
12
|
+
module StringExtension
|
13
|
+
|
14
|
+
# Write the string to a sepcified path.
|
15
|
+
#
|
16
|
+
def write(path)
|
17
|
+
File.open(path, 'w') do |file|
|
18
|
+
file.write(self)
|
19
|
+
end
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# One-liner on what the user must have/do to make it available.
|
26
|
+
# Used in DependencyError.
|
27
|
+
#
|
28
|
+
def self.instructions
|
29
|
+
if gem_dependencies.any?
|
30
|
+
'gem install ' + gem_dependencies.join(' ')
|
31
|
+
else
|
32
|
+
'Google it :)'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Wether the requirements are met in the current environment.
|
37
|
+
# Typically checks if the required gems and/or command line
|
38
|
+
# stuff is available.
|
39
|
+
#
|
40
|
+
def self.available?
|
41
|
+
gem_dependencies.all? {|d| Gem.available?(d) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# A list of Rubygems needed for the component to work.
|
45
|
+
#
|
46
|
+
def self.gem_dependencies
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
# A list of things to be required on initialization.
|
51
|
+
#
|
52
|
+
def self.require_libs
|
53
|
+
gem_dependencies
|
54
|
+
end
|
55
|
+
|
56
|
+
# The class of the wrapped compiler, or false if none
|
57
|
+
# is used.
|
58
|
+
#
|
59
|
+
def self.compiler_class
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
# A default instance without options.
|
64
|
+
#
|
65
|
+
def self.default
|
66
|
+
@default ||= new
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.new(*prms)
|
70
|
+
unless available?
|
71
|
+
raise Shift::DependencyError, "#{self} not available. " +
|
72
|
+
"Possible fix: #{instructions}"
|
73
|
+
end
|
74
|
+
@req ||= require_libs.each {|str| require str }
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create a new instance. Ignores the given options.
|
79
|
+
#
|
80
|
+
def initialize(*prms)
|
81
|
+
if self.class.compiler_class
|
82
|
+
@engine = self.class.compiler_class.new(*prms)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Process the supplied string, returning the resulting `String` (with a #write method attached to it).
|
87
|
+
#
|
88
|
+
def process(str)
|
89
|
+
process_plain(str).extend(StringExtension)
|
90
|
+
end
|
91
|
+
alias :compress :process
|
92
|
+
alias :compile :process
|
93
|
+
alias :transform :process
|
94
|
+
|
95
|
+
# Process the supplied string, returning the resulting `String`.
|
96
|
+
#
|
97
|
+
def process_plain(str)
|
98
|
+
str.dup
|
99
|
+
end
|
100
|
+
|
101
|
+
# Read and process a file.
|
102
|
+
#
|
103
|
+
# @raise [DependencyError] when none of the mapped
|
104
|
+
# implementations are available.
|
105
|
+
#
|
106
|
+
# @return [String] The processed `String`
|
107
|
+
#
|
108
|
+
def read(path)
|
109
|
+
process(File.read(path)).extend(StringExtension)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
Echo = Identity
|
114
|
+
end
|
115
|
+
|
116
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Shift
|
3
|
+
class RDiscount < Identity
|
4
|
+
|
5
|
+
def self.gem_dependencies
|
6
|
+
%w{rdiscount}
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(*switches)
|
10
|
+
@switches = switches
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_plain(str)
|
14
|
+
::RDiscount.new(str, *@switches).to_html
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Shift
|
2
|
+
class Redcarpet < Identity
|
3
|
+
|
4
|
+
def self.gem_dependencies
|
5
|
+
%w{redcarpet}
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(*switches)
|
9
|
+
@switches = switches
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_plain(str)
|
13
|
+
::Redcarpet.new(str, *@switches).to_html
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
RedCarpet = Redcarpet
|
18
|
+
end
|
data/lib/shift/c/sass.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Shift
|
3
|
+
class YUICompressor < Identity
|
4
|
+
|
5
|
+
def self.gem_dependencies
|
6
|
+
%w{yui-compressor}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.require_libs
|
10
|
+
%w{yui/compressor}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.compiler_class
|
14
|
+
YUI::JavaScriptCompressor
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_plain(str)
|
18
|
+
@engine.compress(str)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/shift/errors.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Shift
|
4
|
+
|
5
|
+
# Shift base error, from which other errors inherit.
|
6
|
+
#
|
7
|
+
class Error < StandardError; end
|
8
|
+
|
9
|
+
# Raised when the environment needed to perform a given
|
10
|
+
# action is not available. Typically, you will need to
|
11
|
+
# install another gem or some library/software.
|
12
|
+
#
|
13
|
+
class DependencyError < Error; end
|
14
|
+
|
15
|
+
# Raised when you try to read a file for which there are
|
16
|
+
# no mappings without specifying its type.
|
17
|
+
#
|
18
|
+
class UnknownFormatError < Error; end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Shift
|
3
|
+
|
4
|
+
# Mappings from file names to implementation classes. The
|
5
|
+
# classes are listed in order of preference per type.
|
6
|
+
#
|
7
|
+
MAPPINGS = {
|
8
|
+
'echo' => %w{ Identity },
|
9
|
+
'js' => %w{ UglifyJS ClosureCompiler YUICompressor },
|
10
|
+
'coffee' => %w{ CoffeeScript },
|
11
|
+
'sass' => %w{ Sass },
|
12
|
+
'md' => %w{ RDiscount Redcarpet }
|
13
|
+
}
|
14
|
+
|
15
|
+
# @raise [DependencyError] when none of the mapped
|
16
|
+
# implementations are available.
|
17
|
+
#
|
18
|
+
# @return [Class] The preferred available class associated
|
19
|
+
# with the file or extension.
|
20
|
+
#
|
21
|
+
def self.best_available_mapping_for(key)
|
22
|
+
MAPPINGS[key].each do |kls_name|
|
23
|
+
kls = const_get(kls_name)
|
24
|
+
return kls if kls.available?
|
25
|
+
end
|
26
|
+
help = const_get(MAPPINGS[key].first)::INSTRUCTIONS
|
27
|
+
raise DependencyError, "no implementation available for " +
|
28
|
+
"#{key.inspect}. Possible solution: #{help}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
data/lib/shift.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# SHIFT
|
4
|
+
#
|
5
|
+
# State and logic-less compiler and compressor interface
|
6
|
+
#
|
7
|
+
# (c) 2011 Jostein Berre Eliassen - MIT licence
|
8
|
+
|
9
|
+
|
10
|
+
module Shift
|
11
|
+
|
12
|
+
VERSION = '0.1.0'
|
13
|
+
|
14
|
+
require 'shift/errors'
|
15
|
+
require 'shift/mappings'
|
16
|
+
|
17
|
+
|
18
|
+
# components
|
19
|
+
|
20
|
+
autoload :Identity, 'shift/c/identity'
|
21
|
+
autoload :UglifyJS, 'shift/c/uglify_js'
|
22
|
+
autoload :ClosureCompiler, 'shift/c/closure_compiler'
|
23
|
+
autoload :YUICompressor, 'shift/c/yui_compressor'
|
24
|
+
autoload :CoffeeScript, 'shift/c/coffee_script'
|
25
|
+
autoload :Sass, 'shift/c/sass'
|
26
|
+
autoload :RDiscount, 'shift/c/rdiscount'
|
27
|
+
autoload :Redcarpet, 'shift/c/redcarpet'
|
28
|
+
autoload :RedCarpet, 'shift/c/redcarpet'
|
29
|
+
|
30
|
+
|
31
|
+
# Read and process a file with the mapped component.
|
32
|
+
#
|
33
|
+
# @see Shift.[]
|
34
|
+
#
|
35
|
+
# (see Identity#read)
|
36
|
+
#
|
37
|
+
def self.read(path, opts={})
|
38
|
+
self[path].new(opts).read(path)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the preferred available class mapped to match the
|
42
|
+
# given filename or extension.
|
43
|
+
#
|
44
|
+
# @raise [UnknownFormatError] when none of the mappings match.
|
45
|
+
#
|
46
|
+
# (see Shift.best_available_mapping_for)
|
47
|
+
#
|
48
|
+
def self.[](file)
|
49
|
+
pattern = File.basename(file.to_s.downcase)
|
50
|
+
until pattern.empty?
|
51
|
+
if MAPPINGS[pattern]
|
52
|
+
return best_available_mapping_for(pattern)
|
53
|
+
end
|
54
|
+
pattern.sub!(/^[^.]*\.?/, '')
|
55
|
+
end
|
56
|
+
raise UnknownFormatError, "no mapping matches #{file}"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
data/shift.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'shift'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
|
6
|
+
s.name = 'shift'
|
7
|
+
|
8
|
+
s.author = 'Jostein Berre Eliassen'
|
9
|
+
s.email = 'josteinpost@gmail.com'
|
10
|
+
s.homepage = "http://github.com/jbe/shift"
|
11
|
+
s.license = "MIT"
|
12
|
+
|
13
|
+
s.version = Shift::VERSION
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
|
16
|
+
s.summary = 'Shift is a generic interface to different compilers, compressors, etc.'
|
17
|
+
s.description = 'Shift is a generic interface to different compilers, compressors, etc. What the Tilt gem does for template languages, Shift does for compilers and compressors.'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.bindir = 'bin'
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class ClosureCompilerTest < IdentityTest
|
5
|
+
|
6
|
+
FUNCTION = <<-eos
|
7
|
+
function hello(name) {
|
8
|
+
alert('Hello, ' + name);
|
9
|
+
}
|
10
|
+
eos
|
11
|
+
|
12
|
+
INVOCATION = <<-eos
|
13
|
+
hello('New user');
|
14
|
+
eos
|
15
|
+
|
16
|
+
def subject
|
17
|
+
Shift::ClosureCompiler
|
18
|
+
end
|
19
|
+
|
20
|
+
def options
|
21
|
+
{:compilation_level => 'ADVANCED_OPTIMIZATIONS'}
|
22
|
+
end
|
23
|
+
|
24
|
+
def transformations
|
25
|
+
{
|
26
|
+
[FUNCTION, INVOCATION].join("\n") =>
|
27
|
+
"alert(\"Hello, New user\");\n",
|
28
|
+
FUNCTION => "\n",
|
29
|
+
INVOCATION => "hello(\"New user\");\n"
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class CoffeeScriptTest < IdentityTest
|
5
|
+
|
6
|
+
def subject
|
7
|
+
Shift::CoffeeScript
|
8
|
+
end
|
9
|
+
|
10
|
+
def options
|
11
|
+
{:bare => true}
|
12
|
+
end
|
13
|
+
|
14
|
+
def transformations
|
15
|
+
{
|
16
|
+
'hello' => 'hello;',
|
17
|
+
'x = 23' => "var x;\nx = 23;",
|
18
|
+
'frog -> "croak"' =>
|
19
|
+
"frog(function() {\n return \"croak\";\n});"
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'helper')
|
2
|
+
|
3
|
+
# Test Shift::Identity.
|
4
|
+
class IdentityTest < TestCase
|
5
|
+
|
6
|
+
def subject
|
7
|
+
Shift::Identity
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance
|
11
|
+
@instance ||= subject.new(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def options
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def transformations
|
19
|
+
{
|
20
|
+
'echo' => 'echo',
|
21
|
+
'' => ''
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'instructions' do
|
26
|
+
unless subject == Shift::Identity
|
27
|
+
refute_same subject.instructions,
|
28
|
+
subject.superclass.instructions,
|
29
|
+
'gives instructions'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
test 'has_default_instance' do
|
34
|
+
assert_instance_of subject, subject.default
|
35
|
+
end
|
36
|
+
|
37
|
+
test 'process' do
|
38
|
+
transformations.each do |a, b|
|
39
|
+
assert_equal b, instance.process(a)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# the usual ruby system file errors will just pass through,
|
44
|
+
# no use in testing for them.
|
45
|
+
|
46
|
+
test 'read' do
|
47
|
+
transformations.each do |a, b|
|
48
|
+
with_tempfile(a) do |path|
|
49
|
+
assert_equal b, instance.read(path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
test 'read and write' do
|
55
|
+
transformations.each do |a, b|
|
56
|
+
with_tempfile(a) do |src_path|
|
57
|
+
with_tempfile do |trg_path|
|
58
|
+
instance.read(src_path).write(trg_path)
|
59
|
+
assert_equal b, IO.read(trg_path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
test 'process and write' do
|
66
|
+
transformations.each do |a, b|
|
67
|
+
with_tempfile do |path|
|
68
|
+
instance.process(a).write(path)
|
69
|
+
assert_equal b, IO.read(path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
test 'initialize when unavailable' do
|
75
|
+
wrap = Class.new(subject).extend(Unavabelizer)
|
76
|
+
assert_raises(Shift::DependencyError) { wrap.new }
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class RDiscountTest < IdentityTest
|
5
|
+
|
6
|
+
def subject
|
7
|
+
Shift::RDiscount
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance
|
11
|
+
subject.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def transformations
|
15
|
+
{ 'hello' => "<p>hello</p>\n" }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class RedCarpetTest < IdentityTest
|
5
|
+
|
6
|
+
def subject
|
7
|
+
Shift::Redcarpet
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance
|
11
|
+
subject.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def transformations
|
15
|
+
{ 'hello' => "<p>hello</p>\n" }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/test/c/sass_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class UglifyJSTest < IdentityTest
|
5
|
+
|
6
|
+
FUNCTION = <<-eos
|
7
|
+
function hello(name) {
|
8
|
+
alert('Hello, ' + name);
|
9
|
+
}
|
10
|
+
eos
|
11
|
+
|
12
|
+
INVOCATION = <<-eos
|
13
|
+
hello('New user');
|
14
|
+
eos
|
15
|
+
|
16
|
+
def subject
|
17
|
+
Shift::UglifyJS
|
18
|
+
end
|
19
|
+
|
20
|
+
def options
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
def transformations
|
25
|
+
{
|
26
|
+
[FUNCTION, INVOCATION].join("\n") =>
|
27
|
+
"function hello(a){alert(\"Hello, \"+a)}hello(\"New user\")",
|
28
|
+
FUNCTION => "function hello(a){alert(\"Hello, \"+a)}",
|
29
|
+
INVOCATION => "hello(\"New user\")"
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'identity_test')
|
2
|
+
|
3
|
+
|
4
|
+
class YUICompressorTest < IdentityTest
|
5
|
+
|
6
|
+
FUNCTION = <<-eos
|
7
|
+
function hello(name) {
|
8
|
+
alert('Hello, ' + name);
|
9
|
+
}
|
10
|
+
eos
|
11
|
+
|
12
|
+
INVOCATION = <<-eos
|
13
|
+
hello('New user');
|
14
|
+
eos
|
15
|
+
|
16
|
+
def subject
|
17
|
+
Shift::YUICompressor
|
18
|
+
end
|
19
|
+
|
20
|
+
def options
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
def transformations
|
25
|
+
{
|
26
|
+
[FUNCTION, INVOCATION].join("\n") =>
|
27
|
+
"function hello(name){alert(\"Hello, \"+name)}hello(\"New user\");",
|
28
|
+
FUNCTION => "function hello(name){alert(\"Hello, \"+name)};",
|
29
|
+
INVOCATION => "hello(\"New user\");"
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class TestCase < MiniTest::Unit::TestCase
|
4
|
+
def self.test(name, &block)
|
5
|
+
test_name = "test_#{name.gsub(/\s+/, '_')}".to_sym
|
6
|
+
defined = instance_method(test_name) rescue false
|
7
|
+
raise "#{test_name} already defined in #{self}" if defined
|
8
|
+
|
9
|
+
block ||= proc { skip }
|
10
|
+
define_method(test_name, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_tempfile(data=nil)
|
14
|
+
file = Tempfile.new('shift_test')
|
15
|
+
file.write(data) if data
|
16
|
+
file.close
|
17
|
+
yield(file.path)
|
18
|
+
file.unlink
|
19
|
+
end
|
20
|
+
|
21
|
+
def file(name)
|
22
|
+
File.join(File.dirname(__FILE__), 'data', name)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
module Unavabelizer
|
32
|
+
def available?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
data/test/shift_test.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
require File.join(File.dirname(__FILE__), 'support')
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
# Test Shift base module methods
|
8
|
+
class ShiftTest < TestCase
|
9
|
+
|
10
|
+
test 'read nonexistant file' do
|
11
|
+
assert_raises(Errno::ENOENT) do
|
12
|
+
Shift.read('/ubongobaluba-hepp-piglets/seriously.echo')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'read file' do
|
17
|
+
assert_match Shift.read(file 'letter.echo'),
|
18
|
+
/electric power drill/
|
19
|
+
end
|
20
|
+
|
21
|
+
test 'reads existing file and writes result' do
|
22
|
+
with_tempfile do |path|
|
23
|
+
Shift.read(file('letter.echo')).write(path)
|
24
|
+
assert_equal(IO.read(file 'letter.echo'), IO.read(path))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# mappings
|
30
|
+
|
31
|
+
test 'has mappings' do
|
32
|
+
assert_kind_of Hash, Shift::MAPPINGS
|
33
|
+
end
|
34
|
+
|
35
|
+
test 'reads unmapped file' do
|
36
|
+
assert_raises(Shift::UnknownFormatError) do
|
37
|
+
Shift.read('oy-oy-oy.noformatforthis')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'gets available mapping' do
|
42
|
+
assert_equal Shift[:echo], Shift::Identity, 'given type'
|
43
|
+
assert_equal Shift['treaty.null'], NullComponent, 'given path'
|
44
|
+
assert_equal Shift['some/path/hey.bingo.bongo'],
|
45
|
+
NullComponent, 'with detailed mapping'
|
46
|
+
end
|
47
|
+
|
48
|
+
test 'prioritizes' do
|
49
|
+
assert_equal Shift['file.null'], NullComponent,
|
50
|
+
'first when available'
|
51
|
+
assert_equal Shift['file.prioritized'], NullComponent,
|
52
|
+
'second when available but not first'
|
53
|
+
end
|
54
|
+
|
55
|
+
test 'raises DependencyError' do
|
56
|
+
begin
|
57
|
+
Shift[:unavailable] # defined in test/support.rb
|
58
|
+
assert false, 'when no components available'
|
59
|
+
rescue Shift::DependencyError => err
|
60
|
+
assert_match /meditate/, err.message, 'with helpful message'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/test/support.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
class UnavailableComponent < Shift::Identity
|
3
|
+
|
4
|
+
INSTRUCTIONS = 'meditate.'
|
5
|
+
|
6
|
+
def self.available?
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class NullComponent < Shift::Identity
|
13
|
+
|
14
|
+
def self.available?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(str)
|
19
|
+
''
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Shift::MAPPINGS['unavailable'] = ['UnavailableComponent'] * 10
|
25
|
+
|
26
|
+
Shift::MAPPINGS['null'] = %w{NullComponent}
|
27
|
+
Shift::MAPPINGS['bingo.bongo'] = %w{NullComponent}
|
28
|
+
|
29
|
+
Shift::MAPPINGS['prioritized'] = %w{UnavailableComponent
|
30
|
+
NullComponent Identity}
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shift
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jostein Berre Eliassen
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-05-09 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Shift is a generic interface to different compilers, compressors, etc. What the Tilt gem does for template languages, Shift does for compilers and compressors.
|
22
|
+
email: josteinpost@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- .gitignore
|
31
|
+
- .yardopts
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
- Rakefile
|
35
|
+
- lib/shift.rb
|
36
|
+
- lib/shift/c/closure_compiler.rb
|
37
|
+
- lib/shift/c/coffee_script.rb
|
38
|
+
- lib/shift/c/identity.rb
|
39
|
+
- lib/shift/c/rdiscount.rb
|
40
|
+
- lib/shift/c/redcarpet.rb
|
41
|
+
- lib/shift/c/sass.rb
|
42
|
+
- lib/shift/c/uglify_js.rb
|
43
|
+
- lib/shift/c/yui_compressor.rb
|
44
|
+
- lib/shift/errors.rb
|
45
|
+
- lib/shift/mappings.rb
|
46
|
+
- shift.gemspec
|
47
|
+
- test/c/closure_compiler_test.rb
|
48
|
+
- test/c/coffee_script_test.rb
|
49
|
+
- test/c/identity_test.rb
|
50
|
+
- test/c/rdiscount_test.rb
|
51
|
+
- test/c/redcarpet_test.rb
|
52
|
+
- test/c/sass_test.rb
|
53
|
+
- test/c/uglify_js_test.rb
|
54
|
+
- test/c/yui_compressor_test.rb
|
55
|
+
- test/data/letter.echo
|
56
|
+
- test/data/piglet.coffee
|
57
|
+
- test/helper.rb
|
58
|
+
- test/shift_test.rb
|
59
|
+
- test/support.rb
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/jbe/shift
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
|
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
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.3.7
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Shift is a generic interface to different compilers, compressors, etc.
|
92
|
+
test_files: []
|
93
|
+
|