code-ruby 0.8.5 → 0.9.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: deb295fa0a68512686041fc2d29d7097a429a4e53d31e44f90028bccea2be8c4
4
- data.tar.gz: a8011e7b16501d6766b02f15139481dfec4fadc23201dd5e772340550460c9d6
3
+ metadata.gz: 1f92c4c4db84ee34d5e5d58325ab4de49d9346a738239aba44682db9e2530e7e
4
+ data.tar.gz: 6d056953ec6852d7824343f45198e0b35f13db881420fdce645753e091249ac7
5
5
  SHA512:
6
- metadata.gz: 1caa4cfa4ca44dfa4ff820acd8c7f49f8f9586bfefb1b0f43bd6e6eda0fd835d5d8f2bc91ec748dcbdd75a64553bf3cb75adc9059521cdbd64fc480b0f4ea226
7
- data.tar.gz: 20cb4e9b6089121edd2d9968726b00a76b9d2f14b7a9adc29de0b3c98711f7724e2d52e6824944b2675681e0c9d7d884eb17ff2a06984f9f6b9e76af1afbaf52
6
+ metadata.gz: 98ddcc59fe93cf2625cbc521a08b2119bcb8fc1bd8cd6fd3da6fb40039a73aa8835e35234c3e77842fbbcc6fa57fe51377e96f457bfd6e391a5bd96d37229296
7
+ data.tar.gz: 7b2723a4c912be64c3c7588fb905b40c6cd8fc510a61ca56a7af21037bd8d27d29a667659ea07a97d096d9a44d01dc6a5064f7a45ee58e39770d016a479353af
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (0.8.4)
4
+ code-ruby (0.9.0)
5
5
  activesupport (~> 7)
6
6
  bigdecimal (~> 3)
7
+ json (~> 2)
7
8
  language-ruby (~> 0)
8
9
  zeitwerk (~> 2)
9
10
 
@@ -29,6 +30,7 @@ GEM
29
30
  ruby2_keywords
30
31
  i18n (1.14.1)
31
32
  concurrent-ruby (~> 1.0)
33
+ json (2.7.1)
32
34
  language-ruby (0.6.2)
33
35
  zeitwerk (~> 2)
34
36
  minitest (5.22.2)
@@ -45,7 +47,7 @@ GEM
45
47
  rspec-mocks (3.13.0)
46
48
  diff-lcs (>= 1.2.0, < 2.0)
47
49
  rspec-support (~> 3.13.0)
48
- rspec-support (3.13.0)
50
+ rspec-support (3.13.1)
49
51
  ruby-prof (1.7.0)
50
52
  ruby2_keywords (0.0.5)
51
53
  tzinfo (2.0.6)
data/README.md CHANGED
@@ -1,7 +1,3 @@
1
1
  # code-ruby
2
2
 
3
3
  A programming language, like `Code.evaluate("1 + 1") # => 2`
4
-
5
- TODO
6
-
7
- - [ ] `code` command
data/code-ruby.gemspec CHANGED
@@ -10,14 +10,15 @@ Gem::Specification.new do |s|
10
10
  s.description = 'A programming language, like Code.evaluate("1 + 1") # => 2'
11
11
  s.authors = ["Dorian Marié"]
12
12
  s.email = "dorian@dorianmarie.fr"
13
- s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
13
+ s.files = `git ls-files`.lines.map(&:strip)
14
14
  s.require_paths = ["lib"]
15
15
  s.homepage = "https://github.com/dorianmariecom/code-ruby"
16
16
  s.license = "MIT"
17
17
  s.executables = "code"
18
18
 
19
+ s.add_dependency "activesupport", "~> 7"
19
20
  s.add_dependency "bigdecimal", "~> 3"
21
+ s.add_dependency "json", "~> 2"
20
22
  s.add_dependency "language-ruby", "~> 0"
21
- s.add_dependency "activesupport", "~> 7"
22
23
  s.add_dependency "zeitwerk", "~> 2"
