ruby_scribe 0.0.3 → 0.0.4

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.
@@ -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