sexp2ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Sexp2Ruby
2
+ VERSION = "0.0.1"
3
+ end
data/lib/sexp2ruby.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "sexp2ruby/version"
2
+ require "sexp2ruby/errors"
3
+ require "sexp2ruby/core_extensions/regexp"
4
+ require "sexp2ruby/processor"
data/sexp2ruby.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "sexp2ruby/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sexp2ruby"
8
+ spec.version = Sexp2Ruby::VERSION
9
+ spec.authors = ['Ryan Davis', 'Jared Beck']
10
+ spec.email = ['jared@jaredbeck.com']
11
+ spec.summary = 'Generates ruby from RubyParser S-expressions'
12
+ spec.description = <<-EOS
13
+ Generates ruby from RubyParser-compatible S-expressions.
14
+ It is a fork of ruby2ruby with slightly different goals.
15
+ EOS
16
+ spec.homepage = 'https://github.com/jaredbeck/sexp2ruby'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.required_ruby_version = ">= 1.9.3"
25
+
26
+ spec.add_runtime_dependency "sexp_processor", "~> 4.6"
27
+
28
+ spec.add_development_dependency "rspec-core", "~> 3.2"
29
+ spec.add_development_dependency "rspec-expectations", "~> 3.2"
30
+ spec.add_development_dependency "rspec-mocks", "~> 3.2"
31
+ spec.add_development_dependency "ruby_parser", "~> 3.7"
32
+ end
@@ -0,0 +1,183 @@
1
+ require 'ruby_parser'
2
+
3
+ module Sexp2Ruby
4
+ RSpec.describe Processor do
5
+ let(:processor) { described_class.new }
6
+ let(:processor_hash19) { described_class.new(hash_syntax: :ruby19) }
7
+
8
+ describe "#new" do
9
+ it "accepts hash_syntax option" do
10
+ expect(processor_hash19.hash_syntax).to eq(:ruby19)
11
+ end
12
+
13
+ context "unknown option" do
14
+ it "raises error" do
15
+ expect {
16
+ described_class.new(foo: "bar")
17
+ }.to raise_error(InvalidOption)
18
+ end
19
+ end
20
+
21
+ context "bad option value" do
22
+ it "raises error" do
23
+ expect {
24
+ described_class.new(hash_syntax: "banana")
25
+ }.to raise_error(InvalidOption)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#extract_option" do
31
+ context "valid value" do
32
+ it "returns value" do
33
+ expect(
34
+ processor.extract_option([:a, :b], :b, :c)
35
+ ).to eq(:b)
36
+ end
37
+ end
38
+
39
+ context "nil value" do
40
+ it "returns default value" do
41
+ expect(
42
+ processor.extract_option([:a, :b], nil, :c)
43
+ ).to eq(:c)
44
+ end
45
+ end
46
+
47
+ context "invalid value" do
48
+ it "raises error" do
49
+ expect {
50
+ processor.extract_option([:a, :b], :d, :c)
51
+ }.to raise_error(InvalidOption)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#process" do
57
+ context "hash" do
58
+ it "ruby19_one_pair" do
59
+ inp = s(:hash, s(:lit, :foo), s(:str, "bar"))
60
+ compare(inp, '{ foo: "bar" }', processor_hash19)
61
+ end
62
+
63
+ it "ruby19_when_key_has_special_chars" do
64
+ inp = s(:hash, s(:str, "hurr:durr"), s(:str, "bar"))
65
+ compare(inp, '{ "hurr:durr" => "bar" }', processor_hash19)
66
+ end
67
+
68
+ it "ruby19_when_key_is_not_a_literal" do
69
+ inp = s(:hash, s(:call, nil, :foo, s(:str, "bar")), s(:str, "baz"))
70
+ compare(inp, '{ foo("bar") => "baz" }', processor_hash19)
71
+ end
72
+
73
+ it "ruby19_mixed_pairs" do
74
+ inp = s(:hash, s(:lit, :foo), s(:str, "bar"), s(:lit, 0.7), s(:str, "baz"))
75
+ compare(inp, '{ foo: "bar", 0.7 => "baz" }', processor_hash19)
76
+ end
77
+
78
+ describe "parentheses" do
79
+ it "does not wrap string in parens" do
80
+ inp = s(:hash, s(:lit, :k), s(:str, "banana"))
81
+ out = '{ :k => "banana" }'
82
+ compare(inp, out, processor)
83
+ end
84
+
85
+ it "does not wrap number in parens" do
86
+ inp = s(:hash, s(:lit, :k), s(:lit, 0.07))
87
+ out = "{ :k => 0.07 }"
88
+ compare(inp, out, processor)
89
+ end
90
+
91
+ it "does not wrap boolean in parens" do
92
+ inp = s(:hash, s(:lit, :k), s(:true))
93
+ out = "{ :k => true }"
94
+ compare(inp, out, processor)
95
+ end
96
+
97
+ it "does not wrap nil in parens" do
98
+ inp = s(:hash, s(:lit, :k), s(:nil))
99
+ out = "{ :k => nil }"
100
+ compare(inp, out, processor)
101
+ end
102
+
103
+ it "does not wrap local variable (lvar) in parens" do
104
+ inp = s(:hash, s(:lit, :k), s(:lvar, :x))
105
+ out = "{ :k => x }"
106
+ compare(inp, out, processor, false)
107
+ end
108
+
109
+ it "does not wrap call in parens" do
110
+ inp = s(:hash, s(:lit, :k), s(:call, nil, :foo, s(:lit, :bar)))
111
+ out = "{ :k => foo(:bar) }"
112
+ compare(inp, out, processor)
113
+ end
114
+
115
+ it "wraps method call with block (iter) in parens" do
116
+ iter = s(:iter, s(:call, nil, :foo), s(:args), s(:str, "bar"))
117
+ inp = s(:hash, s(:lit, :k), iter)
118
+ out = '{ :k => (foo { "bar" }) }'
119
+ compare(inp, out, processor, false)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ describe "#ruby19_hash_key?" do
126
+ context "symbol" do
127
+ it "returns true" do
128
+ expect(processor.ruby19_hash_key?(s(:lit, :foo))).to eq(true)
129
+ end
130
+ end
131
+
132
+ context "not a symbol" do
133
+ it "returns false" do
134
+ expect(processor.ruby19_hash_key?(s(:str, "foo"))).to eq(false)
135
+ expect(processor.ruby19_hash_key?(s(:lit, 7))).to eq(false)
136
+ expect(processor.ruby19_hash_key?(s(:true))).to eq(false)
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "#util_dthing" do
142
+ let(:interpolation) {
143
+ s('a"b',
144
+ s(:evstr, s(:call, s(:lit, 1), :+, s(:lit, 1))),
145
+ s(:str, 'c"d/e')
146
+ )
147
+ }
148
+
149
+ context "dregx" do
150
+ it "generates regex with interpolation" do
151
+ out = '/a"b#{(1 + 1)}c"d\/e/'
152
+ expect(out).to eval_to(/a"b2c"d\/e/)
153
+ expect(processor.util_dthing(:dregx, interpolation)).to eq(out[1..-2])
154
+ end
155
+
156
+ it "generates regex with interpolation" do
157
+ interpolation = s('[\/\"]', s(:evstr, s(:lit, 42)))
158
+ out = '/[\/\"]#{42}/'
159
+ expect(out).to eval_to(/[\/\"]42/)
160
+ expect(processor.util_dthing(:dregx, interpolation)).to eq(out[1..-2])
161
+ end
162
+ end
163
+
164
+ context "dstr" do
165
+ it "generates string with interpolation" do
166
+ out = '"a\"b#{(1 + 1)}c\"d/e"'
167
+ expect(out).to eval_to('a"b2c"d/e')
168
+ expect(processor.util_dthing(:dstr, interpolation)).to eq(out[1..-2])
169
+ end
170
+ end
171
+ end
172
+
173
+ def compare(sexp, expected_ruby, processor, check_sexp = true, expected_eval = nil)
174
+ if check_sexp
175
+ expect(RubyParser.new.process(expected_ruby)).to eq(sexp)
176
+ end
177
+ expect(processor.process(sexp)).to eq(expected_ruby)
178
+ if expected_eval
179
+ expect(eval(expected_ruby)).to eq(expected_eval)
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,10 @@
1
+ require "sexp2ruby"
2
+
3
+ RSpec::Matchers.define :eval_to do |expected|
4
+ match do |actual|
5
+ eval(actual) == expected
6
+ end
7
+ failure_message do |actual|
8
+ "expected #{actual} to evaluate to #{expected}"
9
+ end
10
+ end