23
24
  end
@@ -10,14 +10,6 @@ class Code
10
10
  @name = name
11
11
  end
12
12
 
13
- def self.name
14
- "Argument"
15
- end
16
-
17
- def inspect
18
- to_s
19
- end
20
-
21
13
  def keyword?
22
14
  !regular?
23
15
  end
@@ -29,10 +21,6 @@ class Code
29
21
  def regular?
30
22
  !name
31
23
  end
32
-
33
- def to_s
34
- "<Argument #{value.inspect}>"
35
- end
36
24
  end
37
25
  end
38
26
  end
@@ -60,6 +60,10 @@ class Code
60
60
  def truthy?
61
61
  raw
62
62
  end
63
+
64
+ def as_json(...)
65
+ raw.as_json(...)
66
+ end
63
67
  end
64
68
  end
65
69
  end
@@ -24,6 +24,10 @@ class Code
24
24
  def self.name
25
25
  "Class"
26
26
  end
27
+
28
+ def as_json(...)
29
+ raw.name.as_json(...)
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -37,6 +37,10 @@ class Code
37
37
  def to_s
38
38
  raw.to_s
39
39
  end
40
+
41
+ def as_json(...)
42
+ raw.as_json(...)
43
+ end
40
44
  end
41
45
  end
42
46
  end
@@ -309,6 +309,10 @@ class Code
309
309
  def to_s
310
310
  raw.to_s("F")
311
311
  end
312
+
313
+ def as_json(...)
314
+ raw.as_json(...)
315
+ end
312
316
  end
313
317
  end
314
318
  end
@@ -648,6 +648,10 @@ class Code
648
648
  def to_s
649
649
  "{#{raw.map { |key, value| "#{key.inspect} => #{value.inspect}" }.join(", ")}}"
650
650
  end
651
+
652
+ def as_json(...)
653
+ raw.as_json(...)
654
+ end
651
655
  end
652
656
  end
653
657
  end
@@ -43,6 +43,10 @@ class Code
43
43
  def to_s
44
44
  raw.to_s
45
45
  end
46
+
47
+ def as_json(...)
48
+ raw.as_json(...)
49
+ end
46
50
  end
47
51
  end
48
52
  end
@@ -87,6 +87,10 @@ class Code
87
87
  def to_s
88
88
  ""
89
89
  end
90
+
91
+ def as_json(...)
92
+ raw.as_json(...)
93
+ end
90
94
  end
91
95
  end
92
96
  end
@@ -91,10 +91,6 @@ class Code
91
91
  end
92
92
  end
93
93
  end
94
-
95
- def to_s
96
- "global"
97
- end
98
94
  end
99
95
  end
100
96
  end
@@ -6,15 +6,11 @@ class Code
6
6
  attr_reader :raw
7
7
 
8
8
  def initialize(whole, exponent: nil)
9
+ whole = whole.raw if whole.is_a?(Integer)
9
10
  @raw = whole.to_i
10
-
11
11
  return unless exponent
12
-
13
- unless exponent.is_a?(Number)
14
- raise Code::Error::TypeError, "exponent is not a number"
15
- end
16
-
17
- @raw *= 10**exponent.raw
12
+ exponent = exponent.raw if exponent.is_a?(Number)
13
+ @raw *= 10**exponent
18
14
  end
19
15
 
20
16
  def self.name
@@ -407,6 +403,10 @@ class Code
407
403
  def to_s
408
404
  raw.to_s
409
405
  end
406
+
407
+ def as_json(...)
408
+ raw.as_json(...)
409
+ end
410
410
  end
411
411
  end
412
412
  end
@@ -211,6 +211,10 @@ class Code
211
211
  def to_s
212
212
  "[#{raw.map(&:inspect).join(", ")}]"
213
213
  end
214
+
215
+ def as_json(...)
216
+ raw.as_json(...)
217
+ end
214
218
  end
215
219
  end
216
220
  end
@@ -24,6 +24,10 @@ class Code
24
24
  def truthy?
