immutable-struct 2.2.1 → 2.2.2
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.
- 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)
|