code-ruby 3.1.2 → 4.0.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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/code +100 -20
  4. data/lib/code/concerns/shared.rb +335 -15
  5. data/lib/code/format.rb +33 -15
  6. data/lib/code/network.rb +82 -0
  7. data/lib/code/node/call.rb +80 -2
  8. data/lib/code/node/call_argument.rb +14 -0
  9. data/lib/code/node/code.rb +4 -3
  10. data/lib/code/node/function_parameter.rb +7 -4
  11. data/lib/code/node/list.rb +32 -2
  12. data/lib/code/node/square_bracket.rb +4 -2
  13. data/lib/code/object/base_64.rb +132 -6
  14. data/lib/code/object/boolean.rb +56 -0
  15. data/lib/code/object/class.rb +143 -2
  16. data/lib/code/object/code.rb +108 -7
  17. data/lib/code/object/context.rb +59 -1
  18. data/lib/code/object/cryptography.rb +69 -0
  19. data/lib/code/object/date.rb +13800 -462
  20. data/lib/code/object/decimal.rb +1098 -0
  21. data/lib/code/object/dictionary.rb +1861 -11
  22. data/lib/code/object/duration.rb +24 -0
  23. data/lib/code/object/function.rb +289 -27
  24. data/lib/code/object/global.rb +447 -1
  25. data/lib/code/object/html.rb +181 -7
  26. data/lib/code/object/http.rb +253 -17
  27. data/lib/code/object/ics.rb +76 -13
  28. data/lib/code/object/identifier_list.rb +30 -10
  29. data/lib/code/object/integer.rb +1265 -2
  30. data/lib/code/object/json.rb +80 -1
  31. data/lib/code/object/list.rb +3371 -10
  32. data/lib/code/object/nothing.rb +53 -0
  33. data/lib/code/object/number.rb +120 -0
  34. data/lib/code/object/parameter.rb +149 -0
  35. data/lib/code/object/range.rb +530 -14
  36. data/lib/code/object/smtp.rb +103 -12
  37. data/lib/code/object/string.rb +968 -3
  38. data/lib/code/object/super.rb +11 -1
  39. data/lib/code/object/time.rb +13932 -498
  40. data/lib/code/object/url.rb +67 -0
  41. data/lib/code/object.rb +582 -0
  42. data/lib/code/parser.rb +194 -55
  43. data/lib/code-ruby.rb +3 -0
  44. data/lib/code.rb +30 -3
  45. metadata +135 -84
  46. data/.github/dependabot.yml +0 -15
  47. data/.github/workflows/ci.yml +0 -38
  48. data/.gitignore +0 -30
  49. data/.node-version +0 -1
  50. data/.npm-version +0 -1
  51. data/.prettierignore +0 -2
  52. data/.rspec +0 -1
  53. data/.rubocop.yml +0 -140
  54. data/.ruby-version +0 -1
  55. data/.tool-versions +0 -3
  56. data/AGENTS.md +0 -43
  57. data/Gemfile +0 -22
  58. data/Gemfile.lock +0 -292
  59. data/Rakefile +0 -5
  60. data/bin/bundle +0 -123
  61. data/bin/bundle-audit +0 -31
  62. data/bin/bundler-audit +0 -31
  63. data/bin/dorian +0 -31
  64. data/bin/rspec +0 -31
  65. data/bin/rubocop +0 -31
  66. data/bin/test +0 -5
  67. data/code-ruby.gemspec +0 -34
  68. data/docs/precedence.txt +0 -36
  69. data/package-lock.json +0 -14
  70. data/package.json +0 -7
  71. data/spec/bin/code_spec.rb +0 -48
  72. data/spec/code/format_spec.rb +0 -153
  73. data/spec/code/node/call_spec.rb +0 -11
  74. data/spec/code/object/boolean_spec.rb +0 -18
  75. data/spec/code/object/cryptography_spec.rb +0 -25
  76. data/spec/code/object/decimal_spec.rb +0 -50
  77. data/spec/code/object/dictionary_spec.rb +0 -98
  78. data/spec/code/object/function_spec.rb +0 -268
  79. data/spec/code/object/http_spec.rb +0 -33
  80. data/spec/code/object/ics_spec.rb +0 -50
  81. data/spec/code/object/integer_spec.rb +0 -42
  82. data/spec/code/object/list_spec.rb +0 -22
  83. data/spec/code/object/nothing_spec.rb +0 -14
  84. data/spec/code/object/range_spec.rb +0 -23
  85. data/spec/code/object/string_spec.rb +0 -26
  86. data/spec/code/parser/boolean_spec.rb +0 -11
  87. data/spec/code/parser/chained_call_spec.rb +0 -16
  88. data/spec/code/parser/dictionary_spec.rb +0 -18
  89. data/spec/code/parser/function_spec.rb +0 -16
  90. data/spec/code/parser/group_spec.rb +0 -11
  91. data/spec/code/parser/if_modifier_spec.rb +0 -18
  92. data/spec/code/parser/list_spec.rb +0 -17
  93. data/spec/code/parser/number_spec.rb +0 -11
  94. data/spec/code/parser/string_spec.rb +0 -20
  95. data/spec/code/parser_spec.rb +0 -52
  96. data/spec/code/type_spec.rb +0 -21
  97. data/spec/code_spec.rb +0 -717
  98. data/spec/spec_helper.rb +0 -21
  99. data/spec/zeitwerk/loader_spec.rb +0 -7
