code-ruby 3.0.5 → 3.0.7

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile.lock +1 -1
  4. data/VERSION +1 -1
  5. data/bin/code +27 -3
  6. data/lib/code/error.rb +10 -5
  7. data/lib/code/extensions/active_support.rb +9 -0
  8. data/lib/code/extensions/array.rb +7 -0
  9. data/lib/code/extensions/big_decimal.rb +9 -0
  10. data/lib/code/extensions/class.rb +7 -0
  11. data/lib/code/extensions/false_class.rb +7 -0
  12. data/lib/code/extensions/float.rb +9 -0
  13. data/lib/code/extensions/hash.rb +7 -0
  14. data/lib/code/extensions/integer.rb +9 -0
  15. data/lib/code/extensions/module.rb +7 -0
  16. data/lib/code/extensions/nil_class.rb +7 -0
  17. data/lib/code/extensions/nokogiri.rb +11 -0
  18. data/lib/code/extensions/object.rb +9 -0
  19. data/lib/code/extensions/string.rb +7 -0
  20. data/lib/code/extensions/symbol.rb +7 -0
  21. data/lib/code/extensions/true_class.rb +7 -0
  22. data/lib/code/extensions/word_number_comparaisons.rb +407 -0
  23. data/lib/code/format.rb +73 -56
  24. data/lib/code/node/code.rb +2 -1
  25. data/lib/code/node/statement.rb +4 -2
  26. data/lib/code/node/string.rb +1 -1
  27. data/lib/code/node/while.rb +8 -10
  28. data/lib/code/object/date.rb +37 -17
  29. data/lib/code/object/function.rb +23 -11
  30. data/lib/code/object/global.rb +5 -4
  31. data/lib/code/object/html.rb +169 -52
  32. data/lib/code/object/http.rb +8 -8
  33. data/lib/code/object/ics.rb +22 -18
  34. data/lib/code/object/identifier_list.rb +12 -10
  35. data/lib/code/object/list.rb +1 -5
  36. data/lib/code/object/super.rb +2 -1
  37. data/lib/code/object/time.rb +46 -44
  38. data/lib/code/parser.rb +342 -121
  39. data/lib/code-ruby.rb +16 -149
  40. data/spec/bin/code_spec.rb +32 -0
  41. data/spec/code/format_spec.rb +11 -10
  42. data/spec/code/node/call_spec.rb +1 -3
  43. data/spec/code/object/function_spec.rb +4 -8
  44. data/spec/code/object/http_spec.rb +20 -0
  45. data/spec/code/object/ics_spec.rb +5 -5
  46. data/spec/code/object/list_spec.rb +1 -1
  47. data/spec/code/parser_spec.rb +22 -0
  48. data/spec/code_spec.rb +347 -328
  49. metadata +18 -2
  50. data/applies +0 -0
data/spec/code_spec.rb CHANGED
@@ -165,332 +165,348 @@ RSpec.describe Code do
165
165
  end
166
166
  end
167
167
 
