code-ruby 3.0.13 → 3.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6baa3c7c3ef6b602529e2af4a36200029291ed89c115f1c1f3d750a8e815cc03
4
- data.tar.gz: aa9c876fb0aff915f895ec3d209d25ac06270e3809eadba8f5e71b37b9cb29db
3
+ metadata.gz: 4fb3de0080323ce38f0bc61db99084820616f9b437d94fbf973a4a0736da7a0b
4
+ data.tar.gz: 476a64d5bcd78add94f15f25c3d1d086c3263aa66dd4af73443e8ad5e126fc9e
5
5
  SHA512:
6
- metadata.gz: 444adc5d9b9a5223b82a921a888558a2f23dc40344a12b383137581b910ba4e33ea13873a2e5ce6698dc130ae77775cf9aa5b3e0e23f70947418301831df96b9
7
- data.tar.gz: bcf7ecebc28788a2cf7464e3a7b67f32b2bfb86ec3d6365ca58674914d852181d27f3a051b596665807acd67291c2026d227b98d33603260e3122417e04d6a76
6
+ metadata.gz: 9851fd43383c19e34984f2124980f788beb59b22e2fd6cd5871a4e3107f62b908770c0b1575b58e1f84cefef574740877f9de9d18bb41ac22eaac531a85136bb
7
+ data.tar.gz: b620dc0c1eb65e83fe5d8ade6047fc712a1149f258a1275fc46163f4852a7efd2e5107fc979396daaa95672bb2216c43b3796176abf27d6c0f7bd35c9fba1cf4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (3.0.13)
4
+ code-ruby (3.1.1)
5
5
  activesupport
6
6
  base64
7
7
  bigdecimal
@@ -20,7 +20,7 @@ PATH
20
20
  GEM
21
21
  remote: https://rubygems.org/
22
22
  specs:
23
- activesupport (8.1.2)
23
+ activesupport (8.1.3)
24
24
  base64
25
25
  bigdecimal
26
26
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -33,11 +33,11 @@ GEM
33
33
  securerandom (>= 0.3)
34
34
  tzinfo (~> 2.0, >= 2.0.5)
35
35
  uri (>= 0.13.1)
36
- addressable (2.8.9)
36
+ addressable (2.9.0)
37
37
  public_suffix (>= 2.0.2, < 8.0)
38
38
  ast (2.4.3)
39
39
  base64 (0.3.0)
40
- bigdecimal (4.0.1)
40
+ bigdecimal (4.1.2)
41
41
  bundler-audit (0.9.3)
42
42
  bundler (>= 1.2.0)
43
43
  thor (~> 1.0)
@@ -79,7 +79,7 @@ GEM
79
79
  dorian-to_struct (2.0.2)
80
80
  drb (2.2.3)
81
81
  geom2d (0.4.1)
82
- git (4.3.1)
82
+ git (4.3.2)
83
83
  activesupport (>= 5.0)
84
84
  addressable (~> 2.8)
85
85
  process_executer (~> 4.0)
@@ -89,23 +89,20 @@ GEM
89
89
  thor
90
90
  tilt
91
91
  hashdiff (1.2.1)
92
- hexapdf (1.6.0)
92
+ hexapdf (1.8.0)
93
93
  cmdparse (~> 3.0, >= 3.0.3)
94
94
  geom2d (~> 0.4, >= 0.4.1)
95
95
  openssl (>= 2.2.1)
96
96
  strscan (>= 3.1.2)
97
97
  i18n (1.14.8)
98
98
  concurrent-ruby (~> 1.0)
99
- icalendar (2.12.2)
99
+ icalendar (2.12.3)
100
100
  base64
101
101
  ice_cube (~> 0.16)
102
102
  logger
103
103
  ostruct
104
104
  ice_cube (0.17.0)
105
- json (2.18.1)
106
- json-schema (6.1.0)
107
- addressable (~> 2.8)
108
- bigdecimal (>= 3.1, < 5)
105
+ json (2.19.5)
109
106
  language-ruby (1.2.0)
110
107
  dorian-arguments
111
108
  zeitwerk
@@ -121,19 +118,16 @@ GEM
121
118
  net-imap
122
119
  net-pop
123
120
  net-smtp
124
- mcp (0.7.1)
125
- json-schema (>= 4.1)
126
121
  mini_mime (1.1.5)
