code-ruby 3.0.13 → 3.1.2

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.
@@ -3,22 +3,22 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe Code::Object::Function do
6
- # [
7
- # ["even? = (i) => { i.even? } even?(2)", "true"],
8
- # ["even? = (i:) => { i.even? } even?(i: 2)", "true"],
9
- # ["add = (a, b) => { a + b } add(1, 2)", "3"],
10
- # ["minus = (a:, b:) => { a - b } minus(b: 1, a: 2)", "1"]
11
- # ].each do |input, expected|
12
- # it "#{input} == #{expected}" do
13
- # expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
14
- # end
15
- # end
6
+ [
7
+ ["even? = (i) => { i.even? } even?(2)", "true"],
8
+ ["even? = (i:) => { i.even? } even?(i: 2)", "true"],
9
+ ["add = (a, b) => { a + b } add(1, 2)", "3"],
10
+ ["minus = (a:, b:) => { a - b } minus(b: 1, a: 2)", "1"]
11
+ ].each do |input, expected|
12
+ it "#{input} == #{expected}" do
13
+ expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
14
+ end
15
+ end
16
16
 
17
17
  context "valid" do
18
18
  [
19
19
  "f = () => {} f",
20
- "f = (x) => {} f(1)"
21
- # "f = (x:) => {} f(x: 1)"
20
+ "f = (x) => {} f(1)",
21
+ "f = (x:) => {} f(x: 1)"
22
22
  ].each do |input|
23
23
  it "#{input} is valid" do
24
24
  Code.evaluate(input)
@@ -38,6 +38,46 @@ RSpec.describe Code::Object::Function do
38
38
  end
39
39
  end
40
40
 
41
+ it "evaluates omitted keyword argument defaults" do
42
+ result = Code.evaluate(<<~CODE)
43
+ f = (number: 1, text: "fallback", missing: nothing, headers: {}) => {
44
+ [
45
+ number,
46
+ text,
47
+ missing.nothing?,
48
+ headers.merge({ authorization: "Bearer x" })
49
+ ]
50
+ }
51
+
52
+ f()
53
+ CODE
54
+
55
+ expect(result).to eq(
56
+ Code.evaluate('[1, "fallback", true, { authorization: "Bearer x" }]')
57
+ )
58
+ end
59
+
60
+ it "evaluates keyword defaults in the call context" do
61
+ expect(Code.evaluate("f = (a:, b: a + 1) => { b } f(a: 2)")).to eq(
62
+ Code.evaluate("3")
63
+ )
64
+ end
65
+
66
+ it "binds self when calling a function stored on a dictionary" do
67
+ result = Code.evaluate(<<~CODE)
68
+ object = {}
69
+ object.value = 1
70
+ object.fetch = () => {
71
+ self.value
72
+ }
73
+ object.fetch()
74
+ CODE
75
+
76
+ expect(result).to eq(
77
+ Code.evaluate("1")
78
+ )
79
+ end
80
+
41
81
  it "captures self for constructor-like functions that return self" do
42
82
  result = Code.evaluate(<<~CODE)
43
83
  User = (given_name:, family_name:, birth_date:) => {
@@ -72,6 +112,73 @@ RSpec.describe Code::Object::Function do
72
112
  )
73
113
  end
74
114
 
