code-ruby 0.8.5 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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