127
- mini_racer (0.20.0)
122
+ mini_racer (0.21.0)
128
123
  libv8-node (~> 24.12.0.1)
129
- minitest (6.0.2)
124
+ minitest (6.0.6)
130
125
  drb (~> 2.0)
131
126
  prism (~> 1.5)
132
- mustermann (3.0.4)
133
- ruby2_keywords (~> 0.0.1)
127
+ mustermann (3.1.1)
134
128
  net-http (0.9.1)
135
129
  uri (>= 0.11.1)
136
- net-imap (0.6.3)
130
+ net-imap (0.6.4)
137
131
  date
138
132
  net-protocol
139
133
  net-pop (0.1.2)
@@ -142,34 +136,34 @@ GEM
142
136
  timeout
143
137
  net-smtp (0.5.1)
144
138
  net-protocol
145
- nokogiri (1.19.1-arm64-darwin)
139
+ nokogiri (1.19.3-arm64-darwin)
146
140
  racc (~> 1.4)
147
- nokogiri (1.19.1-x86_64-linux-gnu)
141
+ nokogiri (1.19.3-x86_64-linux-gnu)
148
142
  racc (~> 1.4)
149
- openssl (4.0.1)
143
+ openssl (4.0.2)
150
144
  ostruct (0.6.3)
151
- parallel (1.27.0)
152
- parser (3.3.10.2)
145
+ parallel (2.1.0)
146
+ parser (3.3.11.1)
153
147
  ast (~> 2.4.1)
154
148
  racc
155
149
  prettier_print (1.2.1)
156
150
  prism (1.9.0)
157
- process_executer (4.0.2)
151
+ process_executer (4.0.4)
158
152
  track_open_instances (~> 0.1)
159
- public_suffix (7.0.2)
153
+ public_suffix (7.0.5)
160
154
  racc (1.8.1)
161
- rack (3.2.5)
155
+ rack (3.2.6)
162
156
  rack-protection (4.2.1)
163
157
  base64 (>= 0.1.0)
164
158
  logger (>= 1.6.0)
165
159
  rack (>= 3.0.0, < 4)
166
- rack-session (2.1.1)
160
+ rack-session (2.1.2)
167
161
  base64 (>= 0.1.0)
168
162
  rack (>= 3.0.0)
169
163
  rainbow (3.1.1)
170
- rake (13.3.1)
164
+ rake (13.4.2)
171
165
  rchardet (1.10.0)
172
- regexp_parser (2.11.3)
166
+ regexp_parser (2.12.0)
173
167
  rexml (3.4.4)
174
168
  rspec (3.13.2)
175
169
  rspec-core (~> 3.13.0)
@@ -184,24 +178,23 @@ GEM
184
178
  diff-lcs (>= 1.2.0, < 2.0)
185
179
  rspec-support (~> 3.13.0)
186
180
  rspec-support (3.13.7)
187
- rubocop (1.85.0)
181
+ rubocop (1.86.2)
188
182
  json (~> 2.3)
189
183
  language_server-protocol (~> 3.17.0.2)
190
184
  lint_roller (~> 1.1.0)
191
- mcp (~> 0.6)
192
- parallel (~> 1.10)
185
+ parallel (>= 1.10)
193
186
  parser (>= 3.3.0.2)
194
187
  rainbow (>= 2.2.2, < 4.0)
195
188
  regexp_parser (>= 2.9.3, < 3.0)
196
189
  rubocop-ast (>= 1.49.0, < 2.0)
197
190
  ruby-progressbar (~> 1.7)
198
191
  unicode-display_width (>= 2.4.0, < 4.0)
199
- rubocop-ast (1.49.0)
192
+ rubocop-ast (1.49.1)
200
193
  parser (>= 3.3.7.2)
201
194
  prism (~> 1.7)
202
- rubocop-capybara (2.22.1)
195
+ rubocop-capybara (2.23.0)
203
196
  lint_roller (~> 1.1)
204
- rubocop (~> 1.72, >= 1.72.1)
197
+ rubocop (~> 1.81)
205
198
  rubocop-factory_bot (2.28.0)
206
199
  lint_roller (~> 1.1)
207
200
  rubocop (~> 1.72, >= 1.72.1)
@@ -209,7 +202,7 @@ GEM
209
202
  lint_roller (~> 1.1)
210
203
  rubocop (>= 1.75.0, < 2.0)
211
204
  rubocop-ast (>= 1.47.1, < 2.0)
