ruby_scribe 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,6 +4,7 @@ require "ruby_parser"
4
4
 
5
5
  require "ruby_scribe/emitter_helpers"
6
6
  require "ruby_scribe/emitter"
7
+ require "ruby_scribe/transformer_helpers"
7
8
  require "ruby_scribe/transformer"
8
9
  require "ruby_scribe/ext/sexp"
9
10
 
@@ -1,9 +1,12 @@
1
1
  module RubyScribe
2
+
2
3
  # Takes a raw S-expression and transforms it (replaces the node with the return value of the process method).
3
4
  # This is meant to be subclassed, and in the process method you should either return a transformed version of the node,
4
5
  # or just call super which will leave the node in place and iterate through the children.
5
6
  #
6
7
  class Transformer
8
+ include TransformerHelpers
9
+
7
10
  def transform(sexp)
8
11
  if sexp.is_a?(Sexp)
9
12
  Sexp.new(*([sexp.kind] + sexp.body.map {|c| transform(c) }))
@@ -0,0 +1,7 @@
1
+ module RubyScribe
2
+ module TransformerHelpers
3
+ def sexp?(e)
4
+ e.is_a?(Sexp)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,43 @@
1
+ module RubyScribe
2
+ module Transformers
3
+
4
+ # = Block Method To Proc Transform
5
+ # Looks for cases where we're calling a single method on a single-parameter block argument.
6
+ # These instances can use the Ruby 1.9 and ActiveSupport "Symbol#to_proc" trick.
7
+ #
8
+ # Example:
9
+ #
10
+ # collection.map {|d| d.name }
11
+ #
12
+ # Transforms To:
13
+ #
14
+ # collect.map(&:name)
15
+ #
16
+ class BlockMethodToProcifier < Transformer
17
+ def transform(e)
18
+ if matches_block_to_use_tap?(e)
19
+ super transform_block_to_use_tap(e)
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def matches_block_to_use_tap?(e)
26
+ e.is_a?(Sexp) && e.kind == :iter && # Calls block
27
+ e.body[2] && e.body[2].kind == :call && # Body of block is a simple method call
28
+ e.body[2].body[0].kind == :lvar && # Simple method call is on a local variable
29
+ e.body[1].kind == :lasgn && # Block parameter is a single assign
30
+ e.body[2].body[0].body[0] == e.body[1].body[0] # Local variable is identical to the first block argument
31
+ end
32
+
33
+ def transform_block_to_use_tap(e)
34
+ s(:call,
35
+ e.body[0].body[0],
36
+ e.body[0].body[1],
37
+ s(:arglist, s(:block_pass, s(:lit, e.body[2].body[1])))
38
+ )
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module RubyScribe
2
+ module Transformers
3
+
4
+ # = Custom Transformer
5
+ # Allows for implementation of the transform to happen in-line through a block passed to the initializer.
6
+ #
7
+ # Example:
8
+ #
9
+ # # This contrived example reverses all string literals
10
+ # RubyScribe::Transformers::Custom.new do |expression|
11
+ # if sexp?(expression) && expression.kind == :str
12
+ # s(:str, expression.body[0].reverse)
13
+ # else
14
+ # super
15
+ # end
16
+ # end
17
+ #
18
+ class Custom < RubyScribe::Transformer
19
+ def initialize(&block)
20
+ class_eval do
21
+ define_method(:transform, &block)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -1,9 +1,26 @@
1
1
  module RubyScribe
2
2
  module Transformers
3
+
4
+ # = For to Each Block Transform
5
+ # Finds instances using "for something in collection"-style of iteration and converts
6
+ # it to using a standard collection.each block.
7
+ #
8
+ # Example:
9
+ #
10
+ # for element in collection
11
+ # do_something(element)
12
+ # end
13
+ #
14
+ # Converts To:
15
+ #
16
+ # collection.each do |element|
17
+ # do_something(element)
18
+ # end
19
+ #
3
20
  class Eachifier < Transformer
4
21
  def transform(e)
5
22
  if e.is_a?(Sexp) && e.kind == :for
6
- transform_for_to_each(e)
23
+ super transform_for_to_each(e)
7
24
  else
8
25
  super
9
26
  end
@@ -17,5 +34,6 @@ module RubyScribe
17
34
  )
18
35
  end
19
36
  end
37
+
20
38
  end
