code-ruby 3.0.1 → 3.0.3
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 +4 -4
- data/Gemfile.lock +8 -1
- data/VERSION +1 -1
- data/code-ruby.gemspec +1 -0
- data/lib/code/object/global.rb +3 -0
- data/lib/code/object/ics.rb +93 -0
- data/lib/code/parser.rb +10 -1
- data/lib/code-ruby.rb +1 -0
- data/spec/code_spec.rb +49 -0
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4adfeb34ead1c64815b7c1441fe958bdb800124060f85fe34e3e069d84471bf9
|
|
4
|
+
data.tar.gz: 47d16a2b78dfa78f09c3a85ceb96bec36462e5370c46c6da5bfa6cbeccb16704
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3282f6775597df25231b0aef32f5d0a8a11d7cee0f33abdb0989c843fff87a16f908a01998c1269d81bebe507c5023cf56095a8037624be68066a6e82c15443
|
|
7
|
+
data.tar.gz: 49e847ebb8e89dad0b072b25e3116718d3cbb3de2c4b0252cb8e2d7ae0462e93992554dadbf1665f91cb821fd9fe05f0a1769b3a06ab8d1632d4d1a268b828bb
|
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
code-ruby (3.0.
|
|
4
|
+
code-ruby (3.0.3)
|
|
5
5
|
activesupport
|
|
6
6
|
base64
|
|
7
7
|
bigdecimal
|
|
8
8
|
did-you-mean
|
|
9
9
|
dorian-arguments
|
|
10
|
+
icalendar
|
|
10
11
|
json
|
|
11
12
|
language-ruby
|
|
12
13
|
mail
|
|
@@ -95,6 +96,12 @@ GEM
|
|
|
95
96
|
strscan (>= 3.1.2)
|
|
96
97
|
i18n (1.14.8)
|
|
97
98
|
concurrent-ruby (~> 1.0)
|
|
99
|
+
icalendar (2.12.2)
|
|
100
|
+
base64
|
|
101
|
+
ice_cube (~> 0.16)
|
|
102
|
+
logger
|
|
103
|
+
ostruct
|
|
104
|
+
ice_cube (0.17.0)
|
|
98
105
|
json (2.18.1)
|
|
99
106
|
json-schema (6.1.0)
|
|
100
107
|
addressable (~> 2.8)
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0.
|
|
1
|
+
3.0.3
|
data/code-ruby.gemspec
CHANGED
data/lib/code/object/global.rb
CHANGED
|
@@ -167,6 +167,9 @@ class Code
|
|
|
167
167
|
when "Json"
|
|
168
168
|
sig(args) { Object.repeat }
|
|
169
169
|
code_arguments.any? ? Json.new(*code_arguments.raw) : Class.new(Json)
|
|
170
|
+
when "Ics"
|
|
171
|
+
sig(args) { Object.repeat }
|
|
172
|
+
code_arguments.any? ? Ics.new(*code_arguments.raw) : Class.new(Ics)
|
|
170
173
|
when "evaluate"
|
|
171
174
|
sig(args) { Object }
|
|
172
175
|
Code.code_evaluate(code_value.code_to_string, **globals)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Code
|
|
4
|
+
class Object
|
|
5
|
+
class Ics < Object
|
|
6
|
+
EVENT_ATTRIBUTES = %i[
|
|
7
|
+
uid
|
|
8
|
+
summary
|
|
9
|
+
description
|
|
10
|
+
location
|
|
11
|
+
url
|
|
12
|
+
status
|
|
13
|
+
organizer
|
|
14
|
+
categories
|
|
15
|
+
attendees
|
|
16
|
+
geo
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
def self.call(**args)
|
|
20
|
+
code_operator = args.fetch(:operator, nil).to_code
|
|
21
|
+
code_arguments = args.fetch(:arguments, []).to_code
|
|
22
|
+
code_value = code_arguments.code_first
|
|
23
|
+
|
|
24
|
+
case code_operator.to_s
|
|
25
|
+
when "parse"
|
|
26
|
+
sig(args) { String }
|
|
27
|
+
code_parse(code_value)
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.code_parse(value)
|
|
34
|
+
source = value.to_code.raw
|
|
35
|
+
calendars = ::Icalendar::Calendar.parse(source)
|
|
36
|
+
calendars
|
|
37
|
+
.flat_map(&:events)
|
|
38
|
+
.map { |event| serialize_event(event) }
|
|
39
|
+
.to_code
|
|
40
|
+
rescue StandardError
|
|
41
|
+
[].to_code
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.serialize_event(event)
|
|
45
|
+
EVENT_ATTRIBUTES.each_with_object({}) do |attribute, result|
|
|
46
|
+
next unless event.respond_to?(attribute)
|
|
47
|
+
|
|
48
|
+
serialized = serialize_value(event.public_send(attribute))
|
|
49
|
+
serialized = serialized.flatten(1) if attribute == :categories && serialized.is_a?(::Array)
|
|
50
|
+
result[attribute] = serialized unless serialized.nil?
|
|
51
|
+
end.merge(
|
|
52
|
+
timestamp: serialize_value(event.dtstamp),
|
|
53
|
+
starts_at: serialize_value(event.dtstart),
|
|
54
|
+
ends_at: serialize_value(event.dtend),
|
|
55
|
+
all_day: !!serialize_date_like(event.dtstart).is_a?(::Date)
|
|
56
|
+
).compact
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.serialize_value(value)
|
|
60
|
+
case value
|
|
61
|
+
when nil
|
|
62
|
+
nil
|
|
63
|
+
when ::String, ::Symbol, ::Integer, ::Float, ::BigDecimal, true, false
|
|
64
|
+
value
|
|
65
|
+
when ::Array
|
|
66
|
+
value.map { |item| serialize_value(item) }
|
|
67
|
+
when ::Hash
|
|
68
|
+
value.transform_values { |item| serialize_value(item) }
|
|
69
|
+
else
|
|
70
|
+
serialized_date = serialize_date_like(value)
|
|
71
|
+
return serialized_date unless serialized_date.nil?
|
|
72
|
+
|
|
73
|
+
if value.respond_to?(:value)
|
|
74
|
+
serialize_value(value.value)
|
|
75
|
+
elsif value.respond_to?(:to_ical)
|
|
76
|
+
value.to_ical
|
|
77
|
+
else
|
|
78
|
+
value.to_s
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.serialize_date_like(value)
|
|
84
|
+
case value
|
|
85
|
+
when ::Time, ::Date, ::ActiveSupport::TimeWithZone
|
|
86
|
+
value
|
|
87
|
+
when ::DateTime
|
|
88
|
+
value.to_time
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/code/parser.rb
CHANGED
|
@@ -64,6 +64,8 @@ class Code
|
|
|
64
64
|
=>
|
|
65
65
|
].sort_by(&:length).reverse.freeze
|
|
66
66
|
|
|
67
|
+
ASSIGNMENT_RHS_MIN_BP = 20
|
|
68
|
+
|
|
67
69
|
INFIX_PRECEDENCE = {
|
|
68
70
|
"if" => [10, 9],
|
|
69
71
|
"unless" => [10, 9],
|
|
@@ -293,7 +295,13 @@ class Code
|
|
|
293
295
|
when "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=",
|
|
294
296
|
"^=", "||=", "&&="
|
|
295
297
|
skip_newlines
|
|
296
|
-
{
|
|
298
|
+
{
|
|
299
|
+
right_operation: {
|
|
300
|
+
left: left,
|
|
301
|
+
operator: operator,
|
|
302
|
+
right: parse_expression(ASSIGNMENT_RHS_MIN_BP)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
297
305
|
when "if", "unless", "while", "until", "rescue"
|
|
298
306
|
skip_newlines
|
|
299
307
|
{ right_operation: { left: left, operator: operator, right: parse_expression(right_bp) } }
|
|
@@ -816,6 +824,7 @@ class Code
|
|
|
816
824
|
def continuation_after_newline?(token)
|
|
817
825
|
return false if token.type == :eof
|
|
818
826
|
return true if token.type == :operator && INFIX_PRECEDENCE.key?(token.value)
|
|
827
|
+
return true if token.type == :keyword && %w[or and rescue].include?(token.value)
|
|
819
828
|
return true if token.type == :punctuation && token.value == "?"
|
|
820
829
|
|
|
821
830
|
token.type == :operator && [".", "::", "&."].include?(token.value)
|
data/lib/code-ruby.rb
CHANGED
data/spec/code_spec.rb
CHANGED
|
@@ -151,6 +151,7 @@ RSpec.describe Code do
|
|
|
151
151
|
Json.parse('[]')
|
|
152
152
|
Json.parse('{}')
|
|
153
153
|
Json.parse('random-string')
|
|
154
|
+
Ics.parse("BEGIN:VCALENDAR\nEND:VCALENDAR")
|
|
154
155
|
{}["".to_string]
|
|
155
156
|
] + ["Time.hour >= 6 and Time.hour <= 23"]
|
|
156
157
|
).each do |input|
|
|
@@ -401,6 +402,7 @@ RSpec.describe Code do
|
|
|
401
402
|
["true ? 1", "1"],
|
|
402
403
|
["true and false", "false"],
|
|
403
404
|
["true or false", "true"],
|
|
405
|
+
["weekday? = false\n or true\nweekday?", "true"],
|
|
404
406
|
["true || false", "true"],
|
|
405
407
|
["unless false 1", "1"],
|
|
406
408
|
["unless true 1", "nothing"],
|
|
@@ -556,4 +558,51 @@ RSpec.describe Code do
|
|
|
556
558
|
described_class.evaluate("UnknownConstant.zone = 1")
|
|
557
559
|
end.to raise_error(Code::Error, /UnknownConstant is not defined/)
|
|
558
560
|
end
|
|
561
|
+
|
|
562
|
+
it "parses ics events into dictionaries" do
|
|
563
|
+
result = described_class.evaluate(<<~CODE)
|
|
564
|
+
events = Ics.parse("BEGIN:VCALENDAR
|
|
565
|
+
VERSION:2.0
|
|
566
|
+
PRODID:-//code-ruby//EN
|
|
567
|
+
BEGIN:VEVENT
|
|
568
|
+
UID:event-1
|
|
569
|
+
DTSTART:20260327T120000Z
|
|
570
|
+
DTEND:20260327T133000Z
|
|
571
|
+
SUMMARY:Lunch
|
|
572
|
+
DESCRIPTION:Team lunch
|
|
573
|
+
LOCATION:Paris
|
|
574
|
+
URL:https://example.com/events/1
|
|
575
|
+
CATEGORIES:food,team
|
|
576
|
+
END:VEVENT
|
|
577
|
+
END:VCALENDAR")
|
|
578
|
+
|
|
579
|
+
{
|
|
580
|
+
size: events.size,
|
|
581
|
+
summary: events.first.summary,
|
|
582
|
+
location: events.first.location,
|
|
583
|
+
uid: events.first.uid,
|
|
584
|
+
categories: events.first.categories,
|
|
585
|
+
starts_at: events.first.starts_at.to_string,
|
|
586
|
+
ends_at: events.first.ends_at.to_string,
|
|
587
|
+
all_day: events.first.all_day
|
|
588
|
+
}
|
|
589
|
+
CODE
|
|
590
|
+
|
|
591
|
+
expect(result).to eq(
|
|
592
|
+
{
|
|
593
|
+
size: 1,
|
|
594
|
+
summary: "Lunch",
|
|
595
|
+
location: "Paris",
|
|
596
|
+
uid: "event-1",
|
|
597
|
+
categories: ["food", "team"],
|
|
598
|
+
starts_at: "2026-03-27 12:00:00 UTC",
|
|
599
|
+
ends_at: "2026-03-27 13:30:00 UTC",
|
|
600
|
+
all_day: false
|
|
601
|
+
}.to_code
|
|
602
|
+
)
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
it "returns an empty list for invalid ics" do
|
|
606
|
+
expect(described_class.evaluate('Ics.parse("not an ics file")')).to eq([].to_code)
|
|
607
|
+
end
|
|
559
608
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: code-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Marié
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: icalendar
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
82
96
|
- !ruby/object:Gem::Dependency
|
|
83
97
|
name: json
|
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -275,6 +289,7 @@ files:
|
|
|
275
289
|
- lib/code/object/global.rb
|
|
276
290
|
- lib/code/object/html.rb
|
|
277
291
|
- lib/code/object/http.rb
|
|
292
|
+
- lib/code/object/ics.rb
|
|
278
293
|
- lib/code/object/identifier_list.rb
|
|
279
294
|
- lib/code/object/integer.rb
|
|
280
295
|
- lib/code/object/json.rb
|