ryo.rb 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 61ffcab409332ea4c6979e302c9d27f467575ef2b8167f07805e7f20ad523002
|
4
|
+
data.tar.gz: fb5f3050e9a373616e377a5d76c0605f7ce6e655ca8a2822619dd63d7426182d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1158833ffda85f18fabdb7f7830fe0a104f91bfe5d19d2f0d8a21e51ac5d95d34dd6744d100b0ba6e0c5c6ece7ecdd38a80f59f2ceb55898403d490de9be14b6
|
7
|
+
data.tar.gz: c92c24f793503360539dc314c1c15d9349f23efee1cc29ca9af2dab497528d1b7186f77ee839f7ce36592c223ae6a4b8bdd058cb00091361a098268bad0e5af3
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: Ryo specs
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ main ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
specs:
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
os: [ubuntu-latest]
|
15
|
+
ruby: [3.1, 3.2]
|
16
|
+
runs-on: ${{ matrix.os }}
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
22
|
+
- run: bundle install
|
23
|
+
- run: rspec -Ilib -rryo spec/
|
data/.gitignore
ADDED
data/.gitlab-ci.yml
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
##
|
2
|
+
# Plugins
|
3
|
+
require:
|
4
|
+
- standard
|
5
|
+
- rubocop-rspec
|
6
|
+
|
7
|
+
##
|
8
|
+
# Defaults: standard-rb
|
9
|
+
inherit_gem:
|
10
|
+
standard: config/base.yml
|
11
|
+
|
12
|
+
##
|
13
|
+
# Enabled cops
|
14
|
+
Style/FrozenStringLiteralComment:
|
15
|
+
Enabled: true
|
16
|
+
|
17
|
+
##
|
18
|
+
# Disabled cops
|
19
|
+
Layout/MultilineMethodCallIndentation:
|
20
|
+
Enabled: false
|
21
|
+
Style/LambdaCall:
|
22
|
+
Enabled: false
|
23
|
+
Lint/AssignmentInCondition:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
##
|
27
|
+
# Disabled cops (rspec)
|
28
|
+
RSpec/FilePath:
|
29
|
+
Enabled: false
|
30
|
+
RSpec/NestedGroups:
|
31
|
+
Enabled: false
|
32
|
+
RSpec/NotToNot:
|
33
|
+
Enabled: false
|
34
|
+
RSpec/EmptyLineAfterHook:
|
35
|
+
Enabled: false
|
36
|
+
RSpec/EmptyLineAfterSubject:
|
37
|
+
Enabled: false
|
38
|
+
RSpec/DescribedClass:
|
39
|
+
Enabled: false
|
40
|
+
RSpec/MultipleExpectations:
|
41
|
+
Enabled: false
|
42
|
+
RSpec/EmptyLineAfterFinalLet:
|
43
|
+
Enabled: false
|
44
|
+
Style/NilComparison:
|
45
|
+
Exclude:
|
46
|
+
- spec/ryo_object_spec.rb
|
47
|
+
RSpec/DescribeClass:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
AllCops:
|
51
|
+
Include:
|
52
|
+
- 'lib/*.rb'
|
53
|
+
- 'lib/**/*.rb'
|
54
|
+
- 'spec/*.rb'
|
55
|
+
- 'spec/**/*.rb'
|
56
|
+
TargetRubyVersion: 3.2
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright 2022
|
4
|
+
0x1eef
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,373 @@
|
|
1
|
+
## About
|
2
|
+
|
3
|
+
Ryo implements prototype-based inheritance, in Ruby.
|
4
|
+
|
5
|
+
Ryo's implementation of prototype-based inheritance offers
|
6
|
+
a flexible approach for establishing object relationships,
|
7
|
+
and building configuration objects. Ryo can also act as a
|
8
|
+
recursive OpenStruct alternative. JavaScript's implementation of
|
9
|
+
prototype-based inheritance served as a reference point
|
10
|
+
for Ryo's implementation.
|
11
|
+
|
12
|
+
## Examples
|
13
|
+
|
14
|
+
### Prototypes
|
15
|
+
|
16
|
+
#### Point object
|
17
|
+
|
18
|
+
The following example demonstrates how prototype-based inheritance is
|
19
|
+
implemented in Ryo. The example introduces three objects to form a
|
20
|
+
single point object with the properties, "x" and "y". The
|
21
|
+
[Ryo()](https://0x1eef.github.io/x/ryo.rb/top-level-namespace.html#Ryo-instance_method)
|
22
|
+
method seen in the example returns an instance of
|
23
|
+
[Ryo::Object](https://0x1eef.github.io/x/ryo.rb/Ryo/Object.html):
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require "ryo"
|
27
|
+
|
28
|
+
point_x = Ryo(x: 5)
|
29
|
+
point_y = Ryo({y: 10}, point_x)
|
30
|
+
point = Ryo({}, point_y)
|
31
|
+
p [point.x, point.y]
|
32
|
+
|
33
|
+
##
|
34
|
+
# [5, 10]
|
35
|
+
```
|
36
|
+
|
37
|
+
#### Ryo.fn
|
38
|
+
|
39
|
+
The following example demonstrates a Ryo function.
|
40
|
+
[`Ryo.fn`](https://0x1eef.github.io/x/ryo.rb/Ryo/Keywords.html#function-instance_method)
|
41
|
+
will bind its `self` to the Ryo object it is assigned to, and when the function
|
42
|
+
is called it will have access to the properties of the Ryo object:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require "ryo"
|
46
|
+
|
47
|
+
point_x = Ryo(x: 5)
|
48
|
+
point_y = Ryo({y: 10}, point_x)
|
49
|
+
point = Ryo({
|
50
|
+
multiply: Ryo.fn { |m| [x * m, y * m] }
|
51
|
+
}, point_y)
|
52
|
+
p point.multiply.call(2)
|
53
|
+
|
54
|
+
##
|
55
|
+
# [10, 20]
|
56
|
+
```
|
57
|
+
|
58
|
+
#### Ryo.lazy
|
59
|
+
|
60
|
+
The following example demonstrates a lazy Ryo value.
|
61
|
+
[`Ryo.lazy`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#lazy-class_method)
|
62
|
+
creates a lazy value that is not evaluated until a property is accessed
|
63
|
+
for the first time. It is similar to a Ryo function but it does not require
|
64
|
+
that the `#call` method be used, and after the property is accessed for the
|
65
|
+
first time the lazy value is replaced by the evaluated value:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
require "ryo"
|
69
|
+
|
70
|
+
point_x = Ryo(x: Ryo.lazy { 5 })
|
71
|
+
point_y = Ryo({y: Ryo.lazy { 10 }}, point_x)
|
72
|
+
point = Ryo({sum: Ryo.lazy { x + y }}, point_y)
|
73
|
+
print "point.x = ", point.x, "\n"
|
74
|
+
print "point.y = ", point.y, "\n"
|
75
|
+
print "point.sum = ", point.sum, "\n"
|
76
|
+
|
77
|
+
##
|
78
|
+
# point.x = 5
|
79
|
+
# point.y = 10
|
80
|
+
# point.sum = 15
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
### Iteration
|
85
|
+
|
86
|
+
#### Ryo.each
|
87
|
+
|
88
|
+
The
|
89
|
+
[`Ryo.each`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#each-class_method)
|
90
|
+
method can iterate through the properties of a Ryo object, and
|
91
|
+
its prototype(s). Ryo is designed to not mix its implementation
|
92
|
+
with the objects it creates - that's why
|
93
|
+
[`Ryo.each`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#each-class_method)
|
94
|
+
is not implemented directly on a Ryo object.
|
95
|
+
|
96
|
+
A demonstration of [`Ryo.each`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#each-class_method):
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require "ryo"
|
100
|
+
|
101
|
+
point = Ryo(x: 10, y: 20)
|
102
|
+
Ryo.each(point) do |key, value|
|
103
|
+
p [key, value]
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# ["x", 10]
|
108
|
+
# ["y", 20]
|
109
|
+
```
|
110
|
+
|
111
|
+
#### Ryo.map!
|
112
|
+
|
113
|
+
[`Ryo::Enumerable`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html)
|
114
|
+
methods can return a new copy of a Ryo object and its prototypes, or mutate
|
115
|
+
a Ryo object and its prototypes in-place. The following example demonstrates
|
116
|
+
an in-place map operation on a Ryo object with
|
117
|
+
[`Ryo.map!`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html#map!-instance_method).
|
118
|
+
The counterpart of
|
119
|
+
[`Ryo.map!`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html#map!-instance_method)
|
120
|
+
is
|
121
|
+
[`Ryo.map`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html#map-instance_method),
|
122
|
+
and it returns a new copy of a Ryo object and its prototypes.
|
123
|
+
|
124
|
+
A demonstration of [`Ryo.map!`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html#map!-instance_method):
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
require "ryo"
|
128
|
+
|
129
|
+
point_x = Ryo(x: 2)
|
130
|
+
point_y = Ryo({y: 4}, point_x)
|
131
|
+
point = Ryo({}, point_y)
|
132
|
+
|
133
|
+
Ryo.map!(point) { |key, value| value * 2 }
|
134
|
+
p [point.x, point.y]
|
135
|
+
p [point_x.x, point_y.y]
|
136
|
+
|
137
|
+
##
|
138
|
+
# [4, 8]
|
139
|
+
# [4, 8]
|
140
|
+
```
|
141
|
+
|
142
|
+
#### Ancestors
|
143
|
+
|
144
|
+
All [`Ryo::Enumerable`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html)
|
145
|
+
methods support an optional `ancestors` option.
|
146
|
+
|
147
|
+
`ancestors` is an integer that determines how far up the prototype chain a
|
148
|
+
[`Ryo::Enumerable`](https://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html)
|
149
|
+
method can go. 0 covers a Ryo object, and none of the prototypes in its
|
150
|
+
prototype chain. 1 covers a Ryo object, and one of the prototypes in its
|
151
|
+
prototype chain - and so on.
|
152
|
+
|
153
|
+
When the `ancestors` option is not provided, the default behavior of
|
154
|
+
[`Ryo::Enumerable`](http://0x1eef.github.io/x/ryo.rb/Ryo/Enumerable.html)
|
155
|
+
methods is to traverse the entire prototype chain. The following example
|
156
|
+
demonstrates using the `ancestors` option with
|
157
|
+
[`Ryo.find`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#find-class_method):
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
require "ryo"
|
161
|
+
|
162
|
+
point_x = Ryo(x: 5)
|
163
|
+
point_y = Ryo({y: 10}, point_x)
|
164
|
+
point = Ryo({}, point_y)
|
165
|
+
|
166
|
+
p Ryo.find(point, ancestors: 0) { |k,v| v == 5 } # => nil
|
167
|
+
p Ryo.find(point, ancestors: 1) { |k,v| v == 5 } # => nil
|
168
|
+
p Ryo.find(point, ancestors: 2) { |k,v| v == 5 }.x # => point_x.x
|
169
|
+
p Ryo.find(point) { |k,v| v == 5 }.x # => point_x.x
|
170
|
+
```
|
171
|
+
|
172
|
+
### Recursion
|
173
|
+
|
174
|
+
#### Ryo.from
|
175
|
+
|
176
|
+
The [`Ryo.from`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#from-class_method) method has
|
177
|
+
the same interface as the [`Ryo`](https://0x1eef.github.io/x/ryo.rb/top-level-namespace.html#Ryo-instance_method)
|
178
|
+
method, but it is implemented to recursively walk a Hash object and create Ryo objects
|
179
|
+
from other Hash objects found along the way. Recursion is not the default behavior
|
180
|
+
because it has the potential to be slow when given a complex Hash object that's
|
181
|
+
very large - otherwise there shouldn't be a noticeable performance impact.
|
182
|
+
|
183
|
+
The following example demonstrates [`Ryo.from`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#from-class_method):
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
require "ryo"
|
187
|
+
|
188
|
+
point = Ryo.from({
|
189
|
+
x: {to_i: 0},
|
190
|
+
y: {to_i: 10}
|
191
|
+
})
|
192
|
+
p [point.x.to_i, point.y.to_i]
|
193
|
+
|
194
|
+
##
|
195
|
+
# [0, 10]
|
196
|
+
```
|
197
|
+
|
198
|
+
#### Ryo.from with an Array
|
199
|
+
|
200
|
+
The [`Ryo.from`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#from-class_method) method can
|
201
|
+
walk an Array object, and create Ryo objects from Hash objects found along the way.
|
202
|
+
An object that can't be turned into a Ryo object is left as-is. The following
|
203
|
+
example demonstrates how that works in practice:
|
204
|
+
|
205
|
+
``` ruby
|
206
|
+
require "ryo"
|
207
|
+
|
208
|
+
points = Ryo.from([
|
209
|
+
{x: {to_i: 2}},
|
210
|
+
"foobar",
|
211
|
+
{y: {to_i: 4}}
|
212
|
+
])
|
213
|
+
|
214
|
+
p points[0].x.to_i
|
215
|
+
p points[1]
|
216
|
+
p points[2].y.to_i
|
217
|
+
|
218
|
+
##
|
219
|
+
# 2
|
220
|
+
# "foobar"
|
221
|
+
# 4
|
222
|
+
```
|
223
|
+
|
224
|
+
#### Ryo.from with OpenStruct
|
225
|
+
|
226
|
+
All methods that can create Ryo objects support turning a Struct, or OpenStruct object
|
227
|
+
into a Ryo object. The following example demonstrates how
|
228
|
+
[`Ryo.from`](https://0x1eef.github.io/x/ryo.rb/Ryo.html#from-class_method)
|
229
|
+
can recursively turn an OpenStruct object into Ryo objects. The example also assigns
|
230
|
+
a prototype to the Ryo object created from the OpenStruct:
|
231
|
+
|
232
|
+
``` ruby
|
233
|
+
require "ryo"
|
234
|
+
require "ostruct"
|
235
|
+
|
236
|
+
point = Ryo.from(
|
237
|
+
OpenStruct.new(x: {to_i: 5}),
|
238
|
+
Ryo.from(y: {to_i: 10})
|
239
|
+
)
|
240
|
+
p [point.x.to_i, point.y.to_i]
|
241
|
+
|
242
|
+
##
|
243
|
+
# [5, 10]
|
244
|
+
```
|
245
|
+
|
246
|
+
### BasicObject
|
247
|
+
|
248
|
+
#### Ryo::BasicObject
|
249
|
+
|
250
|
+
All of the previous examples have been working with instances of
|
251
|
+
[Ryo::Object](https://0x1eef.github.io/x/ryo.rb/Ryo/Object.html),
|
252
|
+
a subclass of Ruby's Object class. In comparison, [Ryo::BasicObject](https://0x1eef.github.io/x/ryo.rb/Ryo/BasicObject.html) -
|
253
|
+
a subclass of Ruby's BasicObject class, provides an object
|
254
|
+
with fewer methods. The following example demonstrates
|
255
|
+
how to create an instance of [Ryo::BasicObject](https://0x1eef.github.io/x/ryo.rb/Ryo/BasicObject.html):
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
require "ryo"
|
259
|
+
|
260
|
+
point_x = Ryo::BasicObject(x: 0)
|
261
|
+
point_y = Ryo::BasicObject({y: 0}, point_x)
|
262
|
+
point = Ryo::BasicObject({}, point_y)
|
263
|
+
p [point.x, point.y]
|
264
|
+
|
265
|
+
##
|
266
|
+
# [0, 0]
|
267
|
+
```
|
268
|
+
|
269
|
+
#### Ryo::BasicObject.from
|
270
|
+
|
271
|
+
[Ryo::BasicObject.from](https://0x1eef.github.io/x/ryo.rb/Ryo/BasicObject.html#from-class_method)
|
272
|
+
is identical to Ryo.from but rather than returning instance(s) of [Ryo::Object](https://0x1eef.github.io/x/ryo.rb/Ryo/Object.html)
|
273
|
+
it returns instance(s) of [Ryo::BasicObject](https://0x1eef.github.io/x/ryo.rb/Ryo/BasicObject.html)
|
274
|
+
instead:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
require "ryo"
|
278
|
+
|
279
|
+
point = Ryo::BasicObject.from({
|
280
|
+
x: {to_i: 2},
|
281
|
+
y: {to_i: 4}
|
282
|
+
})
|
283
|
+
p [point.x.to_i, point.y.to_i]
|
284
|
+
|
285
|
+
##
|
286
|
+
# [2, 4]
|
287
|
+
```
|
288
|
+
|
289
|
+
### Collisions
|
290
|
+
|
291
|
+
#### Resolution strategy
|
292
|
+
|
293
|
+
When a property and method collide, Ryo tries to find the best resolution. Since Ryo properties
|
294
|
+
don't accept arguments, and methods can - we are able to distinguish a property from a method in
|
295
|
+
many cases.
|
296
|
+
|
297
|
+
Consider this example, where a property collides with the `Kernel#then` method. This example
|
298
|
+
would work the same for other methods that accept a block and/or arguments:
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
require "ryo"
|
302
|
+
|
303
|
+
ryo = Ryo::Object(then: 12)
|
304
|
+
p ryo.then # => 12
|
305
|
+
p ryo.then { 34 } # => 34
|
306
|
+
```
|
307
|
+
|
308
|
+
### Beyond Hash objects
|
309
|
+
|
310
|
+
#### Duck typing
|
311
|
+
|
312
|
+
The documentation has used simple terms to describe the objects that Ryo works
|
313
|
+
with: Hash and Array objects. But actually, Ryo uses duck typing, so any object
|
314
|
+
that implements `#each_pair` can be treated as a Hash object, and any object that
|
315
|
+
implements `#each` can be treated as an Array object. Note that only
|
316
|
+
[Ryo.from](https://0x1eef.github.io/x/ryo.rb/Ryo.html#from-class_method),
|
317
|
+
[Ryo::Object.from](https://0x1eef.github.io/x/ryo.rb/Ryo/Object.html#from-class_method)
|
318
|
+
and
|
319
|
+
[Ryo::BasicObject.from](https://0x1eef.github.io/x/ryo.rb/Ryo/BasicObject.html#from-class_method)
|
320
|
+
can handle Array/#each objects.
|
321
|
+
|
322
|
+
Here's an example of how to turn your own custom object, which implements
|
323
|
+
`#each_pair`, into a Ryo object:
|
324
|
+
|
325
|
+
``` ruby
|
326
|
+
require "ryo"
|
327
|
+
|
328
|
+
class Point
|
329
|
+
def initialize
|
330
|
+
@x = 5
|
331
|
+
@y = 10
|
332
|
+
end
|
333
|
+
|
334
|
+
def each_pair
|
335
|
+
yield("x", @x)
|
336
|
+
yield("y", @y)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
point = Ryo(Point.new)
|
341
|
+
p point.x # => 5
|
342
|
+
p point.y # => 10
|
343
|
+
```
|
344
|
+
|
345
|
+
## Sources
|
346
|
+
|
347
|
+
* [Source code (GitHub)](https://github.com/0x1eef/ryo.rb#readme)
|
348
|
+
* [Source code (GitLab)](https://gitlab.com/0x1eef/ryo.rb#about)
|
349
|
+
|
350
|
+
## <a id='install'>Install</a>
|
351
|
+
|
352
|
+
Ryo is distributed as a RubyGem through its git repositories. <br>
|
353
|
+
[GitHub](https://github.com/0x1eef/ryo.rb),
|
354
|
+
and
|
355
|
+
[GitLab](https://gitlab.com/0x1eef/ryo.rb)
|
356
|
+
are available as sources.
|
357
|
+
|
358
|
+
**Gemfile**
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
gem "ryo.rb", github: "0x1eef/ryo.rb", tag: "v0.4.4"
|
362
|
+
```
|
363
|
+
|
364
|
+
## Thanks
|
365
|
+
|
366
|
+
Thanks to
|
367
|
+
[@awfulcooking (mooff)](https://github.com/awfulcooking)
|
368
|
+
for the helpful discussions and advice.
|
369
|
+
|
370
|
+
## License
|
371
|
+
|
372
|
+
This project is released under the terms of the MIT license. <br>
|
373
|
+
See [./LICENSE.txt](./LICENSE.txt) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# {Ryo::BasicObject Ryo::BasicObject} is a Ryo object and subclass
|
5
|
+
# of Ruby's BasicObject class that can be created by using
|
6
|
+
# {Ryo.BasicObject Ryo.BasicObject()},
|
7
|
+
# {Ryo::BasicObject.from Ryo::BasicObject.from}, or
|
8
|
+
# {Ryo::BasicObject.create Ryo::BasicObject.create}.
|
9
|
+
class Ryo::BasicObject < BasicObject
|
10
|
+
##
|
11
|
+
# @param props (see Ryo::Builder.build)
|
12
|
+
# @param prototype (see Ryo::Builder.build)
|
13
|
+
#
|
14
|
+
# @return [Ryo::BasicObject]
|
15
|
+
# Returns an instance of {Ryo::BasicObject Ryo::BasicObject}.
|
16
|
+
def self.create(props, prototype = nil)
|
17
|
+
::Ryo::Builder.build(self, props, prototype)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Creates a Ryo object by recursively walking a Hash object.
|
22
|
+
#
|
23
|
+
# @param props (see Ryo::Builder.recursive_build)
|
24
|
+
# @param prototype (see Ryo::Builder.recursive_build)
|
25
|
+
#
|
26
|
+
# @return [Ryo::BasicObject]
|
27
|
+
# Returns an instance of {Ryo::BasicObject Ryo::BasicObject}.
|
28
|
+
def self.from(props, prototype = nil)
|
29
|
+
::Ryo::Builder.recursive_build(self, props, prototype)
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Duplicates the internals of a Ryo object.
|
34
|
+
#
|
35
|
+
# @param [Ryo::BasicObject] ryo
|
36
|
+
# A Ryo object.
|
37
|
+
#
|
38
|
+
# @return [Ryo::BasicObject]
|
39
|
+
# Returns a Ryo object.
|
40
|
+
def initialize_dup(ryo)
|
41
|
+
::Ryo.set_table_of(self, ::Ryo.table_of(ryo).dup)
|
42
|
+
::Ryo.extend!(self, ::Ryo)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# @example
|
48
|
+
# point = Ryo::BasicObject(x: 0, y: 0)
|
49
|
+
# p [point.x, point.y] # => [0, 0]
|
50
|
+
#
|
51
|
+
# @param props (see Ryo::Builder.build)
|
52
|
+
# @param prototype (see Ryo::Builder.build)
|
53
|
+
#
|
54
|
+
# @return [Ryo::BasicObject]
|
55
|
+
# Returns an instance of {Ryo::BasicObject Ryo::BasicObject}.
|
56
|
+
def Ryo.BasicObject(props, prototype = nil)
|
57
|
+
Ryo::BasicObject.create(props, prototype)
|
58
|
+
end
|