212
- rubocop-rails (2.34.3)
205
+ rubocop-rails (2.35.0)
213
206
  activesupport (>= 4.2.0)
214
207
  lint_roller (~> 1.1)
215
208
  rack (>= 1.1)
@@ -225,11 +218,10 @@ GEM
225
218
  lint_roller (~> 1.1)
226
219
  rubocop (~> 1.72, >= 1.72.1)
227
220
  rubocop-rspec (~> 3.5)
228
- ruby-prof (2.0.3)
221
+ ruby-prof (2.0.4)
229
222
  base64
230
223
  ostruct
231
224
  ruby-progressbar (1.13.0)
232
- ruby2_keywords (0.0.5)
233
225
  securerandom (0.4.1)
234
226
  sinatra (4.2.1)
235
227
  logger (>= 1.6.0)
@@ -238,7 +230,7 @@ GEM
238
230
  rack-protection (= 4.2.1)
239
231
  rack-session (>= 2.0.0, < 3)
240
232
  tilt (~> 2.0)
241
- strscan (3.1.7)
233
+ strscan (3.1.8)
242
234
  syntax_tree (6.3.0)
243
235
  prettier_print (>= 1.2.0)
244
236
  syntax_tree-haml (4.0.3)
@@ -253,7 +245,7 @@ GEM
253
245
  unicode-display_width (>= 1.1.1, < 4)
254
246
  thor (1.5.0)
255
247
  tilt (2.7.0)
256
- timeout (0.6.0)
248
+ timeout (0.6.1)
257
249
  track_open_instances (0.1.15)
258
250
  tzinfo (2.0.6)
259
251
  concurrent-ruby (~> 1.0)
@@ -264,7 +256,7 @@ GEM
264
256
  w_syntax_tree-erb (0.12.0)
265
257
  prettier_print (~> 1.2, >= 1.2.0)
266
258
  syntax_tree (~> 6.1, >= 6.1.1)
267
- webmock (3.26.1)
259
+ webmock (3.26.2)
268
260
  addressable (>= 2.8.0)
269
261
  crack (>= 0.3.2)
270
262
  hashdiff (>= 0.4.0, < 2.0.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.13
1
+ 3.1.1
data/lib/code/format.rb CHANGED
@@ -11,6 +11,7 @@ class Code
11
11
  MAX_INLINE_CALL_ARGUMENTS_LENGTH = 80
12
12
  MAX_INLINE_BLOCK_BODY_LENGTH = 40
13
13
  CONTINUATION_PADDING = 4
14
+ BOOLEAN_WORD_OPERATORS = %w[and or].freeze
14
15
 
15
16
  class << self
16
17
  def format(parsed)
@@ -328,13 +329,30 @@ class Code
328
329
  end
329
330
 
330
331
  def format_dictionary_statement_code(statement_code)
331
- key = format_nested_statement(statement_code[:statement], indent: 0)
332
+ key =
333
+ format_dictionary_statement_key(statement_code[:statement]) ||
334
+ format_nested_statement(statement_code[:statement], indent: 0)
332
335
  return key unless statement_code.key?(:code)
333
336
 
334
337
  value = format_code_inline(statement_code[:code], indent: 0)
335
338
  "#{key}: #{value}"
336
339
  end
337
340
 
341
+ def format_dictionary_statement_key(statement)
342
+ return unless statement.is_a?(Hash) && statement.key?(:string)
343
+
344
+ parts = Array(statement[:string])
345
+ return unless parts.one?
346
+
347
+ part = parts.first
348
+ return unless part.is_a?(Hash) && part.key?(:text)
349
+
350
+ text = part[:text].to_s
351
+ return unless text.match?(/\A[a-z_][a-z0-9_]*\z/)
352
+
353
+ text
354
+ end
355
+
338
356
  def format_call(call, indent:)
339
357
  name = call[:name]
340
358
  raw_arguments = call[:arguments].presence || []
@@ -444,8 +462,8 @@ class Code
444
462
  if right.include?("\n")
445
463
  first_line, *rest = right.lines(chomp: true)
446
464
  if multiline_operand_statement?(other[:statement]) ||
447
- !%w[and or].include?(operator)
448
- [ "#{expression} #{operator} #{first_line.lstrip}", *rest ].join("\n")
465
+ !BOOLEAN_WORD_OPERATORS.include?(operator)
466
+ ["#{expression} #{operator} #{first_line.lstrip}", *rest].join("\n")
449
467
  else
450
468
  [
451
469
  "#{expression}\n#{INDENT * (indent + 1)}#{operator} #{first_line.lstrip}",
@@ -456,7 +474,7 @@ class Code
456
474
  right_lines =
457
475
  if right.include?("\n")
458
476
  right.lines(chomp: true).map(&:lstrip)
459
- elsif %w[and or].include?(operator)
477
+ elsif BOOLEAN_WORD_OPERATORS.include?(operator)
460
478
  right.split(" #{operator} ")
461
479
  else
462
480
  [right]
@@ -492,7 +510,7 @@ class Code
492
510
  expression
493
511
  .lines(chomp: true)[1..]
494
512
  .to_a
495
- .reject { |line| line.strip.empty? || line.strip.match?(/\A[\)\]\}]+\z/) }
513
+ .reject { |line| line.strip.empty? || line.strip.match?(/\A[\])}]+\z/) }
496
514
 
