shift 0.1.0 → 0.4.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/CHANGELOG +20 -0
- data/README.md +105 -37
- data/Rakefile +1 -1
- data/TODO +3 -0
- data/bin/shifter +5 -0
- data/lib/shift.rb +42 -36
- data/lib/shift/action_map.rb +106 -0
- data/lib/shift/cli.rb +43 -0
- data/lib/shift/errors.rb +7 -3
- data/lib/shift/i/closure_compiler.rb +23 -0
- data/lib/shift/{c → i}/coffee_script.rb +6 -3
- data/lib/shift/i/echo.rb +9 -0
- data/lib/shift/{c → i}/rdiscount.rb +6 -2
- data/lib/shift/{c → i}/redcarpet.rb +6 -2
- data/lib/shift/{c → i}/sass.rb +6 -2
- data/lib/shift/{c → i}/uglify_js.rb +7 -3
- data/lib/shift/{c → i}/yui_compressor.rb +7 -3
- data/lib/shift/i/zlib_reader.rb +23 -0
- data/lib/shift/i/zlib_writer.rb +27 -0
- data/lib/shift/{c/identity.rb → interface.rb} +39 -42
- data/lib/shift/interface_list.rb +67 -0
- data/lib/shift/interfaces.rb +20 -0
- data/lib/shift/mapper.rb +95 -0
- data/lib/shift/mappings.rb +51 -28
- data/lib/shift/mappings.yml +6 -0
- data/lib/shift/string.rb +125 -0
- data/shift.gemspec +3 -2
- data/test/data/letter.echo +0 -5
- data/test/helper.rb +2 -12
- data/test/{c → i}/closure_compiler_test.rb +6 -2
- data/test/{c → i}/coffee_script_test.rb +6 -2
- data/test/i/rdiscount_test.rb +22 -0
- data/test/i/redcarpet_test.rb +22 -0
- data/test/i/sass_test.rb +19 -0
- data/test/{c → i}/uglify_js_test.rb +6 -2
- data/test/{c → i}/yui_compressor_test.rb +6 -2
- data/test/i/zlib_reader.rb +24 -0
- data/test/interface_test.rb +60 -0
- data/test/mapper_test.rb +144 -0
- data/test/shift_test.rb +12 -43
- data/test/string_test.rb +39 -0
- metadata +39 -24
- data/lib/shift/c/closure_compiler.rb +0 -19
- data/test/c/identity_test.rb +0 -79
- data/test/c/rdiscount_test.rb +0 -18
- data/test/c/redcarpet_test.rb +0 -18
- data/test/c/sass_test.rb +0 -14
- data/test/support.rb +0 -37
data/CHANGELOG
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
HEAD
|
4
|
+
Interface defaults
|
5
|
+
|
6
|
+
0.4.0
|
7
|
+
Major restructuring
|
8
|
+
Shift::String for method chaning
|
9
|
+
Gzip support
|
10
|
+
|
11
|
+
0.3.0
|
12
|
+
Shell command
|
13
|
+
|
14
|
+
0.2.0
|
15
|
+
Shift.inspect_mappings
|
16
|
+
Named actions
|
17
|
+
|
18
|
+
0.1.0
|
19
|
+
Working Rubygem with support for some formats.
|
20
|
+
|
data/README.md
CHANGED
@@ -1,99 +1,167 @@
|
|
1
1
|
|
2
|
-
Shift
|
2
|
+
Shift
|
3
3
|
=====
|
4
4
|
|
5
|
-
**
|
5
|
+
**Compiler and compressor interface**
|
6
6
|
|
7
7
|
---
|
8
8
|
|
9
|
-
Shift is a generic Ruby interface to different compilers, compressors, transformers and so on.
|
9
|
+
Shift is a generic Ruby interface to different compilers, compressors, transformers and so on. It is also an easy way to chain actions that transform something and build compiler chains.
|
10
10
|
|
11
|
-
*
|
12
|
-
*
|
11
|
+
* Installation: `gem install shift`
|
12
|
+
* API Documentation
|
13
|
+
* [Latest Gem](http://rubydoc.info/gems/shift/frames)
|
14
|
+
* [Github master](http://rubydoc.info/github/jbe/shift/master/frames)
|
15
|
+
* [Default mappings](https://github.com/jbe/shift/blob/master/lib/shift/mappings.rb)
|
13
16
|
|
14
|
-
|
17
|
+
Shift contains:
|
15
18
|
|
16
|
-
|
19
|
+
* A convention for compiler interfaces
|
20
|
+
* A collection of such interfaces for common compilers, compressors etc.
|
21
|
+
* A way to describe actions that can be performed on a given format (such as `:render` for `markdown` files)
|
22
|
+
* A way to map such format actions to lists of compilers performing that action
|
23
|
+
* A way to return the best available compiler from such a list
|
24
|
+
* Default mappings for all of the above
|
17
25
|
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
To read and process a file, using the preferred available default component for that filetype:
|
27
|
+
This means you can do things like
|
22
28
|
|
23
29
|
```ruby
|
24
30
|
|
25
|
-
|
31
|
+
(Shift.read('cup.coffee').compile.compress << '/* pidgeon */'
|
32
|
+
).gzip.write
|
26
33
|
|
27
34
|
```
|
28
35
|
|
29
|
-
|
36
|
+
This reads `cup.coffee`, compiles it using coffeescript, compresses it using UglifyJS, appends the pidgeon comment to the minified javascript, gzips all of that, and then saves it as `cup.min.js.gz`. Since no file name was given, it is determined automatically.
|
37
|
+
|
38
|
+
The aim is to include a large library of interfaces, which are lazy loaded on demand, to allow a huge variety of operations.
|
39
|
+
|
40
|
+
|
41
|
+
### Examples
|
30
42
|
|
31
43
|
```ruby
|
32
44
|
|
33
|
-
Shift
|
45
|
+
str = Shift("hello", "hi.markdown") # => "hello"
|
46
|
+
str.class # => Shift::String
|
47
|
+
str.name # => "hi.markdown"
|
48
|
+
|
49
|
+
result = str.render # => "<p>hello</p>"
|
50
|
+
result.name # => "hi.html"
|
34
51
|
|
35
52
|
```
|
36
53
|
|
37
|
-
The
|
54
|
+
The `Shift::String` works like a normal string, except that it has an associated name, and that it can be transformed like we just did. The string name is theoretical only; it does not necessarily exist as a file. Therefore, it can also simply represent the format. There are methods to read and write from file paths however:
|
38
55
|
|
39
56
|
```ruby
|
40
57
|
|
41
|
-
|
42
|
-
|
58
|
+
str_a = Shift.read('script.js') # => "var hello; hello = 31;"
|
59
|
+
str_a.class # => Shift::String
|
60
|
+
|
61
|
+
str_b = Shift('hello', 'message.txt')
|
62
|
+
str_b.write
|
63
|
+
str_b.write('message_copy.txt')
|
43
64
|
|
44
65
|
```
|
45
66
|
|
46
|
-
|
67
|
+
Pretty handy. It can automatically write the string to the path that it automatically figured out.
|
68
|
+
|
69
|
+
Now, in the first example, how did it know how to render markdown? Because i have RDiscount installed and it is the preferred default interface for doing `:render` to `.markdown` files.
|
47
70
|
|
48
71
|
```ruby
|
49
72
|
|
50
|
-
|
51
|
-
|
52
|
-
|
73
|
+
str = Shift("hello", "hi.markdown") # => "hello"
|
74
|
+
str.render # => "<p>hello</p>"
|
75
|
+
str.interface # => #<Shift::RDiscount:0xa0ad818>
|
76
|
+
|
77
|
+
Shift[:markdown] # => Shift::RDiscount
|
78
|
+
Shift[:md] # => Shift::RDiscount
|
79
|
+
Shift['somefile.js'] # => Shift::UglifyJS
|
80
|
+
|
81
|
+
Shift[:js, :compress] # => Shift::UglifyJS
|
82
|
+
Shift[:js, :gzip] # => Shift::ZlibCompressor
|
83
|
+
|
84
|
+
puts Shift.inspect_actions
|
85
|
+
# =>
|
86
|
+
# GLOBAL: gzip
|
87
|
+
# echo: default
|
88
|
+
# coffee: compile
|
89
|
+
# gzip, gz: inflate
|
90
|
+
# js: compress
|
91
|
+
# md, markdown: render
|
92
|
+
# sass: compile
|
53
93
|
|
54
94
|
```
|
55
|
-
|
95
|
+
If i tried to look up a format, and it had mappings, but none of the underlying handlers were available, Shift would raise a `Shift::DependencyError` including installation instructions.
|
96
|
+
|
97
|
+
What if i want to work with the interfaces without any magic?
|
56
98
|
|
57
99
|
```ruby
|
58
100
|
|
59
|
-
Shift::
|
101
|
+
Shift::ClosureCompiler.available? # => true
|
102
|
+
|
103
|
+
iface = Shift::ClosureCompiler.new(
|
104
|
+
:compilation_level => 'ADVANCED_OPTIMIZATIONS')
|
105
|
+
iface.process(str)
|
106
|
+
iface.rename(file_path)
|
60
107
|
|
61
108
|
```
|
62
109
|
|
63
|
-
|
110
|
+
Being interfaces, they all work the same way.
|
111
|
+
|
112
|
+
|
113
|
+
### Defining new mappings
|
64
114
|
|
65
115
|
```ruby
|
66
116
|
|
67
|
-
Shift
|
117
|
+
Shift.map('myformat', 'myaliasformat',
|
118
|
+
:default => :crush,
|
119
|
+
:crush => 'MyFormatCrusher',
|
120
|
+
:stabilize => %w{MyFormatStabilizer AlternativeMyFormatStabilizer}
|
121
|
+
)
|
68
122
|
|
69
123
|
```
|
70
124
|
|
71
|
-
|
125
|
+
Or globally, for all formats:
|
72
126
|
|
73
127
|
```ruby
|
128
|
+
Shift.global.map(
|
129
|
+
:mix_up => 'Mixuper'
|
130
|
+
)
|
131
|
+
```
|
74
132
|
|
75
|
-
|
133
|
+
To reset all mappings:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
|
137
|
+
Shift::MAP = {}
|
76
138
|
|
77
139
|
```
|
78
140
|
|
79
|
-
|
141
|
+
* [Default mappings](https://github.com/jbe/shift/blob/master/lib/shift/mappings.rb)
|
142
|
+
|
143
|
+
|
144
|
+
### Shifter command line tool
|
80
145
|
|
81
|
-
|
82
|
-
* ClosureCompiler
|
83
|
-
* YUICompressor
|
84
|
-
* CoffeeScript
|
85
|
-
* Sass
|
86
|
-
* RDiscount
|
87
|
-
* Redcarpet
|
146
|
+
There is also a command line tool called `shifter`.
|
88
147
|
|
148
|
+
```bash
|
89
149
|
|
90
|
-
|
150
|
+
shifter
|
151
|
+
|
152
|
+
shifter sheet.sass
|
153
|
+
shifter file.js compress
|
154
|
+
shifter style.s compile sass
|
155
|
+
|
156
|
+
some-tool | shifter - js > myfile.min.js
|
157
|
+
some-tool | shifter - js compress > myfile.min.js
|
158
|
+
|
159
|
+
```
|
91
160
|
|
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
161
|
|
94
162
|
### Bye
|
95
163
|
|
96
|
-
Bye Bye, see you.
|
164
|
+
Bye Bye, see you. Contribute some interfaces and mappings if you want to.
|
97
165
|
|
98
166
|
---
|
99
167
|
|
data/Rakefile
CHANGED
data/TODO
ADDED
data/bin/shifter
ADDED
data/lib/shift.rb
CHANGED
@@ -2,59 +2,65 @@
|
|
2
2
|
|
3
3
|
# SHIFT
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Compiler and compressor interface framework
|
6
6
|
#
|
7
7
|
# (c) 2011 Jostein Berre Eliassen - MIT licence
|
8
8
|
|
9
9
|
|
10
10
|
module Shift
|
11
11
|
|
12
|
-
VERSION = '0.
|
12
|
+
VERSION = '0.4.0'
|
13
13
|
|
14
14
|
require 'shift/errors'
|
15
|
+
require 'shift/mapper'
|
16
|
+
require 'shift/interfaces'
|
15
17
|
require 'shift/mappings'
|
16
18
|
|
19
|
+
autoload :String, 'shift/string'
|
17
20
|
|
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
21
|
|
22
|
+
class << self
|
23
|
+
|
24
|
+
# (see Shift::String.new)
|
25
|
+
#
|
26
|
+
def new(*args)
|
27
|
+
Shift::String.new(*args)
|
28
|
+
end
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
30
|
+
# Read a file, returning a Shift string.
|
31
|
+
# @param [String] path the file to read from.
|
32
|
+
# @param [String] new_path treat the shift string as if it came
|
33
|
+
# from this path. Can be used to override file type behavior.
|
34
|
+
#
|
35
|
+
# (see Shift.new)
|
36
|
+
#
|
37
|
+
def read(path, new_path=nil)
|
38
|
+
new(File.read(path), new_path || path)
|
39
|
+
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
return best_available_mapping_for(pattern)
|
41
|
+
# Read and concatenate several files.
|
42
|
+
# (see Shift.read)
|
43
|
+
#
|
44
|
+
# TODO: glob
|
45
|
+
#
|
46
|
+
def concat(*globs)
|
47
|
+
buf = new('', File.extname(globs.first))
|
48
|
+
globs.each do |glob|
|
49
|
+
Dir[glob].each do |file|
|
50
|
+
buf.read_append(file)
|
51
|
+
end
|
53
52
|
end
|
54
|
-
|
53
|
+
buf
|
55
54
|
end
|
56
|
-
|
55
|
+
alias :cat :concat
|
56
|
+
|
57
57
|
end
|
58
|
+
end
|
58
59
|
|
60
|
+
# (see Shift.new)
|
61
|
+
#
|
62
|
+
def Shift(*args)
|
63
|
+
Shift.new(*args)
|
59
64
|
end
|
60
65
|
|
66
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
module Shift
|
3
|
+
|
4
|
+
# A mapping of actions to interfaces. Handles validation,
|
5
|
+
# lookup, updates, link walking etc.
|
6
|
+
#
|
7
|
+
class ActionMap
|
8
|
+
|
9
|
+
def initialize(fallback, actions={})
|
10
|
+
@actions = {}
|
11
|
+
@fallback = fallback
|
12
|
+
map(actions)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :fallback
|
16
|
+
|
17
|
+
# Return a duplicate ActionHash without fallback,
|
18
|
+
# to be used for local queries.
|
19
|
+
def local
|
20
|
+
self.class.new(nil).map(@actions)
|
21
|
+
end
|
22
|
+
|
23
|
+
def map(actions)
|
24
|
+
begin
|
25
|
+
original = @actions.dup
|
26
|
+
parse(actions)
|
27
|
+
validate
|
28
|
+
rescue Shift::Error
|
29
|
+
@actions = original
|
30
|
+
raise
|
31
|
+
end; self
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_hash(inherit=true)
|
35
|
+
(inherit && @fallback) ?
|
36
|
+
@actions.merge(@fallback) : @actions.dup
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return a hash of mappings that are not links
|
40
|
+
def atoms(inherit=false)
|
41
|
+
to_hash(inherit).delete_if {|k,v| v.is_a?(Symbol) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def eql?(other)
|
45
|
+
(eql_id.eql? other.eql_id) &&
|
46
|
+
(@fallback.eql? other.fallback)
|
47
|
+
end
|
48
|
+
alias :== :eql?
|
49
|
+
|
50
|
+
def eql_id
|
51
|
+
[to_hash(false), @fallback]
|
52
|
+
end
|
53
|
+
def hash; eql_id.hash; end
|
54
|
+
|
55
|
+
# Look up an action
|
56
|
+
def [](action)
|
57
|
+
action = action.to_sym
|
58
|
+
item = @actions[action] || (@fallback[action] if @fallback)
|
59
|
+
item.is_a?(Symbol) ? self[item] : item
|
60
|
+
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
'ActionMap' + @actions.inspect
|
64
|
+
end
|
65
|
+
|
66
|
+
def dup
|
67
|
+
self.class.new(fallback ? @fallback.dup : @fallback, @actions)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def parse(hsh)
|
73
|
+
hsh = {:default => hsh} unless hsh.is_a?(Hash)
|
74
|
+
|
75
|
+
hsh.each do |name, handler|
|
76
|
+
h = case handler
|
77
|
+
when Symbol, InterfaceList then handler
|
78
|
+
when Array then handler.map {|cls| cls.to_s }
|
79
|
+
else [handler.to_s]
|
80
|
+
end
|
81
|
+
@actions[name] = h.is_a?(Array) ? InterfaceList.new(h) : h
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def validate
|
87
|
+
@actions.each do |name, handler|
|
88
|
+
cycle_search(handler) if handler.is_a?(Symbol)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def cycle_search(action, visited=[])
|
93
|
+
visited << action
|
94
|
+
item = @actions[action]
|
95
|
+
|
96
|
+
raise(MappingError, "bad link #{action.inspect}") unless item
|
97
|
+
|
98
|
+
if visited.include?(item)
|
99
|
+
raise MappingError, 'cycle detected ' + visited.inspect
|
100
|
+
end
|
101
|
+
|
102
|
+
cycle_search(item, visited) if item.is_a?(Symbol)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|