code-ruby 3.0.3 → 3.0.5

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: 4adfeb34ead1c64815b7c1441fe958bdb800124060f85fe34e3e069d84471bf9
4
- data.tar.gz: 47d16a2b78dfa78f09c3a85ceb96bec36462e5370c46c6da5bfa6cbeccb16704
3
+ metadata.gz: 71c8d3a9deab8aee738543c6fb7a3b06732c5ecd0fbd042ec41c7e3d3a351bf1
4
+ data.tar.gz: e828824ea1a50b4fa0cdb071aeee9b0042933dfad6b80c90ee91df7010946fd1
5
5
  SHA512:
6
- metadata.gz: f3282f6775597df25231b0aef32f5d0a8a11d7cee0f33abdb0989c843fff87a16f908a01998c1269d81bebe507c5023cf56095a8037624be68066a6e82c15443
7
- data.tar.gz: 49e847ebb8e89dad0b072b25e3116718d3cbb3de2c4b0252cb8e2d7ae0462e93992554dadbf1665f91cb821fd9fe05f0a1769b3a06ab8d1632d4d1a268b828bb
6
+ metadata.gz: 75f6791a43a543361becdde69f55c47134dc2386ef6e018df1fed31b261f748afb1a16233cccc1d1512f95858866654954250b2d48570510e3910e71efdc38f7
7
+ data.tar.gz: '041049251701b05e2ea7f3c95ed48173b4cdc1fc76dfef311e5109f6c3c25de51fe9c0a604261915759617dc1684a59d01defffc5578e9eb57d65bb9c1658201'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (3.0.3)
4
+ code-ruby (3.0.5)
5
5
  activesupport
6
6
  base64
7
7
  bigdecimal
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.3
1
+ 3.0.5
data/lib/code/format.rb CHANGED
@@ -413,10 +413,22 @@ class Code
413
413
  else
414
414
  candidate = "#{expression} #{operator} #{right}"
415
415
  if expression.include?("\n") || candidate.length > MAX_LINE_LENGTH
416
- right_parts = right.split(" #{operator} ")
416
+ right_lines =
417
+ if right.include?("\n")
418
+ right.lines(chomp: true).map(&:lstrip)
419
+ else
420
+ right.split(" #{operator} ")
421
+ end
417
422
  continuation_lines =
418
- right_parts.map do |part|
419
- "#{INDENT * (indent + 1)}#{operator} #{part}"
423
+ right_lines.each_with_index.map do |line, index|
424
+ content = line.lstrip
425
+ prefix =
426
+ if content.start_with?("#{operator} ")
427
+ ""
428
+ else
429
+ "#{operator} "
430
+ end
431
+ "#{INDENT * (indent + 1)}#{prefix}#{content}"
420
432
  end
421
433
  "#{expression}\n#{continuation_lines.join("\n")}"
422
434
  else
@@ -46,7 +46,14 @@ class Code
46
46
  next unless event.respond_to?(attribute)
47
47
 
48
48
  serialized = serialize_value(event.public_send(attribute))
49
- serialized = serialized.flatten(1) if attribute == :categories && serialized.is_a?(::Array)
49
+ serialized =
50
+ if attribute == :categories && serialized.is_a?(::Array)
51
+ serialized.flatten(1)
52
+ elsif scalar_event_attribute?(attribute) && serialized.is_a?(::Array)
53
+ serialized.join(",")
54
+ else
55
+ serialized
56
+ end
50
57
  result[attribute] = serialized unless serialized.nil?