497
515
  return false if continuation_lines.empty?
498
516
 
@@ -502,7 +520,7 @@ class Code
502
520
  continuation_lines.any? do |line|
503
521
  next false unless line[/\A */].to_s.length == base_indent
504
522
 
505
- line.lstrip.match?(/\A(\+|-|\*|\/|%|<<|>>|\||\^|&|and\b|or\b)/)
523
+ line.lstrip.match?(%r{\A(\+|-|\*|/|%|<<|>>|\||\^|&|and\b|or\b)})
506
524
  end
507
525
  end
508
526
 
@@ -713,6 +731,7 @@ class Code
713
731
 
714
732
  return true if statement.key?(:dictionnary) || statement.key?(:list)
715
733
  return true if statement.key?(:call)
734
+
716
735
  if statement.key?(:left_operation)
717
736
  operation = statement[:left_operation]
718
737
  others = Array(operation[:others])
@@ -791,6 +810,8 @@ class Code
791
810
  index, token = split
792
811
  left = line[0...index]
793
812
  right = line[(index + token.length)..]
813
+ return [line] if token == "." && left.strip.empty?
814
+
794
815
  continuation = "#{indent}#{INDENT}#{right.lstrip}"
795
816
 
796
817
  first_line =
@@ -51,12 +51,20 @@ class Code
51
51
 
52
52
  @others.reduce(first) do |left, right|
53
53
  if right.call?
54
- right.statement.evaluate(**args, object: left)
54
+ right.statement.evaluate(
55
+ **args,
56
+ object: left,
57
+ previous_object: args.fetch(:object)
58
+ )
55
59
  elsif right.safe_call?
56
60
  if left.is_an?(Object::Nothing)
57
61
  Object::Nothing.new
58
62
  else
59
- right.statement.evaluate(**args, object: left)
63
+ right.statement.evaluate(
64
+ **args,
65
+ object: left,
66
+ previous_object: args.fetch(:object)
67
+ )
60
68
  end
61
69
  elsif (right.or? && left.truthy?) || (right.and? && left.falsy?)
62
70
  left
@@ -76,9 +84,18 @@ class Code
76
84
  list = Object::IdentifierList.new([first])
77
85
 
78
86
  (@others || []).each do |other|
79
- list.code_append(
80
- other.statement.resolve(**args, object: list.code_last)
81
- )
87
+ resolved =
88
+ other.statement.resolve(
89
+ **args,
90
+ object: list.code_last,
91
+ previous_object: args.fetch(:object)
92
+ )
93
+
94
+ if resolved.is_a?(Object::IdentifierList)
95
+ resolved.raw.each { |identifier| list.code_append(identifier) }
96
+ else
97
+ list.code_append(resolved)
98
+ end
82
99
  end
83
100
 
84
101
  list
@@ -13,9 +13,10 @@ class Code
13
13
 
14
14
  def evaluate(**args)
15
15
  left = @left&.evaluate(**args) || Object::Nothing.new
16
+ index_args = args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
16
17
 
17
18
  (@statements || []).reduce(left) do |object, statement|
18
- object.code_fetch(statement.evaluate(**args))
19
+ object.code_fetch(statement.evaluate(**index_args))
19
20
  end
20
21
  end
21
22
 
@@ -29,8 +30,10 @@ class Code
29
30
  Object::IdentifierList.new([left])
30
31
  end
