shift 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +20 -0
  2. data/README.md +105 -37
  3. data/Rakefile +1 -1
  4. data/TODO +3 -0
  5. data/bin/shifter +5 -0
  6. data/lib/shift.rb +42 -36
  7. data/lib/shift/action_map.rb +106 -0
  8. data/lib/shift/cli.rb +43 -0
  9. data/lib/shift/errors.rb +7 -3
  10. data/lib/shift/i/closure_compiler.rb +23 -0
  11. data/lib/shift/{c → i}/coffee_script.rb +6 -3
  12. data/lib/shift/i/echo.rb +9 -0
  13. data/lib/shift/{c → i}/rdiscount.rb +6 -2
  14. data/lib/shift/{c → i}/redcarpet.rb +6 -2
  15. data/lib/shift/{c → i}/sass.rb +6 -2
  16. data/lib/shift/{c → i}/uglify_js.rb +7 -3
  17. data/lib/shift/{c → i}/yui_compressor.rb +7 -3
  18. data/lib/shift/i/zlib_reader.rb +23 -0
  19. data/lib/shift/i/zlib_writer.rb +27 -0
  20. data/lib/shift/{c/identity.rb → interface.rb} +39 -42
  21. data/lib/shift/interface_list.rb +67 -0
  22. data/lib/shift/interfaces.rb +20 -0
  23. data/lib/shift/mapper.rb +95 -0
  24. data/lib/shift/mappings.rb +51 -28
  25. data/lib/shift/mappings.yml +6 -0
  26. data/lib/shift/string.rb +125 -0
  27. data/shift.gemspec +3 -2
  28. data/test/data/letter.echo +0 -5
  29. data/test/helper.rb +2 -12
  30. data/test/{c → i}/closure_compiler_test.rb +6 -2
  31. data/test/{c → i}/coffee_script_test.rb +6 -2
  32. data/test/i/rdiscount_test.rb +22 -0
  33. data/test/i/redcarpet_test.rb +22 -0
  34. data/test/i/sass_test.rb +19 -0
  35. data/test/{c → i}/uglify_js_test.rb +6 -2
  36. data/test/{c → i}/yui_compressor_test.rb +6 -2
  37. data/test/i/zlib_reader.rb +24 -0
  38. data/test/interface_test.rb +60 -0
  39. data/test/mapper_test.rb +144 -0
  40. data/test/shift_test.rb +12 -43
  41. data/test/string_test.rb +39 -0
  42. metadata +39 -24
  43. data/lib/shift/c/closure_compiler.rb +0 -19
  44. data/test/c/identity_test.rb +0 -79
  45. data/test/c/rdiscount_test.rb +0 -18
  46. data/test/c/redcarpet_test.rb +0 -18
  47. data/test/c/sass_test.rb +0 -14
  48. data/test/support.rb +0 -37
@@ -0,0 +1,43 @@
1
+
2
+ require 'shift'
3
+
4
+
5
+ module Shift
6
+
7
+ # The `shifter` command line interface.
8
+ #
9
+ class CLI
10
+
11
+ USAGE = "Usage: shifter <file> [action] [format]\n" +
12
+ "In pipe: shifter - format [action]\n\n" +
13
+ "Available formats and actions:\n\n" +
14
+ Shift.inspect_actions
15
+
16
+ def self.new
17
+ begin
18
+ super(*ARGV)
19
+ rescue ArgumentError
20
+ abort USAGE
21
+ end
22
+ end
23
+
24
+ def initialize(path, a=nil, b=nil)
25
+ @path = path
26
+ @format, @action = stdin? ? [a,b] : [b,a]
27
+ @format ||= :echo
28
+ @action ||= :default
29
+ end
30
+
31
+ def stdin?
32
+ @path == '-'
33
+ end
34
+
35
+ def data
36
+ stdin? ? STDIN.read : File.read(@path)
37
+ end
38
+
39
+ def run!
40
+ puts Shift(data, @format).process(@action)
41
+ end
42
+ end
43
+ end
@@ -12,9 +12,13 @@ module Shift
12
12
  #
13
13
  class DependencyError < Error; end
14
14
 
15
- # Raised when you try to read a file for which there are
16
- # no mappings without specifying its type.
15
+ class LookupError < Error; end
16
+ class UnknownFormatError < LookupError; end
17
+ class UnknownActionError < LookupError; end
18
+
19
+ # Raised when encountering invalid mappings, like
20
+ # when there is a cycle in the map.
17
21
  #
18
- class UnknownFormatError < Error; end
22
+ class MappingError < Error; end
19
23
 
20
24
  end
@@ -0,0 +1,23 @@
1
+
2
+
3
+ module Shift
4
+ class ClosureCompiler < Interface
5
+
6
+ def self.gem_dependencies
7
+ %w{closure-compiler}
8
+ end
9
+
10
+ def self.engine_class
11
+ Closure::Compiler
12
+ end
13
+
14
+ def self.target_format
15
+ 'min.js'
16
+ end
17
+
18
+ def process(str)
19
+ @engine.compile(str)
20
+ end
21
+
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Shift
3
- class CoffeeScript < Identity
3
+ class CoffeeScript < Interface
4
4
 
