sexp2ruby 0.0.1

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