code-ruby 3.1.2 → 4.0.0

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/code +97 -20
  4. data/lib/code/concerns/shared.rb +331 -15
  5. data/lib/code/format.rb +15 -1
  6. data/lib/code/network.rb +87 -0
  7. data/lib/code/node/call.rb +79 -2
  8. data/lib/code/node/call_argument.rb +14 -0
  9. data/lib/code/node/code.rb +5 -4
  10. data/lib/code/node/function_parameter.rb +7 -4
  11. data/lib/code/node/list.rb +29 -1
  12. data/lib/code/object/base_64.rb +132 -6
  13. data/lib/code/object/boolean.rb +60 -0
  14. data/lib/code/object/class.rb +138 -2
  15. data/lib/code/object/code.rb +111 -3
  16. data/lib/code/object/context.rb +57 -1
  17. data/lib/code/object/cryptography.rb +63 -0
  18. data/lib/code/object/date.rb +13339 -462
  19. data/lib/code/object/decimal.rb +1725 -0
  20. data/lib/code/object/dictionary.rb +1790 -11
  21. data/lib/code/object/duration.rb +28 -0
  22. data/lib/code/object/function.rb +261 -23
  23. data/lib/code/object/global.rb +534 -1
  24. data/lib/code/object/html.rb +179 -7
  25. data/lib/code/object/http.rb +244 -14
  26. data/lib/code/object/ics.rb +75 -13
  27. data/lib/code/object/identifier_list.rb +17 -2
  28. data/lib/code/object/integer.rb +1937 -2
  29. data/lib/code/object/json.rb +75 -1
  30. data/lib/code/object/list.rb +3383 -10
  31. data/lib/code/object/nothing.rb +53 -0
  32. data/lib/code/object/number.rb +110 -0
  33. data/lib/code/object/parameter.rb +140 -0
  34. data/lib/code/object/range.rb +576 -14
  35. data/lib/code/object/smtp.rb +95 -12
  36. data/lib/code/object/string.rb +944 -3
  37. data/lib/code/object/super.rb +10 -1
  38. data/lib/code/object/time.rb +13358 -498
  39. data/lib/code/object/url.rb +65 -0
  40. data/lib/code/object.rb +543 -0
  41. data/lib/code/parser.rb +161 -24
  42. data/lib/code-ruby.rb +3 -0
  43. data/lib/code.rb +30 -3
  44. metadata +135 -84
  45. data/.github/dependabot.yml +0 -15
  46. data/.github/workflows/ci.yml +0 -38
  47. data/.gitignore +0 -30
  48. data/.node-version +0 -1
  49. data/.npm-version +0 -1
  50. data/.prettierignore +0 -2
  51. data/.rspec +0 -1
  52. data/.rubocop.yml +0 -140
  53. data/.ruby-version +0 -1
  54. data/.tool-versions +0 -3
  55. data/AGENTS.md +0 -43
  56. data/Gemfile +0 -22
  57. data/Gemfile.lock +0 -292
  58. data/Rakefile +0 -5
  59. data/bin/bundle +0 -123
  60. data/bin/bundle-audit +0 -31
  61. data/bin/bundler-audit +0 -31
  62. data/bin/dorian +0 -31
  63. data/bin/rspec +0 -31
  64. data/bin/rubocop +0 -31
  65. data/bin/test +0 -5
  66. data/code-ruby.gemspec +0 -34
  67. data/docs/precedence.txt +0 -36
  68. data/package-lock.json +0 -14
  69. data/package.json +0 -7
  70. data/spec/bin/code_spec.rb +0 -48
  71. data/spec/code/format_spec.rb +0 -153
  72. data/spec/code/node/call_spec.rb +0 -11
  73. data/spec/code/object/boolean_spec.rb +0 -18
  74. data/spec/code/object/cryptography_spec.rb +0 -25
  75. data/spec/code/object/decimal_spec.rb +0 -50
  76. data/spec/code/object/dictionary_spec.rb +0 -98
  77. data/spec/code/object/function_spec.rb +0 -268
  78. data/spec/code/object/http_spec.rb +0 -33
  79. data/spec/code/object/ics_spec.rb +0 -50
  80. data/spec/code/object/integer_spec.rb +0 -42
  81. data/spec/code/object/list_spec.rb +0 -22
  82. data/spec/code/object/nothing_spec.rb +0 -14
  83. data/spec/code/object/range_spec.rb +0 -23
  84. data/spec/code/object/string_spec.rb +0 -26
  85. data/spec/code/parser/boolean_spec.rb +0 -11
  86. data/spec/code/parser/chained_call_spec.rb +0 -16
  87. data/spec/code/parser/dictionary_spec.rb +0 -18
  88. data/spec/code/parser/function_spec.rb +0 -16
  89. data/spec/code/parser/group_spec.rb +0 -11
  90. data/spec/code/parser/if_modifier_spec.rb +0 -18
  91. data/spec/code/parser/list_spec.rb +0 -17
  92. data/spec/code/parser/number_spec.rb +0 -11
  93. data/spec/code/parser/string_spec.rb +0 -20
  94. data/spec/code/parser_spec.rb +0 -52
  95. data/spec/code/type_spec.rb +0 -21
  96. data/spec/code_spec.rb +0 -717
  97. data/spec/spec_helper.rb +0 -21
  98. data/spec/zeitwerk/loader_spec.rb +0 -7