@@ -3,6 +3,34 @@
3
3
  class Code
4
4
  class Object
5
5
  class Ics < Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Ics",
8
+ description:
9
+ "parses icalendar text and exposes events as dictionaries.",
10
+ examples: [
11
+ "Ics.parse(\"BEGIN:VCALENDAR\\nBEGIN:VEVENT\\nSUMMARY:meet\\nEND:VEVENT\\nEND:VCALENDAR\")",
12
+ "Ics.parse(\"BEGIN:VCALENDAR\\nVERSION:2.0\\nEND:VCALENDAR\")",
13
+ "Ics.parse(\"\")"
14
+ ]
15
+ }.freeze
16
+ CLASS_FUNCTIONS = {
17
+ "parse" => {
18
+ name: "parse",
19
+ description: "returns event dictionaries parsed from icalendar text.",
20
+ examples: [
21
+ "Ics.parse(\"BEGIN:VCALENDAR\\nBEGIN:VEVENT\\nUID:1\\nSUMMARY:demo\\nEND:VEVENT\\nEND:VCALENDAR\")",
22
+ "Ics.parse(\"BEGIN:VCALENDAR\\nEND:VCALENDAR\")",
23
+ "Ics.parse(\"not calendar data\")"
24
+ ]
25
+ }
26
+ }.freeze
27
+
28
+ def self.function_documentation(scope)
29
+ return CLASS_FUNCTIONS if scope == :class
30
+
31
+ {}
32
+ end
33
+
6
34
  EVENT_ATTRIBUTES = %i[
7
35
  uid
8
36
  summary
@@ -15,6 +43,9 @@ class Code
15
43
  attendees
16
44
  geo
17
45
  ].freeze
46
+ MISSING_ATTRIBUTE = Object.new.freeze
47
+ MAX_EVENTS = 10_000
48
+ MAX_NESTING = 50
18
49
 
19
50
  def self.call(**args)
20
51
  code_operator = args.fetch(:operator, nil).to_code
@@ -32,11 +63,14 @@ class Code
32
63
 
33
64
  def self.code_parse(value)
34
65
  source = value.to_code.raw
66
+ ::Code.ensure_input_size!(source, label: "ics")
35
67
  calendars = ::Icalendar::Calendar.parse(source)
36
- calendars
37
- .flat_map(&:events)
38
- .map { |event| serialize_event(event) }
39
- .to_code
68
+ events = calendars.flat_map(&:events)
69
+ raise Error, "ics has too many events" if events.size > MAX_EVENTS
70
+
71
+ events.map { |event| serialize_event(event) }.to_code
72
+ rescue ::Code::Error
73
+ raise
40
74
  rescue StandardError
41
75
  [].to_code
42
76
  end
@@ -44,9 +78,9 @@ class Code
44
78
  def self.serialize_event(event)
45
79
  EVENT_ATTRIBUTES
46
80
  .each_with_object({}) do |attribute, result|
47
- next unless event.respond_to?(attribute)
81
+ serialized = serialize_value(event_attribute(event, attribute))
82
+ next if serialized == MISSING_ATTRIBUTE
48
83
 