25
25
  false
26
26
  end
27
+
28
+ def as_json(...)
29
+ raw.as_json(...)
30
+ end
27
31
  end
28
32
  end
29
33
  end
@@ -138,6 +138,10 @@ class Code
138
138
  def to_s
139
139
  raw.to_s
140
140
  end
141
+
142
+ def as_json(...)
143
+ raw.as_json(...)
144
+ end
141
145
  end
142
146
  end
143
147
  end
@@ -100,6 +100,10 @@ class Code
100
100
  def to_s
101
101
  raw
102
102
  end
103
+
104
+ def as_json(...)
105
+ raw.as_json(...)
106
+ end
103
107
  end
104
108
  end
105
109
  end
@@ -77,6 +77,10 @@ class Code
77
77
  def to_s
78
78
  raw.to_s
79
79
  end
80
+
81
+ def as_json(...)
82
+ raw.as_json(...)
83
+ end
80
84
  end
81
85
  end
82
86
  end
data/lib/code/object.rb CHANGED
@@ -319,5 +319,13 @@ class Code
319
319
  def truthy?
320
320
  true
321
321
  end
322
+
323
+ def to_json(...)
324
+ as_json(...).to_json
325
+ end
326
+
327
+ def as_json(...)
328
+ raise NotImplementedError, "#{self.class}#as_json"
329
+ end
322
330
  end
323
331
  end
data/lib/code/version.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative "../code"
4
4
 
5
- Code::Version = Gem::Version.new("0.8.5")
5
+ Code::Version = Gem::Version.new("0.9.1")
data/lib/code-ruby.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support"
4
+ require "active_support/core_ext/date/conversions"
5
+ require "active_support/core_ext/numeric/time"
6
+ require "active_support/core_ext/object/json"
3
7
  require "bigdecimal"
8
+ require "json"
9
+ require "language-ruby"
4
10
  require "stringio"
5
11
  require "timeout"
6
12
  require "zeitwerk"
7
- require "language-ruby"
8
- require "active_support"
9
- require "active_support/core_ext/numeric/time"
10
- require "active_support/core_ext/date/conversions"
11
13
 
12
14
  loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
13
15
  loader.ignore("#{__dir__}/code-ruby.rb")
data/lib/code.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Code
4
- EMPTY_STRING = ""
5
4
  GLOBALS = %i[output error context object].freeze
6
5
  DEFAULT_TIMEOUT = 0
7
6
 
@@ -9,50 +8,35 @@ class Code
9
8
  input,
10
9
  output: StringIO.new,
11
10
  error: StringIO.new,
12
- timeout: DEFAULT_TIMEOUT,
13
- ruby: {}
11
+ timeout: DEFAULT_TIMEOUT
14
12
  )
15
13
  @input = input
16
- @parsed = Timeout.timeout(timeout) { ::Code::Parser.parse(input).to_raw }
17
14
  @output = output
18
15
  @error = error
19
16
  @timeout = timeout || DEFAULT_TIMEOUT
20
- @ruby = ::Code::Ruby.to_code(ruby || {}).code_to_context
17
+ @context = Object::Context.new
21
18
  end
22
19
 
23
20
  def self.evaluate(
24
21
  input,
25
- context = EMPTY_STRING,
26
22
  output: StringIO.new,
27
23
  error: StringIO.new,
28
- timeout: DEFAULT_TIMEOUT,
29
- ruby: {}
24
+ timeout: DEFAULT_TIMEOUT
30
25
  )
31
- new(input, output:, error:, timeout:, ruby:).evaluate(context)
26
+ new(input, output:, error:, timeout:).evaluate
32
27
  end
33
28
 
34
- def evaluate(context = EMPTY_STRING)
29
+ def evaluate
35
30
  Timeout.timeout(timeout) do