21
39
  end
@@ -0,0 +1,68 @@
1
+ module RubyScribe
2
+ module Transformers
3
+
4
+ # = Symbol Names
5
+ # Sometimes you'll come across code written by a Java developer who uses camel-cased names for variables and methods.
6
+ # This transformer will allow you to apply some transformation (defined through a block to initialize) to every symbol name
7
+ # in the application, which includes all of these:
8
+ # * Method Names (Definitions and Calls)
9
+ # * Local Variable Names
10
+ # * Instance Variable Names
11
+ # * Class Variable Names
12
+ #
13
+ # Big Caveat: This transformer does not detect instances where the symbol name is referenced inside an eval-ed string, for example
14
+ # when you are instance_evaling some large string. As always, run your tests after any transformation.
15
+ #
16
+ # Example:
17
+ #
18
+ # # Underscores symbol names
19
+ # RubyScribe::Transformers::SymbolNames.new {|name| name.underscore }
20
+ #
21
+ # # Given This:
22
+ # def myMethod
23
+ # myVariable = 1
24
+ # @iVar = myMethod(myVariable)
25
+ # end
26
+ #
27
+ # # Translates to This:
28
+ # def my_method
29
+ # my_variable = 1
30
+ # @i_var = my_method(my_variable)
31
+ # end
32
+ #
33
+ # # Or maybe translate from English to French
34
+ # RubyScribe::Transformers::SymbolNames.new {|name| Google::Translate.new(:fr).translate(name) }
35
+ #
36
+ class SymbolNames < RubyScribe::Transformer
37
+ def initialize(&block)
38
+ @name_transformer = block
39
+ end
40
+
41
+ def transform(e)
42
+ super transform_symbols(e)
43
+ end
44
+
45
+ def transform_symbols(e)
46
+ return e unless sexp?(e)
47
+
48
+ case e.kind
49
+ when :defn
50
+ e.clone.tap {|c| c[1] = @name_transformer.call(c[1].to_s).to_sym }
51
+ when :defs
52
+ e.clone.tap {|c| c[2] = @name_transformer.call(c[2].to_s).to_sym }
53
+ when :call
54
+ e.clone.tap {|c| c[2] = @name_transformer.call(c[2].to_s).to_sym }
55
+ when :lasgn, :lvar
56
+ e.clone.tap {|c| c[1] = @name_transformer.call(c[1].to_s).to_sym }
57
+ when :cvdecl, :cvasgn, :cvar
58
+ e.clone.tap {|c| c[1] = ("@@" + @name_transformer.call(c[1].to_s.gsub(/^@@/, ''))).to_sym }
59
+ when :iasgn, :ivar
60
+ e.clone.tap {|c| c[1] = ("@" + @name_transformer.call(c[1].to_s.gsub(/^@/, ''))).to_sym }
61
+ else
62
+ e
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,73 @@
1
+ module RubyScribe
2
+ module Transformers
3
+
4
+ # = Method Temporary Variables to Tap Block
5
+ # Looks for instances of method declarations where:
6
+ # 1. A local variable is assigned to something as the first statement.
7
+ # 2. As the last statement, that local variable is returned is expressed.
8
+ #
9
+ # These instances of using "temporary variables" to construct something for returning can be more
10
+ # elegantly expressed by using the .tap idiom.
11
+ #
12
+ # Example:
13
+ #
14
+ # def my_method
15
+ # temp = ""
16
+ # temp << "Something"
17
+ # call_something(temp)
18
+ # temp
19
+ # end
20
+ #
21
+ # Converts To:
22
+ #
23
+ # def my_method
24
+ # "".tap do |temp|
25
+ # temp << "Something"
26
+ # call_something(temp)
27
+ # end
28
+ # end
29
+ #
30
+ class Tapifier < Transformer
31
+ def transform(e)
32
+ if matches_method_to_use_tap?(e)
33
+ super transform_method_to_use_tap(e)
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def matches_method_to_use_tap?(e)
40
+ e.is_a?(Sexp) && [:defn, :defs].include?(e.kind) &&
41
+ matches_block_to_use_tap?(e.body[2])
42
+ end
43
+
44
+ def matches_block_to_use_tap?(e)
45
+ return matches_block_to_use_tap?(e.body[0]) if e.body.size == 1 && e.body[0].kind == :block
46
+
47
+ e.is_a?(Sexp) && e.kind == :block && # Some block (or method body)
48
+ e.body[0].kind == :lasgn && # The first line assigns to a local variable
49
+ expresses_local_variable?(e.body[-1], e.body[0].body[0])
50
+ end
51
+
52
+ def expresses_local_variable?(e, local_variable)
53
+ (e.kind == :lvar && e.body[0] == local_variable) ||
54
+ (e.kind == :return && expresses_local_variable?(e.body[0], local_variable))
55
+ end
56
+
57
+ def transform_method_to_use_tap(e)
58
+ s(e.kind, e.body[0], e.body[1], transform_block_to_use_tap(e.body[2]))
59
+ end
60
+
61
+ def transform_block_to_use_tap(e)
62
+ return s(:scope, transform_block_to_use_tap(e.body[0])) if e.body.size == 1 && e.kind == :scope && e.body[0].kind == :block
63
+
64
+ s(:block, s(:iter,
65
+ s(:call, e.body[0].body[1], :tap, s(:arglist)),
66
+ s(:lasgn, e.body[0].body[0]),
67
+ s(*([:block] + e.body[1..-2]))
68
+ ))
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyScribe
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -3,7 +3,7 @@ RSpec::Matchers.define :transform_to do |desc, expected|
3
3
  actual_as_sexp = actual.is_a?(String) ? RubyParser.new.parse(actual) : actual
