ryo.rb 0.5.5 → 0.5.7

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +2 -2
  3. data/.rubocop.yml +2 -0
  4. data/README.md +46 -27
  5. data/lib/ryo/basic_object.rb +7 -11
  6. data/lib/ryo/builder.rb +35 -54
  7. data/lib/ryo/enumerable.rb +62 -79
  8. data/lib/ryo/json.rb +6 -11
  9. data/lib/ryo/keywords.rb +16 -28
  10. data/lib/ryo/object.rb +6 -10
  11. data/lib/ryo/reflect.rb +54 -170
  12. data/lib/ryo/utils.rb +68 -0
  13. data/lib/ryo/version.rb +1 -1
  14. data/lib/ryo/yaml.rb +6 -11
  15. data/lib/ryo.rb +26 -37
  16. data/ryo.rb.gemspec +1 -0
  17. data/share/{ryo.rb/examples → examples/ryo.rb}/1.0_prototypes_point_object.rb +1 -0
  18. data/share/{ryo.rb/examples → examples/ryo.rb}/1.1_prototypes_ryo_fn.rb +1 -0
  19. data/share/{ryo.rb/examples → examples/ryo.rb}/2.0_iteration_each.rb +1 -0
  20. data/share/{ryo.rb/examples → examples/ryo.rb}/2.1_iteration_map.rb +1 -0
  21. data/share/{ryo.rb/examples → examples/ryo.rb}/2.2_iteration_ancestors.rb +1 -0
  22. data/share/examples/ryo.rb/3.0_recursion_ryo_from.rb +20 -0
  23. data/share/{ryo.rb/examples → examples/ryo.rb}/3.1_recursion_ryo_from_with_array.rb +5 -4
  24. data/share/{ryo.rb/examples → examples/ryo.rb}/3.2_recursion_ryo_from_with_openstruct.rb +3 -3
  25. data/share/{ryo.rb/examples → examples/ryo.rb}/4.0_basicobject_ryo_basicobject.rb +1 -0
  26. data/share/{ryo.rb/examples → examples/ryo.rb}/4.1_basicobject_ryo_basicobject_from.rb +1 -0
  27. data/share/{ryo.rb/examples → examples/ryo.rb}/5_collisions_resolution_strategy.rb +1 -0
  28. data/share/{ryo.rb/examples → examples/ryo.rb}/6_beyond_hash_objects.rb +1 -0
  29. data/share/{ryo.rb/examples → examples/ryo.rb}/7_ryo_memo.rb +1 -0
  30. data/share/ryo.rb/NEWS +11 -0
  31. data/spec/readme_spec.rb +2 -2
  32. data/spec/ryo_enumerable_spec.rb +1 -1
  33. data/spec/ryo_json_spec.rb +1 -1
  34. data/spec/ryo_keywords_spec.rb +1 -1
  35. data/spec/ryo_prototypes_spec.rb +1 -1
  36. data/spec/ryo_reflect_spec.rb +8 -0
  37. data/spec/ryo_spec.rb +1 -1
  38. data/spec/ryo_yaml_spec.rb +1 -1
  39. metadata +34 -18
  40. data/share/ryo.rb/examples/3.0_recursion_ryo_from.rb +0 -13
  41. /data/share/{ryo.rb/examples → examples/ryo.rb}/setup.rb +0 -0
  42. /data/{LICENSE → share/ryo.rb/LICENSE} +0 -0
data/lib/ryo/json.rb CHANGED
@@ -1,45 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Ryo::JSON Ryo::JSON} module provides a number of methods
5
- # for coercing JSON data into a Ryo object. It must be required
6
- # separately to Ryo (ie: require "ryo/json"), and the methods of
7
- # this module are then available on the {Ryo Ryo} module.
4
+ # The {Ryo::JSON Ryo::JSON} module provides a number of options
5
+ # for coercing JSON data into a Ryo object. The methods of
6
+ # this module are available on the {Ryo Ryo} module as singleton
7
+ # methods
8
8
  module Ryo::JSON
9
- require "json"
10
9
  extend self
11
10
 
12
11
  ##
13
12
  # @example
14
13
  # Ryo.from_json(path: "/foo/bar/baz.json")
15
14
  # Ryo.from_json(string: "[]")
16
- #
17
15
  # @param [String] path
18
16
  # The path to a JSON file
19
- #
20
17
  # @param [String] string
21
18
  # A blob of JSON
22
- #
23
19
  # @param [Ryo] object
24
20
  # {Ryo::Object Ryo::Object}, or {Ryo::BasicObject Ryo::BasicObject}
25
21
  # Defaults to {Ryo::Object Ryo::Object}
26
- #
27
22
  # @raise [SystemCallError]
28
23
  # Might raise a number of Errno exceptions
29
- #
30
24
  # @return [Ryo::Object, Ryo::BasicObject]
31
25
  # Returns a Ryo object
32
26
  def from_json(path: nil, string: nil, object: Ryo::Object)
33
27
  if path && string
34
28
  raise ArgumentError, "Provide a path or string but not both"
35
29
  elsif path
30
+ require "json" unless defined?(JSON)
36
31
  object.from JSON.parse(File.binread(path))
37
32
  elsif string
33
+ require "json" unless defined?(JSON)
38
34
  object.from JSON.parse(string)
39
35
  else
40
36
  raise ArgumentError, "No path or string provided"
41
37
  end
42
38
  end
43
-
44
39
  Ryo.extend(self)
45
40
  end
data/lib/ryo/keywords.rb CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  ##
4
4
  # The {Ryo::Keywords Ryo::Keywords} module implements Ryo equivalents
5
- # to some of JavaScript's keywords - for example: the **in** and **delete**
6
- # operators. The instance methods of this module are available as singleton
5
+ # for some of JavaScript's keywords (eg: the **in** and **delete** operators).
6
+ # The instance methods of this module are available as singleton
7
7
  # methods on the {Ryo} module.
8
8
  module Ryo::Keywords
9
9
  ##
@@ -24,44 +24,32 @@ module Ryo::Keywords
24
24
  alias_method :function, :fn
25
25
 
26
26
  ##
27
- # Equivalent to JavaScript's **in** operator.
28
- #
27
+ # Equivalent to JavaScript's **in** operator
29
28
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
30
- # A Ryo object.
31
- #
29
+ # A Ryo object
32
30
  # @param [<String, #to_s>] property
33
- # A property name.
34
- #
31
+ # A property name
35
32
  # @return [Boolean]
36
- # Returns true when **property** is a member of **ryo**, or its prototype chain.
33
+ # Returns true when **property** is a member of **ryo**, or its prototype chain
37
34
  def in?(ryo, property)
38
35
  return false unless ryo
39
36
  property?(ryo, property) || in?(prototype_of(ryo), property)
40
37
  end
41
38
 
42
39
  ##
43
- # The {#delete} method deletes a property from a Ryo object.
44
- # More or less equivalent to JavaScript's "delete" operator.
45
- #
46
- # @note
47
- # This method does not delete properties from the prototype(s)
48
- # of a Ryo object. <br>
49
- # For that - see {Ryo::Reflect#delete! Ryo::Reflect#delete!}.
50
- #
40
+ # The {#delete} method deletes a property from a Ryo object
41
+ # @see Ryo::Reflect#delete!
51
42
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
52
- # A Ryo object.
53
- #
43
+ # A Ryo object
54
44
  # @param [<String, #to_s>] property
55
- # A property name.
56
- #
45
+ # A property name
46
+ # @param [Integer] ancestors
47
+ # The number of prototypes to traverse.
48
+ # Defaults to the entire prototype chain.
57
49
  # @return [void]
58
- def delete(ryo, property)
59
- property = property.to_s
60
- if property?(ryo, property)
61
- table_of(ryo).delete(property)
62
- else
63
- return if getter_defined?(ryo, property)
64
- define_method!(ryo, property) { ryo[property] }
50
+ def delete(ryo, property, ancestors: nil)
51
+ each_ryo(ryo, ancestors:) do
52
+ Ryo.property?(_1, property) ? table_of(_1).delete(property) : nil
65
53
  end
66
54
  end
67
55
  end
data/lib/ryo/object.rb CHANGED
@@ -10,19 +10,17 @@ class Ryo::Object
10
10
  ##
11
11
  # @param props (see Ryo::Builder.build)
12
12
  # @param prototype (see Ryo::Builder.build)
13
- #
14
13
  # @return [Ryo::Object]
15
- # Returns an instance of {Ryo::Object Ryo::Object}.
14
+ # Returns an instance of {Ryo::Object Ryo::Object}
16
15
  def self.create(props, prototype = nil)
17
16
  Ryo::Builder.build(self, props, prototype)
18
17
  end
19
18
 
20
19
  ##
21
- # Creates a Ryo object by recursively walking a Hash object.
20
+ # Creates a Ryo object by recursively walking a Hash object
22
21
  #
23
22
  # @param props (see Ryo::Builder.recursive_build)
24
23
  # @param prototype (see Ryo::Builder.recursive_build)
25
- #
26
24
  # @return [Ryo::Object]
27
25
  # Returns an instance of {Ryo::Object Ryo::Object}.
28
26
  def self.from(props, prototype = nil)
@@ -30,13 +28,12 @@ class Ryo::Object
30
28
  end
31
29
 
32
30
  ##
33
- # Duplicates the internals of a Ryo object.
31
+ # Duplicates the internals of a Ryo object
34
32
  #
35
33
  # @param [Ryo::Object] ryo
36
- # A Ryo object.
37
- #
34
+ # A Ryo object
38
35
  # @return [Ryo::Object]
39
- # Returns a Ryo object.
36
+ # Returns a Ryo object
40
37
  def initialize_dup(ryo)
41
38
  Ryo.set_table_of(self, Ryo.table_of(ryo).dup)
42
39
  Ryo.extend!(self, Ryo)
@@ -50,9 +47,8 @@ end
50
47
  #
51
48
  # @param props (see Ryo::Builder.build)
52
49
  # @param prototype (see Ryo::Builder.build)
53
- #
54
50
  # @return [Ryo::Object]
55
- # Returns an instance of {Ryo::Object Ryo::Object}.
51
+ # Returns an instance of {Ryo::Object Ryo::Object}
56
52
  def Ryo.Object(props, prototype = nil)
57
53
  Ryo::Object.create(props, prototype)
58
54
  end
data/lib/ryo/reflect.rb CHANGED
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Ryo::Reflect Ryo::Reflect} module implements equivalents
5
- # from JavaScript's [`Relfect` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect),
6
- # and equivalents for some of the static methods on JavaScript's
7
- # [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) constructor.
4
+ # The {Ryo::Reflect Ryo::Reflect} module mirrors
5
+ # JavaScript's [`Relfect` object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect),
6
+ # and some of the static methods on JavaScript's
7
+ # [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
8
+ # as well.
8
9
  #
9
- # This module also implements Ryo-specific reflection features as well. The
10
- # instance methods of this module are available as singleton methods
11
- # on the {Ryo Ryo} module.
10
+ # {Ryo::Reflect Ryo::Reflect} also implements Ryo-specific
11
+ # reflection features. The instance methods of this module
12
+ # are available as singleton methods on the {Ryo Ryo}
13
+ # module.
12
14
  module Ryo::Reflect
13
15
  extend self
14
16
 
@@ -16,7 +18,7 @@ module Ryo::Reflect
16
18
  # @group JavaScript equivalents (Reflect)
17
19
 
18
20
  ##
19
- # Equivalent to JavaScript's `Reflect.getPrototypeOf`.
21
+ # Equivalent to JavaScript's `Reflect.getPrototypeOf`
20
22
  #
21
23
  # @param [Ryo] ryo
22
24
  # A Ryo object.
@@ -29,7 +31,7 @@ module Ryo::Reflect
29
31
  end
30
32
 
31
33
  ##
32
- # Equivalent to JavaScript's `Reflect.setPrototypeOf`.
34
+ # Equivalent to JavaScript's `Reflect.setPrototypeOf`
33
35
  #
34
36
  # @param [Ryo] ryo
35
37
  # A Ryo object.
@@ -45,17 +47,14 @@ module Ryo::Reflect
45
47
  end
46
48
 
47
49
  ##
48
- # Equivalent to JavaScript's `Reflect.defineProperty`.
50
+ # Equivalent to JavaScript's `Reflect.defineProperty`
49
51
  #
50
52
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
51
- # A Ryo object.
52
- #
53
+ # A Ryo object
53
54
  # @param [<String, #to_s>] property
54
- # The name of the property.
55
- #
55
+ # The name of a property
56
56
  # @param [Object, BasicObject] value
57
- # The value of the property.
58
- #
57
+ # The property's value
59
58
  # @return [void]
60
59
  def define_property(ryo, property, value)
61
60
  table, property = table_of(ryo), property.to_s
@@ -76,13 +75,12 @@ module Ryo::Reflect
76
75
 
77
76
  ##
78
77
  # Equivalent to JavaScript's `Reflect.ownKeys`, and
79
- # JavaScript's `Object.keys`.
78
+ # JavaScript's `Object.keys`
80
79
  #
81
80
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
82
- # A Ryo object.
83
- #
81
+ # A Ryo object
84
82
  # @return [Array<String>]
85
- # Returns the properties defined on a Ryo object.
83
+ # Returns the properties defined on a Ryo object
86
84
  def properties_of(ryo)
87
85
  table_of(ryo).keys
88
86
  end
@@ -97,13 +95,11 @@ module Ryo::Reflect
97
95
  # and `Object.prototype.hasOwnProperty`.
98
96
  #
99
97
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
100
- # A Ryo object.
101
- #
98
+ # A Ryo object
102
99
  # @param [<String, #to_s>] property
103
- # A property name.
104
- #
100
+ # The name of a property
105
101
  # @return [Boolean]
106
- # Returns true when **property** is a member of a Ryo object.
102
+ # Returns true when the property is a member of a Ryo object
107
103
  def property?(ryo, property)
108
104
  table_of(ryo).key?(property.to_s)
109
105
  end
@@ -111,14 +107,11 @@ module Ryo::Reflect
111
107
  ##
112
108
  # Equivalent to JavaScript's `Object.assign`.
113
109
  #
114
- #
115
110
  # @param [Ryo, Hash, #to_hash] target
116
- # The target object.
117
- #
111
+ # The target object
118
112
  # @param [Ryo, Hash, #to_hash] sources
119
113
  # A variable number of source objects that
120
- # will be merged into the target object.
121
- #
114
+ # will be merged with the target object
122
115
  # @return [Ryo]
123
116
  # Returns the modified target object.
124
117
  def assign(target, *sources)
@@ -133,30 +126,10 @@ module Ryo::Reflect
133
126
  # @group Ryo-specific
134
127
 
135
128
  ##
136
- # The {#delete!} method deletes a property from a Ryo object,
137
- # and from the prototypes in its prototype chain.
138
- #
139
- # @see Ryo::Keywords#delete
140
- #
141
129
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
142
- # A Ryo object.
143
- #
144
- # @param [<String, #to_s>] property
145
- # A property name.
146
- #
147
- # @return [void]
148
- def delete!(ryo, property)
149
- [ryo, *prototype_chain_of(ryo)].each do
150
- Ryo.delete(_1, property.to_s)
151
- end
152
- end
153
-
154
- ##
155
- # @param [<Ryo::Object, Ryo::BasicObject>] ryo
156
- # A Ryo object.
157
- #
130
+ # A Ryo object
158
131
  # @return [Array<Ryo::Object, Ryo::BasicObject>]
159
- # Returns the prototype chain of a Ryo object.
132
+ # Returns the prototype chain of a Ryo object
160
133
  def prototype_chain_of(ryo)
161
134
  prototypes = []
162
135
  loop do
@@ -169,20 +142,21 @@ module Ryo::Reflect
169
142
 
170
143
  ##
171
144
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
172
- # A Ryo object.
173
- #
145
+ # A Ryo object
174
146
  # @param [Boolean] recursive
175
147
  # When true, nested Ryo objects are replaced by
176
- # their table as well.
177
- #
148
+ # their table as well
178
149
  # @return [Hash]
179
- # Returns the table of a Ryo object.
150
+ # Returns the table of a Ryo object
180
151
  def table_of(ryo, recursive: false)
181
152
  table = kernel(:instance_variable_get).bind_call(ryo, :@_table)
182
153
  if recursive
183
154
  table.each do |key, value|
184
155
  if ryo?(value)
185
156
  table[key] = table_of(value, recursive:)
157
+ elsif value.respond_to?(:each)
158
+ table[key] = value.respond_to?(:each_pair) ?
159
+ value : value.map { table_of(_1, recursive:) }
186
160
  end
187
161
  end
188
162
  end
@@ -190,14 +164,12 @@ module Ryo::Reflect
190
164
  end
191
165
 
192
166
  ##
193
- # Sets the table of a Ryo object.
167
+ # Sets the table of a Ryo object
194
168
  #
195
169
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
196
- # A Ryo object.
197
- #
170
+ # A Ryo object
198
171
  # @param [Hash] table
199
- # The table to assign to a Ryo object.
200
- #
172
+ # The table
201
173
  # @return [nil]
202
174
  def set_table_of(ryo, table)
203
175
  kernel(:instance_variable_set)
@@ -207,51 +179,42 @@ module Ryo::Reflect
207
179
 
208
180
  ##
209
181
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
210
- # A Ryo object.
211
- #
182
+ # A Ryo object
212
183
  # @param [<String, Symbol>] method
213
- # The name of a method.
214
- #
184
+ # The name of a method
215
185
  # @param [::Object, ::BasicObject] args
216
- # Zero or more arguments to call **method** with.
217
- #
186
+ # Zero or more method arguments
218
187
  # @param [Proc] b
219
- # An optional block to pass to **method**.
220
- #
188
+ # An optional block
221
189
  # @return [::Object, ::BasicObject]
222
- # Returns the return value of the method call.
223
- def call_method(ryo, method, *, &b)
190
+ def call_method(ryo, method, *args, &b)
224
191
  kernel(:__send__)
225
- .bind_call(ryo, method, *, &b)
192
+ .bind_call(ryo, method, *args, &b)
226
193
  end
227
194
 
228
195
  ##
229
196
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
230
- # A Ryo object.
231
- #
197
+ # A Ryo object
232
198
  # @return [Class]
233
- # Returns the class of a Ryo object.
199
+ # Returns the class of a Ryo object
234
200
  def class_of(ryo)
235
201
  kernel(:class).bind_call(ryo)
236
202
  end
237
203
 
238
204
  ##
239
205
  # @param [Ryo::Function, Object, BasicObject] obj
240
- # An object.
241
- #
206
+ # An object
242
207
  # @return [Boolean]
243
- # Returns true when the given object is a Ryo function.
208
+ # Returns true when given a Ryo function
244
209
  def function?(obj)
245
210
  Ryo::Function === obj
246
211
  end
247
212
 
248
213
  ##
249
214
  # @param [Ryo::Function, Object, BasicObject] obj
250
- # An object.
251
- #
215
+ # An object
252
216
  # @return [Boolean]
253
- # Returns true when the given object is an instance
254
- # of {Ryo::Memo Ryo::Memo}.
217
+ # Returns true when given a Ryo memo
255
218
  def memo?(obj)
256
219
  Ryo::Memo === obj
257
220
  end
@@ -264,33 +227,29 @@ module Ryo::Reflect
264
227
  # Ryo.ryo?(Object.new) # => false
265
228
  #
266
229
  # @param [Object, BasicObject] obj
267
- # An object.
268
- #
230
+ # An object
269
231
  # @return [Boolean]
270
- # Returns true when the given object is a Ryo object.
232
+ # Returns true when given a Ryo object
271
233
  def ryo?(obj)
272
234
  Ryo === obj
273
235
  end
274
236
 
275
237
  ##
276
238
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo1
277
- # A Ryo object.
278
- #
239
+ # A Ryo object
279
240
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo2
280
- # A Ryo object.
281
- #
241
+ # A Ryo object
282
242
  # @return [Boolean]
283
- # Returns true when two Ryo objects are the same object.
243
+ # Returns true when the two Ryo objects are strictly equal
284
244
  def equal?(ryo1, ryo2)
285
245
  kernel(:equal?).bind_call(ryo1, ryo2)
286
246
  end
287
247
 
288
248
  ##
289
249
  # @param [<Ryo::Object, Ryo::BasicObject>] ryo
290
- # A Ryo object.
291
- #
250
+ # A Ryo object
292
251
  # @return [String]
293
- # Returns a String representation of a Ryo object.
252
+ # Returns a String representation of a Ryo object
294
253
  def inspect_object(ryo)
295
254
  format(
296
255
  "#<Ryo object=%{object} proto=%{proto} table=%{table}>",
@@ -301,80 +260,5 @@ module Ryo::Reflect
301
260
  end
302
261
  # @endgroup
303
262
 
304
- ##
305
- # @param [<Ryo::Object, Ryo::BasicObject>] ryo
306
- # A Ryo object.
307
- #
308
- # @param [<String, Symbol>] method
309
- # The name of the method.
310
- #
311
- # @param [Proc] b
312
- # The method's body.
313
- #
314
- # @private
315
- private def define_method!(ryo, method, &b)
316
- kernel(:define_singleton_method)
317
- .bind_call(ryo, method, &b)
318
- end
319
-
320
- ##
321
- # @param [<Ryo::Object, Ryo::BasicObject>] ryo
322
- # A Ryo object.
323
- #
324
- # @param [<String, #to_s>] property
325
- # The name of the property.
326
- #
327
- # @return [Boolean]
328
- # Returns true when the property has been
329
- # defined with a getter method.
330
- #
331
- # @private
332
- private def getter_defined?(ryo, property)
333
- kernel(:method)
334
- .bind_call(ryo, property)
335
- .source_location
336
- &.dig(0) == __FILE__
337
- end
338
-
339
- ##
340
- #
341
- # @param [<Ryo::Object, Ryo::BasicObject>] ryo
342
- # A Ryo object.
343
- #
344
- # @param [<String, #to_s>] property
345
- # The name of the property.
346
- #
347
- # @return [Boolean]
348
- # Returns true when the property has been
349
- # defined with a setter method.
350
- #
351
- # @private
352
- private def setter_defined?(ryo, property)
353
- getter_defined?(ryo, "#{property}=")
354
- end
355
-
356
- ##
357
- # @private
358
- private def merge!(obj1, obj2)
359
- obj1, obj2 = to_hash(obj1), to_hash(obj2)
360
- obj2.each { obj1[_1.to_s] = _2 }
361
- obj1
362
- end
363
-
364
- ##
365
- # @private
366
- private def to_hash(obj)
367
- if ryo?(obj)
368
- table_of(obj)
369
- else
370
- Hash.try_convert(obj)
371
- end
372
- end
373
-
374
- ##
375
- # @private
376
- def kernel(name)
377
- Module.instance_method(name)
378
- end
379
- # @endgroup
263
+ include Ryo::Utils
380
264
  end
data/lib/ryo/utils.rb ADDED
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The {Ryo::Utils Ryo::Utils} module provides utility
5
+ # methods that are internal to Ryo. This module
6
+ # and its methods should not be used directly.
7
+ # @api private
8
+ module Ryo::Utils
9
+ private
10
+
11
+ ##
12
+ # @param [<Ryo::Object, Ryo::BasicObject>] ryo
13
+ # A Ryo object
14
+ # @param [<String, Symbol>] method
15
+ # The name of a method
16
+ # @param [Proc] b
17
+ # The method's implementation
18
+ def define_method!(ryo, method, &b)
19
+ kernel(:define_singleton_method)
20
+ .bind_call(ryo, method, &b)
21
+ end
22
+
23
+ ##
24
+ # @param [<Ryo::Object, Ryo::BasicObject>] ryo
25
+ # A Ryo object
26
+ # @param [<String, #to_s>] property
27
+ # The name of a property
28
+ # @return [Boolean]
29
+ # Returns true for a Ryo property that has a
30
+ # corresponding getter method implemented by Ryo
31
+ def getter_defined?(ryo, property)
32
+ path = kernel(:method)
33
+ .bind_call(ryo, property)
34
+ .source_location
35
+ &.dig(0)
36
+ path ? File.dirname(path) == File.dirname(__FILE__) : false
37
+ end
38
+
39
+ ##
40
+ # @param [<Ryo::Object, Ryo::BasicObject>] ryo
41
+ # A Ryo object
42
+ # @param [<String, #to_s>] property
43
+ # The name of a property
44
+ # @return [Boolean]
45
+ # Returns true for a Ryo property that has a
46
+ # corresponding setter method implemented by Ryo
47
+ def setter_defined?(ryo, property)
48
+ getter_defined?(ryo, "#{property}=")
49
+ end
50
+
51
+ def merge!(obj1, obj2)
52
+ obj1, obj2 = to_hash(obj1), to_hash(obj2)
53
+ obj2.each { obj1[_1.to_s] = _2 }
54
+ obj1
55
+ end
56
+
57
+ def to_hash(obj)
58
+ if ryo?(obj)
59
+ table_of(obj)
60
+ else
61
+ Hash.try_convert(obj)
62
+ end
63
+ end
64
+
65
+ def kernel(name)
66
+ Module.instance_method(name)
67
+ end
68
+ end
data/lib/ryo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ryo
4
- VERSION = "0.5.5"
4
+ VERSION = "0.5.7"
5
5
  end
data/lib/ryo/yaml.rb CHANGED
@@ -1,45 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ##
4
- # The {Ryo::YAML Ryo::YAML} module provides a number of methods
5
- # for coercing YAML data into a Ryo object. It must be required
6
- # separately to Ryo (ie: require "ryo/yaml"), and the methods of
7
- # this module are then available on the {Ryo Ryo} module.
4
+ # The {Ryo::YAML Ryo::YAML} module provides a number of options
5
+ # for coercing YAML data into a Ryo object. The methods of
6
+ # this module are available as singleton methods on the {Ryo Ryo}
7
+ # module
8
8
  module Ryo::YAML
9
- require "yaml"
10
9
  extend self
11
10
 
12
11
  ##
13
12
  # @example
14
13
  # Ryo.from_yaml(path: "/foo/bar/baz.yaml")
15
14
  # Ryo.from_yaml(string: "---\nfoo: bar\n")
16
- #
17
15
  # @param [String] path
18
16
  # The path to a YAML file
19
- #
20
17
  # @param [String] string
21
18
  # A blob of YAML
22
- #
23
19
  # @param [Ryo] object
24
20
  # {Ryo::Object Ryo::Object}, or {Ryo::BasicObject Ryo::BasicObject}
25
21
  # Defaults to {Ryo::Object Ryo::Object}
26
- #
27
22
  # @raise [SystemCallError]
28
23
  # Might raise a number of Errno exceptions
29
- #
30
24
  # @return [Ryo::Object, Ryo::BasicObject]
31
25
  # Returns a Ryo object
32
26
  def from_yaml(path: nil, string: nil, object: Ryo::Object)
33
27
  if path && string
34
28
  raise ArgumentError, "Provide a path or string but not both"
35
29
  elsif path
30
+ require "yaml" unless defined?(YAML)
36
31
  object.from YAML.load_file(path)
37
32
  elsif string
33
+ require "yaml" unless defined?(YAML)
38
34
  object.from YAML.load(string)
39
35
  else
40
36
  raise ArgumentError, "No path or string provided"
41
37
  end
42
38
  end
43
-
44
39
  Ryo.extend(self)
45
40
  end