good 0.1.1 → 0.1.3
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.
- data/lib/good.rb +16 -8
- data/spec/good_spec.rb +31 -23
- metadata +3 -3
data/lib/good.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
class Good
|
2
|
-
VERSION = "0.1.
|
1
|
+
class Good
|
2
|
+
VERSION = "0.1.3"
|
3
3
|
|
4
|
-
class Value
|
4
|
+
class Value
|
5
5
|
def self.new(*members, &block)
|
6
6
|
Good.generate(false, *members, &block)
|
7
|
-
end
|
7
|
+
end
|
8
8
|
end
|
9
9
|
|
10
|
-
class Record
|
10
|
+
class Record
|
11
11
|
def self.new(*members, &block)
|
12
12
|
Good.generate(true, *members, &block)
|
13
|
-
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.validate_constructor_attributes(attributes, allowed)
|
17
|
+
if (unrecognized = attributes.keys.map(&:to_sym) - allowed).any?
|
18
|
+
raise ArgumentError, "Unrecognized parameter(s): #{unrecognized.join(', ')}"
|
19
|
+
end
|
14
20
|
end
|
15
21
|
|
16
22
|
def self.generate(mutable, *members, &block)
|
17
23
|
Class.new do
|
18
24
|
mutable ? attr_accessor(*members) : attr_reader(*members)
|
19
25
|
|
20
|
-
const_set(:MEMBERS, members.
|
26
|
+
const_set(:MEMBERS, members.map(&:to_sym).freeze)
|
21
27
|
|
22
28
|
def self.coerce(coercable)
|
23
29
|
case coercable
|
@@ -27,17 +33,19 @@ class Good
|
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
36
|
+
|
30
37
|
if mutable
|
31
38
|
def initialize(attributes = {})
|
39
|
+
Good.validate_constructor_attributes(attributes, self.class::MEMBERS)
|
32
40
|
attributes.each { |k, v| send("#{k}=", v) }
|
33
41
|
end
|
34
42
|
else
|
35
43
|
def initialize(attributes = {})
|
44
|
+
Good.validate_constructor_attributes(attributes, self.class::MEMBERS)
|
36
45
|
attributes.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
37
46
|
end
|
38
47
|
end
|
39
48
|
|
40
|
-
|
41
49
|
def attributes
|
42
50
|
{}.tap { |h| self.class::MEMBERS.each { |m| h[m] = send(m) } }
|
43
51
|
end
|
data/spec/good_spec.rb
CHANGED
@@ -24,27 +24,31 @@ shared_examples :good do
|
|
24
24
|
it "allows 0 argument construction" do
|
25
25
|
person = Person.new
|
26
26
|
end
|
27
|
+
|
28
|
+
it "raises an ArgumentError if given an unrecognized parameter" do
|
29
|
+
expect { Person.new(:wrong => nil) }.to raise_error(ArgumentError)
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
describe "#==" do
|
30
34
|
it "is true if all the parameters are ==" do
|
31
35
|
bob_1 = Person.new(:name => "Bob", :age => 50)
|
32
36
|
bob_2 = Person.new(:name => "Bob", :age => 50)
|
33
|
-
|
37
|
+
|
34
38
|
expect(bob_1).to eq(bob_2)
|
35
39
|
end
|
36
|
-
|
40
|
+
|
37
41
|
it "is false if any attributes are not #==" do
|
38
42
|
bob = Person.new(:name => "Bob", :age => 50)
|
39
43
|
ted = Person.new(:name => "Ted", :age => 50)
|
40
|
-
|
44
|
+
|
41
45
|
expect(bob).not_to eq(ted)
|
42
46
|
end
|
43
|
-
|
47
|
+
|
44
48
|
it "is false if the other object is not of the same class" do
|
45
49
|
bob = Person.new(:name => "Bob", :age => 50)
|
46
50
|
alien_bob = described_class.new(:name, :age).new(:name => "Bob", :age => 50)
|
47
|
-
|
51
|
+
|
48
52
|
expect(bob).not_to eq(alien_bob)
|
49
53
|
end
|
50
54
|
end
|
@@ -53,51 +57,56 @@ shared_examples :good do
|
|
53
57
|
it "is true if all the parameters are ==" do
|
54
58
|
bob_1 = Person.new(:name => "Bob", :age => 50)
|
55
59
|
bob_2 = Person.new(:name => "Bob", :age => 50)
|
56
|
-
|
60
|
+
|
57
61
|
expect(bob_1).to eql(bob_2)
|
58
62
|
end
|
59
|
-
|
63
|
+
|
60
64
|
it "is false if any attributes are not #==" do
|
61
65
|
bob = Person.new(:name => "Bob", :age => 50)
|
62
66
|
ted = Person.new(:name => "Ted", :age => 50)
|
63
|
-
|
67
|
+
|
64
68
|
expect(bob).not_to eql(ted)
|
65
69
|
end
|
66
|
-
|
70
|
+
|
67
71
|
it "is false if the other object is not of the same class" do
|
68
72
|
bob = Person.new(:name => "Bob", :age => 50)
|
69
73
|
alien_bob = Struct.new(:name, :age).new("Bob", 50)
|
70
|
-
|
74
|
+
|
71
75
|
expect(bob).not_to eql(alien_bob)
|
72
76
|
end
|
73
77
|
end
|
74
|
-
|
78
|
+
|
75
79
|
describe "#hash" do
|
76
80
|
it "is stable" do
|
77
81
|
bob_1 = Person.new(:name => "Bob")
|
78
82
|
bob_2 = Person.new(:name => "Bob")
|
79
|
-
|
83
|
+
|
80
84
|
expect(bob_1.hash).to eq(bob_2.hash)
|
81
85
|
end
|
82
|
-
|
86
|
+
|
83
87
|
it "varies with the parameters" do
|
84
88
|
bob = Person.new(:name => "Bob", :age => 50)
|
85
89
|
ted = Person.new(:name => "Ted", :age => 50)
|
86
|
-
|
90
|
+
|
87
91
|
expect(bob.hash).not_to eql(ted.hash)
|
88
92
|
end
|
89
93
|
end
|
90
|
-
|
94
|
+
|
91
95
|
describe "::MEMBERS" do
|
92
96
|
it "is the list of member variables" do
|
93
97
|
expect(Person::MEMBERS).to eq([:name, :age])
|
94
98
|
end
|
95
|
-
|
99
|
+
|
96
100
|
it "is frozen" do
|
97
101
|
expect { Person::MEMBERS << :height }.to raise_error(/can't modify frozen/)
|
98
102
|
end
|
103
|
+
|
104
|
+
it "always contains symbols (even if defined with strings)" do
|
105
|
+
klass = described_class.new("a", :b)
|
106
|
+
expect(klass::MEMBERS).to eq([:a, :b])
|
107
|
+
end
|
99
108
|
end
|
100
|
-
|
109
|
+
|
101
110
|
describe "#members" do
|
102
111
|
it "is the list of member variables" do
|
103
112
|
person = Person.new
|
@@ -152,17 +161,17 @@ shared_examples :good do
|
|
152
161
|
person = Person.new
|
153
162
|
expect(Person.coerce(person)).to be(person)
|
154
163
|
end
|
155
|
-
|
164
|
+
|
156
165
|
it "initializes a new instance if the input is a hash" do
|
157
166
|
person = Person.coerce({:name => "Bob"})
|
158
167
|
expect(person).to eq(Person.new(:name => "Bob"))
|
159
168
|
end
|
160
|
-
|
169
|
+
|
161
170
|
it "raises a TypeError otherwise" do
|
162
171
|
expect { Person.coerce("15 lbs of squirrel fur") }.to raise_error(TypeError)
|
163
172
|
end
|
164
173
|
end
|
165
|
-
|
174
|
+
|
166
175
|
describe "block construction" do
|
167
176
|
let(:car_klass) do
|
168
177
|
described_class.new(:wheels) do
|
@@ -171,7 +180,7 @@ shared_examples :good do
|
|
171
180
|
end
|
172
181
|
end
|
173
182
|
end
|
174
|
-
|
183
|
+
|
175
184
|
it "allows definition of methods" do
|
176
185
|
car = car_klass.new(:wheels => 4)
|
177
186
|
expect(car.drive).to eq("Driving with all 4 wheels!")
|
@@ -184,7 +193,7 @@ describe Good::Value do
|
|
184
193
|
|
185
194
|
it "is immutable" do
|
186
195
|
person = Person.new
|
187
|
-
expect { person.name = "Bob" }.to raise_error(NoMethodError)
|
196
|
+
expect { person.name = "Bob" }.to raise_error(NoMethodError)
|
188
197
|
end
|
189
198
|
end
|
190
199
|
|
@@ -196,4 +205,3 @@ describe Good::Record do
|
|
196
205
|
expect { person.name = "Bob" }.to change { person.name }.to("Bob")
|
197
206
|
end
|
198
207
|
end
|
199
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-07-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
95
|
version: '0'
|
96
96
|
requirements: []
|
97
97
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.8.23
|
98
|
+
rubygems_version: 1.8.23.2
|
99
99
|
signing_key:
|
100
100
|
specification_version: 3
|
101
101
|
summary: Good::Value and Good::Record
|