5
5
  def self.gem_dependencies
6
6
  %w{coffee-script}
@@ -10,12 +10,15 @@ module Shift
10
10
  %w{coffee-script}
11
11
  end
12
12
 
13
+ def self.target_format
14
+ 'js'
15
+ end
16
+
13
17
  def initialize(opts={})
14
18
  @opts = opts
15
19
  end
16
20
 
17
-
18
- def process_plain(str)
21
+ def process(str)
19
22
  ::CoffeeScript.compile(str, @opts)
20
23
  end
21
24
 
@@ -0,0 +1,9 @@
1
+ module Shift
2
+ class Echo < Interface
3
+
4
+ def self.keep_extension?
5
+ true
6
+ end
7
+
8
+ end
9
+ end
@@ -1,16 +1,20 @@
1
1
 
2
2
  module Shift
3
- class RDiscount < Identity
3
+ class RDiscount < Interface
4
4
 
5
5
  def self.gem_dependencies
6
6
  %w{rdiscount}
7
7
  end
8
8
 
9
+ def self.target_format
10
+ 'html'
11
+ end
12
+
9
13
  def initialize(*switches)
10
14
  @switches = switches
11
15
  end
12
16
 
13
- def process_plain(str)
17
+ def process(str)
14
18
  ::RDiscount.new(str, *@switches).to_html
15
19
  end
16
20
 
@@ -1,15 +1,19 @@
1
1
  module Shift
2
- class Redcarpet < Identity
2
+ class Redcarpet < Interface
3
3
 
4
4
  def self.gem_dependencies
5
5
  %w{redcarpet}
6
6
  end
7
7
 
8
+ def self.target_format
9
+ 'html'
10
+ end
11
+
8
12
  def initialize(*switches)
9
13
  @switches = switches
10
14
  end
11
15
 
12
- def process_plain(str)
16
+ def process(str)
13
17
  ::Redcarpet.new(str, *@switches).to_html
14
18
  end
15
19
 
@@ -1,15 +1,19 @@
1
1
  module Shift
2
- class Sass < Identity
2
+ class Sass < Interface
3
3
 
4
4
  def self.gem_dependencies
5
5
  %w{sass}
6
6
  end
7
7
 
8
+ def self.target_format
9
+ 'css'
10
+ end
11
+
8
12
  def initialize(opts={})
9
13
  @opts = opts
10
14
  end
11
15
 
12
- def process_plain(str)
16
+ def process(str)
13
17
  ::Sass::Engine.new(str, @opts).render
14
18
  end
15
19
 
@@ -1,17 +1,21 @@
1
1
 
2
2
 
3
3
  module Shift
4
- class UglifyJS < Identity
4
+ class UglifyJS < Interface
5
5
 
6
6
  def self.gem_dependencies
7
7
  %w{uglifier}
8
8
  end
9
9
 
10
- def self.compiler_class
10
+ def self.engine_class
11
11
  Uglifier
12
12
  end
13
13
 
14
- def process_plain(str)
14
+ def self.target_format
15
+ 'min.js'
16
+ end
17
+
18
+ def process(str)
15
19
  @engine.compile(str)
16
20
  end
17
21
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Shift
3
- class YUICompressor < Identity
3
+ class YUICompressor < Interface
4
4
 
5
5
  def self.gem_dependencies
6
6
  %w{yui-compressor}
@@ -10,11 +10,15 @@ module Shift
10
10
  %w{yui/compressor}
11
11
  end
12
12
 
13
- def self.compiler_class
13
+ def self.engine_class
14
14
  YUI::JavaScriptCompressor
15
15
  end
16
16
 
17
- def process_plain(str)
17
+ def self.target_format
18
+ 'min.js'
19
+ end
20
+
21
+ def process(str)
18
22
  @engine.compress(str)
19
23
  end
20
24
 
@@ -0,0 +1,23 @@
1
+ module Shift
2
+ class ZlibReader < Interface
3
+
4
+ def self.keep_extension?
5
+ false
6
+ end
7
+
8
+ def self.target_format
9
+ false
10
+ end
11
+
12
+ def initialize
13
+ require 'zlib'
14
+ require 'stringio'
15
+ end
16
+
17
+ def process(data)
18
+ gz = Zlib::GzipReader.new(StringIO.new(data))
19
+ gz.read
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Shift
2
+ class ZlibWriter < Interface
3
+
4
+ def self.keep_extension?
5
+ true
6
+ end
7
+
8
+ def self.target_format
9
+ 'gz'
10
+ end
11
+
12
+ def initialize
13
+ require 'zlib'
14
+ require 'stringio'
15
+ end
16
+
17
+ def process(str)
18
+ StringIO.open('', 'w') do |io|
19
+ gz = Zlib::GzipWriter.new(io)
20
+ gz.write(str)
21
+ gz.close
22
+ io.string
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -1,28 +1,13 @@
1
1
 