31
32
 
33
+ index_args = args.merge(object: args.fetch(:previous_object, args.fetch(:object)))
34
+
32
35
  (@statements || []).each do |statement|
33
- list.code_append(statement.evaluate(**args))
36
+ list.code_append(statement.evaluate(**index_args))
34
37
  end
35
38
 
36
39
  list
@@ -134,6 +134,11 @@ class Code
134
134
  code_arguments = args.fetch(:arguments, List.new).to_code
135
135
  globals = multi_fetch(args, *GLOBALS)
136
136
  code_value = code_arguments.code_first
137
+ stored_value = code_fetch(code_operator)
138
+
139
+ if stored_value.is_a?(Function)
140
+ return stored_value.call(**args, operator: nil, bound_self: self)
141
+ end
137
142
 
138
143
  case code_operator.to_s
139
144
  when "[]", "at", "get"
@@ -146,11 +151,11 @@ class Code
146
151
  sig(args)
147
152
  code_clear
148
153
  when "compact!"
149
- sig(args)
150
- code_compact!
154
+ sig(args) { (Function | Class).maybe }
155
+ code_compact!(code_value, **globals)
151
156
  when "compact"
152
- sig(args)
153
- code_compact
157
+ sig(args) { (Function | Class).maybe }
158
+ code_compact(code_value, **globals)
154
159
  when "delete"
155
160
  sig(args) { Object.repeat(1) }
156
161
  code_delete(*code_arguments.raw, **globals)
@@ -581,12 +586,45 @@ class Code
581
586
  self
582
587
  end
583
588
 
584
- def code_compact
585
- Dictionary.new(raw.dup.delete_if { |_, value| value.falsy? })
589
+ def code_compact(argument = nil, **globals)
590
+ code_argument = argument.to_code
591
+
592
+ Dictionary.new(
593
+ raw.reject.with_index do |(key, value), index|
594
+ if code_argument.nothing?
595
+ value.nothing?
596
+ elsif code_argument.is_a?(Class)
597
+ value.is_a?(code_argument.raw)
598
+ else
599
+ code_argument.call(
600
+ arguments: List.new([value, key, Integer.new(index), self]),
601
+ **globals
602
+ ).truthy?
603
+ end
604
+ rescue Error::Next => e
605
+ e.code_value.truthy?
606
+ end
607
+ )
586
608
  end
587
609
 
588
- def code_compact!
589
- raw.delete_if { |_, value| value.falsy? }
610
+ def code_compact!(argument = nil, **globals)
611
+ code_argument = argument.to_code
612
+
613
+ raw.reject!.with_index do |(key, value), index|
614
+ if code_argument.nothing?
615
+ value.nothing?
616
+ elsif code_argument.is_a?(Class)
617
+ value.is_a?(code_argument.raw)
618
+ else
619
+ code_argument.call(
620
+ arguments: List.new([value, key, Integer.new(index), self]),
621
+ **globals
622
+ ).truthy?
623
+ end
624
+ rescue Error::Next => e
625
+ e.code_value.truthy?
626
+ end
627
+
590
628
  self
591
629
  end
592
630
 
@@ -33,6 +33,7 @@ class Code
33
33
  code_call(
34
34
  *code_arguments.raw,
35
35
  explicit_arguments: args.fetch(:explicit_arguments, true),
36
+ bound_self: args.fetch(:bound_self, nil),
36
37
  **globals
37
38
  )
38
39
  when "extend"
@@ -64,10 +65,11 @@ class Code
64
65
  code_arguments = arguments.to_code
65
66
  code_context = Context.new({}, definition_context || globals[:context])
66
67
  code_self = bound_self.to_code
67
- code_self = captured_self if code_self.nothing? && captured_self
68
68
  code_self = Dictionary.new if code_self.nil? || code_self.nothing?
69
+ code_parent = captured_self.to_code
69
70
 
70
71
  code_context.code_set("self", code_self)
72
+ bind_parent(code_context, code_self, code_parent)
71
73
 
72
74
  if parent.is_a?(Function)