168
- [
168
+ examples =
169
169
  [
170
- "user = { name: :Dorian, age: 31 } user.delete_if(String) user.keys",
171
- '["age"]'
172
- ],
173
- [
174
- "(user = { name: :Dorian, age: 31 }).transform_values { user.name }.age",
175
- ":Dorian"
176
- ],
177
- ["{ end: 2 }.keys.first", ":end"],
178
- %w[!!1 true],
179
- %w[!!true true],
180
- %w[!false true],
181
- %w["" ''],
182
- %w["Hello".downcase :hello],
183
- %w['' ""],
184
- %w[+1 1],
185
- %w[+1.0 1.0],
186
- %w[+true true],
187
- %w[--1 1],
188
- %w[--1.0 1.0],
189
- %w[-1 -1],
190
- %w[-1.0 -1.0],
191
- %w[0 0],
192
- %w[0b10 2],
193
- %w[0o10 8],
194
- %w[0x10 16],
195
- %w[1.0e1 10.0],
196
- %w[1.1 1.1],
197
- %w[1.day.ago.after? false],
198
- %w[1.day.ago.after?(1.day.from_now) false],
199
- %w[1.day.ago.before? true],
200
- %w[1.day.ago.before?(1.day.from_now) true],
201
- %w[1.day.from_now.after? true],
202
- %w[1.day.from_now.after?(1.day.ago) true],
203
- %w[1.day.from_now.before? false],
204
- %w[1.day.from_now.before?(1.day.ago) false],
205
- %w[10e1.0 100.0],
206
- %w[1e1 10],
207
- %w[2.days.ago.future? false],
208
- %w[2.days.ago.past? true],
209
- %w[2.days.from_now.future? true],
210
- %w[2.days.from_now.past? false],
211
- %w[9975×14÷8 17456.25],
212
- %w["Hello".starts_with?("He") true],
213
- %w["Hello".starts_with?("lo") false],
214
- %w["Hello".start_with?("He") true],
215
- %w["Hello".start_with?("lo") false],
216
- %w["Hello".ends_with?("lo") true],
217
- %w["Hello".ends_with?("He") false],
218
- %w["Hello".end_with?("lo") true],
219
- %w["Hello".end_with?("He") false],
220
- %w[:Hello.include?(:H) true],
221
- %w[:admin? :admin?],
222
- %w[:hello :hello],
223
- %w[:update!.to_string :update!],
224
- %w[Boolean(1) true],
225
- %w[Boolean(2.days.ago) true],
226
- %w[Boolean(2.days.ago) true],
227
- %w[Boolean(false) false],
228
- %w[Boolean(nothing) false],
229
- %w[Boolean(true) true],
230
- %w[Boolean({}) true],
231
- %w[Boolean.new false],
232
- %w[Boolean.new(1) true],
233
- %w[Boolean.new(2.days.ago) true],
234
- %w[Boolean.new(2.days.ago) true],
235
- %w[Boolean.new(false) false],
236
- %w[Boolean.new(nothing) false],
237
- %w[Boolean.new(true) true],
238
- %w[Boolean.new({}) true],
239
- %w[Class(2.days.ago) Time],
240
- %w[Class(Boolean) Boolean],
241
- %w[Class(Time) Time],
242
- %w[Class(nothing) Nothing],
243
- %w[Class(true) Boolean],
244
- %w[String String],
245
- %w[Time Time],
246
- %w[Class Class],
247
- %w[Class.new Nothing],
248
- %w[Class.new Nothing],
249
- %w[Class.new(2.days.ago) Time],
250
- %w[Class.new(Boolean) Boolean],
251
- %w[Class.new(Date) Date],
252
- %w[Class.new(Time) Time],
253
- %w[Class.new(nothing) Nothing],
254
- %w[Class.new(true) Boolean],
255
- %w[Date Date],
256
- ["Date.format == Date.format(:default)", "true"],
257
- %w[Date("0001-01-01").to_string :0001-01-01],
258
- ["Date(\"2024-03-02\").format == Date(\"2024-03-02\").format(:default)", "true"],
259
- %w[Date("2024-03-02").to_string :2024-03-02],
260
- %w[Decimal(1) 1.0],
261
- %w[Decimal(1) 1],
262
- %w[Decimal(:1) 1.0],
263
- %w[Decimal(:1) 1],
264
- %w[Decimal.new 0],
265
- %w[Decimal.new(1) 1.0],
266
- %w[Decimal.new(1) 1],
267
- %w[Decimal.new(:1) 1.0],
268
- %w[Decimal.new(:1) 1],
269
- %w[false false],
270
- %w[true true],
271
- %w[{} {}],
272
- ["'Hello Dorian'", '"Hello Dorian"'],
273
- ["'Hello \\{name}'", ':Hello + " \\{name}"'],
274
- ["'Hello {1}'", '"Hello 1"'],
275
- ["(true false)", "false"],
276
- ["1 & 1", "1"],
277
- ["1 + 1", "2"],
278
- ["1 < 2", "true"],
279
- ["1 << 1", "2"],
280
- ["1 <= 1", "true"],
281
- ["1 == 1 and 2 == 2", "true"],
282
- ["1 == 1", "true"],
283
- ["1 > 2", "false"],
284
- ["1 >= 1", "true"],
285
- ["1 >> 1", "0"],
286
- ["1 ^ 2", "3"],
287
- ["1 | 2", "3"],
288
- ["2 * 3 + 2", "8"],
289
- ["2 * 3", "6"],
290
- ["2 ** 3 ** 2", "512"],
291
- ["2 ** 3", "8"],
292
- ["2 + 2 * 3", "8"],
293
- ["2 / 4", "0.5"],
294
- ["3.times {}", "3"],
295
- ["4 % 3", "1"],
296
- ["Boolean([])", "true"],
297
- ["Boolean(true, false)", "true"],
298
- ["Boolean.new([])", "true"],
299
- ["Boolean.new(false, true)", "false"],
300
- ["Boolean.new(true, false)", "true"],
301
- ["Class(true, 1)", "Boolean"],
302
- ["Class.new(Boolean, Time)", "Boolean"],
303
- ["Class.new(Time, Boolean)", "Time"],
304
- %w[Date("2024-3-2").to_string :2024-03-02],
305
- %w[Date("2024-3-2").to_string :2024-03-02],
306
- ["Time.format.present?", "true"],
307
- ["Class(Time.format(locale: :fr))", "String"],
308
- ["Time.new(\"2024-03-05.06:10:59.UTC\").format(locale: :fr) == Time.new(\"2024-03-05.06:10:59.UTC\").format(:default, locale: :fr)", "true"],
309
- ["Time.new(\"2024-03-05.06:10:59.UTC\").format == Time.new(\"2024-03-05.06:10:59.UTC\").format(:default)", "true"],
310
- ["Decimal(1, :2)", "100"],
311
- ["Decimal(:1, 2)", "100.0"],
312
- ["Decimal.new(1, :2)", "100"],
313
- ["Decimal.new(:1, 2)", "100.0"],
314
- ["[,,].include?(4)", "false"],
315
- ["[,].include?(4)", "false"],
316
- ["[1, 2, 3, \n ].include?(4)", "false"],
317
- ["[1, 2, 3,].include?(4)", "false"],
318
- ["[1, 2, 3]", "[1, 2, 3]"],
319
- ["[1, 2, 3].include?(2)", "true"],
320
- ["[1, 2, 3].include?(4)", "false"],
321
- ["[1, 2, 3].map { |i| continue(0) if i.even? i ** 2}", "[1, 0, 9]"],
322
- ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
323
- ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
324
- ["[1, 2, 3].select { |n| n.even? }", "[2]"],
325
- ["[1, 2, 3].select { |n| n.even? }.select { |n| n.odd? }", "[]"],
326
- ["[1, 2, 3, 4].uniq { |n| n % 2 }", "[1, 2]"],
327
- ['[1, "1", 2, "2"].uniq(String)', '["1", "2"]'],
328
- ["[1, 2, 3, 4].uniq(String)", "[]"],
329
- ["[1, 2].map(&:to_string)", "[:1, :2]"],
330
- ["[1, 2].map(&:to_string)", '["1", "2"]'],
331
- ["[1, 2].map(&:to_string)", '["1", "2"]'],
332
- ["[1].each do end", "[1]"],
333
- ["[[true]]", "[[true]]"],
334
- ["[]", "[]"],
335
- ["\r\n", "nothing"],
336
- ["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
337
- ["a = 0\nb = 0\nwhile a < 4\n a += 1\n continue if a == 2\n b += a\nend\nb", "8"],
338
- ["a = 0 loop a += 1 break end a", "1"],
339
- ["x = loop break(42) end x", "42"],
340
- ["a = 0\nuntil a > 10 a += 1 end a", "11"],
341
- ["a = 0\nwhile a < 10 a += 1 end a", "10"],
342
- ["a = 1 3.times { a += 1 } a", "4"],
343
- ["a = 1 a *= 2 a", "2"],
344
- ["a = 1 a += 1 a", "2"],
345
- ["a = 1 a += 1 a", "2"],
346
- ["a = 1 a -= 1 a", "0"],
347
- ["a = 3 a -= 1 if false a", "3"],
348
- ["a = 1 a = - 1 if false a", "1"],
349
- ["a = 3\n(a -= 1) if false\na", "3"],
350
- ["a = 1 a /= 2 a", "0.5"],
351
- ["a = 1 a <<= 1 a", "2"],
352
- ["a = 1 a >>= 1 a", "0"],
353
- ["a = 1 a ^= 2 a", "3"],
354
- ["a = 1 a |= 2 a", "3"],
355
- ["a = 1 a", "1"],
356
- ["a = 1 b = 2 c = a + b c", "3"],
357
- ["a = 1", "1"],
358
- ["a = 10 a %= 2 a", "0"],
359
- ["a = b = c = 1 a + b + c", "3"],
360
- ["a = false a &&= true a", "false"],
361
- ["a = false a ||= true a", "true"],
362
- ["a rescue :oops", ":oops"],
363
- ["false ? 1 : 2", "2"],
364
- ["false ? 1", ""],
365
- ["false || false", "false"],
366
- ["if false 1 else 2", "2"],
367
- ["if false 1 else if false 2", "nothing"],
368
- ["if false 1 else if true 2", "2"],
369
- ["if false 1 else if false 2 else 3 end", "3"],
370
- ["if false 1 else if true 2 else 3 end", "2"],
371
- ["if false 1 else unless false 2", "2"],
372
- ["if false 1 else unless true 2 else 3 end", "3"],
373
- ["if false 1 else unless true 2", "nothing"],
374
- ["if false 1 elsif false 2", "nothing"],
375
- ["if false 1 elsif true 2", "2"],
376
- ["if false 1", "nothing"],
377
- ["break(5)", "5"],
378
- ["continue(6)", "6"],
379
- ["if true 1", "1"],
380
- ["next(7)", "7"],
381
- ["not not false", "false"],
382
- ["not true", "false"],
383
- ["return(9)", "9"],
384
- ["f = () => { return(3) 4 } f()", "3"],
385
- ["a = 1 orirginal = 2 orirginal", "2"],
386
- ["a = 1 andy = 2 andy", "2"],
387
- ["ifonly = 1 ifonly", "1"],
388
- ["unlessy = 2 unlessy", "2"],
389
- ["elsiffy = 3 elsiffy", "3"],
390
- ["elsunlessy = 4 elsunlessy", "4"],
391
- ["elsewhere = 5 elsewhere", "5"],
392
- ["whiley = 6 whiley", "6"],
393
- ["untily = 7 untily", "7"],
394
- ["looped = 8 looped", "8"],
395
- ["doable = 9 doable", "9"],
396
- ["beginner = 10 beginner", "10"],
397
- ["endless = 11 endless", "11"],
398
- ["trueblue = 12 trueblue", "12"],
399
- ["falsey = 13 falsey", "13"],
400
- ["nothingness = 14 nothingness", "14"],
401
- ["random = 1 random", "1"],
402
- ["true && false", "false"],
403
- ["true && true", "true"],
404
- ["true ? 1 : 2", "1"],
405
- ["true ? 1", "1"],
406
- ["true and false", "false"],
407
- ["true or false", "true"],
408
- ["weekday? = false\n or true\nweekday?", "true"],
409
- ["true || false", "true"],
410
- ["unless false 1", "1"],
411
- ["unless true 1", "nothing"],
412
- ["until true", "nothing"],
413
- ["while false", "nothing"],
414
- ["{ a: 1, b: 2, c: 3 }", '{"a" => 1, "b" => 2, "c" => 3}'],
415
- ['"Hello Dorian"', '"Hello Dorian"'],
416
- ['"Hello \\{name}"', '"Hello \\{" + "name}"'],
417
- ['"Hello {1}"', '"Hello 1"'],
418
- ['user = {} user.name = "Dorian" user.name', ":Dorian"],
419
- ['user = {} user[:name] = "Dorian" user[:name]', ":Dorian"],
420
- ['value = {} email = "a" calendar_id = "c" value[email] ||= {} value[email][calendar_id] ||= {} value[email][calendar_id]', "{}"],
421
- ['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}'],
422
- ['{ "first_name": "Dorian" }.as_json', '{"first_name" => "Dorian"}'],
423
- %w[nothing.to_json :null],
424
- %w[1.to_json "1"],
425
- %w[1.0.to_json '"1.0"'],
426
- %w[1.1.to_json '"1.1"'],
427
- ["a = {} a.merge!(a: 1) a", "{a: 1}"],
428
- ["a = {} a.merge(a: 1) a", "{}"],
429
- %w[1&.even? false],
430
- ["nothing&.even? || 1", "1"],
431
- ["nothing&.even? && 1", "nothing"],
432
- %w[2&.even? true],
433
- ["a = 1 a&.even?", "false"],
434
- ["a = 2 a&.even?", "true"],
435
- ["a = nothing a&.even?", "nothing"],
436
- ["false && puts(:Hello)", "false"],
437
- ["true || puts(:Hello)", "true"],
438
- ["false and puts(:Hello)", "false"],
439
- ["true or puts(:Hello)", "true"],
440
- ["[1, 2].join", "'12'"],
441
- ["[1, 2].join(',')", "'1,2'"],
442
- ["[nothing, 2].join(',')", "',2'"],
443
- ["[nothing, nothing].join", "''"],
444
- ["[1, 2].select(&:even?)", "[2]"],
445
- ["[1, 2].reject(&:even?)", "[1]"],
446
- ["a = [1, 2] a.select!(&:even?) a", "[2]"],
447
- ["a = [1, 2] a.reject!(&:even?) a", "[1]"],
448
- ["[1, 2].map(&:even?)", "[false, true]"],
449
- ["a = [1, 2] a.map!(&:even?) a", "[false, true]"],
450
- ["Html.p { \"Hello\" }.to_html", "'<p>Hello</p>'"],
451
- ["Html.br.to_html", "'<br>'"],
452
- [
453
- "Html.div { Html.div { Html.span { :Nested } } }.to_html",
454
- "'<div><div><span>Nested</span></div></div>'"
455
- ],
456
- [
457
- "Html.div { Html.p { Html.span { :hello } } }.to_html",
458
- "'<div><p><span>hello</span></p></div>'"
459
- ],
460
- [
461
- "Html.join([Html.p { :hello }, Html.p { :world }], Html.br).to_html",
462
- "'<p>hello</p><br><p>world</p>'"
463
- ],
464
- ["Html.join.to_html", "''"],
465
- [
466
- "Html.div { Html.text(\"<span>\") }.to_html",
467
- "'<div>&lt;span&gt;</div>'"
468
- ],
469
- [
470
- "Html.div { Html.raw(\"<span>ok</span>\") }.to_html",
471
- "'<div><span>ok</span></div>'"
472
- ],
473
- [
474
- "Html.escape(\"<span>&</span>\")",
475
- "'&lt;span&gt;&amp;&lt;/span&gt;'"
476
- ],
477
- [
478
- "Html.unescape(\"A&nbsp;&nbsp;B &amp; C\")",
479
- "'A  B & C'"
480
- ],
481
- ["[1, 2, 3].any?", "true"],
482
- ["[1, 2, 3].any?(&:even?)", "true"],
483
- ["[1, 2, 3].none?", "false"],
484
- ["[1, 2, 3].none?(&:even?)", "false"],
485
- ["subject = 1 { subject }", "{ subject: 1 }"],
486
- ["subject = 1 { subject: }", "{ subject: 1 }"],
487
- ["'{1} {2}'", "'1 2'"],
488
- ['Time.zone = "Etc/UTC"', '"Etc/UTC"'],
489
- %w[Json.parse("1") 1],
490
- %w[{a:1}.to_query "a=1"],
491
- ["dorian = 1 dorian#.to_something", "1"],
492
- ["", ""]
493
- ].each do |input, expected|
170
+ [
171
+ "user = { name: :Dorian, age: 31 } user.delete_if(String) user.keys",
172
+ '["age"]'
173
+ ],
174
+ [
175
+ "(user = { name: :Dorian, age: 31 }).transform_values { user.name }.age",
176
+ ":Dorian"
177
+ ],
178
+ ["{ end: 2 }.keys.first", ":end"],
179
+ %w[!!1 true],
180
+ %w[!!true true],
181
+ %w[!false true],
182
+ %w["" ''],
183
+ %w["Hello".downcase :hello],
184
+ %w['' ""],
185
+ %w[+1 1],
186
+ %w[+1.0 1.0],
187
+ %w[+true true],
188
+ %w[--1 1],
189
+ %w[--1.0 1.0],
190
+ %w[-1 -1],
191
+ %w[-1.0 -1.0],
192
+ %w[0 0],
193
+ %w[0b10 2],
194
+ %w[0o10 8],
195
+ %w[0x10 16],
196
+ %w[1.0e1 10.0],
197
+ %w[1.1 1.1],
198
+ %w[1.day.ago.after? false],
199
+ %w[1.day.ago.after?(1.day.from_now) false],
200
+ %w[1.day.ago.before? true],
201
+ %w[1.day.ago.before?(1.day.from_now) true],
202
+ %w[1.day.from_now.after? true],
203
+ %w[1.day.from_now.after?(1.day.ago) true],
204
+ %w[1.day.from_now.before? false],
205
+ %w[1.day.from_now.before?(1.day.ago) false],
206
+ %w[10e1.0 100.0],
207
+ %w[1e1 10],
208
+ %w[2.days.ago.future? false],
209
+ %w[2.days.ago.past? true],
210
+ %w[2.days.from_now.future? true],
211
+ %w[2.days.from_now.past? false],
212
+ %w[9975×14÷8 17456.25],
213
+ %w["Hello".starts_with?("He") true],
214
+ %w["Hello".starts_with?("lo") false],
215
+ %w["Hello".start_with?("He") true],
216
+ %w["Hello".start_with?("lo") false],
217
+ %w["Hello".ends_with?("lo") true],
218
+ %w["Hello".ends_with?("He") false],
219
+ %w["Hello".end_with?("lo") true],
220
+ %w["Hello".end_with?("He") false],
221
+ %w[:Hello.include?(:H) true],
222
+ %w[:admin? :admin?],
223
+ %w[:hello :hello],
224
+ %w[:update!.to_string :update!],
225
+ %w[Boolean(1) true],
226
+ %w[Boolean(2.days.ago) true],
227
+ %w[Boolean(2.days.ago) true],
228
+ %w[Boolean(false) false],
229
+ %w[Boolean(nothing) false],
230
+ %w[Boolean(true) true],
231
+ %w[Boolean({}) true],
232
+ %w[Boolean.new false],
233
+ %w[Boolean.new(1) true],
234
+ %w[Boolean.new(2.days.ago) true],
235
+ %w[Boolean.new(2.days.ago) true],
236
+ %w[Boolean.new(false) false],
237
+ %w[Boolean.new(nothing) false],
238
+ %w[Boolean.new(true) true],
239
+ %w[Boolean.new({}) true],
240
+ %w[Class(2.days.ago) Time],
241
+ %w[Class(Boolean) Boolean],
242
+ %w[Class(Time) Time],
243
+ %w[Class(nothing) Nothing],
244
+ %w[Class(true) Boolean],
245
+ %w[String String],
246
+ %w[Time Time],
247
+ %w[Class Class],
248
+ %w[Class.new Nothing],
249
+ %w[Class.new Nothing],
250
+ %w[Class.new(2.days.ago) Time],
251
+ %w[Class.new(Boolean) Boolean],
252
+ %w[Class.new(Date) Date],
253
+ %w[Class.new(Time) Time],
254
+ %w[Class.new(nothing) Nothing],
255
+ %w[Class.new(true) Boolean],
256
+ %w[Date Date],
257
+ ["Date.format == Date.format(:default)", "true"],
258
+ %w[Date("0001-01-01").to_string :0001-01-01],
259
+ [
260
+ "Date(\"2024-03-02\").format == Date(\"2024-03-02\").format(:default)",
261
+ "true"
262
+ ],
263
+ %w[Date("2024-03-02").to_string :2024-03-02],
264
+ %w[Decimal(1) 1.0],
265
+ %w[Decimal(1) 1],
266
+ %w[Decimal(:1) 1.0],
267
+ %w[Decimal(:1) 1],
268
+ %w[Decimal.new 0],
269
+ %w[Decimal.new(1) 1.0],
270
+ %w[Decimal.new(1) 1],
271
+ %w[Decimal.new(:1) 1.0],
272
+ %w[Decimal.new(:1) 1],
273
+ %w[false false],
274
+ %w[true true],
275
+ %w[{} {}],
276
+ ["'Hello Dorian'", '"Hello Dorian"'],
277
+ ["'Hello \\{name}'", ':Hello + " \\{name}"'],
278
+ ["'Hello {1}'", '"Hello 1"'],
279
+ ["(true false)", "false"],
280
+ ["1 & 1", "1"],
281
+ ["1 + 1", "2"],
282
+ ["1 < 2", "true"],
283
+ ["1 << 1", "2"],
284
+ ["1 <= 1", "true"],
285
+ ["1 == 1 and 2 == 2", "true"],
286
+ ["1 == 1", "true"],
287
+ ["1 > 2", "false"],
288
+ ["1 >= 1", "true"],
289
+ ["1 >> 1", "0"],
290
+ ["1 ^ 2", "3"],
291
+ ["1 | 2", "3"],
292
+ ["2 * 3 + 2", "8"],
293
+ ["2 * 3", "6"],
294
+ ["2 ** 3 ** 2", "512"],
295
+ ["2 ** 3", "8"],
296
+ ["2 + 2 * 3", "8"],
297
+ ["2 / 4", "0.5"],
298
+ ["3.times {}", "3"],
299
+ ["4 % 3", "1"],
300
+ ["Boolean([])", "true"],
301
+ ["Boolean(true, false)", "true"],
302
+ ["Boolean.new([])", "true"],
303
+ ["Boolean.new(false, true)", "false"],
304
+ ["Boolean.new(true, false)", "true"],
305
+ ["Class(true, 1)", "Boolean"],
306
+ ["Class.new(Boolean, Time)", "Boolean"],
307
+ ["Class.new(Time, Boolean)", "Time"],
308
+ %w[Date("2024-3-2").to_string :2024-03-02],
309
+ %w[Date("2024-3-2").to_string :2024-03-02],
310
+ %w[Time.format.present? true],
311
+ ["Class(Time.format(locale: :fr))", "String"],
312
+ [
313
+ "Time.new(\"2024-03-05.06:10:59.UTC\").format(locale: :fr) == Time.new(\"2024-03-05.06:10:59.UTC\").format(:default, locale: :fr)",
314
+ "true"
315
+ ],
316
+ [
317
+ "Time.new(\"2024-03-05.06:10:59.UTC\").format == Time.new(\"2024-03-05.06:10:59.UTC\").format(:default)",
318
+ "true"
319
+ ],
320
+ ["Decimal(1, :2)", "100"],
321
+ ["Decimal(:1, 2)", "100.0"],
322
+ ["Decimal.new(1, :2)", "100"],
323
+ ["Decimal.new(:1, 2)", "100.0"],
324
+ ["[,,].include?(4)", "false"],
325
+ ["[,].include?(4)", "false"],
326
+ ["[1, 2, 3, \n ].include?(4)", "false"],
327
+ ["[1, 2, 3,].include?(4)", "false"],
328
+ ["[1, 2, 3]", "[1, 2, 3]"],
329
+ ["[1, 2, 3].include?(2)", "true"],
330
+ ["[1, 2, 3].include?(4)", "false"],
331
+ ["[1, 2, 3].map { |i| continue(0) if i.even? i ** 2}", "[1, 0, 9]"],
332
+ ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
333
+ ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
334
+ ["[1, 2, 3].select { |n| n.even? }", "[2]"],
335
+ ["[1, 2, 3].select { |n| n.even? }.select { |n| n.odd? }", "[]"],
336
+ ["[1, 2, 3, 4].uniq { |n| n % 2 }", "[1, 2]"],
337
+ ['[1, "1", 2, "2"].uniq(String)', '["1", "2"]'],
338
+ ["[1, 2, 3, 4].uniq(String)", "[]"],
339
+ ["[1, 2].map(&:to_string)", "[:1, :2]"],
340
+ ["[1, 2].map(&:to_string)", '["1", "2"]'],
341
+ ["[1, 2].map(&:to_string)", '["1", "2"]'],
342
+ ["[1].each do end", "[1]"],
343
+ ["[[true]]", "[[true]]"],
344
+ ["[]", "[]"],
345
+ ["\r\n", "nothing"],
346
+ ["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
347
+ [
348
+ "a = 0\nb = 0\nwhile a < 4\n a += 1\n continue if a == 2\n b += a\nend\nb",
349
+ "8"
350
+ ],
351
+ ["a = 0 loop a += 1 break end a", "1"],
352
+ ["x = loop break(42) end x", "42"],
353
+ ["a = 0\nuntil a > 10 a += 1 end a", "11"],
354
+ ["a = 0\nwhile a < 10 a += 1 end a", "10"],
355
+ ["a = 1 3.times { a += 1 } a", "4"],
356
+ ["a = 1 a *= 2 a", "2"],
357
+ ["a = 1 a += 1 a", "2"],
358
+ ["a = 1 a += 1 a", "2"],
359
+ ["a = 1 a -= 1 a", "0"],
360
+ ["a = 3 a -= 1 if false a", "3"],
361
+ ["a = 1 a = - 1 if false a", "1"],
362
+ ["a = 3\n(a -= 1) if false\na", "3"],
363
+ ["a = 1 a /= 2 a", "0.5"],
364
+ ["a = 1 a <<= 1 a", "2"],
365
+ ["a = 1 a >>= 1 a", "0"]
366
+ ] +
367
+ [
368
+ ["a = 1 a ^= 2 a", "3"],
369
+ ["a = 1 a |= 2 a", "3"],
370
+ ["a = 1 a", "1"],
371
+ ["a = 1 b = 2 c = a + b c", "3"],
372
+ ["a = 1", "1"],
373
+ ["a = 10 a %= 2 a", "0"],
374
+ ["a = b = c = 1 a + b + c", "3"],
375
+ ["a = false a &&= true a", "false"],
376
+ ["a = false a ||= true a", "true"],
377
+ ["a rescue :oops", ":oops"],
378
+ ["false ? 1 : 2", "2"],
379
+ ["false ? 1", ""],
380
+ ["false || false", "false"],
381
+ ["if false 1 else 2", "2"],
382
+ ["if false 1 else if false 2", "nothing"],
383
+ ["if false 1 else if true 2", "2"],
384
+ ["if false 1 else if false 2 else 3 end", "3"],
385
+ ["if false 1 else if true 2 else 3 end", "2"],
386
+ ["if false 1 else unless false 2", "2"],
387
+ ["if false 1 else unless true 2 else 3 end", "3"],
388
+ ["if false 1 else unless true 2", "nothing"],
389
+ ["if false 1 elsif false 2", "nothing"],
390
+ ["if false 1 elsif true 2", "2"],
391
+ ["if false 1", "nothing"],
392
+ %w[break(5) 5],
393
+ %w[continue(6) 6],
394
+ ["if true 1", "1"],
395
+ %w[next(7) 7],
396
+ ["not not false", "false"],
397
+ ["not true", "false"],
398
+ %w[return(9) 9],
399
+ ["f = () => { return(3) 4 } f()", "3"],
400
+ ["a = 1 orirginal = 2 orirginal", "2"],
401
+ ["a = 1 andy = 2 andy", "2"],
402
+ ["ifonly = 1 ifonly", "1"],
403
+ ["unlessy = 2 unlessy", "2"],
404
+ ["elsiffy = 3 elsiffy", "3"],
405
+ ["elsunlessy = 4 elsunlessy", "4"],
406
+ ["elsewhere = 5 elsewhere", "5"],
407
+ ["whiley = 6 whiley", "6"],
408
+ ["untily = 7 untily", "7"],
409
+ ["looped = 8 looped", "8"],
410
+ ["doable = 9 doable", "9"],
411
+ ["beginner = 10 beginner", "10"],
412
+ ["endless = 11 endless", "11"],
413
+ ["trueblue = 12 trueblue", "12"],
414
+ ["falsey = 13 falsey", "13"],
415
+ ["nothingness = 14 nothingness", "14"],
416
+ ["random = 1 random", "1"],
417
+ ["true && false", "false"],
418
+ ["true && true", "true"],
419
+ ["true ? 1 : 2", "1"],
420
+ ["true ? 1", "1"],
421
+ ["true and false", "false"],
422
+ ["true or false", "true"],
423
+ ["weekday? = false\n or true\nweekday?", "true"],
424
+ ["true || false", "true"],
425
+ ["unless false 1", "1"],
426
+ ["unless true 1", "nothing"],
427
+ ["until true", "nothing"],
428
+ ["while false", "nothing"],
429
+ ["{ a: 1, b: 2, c: 3 }", '{"a" => 1, "b" => 2, "c" => 3}'],
430
+ ['"Hello Dorian"', '"Hello Dorian"'],
431
+ ['"Hello \\{name}"', '"Hello \\{" + "name}"'],
432
+ ['"Hello {1}"', '"Hello 1"'],
433
+ ['user = {} user.name = "Dorian" user.name', ":Dorian"],
434
+ ['user = {} user[:name] = "Dorian" user[:name]', ":Dorian"],
435
+ [
436
+ 'value = {} email = "a" calendar_id = "c" value[email] ||= {} value[email][calendar_id] ||= {} value[email][calendar_id]',
437
+ "{}"
438
+ ],
439
+ ['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}'],
440
+ ['{ "first_name": "Dorian" }.as_json', '{"first_name" => "Dorian"}'],
441
+ %w[nothing.to_json :null],
442
+ %w[1.to_json "1"],
443
+ %w[1.0.to_json '"1.0"'],
444
+ %w[1.1.to_json '"1.1"'],
445
+ ["a = {} a.merge!(a: 1) a", "{a: 1}"],
446
+ ["a = {} a.merge(a: 1) a", "{}"],
447
+ %w[1&.even? false],
448
+ ["nothing&.even? || 1", "1"],
449
+ ["nothing&.even? && 1", "nothing"],
450
+ %w[2&.even? true],
451
+ ["a = 1 a&.even?", "false"],
452
+ ["a = 2 a&.even?", "true"],
453
+ ["a = nothing a&.even?", "nothing"],
454
+ ["false && puts(:Hello)", "false"],
455
+ ["true || puts(:Hello)", "true"],
456
+ ["false and puts(:Hello)", "false"],
457
+ ["true or puts(:Hello)", "true"],
458
+ ["[1, 2].join", "'12'"],
459
+ ["[1, 2].join(',')", "'1,2'"],
460
+ ["[nothing, 2].join(',')", "',2'"],
461
+ ["[nothing, nothing].join", "''"],
462
+ ["[1, 2].select(&:even?)", "[2]"],
463
+ ["[1, 2].reject(&:even?)", "[1]"],
464
+ ["a = [1, 2] a.select!(&:even?) a", "[2]"],
465
+ ["a = [1, 2] a.reject!(&:even?) a", "[1]"],
466
+ ["[1, 2].map(&:even?)", "[false, true]"]
467
+ ] +
468
+ [
469
+ ["a = [1, 2] a.map!(&:even?) a", "[false, true]"],
470
+ ["Html.p { \"Hello\" }.to_html", "'<p>Hello</p>'"],
471
+ %w[Html.br.to_html '<br>'],
472
+ [
473
+ "Html.div { Html.div { Html.span { :Nested } } }.to_html",
474
+ "'<div><div><span>Nested</span></div></div>'"
475
+ ],
476
+ [
477
+ "Html.div { Html.p { Html.span { :hello } } }.to_html",
478
+ "'<div><p><span>hello</span></p></div>'"
479
+ ],
480
+ [
481
+ "Html.join([Html.p { :hello }, Html.p { :world }], Html.br).to_html",
482
+ "'<p>hello</p><br><p>world</p>'"
483
+ ],
484
+ %w[Html.join.to_html ''],
485
+ [
486
+ "Html.div { Html.text(\"<span>\") }.to_html",
487
+ "'<div>&lt;span&gt;</div>'"
488
+ ],
489
+ [
490
+ "Html.div { Html.raw(\"<span>ok</span>\") }.to_html",
491
+ "'<div><span>ok</span></div>'"
492
+ ],
493
+ ["Html.escape(\"<span>&</span>\")", "'&lt;span&gt;&amp;&lt;/span&gt;'"],
494
+ ["Html.unescape(\"A&nbsp;&nbsp;B &amp; C\")", "'A  B & C'"],
495
+ ["[1, 2, 3].any?", "true"],
496
+ ["[1, 2, 3].any?(&:even?)", "true"],
497
+ ["[1, 2, 3].none?", "false"],
498
+ ["[1, 2, 3].none?(&:even?)", "false"],
499
+ ["subject = 1 { subject }", "{ subject: 1 }"],
500
+ ["subject = 1 { subject: }", "{ subject: 1 }"],
501
+ ["'{1} {2}'", "'1 2'"],
502
+ ['Time.zone = "Etc/UTC"', '"Etc/UTC"'],
503
+ %w[Json.parse("1") 1],
504
+ %w[{a:1}.to_query "a=1"],
505
+ ["dorian = 1 dorian#.to_something", "1"],
506
+ ["", ""]
507
+ ]
508
+
509
+ examples.each do |input, expected|
494
510
  it "#{input} == #{expected}" do
495
511
  formatted = format_input(input)
496
512
 
@@ -501,7 +517,8 @@ RSpec.describe Code do
501
517
  expect(output.string).to eq("")
502
518
 
503
519
  formatted_output = StringIO.new
504
- formatted_result = described_class.evaluate(formatted, output: formatted_output)
520
+ formatted_result =
521
+ described_class.evaluate(formatted, output: formatted_output)
505
522
  expect(formatted_result).to eq(code_input)
506
523
  expect(formatted_output.string).to eq("")
507
524
  next if code_input.is_a?(Code::Object::Decimal)
@@ -597,7 +614,7 @@ RSpec.describe Code do
597
614
  summary: "Lunch",
598
615
  location: "Paris",
599
616
  uid: "event-1",
600
- categories: ["food", "team"],
617
+ categories: %w[food team],
601
618
  starts_at: "2026-03-27 12:00:00 UTC",
602
619
  ends_at: "2026-03-27 13:30:00 UTC",
603
620
  all_day: false
@@ -606,6 +623,8 @@ RSpec.describe Code do
606
623
  end
607
624
 
608
625
  it "returns an empty list for invalid ics" do
609
- expect(described_class.evaluate('Ics.parse("not an ics file")')).to eq([].to_code)
626
+ expect(described_class.evaluate('Ics.parse("not an ics file")')).to eq(
627
+ [].to_code
628
+ )
610
629
  end
611
630
  end