data/docs/precedence.txt DELETED
@@ -1,36 +0,0 @@
1
- Code
2
- Statement
3
- Splat
4
- Class
5
- While
6
- If
7
- IfModifier
8
- OrKeyword
9
- NotKeyword
10
- Equal
11
- Rescue
12
- Ternary
13
- Range
14
- OrOperator
15
- AndOperator
16
- Equality
17
- Greater
18
- BitwiseOr
19
- BitwiseAnd
20
- Shift
21
- Addition
22
- Multiplication
23
- Negation
24
- ChainedCall
25
- SquareBracket
26
- UnaryMinus
27
- Power
28
- Function
29
- Dictionary
30
- List
31
- String
32
- Number
33
- Boolean
34
- Nothing
35
- Group
36
- Call
data/package-lock.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "name": "code-ruby",
3
- "lockfileVersion": 3,
4
- "requires": true,
5
- "packages": {
6
- "": {
7
- "license": "MIT",
8
- "engines": {
9
- "node": "24.4.1",
10
- "npm": "11.4.2"
11
- }
12
- }
13
- }
14
- }
data/package.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "license": "MIT",
3
- "engines": {
4
- "node": "24.4.1",
5
- "npm": "11.4.2"
6
- }
7
- }
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
- require "spec_helper"
5
-
6
- RSpec.describe "bin/code" do
7
- let(:bin) { File.expand_path("../../bin/code", __dir__) }
8
- let(:tmp_glob_dir) { File.expand_path("../tmp/code_glob", __dir__) }
9
-
10
- after { FileUtils.rm_rf(tmp_glob_dir) }
11
-
12
- it "formats input with -f" do
13
- stdout, stderr, status = Open3.capture3(bin, "-f", "{a:1}")
14
-
15
- expect(status.success?).to be(true)
16
- expect(stderr).to eq("")
17
- expect(stdout).to eq("{ a: 1 }")
18
- end
19
-
20
- it "formats quoted globbed files with -f" do
21
- FileUtils.mkdir_p(File.join(tmp_glob_dir, "a"))
22
- FileUtils.mkdir_p(File.join(tmp_glob_dir, "b"))
23
- File.write(File.join(tmp_glob_dir, "a", "first.code"), "{a:1}")
24
- File.write(File.join(tmp_glob_dir, "b", "second.code"), "{b:2}")
25
-
26
- stdout, stderr, status =
27
- Open3.capture3(bin, "-f", File.join(tmp_glob_dir, "**", "*.code"))
28
-
29
- expect(status.success?).to be(true)
30
- expect(stderr).to eq("")
31
- expect(stdout).to eq("{ a: 1 }\n\n{ b: 2 }")
32
- end
33
-
34
- it "formats shell-expanded globbed files with -f" do
35
- FileUtils.mkdir_p(File.join(tmp_glob_dir, "a"))
36
- FileUtils.mkdir_p(File.join(tmp_glob_dir, "b"))
37
- first = File.join(tmp_glob_dir, "a", "first.code")
38
- second = File.join(tmp_glob_dir, "b", "second.code")
39
- File.write(first, "{a:1}")
40
- File.write(second, "{b:2}")
41
-
42
- stdout, stderr, status = Open3.capture3(bin, "-f", first, second)
43
-
44
- expect(status.success?).to be(true)
45
- expect(stderr).to eq("")
46
- expect(stdout).to eq("{ a: 1 }\n\n{ b: 2 }")
47
- end
48
- end
@@ -1,153 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "json"
5
-
6
- RSpec.describe Code::Format do
7
- describe ".format" do
8
- [
9
- %w[Time.now.second Time.now.second],
10
- %w[{} {}],
11
- ["[]", "[]"],
12
- %w["" ""],
13
- %w[100000 100_000],
14
- %w[1000000 1_000_000],
15
- %w[1.0000000001 1.000_000_000_1],
16
- ["true || false", "true or false"],
17
- ["true && false", "true and false"],
18
- ["{a:1}", "{ a: 1 }"],
19
- ["{ :mobility: mobility }", "{ mobility: mobility }"],
20
- ['{ "hello": "world" }', "{ hello: :world }"],
21
- ['{ "hello" => "world" }', "{ hello: :world }"],
22
- ["[1,2,3]", "[1, 2, 3]"],
23
- ["[1, 2, 3].select { |n| n.even? }", "[1, 2, 3].select { |n| n.even? }"],
24
- [
25
- "if true 1 elsif false 2 else 3 end",
26
- "if true\n 1\nelsif false\n 2\nelse\n 3\nend"
27
- ],
28
- [
29
- "if false 1 else if true 2 else 3 end",
30
- "if false\n 1\nelse\n if true\n 2\n else\n 3\n end\nend"
31
- ],
32
- [
33
- "event[:uid].present? and event[:summary].present? and event[:starts_at].present? and event[:ends_at].present?",
34
- "event[:uid].present?\n and event[:summary].present?\n and event[:starts_at].present?\n and event[:ends_at].present?"
35
- ],
36
- [
37
- "Html.p { \"x{Time.new(event.starts_at).format}y\" + \"{Time.new(event.ends_at).format}\" }",
38
- "Html.p {\n \"x{Time.new(event.starts_at).format}y\"\n + \"{Time.new(event.ends_at).format}\"\n}"
39
- ],
40
- [
41
- "sum = (a, b: 2) => { a + b } sum(1)",
42
- "sum = (a, b: 2) => {\n a + b\n}\n\nsum(1)"
43
- ],
44
- [
45
- "Http.post(\"https://api.openai.com/v1/chat/completions\", headers: { authorization: \"Bearer {open_ai_api_key}\", \"content-type\": \"application/json\" }, body: { model: model, messages: [{ role: \"system\", content: \"hello\" }, { role: \"user\", content: \"world\" }] }.to_json)",
46
- "Http.post(\n \"https://api.openai.com/v1/chat/completions\",\n headers: {\n authorization: \"Bearer {open_ai_api_key}\",\n \"content-type\": \"application/json\"\n },\n body: {\n model: model,\n messages: [\n { role: :system, content: :hello },\n { role: :user, content: :world }\n ]\n }.to_json\n)"
47
- ],
48
- [
49
- "proxy_url = (url) => { \"{proxy_base_url}?{{ url: url, disposition: proxy_inline_disposition }.to_query}\" }",
50
- "proxy_url = (url) => {\n \"{proxy_base_url}?\"\n + \"{{\n url: url,\n disposition: proxy_inline_disposition\n }.to_query}\"\n}"
51
- ],
52
- [
53
- "x = { content: \"you select the funniest tweets for a french \" + \"audience from the provided candidates. pick up to \" + \"{max_selected}. prioritize genuinely funny content\" + \" (jokes, memes, punchlines, absurd). avoid \" + \"politics/news/serious content. output only json.\" }",
54
- "x = {\n content: \"you select the funniest tweets for a french \"\n + \"audience from the provided candidates. pick up to \"\n + \"{max_selected}. prioritize genuinely funny content\"\n + \" (jokes, memes, punchlines, absurd). avoid \"\n + \"politics/news/serious content. output only json.\"\n}"
55
- ],
56
- [
57
- "blocks << { title: \"hello world\", description: \"lorem ipsum dolor es sit\", position: 1 }",
58
- "blocks << {\n title: \"hello world\",\n description: \"lorem ipsum dolor es sit\",\n position: 1\n}"
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
- ],
64
- [
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)",
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)"
67
- ],
68
- [
69
- "safe = post.present? and !post[:over_18] and post[:post_hint] == :image and post[:url].to_string.strip.presence and (post[:url].to_string.strip.ends_with?(\".jpg\") or post[:url].to_string.strip.ends_with?(\".jpeg\") or post[:url].to_string.strip.ends_with?(\".png\") or post[:url].to_string.strip.include?(\"i.redd.it\"))",
70
- "safe = post.present?\n and !post[:over_18]\n and post[:post_hint] == :image\n and post[:url].to_string.strip.presence\n and (\n post[:url].to_string.strip.ends_with?(\".jpg\")\n or post[:url].to_string.strip.ends_with?(\".jpeg\")\n or post[:url].to_string.strip.ends_with?(\".png\")\n or post[:url].to_string.strip.include?(\"i.redd.it\")\n)"
71
- ],
72
- [
73
- "items.each { |item, index| proxied_image_url = if image_url proxy_url(image_url) else nothing end }",
74
- "items.each { |item, index|\n proxied_image_url = if image_url\n proxy_url(image_url)\n else\n nothing\n end\n}"
75
- ],
76
- [
77
- "lines << \"vacances scolaires france {zone} (prochains {months_ahead} mois) :\".downcase",
78
- "lines << (\n \"vacances scolaires france {zone} (prochains \"\n + \"{months_ahead} mois) :\"\n).downcase"
79
- ],
80
- [
81
- "src = \"https://proxy.dorianmarie.com?\" + \"{{ url: src, disposition: :inline }.to_query}\" if src",
82
- "src = (\n \"https://proxy.dorianmarie.com?\"\n + \"{{ url: src, disposition: :inline }.to_query}\"\n) if src"
83
- ],
84
- [
85
- "inline_image = image ? \"https://proxy.dorianmarie.com?\" + \"{inline_params.to_query}\" : nothing",
86
- "inline_image = image ? (\n \"https://proxy.dorianmarie.com?\"\n + \"{inline_params.to_query}\"\n) : nothing"
87
- ]
88
- ].each do |input, expected|
89
- it "formats #{input.inspect}" do
90
- expect(described_class.format(Code.parse(input))).to eq(expected)
91
- end
92
-
93
- it "formats #{input.inspect} idempotently" do
94
- formatted = described_class.format(Code.parse(input))
95
-
96
- expect(described_class.format(Code.parse(formatted))).to eq(formatted)
97
- end
98
- end
99
-
100
- it "round-trips parse and evaluation semantics for formatted code" do
101
- input = "user = {name: :Dorian, age: 31} user.age"
102
- formatted = described_class.format(Code.parse(input))
103
-
104
- expect(Code.parse(formatted)).to be_present
105
- expect(Code.evaluate(formatted)).to eq(Code.evaluate(input))
106
- end
107
-
108
- it "keeps grouped multiline receivers stable" do
109
- input = <<~CODE.chomp
110
- lines << (
111
- "vacances scolaires france {zone} (prochains "
112
- + "{months_ahead} mois) :"
113
- ).downcase
114
- CODE
115
-
116
- expect(described_class.format(Code.parse(input))).to eq(input)
117
- end
118
-
119
- it "does not split operators inside string interpolations when wrapping" do
120
- input =
121
- %q(body_text = "{title}\n\n{description}\n\ningrédients :\n" + ingredients.map { |ingredient| "- {ingredient}" }.join("\n") + "\n\ninstructions :\n" + instructions.map { |instruction, index| "{index + 1}. {instruction}" }.join("\n"))
122
-
123
- formatted = described_class.format(Code.parse(input))
124
-
125
- expect(formatted).to include(
126
- 'instructions.map { |instruction, index| "{index + 1}. {instruction}" }'
127
- )
128
- expect(formatted).not_to include(
129
- "\"{index\n + 1}. {instruction}\""
130
- )
131
- end
132
-
133
- it "does not emit whitespace-only blank lines" do
134
- input = <<~CODE.chomp
135
- body = Html.join(elements.map { |element| title = element.at_css(".x") value = element.at_css(".y") title }, Html.br)
136
- CODE
137
-
138
- formatted = described_class.format(Code.parse(input))
139
-
140
- expect(formatted.lines).not_to include(match(/\A[ \t]+\n\z/))
141
- end
142
-
143
- it "keeps lines within 80 characters" do
144
- input = <<~CODE.chomp
145
- src = "https://proxy.dorianmarie.com?" + "{{ url: src, disposition: :inline }.to_query}" if src
146
- CODE
147
-
148
- formatted = described_class.format(Code.parse(input))
149
-
150
- expect(formatted.lines.map { |line| line.chomp.length }.max).to be <= 80
151
- end
152
- end
153
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe "parser call" do
6
- ["f(end: 2)"].each do |input|
7
- it "parses #{input}" do
8
- Code::Parser.parse(input)
9
- end
10
- end
11
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Code::Object::Boolean do
6
- [
7
- ["true & false", "false"],
8
- %w[true.bitwise_and(false) false],
9
- ["true | false", "true"],
10
- %w[true.bitwise_or(false) true],
11
- ["true ^ false", "true"],
12
- %w[true.bitwise_xor(false) true]
13
- ].each do |input, expected|
14
- it "#{input} == #{expected}" do
15
- expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
16
- end
17
- end
18
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Code::Object::Cryptography do
6
- it "returns sha256 hexdigest by default" do
7
- expect(Code.evaluate("Cryptography.sha256(:hello)").to_s).to eq(
8
- Digest::SHA256.hexdigest("hello")
9
- )
10
- end
11
-
12
- it "supports base64 format for sha1" do
13
- expected = Base64.strict_encode64(Digest::SHA1.digest("hello"))
14
-
15
- expect(
16
- Code.evaluate("Cryptography.sha1(:hello, format: :base64)").to_s
17
- ).to eq(expected)
18
- end
19
-
20
- it "raises on unknown formats" do
21
- expect do
22
- Code.evaluate("Cryptography.sha256(:hello, format: :unknown)")
23
- end.to raise_error(Code::Error, /unknown format/i)
24
- end
25
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Code::Object::Decimal do
6
- [
7
- ["10.2 % 5.1", "0"],
8
- ["5.5 * 2.5", "13.75"],
9
- ["1.5 ** 2", "2.25"],
10
- ["1.5 + 1.5", "3"],
11
- ["1.5 - 0.5", "1"],
12
- %w[-1.5 -3/2],
13
- ["1.5 / 2", "0.75"],
14
- ["1.1 < 1.2", "true"],
15
- ["1.1 <= 1.1", "true"],
16
- ["1.1 <=> 1.2", "-1"],
17
- ["1.1 > 0.9", "true"],
18
- ["1.1 >= 1.1", "true"],
19
- ["1.1 === 1.1", "true"],
20
- %w[-1.1.abs 1.1],
21
- %w[-1.1.ceil -1],
22
- %w[-1.123.ceil(2) -1.12],
23
- %w[-1.1.floor -2],
24
- %w[-1.123.floor(2) -1.13],
25
- %w[1.1.round 1],
26
- %w[1.123.round(2) 1.12],
27
- %w[0.0.zero? true],
28
- %w[1.2.truncate 1],
29
- %w[1.234.truncate(2) 1.23],
30
- %w[1.2.to_string "1.2"],
31
- %w[1.2.to_integer 1],
32
- %w[1.2.to_decimal 1.2],
33
- %w[4.0.sqrt 2.0],
34
- %w[0.0.zero? true],
35
- %w[1.0.one? true],
36
- %w[2.0.two? true],
37
- %w[3.0.three? true],
38
- %w[4.0.four? true],
39
- %w[5.0.five? true],
40
- %w[6.0.six? true],
41
- %w[7.0.seven? true],
42
- %w[8.0.eight? true],
43
- %w[9.0.nine? true],
44
- %w[10.0.ten? true]
45
- ].each do |input, expected|
46
- it "#{input} == #{expected}" do
47
- expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
48
- end
49
- end
50
- end
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Code::Object::Dictionary do
6
- let(:dictionary) { 'dictionary = { first_name: "Dorian", language: :code }' }
7
-
8
- [
9
- [
10
- "dictionary.select { |key| key == :first_name }",
11
- "{ first_name: :Dorian }"
12
- ],
13
- [
14
- "dictionary.select { |_, value| value == :Dorian }",
15
- "{ first_name: :Dorian }"
16
- ],
17
- [
18
- "dictionary.select { |_, _, index, _| index.zero? }",
19
- "{ first_name: :Dorian }"
20
- ],
21
- [
22
- "dictionary.select { |_, _, _, dictionary| dictionary.two? }",
23
- "dictionary"
24
- ],
25
- [
26
- "dictionary.fetch(:first_name, :last_name) { :Marié }",
27
- "{ first_name: :Dorian, last_name: :Marié }"
28
- ],
29
- ["dictionary.delete(:last_name) { true }", "true"],
30
- [
31
- "dictionary.delete(:first_name, :language)",
32
- "{ first_name: :Dorian, language: :code }"
33
- ],
34
- %w[dictionary.any?(String) true],
35
- %w[dictionary.dig(:first_name) :Dorian],
36
- %w[dictionary.empty? false],
37
- %w[dictionary.falsy? false],
38
- %w[dictionary.fetch(:first_name) :Dorian],
39
- %w[dictionary.first_name :Dorian],
40
- %w[dictionary.has_key?(:first_name) true],
41
- %w[dictionary.has_key?(:unknown) false],
42
- %w[dictionary.has_value?(:Dorian) true],
43
- %w[dictionary.has_value?(:Unknown) false],
44
- %w[dictionary.key(:Dorian) :first_name],
45
- %w[dictionary.key(:Unknown) nothing],
46
- %w[dictionary.language :code],
47
- %w[dictionary.select!(Integer) {}],
48
- %w[dictionary.select(Integer) {}],
49
- %w[dictionary.select(String) dictionary],
50
- %w[dictionary.truthy? true],
51
- ["d = { a: nothing } d.compact! d", "{}"],
52
- ["dictionary <= dictionary", "true"],
53
- ["dictionary <=> dictionary", "0"],
54
- ["dictionary === dictionary", "true"],
55
- ["dictionary > { first_name: :Dorian", "true"],
56
- ["dictionary >= {}", "true"],
57
- ["dictionary.except(:first_name, :language)", "{}"],
58
- ["dictionary.fetch(:first_name, :language)", "dictionary"],
59
- ["dictionary.fetch(:unknown) { :Good }", ":Good"],
60
- ["dictionary.fetch_values(:first_name)", "[:Dorian]"],
61
- ["dictionary.fetch_values(:first_name, :language)", "[:Dorian, :code]"],
62
- ["dictionary.invert", "{ Dorian: :first_name, code: :language }"],
63
- ["dictionary.key(:Unknown) { true }", "true"],
64
- ["dictionary.select!(Integer) dictionary", "{}"],
65
- ["dictionary.select(Integer) dictionary", "dictionary"],
66
- ["dictionary[:first_name]", ":Dorian"],
67
- ["{ a: 1 }.merge(b: 2)", "{ a: 1, b: 2 }"],
68
- ["{ a: 1 }.merge({ a: 2 }) { |_, old, new| old + new }", "{ a: 3 }"],
69
- ["{ a: 1 }.merge({ a: 2 })", "{ a: 2 }"],
70
- ["{ a: 1 }.merge({ b: 2 })", "{ a: 1, b: 2 }"],
71
- ["{ a: 1, b: [2, 3] }.flatten", "[:a, 1, :b, 2, 3]"],
72
- ["{ a: 1, b: [2, 3] }.flatten(-1)", "[:a, 1, :b, 2, 3]"],
73
- ["{ a: 1, b: [2, 3] }.flatten(0)", "[[:a, 1], [:b, [2, 3]]]"],
74
- ["{ a: 1, b: [2, 3] }.flatten(1)", "[:a, 1, :b, [2, 3]]"],
75
- ["{ a: 1, b: [2, 3] }.to_list", "[[:a, 1], [:b, [2, 3]]]"],
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 }"],
79
- ["{ age: 31 }.delete_unless { |key| key == :age }", "{ age: 31 }"],
80
- ["{ age: 31 }.delete_unless(Integer)", "{ age: 31 }"],
81
- ["{ age: 31 }.keep_unless { |key| key == :age }", "{}"],
82
- ["{ age: 31 }.keep_unless(Integer)", "{}"],
83
- ["{ first_name: :Dorian } < dictionary", "true"],
84
- ["{ first_name: :Dorian }.any? { |_, value| value == :Dorian }", "true"],
85
- ["{ first_name: :Dorian }.delete_if { |_, value| value == :Dorian }", "{}"],
86
- ["{ first_name: :Dorian }.delete_if(String)", "{}"],
87
- ["{ first_name: :Dorian }.keep_if { |_, value| value != :Dorian }", "{}"],
88
- ["{ first_name: :Dorian }.keep_if(String)", "{ first_name: :Dorian }"],
89
- ["{} == {}", "true"],
90
- ['{ "name" => "Dorian" } == { name: :Dorian }', "true"]
91
- ].each do |input, expected|
92
- it "#{input} == #{expected}" do
93
- expect(Code.evaluate("#{dictionary} #{input}")).to eq(
94
- Code.evaluate("#{dictionary} #{expected}")
95
- )
96
- end
97
- end
98
- end