73
75
  code_context.code_set(
@@ -110,7 +112,18 @@ class Code
110
112
  code_arguments.raw[index].to_code
111
113
  end
112
114
 
113
- code_argument = code_parameter.code_default if code_argument.nothing?
115
+ if code_argument.nothing?
116
+ code_default = code_parameter.code_default
117
+ code_argument =
118
+ if code_default.is_a?(Code)
119
+ code_default.code_evaluate(
120
+ **globals,
121
+ context: code_context
122
+ )
123
+ else
124
+ code_default
125
+ end
126
+ end
114
127
 
115
128
  code_context.code_set(code_parameter.code_name, code_argument)
116
129
  end
@@ -200,6 +213,18 @@ class Code
200
213
 
201
214
  private
202
215
 
216
+ def bind_parent(code_context, code_self, code_parent)
217
+ return if code_parent.nothing?
218
+
219
+ code_context.code_set("parent", code_parent)
220
+
221
+ return unless code_self.is_a?(Dictionary)
222
+ return if code_self == code_parent
223
+ return if code_self.code_has_key?("parent").truthy?
224
+
225
+ code_self.code_set("parent", code_parent)
226
+ end
227
+
203
228
  def captured_self
204
229
  self_from(definition_context)
205
230
  end
@@ -241,7 +266,7 @@ class Code
241
266
  if code_parameter.code_default.code_to_string.raw == "nothing"
242
267
  []
243
268
  else
244
- Code.parse(code_parameter.code_default.to_s)
269
+ ::Code.parse(code_parameter.code_default.to_s)
245
270
  end
246
271
  )
247
272
  end
@@ -1545,25 +1545,22 @@ class Code
1545
1545
  def code_compact(argument = nil, **globals)
1546
1546
  code_argument = argument.to_code
1547
1547
 
1548
- index = 0
1549
-
1550
1548
  List.new(
1551
- raw.select do |code_element|
1552
- if code_argument.is_a?(Function)
1553
- code_argument
1554
- .call(
1555
- arguments: List.new([code_element, Integer.new(index), self]),
1556
- **globals
1557
- )
1558
- .truthy?
1559
- .tap { index += 1 }
1549
+ raw.reject.with_index do |code_element, index|
1550
+ if code_argument.nothing?
1551
+ code_element.nothing?
1552
+ elsif code_argument.is_a?(Function)
1553
+ code_argument.call(
1554
+ arguments: List.new([code_element, Integer.new(index), self]),
1555
+ **globals
1556
+ ).truthy?
1560
1557
  elsif code_argument.is_a?(Class)
1561
- code_element.is_a?(code_argument.raw).tap { index += 1 }
1558
+ code_element.is_a?(code_argument.raw)
1562
1559
  else
1563
- code_element.truthy?.tap { index += 1 }
1560
+ false
1564
1561
  end
1565
1562
  rescue Error::Next => e
1566
- e.code_value.truhty?.tap { index += 1 }
1563
+ e.code_value.truthy?
1567
1564
  end
1568
1565
  )
1569
1566
  end
@@ -1571,24 +1568,21 @@ class Code
1571
1568
  def code_compact!(argument = nil, **globals)
1572
1569
  code_argument = argument.to_code
1573
1570
 
1574
- index = 0
1575
-
1576
- raw.select! do |code_element|
1577
- if code_argument.is_a?(Function)
1578
- code_argument
1579
- .call(
1580
- arguments: List.new([code_element, Integer.new(index), self]),
1581
- **globals
1582
- )
1583
- .truthy?
1584
- .tap { index += 1 }
1571
+ raw.reject!.with_index do |code_element, index|
1572
+ if code_argument.nothing?
1573
+ code_element.nothing?
1574
+ elsif code_argument.is_a?(Function)
1575
+ code_argument.call(
1576
+ arguments: List.new([code_element, Integer.new(index), self]),
1577
+ **globals
1578
+ ).truthy?
1585
1579
  elsif code_argument.is_a?(Class)
1586
- code_element.is_a?(code_argument.raw).tap { index += 1 }
1580
+ code_element.is_a?(code_argument.raw)
1587
1581
  else
1588
- code_element.truthy?.tap { index += 1 }
1582
+ false
1589
1583
  end
1590
1584
  rescue Error::Next => e
1591
- e.code_value.truhty?.tap { index += 1 }
1585
+ e.code_value.truthy?
1592
1586
  end
1593
1587
 
1594
1588
  self
@@ -16,6 +16,9 @@ RSpec.describe Code::Format do
16
16
  ["true || false", "true or false"],
17
17
  ["true && false", "true and false"],
18
18
  ["{a:1}", "{ a: 1 }"],