4
4
  expected_as_sexp = expected.is_a?(String) ? RubyParser.new.parse(expected) : expected
5
5
 
6
- @transformed = described_class.new.transform(actual_as_sexp)
6
+ @transformed = described_class.transform(actual_as_sexp)
7
7
  @transformed == expected_as_sexp
8
8
  end
9
9
 
@@ -18,4 +18,8 @@ RSpec::Matchers.define :transform_to do |desc, expected|
18
18
  description do
19
19
  "transform with to #{desc}"
20
20
  end
21
+
22
+ def transformed_as_ruby
23
+ RubyScribe::Emitter.new.emit(@transformed)
24
+ end
21
25
  end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe RubyScribe::Transformer do
3
+ describe RubyScribe::Transformer.new do
4
4
 
5
5
  describe "a for block" do
6
6
  subject { %{
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ describe RubyScribe::Transformers::BlockMethodToProcifier.new do
4
+
5
+ describe "a block call with method invocation" do
6
+ subject { %{collection.map {|d| d.name }}}
7
+ it { should transform_to("use Symbol#to_proc", %{collection.map(&:name)}) }
8
+ end
9
+
10
+ describe "a block call with chained method invocation" do
11
+ subject { %{collection.map {|d| d.name.first }}}
12
+ it { should transform_to("itself", subject) }
13
+ end
14
+
15
+ describe "a multiple-argument block call with method invocation" do
16
+ subject { %{collection.map {|d, i| d.name + i }}}
17
+ it { should transform_to("itself", subject) }
18
+ end
19
+
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ CustomTransformer = RubyScribe::Transformers::Custom.new do |expression|
4
+ if sexp?(expression) && expression.kind == :str
5
+ s(:str, expression.body[0].reverse)
6
+ else
7
+ super
8
+ end
9
+ end
10
+
11
+ describe CustomTransformer do
12
+
13
+ describe "some strings" do
14
+ subject { %{
15
+ s = "one"
16
+ t = "two"
17
+ }}
18
+
19
+ it { should transform_to("reverse", %{
20
+ s = "eno"
21
+ t = "owt"
22
+ }) }
23
+ end
24
+
25
+ end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe RubyScribe::Transformers::Eachifier do
3
+ describe RubyScribe::Transformers::Eachifier.new do
4
4
 
5
5
  describe "a for block" do
6
6
  subject { %{
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ SymbolUnderscorer = RubyScribe::Transformers::SymbolNames.new do |name|
4
+ name.underscore
5
+ end
6
+
7
+ describe SymbolUnderscorer do
8
+
9
+ describe "method definition" do
10
+ subject { %{def myMethod; end; def self.anotherMethod; end} }
11
+ it { should transform_to("underscored", %{def my_method; end; def self.another_method; end}) }
12
+ end
13
+
14
+ describe "method call" do
15
+ subject { %{myMethod.doSomething} }
16
+ it { should transform_to("underscored", %{my_method.do_something}) }
17
+ end
18
+
19
+ describe "method arguments" do
20
+ subject { %{my(argOne, argTwo)} }
21
+ it { should transform_to("underscored", %{my(arg_one, arg_two)}) }
22
+ end
23
+
24
+ describe "block arguments" do
25
+ subject { %{collection.each {|argOne, argTwo| something }} }
26
+ it { should transform_to("underscored", %{collection.each {|arg_one, arg_two| something }}) }
27
+ end
28
+
29
+ describe "local variable" do
30
+ subject { %{def my; myVariable = 1; myVariable; end} }
31
+ it { should transform_to("underscored", %{def my; my_variable = 1; my_variable; end}) }
32
+ end
33
+
34
+ describe "instance variable" do
35
+ subject { %{@myVariable = 1; @myVariable} }
36
+ it { should transform_to("underscored", %{@my_variable = 1; @my_variable}) }
37
+ end
38
+
39
+ describe "class variable" do
40
+ subject { %{@@myVariable = 1; @@myVariable; def my; @@myVariable = 1; end} }
41
+ it { should transform_to("underscored", %{@@my_variable = 1; @@my_variable; def my; @@my_variable = 1; end}) }
42
+ end
43
+
44
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyScribe::Transformers::Tapifier.new do
4
+
5
+ describe "a method setting up and returning a temporary variable" do
6
+ subject {%{
7
+ def my_method
8
+ temp = ""
9
+ temp << "Something"
10
+ call_something(temp)
11
+ temp
12
+ end
13
+ }}
14
+
15
+ it { should transform_to("wrap in #tap", %{
16
+ def my_method
17
+ "".tap do |temp|
18
+ temp << "Something"
19
+ call_something(temp)
20
+ end
21
+ end
22
+ }) }
23
+ end
24
+
25
+ describe "a method setting up and explicitly returning a temporary variable" do
26
+ subject {%{
27
+ def my_method
28
+ temp = ""
29
+ temp << "Something"
30
+ call_something(temp)
31
+ return temp
32
+ end
33
+ }}
34
+
35
+ it { should transform_to("wrap in #tap", %{
36
+ def my_method
37
+ "".tap do |temp|
38
+ temp << "Something"
39
+ call_something(temp)
40
+ end
41
+ end
42
+ }) }
43
+ end
44
+
45
+ describe "a method setting up and not returning a temporary variable" do
46
+ subject {%{
47
+ def my_method
48
+ temp = ""
49
+ temp << "Something"
50
+ call_something(temp)
51
+ end
52
+ }}
53
+
54
+ it { should transform_to("itself", subject) }
55
+ end
56
+
57
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_scribe
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ben Hughes
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-27 00:00:00 +03:00
18
+ date: 2010-10-28 00:00:00 +03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -78,7 +78,12 @@ files:
78
78
  - lib/ruby_scribe/ext/sexp.rb
79
79
  - lib/ruby_scribe/runner.rb
80
80
  - lib/ruby_scribe/transformer.rb
81
+ - lib/ruby_scribe/transformer_helpers.rb
82
+ - lib/ruby_scribe/transformers/block_method_to_procifier.rb
83
+ - lib/ruby_scribe/transformers/custom.rb
81
84
  - lib/ruby_scribe/transformers/eachifier.rb
85
+ - lib/ruby_scribe/transformers/symbol_names.rb
86
+ - lib/ruby_scribe/transformers/tapifier.rb
82
87
  - lib/ruby_scribe/version.rb
83
88
  - lib/ruby_scribe.rb
84
89
  - spec/examples/identity.rb
@@ -89,7 +94,11 @@ files:
89
94
  - spec/ruby_scribe/emitter_examples_spec.rb
90
95
  - spec/ruby_scribe/emitter_spec.rb
91
96
  - spec/ruby_scribe/transformer_spec.rb
97
+ - spec/ruby_scribe/transformers/block_method_to_procifier_spec.rb
98
+ - spec/ruby_scribe/transformers/custom_spec.rb
92
99
  - spec/ruby_scribe/transformers/eachifier_spec.rb
100
+ - spec/ruby_scribe/transformers/symbol_names_spec.rb
101
+ - spec/ruby_scribe/transformers/tapifier_spec.rb
93
102
  - spec/spec_helper.rb
94
103
  - LICENSE
95
104
  - Rakefile