code-ruby 3.1.2 → 4.0.0

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