36
- context =
37
- if context == EMPTY_STRING
38
- Object::Context.new
39
- else
40
- Code.evaluate(
41
- context,
42
- timeout:,
43
- output:,
44
- error:,
45
- ruby:
46
- ).code_to_context
47
- end
48
-
49
- context = ruby.merge(context)
50
-
51
- Node::Code.new(parsed).evaluate(context:, output:, error:)
31
+ Node::Code.new(::Code::Parser.parse(input).to_raw).evaluate(
32
+ context:,
33
+ output:,
34
+ error:
35
+ )
52
36
  end
53
37
  end
54
38
 
55
39
  private
56
40
 
57
- attr_reader :input, :parsed, :timeout, :output, :error, :ruby
41
+ attr_reader :input, :timeout, :output, :error, :context
58
42
  end
@@ -3,95 +3,95 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe Code::Object::Dictionary do
6
- let(:context) { '{ first_name: "Dorian", language: :code }' }
6
+ let(:dictionary) { 'dictionary = { first_name: "Dorian", language: :code }' }
7
7
 
8
8
  [
9
- ["{} == {}", "true"],
10
- ['{ "name" => "Dorian" } == { name: :Dorian }', "true"],
11
- %w[context.first_name :Dorian],
12
- ["context[:first_name]", ":Dorian"],
13
- %w[context.language :code],
14
- ["context > { first_name: :Dorian", "true"],
15
- ["{ first_name: :Dorian } < context", "true"],
16
- ["context <= context", "true"],
17
- ["context >= {}", "true"],
18
- ["context <=> context", "0"],
19
- ["context === context", "true"],
20
- ["{ first_name: :Dorian }.any? { |_, value| value == :Dorian }", "true"],
21
- %w[context.any?(String) true],
22
- ["{ a: nothing }.compact", "{}"],
23
- ["d = { a: nothing } d.compact! d", "{}"],
24
- %w[context.dig(:first_name) :Dorian],
25
- %w[context.empty? false],
26
- %w[context.truthy? true],
27
- %w[context.falsy? false],
28
- %w[context.fetch(:first_name) :Dorian],
29
- ["context.fetch(:unknown) { :Good }", ":Good"],
30
- ["context.fetch(:first_name, :language)", "context"],
31
9
  [
32
- "context.fetch(:first_name, :last_name) { :Marié }",
33
- "{ first_name: :Dorian, last_name: :Marié }"
10
+ "dictionary.select { |key, value, dictionnary, index| key == :first_name }",
11
+ "{ first_name: :Dorian }"
34
12
  ],
35
- ["context.delete(:last_name) { true }", "true"],
36
13
  [
37
- "context.delete(:first_name, :language)",
38
- "{ first_name: :Dorian, language: :code }"
14
+ "dictionary.select { |key, value, dictionnary, index| value == :Dorian }",
15
+ "{ first_name: :Dorian }"
39
16
  ],
40
- ["{ first_name: :Dorian }.delete_if(String)", "{}"],
41
- ["{ age: 31 }.delete_unless(Number)", "{ age: 31 }"],
42
- ["{ age: 31 }.delete_unless(Integer)", "{ age: 31 }"],
43
- ["{ first_name: :Dorian }.delete_if { |_, value| value == :Dorian }", "{}"],
44
- ["{ age: 31 }.delete_unless { |key| key == :age }", "{ age: 31 }"],
45
- ["{ first_name: :Dorian }.keep_if(String)", "{ first_name: :Dorian }"],
46
- ["{ age: 31 }.keep_unless(Number)", "{}"],
47
- ["{ age: 31 }.keep_unless(Integer)", "{}"],
48
- ["{ first_name: :Dorian }.keep_if { |_, value| value != :Dorian }", "{}"],
49
- ["{ age: 31 }.keep_unless { |key| key == :age }", "{}"],
50
- ["context.except(:first_name, :language)", "{}"],
51
- ["context.fetch_values(:first_name, :language)", "[:Dorian, :code]"],
52
- ["context.fetch_values(:first_name)", "[:Dorian]"],
53
- %w[context.select(String) context],
54
- %w[context.select(Integer) {}],
55
- ["context.select(Integer) context", "context"],
56
- %w[context.select!(Integer) {}],
57
- ["context.select!(Integer) context", "{}"],
58
17
  [
59
- "context.select { |key, value, dictionnary, index| key == :first_name }",
60
- "{ first_name: :Dorian }"
18
+ "dictionary.select { |key, value, dictionnary, index| dictionnary.two? }",
19
+ "dictionary"
61
20
  ],
62
21
  [
63
- "context.select { |key, value, dictionnary, index| value == :Dorian }",
22
+ "dictionary.select { |key, value, dictionnary, index| index.zero? }",
64
23
  "{ first_name: :Dorian }"
65
24
  ],
66
25
  [
67
- "context.select { |key, value, dictionnary, index| dictionnary.two? }",
68
- "context"
26
+ "dictionary.fetch(:first_name, :last_name) { :Marié }",
27
+ "{ first_name: :Dorian, last_name: :Marié }"
69
28
  ],
29
+ ["dictionary.delete(:last_name) { true }", "true"],
70
30
  [
71
- "context.select { |key, value, dictionnary, index| index.zero? }",
72
- "{ first_name: :Dorian }"
31
+ "dictionary.delete(:first_name, :language)",
32
+ "{ first_name: :Dorian, language: :code }"
73
33
  ],
74
- ["{ a: 1, b: [2, 3] }.to_list", "[[:a, 1], [:b, [2, 3]]]"],
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 }"],
75
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]"],
76
73
  ["{ a: 1, b: [2, 3] }.flatten(0)", "[[:a, 1], [:b, [2, 3]]]"],
77
74
  ["{ a: 1, b: [2, 3] }.flatten(1)", "[:a, 1, :b, [2, 3]]"],
78
- ["{ a: 1, b: [2, 3] }.flatten(-1)", "[:a, 1, :b, 2, 3]"],
79
- %w[context.has_key?(:first_name) true],
80
- %w[context.has_key?(:unknown) false],
81
- %w[context.has_value?(:Dorian) true],
82
- %w[context.has_value?(:Unknown) false],
83
- ["context.invert", "{ Dorian: :first_name, code: :language }"],
84
- %w[context.key(:Dorian) :first_name],
85
- %w[context.key(:Unknown) nothing],
86
- ["context.key(:Unknown) { true }", "true"],
87
- ["{ a: 1 }.merge(b: 2)", "{ a: 1, b: 2 }"],
88
- ["{ a: 1 }.merge({ b: 2 })", "{ a: 1, b: 2 }"],
89
- ["{ a: 1 }.merge({ a: 2 })", "{ a: 2 }"],
90
- ["{ a: 1 }.merge({ a: 2 }) { |_, old, new| old + new }", "{ a: 3 }"]
75
+ ["{ a: 1, b: [2, 3] }.to_list", "[[:a, 1], [:b, [2, 3]]]"],
76
+ ["{ a: nothing }.compact", "{}"],
77
+ ["{ age: 31 }.delete_unless { |key| key == :age }", "{ age: 31 }"],
78
+ ["{ age: 31 }.delete_unless(Integer)", "{ age: 31 }"],
79
+ ["{ age: 31 }.delete_unless(Number)", "{ age: 31 }"],
80
+ ["{ age: 31 }.keep_unless { |key| key == :age }", "{}"],
81
+ ["{ age: 31 }.keep_unless(Integer)", "{}"],
82
+ ["{ age: 31 }.keep_unless(Number)", "{}"],
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
91
  ].each do |input, expected|
92
92
  it "#{input} == #{expected}" do
93
- expect(Code.evaluate(input, context)).to eq(
94
- Code.evaluate(expected, context)
93
+ expect(Code.evaluate("#{dictionary} #{input}")).to eq(
94
+ Code.evaluate("#{dictionary} #{expected}")
95
95
  )
96
96
  end
97
97
  end
@@ -2,12 +2,12 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe Code::Object::String do
4
4
  [
5
- [":a", "a"],
6
- [":a_b_c_0123", "a_b_c_0123"],
7
- ['"Hello"', "Hello"],
8
- ["'Hello'", "Hello"],
5
+ %w[:a a],
6
+ %w[:a_b_c_0123 a_b_c_0123],
7
+ %w["Hello" Hello],
8
+ %w['Hello' Hello],
9
9
  ['"Hello\nWorld"', "Hello\nWorld"],
10
- ["'Hello\\nWorld'", "Hello\nWorld"],
10
+ ["'Hello\\nWorld'", "Hello\nWorld"]
11
11
  ].each do |input, expected|
12
12
  it "(#{input}).to_s == #{expected.inspect}" do
13
13
  expect(Code.evaluate(input).to_s).to eq(expected)
data/spec/code_spec.rb CHANGED
@@ -57,14 +57,14 @@ RSpec.describe Code do
57
57
  ["1 >> 1", "0"],
58
58
  ["1 ^ 2", "3"],
59
59
  ["1 | 2", "3"],
60
- ["1.day.ago.after?(1.day.from_now)", 'false'],
61
- ["1.day.ago.before?(1.day.from_now)", 'true'],
62
- ["1.day.from_now.after?(1.day.ago)", 'true'],
63
- ["1.day.from_now.before?(1.day.ago)", 'false'],
64
- ["1.day.ago.after?", 'false'],
65
- ["1.day.ago.before?", 'true'],
66
- ["1.day.from_now.after?", "true"],
67
- ["1.day.from_now.before?", "false"],
60
+ %w[1.day.ago.after?(1.day.from_now) false],
61
+ %w[1.day.ago.before?(1.day.from_now) true],
62
+ %w[1.day.from_now.after?(1.day.ago) true],
63
+ %w[1.day.from_now.before?(1.day.ago) false],
64
+ %w[1.day.ago.after? false],
65
+ %w[1.day.ago.before? true],
66
+ %w[1.day.from_now.after? true],
67
+ %w[1.day.from_now.before? false],
68
68
  ["2 * 3 + 2", "8"],
69
69
  ["2 * 3", "6"],
70
70
  ["2 ** 3 ** 2", "512"],
@@ -141,13 +141,30 @@ RSpec.describe Code do
141
141
  ['"Hello {1}"', '"Hello 1"'],
142
142
  ['user = {} user.name = "Dorian" user.name', ":Dorian"],
143
143
  ['user = {} user[:name] = "Dorian" user[:name]', ":Dorian"],
144
- ['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}'],
144
+ ['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}']
145
145
  ].each do |input, expected|
146
146
  it "#{input} == #{expected}" do
147
147
  expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
148
148
  end
149
+
150
+ it "#{input} converts to json like #{expected}" do
151
+ expect(Code.evaluate(input).to_json).to eq(Code.evaluate(expected).to_json)
152
+ end
149
153
  end
150
154
 
155
+ %w[
156
+ 1.day.ago
157
+ 1.day.from_now
158
+ 1.hour.ago
159
+ 1.hour.from_now
160
+ 2.days.ago
161
+ 2.days.from_now
162
+ 2.hours.ago
163
+ 2.hours.from_now
164
+ Date.tomorrow
165
+ Time.tomorrow
166
+ ].each { |input| it(input) { Code.evaluate(input) } }
167
+
151
168
  [["puts(true)", "true\n"], %w[print(false) false]].each do |input, expected|
152
169
  it "#{input} prints #{expected}" do
153
170
  output = StringIO.new
@@ -155,62 +172,4 @@ RSpec.describe Code do
155
172
  expect(output.string).to eq(expected)
156
173
  end
157
174
  end