19
+ ["{ :mobility: mobility }", "{ mobility: mobility }"],
20
+ ['{ "hello": "world" }', "{ hello: :world }"],
21
+ ['{ "hello" => "world" }', "{ hello: :world }"],
19
22
  ["[1,2,3]", "[1, 2, 3]"],
20
23
  ["[1, 2, 3].select { |n| n.even? }", "[1, 2, 3].select { |n| n.even? }"],
21
24
  [
@@ -54,6 +57,10 @@ RSpec.describe Code::Format do
54
57
  "blocks << { title: \"hello world\", description: \"lorem ipsum dolor es sit\", position: 1 }",
55
58
  "blocks << {\n title: \"hello world\",\n description: \"lorem ipsum dolor es sit\",\n position: 1\n}"
56
59
  ],
60
+ [
61
+ "query = { :mobility: mobility, :unit_organization_id: unit_organization_id, :limit: limit, :offset: offset }",
62
+ "query = {\n mobility: mobility,\n unit_organization_id: unit_organization_id,\n limit: limit,\n offset: offset\n}"
63
+ ],
57
64
  [
58
65
  "sections << Html.join([Html.p { Html.b { \"{index + 1}. {title}\" } }, Html.p { query } if query.presence, Html.p { Html.a(href: link || inline_url) { :source } } if (link || inline_url), Html.p { Html.a(href: inline_url) { Html.img(src: inline_url, alt: title) } }, Html.p { Html.a(href: attachment_url) { \"télécharger\" } }].compact)",
59
66
  "sections << Html.join(\n [\n Html.p { Html.b { \"{index + 1}. {title}\" } },\n Html.p { query } if query.presence,\n Html.p {\n Html.a(href: link or inline_url) { :source }\n } if (link or inline_url),\n Html.p {\n Html.a(href: inline_url) { Html.img(src: inline_url, alt: title) }\n },\n Html.p {\n Html.a(href: attachment_url) { \"télécharger\" }\n }\n ].compact\n)"
@@ -140,7 +147,7 @@ RSpec.describe Code::Format do
140
147
 
141
148
  formatted = described_class.format(Code.parse(input))
142
149
 
143
- expect(formatted.lines.map(&:chomp).map(&:length).max).to be <= 80
150
+ expect(formatted.lines.map { |line| line.chomp.length }.max).to be <= 80
144
151
  end
145
152
  end
146
153
  end
@@ -74,6 +74,8 @@ RSpec.describe Code::Object::Dictionary do
74
74
  ["{ a: 1, b: [2, 3] }.flatten(1)", "[:a, 1, :b, [2, 3]]"],
75
75
  ["{ a: 1, b: [2, 3] }.to_list", "[[:a, 1], [:b, [2, 3]]]"],
76
76
  ["{ a: nothing }.compact", "{}"],
77
+ ["{ a: nothing, b: 1, c: false, d: \"\" }.compact", '{ b: 1, c: false, d: "" }'],
78
+ ["{ a: nothing, b: 1, c: false, d: \"\" }.compact(&:blank?)", "{ b: 1 }"],
77
79
  ["{ age: 31 }.delete_unless { |key| key == :age }", "{ age: 31 }"],
78
80
  ["{ age: 31 }.delete_unless(Integer)", "{ age: 31 }"],
79
81
  ["{ age: 31 }.keep_unless { |key| key == :age }", "{}"],
@@ -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
@@ -328,6 +328,18 @@ RSpec.describe Code do
328
328
  ["[1, 2, 3]", "[1, 2, 3]"],
329
329
  ["[1, 2, 3].include?(2)", "true"],
330
330
  ["[1, 2, 3].include?(4)", "false"],
331
+ [
332
+ "summary = { by_status: { draft: 1 } } k = \"draft\" summary.by_status[k]",
333
+ "1"
334
+ ],
335
+ [
336
+ "summary = { by_status: {} } k = \"draft\" summary.by_status[k] = 1 summary",
337
+ '{"by_status" => {"draft" => 1}}'
338
+ ],
339
+ [
340
+ "summary = { by_status: {} } [{status: \"draft\"}].each { |track| summary.by_status[track[:status]] = 1 } summary",
341
+ '{"by_status" => {"draft" => 1}}'
342
+ ],
331
343
  ["[1, 2, 3].map { |i| continue(0) if i.even? i ** 2}", "[1, 0, 9]"],
332
344
  ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
333
345
  ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié