ryo.rb 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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: []