158
-
159
- it "converts nil" do
160
- ruby = Code::Ruby.from_code(Code.evaluate("a", ruby: { a: nil }))
161
-
162
- expect(ruby).to eq(nil)
163
- end
164
-
165
- it "works with downcase" do
166
- expect(Code.evaluate("downcase", "{ downcase: 1 }")).to eq(
167
- Code.evaluate("1")
168
- )
169
- end
170
-
171
- describe "#fetch" do
172
- it "returns the value when present" do
173
- expect(Code.evaluate("fetch(:downcase)", "{ downcase: 1 }")).to eq(
174
- Code.evaluate("1")
175
- )
176
- end
177
-
178
- it "returns the default value when not present" do
179
- expect(Code.evaluate("fetch(:downcase) { 2 }")).to eq(
180
- Code.evaluate("2")
181
- )
182
- end
183
- end
184
-
185
- it "works with nested objects" do
186
- expect(
187
- Code.evaluate("items.first.title", ruby: { items: [{ title: "Hello" }] })
188
- ).to eq(Code.evaluate(":Hello"))
189
- end
190
-
191
- it "works with arrays" do
192
- expect(
193
- Code.evaluate(
194
- "items.map { |item| item.title }",
195
- ruby: {
196
- items: [{ title: "Hello" }]
197
- }
198
- )
199
- ).to eq(Code.evaluate("[:Hello]"))
200
- end
201
-
202
- [
203
- "1.day.ago",
204
- "1.day.from_now",
205
- "1.hour.ago",
206
- "1.hour.from_now",
207
- "2.days.ago",
208
- "2.days.from_now",
209
- "2.hours.ago",
210
- "2.hours.from_now",
211
- "Date.tomorrow",
212
- "Time.tomorrow",
213
- ].each do |input|
214
- it(input) { Code.evaluate(input) }
215
- end
216
175
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.5
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-26 00:00:00.000000000 Z
11
+ date: 2024-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '7'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bigdecimal
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -25,33 +39,33 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '3'
27
41
  - !ruby/object:Gem::Dependency
28
- name: language-ruby
42
+ name: json
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '0'
47
+ version: '2'
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '0'
54
+ version: '2'
41
55
  - !ruby/object:Gem::Dependency
42
- name: activesupport
56
+ name: language-ruby
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '7'
61
+ version: '0'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '7'
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: zeitwerk
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -130,7 +144,6 @@ files:
130
144
  - lib/code/object/nothing.rb
131
145
  - lib/code/object/number.rb
132
146
  - lib/code/object/range.rb
133
- - lib/code/object/ruby_function.rb
134
147
  - lib/code/object/string.rb
135
148
  - lib/code/object/time.rb
136
149
  - lib/code/parser.rb
@@ -174,7 +187,6 @@ files:
174
187
  - lib/code/parser/unary_minus.rb
175
188
  - lib/code/parser/while.rb
176
189
  - lib/code/parser/whitespace.rb
177
- - lib/code/ruby.rb
178
190
  - lib/code/type.rb
179
191
  - lib/code/type/hash.rb
