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,59 @@
3
3
  class Code
4
4
  class Object
5
5
  class Url < Object
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Url",
8
+ description:
9
+ "encodes text for urls, decodes url-escaped text, and parses strings as urls.",
10
+ examples: [
11
+ "Url.encode(\"hello world\")",
12
+ "Url.decode(\"a%2Fb%3Fx%3D1\")",
13
+ "Url.parse(\"https://example.com/a?b=1\")"
14
+ ]
15
+ }.freeze
16
+ CLASS_FUNCTIONS = {
17
+ "encode" => {
18
+ name: "encode",
19
+ description: "returns url-escaped text for a value.",
20
+ examples: [
21
+ "Url.encode(\"hello world\")",
22
+ "Url.encode(\"a/b?x=1\")",
23
+ "Url.encode(:hello)"
24
+ ]
25
+ },
26
+ "decode" => {
27
+ name: "decode",
28
+ description: "returns text decoded from url-escaped text.",
29
+ examples: [
30
+ "Url.decode(\"hello+world\")",
31
+ "Url.decode(\"a%2Fb%3Fx%3D1\")",
32
+ "Url.decode(Url.encode(\"a+b\"))"
33
+ ]
34
+ },
35
+ "parse" => {
36
+ name: "parse",
37
+ description:
38
+ "returns a url parsed from a value, or an empty url when parsing fails.",
39
+ examples: [
40
+ "Url.parse(\"https://example.com/a?b=1\")",
41
+ "Url.parse(\"/path\")",
42
+ "Url.parse(\"not a url\")"
43
+ ]
44
+ }
45
+ }.freeze
46
+
47
+ def self.function_documentation(scope)
48
+ return CLASS_FUNCTIONS if scope == :class
49
+
50
+ {}
51
+ end
52
+
53
+ def initialize(*args, **_kargs, &_block)
54
+ self.raw = ::URI.parse(args.first.to_s)
55
+ rescue ::URI::InvalidURIError
56
+ self.raw = ::URI.parse("")
57
+ end
58
+
6
59
  def self.call(**args)
7
60
  code_operator = args.fetch(:operator, nil).to_code
8
61
  code_arguments = args.fetch(:arguments, []).to_code
@@ -11,6 +64,12 @@ class Code
11
64
  when "encode"
12
65
  sig(args) { Object.maybe }
13
66
  code_encode(*code_arguments.raw)
67
+ when "decode"
68
+ sig(args) { Object.maybe }
69
+ code_decode(*code_arguments.raw)
70
+ when "parse"
71
+ sig(args) { Object.maybe }
72
+ code_parse(*code_arguments.raw)
14
73
  else
15
74
  super
16
75
  end
@@ -21,6 +80,14 @@ class Code
21
80
 
22
81
  String.new(CGI.escape(string.to_s))
23
82
  end
83
+
84
+ def self.code_decode(string = nil)
85
+ String.new(CGI.unescape(string.to_s))
86
+ end
87
+
88
+ def self.code_parse(string = nil)
89
+ new(string)
90
+ end
24
91
  end
25
92
  end
26
93
  end
data/lib/code/object.rb CHANGED
@@ -11,7 +11,539 @@ class Code
11
11
  ::String,
12
12
  ::BigDecimal
13
13
  ].freeze
