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 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)