180
192
  - lib/code/type/maybe.rb
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Code
4
- class Object
5
- class RubyFunction < Function
6
- attr_reader :raw
7
-
8
- def initialize(raw)
9
- @raw = raw
10
- end
11
-
12
- def self.name
13
- "RubyFunction"
14
- end
15
-
16
- # TODO: fix / refactor
17
- def code_call(args:, globals:)
18
- regular_arguments =
19
- args
20
- .select(&:regular?)
21
- .map(&:value)
22
- .map { |argument| Ruby.from_code(argument) }
23
-
24
- keyword_arguments =
25
- args
26
- .select(&:keyword?)
27
- .map do |argument|
28
- [argument.name.to_sym, Ruby.from_code(argument.value)]
29
- end
30
- .to_h
31
-
32
- Ruby.to_code(raw.call(*regular_arguments, **keyword_arguments))
33
- end
34
- end
35
- end
36
- end
data/lib/code/ruby.rb DELETED
@@ -1,161 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Code
4
- class Ruby
5
- def initialize(raw = {})
6
- @raw = raw
7
- end
8
-
9
- def self.to_code(raw)
10
- new(raw).to_code
11
- end
12
-
13
- def self.from_code(raw)
14
- new(raw).from_code
15
- end
16
-
17
- def to_code
18
- if code?
19
- raw
20
- elsif nil?
21
- ::Code::Object::Nothing.new
22
- elsif true?
23
- ::Code::Object::Boolean.new(raw)
24
- elsif false?
25
- ::Code::Object::Boolean.new(raw)
26
- elsif string?
27
- ::Code::Object::String.new(raw)
28
- elsif symbol?
29
- ::Code::Object::String.new(raw.to_s)
30
- elsif integer?
31
- ::Code::Object::Integer.new(raw)
32
- elsif float?
33
- ::Code::Object::Decimal.new(raw.to_s)
34
- elsif big_decimal?
35
- ::Code::Object::Decimal.new(raw)
36
- elsif hash?
37
- ::Code::Object::Dictionary.new(
38
- raw
39
- .map do |key, value|
40
- [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
41
- end
42
- .to_h
43
- )
44
- elsif array?
45
- ::Code::Object::List.new(
46
- raw.map { |element| ::Code::Ruby.to_code(element) }
47
- )
48
- elsif proc?
49
- ::Code::Object::RubyFunction.new(raw)
50
- else
51
- raise "Unsupported class #{raw.class} for Ruby to Code conversion"
52
- end
53
- end
54
-
55
- def from_code
56
- if code?
57
- if code_nothing? || code_boolean? || code_decimal? || code_integer? ||
58
- code_range? || code_string?
59
- raw.raw
60
- elsif code_dictionnary?
61
- raw
62
- .raw
63
- .map do |key, value|
64
- [::Code::Ruby.from_code(key), ::Code::Ruby.from_code(value)]
65
- end
66
- .to_h
67
- elsif code_list?
68
- raw.raw.map { |element| ::Code::Ruby.from_code(element) }
69
- else
70
- raise "Unsupported class #{raw.class} for Code to Ruby conversion"
71
- end
72
- else
73
- raw
74
- end
75
- end
76
-
77
- private
78
-
79
- attr_reader :raw
80
-
81
- def code?
82
- raw.is_a?(::Code::Object)
83
- end
84
-
85
- def nil?
86
- raw.is_a?(::NilClass)
87
- end
88
-
89
- def true?
90
- raw.is_a?(::TrueClass)
91
- end
92
-
93
- def false?
94
- raw.is_a?(::FalseClass)
95
- end
96
-
97
- def hash?
98
- raw.is_a?(::Hash)
99
- end
100
-
101
- def array?
102
- raw.is_a?(::Array)
103
- end
104
-
105
- def string?
106
- raw.is_a?(::String)
107
- end
108
-
109
- def symbol?
110
- raw.is_a?(::Symbol)
111
- end
112
-
113
- def integer?
114
- raw.is_a?(::Integer)
115
- end
116
-
117
- def float?
118
- raw.is_a?(::Float)
119
- end
120
-
121
- def big_decimal?
122
- raw.is_a?(::BigDecimal)
123
- end
124
-
125
- def proc?
126
- raw.is_a?(::Proc)
127
- end
128
-
129
- def code_nothing?
130
- raw.is_a?(::Code::Object::Nothing)
131
- end
132
-
133
- def code_boolean?
134
- raw.is_a?(::Code::Object::Boolean)
135
- end
136
-
137
- def code_decimal?
138
- raw.is_a?(::Code::Object::Decimal)
139
- end
140
-
141
- def code_integer?
142
- raw.is_a?(::Code::Object::Integer)
143
- end
144
-
145
- def code_range?
146
- raw.is_a?(::Code::Object::Range)
147
- end
148
-
149
- def code_string?
150
- raw.is_a?(::Code::Object::String)
151
- end
152
-
153
- def code_dictionnary?
154
- raw.is_a?(::Code::Object::Dictionary)
155
- end
156
-
157
- def code_list?
158
- raw.is_a?(::Code::Object::List)
159
- end
160
- end
161
- end