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.
- data/lib/ruby_scribe.rb +1 -0
- data/lib/ruby_scribe/transformer.rb +3 -0
- data/lib/ruby_scribe/transformer_helpers.rb +7 -0
- data/lib/ruby_scribe/transformers/block_method_to_procifier.rb +43 -0
- data/lib/ruby_scribe/transformers/custom.rb +27 -0
- data/lib/ruby_scribe/transformers/eachifier.rb +19 -1
- data/lib/ruby_scribe/transformers/symbol_names.rb +68 -0
- data/lib/ruby_scribe/transformers/tapifier.rb +73 -0
- data/lib/ruby_scribe/version.rb +1 -1
- data/spec/matchers/should_transform_to.rb +5 -1
- data/spec/ruby_scribe/transformer_spec.rb +1 -1
- data/spec/ruby_scribe/transformers/block_method_to_procifier_spec.rb +20 -0
- data/spec/ruby_scribe/transformers/custom_spec.rb +25 -0
- data/spec/ruby_scribe/transformers/eachifier_spec.rb +1 -1
- data/spec/ruby_scribe/transformers/symbol_names_spec.rb +44 -0
- data/spec/ruby_scribe/transformers/tapifier_spec.rb +57 -0
- metadata +13 -4
data/lib/ruby_scribe.rb
CHANGED
@@ -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,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
|
data/lib/ruby_scribe/version.rb
CHANGED
@@ -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.
|
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
|
@@ -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
|
@@ -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:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
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-
|
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
|