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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/specs.yml +23 -0
  3. data/.gitignore +6 -0
  4. data/.gitlab-ci.yml +9 -0
  5. data/.rubocop.yml +56 -0
  6. data/.yardoc-template/default/fulldoc/html/css/0x1eef.css +15 -0
  7. data/.yardoc-template/default/layout/html/setup.rb +5 -0
  8. data/.yardoc-template/default/module/setup.rb +7 -0
  9. data/.yardopts +4 -0
  10. data/Gemfile +5 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +373 -0
  13. data/Rakefile +3 -0
  14. data/lib/ryo/basic_object.rb +58 -0
  15. data/lib/ryo/builder.rb +106 -0
  16. data/lib/ryo/enumerable.rb +214 -0
  17. data/lib/ryo/function.rb +68 -0
  18. data/lib/ryo/keywords.rb +67 -0
  19. data/lib/ryo/lazy.rb +4 -0
  20. data/lib/ryo/object.rb +58 -0
  21. data/lib/ryo/reflect.rb +379 -0
  22. data/lib/ryo/version.rb +5 -0
  23. data/lib/ryo.rb +197 -0
  24. data/ryo.rb.gemspec +21 -0
  25. data/share/ryo.rb/examples/1.0_prototypes_point_object.rb +12 -0
  26. data/share/ryo.rb/examples/1.1_prototypes_ryo_fn.rb +14 -0
  27. data/share/ryo.rb/examples/2.0_iteration_each.rb +13 -0
  28. data/share/ryo.rb/examples/2.1_iteration_map.rb +16 -0
  29. data/share/ryo.rb/examples/2.2_iteration_ancestors.rb +13 -0
  30. data/share/ryo.rb/examples/3.0_recursion_ryo_from.rb +13 -0
  31. data/share/ryo.rb/examples/3.1_recursion_ryo_from_with_array.rb +19 -0
  32. data/share/ryo.rb/examples/3.2_recursion_ryo_from_with_openstruct.rb +14 -0
  33. data/share/ryo.rb/examples/4.0_basicobject_ryo_basicobject.rb +12 -0
  34. data/share/ryo.rb/examples/4.1_basicobject_ryo_basicobject_from.rb +13 -0
  35. data/share/ryo.rb/examples/5_collisions_resolution_strategy.rb +8 -0
  36. data/share/ryo.rb/examples/6_beyond_hash_objects.rb +20 -0
  37. data/share/ryo.rb/examples/7_ryo_lazy.rb +14 -0
  38. data/share/ryo.rb/examples/setup.rb +3 -0
  39. data/spec/readme_spec.rb +79 -0
  40. data/spec/ryo_basic_object_spec.rb +60 -0
  41. data/spec/ryo_enumerable_spec.rb +197 -0
  42. data/spec/ryo_keywords_spec.rb +86 -0
  43. data/spec/ryo_object_spec.rb +71 -0
  44. data/spec/ryo_prototypes_spec.rb +45 -0
  45. data/spec/ryo_reflect_spec.rb +175 -0
  46. data/spec/ryo_spec.rb +130 -0
  47. data/spec/setup.rb +5 -0
  48. metadata +173 -0
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "setup"
4
+
5
+ RSpec.describe Ryo::Reflect do
6
+ describe ".ryo?" do
7
+ subject { Ryo.ryo?(object) }
8
+
9
+ context "when given an instance of Ryo::BasicObject" do
10
+ let(:object) { Ryo::BasicObject(x: 1, y: 1) }
11
+ it { is_expected.to be(true) }
12
+ end
13
+
14
+ context "when given an instance of Ryo::Object" do
15
+ let(:object) { Ryo::Object(x: 2, y: 2) }
16
+ it { is_expected.to be(true) }
17
+ end
18
+
19
+ context "when given an instance of Object" do
20
+ let(:object) { Object.new }
21
+ it { is_expected.to be(false) }
22
+ end
23
+
24
+ context "when given an instance of Hash" do
25
+ let(:object) { {} }
26
+ it { is_expected.to be(false) }
27
+ end
28
+ end
29
+
30
+ describe ".property?" do
31
+ subject { Ryo.property?(point_b, property) }
32
+ let(:point_a) { Ryo(x: 0) }
33
+ let(:point_b) { Ryo({y: 0}, point_a) }
34
+
35
+ context "when given a property belonging to a prototype" do
36
+ let(:property) { "x" }
37
+ it { is_expected.to be(false) }
38
+ end
39
+
40
+ context "when given a property belonging to self" do
41
+ let(:property) { "y" }
42
+ it { is_expected.to be(true) }
43
+ end
44
+ end
45
+
46
+ describe ".properties_of" do
47
+ context "when properties of a prototype are excluded" do
48
+ subject { Ryo.properties_of(point_b) }
49
+ let(:point_a) { Ryo(x: 0) }
50
+ let(:point_b) { Ryo({y: 0}, point_a) }
51
+ it { is_expected.to eq(["y"]) }
52
+ end
53
+ end
54
+
55
+ describe ".set_prototype_of" do
56
+ context "when the prototype of point_c is changed to point_b" do
57
+ subject { point_c.y }
58
+ let(:point_a) { Ryo(x: 0, y: 0) }
59
+ let(:point_b) { Ryo(y: 5) }
60
+ let(:point_c) { Ryo({}, point_a) }
61
+
62
+ before { Ryo.set_prototype_of(point_c, point_b) }
63
+ it { is_expected.to eq(5) }
64
+ end
65
+ end
66
+
67
+ describe ".prototype_of" do
68
+ let(:point_a) { Ryo(x: 0, y: 0) }
69
+ let(:point_b) { Ryo({y: 5}, point_a) }
70
+
71
+ context "when given an object with a prototype" do
72
+ subject { Ryo.prototype_of(point_b) }
73
+ it { is_expected.to be(point_a) }
74
+ end
75
+
76
+ context "when given an object without a prototype" do
77
+ subject { Ryo.prototype_of(point_a) }
78
+ it { is_expected.to be(nil) }
79
+ end
80
+ end
81
+
82
+ describe ".prototype_chain_of" do
83
+ context "when given the last prototype (point_c)" do
84
+ subject { Ryo.prototype_chain_of(point_c) }
85
+ let(:root) { Ryo({a: 1}) }
86
+ let(:point_a) { Ryo({b: 2}, root) }
87
+ let(:point_b) { Ryo({c: 3}, point_a) }
88
+ let(:point_c) { Ryo({d: 4}, point_b) }
89
+
90
+ it { is_expected.to eq([point_b, point_a, root]) }
91
+ end
92
+
93
+ context "when given an object without a prototype" do
94
+ subject { Ryo.prototype_chain_of(Ryo({})) }
95
+ it { is_expected.to eq([]) }
96
+ end
97
+ end
98
+
99
+ describe ".class_of" do
100
+ context "when given an instance of Ryo::BasicObject" do
101
+ subject { Ryo.class_of Ryo::BasicObject({}) }
102
+ it { is_expected.to eq(Ryo::BasicObject) }
103
+ end
104
+
105
+ context "when given an instance of Ryo::Object" do
106
+ subject { Ryo.class_of Ryo::Object({}) }
107
+ it { is_expected.to eq(Ryo::Object) }
108
+ end
109
+ end
110
+
111
+ describe ".equal?" do
112
+ let(:point_a) { Ryo::BasicObject(x: 5, y: 5) }
113
+ let(:point_b) { Ryo::BasicObject(x: 5, y: 5) }
114
+
115
+ context "when two objects are the same object" do
116
+ subject { Ryo.equal?(point_a, point_a) }
117
+ it { is_expected.to be(true) }
118
+ end
119
+
120
+ context "when two objects are distinct objects" do
121
+ subject { Ryo.equal?(point_a, point_b) }
122
+ it { is_expected.to be(false) }
123
+ end
124
+ end
125
+
126
+ describe ".assign" do
127
+ let(:point_a) { Ryo(x: 0, y: 0) }
128
+ let(:point_b) { Ryo(y: 10) }
129
+
130
+ context "when point_b is assigned to point_a" do
131
+ subject { Ryo.assign(point_a, point_b) }
132
+ it { is_expected.to eq("x" => 0, "y" => 10) }
133
+ it { is_expected.to be_instance_of(Ryo::Object) }
134
+ end
135
+
136
+ context "when a Ryo object and Hash object are assigned to point_a" do
137
+ subject { Ryo.assign(point_a, point_b, {move: fn}) }
138
+ let(:fn) { Ryo.fn {} }
139
+ it { is_expected.to eq("x" => 0, "y" => 10, "move" => fn) }
140
+ it { is_expected.to be_instance_of(Ryo::Object) }
141
+ it { is_expected.to be(point_a) }
142
+ end
143
+ end
144
+
145
+ describe ".table_of" do
146
+ subject(:table) { Ryo.table_of(ryo, recursive:) }
147
+
148
+ context "without recursion" do
149
+ let(:recursive) { false }
150
+ context "when given a Ryo object" do
151
+ let(:ryo) { Ryo(x: 1, y:1) }
152
+ it { is_expected.to be_instance_of(Hash) }
153
+ it { is_expected.to eq("x" => 1, "y" => 1) }
154
+ end
155
+ end
156
+
157
+ context "with recursion" do
158
+ let(:recursive) { true }
159
+ context "when a Ryo object nests another Ryo object" do
160
+ let(:ryo) { Ryo(point: Ryo(x: 1, y: 1)) }
161
+ it { expect(table).to be_instance_of(Hash) }
162
+ it { expect(table["point"]).to be_instance_of(Hash) }
163
+ it { is_expected.to eq("point" => {"x" => 1, "y" => 1}) }
164
+ end
165
+
166
+ context "when a Ryo object has a nest depth of two" do
167
+ let(:ryo) { Ryo(point: Ryo(point: Ryo(x: 1, y: 1))) }
168
+ it { expect(table).to be_instance_of(Hash) }
169
+ it { expect(table["point"]).to be_instance_of(Hash) }
170
+ it { expect(table["point"]["point"]).to be_instance_of(Hash) }
171
+ it { is_expected.to eq("point" => {"point" => {"x" => 1, "y" => 1}}) }
172
+ end
173
+ end
174
+ end
175
+ end
data/spec/ryo_spec.rb ADDED
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "setup"
4
+
5
+ RSpec.describe Ryo do
6
+ describe ".from" do
7
+ context "when given an instance of Ryo::Object" do
8
+ subject { Ryo.from(point) }
9
+ let(:point) { Ryo(x: 5, y: 10) }
10
+ it { is_expected.to eq(point) }
11
+ end
12
+
13
+ context "when given { key => Hash<Symbol, Integer> }" do
14
+ subject { point.x.to_i }
15
+ let(:point) { Ryo.from({x: {to_i: 4}}) }
16
+ it { is_expected.to eq(4) }
17
+ end
18
+
19
+ context "when given { key => Array<String> }" do
20
+ subject { Ryo.from(key: %w[foo bar baz]) }
21
+ it { is_expected.to be_instance_of(Ryo::Object) }
22
+ it { is_expected.to eq("key" => %w[foo bar baz]) }
23
+ end
24
+
25
+ context "when given { key => Array<String, Ryo::BasicObject> }" do
26
+ subject { Ryo.from(key: ["foo", point]) }
27
+ let(:point) { Ryo::BasicObject(x: 0, y: 0) }
28
+ it { is_expected.to be_instance_of(Ryo::Object) }
29
+ it { is_expected.to eq("key" => ["foo", point]) }
30
+
31
+ context "with equal?" do
32
+ subject { super().key[-1] }
33
+ it { is_expected.to be(point) }
34
+ end
35
+ end
36
+
37
+ context "when given an Array that contains Hash objects" do
38
+ context "when given one Hash object" do
39
+ subject { ary[0].x.to_i }
40
+ let(:ary) { Ryo.from([{x: {to_i: 4}}]) }
41
+ it { is_expected.to eq(4) }
42
+ end
43
+
44
+ context "when given two Hash objects" do
45
+ subject { ary.map { _1.x.to_i } }
46
+ let(:ary) { Ryo.from([{x: {to_i: 4}}, {x: {to_i: 3}}]) }
47
+ it { is_expected.to eq([4, 3]) }
48
+ end
49
+
50
+ context "when given a mix of Hash objects, and other objects" do
51
+ subject { ary.map { (Ryo === _1) ? _1.x.to_i : _1 } }
52
+ let(:ary) { Ryo.from([{x: {to_i: 4}}, "foo"]) }
53
+ it { is_expected.to eq([4, "foo"]) }
54
+ end
55
+
56
+ context "when given a mix of Hash objects, and Ryo objects" do
57
+ subject { ary.map(&:x) }
58
+ let(:ary) { Ryo.from([{x: 1}, Ryo::BasicObject(x: 2)]) }
59
+ it { is_expected.to eq([1, 2]) }
60
+ end
61
+ end
62
+
63
+ context "when given an object that implements #each" do
64
+ subject { ary.map { (Ryo === _1) ? _1.x.to_i : _1 } }
65
+ let(:ary) do
66
+ Ryo.from Class.new {
67
+ def each
68
+ arr = [{x: {to_i: 4}}, "foo"]
69
+ arr.each { yield(_1) }
70
+ end
71
+ }.new
72
+ end
73
+ it { is_expected.to eq([4, "foo"]) }
74
+ end
75
+
76
+ context "when given an object that implements #each_pair" do
77
+ subject { [point.x, point.y] }
78
+ let(:point) do
79
+ Ryo.from Class.new {
80
+ def each_pair
81
+ yield("x", 5)
82
+ yield("y", 10)
83
+ end
84
+ }.new
85
+ end
86
+ it { is_expected.to eq([5, 10]) }
87
+ end
88
+
89
+ context "when given an object that doesn't implement #each or #each_pair" do
90
+ subject(:from) { Ryo.from(Object.new) }
91
+ it { expect { from }.to raise_error(TypeError, %r{does not implement #each / #each_pair}) }
92
+ end
93
+ end
94
+
95
+ describe ".dup" do
96
+ subject(:dup) { Ryo.dup(point_c) }
97
+ let(:point_a) { Ryo::BasicObject(x: 1) }
98
+ let(:point_b) { Ryo::BasicObject({y: 2}, point_a) }
99
+ let(:point_c) { Ryo::BasicObject({}, point_b) }
100
+
101
+ context "when the duplicate is mutated" do
102
+ before { dup.x = 5 }
103
+
104
+ context "when verifying the source wasn't mutated" do
105
+ subject { point_c.x }
106
+ it { is_expected.to eq(1) }
107
+ end
108
+
109
+ context "when verifying the duplicate was mutated" do
110
+ subject { dup.x }
111
+ it { is_expected.to eq(5) }
112
+ end
113
+ end
114
+
115
+ context "when verifying the source and duplicate are distinct objects" do
116
+ subject { Ryo.kernel(:equal?).bind_call(point_c, dup) }
117
+ it { is_expected.to eq(false) }
118
+ end
119
+
120
+ context "when verifying the source and duplicate are eql?" do
121
+ subject { point_c == dup }
122
+ it { is_expected.to be(true) }
123
+ end
124
+
125
+ context "when verifying the prototype chain of the source and duplicate are eql?" do
126
+ subject { Ryo.prototype_chain_of(point_c) == Ryo.prototype_chain_of(dup) }
127
+ it { is_expected.to eq(true) }
128
+ end
129
+ end
130
+ end
data/spec/setup.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "rspec"
5
+ require "ryo"
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ryo.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.4
5
+ platform: ruby
6
+ authors:
7
+ - '0x1eef'
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: yard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: standard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: test-cmd.rb
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.4'
97
+ description: Ryo implements prototype-based inheritance, in Ruby.
98
+ email:
99
+ - 0x1eef@protonmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".github/workflows/specs.yml"
105
+ - ".gitignore"
106
+ - ".gitlab-ci.yml"
107
+ - ".rubocop.yml"
108
+ - ".yardoc-template/default/fulldoc/html/css/0x1eef.css"
109
+ - ".yardoc-template/default/layout/html/setup.rb"
110
+ - ".yardoc-template/default/module/setup.rb"
111
+ - ".yardopts"
112
+ - Gemfile
113
+ - LICENSE.txt
114
+ - README.md
115
+ - Rakefile
116
+ - lib/ryo.rb
117
+ - lib/ryo/basic_object.rb
118
+ - lib/ryo/builder.rb
119
+ - lib/ryo/enumerable.rb
120
+ - lib/ryo/function.rb
121
+ - lib/ryo/keywords.rb
122
+ - lib/ryo/lazy.rb
123
+ - lib/ryo/object.rb
124
+ - lib/ryo/reflect.rb
125
+ - lib/ryo/version.rb
126
+ - ryo.rb.gemspec
127
+ - share/ryo.rb/examples/1.0_prototypes_point_object.rb
128
+ - share/ryo.rb/examples/1.1_prototypes_ryo_fn.rb
129
+ - share/ryo.rb/examples/2.0_iteration_each.rb
130
+ - share/ryo.rb/examples/2.1_iteration_map.rb
131
+ - share/ryo.rb/examples/2.2_iteration_ancestors.rb
132
+ - share/ryo.rb/examples/3.0_recursion_ryo_from.rb
133
+ - share/ryo.rb/examples/3.1_recursion_ryo_from_with_array.rb
134
+ - share/ryo.rb/examples/3.2_recursion_ryo_from_with_openstruct.rb
135
+ - share/ryo.rb/examples/4.0_basicobject_ryo_basicobject.rb
136
+ - share/ryo.rb/examples/4.1_basicobject_ryo_basicobject_from.rb
137
+ - share/ryo.rb/examples/5_collisions_resolution_strategy.rb
138
+ - share/ryo.rb/examples/6_beyond_hash_objects.rb
139
+ - share/ryo.rb/examples/7_ryo_lazy.rb
140
+ - share/ryo.rb/examples/setup.rb
141
+ - spec/readme_spec.rb
142
+ - spec/ryo_basic_object_spec.rb
143
+ - spec/ryo_enumerable_spec.rb
144
+ - spec/ryo_keywords_spec.rb
145
+ - spec/ryo_object_spec.rb
146
+ - spec/ryo_prototypes_spec.rb
147
+ - spec/ryo_reflect_spec.rb
148
+ - spec/ryo_spec.rb
149
+ - spec/setup.rb
150
+ homepage: https://github.com/0x1eef/ryo.rb#readme
151
+ licenses:
152
+ - MIT
153
+ metadata: {}
154
+ post_install_message:
155
+ rdoc_options: []
156
+ require_paths:
157
+ - lib
158
+ required_ruby_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubygems_version: 3.4.10
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Ryo implements prototype-based inheritance, in Ruby.
173
+ test_files: []