115
+ it "binds parent to the enclosing self for nested constructor functions" do
116
+ result = Code.evaluate(<<~CODE)
117
+ Account = (name:) => {
118
+ self.name = name
119
+
120
+ self.Project = (name:) => {
121
+ self.name = name
122
+
123
+ self.Task = (name:) => {
124
+ self.name = name
125
+
126
+ [
127
+ self.name,
128
+ parent.name,
129
+ parent.parent.name
130
+ ]
131
+ }
132
+
133
+ return(self)
134
+ }
135
+
136
+ return(self)
137
+ }
138
+
139
+ account = Account(name: "Acme")
140
+ Project = account.get(:Project)
141
+ project = Project(name: "Migration")
142
+ Task = project.get(:Task)
143
+ Task(name: "Import")
144
+ CODE
145
+
146
+ expect(result).to eq(
147
+ Code.evaluate('["Import", "Migration", "Acme"]')
148
+ )
149
+ end
150
+
151
+ it "allows parent chains deeper than two levels" do
152
+ result = Code.evaluate(<<~CODE)
153
+ A = (name:) => {
154
+ self.name = name
155
+ self.B = (name:) => {
156
+ self.name = name
157
+ self.C = (name:) => {
158
+ self.name = name
159
+ self.D = (name:) => {
160
+ self.name = name
161
+ parent.parent.parent.name
162
+ }
163
+ return(self)
164
+ }
165
+ return(self)
166
+ }
167
+ return(self)
168
+ }
169
+
170
+ a = A(name: "a")
171
+ B = a.get(:B)
172
+ b = B(name: "b")
173
+ C = b.get(:C)
174
+ c = C(name: "c")
175
+ D = c.get(:D)
176
+ D(name: "d")
177
+ CODE
178
+
179
+ expect(result).to eq(Code::Object::String.new("a"))
180
+ end
181
+
75
182
  it "supports constructor methods on functions" do
76
183
  result = Code.evaluate(<<~CODE)