49
- serialized = serialize_value(event.public_send(attribute))
50
84
  serialized =
51
85
  if attribute == :categories && serialized.is_a?(::Array)
52
86
  serialized.flatten(1)
@@ -67,7 +101,36 @@ class Code
67
101
  .compact
68
102
  end
69
103
 
70
- def self.serialize_value(value)
104
+ def self.event_attribute(event, attribute)
105
+ case attribute
106
+ when :uid
107
+ event.uid
108
+ when :summary
109
+ event.summary
110
+ when :description
111
+ event.description
112
+ when :location
113
+ event.location
114
+ when :url
115
+ event.url
116
+ when :status
117
+ event.status
118
+ when :organizer
119
+ event.organizer
120
+ when :categories
121
+ event.categories
122
+ when :attendees
123
+ event.attendees
124
+ when :geo
125
+ event.geo
126
+ end
127
+ rescue NoMethodError
128
+ MISSING_ATTRIBUTE
129
+ end
130
+
131
+ def self.serialize_value(value, depth: 0)
132
+ raise Error, "ics is too deeply nested" if depth > MAX_NESTING
133
+
71
134
  case value
72
135
  when nil
73
136
  nil
@@ -76,17 +139,17 @@ class Code
76
139
  when ::Symbol, ::Integer, ::Float, ::BigDecimal, true, false
77
140
  value
78
141
  when ::Array
79
- value.map { |item| serialize_value(item) }
142
+ value.map { |item| serialize_value(item, depth: depth + 1) }
80
143
  when ::Hash
81
- value.transform_values { |item| serialize_value(item) }
144
+ value.transform_values do |item|
145
+ serialize_value(item, depth: depth + 1)
146
+ end
82
147
  else
83
148
  serialized_date = serialize_date_like(value)
84
149
  return serialized_date unless serialized_date.nil?
85
150
 
86
- if value.respond_to?(:value)
87
- serialize_value(value.value)
88
- elsif value.respond_to?(:to_ical)
89
- normalize_string(value.to_ical)
151
+ if value.is_a?(::Icalendar::Value)
152
+ serialize_value(value.value, depth: depth + 1)
90
153
  else
91
154
  normalize_string(value.to_s)
92
155
  end
@@ -3,6 +3,16 @@
3
3
  class Code
4
4
  class Object
5
5
  class IdentifierList < List
6
+ CLASS_DOCUMENTATION = {
7
+ name: "IdentifierList",
8
+ description:
9
+ "stores ordered identifier names used to assign nested values.",
10
+ examples: [
11
+ "IdentifierList",
12
+ "IdentifierList.new([:user, :name]).first",
13
+ "IdentifierList.new([:user, :name]).join(\".\")"
14
+ ]
15
+ }.freeze
6
16
  def call(**args)
7
17
  code_operator = args.fetch(:operator, nil).to_code
8
18
  code_arguments = args.fetch(:arguments, []).to_code
@@ -83,11 +93,13 @@ class Code
83
93
  end
84
94
 
85
95
  if assignment_operator == "="
86
- receiver.call(
87
- **args,
88
- operator: "#{raw.last}=",
89
- arguments: Object::List.new([code_value])
90
- )
96
+ receiver
97
+ .call(
98
+ **args,
99
+ operator: "#{raw.last}=",
100
+ arguments: Object::List.new([code_value])
101
+ )
102
+ .tap { persist_class_receiver(receiver, args.fetch(:context)) }
91
103
  else
92
104
  next_value =
93
105
  receiver.call(
@@ -100,13 +112,21 @@ class Code
100
112
  arguments: Object::List.new([code_value])
101
113
  )
102
114
 
103
- receiver.call(
104
- **args,
105
- operator: "#{raw.last}=",
106
- arguments: Object::List.new([next_value])
107
- )
115
+ receiver
116
+ .call(
117
+ **args,
118
+ operator: "#{raw.last}=",
119
+ arguments: Object::List.new([next_value])
120
+ )
121
+ .tap { persist_class_receiver(receiver, args.fetch(:context)) }
108
122
  end
109
123
  end
124
+
125
+ def persist_class_receiver(receiver, context)
126
+ return unless receiver.is_a?(Object::Class)
127
+
128
+ context.code_set(raw.first, receiver)
129
+ end
110
130
  end
111
131
  end
112
132
  end