2
-
3
2
  module Shift
4
3
 
5
- # Returns the input unaltered. The purpose is mainly to inherit
6
- # from this when defining other, more useful compilers.
4
+ # The default Shift interface, from which other interfaces must
5
+ # inherit. Also works as an identity function or echo server, in
6
+ # that it echoes what it is given.
7
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
-
8
+ class Interface
24
9
 
25
- # One-liner on what the user must have/do to make it available.
10
+ # One-liner on what the user can do to make it available.
26
11
  # Used in DependencyError.
27
12
  #
28
13
  def self.instructions
@@ -41,7 +26,7 @@ module Shift
41
26
  gem_dependencies.all? {|d| Gem.available?(d) }
42
27
  end
43
28
 
44
- # A list of Rubygems needed for the component to work.
29
+ # A list of Rubygems needed for the interface to work.
45
30
  #
46
31
  def self.gem_dependencies
47
32
  []
@@ -53,10 +38,23 @@ module Shift
53
38
  gem_dependencies
54
39
  end
55
40
 
56
- # The class of the wrapped compiler, or false if none
41
+ # The class of the wrapped generator, or false if none
57
42
  # is used.
58
43
  #
59
- def self.compiler_class
44
+ def self.engine_class
45
+ false
46
+ end
47
+
48
+ # The format typically produced by the generator.
49
+ #
50
+ def self.target_format
51
+ false
52
+ end
53
+
54
+ # Whether to leave the old extension as is and append, like
55
+ # something.css => something.css.gzip
56
+ #
57
+ def self.keep_extension?
60
58
  false
61
59
  end
62
60
 
@@ -75,42 +73,41 @@ module Shift
75
73
  super
76
74
  end
77
75
 
78
- # Create a new instance. Ignores the given options.
76
+ # Create a new instance with the given options.
79
77
  #
80
78
  def initialize(*prms)
81
- if self.class.compiler_class
82
- @engine = self.class.compiler_class.new(*prms)
79
+ if self.class.engine_class
80
+ @engine = self.class.engine_class.new(*prms)
83
81
  end
84
82
  end
85
83
 
86
- # Process the supplied string, returning the resulting `String` (with a #write method attached to it).
84
+ # Process the supplied string, returning the resulting `String`.
87
85
  #
88
86
  def process(str)
89
- process_plain(str).extend(StringExtension)
87
+ str.dup
90
88
  end
91
89
  alias :compress :process
92
90
  alias :compile :process
93
91
  alias :transform :process
94
92
 
95
- # Process the supplied string, returning the resulting `String`.
93
+ # Get the default filename of a transformed file.
94
+ # @param [String] file Path of the original file
95
+ # @return [String] default file name for the result.
96
96
  #
97
- def process_plain(str)
98
- str.dup
99
- end
97
+ def rename(file)
98
+ return nil if file.nil?
100
99
 
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)
100
+ unless self.class.keep_extension?
101
+ file = file.is_a?(Symbol) ?
102
+ file.to_s : file.chomp(File.extname(file))
103
+ end
104
+ if self.class.target_format
105
+ file = file + '.' + self.class.target_format.to_s
106
+ end
107
+ file
110
108
  end
111
109
 
112
110
  end
113
- Echo = Identity
114
111
  end
115
112
 
116
113
 
@@ -0,0 +1,67 @@
1
+ module Shift
2
+ class InterfaceList
3
+
4
+ # Create a new InterfaceList, given an array of class names
5
+ # of shift interfaces. Immutable once created.
6
+ #
7
+ def initialize(ifaces)
8
+ @interfaces = ifaces
9
+ end
10
+
11
+ def to_hash
12
+ @interfaces.dup
13
+ end
14
+
15
+ def eql?(other)
16
+ @interfaces.eql? other.to_hash
17
+ end
18
+ alias :== :eql?
19
+
20
+ def hash
21
+ @interfaces.hash
22
+ end
23
+
24
+ def inspect
25
+ 'InterfaceList' + @interfaces.inspect
26
+ end
27
+
28
+ # Pick the preferred available interface.
29
+ #
30
+ # @raise [DependencyError] when none of the interfaces
31
+ # are available.
32
+ #
33
+ # @return [Class] The preferred available class
34
+ #
35
+ def pick
36
+ each_class {|kls| return kls if kls.available? }
37
+ raise DependencyError, "nothing available: " +
38
+ help_string
39
+ end
40
+
41
+ private
42
+
43
+ def help_string
44
+ if @interfaces.any?
45
+ @interfaces.inspect + " Possible solution: " +
46
+ first_class.instructions
47
+ else
48
+ 'no interfaces mapped'
49
+ end
50
+ end
51
+
52
+ def each_class
53
+ @interfaces.each do |name|
54
+ yield get_iface(name)
55
+ end; nil
56
+ end
57
+
58
+ def first_class
59
+ get_iface(@interfaces.first)
60
+ end
61
+
62
+ def get_iface(name)
63
+ Shift.const_get(name)
64
+ end
65
+
66
+ end
67
+ end