14
+ CLASS_DOCUMENTATION = {
15
+ name: "Object",
16
+ description: "provides the shared behavior available to every value.",
17
+ examples: %w[Object Object.new Object.documentation.description]
18
+ }.freeze
19
+ INSTANCE_FUNCTIONS = {
20
+ "documentation" => {
21
+ name: "documentation",
22
+ description: "returns documentation for the value's class.",
23
+ examples: [
24
+ "1.documentation.name",
25
+ "[].documentation.description",
26
+ "{}.documentation.examples"
27
+ ]
28
+ },
29
+ "functions" => {
30
+ name: "functions",
31
+ description:
32
+ "returns documented instance and class functions available on the value.",
33
+ examples: [
34
+ "[].functions.keys.include?(:map)",
35
+ "1.functions.keys.include?(:to_string)",
36
+ "{}.functions.keys.include?(:fetch)"
37
+ ]
38
+ },
39
+ "instance_functions" => {
40
+ name: "instance_functions",
41
+ description:
42
+ "returns documented functions available on values of the receiver.",
43
+ examples: [
44
+ "[].instance_functions.keys.include?(:map)",
45
+ "1.instance_functions.keys.include?(:to_string)",
46
+ "{}.instance_functions.keys.include?(:fetch)"
47
+ ]
48
+ },
49
+ "class_functions" => {
50
+ name: "class_functions",
51
+ description:
52
+ "returns documented functions available on the receiver constructor.",
53
+ examples: [
54
+ "[].class_functions",
55
+ "List.class_functions.keys.include?(:new)",
56
+ "String.class_functions.keys.include?(:new)"
57
+ ]
58
+ },
59
+ "respond_to?" => {
60
+ name: "respond_to?",
61
+ description:
62
+ "returns whether the value responds to the named function.",
63
+ examples: [
64
+ "[].respond_to?(:map)",
65
+ "1.respond_to?(:zero?)",
66
+ "{}.respond_to?(:fetch)"
67
+ ]
68
+ },
69
+ "present?" => {
70
+ name: "present?",
71
+ description: "returns whether the value is present.",
72
+ examples: ["1.present?", ":hello.present?", "[1].present?"]
73
+ },
74
+ "blank?" => {
75
+ name: "blank?",
76
+ description: "returns whether the value is blank.",
77
+ examples: ["nothing.blank?", "\"\".blank?", "[].blank?"]
78
+ },
79
+ "presence" => {
80
+ name: "presence",
81
+ description: "returns the value when present, otherwise nothing.",
82
+ examples: [":hello.presence", "\"\".presence", "[1].presence"]
83
+ },
84
+ "presence_in" => {
85
+ name: "presence_in",
86
+ description:
87
+ "returns the value when it is included in the list, otherwise nothing.",
88
+ examples: [
89
+ "2.presence_in([1, 2, 3])",
90
+ "4.presence_in([1, 2, 3])",
91
+ "\"a\".presence_in([\"a\", \"b\"])"
92
+ ]
93
+ },
94
+ "is_a?" => {
95
+ name: "is_a?",
96
+ description:
97
+ "returns whether the value is an instance of the class or one of its subclasses.",
98
+ examples: ["1.is_a?(Integer)", ":a.is_a?(String)", "[].is_a?(List)"]
99
+ },
100
+ "is_an?" => {
101
+ name: "is_an?",
102
+ description: "alias for is_a?.",
103
+ examples: ["1.is_an?(Integer)", ":a.is_an?(String)", "[].is_an?(List)"]
104
+ },
105
+ "kind_of?" => {
106
+ name: "kind_of?",
107
+ description: "alias for is_a?.",
108
+ examples: [
109
+ "1.kind_of?(Integer)",
110
+ ":a.kind_of?(String)",
111
+ "[].kind_of?(List)"
112
+ ]
113
+ },
114
+ "instance_of?" => {
115
+ name: "instance_of?",
116
+ description:
117
+ "returns whether the value is exactly an instance of the class.",
118
+ examples: [
119
+ "1.instance_of?(Integer)",
120
+ ":a.instance_of?(String)",
121
+ "[].instance_of?(List)"
122
+ ]
123
+ },
124
+ "!" => {
125
+ name: "!",
126
+ description: "returns the boolean negation of the value.",
127
+ examples: %w[!true !false !nothing]
128
+ },
129
+ "not" => {
130
+ name: "not",
131
+ description: "returns the boolean negation of the value.",
132
+ examples: %w[true.not false.not nothing.not]
133
+ },
134
+ "!=" => {
135
+ name: "!=",
136
+ description:
137
+ "returns whether the value is different from another value.",
138
+ examples: ["1 != 2", ":a != :b", "[1] != [2]"]
139
+ },
140
+ "different" => {
141
+ name: "different",
142
+ description:
143
+ "returns whether the value is different from another value.",
144
+ examples: ["1.different(2)", ":a.different(:b)", "[1].different([2])"]
145
+ },
146
+ "&&" => {
147
+ name: "&&",
148
+ description:
149
+ "returns the other value when the receiver is truthy, otherwise the receiver.",
150
+ examples: ["true && 1", "false && 1", "1 && 2"]
151
+ },
152
+ "and" => {
153
+ name: "and",
154
+ description:
155
+ "returns the other value when the receiver is truthy, otherwise the receiver.",
156
+ examples: %w[true.and(1) false.and(1) 1.and(2)]
157
+ },
158
+ "+" => {
159
+ name: "+",
160
+ description:
161
+ "returns the receiver for object types that do not override plus.",
162
+ examples: ["+Object.new", "+nothing", "+[]"]
163
+ },
164
+ "self" => {
165
+ name: "self",
166
+ description: "returns the receiver.",
167
+ examples: ["1.self", ":a.self", "[].self"]
168
+ },
169
+ ".." => {
170
+ name: "..",
171
+ description:
172
+ "builds an inclusive range from the receiver to another value.",
173
+ examples: %w[1..3 :a..:c Date.today..Date.tomorrow]
174
+ },
175
+ "inclusive_range" => {
176
+ name: "inclusive_range",
177
+ description:
178
+ "builds an inclusive range from the receiver to another value.",
179
+ examples: %w[
180
+ 1.inclusive_range(3)
181
+ :a.inclusive_range(:c)
182
+ Date.today.inclusive_range(Date.tomorrow)
183
+ ]
184
+ },
185
+ "..." => {
186
+ name: "...",
187
+ description:
188
+ "builds an exclusive range from the receiver to another value.",
189
+ examples: %w[1...3 :a...:c Date.today...Date.tomorrow]
190
+ },
191
+ "exclusive_range" => {
192
+ name: "exclusive_range",
193
+ description:
194
+ "builds an exclusive range from the receiver to another value.",
195
+ examples: %w[
196
+ 1.exclusive_range(3)
197
+ :a.exclusive_range(:c)
198
+ Date.today.exclusive_range(Date.tomorrow)
199
+ ]
200
+ },
201
+ "==" => {
202
+ name: "==",
203
+ description: "returns whether the value equals another value.",
204
+ examples: ["1 == 1", ":a == :a", "[1] == [1]"]
205
+ },
206
+ "equal" => {
207
+ name: "equal",
208
+ description: "returns whether the value equals another value.",
209
+ examples: ["1.equal(1)", ":a.equal(:a)", "[1].equal([1])"]
210
+ },
211
+ "equal?" => {
212
+ name: "equal?",
213
+ description: "returns whether the value equals another value.",
214
+ examples: ["1.equal?(1)", ":a.equal?(:a)", "[1].equal?([1])"]
215
+ },
216
+ "same_object?" => {
217
+ name: "same_object?",
218
+ description:
219
+ "returns whether the value and argument are the same object.",
220
+ examples: [
221
+ "a = [] b = a a.same_object?(b)",
222
+ "[].same_object?([])",
223
+ "a = {} b = a a.same_object?(b)"
224
+ ]
225
+ },
226
+ ">" => {
227
+ name: ">",
228
+ description: "returns whether the value is greater than another value.",
229
+ examples: ["2 > 1", ":b > :a", "Date.tomorrow > Date.today"]
230
+ },
231
+ "greater" => {
232
+ name: "greater",
233
+ description: "returns whether the value is greater than another value.",
234
+ examples: %w[
235
+ 2.greater(1)
236
+ :b.greater(:a)
237
+ Date.tomorrow.greater(Date.today)
238
+ ]
239
+ },
240
+ ">=" => {
241
+ name: ">=",
242
+ description:
243
+ "returns whether the value is greater than or equal to another value.",
244
+ examples: ["2 >= 2", ":b >= :a", "Date.today >= Date.today"]
245
+ },
246
+ "greater_or_equal" => {
247
+ name: "greater_or_equal",
248
+ description:
249
+ "returns whether the value is greater than or equal to another value.",
250
+ examples: %w[
251
+ 2.greater_or_equal(2)
252
+ :b.greater_or_equal(:a)
253
+ Date.today.greater_or_equal(Date.today)
254
+ ]
255
+ },
256
+ "<=>" => {
257
+ name: "<=>",
258
+ description:
259
+ "compares the value with another value and returns -1, 0, or 1.",
260
+ examples: ["1 <=> 2", ":a <=> :b", "Date.today <=> Date.tomorrow"]
261
+ },
262
+ "compare" => {
263
+ name: "compare",
264
+ description:
265
+ "compares the value with another value and returns -1, 0, or 1.",
266
+ examples: %w[
267
+ 1.compare(2)
268
+ :a.compare(:b)
269
+ Date.today.compare(Date.tomorrow)
270
+ ]
271
+ },
272
+ "<" => {
273
+ name: "<",
274
+ description: "returns whether the value is less than another value.",
275
+ examples: ["1 < 2", ":a < :b", "Date.today < Date.tomorrow"]
276
+ },
277
+ "less" => {
278
+ name: "less",
279
+ description: "returns whether the value is less than another value.",
280
+ examples: %w[1.less(2) :a.less(:b) Date.today.less(Date.tomorrow)]
281
+ },
282
+ "<=" => {
283
+ name: "<=",
284
+ description:
285
+ "returns whether the value is less than or equal to another value.",
286
+ examples: ["1 <= 1", ":a <= :b", "Date.today <= Date.today"]
287
+ },
288
+ "less_or_equal" => {
289
+ name: "less_or_equal",
290
+ description:
291
+ "returns whether the value is less than or equal to another value.",
292
+ examples: %w[
293
+ 1.less_or_equal(1)
294
+ :a.less_or_equal(:b)
295
+ Date.today.less_or_equal(Date.today)
296
+ ]
297
+ },
298
+ "===" => {
299
+ name: "===",
300
+ description: "returns whether the value strictly equals another value.",
301
+ examples: ["1 === 1", ":a === :a", "[1] === [1]"]
302
+ },
303
+ "strict_equal" => {
304
+ name: "strict_equal",
305
+ description: "returns whether the value strictly equals another value.",
306
+ examples: [
307
+ "1.strict_equal(1)",
308
+ ":a.strict_equal(:a)",
309
+ "[1].strict_equal([1])"
310
+ ]
311
+ },
312
+ "!==" => {
313
+ name: "!==",
314
+ description:
315
+ "returns whether the value does not strictly equal another value.",
316
+ examples: ["1 !== 2", ":a !== :b", "[1] !== [2]"]
317
+ },
318
+ "strict_different" => {
319
+ name: "strict_different",
320
+ description:
321
+ "returns whether the value does not strictly equal another value.",
322
+ examples: [
323
+ "1.strict_different(2)",
324
+ ":a.strict_different(:b)",
325
+ "[1].strict_different([2])"
326
+ ]
327
+ },
328
+ "falsy?" => {
329
+ name: "falsy?",
330
+ description: "returns whether the value is falsy.",
331
+ examples: %w[nothing.falsy? false.falsy? 1.falsy?]
332
+ },
333
+ "truthy?" => {
334
+ name: "truthy?",
335
+ description: "returns whether the value is truthy.",
336
+ examples: %w[1.truthy? true.truthy? nothing.truthy?]
337
+ },
338
+ "true?" => {
339
+ name: "true?",
340
+ description: "returns whether the value is true.",
341
+ examples: %w[true.true? false.true? 1.true?]
342
+ },
343
+ "false?" => {
344
+ name: "false?",
345
+ description: "returns whether the value is false or nothing.",
346
+ examples: %w[false.false? nothing.false? true.false?]
347
+ },
348
+ "||" => {
349
+ name: "||",
350
+ description:
351
+ "returns the receiver when truthy, otherwise the other value.",
352
+ examples: ["nothing || 1", "false || 1", "2 || 1"]
353
+ },
354
+ "or" => {
355
+ name: "or",
356
+ description:
357
+ "returns the receiver when truthy, otherwise the other value.",
358
+ examples: %w[nothing.or(1) false.or(1) 2.or(1)]
359
+ },
360
+ "to_boolean" => {
361
+ name: "to_boolean",
362
+ description: "converts the value to a boolean.",
363
+ examples: ["1.to_boolean", "nothing.to_boolean", "\"\".to_boolean"]
364
+ },
365
+ "to_class" => {
366
+ name: "to_class",
367
+ description: "converts the value to a class object.",
368
+ examples: ["1.to_class", ":a.to_class", "[].to_class"]
369
+ },
370
+ "to_date" => {
371
+ name: "to_date",
372
+ description: "converts the value to a date.",
373
+ examples: [
374
+ "\"2026-05-25\".to_date",
375
+ "Time.now.to_date",
376
+ "Date.today.to_date"
377
+ ]
378
+ },
379
+ "to_decimal" => {
380
+ name: "to_decimal",
381
+ description: "converts the value to a decimal.",
382
+ examples: ["1.to_decimal", "\"1.5\".to_decimal", "2.0.to_decimal"]
383
+ },
384
+ "to_dictionary" => {
385
+ name: "to_dictionary",
386
+ description: "converts the value to a dictionary.",
387
+ examples: [
388
+ "[].to_dictionary",
389
+ "{}.to_dictionary",
390
+ "[[:a, 1]].to_dictionary"
391
+ ]
392
+ },
393
+ "to_duration" => {
394
+ name: "to_duration",
395
+ description: "converts the value to a duration.",
396
+ examples: %w[1.to_duration 1.day.to_duration 2.hours.to_duration]
397
+ },
398
+ "to_integer" => {
399
+ name: "to_integer",
400
+ description: "converts the value to an integer.",
401
+ examples: ["\"1\".to_integer", "1.5.to_integer", "1.to_integer"]
402
+ },
403
+ "to_list" => {
404
+ name: "to_list",
405
+ description: "converts the value to a list.",
406
+ examples: ["1.to_list", "[1].to_list", "{}.to_list"]
407
+ },
408
+ "to_nothing" => {
409
+ name: "to_nothing",
410
+ description: "converts the value to nothing.",
411
+ examples: ["1.to_nothing", ":a.to_nothing", "[].to_nothing"]
412
+ },
413
+ "to_range" => {
414
+ name: "to_range",
415
+ description: "converts the value to a range.",
416
+ examples: ["1.to_range", "(1..3).to_range", "[1, 3].to_range"]
417
+ },
418
+ "to_string" => {
419
+ name: "to_string",
420
+ description: "converts the value to a string.",
421
+ examples: %w[1.to_string true.to_string :a.to_string]
422
+ },
423
+ "inspect" => {
424
+ name: "inspect",
425
+ description:
426
+ "returns a string representation of the value for inspection.",
427
+ examples: ["1.inspect", ":a.inspect", "[1].inspect"]
428
+ },
429
+ "to_time" => {
430
+ name: "to_time",
431
+ description: "converts the value to a time.",
432
+ examples: [
433
+ "\"2026-05-25\".to_time",
434
+ "Date.today.to_time",
435
+ "Time.now.to_time"
436
+ ]
437
+ },
438
+ "as_json" => {
439
+ name: "as_json",
440
+ description: "converts the value to a JSON-compatible value.",
441
+ examples: ["{ a: 1 }.as_json", "[1, 2].as_json", ":a.as_json"]
442
+ },
443
+ "duplicate" => {
444
+ name: "duplicate",
445
+ description: "returns a shallow duplicate of the value.",
446
+ examples: ["[1].duplicate", "{ a: 1 }.duplicate", ":a.duplicate"]
447
+ },
448
+ "deep_duplicate" => {
449
+ name: "deep_duplicate",
450
+ description: "returns a deep duplicate of the value.",
451
+ examples: [
452
+ "[{}].deep_duplicate",
453
+ "{ a: [1] }.deep_duplicate",
454
+ ":a.deep_duplicate"
455
+ ]
456
+ },
457
+ "to_parameter" => {
458
+ name: "to_parameter",
459
+ description: "converts the value to a URL parameter string.",
460
+ examples: [
461
+ "\"Hello world\".to_parameter",
462
+ "\"A/B\".to_parameter",
463
+ "123.to_parameter"
464
+ ]
465
+ },
466
+ "to_json" => {
467
+ name: "to_json",
468
+ description: "serializes the value as JSON.",
469
+ examples: ["{ a: 1 }.to_json", "[1, 2].to_json", "\"a\".to_json"]
470
+ },
471
+ "send" => {
472
+ name: "send",
473
+ description: "calls a function by name with optional arguments.",
474
+ examples: [
475
+ "[1, 2].send(:size)",
476
+ ":hello.send(:upcase)",
477
+ "1.send(:zero?)"
478
+ ]
479
+ },
480
+ "itself" => {
481
+ name: "itself",
482
+ description: "returns the receiver.",
483
+ examples: ["1.itself", ":a.itself", "[].itself"]
484
+ },
485
+ "tap" => {
486
+ name: "tap",
487
+ description:
488
+ "calls a function with the receiver and returns the receiver.",
489
+ examples: [
490
+ "1.tap((value) => { value })",
491
+ ":a.tap((value) => { value.upcase })",
492
+ "[1].tap((value) => { value.size })"
493
+ ]
494
+ },
495
+ "then" => {
496
+ name: "then",
497
+ description:
498
+ "calls a function with the receiver and returns the function result.",
499
+ examples: [
500
+ "1.then((value) => { value + 1 })",
501
+ ":a.then((value) => { value.upcase })",
502
+ "[1].then((value) => { value.size })"
503
+ ]
504
+ },
505
+ "name" => {
506
+ name: "name",
507
+ description: "returns the value's class name.",
508
+ examples: ["1.name", ":a.name", "[].name"]
509
+ },
510
+ "nothing?" => {
511
+ name: "nothing?",
512
+ description: "returns whether the value is nothing.",
513
+ examples: %w[nothing.nothing? 1.nothing? :a.nothing?]
514
+ },
515
+ "something?" => {
516
+ name: "something?",
517
+ description: "returns whether the value is not nothing.",
518
+ examples: %w[1.something? :a.something? nothing.something?]
519
+ }
520
+ }.freeze
521
+ CLASS_FUNCTIONS = {
522
+ "new" => {
523
+ name: "new",
524
+ description: "builds a new value for the class.",
525
+ examples: [
526
+ "List.new([1, 2])",
527
+ "String.new(:hello)",
528
+ "Dictionary.new(a: 1)"
529
+ ]
530
+ }
531
+ }.freeze
14
532
 
533
+ def self.function_documentation(scope)
534
+ case scope
535
+ when :instance
536
+ INSTANCE_FUNCTIONS
537
+ when :class
538
+ CLASS_FUNCTIONS
539
+ else
540
+ {}
541
+ end
542
+ end
543
+
544
+ def self.class_documentation
545
+ self::CLASS_DOCUMENTATION
546
+ end
15
547
  include Concerns::Shared
16
548
  extend Concerns::Shared
17
549
 
@@ -34,6 +566,56 @@ class Code
34
566
  new(*args)
35
567
  end
36
568
 
569
+ def self.functions
570
+ class_functions
571
+ end
572
+
573
+ def self.documentation
574
+ documentation_for(self)
575
+ end
576
+
577
+ def self.instance_functions
578
+ documented_functions_for(self, :instance)
579
+ end
580
+
581
+ def self.class_functions
582
+ documented_functions_for(self, :class)
583
+ end
584
+
585
+ def self.documented_functions_for(klass, scope)
586
+ sorted_dictionary(
587
+ function_documentation_for(klass, scope).transform_keys(&:to_s)
588
+ )
589
+ end
590
+
591
+ def self.documentation_for(klass)
592
+ Dictionary.new(klass.class_documentation.transform_keys(&:to_s))
593
+ end
594
+
595
+ def self.sorted_dictionary(raw)
596
+ Dictionary.new(raw.sort_by { |key, _value| key.to_s }.to_h)
597
+ end
598
+
599
+ def self.function_documentation_for(klass, scope)
600
+ documentation = function_documentation_registry_for(klass, scope)
601
+ inherited_function_documentation_for(klass, scope).merge(documentation)
602
+ end
603
+
604
+ def self.inherited_function_documentation_for(klass, scope)
605
+ return {} unless klass.is_a?(::Class)
606
+
607
+ superclass = klass.superclass
608
+ return {} unless superclass && superclass <= Object
609
+
610
+ function_documentation_for(superclass, scope)
611
+ rescue TypeError
612
+ {}
613
+ end
614
+
615
+ def self.function_documentation_registry_for(klass, scope)
616
+ klass.function_documentation(scope)
617
+ end
618
+
37
619
  def name
38
620
  self.class.name
39
621
  end