51
58
  end.merge(
52
59
  timestamp: serialize_value(event.dtstamp),
@@ -60,7 +67,9 @@ class Code
60
67
  case value
61
68
  when nil
62
69
  nil
63
- when ::String, ::Symbol, ::Integer, ::Float, ::BigDecimal, true, false
70
+ when ::String
71
+ normalize_string(value)
72
+ when ::Symbol, ::Integer, ::Float, ::BigDecimal, true, false
64
73
  value
65
74
  when ::Array
66
75
  value.map { |item| serialize_value(item) }
@@ -73,13 +82,24 @@ class Code
73
82
  if value.respond_to?(:value)
74
83
  serialize_value(value.value)
75
84
  elsif value.respond_to?(:to_ical)
76
- value.to_ical
85
+ normalize_string(value.to_ical)
77
86
  else
78
- value.to_s
87
+ normalize_string(value.to_s)
79
88
  end
80
89
  end
81
90
  end
82
91
 
92
+ def self.scalar_event_attribute?(attribute)
93
+ !%i[categories attendees geo].include?(attribute)
94
+ end
95
+
96
+ def self.normalize_string(value)
97
+ value
98
+ .dup
99
+ .force_encoding(::Encoding::UTF_8)
100
+ .encode(::Encoding::UTF_8, invalid: :replace, undef: :replace)
101
+ end
102
+
83
103
  def self.serialize_date_like(value)
84
104
  case value
85
105
  when ::Time, ::Date, ::ActiveSupport::TimeWithZone
@@ -74,7 +74,7 @@ class Code
74
74
  sig(args)
75
75
  code_strip
76
76
  when "split"
77
- sig(args) { String }
77
+ sig(args) { String.maybe }
78
78
  code_split(code_value)
79
79
  else
80
80
  super
@@ -162,7 +162,11 @@ class Code
162
162
  def code_split(value)
163
163
  code_value = value.to_code
164
164
 
165
- List.new(raw.split(code_value.to_s))
165
+ if code_value.nothing?
166
+ List.new(raw.split)
167
+ else
168
+ List.new(raw.split(code_value.to_s))
169
+ end
166
170
  end
167
171
 
168
172
  def present?
data/lib/code/parser.rb CHANGED
@@ -350,11 +350,10 @@ class Code
350
350
  skip_newlines
351
351
 
352
352
  if match?(:keyword, "if") || match?(:keyword, "unless")
353
- else_operator = advance.value
354
- skip_newlines
355
- else_statement = parse_expression
356
- else_body = parse_body(%w[elsif elsunless else end])
357
- elses << { operator: else_operator, statement: else_statement, body: else_body }
353
+ elses << {
354
+ operator: "else",
355
+ body: [parse_if_expression(advance.value)]
356
+ }
358
357
  else
359
358
  elses << { operator: "else", body: parse_body(%w[end]) }
360
359
  end
@@ -365,6 +364,7 @@ class Code
365
364
  break
366
365
  end
367
366
 
367
+ skip_newlines
368
368
  advance if match?(:keyword, "end")
369
369
 
370
370
  {
@@ -382,6 +382,7 @@ class Code
382
382
 
383
383
  statement = parse_expression unless operator == "loop"
384
384
  body = parse_body(%w[end])
385
+ skip_newlines
385
386
  advance if match?(:keyword, "end")
386
387
 
387
388
  {
@@ -414,7 +415,10 @@ class Code
414
415
  parse_delimited_code("{", "}")
415
416
  elsif match?(:keyword, "do") || match?(:keyword, "begin")
416
417
  advance
417
- parse_code(stop_keywords: ["end"]).tap { advance if match?(:keyword, "end") }
418
+ parse_code(stop_keywords: ["end"]).tap do
419
+ skip_newlines
420
+ advance if match?(:keyword, "end")
421
+ end
418
422
  else
419
423
  [parse_expression]
420
424
  end
@@ -548,6 +552,7 @@ class Code
548
552
  expect(:keyword, "do")
549
553
  parameters = parse_pipe_parameters
550
554
  body = parse_code(stop_keywords: ["end"])
555
+ skip_newlines
551
556
  expect(:keyword, "end")
552
557
  end
553
558
 
@@ -23,6 +23,14 @@ RSpec.describe Code::Format do
23
23
  "if true 1 elsif false 2 else 3 end",
24
24
  "if true\n 1\nelsif false\n 2\nelse\n 3\nend"
25
25
  ],
26
+ [
27
+ "if false 1 else if true 2 else 3 end",
28
+ "if false\n 1\nelse\n if true\n 2\n else\n 3\n end\nend"
29
+ ],
30
+ [
31
+ "event[:uid].present? and event[:summary].present? and event[:starts_at].present? and event[:ends_at].present?",
32
+ "event[:uid].present?\n and event[:summary].present?\n and event[:starts_at].present?\n and event[:ends_at].present?"
33
+ ],
26
34
  [
27
35
  "sum = (a, b: 2) => { a + b } sum(1)",
28
36
  "sum = (a, b: 2) => {\n a + b\n}\n\nsum(1)"
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Code::Object::Ics do
6
+ it "normalizes binary event strings before json serialization" do
7
+ source =
8
+ <<~ICS
9
+ BEGIN:VCALENDAR
10
+ VERSION:2.0
11
+ PRODID:-//code-ruby//EN
12
+ BEGIN:VEVENT
13
+ UID:event-1
14
+ DTSTART:20260327T120000Z
15
+ DTEND:20260327T133000Z
16
+ SUMMARY:Joséphine
17
+ DESCRIPTION:https://luma.com/opv3owre
18
+ END:VEVENT
19
+ END:VCALENDAR
20
+ ICS
21
+
22
+ events = described_class.code_parse(Code::Object::String.new(source.b))
23
+
24
+ expect { expect(events.to_json).to include("Joséphine") }.not_to output.to_stderr
25
+ end
26
+
27
+ it "serializes comma-separated descriptions as strings" do
28
+ source =
29
+ <<~ICS
30
+ BEGIN:VCALENDAR
31
+ VERSION:2.0
32
+ PRODID:-//code-ruby//EN
33
+ BEGIN:VEVENT
34
+ UID:event-2
35
+ DTSTART:20260401T170000Z
36
+ DTEND:20260401T190000Z
37
+ SUMMARY:Le Cirque du Donut
38
+ DESCRIPTION:Bonjour Paris !\\nLe 1er avril, Kate Raworth vous donne rendez-vous, pour une soirée inédite.
39
+ END:VEVENT
40
+ END:VCALENDAR
41
+ ICS
42
+
43
+ events = described_class.code_parse(Code::Object::String.new(source))
44
+ description = events.raw.first.code_get("description").raw
45
+
46
+ expect(description).to be_a(String)
47
+ expect(description).to include("Le 1er avril, Kate Raworth")
48
+ expect(description).to include("rendez-vous, pour")
49
+ end
50
+ end
data/spec/code_spec.rb CHANGED
@@ -366,7 +366,10 @@ RSpec.describe Code do
366
366
  ["if false 1 else 2", "2"],
367
367
  ["if false 1 else if false 2", "nothing"],
368
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"],
369
371
  ["if false 1 else unless false 2", "2"],
372
+ ["if false 1 else unless true 2 else 3 end", "3"],
370
373
  ["if false 1 else unless true 2", "nothing"],
371
374
  ["if false 1 elsif false 2", "nothing"],
372
375
  ["if false 1 elsif true 2", "2"],
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.3
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié
@@ -321,6 +321,7 @@ files:
321
321
  - spec/code/object/decimal_spec.rb
322
322
  - spec/code/object/dictionary_spec.rb
323
323
  - spec/code/object/function_spec.rb
324
+ - spec/code/object/ics_spec.rb
324
325
  - spec/code/object/integer_spec.rb
325
326
  - spec/code/object/list_spec.rb
326
327
  - spec/code/object/nothing_spec.rb