77
184
  User = (given_name:, family_name:) => {
@@ -5,6 +5,8 @@ require "spec_helper"
5
5
  RSpec.describe Code::Object::List do
6
6
  [
7
7
  ["[] == []", "true"],
8
+ ["[nothing, 1, false, \"\"].compact", '[1, false, ""]'],
9
+ ["[nothing, 1, false, \"\"].compact(&:blank?)", "[1]"],
8
10
  ["[1, 2, 3].sum", "6"],
9
11
  ["[1, 2] + [3, 4]", "[1, 2, 3, 4]"],
10
12
  ["[] + []", "[]"],
data/spec/code_spec.rb CHANGED
@@ -13,6 +13,74 @@ RSpec.describe Code do
13
13
  [result, output.string]
14
14
  end
15
15
 
16
+ def control_flow_block_calls
17
+ [
18
+ "[1].each { CONTROL }",
19
+ "[1, 2].any? { CONTROL }",
20
+ "[1].detect { CONTROL }",
21
+ "[1].map { CONTROL }",
22
+ "xs = [1] xs.map! { CONTROL }",
23
+ "[1].max { CONTROL }",
24
+ "[1].none? { CONTROL }",
25
+ "[1].all? { CONTROL }",
26
+ "[1, 2].reduce { |acc, item| CONTROL }",
27
+ "[nothing].compact { CONTROL }",
28
+ "xs = [nothing] xs.compact! { CONTROL }",
29
+ "[1].select { CONTROL }",
30
+ "xs = [1] xs.select! { CONTROL }",
31
+ "[1].reject { CONTROL }",
32
+ "xs = [1] xs.reject! { CONTROL }",
33
+ "[1].sort { CONTROL }",
34
+ "[1].uniq { CONTROL }",
35
+ "(1..1).all? { CONTROL }",
36
+ "(1..1).any? { CONTROL }",
37
+ "(1..1).each { CONTROL }",
38
+ "(1..1).map { CONTROL }",
39
+ "(1..1).select { CONTROL }",
40
+ "{a: 1}.any? { CONTROL }",
41
+ "{a: 1}.compact { CONTROL }",
42
+ "hash = {a: 1} hash.compact! { CONTROL }",
43
+ "{a: 1}.delete(:b) { CONTROL }",
44
+ "hash = {a: 1} hash.delete_if { CONTROL }",
45
+ "hash = {a: 1} hash.delete_unless { CONTROL }",
46
+ "{a: 1}.each { CONTROL }",
47
+ "{a: 1}.fetch(:b) { CONTROL }",
48
+ "hash = {a: 1} hash.keep_if { CONTROL }",
49
+ "hash = {a: 1} hash.keep_unless { CONTROL }",
50
+ "{a: 1}.key(2) { CONTROL }",
51
+ "{a: 1}.merge({a: 2}) { CONTROL }",
52
+ "hash = {a: 1} hash.merge!({a: 2}) { CONTROL }",
53
+ "{a: 1}.select { CONTROL }",
54
+ "hash = {a: 1} hash.select! { CONTROL }",
55
+ "{a: 1}.transform_values { CONTROL }",
56
+ "1.times { CONTROL }",
57
+ "Html.p { CONTROL }",
58
+ "Html.escape { CONTROL }",
59
+ "Html.unescape { CONTROL }",
60
+ "Html.join(Html.br) { CONTROL }",
61
+ "Html.text { CONTROL }",
62
+ "Html.raw { CONTROL }",
63
+ "Html.raw(\"<p>a</p>\").css(\"p\").map { CONTROL }"
64
+ ]
65
+ end
66
+
67
+ %w[return break next continue].each do |control_flow|
68
+ it "continues after #{control_flow} in block methods" do
69
+ aggregate_failures do
70
+ control_flow_block_calls.each do |block_call|
71
+ input = "#{block_call.sub("CONTROL", control_flow)} puts(:end)"
72
+ formatted = format_input(input)
73
+
74
+ _result, output = evaluate_with_output(input)
75
+ _formatted_result, formatted_output = evaluate_with_output(formatted)
76
+
77
+ expect(output).to eq("end\n"), input
78
+ expect(formatted_output).to eq("end\n"), formatted
79
+ end
80
+ end
81
+ end
82
+ end
83
+
16
84
  (
17
85
  [
18
86
  "{ a: 1, b: 2 }.transform_values { |key| key.upcase }",
@@ -328,6 +396,18 @@ RSpec.describe Code do
328
396
  ["[1, 2, 3]", "[1, 2, 3]"],
329
397
  ["[1, 2, 3].include?(2)", "true"],
330
398
  ["[1, 2, 3].include?(4)", "false"],
399
+ [
400
+ "summary = { by_status: { draft: 1 } } k = \"draft\" summary.by_status[k]",
401
+ "1"
402
+ ],
403
+ [
404
+ "summary = { by_status: {} } k = \"draft\" summary.by_status[k] = 1 summary",
405
+ '{"by_status" => {"draft" => 1}}'
406
+ ],
407
+ [
408
+ "summary = { by_status: {} } [{status: \"draft\"}].each { |track| summary.by_status[track[:status]] = 1 } summary",
409
+ '{"by_status" => {"draft" => 1}}'
410
+ ],
331
411
  ["[1, 2, 3].map { |i| continue(0) if i.even? i ** 2}", "[1, 0, 9]"],
332
412
  ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
333
413
  ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
@@ -340,6 +420,7 @@ RSpec.describe Code do
340
420
  ["[1, 2].map(&:to_string)", '["1", "2"]'],
341
421
  ["[1, 2].map(&:to_string)", '["1", "2"]'],
342
422
  ["[1].each do end", "[1]"],
423
+ ["x = [1].each { break(42) } x", "42"],
343
424
  ["[[true]]", "[[true]]"],
344
425
  ["[]", "[]"],
345
426
  ["\r\n", "nothing"],
@@ -350,6 +431,8 @@ RSpec.describe Code do
350
431
  ],
351
432
  ["a = 0 loop a += 1 break end a", "1"],
352
433
  ["x = loop break(42) end x", "42"],
434
+ ["x = loop { |index| break(index) if index > 3 } x", "4"],
435
+ ["x = loop do |index| break(index) if index > 3 end x", "4"],
353
436
  ["a = 0\nuntil a > 10 a += 1 end a", "11"],
354
437
  ["a = 0\nwhile a < 10 a += 1 end a", "10"],
355
438
  ["a = 1 3.times { a += 1 } a", "4"],
@@ -529,7 +612,11 @@ RSpec.describe Code do
529
612
  end
530
613
  end
531
614
 
532
- [["puts(true)", "true\n"], %w[print(false) false]].each do |input, expected|
615
+ [
616
+ ["puts(true)", "true\n"],
617
+ %w[print(false) false],
618
+ ["[1].each { break } puts(:end)", "end\n"]
619
+ ].each do |input, expected|
533
620
  it "#{input} prints #{expected}" do
534
621
  formatted = format_input(input)
535
622
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.13
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié