ryo.rb 0.4.4
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 +7 -0
- data/.github/workflows/specs.yml +23 -0
- data/.gitignore +6 -0
- data/.gitlab-ci.yml +9 -0
- data/.rubocop.yml +56 -0
- data/.yardoc-template/default/fulldoc/html/css/0x1eef.css +15 -0
- data/.yardoc-template/default/layout/html/setup.rb +5 -0
- data/.yardoc-template/default/module/setup.rb +7 -0
- data/.yardopts +4 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +373 -0
- data/Rakefile +3 -0
- data/lib/ryo/basic_object.rb +58 -0
- data/lib/ryo/builder.rb +106 -0
- data/lib/ryo/enumerable.rb +214 -0
- data/lib/ryo/function.rb +68 -0
- data/lib/ryo/keywords.rb +67 -0
- data/lib/ryo/lazy.rb +4 -0
- data/lib/ryo/object.rb +58 -0
- data/lib/ryo/reflect.rb +379 -0
- data/lib/ryo/version.rb +5 -0
- data/lib/ryo.rb +197 -0
- data/ryo.rb.gemspec +21 -0
- data/share/ryo.rb/examples/1.0_prototypes_point_object.rb +12 -0
- data/share/ryo.rb/examples/1.1_prototypes_ryo_fn.rb +14 -0
- data/share/ryo.rb/examples/2.0_iteration_each.rb +13 -0
- data/share/ryo.rb/examples/2.1_iteration_map.rb +16 -0
- data/share/ryo.rb/examples/2.2_iteration_ancestors.rb +13 -0
- data/share/ryo.rb/examples/3.0_recursion_ryo_from.rb +13 -0
- data/share/ryo.rb/examples/3.1_recursion_ryo_from_with_array.rb +19 -0
- data/share/ryo.rb/examples/3.2_recursion_ryo_from_with_openstruct.rb +14 -0
- data/share/ryo.rb/examples/4.0_basicobject_ryo_basicobject.rb +12 -0
- data/share/ryo.rb/examples/4.1_basicobject_ryo_basicobject_from.rb +13 -0
- data/share/ryo.rb/examples/5_collisions_resolution_strategy.rb +8 -0
- data/share/ryo.rb/examples/6_beyond_hash_objects.rb +20 -0
- data/share/ryo.rb/examples/7_ryo_lazy.rb +14 -0
- data/share/ryo.rb/examples/setup.rb +3 -0
- data/spec/readme_spec.rb +79 -0
- data/spec/ryo_basic_object_spec.rb +60 -0
- data/spec/ryo_enumerable_spec.rb +197 -0
- data/spec/ryo_keywords_spec.rb +86 -0
- data/spec/ryo_object_spec.rb +71 -0
- data/spec/ryo_prototypes_spec.rb +45 -0
- data/spec/ryo_reflect_spec.rb +175 -0
- data/spec/ryo_spec.rb +130 -0
- data/spec/setup.rb +5 -0
- metadata +173 -0
data/lib/ryo/reflect.rb
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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.
|
8
|
+
#
|
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.
|
12
|
+
module Ryo::Reflect
|
13
|
+
extend self
|
14
|
+
|
15
|
+
##
|
16
|
+
# @group JavaScript equivalents (Reflect)
|
17
|
+
|
18
|
+
##
|
19
|
+
# Equivalent to JavaScript's `Reflect.getPrototypeOf`.
|
20
|
+
#
|
21
|
+
# @param [Ryo] ryo
|
22
|
+
# A Ryo object.
|
23
|
+
#
|
24
|
+
# @return [Ryo, nil]
|
25
|
+
# Returns the prototype of the *ryo* object.
|
26
|
+
def prototype_of(ryo)
|
27
|
+
kernel(:instance_variable_get)
|
28
|
+
.bind_call(ryo, :@_proto)
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Equivalent to JavaScript's `Reflect.setPrototypeOf`.
|
33
|
+
#
|
34
|
+
# @param [Ryo] ryo
|
35
|
+
# A Ryo object.
|
36
|
+
#
|
37
|
+
# @param [Ryo] prototype
|
38
|
+
# The prototype to assign to *ryo*.
|
39
|
+
#
|
40
|
+
# @return [nil]
|
41
|
+
def set_prototype_of(ryo, prototype)
|
42
|
+
kernel(:instance_variable_set)
|
43
|
+
.bind_call(ryo, :@_proto, prototype)
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Equivalent to JavaScript's `Reflect.defineProperty`.
|
49
|
+
#
|
50
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
51
|
+
# A Ryo object.
|
52
|
+
#
|
53
|
+
# @param [<String, #to_s>] property
|
54
|
+
# The name of the property.
|
55
|
+
#
|
56
|
+
# @param [Object, BasicObject] value
|
57
|
+
# The value of the property.
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
def define_property(ryo, property, value)
|
61
|
+
table, property = table_of(ryo), property.to_s
|
62
|
+
kernel(:tap).bind_call(value) { _1.bind!(ryo) if function?(_1) }
|
63
|
+
table[property] = value
|
64
|
+
# Define setter
|
65
|
+
if !setter_defined?(ryo, property) && property[-1] != "?"
|
66
|
+
define_method!(ryo, "#{property}=") { ryo[property] = _1 }
|
67
|
+
end
|
68
|
+
# Define getter
|
69
|
+
return if getter_defined?(ryo, property)
|
70
|
+
define_method!(ryo, property) { |*args, &b|
|
71
|
+
(args.empty? && b.nil?) ? ryo[property] :
|
72
|
+
super(*args, &b)
|
73
|
+
}
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Equivalent to JavaScript's `Reflect.ownKeys`, and
|
79
|
+
# JavaScript's `Object.keys`.
|
80
|
+
#
|
81
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
82
|
+
# A Ryo object.
|
83
|
+
#
|
84
|
+
# @return [Array<String>]
|
85
|
+
# Returns the properties defined on a Ryo object.
|
86
|
+
def properties_of(ryo)
|
87
|
+
table_of(ryo).keys
|
88
|
+
end
|
89
|
+
|
90
|
+
# @endgroup
|
91
|
+
|
92
|
+
##
|
93
|
+
# @group JavaScript equivalents (Object)
|
94
|
+
|
95
|
+
##
|
96
|
+
# Equivalent to JavaScript's `Object.hasOwn`,
|
97
|
+
# and `Object.prototype.hasOwnProperty`.
|
98
|
+
#
|
99
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
100
|
+
# A Ryo object.
|
101
|
+
#
|
102
|
+
# @param [<String, #to_s>] property
|
103
|
+
# A property name.
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
106
|
+
# Returns true when **property** is a member of a Ryo object.
|
107
|
+
def property?(ryo, property)
|
108
|
+
table_of(ryo).key?(property.to_s)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Equivalent to JavaScript's `Object.assign`.
|
113
|
+
#
|
114
|
+
#
|
115
|
+
# @param [Ryo, Hash, #to_hash] target
|
116
|
+
# The target object.
|
117
|
+
#
|
118
|
+
# @param [Ryo, Hash, #to_hash] sources
|
119
|
+
# A variable number of source objects that
|
120
|
+
# will be merged into the target object.
|
121
|
+
#
|
122
|
+
# @return [Ryo]
|
123
|
+
# Returns the modified target object.
|
124
|
+
def assign(target, *sources)
|
125
|
+
sources.each do |source|
|
126
|
+
to_hash(source).each { target[_1.to_s] = _2 }
|
127
|
+
end
|
128
|
+
target
|
129
|
+
end
|
130
|
+
# @endgroup
|
131
|
+
|
132
|
+
##
|
133
|
+
# @group Ryo-specific
|
134
|
+
|
135
|
+
##
|
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
|
+
# @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
|
+
#
|
158
|
+
# @return [Array<Ryo::Object, Ryo::BasicObject>]
|
159
|
+
# Returns the prototype chain of a Ryo object.
|
160
|
+
def prototype_chain_of(ryo)
|
161
|
+
prototypes = []
|
162
|
+
loop do
|
163
|
+
ryo = prototype_of(ryo)
|
164
|
+
break unless ryo
|
165
|
+
prototypes.push(ryo)
|
166
|
+
end
|
167
|
+
prototypes
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
172
|
+
# A Ryo object.
|
173
|
+
#
|
174
|
+
# @param [Boolean] recursive
|
175
|
+
# When true, nested Ryo objects are replaced by
|
176
|
+
# their table as well.
|
177
|
+
#
|
178
|
+
# @return [Hash]
|
179
|
+
# Returns the table of a Ryo object.
|
180
|
+
def table_of(ryo, recursive: false)
|
181
|
+
table = kernel(:instance_variable_get).bind_call(ryo, :@_table)
|
182
|
+
if recursive
|
183
|
+
table.each do |key, value|
|
184
|
+
if ryo?(value)
|
185
|
+
table[key] = table_of(value, recursive:)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
table
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Sets the table of a Ryo object.
|
194
|
+
#
|
195
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
196
|
+
# A Ryo object.
|
197
|
+
#
|
198
|
+
# @param [Hash] table
|
199
|
+
# The table to assign to a Ryo object.
|
200
|
+
#
|
201
|
+
# @return [nil]
|
202
|
+
def set_table_of(ryo, table)
|
203
|
+
kernel(:instance_variable_set)
|
204
|
+
.bind_call(ryo, :@_table, table)
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
210
|
+
# A Ryo object.
|
211
|
+
#
|
212
|
+
# @param [<String, Symbol>] method
|
213
|
+
# The name of a method.
|
214
|
+
#
|
215
|
+
# @param [::Object, ::BasicObject] args
|
216
|
+
# Zero or more arguments to call **method** with.
|
217
|
+
#
|
218
|
+
# @param [Proc] b
|
219
|
+
# An optional block to pass to **method**.
|
220
|
+
#
|
221
|
+
# @return [::Object, ::BasicObject]
|
222
|
+
# Returns the return value of the method call.
|
223
|
+
def call_method(ryo, method, *args, &b)
|
224
|
+
kernel(:__send__)
|
225
|
+
.bind_call(ryo, method, *args, &b)
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
230
|
+
# A Ryo object.
|
231
|
+
#
|
232
|
+
# @return [Class]
|
233
|
+
# Returns the class of a Ryo object.
|
234
|
+
def class_of(ryo)
|
235
|
+
kernel(:class).bind_call(ryo)
|
236
|
+
end
|
237
|
+
|
238
|
+
##
|
239
|
+
# @param [Ryo::Function, Object, BasicObject] obj
|
240
|
+
# An object.
|
241
|
+
#
|
242
|
+
# @return [Boolean]
|
243
|
+
# Returns true when the given object is a Ryo function.
|
244
|
+
def function?(obj)
|
245
|
+
Ryo::Function === obj
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# @param [Ryo::Function, Object, BasicObject] obj
|
250
|
+
# An object.
|
251
|
+
#
|
252
|
+
# @return [Boolean]
|
253
|
+
# Returns true when the given object is an instance
|
254
|
+
# of {Ryo::Lazy Ryo::Lazy}.
|
255
|
+
def lazy?(obj)
|
256
|
+
Ryo::Lazy === obj
|
257
|
+
end
|
258
|
+
|
259
|
+
##
|
260
|
+
# @example
|
261
|
+
# Ryo.ryo?(Ryo::Object(x: 5, y: 12)) # => true
|
262
|
+
# Ryo.ryo?(Ryo::BasicObject(x: 10, y: 20)) # => true
|
263
|
+
# Ryo.ryo?(Object.new) # => false
|
264
|
+
#
|
265
|
+
# @param [Object, BasicObject] obj
|
266
|
+
# An object.
|
267
|
+
#
|
268
|
+
# @return [Boolean]
|
269
|
+
# Returns true when the given object is a Ryo object.
|
270
|
+
def ryo?(obj)
|
271
|
+
Ryo === obj
|
272
|
+
end
|
273
|
+
|
274
|
+
##
|
275
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo1
|
276
|
+
# A Ryo object.
|
277
|
+
#
|
278
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo2
|
279
|
+
# A Ryo object.
|
280
|
+
#
|
281
|
+
# @return [Boolean]
|
282
|
+
# Returns true when two Ryo objects are the same object.
|
283
|
+
def equal?(ryo1, ryo2)
|
284
|
+
kernel(:equal?).bind_call(ryo1, ryo2)
|
285
|
+
end
|
286
|
+
|
287
|
+
##
|
288
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
289
|
+
# A Ryo object.
|
290
|
+
#
|
291
|
+
# @return [String]
|
292
|
+
# Returns a String representation of a Ryo object.
|
293
|
+
def inspect_object(ryo)
|
294
|
+
format(
|
295
|
+
"#<Ryo object=%{object} proto=%{proto} table=%{table}>",
|
296
|
+
object: Object.instance_method(:to_s).bind_call(ryo),
|
297
|
+
proto: prototype_of(ryo).inspect,
|
298
|
+
table: table_of(ryo).inspect
|
299
|
+
)
|
300
|
+
end
|
301
|
+
# @endgroup
|
302
|
+
|
303
|
+
##
|
304
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
305
|
+
# A Ryo object.
|
306
|
+
#
|
307
|
+
# @param [<String, Symbol>] method
|
308
|
+
# The name of the method.
|
309
|
+
#
|
310
|
+
# @param [Proc] b
|
311
|
+
# The method's body.
|
312
|
+
#
|
313
|
+
# @private
|
314
|
+
private def define_method!(ryo, method, &b)
|
315
|
+
kernel(:define_singleton_method)
|
316
|
+
.bind_call(ryo, method, &b)
|
317
|
+
end
|
318
|
+
|
319
|
+
##
|
320
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
321
|
+
# A Ryo object.
|
322
|
+
#
|
323
|
+
# @param [<String, #to_s>] property
|
324
|
+
# The name of the property.
|
325
|
+
#
|
326
|
+
# @return [Boolean]
|
327
|
+
# Returns true when the property has been
|
328
|
+
# defined with a getter method.
|
329
|
+
#
|
330
|
+
# @private
|
331
|
+
private def getter_defined?(ryo, property)
|
332
|
+
kernel(:method)
|
333
|
+
.bind_call(ryo, property)
|
334
|
+
.source_location
|
335
|
+
&.dig(0) == __FILE__
|
336
|
+
end
|
337
|
+
|
338
|
+
##
|
339
|
+
#
|
340
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
341
|
+
# A Ryo object.
|
342
|
+
#
|
343
|
+
# @param [<String, #to_s>] property
|
344
|
+
# The name of the property.
|
345
|
+
#
|
346
|
+
# @return [Boolean]
|
347
|
+
# Returns true when the property has been
|
348
|
+
# defined with a setter method.
|
349
|
+
#
|
350
|
+
# @private
|
351
|
+
private def setter_defined?(ryo, property)
|
352
|
+
getter_defined?(ryo, "#{property}=")
|
353
|
+
end
|
354
|
+
|
355
|
+
##
|
356
|
+
# @private
|
357
|
+
private def merge!(obj1, obj2)
|
358
|
+
obj1, obj2 = to_hash(obj1), to_hash(obj2)
|
359
|
+
obj2.each { obj1[_1.to_s] = _2 }
|
360
|
+
obj1
|
361
|
+
end
|
362
|
+
|
363
|
+
##
|
364
|
+
# @private
|
365
|
+
private def to_hash(obj)
|
366
|
+
if ryo?(obj)
|
367
|
+
table_of(obj)
|
368
|
+
else
|
369
|
+
Hash.try_convert(obj)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# @private
|
375
|
+
def kernel(name)
|
376
|
+
Module.instance_method(name)
|
377
|
+
end
|
378
|
+
# @endgroup
|
379
|
+
end
|
data/lib/ryo/version.rb
ADDED
data/lib/ryo.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# The {Ryo Ryo} module implements most of its behavior as singleton methods
|
5
|
+
# that are inherited from the {Ryo::Reflect Ryo::Reflect}, and
|
6
|
+
# {Ryo::Keywords Ryo:Keywords} modules.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # Ryo.delete
|
10
|
+
# point = Ryo(x: 0, y: 0)
|
11
|
+
# Ryo.delete(point, "x")
|
12
|
+
# point.x # => nil
|
13
|
+
#
|
14
|
+
# # Ryo.assign
|
15
|
+
# point = Ryo.assign(Ryo({}), {x: 0}, {y: 0})
|
16
|
+
# point.x # => 0
|
17
|
+
module Ryo
|
18
|
+
require_relative "ryo/reflect"
|
19
|
+
require_relative "ryo/keywords"
|
20
|
+
require_relative "ryo/builder"
|
21
|
+
require_relative "ryo/basic_object"
|
22
|
+
require_relative "ryo/object"
|
23
|
+
require_relative "ryo/function"
|
24
|
+
require_relative "ryo/lazy"
|
25
|
+
require_relative "ryo/enumerable"
|
26
|
+
|
27
|
+
extend Ryo::Reflect
|
28
|
+
extend Ryo::Keywords
|
29
|
+
extend Ryo::Enumerable
|
30
|
+
|
31
|
+
##
|
32
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
33
|
+
# A Ryo object.
|
34
|
+
#
|
35
|
+
# @param [Module] mod
|
36
|
+
# A module to extend a Ryo object with.
|
37
|
+
#
|
38
|
+
# @return [<Ryo::Object, Ryo::BasicObject>]
|
39
|
+
# Returns a Ryo object extended by **mod**.
|
40
|
+
def self.extend!(ryo, mod)
|
41
|
+
kernel(:extend).bind_call(ryo, mod)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Duplicates a Ryo object, and its prototype(s).
|
46
|
+
#
|
47
|
+
# @param [<Ryo::Object, Ryo::BasicObject>] ryo
|
48
|
+
# A Ryo object.
|
49
|
+
#
|
50
|
+
# @return [<Ryo::Object, Ryo::BasicObject>]
|
51
|
+
# Returns a duplicated Ryo object.
|
52
|
+
def self.dup(ryo)
|
53
|
+
duplicate = extend!(
|
54
|
+
kernel(:dup).bind_call(ryo),
|
55
|
+
self
|
56
|
+
)
|
57
|
+
if proto = prototype_of(duplicate)
|
58
|
+
set_prototype_of(duplicate, dup(proto))
|
59
|
+
end
|
60
|
+
duplicate
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Creates a lazy Ryo value.
|
65
|
+
#
|
66
|
+
# @param [Proc] b
|
67
|
+
# A proc that is evaluated when a property is first accessed.
|
68
|
+
#
|
69
|
+
# @return [Ryo::Lazy]
|
70
|
+
# Returns an instance of {Ryo::Lazy Ryo::Lazy}.
|
71
|
+
def self.lazy(&b)
|
72
|
+
Ryo::Lazy.new(&b)
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Creates a Ryo object by recursively walking a Hash object.
|
77
|
+
#
|
78
|
+
# @param props (see Ryo::Builder.build)
|
79
|
+
# @param prototype (see Ryo::Builder.build)
|
80
|
+
#
|
81
|
+
# @return [Ryo::Object]
|
82
|
+
# Returns an instance of {Ryo::Object Ryo::Object}.
|
83
|
+
def self.from(props, prototype = nil)
|
84
|
+
Ryo::Object.from(props, prototype)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Returns the prototype of self, or "nil" if self has no prototype.
|
89
|
+
#
|
90
|
+
# @return [<Ryo::Object, Ryo::BasicObject>, nil]
|
91
|
+
def __proto__
|
92
|
+
@_proto
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# @param [String] property
|
97
|
+
# A property name.
|
98
|
+
#
|
99
|
+
# @return [<Object, BasicObject>, nil]
|
100
|
+
# Returns the value at **property**, or nil.
|
101
|
+
#
|
102
|
+
# @note
|
103
|
+
# This method will first try to read **property** from self, and if
|
104
|
+
# it is not found on self the chain of prototypes will be traversed
|
105
|
+
# through instead.
|
106
|
+
def [](property)
|
107
|
+
property = property.to_s
|
108
|
+
if Ryo.property?(self, property)
|
109
|
+
v = @_table[property]
|
110
|
+
Ryo.lazy?(v) ? self[property] = v.call : v
|
111
|
+
else
|
112
|
+
return unless @_proto
|
113
|
+
Ryo.call_method(@_proto, property)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Assigns a property to self.
|
119
|
+
#
|
120
|
+
# @param [String] property
|
121
|
+
# A property name.
|
122
|
+
#
|
123
|
+
# @param [<Object,BasicObject>] value
|
124
|
+
# The value.
|
125
|
+
#
|
126
|
+
# @return [void]
|
127
|
+
def []=(property, value)
|
128
|
+
Ryo.define_property(self, property.to_s, value)
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# @param [<Ryo::Object, Ryo::BasicObject>, Hash, #to_h] other
|
133
|
+
# An object to compare against.
|
134
|
+
#
|
135
|
+
# @return [Boolean]
|
136
|
+
# Returns true **other** is equal to self.
|
137
|
+
def ==(other)
|
138
|
+
if Ryo.ryo?(other)
|
139
|
+
@_table == Ryo.table_of(other)
|
140
|
+
else
|
141
|
+
other = Hash.try_convert(other)
|
142
|
+
return false unless other
|
143
|
+
@_table == other.map { [_1.to_s, _2] }.to_h
|
144
|
+
end
|
145
|
+
end
|
146
|
+
alias_method :eql?, :==
|
147
|
+
|
148
|
+
##
|
149
|
+
# @return [String]
|
150
|
+
# Returns a String representation of a Ryo object.
|
151
|
+
def inspect
|
152
|
+
Ryo.inspect_object(self)
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# @private
|
157
|
+
def pretty_print(q)
|
158
|
+
q.text(inspect)
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# @private
|
163
|
+
def respond_to?(property, include_all = false)
|
164
|
+
respond_to_missing?(property, include_all)
|
165
|
+
end
|
166
|
+
|
167
|
+
##
|
168
|
+
# @private
|
169
|
+
def respond_to_missing?(property, include_all = false)
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# @private
|
175
|
+
def method_missing(name, *args, &b)
|
176
|
+
property = name.to_s
|
177
|
+
if property[-1] == "="
|
178
|
+
property = property[0..-2]
|
179
|
+
self[property] = args.first
|
180
|
+
elsif Ryo.property?(self, property)
|
181
|
+
self[property]
|
182
|
+
elsif @_proto
|
183
|
+
Ryo.call_method(@_proto, name, *args, &b)
|
184
|
+
.tap { _1.bind!(self) if Ryo.function?(_1) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# @param props (see Ryo::Builder.build)
|
191
|
+
# @param prototype (see Ryo::Builder.build)
|
192
|
+
#
|
193
|
+
# @return [Ryo::Object]
|
194
|
+
# Returns a Ryo object.
|
195
|
+
def Ryo(props, prototype = nil)
|
196
|
+
Ryo::Object.create(props, prototype)
|
197
|
+
end
|
data/ryo.rb.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./lib/ryo/version"
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "ryo.rb"
|
6
|
+
gem.authors = ["0x1eef"]
|
7
|
+
gem.email = ["0x1eef@protonmail.com"]
|
8
|
+
gem.homepage = "https://github.com/0x1eef/ryo.rb#readme"
|
9
|
+
gem.version = Ryo::VERSION
|
10
|
+
gem.licenses = ["MIT"]
|
11
|
+
gem.files = `git ls-files`.split($/)
|
12
|
+
gem.require_paths = ["lib"]
|
13
|
+
gem.description = "Ryo implements prototype-based inheritance, in Ruby."
|
14
|
+
gem.summary = gem.description
|
15
|
+
gem.add_development_dependency "yard", "~> 0.9"
|
16
|
+
gem.add_development_dependency "redcarpet", "~> 3.5"
|
17
|
+
gem.add_development_dependency "rspec", "~> 3.10"
|
18
|
+
gem.add_development_dependency "rubocop-rspec", "~> 2.12"
|
19
|
+
gem.add_development_dependency "standard", "~> 1.9"
|
20
|
+
gem.add_development_dependency "test-cmd.rb", "~> 0.4"
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
require "ryo"
|
5
|
+
|
6
|
+
point_x = Ryo(x: 5)
|
7
|
+
point_y = Ryo({y: 10}, point_x)
|
8
|
+
point = Ryo({
|
9
|
+
multiply: Ryo.fn { |m| [x * m, y * m] }
|
10
|
+
}, point_y)
|
11
|
+
p point.multiply.call(2)
|
12
|
+
|
13
|
+
##
|
14
|
+
# [10, 20]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
require "ryo"
|
5
|
+
|
6
|
+
point_x = Ryo(x: 2)
|
7
|
+
point_y = Ryo({y: 4}, point_x)
|
8
|
+
point = Ryo({}, point_y)
|
9
|
+
|
10
|
+
Ryo.map!(point) { |key, value| value * 2 }
|
11
|
+
p [point.x, point.y]
|
12
|
+
p [point_x.x, point_y.y]
|
13
|
+
|
14
|
+
##
|
15
|
+
# [4, 8]
|
16
|
+
# [4, 8]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
require "ryo"
|
5
|
+
|
6
|
+
point_x = Ryo(x: 5)
|
7
|
+
point_y = Ryo({y: 10}, point_x)
|
8
|
+
point = Ryo({}, point_y)
|
9
|
+
|
10
|
+
p Ryo.find(point, ancestors: 0) { |k,v| v == 5 } # => nil
|
11
|
+
p Ryo.find(point, ancestors: 1) { |k,v| v == 5 } # => nil
|
12
|
+
p Ryo.find(point, ancestors: 2) { |k,v| v == 5 }.x # => point_x.x
|
13
|
+
p Ryo.find(point) { |k,v| v == 5 }.x # => point_x.x
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "setup"
|
4
|
+
require "ryo"
|
5
|
+
|
6
|
+
points = Ryo.from([
|
7
|
+
{x: {to_i: 2}},
|
8
|
+
"foobar",
|
9
|
+
{y: {to_i: 4}}
|
10
|
+
])
|
11
|
+
|
12
|
+
p points[0].x.to_i
|
13
|
+
p points[1]
|
14
|
+
p points[2].y.to_i
|
15
|
+
|
16
|
+
##
|
17
|
+
# 2
|
18
|
+
# "foobar"
|
19
|
+
# 4
|