immutable-struct 2.2.1 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/immutable-struct.rb +30 -4
- data/spec/immutable_struct_spec.rb +94 -6
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9c0a785dd4e418fdae0fe7dfd89f3ceef53077b
|
4
|
+
data.tar.gz: 4411b12c43b62e461b86489e55cf2d7996e45172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c5c10d71cea0da0ae00f11d349a19e3f06557f1cfe80195c9f1131681a3e860ed68c5581a9df8971fe26c0a4b3793f6e75568adf5440eeb8a713121fc48b4c4
|
7
|
+
data.tar.gz: fe3d852d289626c1314e323b6610558470ad330feadd5b39509141261d80fa5c4a596f61a860ca3d9ffc8752c51f40c3c41c3c66437b774c57bf5bdd43769606
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
immutable-struct (2.2.
|
4
|
+
immutable-struct (2.2.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -12,7 +12,7 @@ GEM
|
|
12
12
|
rspec-core (~> 3.4.0)
|
13
13
|
rspec-expectations (~> 3.4.0)
|
14
14
|
rspec-mocks (~> 3.4.0)
|
15
|
-
rspec-core (3.4.
|
15
|
+
rspec-core (3.4.2)
|
16
16
|
rspec-support (~> 3.4.0)
|
17
17
|
rspec-expectations (3.4.0)
|
18
18
|
diff-lcs (>= 1.2.0, < 2.0)
|
data/lib/immutable-struct.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# will be evaluated as if it were inside a class definition, allowing you
|
7
7
|
# to add methods, include or extend modules, or do whatever else you want.
|
8
8
|
class ImmutableStruct
|
9
|
-
VERSION='2.2.
|
9
|
+
VERSION='2.2.2' #:nodoc:
|
10
10
|
# Create a new class with the given read-only attributes.
|
11
11
|
#
|
12
12
|
# attributes:: list of symbols or strings that can be used to create attributes.
|
@@ -22,7 +22,7 @@ class ImmutableStruct
|
|
22
22
|
#
|
23
23
|
# Example:
|
24
24
|
#
|
25
|
-
# Person = ImmutableStruct.new(:name, :location, :minor
|
25
|
+
# Person = ImmutableStruct.new(:name, :location, :minor?, [:aliases])
|
26
26
|
#
|
27
27
|
# p = Person.new(name: 'Dave', location: Location.new("DC"), minor: false)
|
28
28
|
# p.name # => 'Dave'
|
@@ -41,6 +41,21 @@ class ImmutableStruct
|
|
41
41
|
# new_person.age # => 41
|
42
42
|
# new_person.active? # => true
|
43
43
|
#
|
44
|
+
# Note that you also get an implementation of `to_h` that will include **all** no-arg methods in its
|
45
|
+
# output:
|
46
|
+
#
|
47
|
+
# Person = ImmutableStruct.new(:name, :location, :minor?, [:aliases])
|
48
|
+
# p = Person.new(name: 'Dave', minor: "yup", aliases: [ "davetron", "davetron5000" ])
|
49
|
+
# p.to_h # => { name: "Dave", minor: "yup", minor?: true, aliases: ["davetron", "davetron5000" ] }
|
50
|
+
#
|
51
|
+
# This has two subtle side-effects:
|
52
|
+
#
|
53
|
+
# * Methods that take no args, but are not 'attributes' will get called by `to_h`. This shouldn't be a
|
54
|
+
# problem, because you should not generally be doing this on a struct-like class.
|
55
|
+
# * Methods that take no args, but call `to_h` will stack overflow. This is because the class'
|
56
|
+
# internals have no way to know about this. This is particularly a problem if you want to
|
57
|
+
# define your own `to_json` method that serializes the result of `to_h`.
|
58
|
+
#
|
44
59
|
def self.new(*attributes,&block)
|
45
60
|
klass = Class.new do
|
46
61
|
attributes.each do |attribute|
|
@@ -82,13 +97,24 @@ class ImmutableStruct
|
|
82
97
|
end
|
83
98
|
|
84
99
|
alias_method :eql?, :==
|
100
|
+
|
101
|
+
define_method(:hash) do
|
102
|
+
attribute_values = attributes.map { |attr| self.send(attr) }
|
103
|
+
(attribute_values + [self.class]).hash
|
104
|
+
end
|
85
105
|
end
|
86
106
|
klass.class_exec(&block) unless block.nil?
|
87
|
-
|
107
|
+
|
108
|
+
imethods = klass.instance_methods(include_super=false).map { |method_name|
|
109
|
+
klass.instance_method(method_name)
|
110
|
+
}.reject { |method|
|
111
|
+
method.arity != 0
|
112
|
+
}.map(&:name).map(&:to_sym)
|
113
|
+
|
88
114
|
klass.class_exec(imethods) do |imethods|
|
89
115
|
define_method(:to_h) do
|
90
116
|
imethods.inject({}) do |hash, method|
|
91
|
-
next hash if [:==, :eql?, :merge].include?(method)
|
117
|
+
next hash if [:==, :eql?, :merge, :hash].include?(method)
|
92
118
|
hash.merge(method.to_sym => self.send(method))
|
93
119
|
end
|
94
120
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper.rb'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module TestModule
|
4
5
|
def hello; "hello"; end
|
@@ -112,14 +113,61 @@ describe ImmutableStruct do
|
|
112
113
|
end
|
113
114
|
|
114
115
|
describe "to_h" do
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
context "vanilla struct with just derived values" do
|
117
|
+
it "should include the output of params and block methods in the hash" do
|
118
|
+
klass = ImmutableStruct.new(:name, :minor?, :location, [:aliases]) do
|
119
|
+
def nick_name
|
120
|
+
'bob'
|
121
|
+
end
|
119
122
|
end
|
123
|
+
instance = klass.new(name: "Rudy", minor: "ayup", aliases: [ "Rudyard", "Roozoola" ])
|
124
|
+
instance.to_h.should == {
|
125
|
+
name: "Rudy",
|
126
|
+
minor: "ayup",
|
127
|
+
minor?: true,
|
128
|
+
location: nil,
|
129
|
+
aliases: [ "Rudyard", "Roozoola"],
|
130
|
+
nick_name: "bob",
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
context "additional method that takes arguments" do
|
135
|
+
it "should not call the additional method" do
|
136
|
+
klass = ImmutableStruct.new(:name, :minor?, :location, [:aliases]) do
|
137
|
+
def nick_name
|
138
|
+
'bob'
|
139
|
+
end
|
140
|
+
def location_near?(other_location)
|
141
|
+
false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
instance = klass.new(name: "Rudy", minor: "ayup", aliases: [ "Rudyard", "Roozoola" ])
|
145
|
+
instance.to_h.should == {
|
146
|
+
name: "Rudy",
|
147
|
+
minor: "ayup",
|
148
|
+
minor?: true,
|
149
|
+
location: nil,
|
150
|
+
aliases: [ "Rudyard", "Roozoola"],
|
151
|
+
nick_name: "bob",
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "no-arg method that uses to_h" do
|
157
|
+
it "blows up" do
|
158
|
+
klass = ImmutableStruct.new(:name, :minor?, :location, [:aliases]) do
|
159
|
+
def nick_name
|
160
|
+
'bob'
|
161
|
+
end
|
162
|
+
def to_json
|
163
|
+
to_h.to_json
|
164
|
+
end
|
165
|
+
end
|
166
|
+
instance = klass.new(name: "Rudy", minor: "ayup", aliases: [ "Rudyard", "Roozoola" ])
|
167
|
+
expect {
|
168
|
+
instance.to_json.should == instance.to_h.to_json
|
169
|
+
}.to raise_error(SystemStackError)
|
120
170
|
end
|
121
|
-
instance = klass.new(flappy: 'bird')
|
122
|
-
instance.to_h.should == {flappy: 'bird', lawsuit: 'pending'}
|
123
171
|
end
|
124
172
|
end
|
125
173
|
|
@@ -192,5 +240,45 @@ describe ImmutableStruct do
|
|
192
240
|
|
193
241
|
end
|
194
242
|
|
243
|
+
describe "hash" do
|
244
|
+
|
245
|
+
it "should have same hash value as itself" do
|
246
|
+
@k1_a.hash.eql?(@k1_a.hash).should be true
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should have same hash value as same class with identical attribute values" do
|
250
|
+
@k1_a.hash.eql?(@k1_c.hash).should be true
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should not have hash value as same class with different attribute values' do
|
254
|
+
@k1_a.hash.eql?(@k1_b.hash).should be false
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'should not have hash value equal to different class with identical attribute values' do
|
258
|
+
@k1_a.hash.eql?(@k3_a.hash).should be false
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'should reject set addition if same instance is already a member' do
|
262
|
+
set = Set.new([@k1_a])
|
263
|
+
set.add?(@k1_a).should be nil
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should reject set addition if different instance, but attributes are the same' do
|
267
|
+
set = Set.new([@k1_a])
|
268
|
+
set.add?(@k1_c).should be nil
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should allow set addition if different instance and attribute values' do
|
272
|
+
set = Set.new([@k1_a])
|
273
|
+
set.add?(@k1_b).should_not be nil
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'should allow set addition if different class' do
|
277
|
+
set = Set.new([@k1_a])
|
278
|
+
set.add?(@k2_a).should_not be nil
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
195
283
|
end
|
196
284
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: immutable-struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stitch Fix Engineering
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-02-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
99
|
version: '0'
|
100
100
|
requirements: []
|
101
101
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.4.
|
102
|
+
rubygems_version: 2.4.8
|
103
103
|
signing_key:
|
104
104
|
specification_version: 4
|
105
105
|
summary: Easily create value objects without the pain of Ruby's Struct (or its setters)
|