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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 08fb6160fb58ba3ab29f75f41a166a0a93332410
4
- data.tar.gz: f9f956a1e7da833ff74758111f12f4c5742fa5dd
3
+ metadata.gz: a9c0a785dd4e418fdae0fe7dfd89f3ceef53077b
4
+ data.tar.gz: 4411b12c43b62e461b86489e55cf2d7996e45172
5
5
  SHA512:
6
- metadata.gz: 376b0a0c7b46858f974bd18563abdf5e20bf429847ea22cbfe0c88e310f446cfc79a4a5b24fc9cbf1371c86721c043cd4fa351177e7c3e8bd7c0e3a681207ffe
7
- data.tar.gz: 2226048c79444ae2c8da4d4e7b83b8f07e87c4b024bd154e03eae7115537d9f55ad4a3a5e1b48b476af067a0bb4b289de7d294f53f208b642af5891bdb7322ee
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.1)
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.1)
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)
@@ -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.1' #:nodoc:
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
- imethods = klass.instance_methods(include_super=false)
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
- it "should include the output of params and block methods in the hash" do
116
- klass = ImmutableStruct.new(:flappy) do
117
- def lawsuit
118
- 'pending'
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.1
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-01-27 00:00:00.000000000 Z
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.5
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)