unique_by 1.0.0 → 2.0.0

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: c877eeb4bee389ff8f304f38825a35ab1ce2c398
4
- data.tar.gz: 87108e22b935547a8deb63355979f9fd2a16eef1
3
+ metadata.gz: 4839ad7e8c93eabad52a34ae43774c3a4fa31ead
4
+ data.tar.gz: d205bd87ab905c57bbc620df6400a36fe9c95fba
5
5
  SHA512:
6
- metadata.gz: e5e50dd6ce9bcb170af31c2e171388c2bec13e650685f62cded4a11fefeaf6d9d8d81a3915f37c4254ba002ce80b1fe0f15938da0356fba6b1020ffac2bc4419
7
- data.tar.gz: 328cdd45129518b3e131ecd8e562ae6e216de0f4ad9da433e2b9e3a9a39c6b597c16fd10d1e3c0d368b43c4ca2ade12c68d5e1b9dc4a34377b169de0b50ef6c4
6
+ metadata.gz: afe1110aa03ea678e269655cbbd8d10ff7cbca5406b62a5193724f5617789db734d93e033a56d6077611e3ef9c54ead59dc5a1f80035c74a73ccbcf29cd5deba
7
+ data.tar.gz: fe7ac2951507f1b2a709f905469881f3e29b4c67d3c1a110b3bd8448d82d869932a13a3758c9de217121b296e989ee50366b17f2cea089ef35deeb1b5babefa4
data/README.md CHANGED
@@ -12,7 +12,9 @@ When do you need this?
12
12
 
13
13
  Add this line to your application's Gemfile:
14
14
 
15
- gem 'unique_by'
15
+ ```ruby
16
+ gem 'unique_by'
17
+ ```
16
18
 
17
19
  And then execute:
18
20
 
@@ -28,84 +30,112 @@ Or install it yourself as:
28
30
 
29
31
  You first need to specify a unique group in your model:
30
32
 
31
- # == Schema Info
32
- #
33
- # Table name: medical_bills
34
- #
35
- # id :integer(11) not null, primary key
36
- # client_id :integer(11)
37
- #
33
+ ```ruby
34
+ # == Schema Info
35
+ #
36
+ # Table name: medical_bills
37
+ #
38
+ # id :integer(11) not null, primary key
39
+ # client_id :integer(11)
40
+ #
38
41
 
39
- class MedicalBill < ActiveRecord::Base
40
- unique_by :client_id, total: 50
41
- end
42
+ class MedicalBill < ActiveRecord::Base
43
+ unique_by client_id: 50 # total of 50 clients
44
+ end
45
+ ```
42
46
 
43
47
  then, you can use these basic methods:
44
48
 
45
- bill1 = MedicalBill.find(123) # from a DB shard for client_id = 1
46
- bill2 = MedicalBill.find(123) # from a DB shard for client_id = 2
49
+ ```ruby
50
+ bill1 = MedicalBill.find(123) # from a DB shard for client_id = 1
51
+ bill2 = MedicalBill.find(123) # from a DB shard for client_id = 2
47
52
 
48
- bill1.unique_id
49
- => "62p"
50
- bill2.unique_id
51
- => "62q"
52
- MedicalBill.find_by_unique_id("62p") # from DB shard for client_id = 1
53
- => #<MedicalBill id: 123, client_id: 1>
54
- MedicalBill.find_by_unique_id("62q") # from DB shard for client_id = 2
55
- => #<MedicalBill id: 123, client_id: 2>
53
+ bill1.unique_id
54
+ => 7873
55
+ bill2.unique_id
56
+ => 7874
57
+ MedicalBill.find_by_unique_id(7873) # from DB shard for client_id = 1
58
+ => #<MedicalBill id: 123, client_id: 1>
59
+ MedicalBill.find_by_unique_id(7874) # from DB shard for client_id = 2
60
+ => #<MedicalBill id: 123, client_id: 2>
61
+ ```
56
62
 
57
63
  You can use the internal methods:
58
64
 
59
- MedicalBill.unique_id_from(1, 123) # gives the unique_id from client_id, id
60
- => "62p"
61
- MedicalBill.id_from("62p") # gives the id
62
- => 123
63
- MedicalBill.id_group_from("62q") # gives the client_id
64
- => 2
65
-
66
- And use bits instead of total:
67
-
68
- class MedicalBill < ActiveRecord::Base
69
- unique_by :client_id, bits: 6 # equivalent to total: 64
70
- end
65
+ ```ruby
66
+ MedicalBill.unique_id_from(123, client_id: 1) # gives the unique_id
67
+ => 7873
68
+ MedicalBill.id_from(7873) # gives the id
69
+ => 123
70
+ MedicalBill.id_group_from(7874) # gives the client_id
71
+ => { client_id: 2 }
72
+ ```
71
73
 
72
74
  You can specify multiple unique group attributes:
73
75
 
74
- class MedicalBill < ActiveRecord::Base
75
- unique_by :client_id, :client_part, total: [50, 5]
76
- end
76
+ ```ruby
77
+ class MedicalBill < ActiveRecord::Base
78
+ unique_by client_id: 50, client_part: 5 # total of 50 clients and 5 parts
79
+ end
80
+ ```
77
81
 
78
82
  ### Multiple tables example
79
83
 
80
- You can supply a block to give your own mechanism for determining the
81
- group:
84
+ You can supply a block to give a custom mechanism for determining the group:
82
85
 
83
- class MedicalBill < ActiveRecord::Base
84
- unique_by(total: 2) { 1 }
85
- end
86
- class UtilityBill < ActiveRecord::Base
87
- unique_by(total: 2) { 2 }
88
- end
86
+ ```ruby
87
+ class MedicalBill < ActiveRecord::Base
88
+ unique_by(type: 2) { { type: 1 } }
89
+ end
90
+ class UtilityBill < ActiveRecord::Base
91
+ unique_by(type: 2) { { type: 2 } }
92
+ end
93
+ ```
89
94
 
90
95
  You can supply both group attributes and a block, and the block can also
91
- return an array:
96
+ return more than one field:
92
97
 
93
- class MedicalBill < ActiveRecord::Base
94
- unique(:client_id, :client_part, total: [50, 5, 10, 20]) { [self.x * self.y, self.z / 2] }
95
- end
98
+ ```ruby
99
+ class MedicalBill < ActiveRecord::Base
100
+ unique_by(client_id: 50, client_part: 5, xy: 10, halfz: 20) do
101
+ { xy: self.x * self.y, halfz: self.z / 2 }
102
+ end
103
+ end
104
+ ```
96
105
 
97
106
  ## Not ActiveRecord
98
107
 
99
108
  The generator module is already included in `ActiveRecord::Base`, but if
100
109
  you want the above methods in another class you can extend it:
101
110
 
102
- class MyClass
103
- extend UniqueBy::Generator
104
-
105
- def self.primary_key
106
- :id # or 'id'
107
- end
108
- end
111
+ ```ruby
112
+ class MyClass
113
+ extend UniqueBy::Generator
114
+
115
+ def self.primary_key
116
+ :id # or 'id'
117
+ end
118
+ end
119
+ ```
120
+
121
+ ## See also
122
+
123
+ After adding the unique groups to the id, the unique_id might turn out pretty
124
+ large. You could use [rebase_attr](https://github.com/odedniv/rebase_attr) to
125
+ fix that:
126
+
127
+ ```ruby
128
+ class MedicalBill < ActiveRecord::Base
129
+ unique_by client_id: 500
130
+ rebase_attr :unique_id, to: 32, readable: true
131
+ end
132
+
133
+ bill = MedicalBill.find(3528918) # from a DB shard for client_id = 1
134
+ bill.unique_id
135
+ => "ywr3bxx"
136
+ MedicalBill.find_by_unique_id(MedicalBill.decode_unique_id("ywr3bxx"))
137
+ => #<MedicalBill id: 3528918, client_id: 1>
138
+ ```
109
139
 
110
140
  ## Contributing
111
141
 
@@ -1,3 +1,3 @@
1
1
  module UniqueBy
2
- VERSION = "1.0.0"
2
+ VERSION = "2.0.0"
3
3
  end
data/lib/unique_by.rb CHANGED
@@ -11,69 +11,65 @@ module UniqueBy
11
11
  # #unique_id => unique_id
12
12
  # ::find_by_unique_id(unique_id) => find_by_id(id_from(unique_id))
13
13
  # ::find_by_unique_id!(unique_id) => find_by_id!(id_from(unique_id))
14
- def unique_by(*group_block_names, total: nil, bits: nil, &group_block)
15
- bits, total = Array(bits), Array(total)
14
+ def unique_by(**group_totals, &group_block)
15
+ raise ArgumentError, "must pass a group definition (Hash of name => total)" if group_totals.empty?
16
+ raise ArgumentError, "group definition must be a Hash of name => Fixnum, #{group_totals.inspect} given" unless group_totals.values.all? { |t| t.is_a?(Fixnum) }
16
17
 
17
- raise ArgumentError, "must pass either total or bits to #unique_by" \
18
- unless total.any? or bits.any?
19
- raise ArgumentError, "both total (#{total.inspect}) and bits (#{bits.inspect}) passed to #unique_by" \
20
- if total.any? and bits.any?
21
- raise ArgumentError, "must pass a group generator block" \
22
- unless group_block_names.any? or block_given?
23
- raise ArgumentError, "amount of group names (#{group_block_names.length}) doesn't match total/bits (#{total.length + bits.length})" \
24
- if (not block_given? and group_block_names.length != total.length + bits.length) or \
25
- (block_given? and group_block_names.length > total.length + bits.length)
26
-
27
- bits = total.map { |t| Math.log2(t).ceil } if bits.empty?
28
- total = bits.map { |b| 2 ** b }
18
+ bits = Hash[group_totals.map { |k, t| [k, Math.log2(t).ceil] }]
19
+ totals = Hash[bits.map { |k, b| [k, 2 ** b] }] # real total
29
20
 
30
21
  pk = primary_key # converting to a local variable
31
22
 
32
- define_singleton_method :"#{pk}_group_value_from" do |group|
33
- Array(group).each_with_index.reduce(0) do |group_value, (g, i)|
34
- raise TypeError, "group must implement #to_i, #{g.inspect} given" \
35
- unless g.respond_to?(:to_i)
36
- (group_value << bits[i]) + (g.to_i % total[i])
23
+ define_singleton_method :"#{pk}_group_value_from" do |**group|
24
+ raise ArgumentError, "unknown #{pk} group keys: #{group.keys - group_totals.keys}" if (group.keys - group_totals.keys).any?
25
+ raise ArgumentError, "missing #{pk} group keys: #{group_totals.keys - group.keys}" if (group_totals.keys - group.keys).any?
26
+ group_totals.keys.reduce(0) do |group_value, group_name|
27
+ g = group[group_name]
28
+ raise TypeError, "#{pk} group #{group_name} must not be nil" if g.nil?
29
+ raise TypeError, "#{pk} group #{group_name} must implement #to_i, #{g.inspect} given" unless g.respond_to?(:to_i)
30
+ (group_value << bits[group_name]) + (g.to_i % totals[group_name])
37
31
  end
38
32
  end
39
33
 
40
- define_singleton_method :"unique_#{pk}_from" do |id, group|
41
- (id.to_i << bits.inject(&:+)) + send(:"#{pk}_group_value_from", group)
34
+ define_singleton_method :"unique_#{pk}_from" do |id, **group|
35
+ break nil if id.nil?
36
+ raise TypeError, "#{pk} must implement #to_i, #{id.inspect} given" unless id.respond_to?(:to_i)
37
+ (id.to_i << bits.values.inject(&:+)) + send(:"#{pk}_group_value_from", **group)
42
38
  end
43
39
 
44
- define_singleton_method :"#{pk}_from" do |id|
45
- id.to_i >> bits.inject(&:+)
40
+ define_singleton_method :"#{pk}_from" do |unique_id|
41
+ break nil if unique_id.nil?
42
+ raise TypeError, "unique_#{pk} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
43
+ unique_id.to_i >> bits.values.inject(&:+)
46
44
  end
47
45
 
48
- define_singleton_method :"#{pk}_group_from" do |id|
49
- group = bits.reverse.zip(total.reverse).map do |b, t|
50
- g = id & (t - 1)
51
- id >>= b
52
- g
53
- end.reverse
54
- group.length == 1 ? group[0] : group
46
+ define_singleton_method :"#{pk}_group_from" do |unique_id|
47
+ break nil if unique_id.nil?
48
+ raise TypeError, "unique_#{pk} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
49
+ Hash[group_totals.keys.reverse.map do |group_name|
50
+ g = unique_id & (totals[group_name] - 1)
51
+ unique_id >>= bits[group_name]
52
+ [group_name, g]
53
+ end.reverse]
55
54
  end
56
55
 
57
56
  define_method :"#{pk}_group" do
58
- group = group_block_names.map { |group_block_name| send(group_block_name) }
59
- group.push(*Array(instance_eval(&group_block))) if group_block
60
- raise ArgumentError, "amount of groups (#{group.length}) doesn't match amount of bits/total (#{bits.length})" if group.length != bits.length
61
- group.length == 1 ? group[0] : group
57
+ group_from_block = group_block ? instance_eval(&group_block) : {}
58
+ raise TypeError, "#{pk} group block must return a Hash with any of the following keys: #{group_totals.keys}, #{group_from_block.inspect} given" unless group_from_block.is_a?(Hash)
59
+ raise ArgumentError, "unknown #{pk} group passed to block: #{group_from_block.keys - group_totals.keys}" if (group_from_block.keys - group_totals.keys).any?
60
+ Hash[(group_totals.keys - group_from_block.keys).map { |group_name| [group_name, send(group_name)] }].merge(group_from_block)
62
61
  end
63
62
 
64
63
  define_method :"unique_#{pk}" do
65
- primary_key = send(pk)
66
- raise TypeError, "#{pk} must implement #to_i, #{primary_key.inspect} given" \
67
- unless primary_key.respond_to?(:to_i)
68
- self.class.send(:"unique_#{pk}_from", primary_key.to_i, send(:"#{pk}_group"))
64
+ self.class.send(:"unique_#{pk}_from", send(pk), **send(:"#{pk}_group"))
69
65
  end
70
66
 
71
- define_singleton_method :"find_by_unique_#{pk}" do |id|
72
- send(:"find_by_#{pk}", send(:"#{pk}_from", id))
67
+ define_singleton_method :"find_by_unique_#{pk}" do |unique_id|
68
+ send(:"find_by_#{pk}", send(:"#{pk}_from", unique_id))
73
69
  end
74
70
 
75
- define_singleton_method :"find_by_unique_#{pk}!" do |id|
76
- send(:"find_by_#{pk}!", send(:"#{pk}_from", id))
71
+ define_singleton_method :"find_by_unique_#{pk}!" do |unique_id|
72
+ send(:"find_by_#{pk}!", send(:"#{pk}_from", unique_id))
77
73
  end
78
74
  end
79
75
  end
@@ -44,7 +44,7 @@ describe UniqueBy::Generator do
44
44
  let(:klass) do
45
45
  Class.new(Struct.new(:bill_id, :client_id)) do
46
46
  include BaseBill
47
- unique_by(:client_id, total: 10)
47
+ unique_by(client_id: 10)
48
48
  end
49
49
  end
50
50
 
@@ -57,17 +57,28 @@ describe UniqueBy::Generator do
57
57
  subject { bill1 }
58
58
 
59
59
  describe "class methods" do
60
- specify { expect(klass.bill_id_group_value_from(2)).to eq(2 % 16) }
61
- specify { expect(klass.unique_bill_id_from(431, 2)).to eq(unique_id) }
60
+ specify { expect(klass.bill_id_group_value_from(client_id: 2)).to eq(2 % 16) }
61
+ specify { expect(klass.unique_bill_id_from(431, client_id: 2)).to eq(unique_id) }
62
62
  specify { expect(klass.bill_id_from(unique_id)).to eq(431) }
63
- specify { expect(klass.bill_id_group_from(unique_id)).to eq(2 % 16) }
63
+ specify { expect(klass.bill_id_group_from(unique_id)).to eq(client_id: 2 % 16) }
64
+
65
+ context "nil id" do
66
+ specify { expect(klass.unique_bill_id_from(nil, client_id: 2)).to be_nil }
67
+ specify { expect(klass.bill_id_from(nil)).to be_nil }
68
+ specify { expect(klass.bill_id_group_from(nil)).to be_nil }
69
+ end
64
70
 
65
71
  include_context "finder methods"
66
72
  end
67
73
 
68
74
  describe "instance methods" do
69
- its(:bill_id_group) { should == 2 }
75
+ its(:bill_id_group) { should == { client_id: 2 } }
70
76
  its(:unique_bill_id) { should == unique_id }
77
+
78
+ context "nil id" do
79
+ before { bill1.bill_id = nil }
80
+ its(:unique_bill_id) { should be_nil }
81
+ end
71
82
  end
72
83
  end
73
84
 
@@ -76,16 +87,16 @@ describe UniqueBy::Generator do
76
87
  subject { bill2 }
77
88
 
78
89
  describe "class methods" do
79
- specify { expect(klass.bill_id_group_value_from(7)).to eq(7 % 16) }
80
- specify { expect(klass.unique_bill_id_from(431, 7)).to eq(unique_id) }
90
+ specify { expect(klass.bill_id_group_value_from(client_id: 7)).to eq(7 % 16) }
91
+ specify { expect(klass.unique_bill_id_from(431, client_id: 7)).to eq(unique_id) }
81
92
  specify { expect(klass.bill_id_from(unique_id)).to eq(431) }
82
- specify { expect(klass.bill_id_group_from(unique_id)).to eq(7 % 16) }
93
+ specify { expect(klass.bill_id_group_from(unique_id)).to eq(client_id: 7 % 16) }
83
94
 
84
95
  include_context "finder methods"
85
96
  end
86
97
 
87
98
  describe "instance methods" do
88
- its(:bill_id_group) { should == 7 }
99
+ its(:bill_id_group) { should == { client_id: 7 } }
89
100
  its(:unique_bill_id) { should == unique_id }
90
101
  end
91
102
  end
@@ -94,12 +105,12 @@ describe UniqueBy::Generator do
94
105
  context "tables" do
95
106
  let(:medical_klass) do
96
107
  Class.new(TablesBase) do
97
- unique_by(total: 2) { 10 }
108
+ unique_by(type: 2) { { type: 10 } }
98
109
  end
99
110
  end
100
111
  let(:utility_klass) do
101
112
  Class.new(TablesBase) do
102
- unique_by(total: 2) { 11 }
113
+ unique_by(type: 2) { { type: 11 } }
103
114
  end
104
115
  end
105
116
 
@@ -113,16 +124,16 @@ describe UniqueBy::Generator do
113
124
  subject { medical_bill }
114
125
 
115
126
  describe "class methods" do
116
- specify { expect(medical_klass.bill_id_group_value_from(10)).to eq(10 % 2) }
117
- specify { expect(medical_klass.unique_bill_id_from(839, 10)).to eq(unique_id) }
127
+ specify { expect(medical_klass.bill_id_group_value_from(type: 10)).to eq(10 % 2) }
128
+ specify { expect(medical_klass.unique_bill_id_from(839, type: 10)).to eq(unique_id) }
118
129
  specify { expect(medical_klass.bill_id_from(unique_id)).to eq(839) }
119
- specify { expect(medical_klass.bill_id_group_from(unique_id)).to eq(10 % 2) }
130
+ specify { expect(medical_klass.bill_id_group_from(unique_id)).to eq(type: 10 % 2) }
120
131
 
121
132
  include_context "finder methods"
122
133
  end
123
134
 
124
135
  describe "instance methods" do
125
- its(:bill_id_group) { should == 10 }
136
+ its(:bill_id_group) { should == { type: 10 } }
126
137
  its(:unique_bill_id) { should == unique_id }
127
138
  end
128
139
  end
@@ -133,16 +144,16 @@ describe UniqueBy::Generator do
133
144
  subject { utility_bill }
134
145
 
135
146
  describe "class methods" do
136
- specify { expect(utility_klass.bill_id_group_value_from(11)).to eq(11 % 2) }
137
- specify { expect(utility_klass.unique_bill_id_from(839, 11)).to eq(unique_id) }
147
+ specify { expect(utility_klass.bill_id_group_value_from(type: 11)).to eq(11 % 2) }
148
+ specify { expect(utility_klass.unique_bill_id_from(839, type: 11)).to eq(unique_id) }
138
149
  specify { expect(utility_klass.bill_id_from(unique_id)).to eq(839) }
139
- specify { expect(utility_klass.bill_id_group_from(unique_id)).to eq(11 % 2) }
150
+ specify { expect(utility_klass.bill_id_group_from(unique_id)).to eq(type: 11 % 2) }
140
151
 
141
152
  include_context "finder methods"
142
153
  end
143
154
 
144
155
  describe "instance methods" do
145
- its(:bill_id_group) { should == 11 }
156
+ its(:bill_id_group) { should == { type: 11 } }
146
157
  its(:unique_bill_id) { should == unique_id }
147
158
  end
148
159
  end
@@ -150,9 +161,8 @@ describe UniqueBy::Generator do
150
161
 
151
162
  context "sharded tables" do
152
163
  let(:medical_klass) do
153
-
154
164
  Class.new(ShardedTablesBase) do
155
- unique_by(:client_id, :x, total: [10, 200, 2, 20]) { [10, y] }
165
+ unique_by(client_id: 10, x: 200, type: 2, y: 20) { { type: 10, y: y } }
156
166
  def x
157
167
  53
158
168
  end
@@ -164,7 +174,7 @@ describe UniqueBy::Generator do
164
174
 
165
175
  let(:utility_klass) do
166
176
  Class.new(ShardedTablesBase) do
167
- unique_by(:client_id, :x, bits: [4, 8, 1, 5]) { [11, y] }
177
+ unique_by(client_id: 2**4, x: 2**8, type: 2**1, y: 2**5) { { type: 11, y: y } }
168
178
  def x
169
179
  853
170
180
  end
@@ -178,17 +188,17 @@ describe UniqueBy::Generator do
178
188
  let(:utility_bill) { utility_klass.new(9428, 8, 255) }
179
189
  let(:id) { 9428 }
180
190
 
181
- context "with total" do
191
+ context "medical bill" do
182
192
  let(:klass) { medical_klass }
183
193
  subject { medical_bill }
184
194
 
185
- let(:tempered_group) { [5 % 16, 53 % 256, 10 % 2, 20 % 32] }
195
+ let(:tempered_group) { { client_id: 5 % 16, x: 53 % 256, type: 10 % 2, y: 20 % 32 } }
186
196
  let(:group_value) { ((5 % 16) << 14) + ((53 % 256) << 6) + ((10 % 2) << 5) + (20 % 32) }
187
197
  let(:unique_id) { (9428 << 18) + group_value }
188
198
 
189
199
  describe "class methods" do
190
- specify { expect(medical_klass.bill_id_group_value_from([5, 53, 10, 20])).to eq(group_value) }
191
- specify { expect(medical_klass.unique_bill_id_from(9428, [5, 53, 10, 20])).to eq(unique_id) }
200
+ specify { expect(medical_klass.bill_id_group_value_from(client_id: 5, x: 53, type: 10, y: 20)).to eq(group_value) }
201
+ specify { expect(medical_klass.unique_bill_id_from(9428, client_id: 5, x: 53, type: 10, y: 20)).to eq(unique_id) }
192
202
  specify { expect(medical_klass.bill_id_from(unique_id)).to eq(9428) }
193
203
  specify { expect(medical_klass.bill_id_group_from(unique_id)).to eq(tempered_group) }
194
204
 
@@ -196,22 +206,22 @@ describe UniqueBy::Generator do
196
206
  end
197
207
 
198
208
  describe "instance methods" do
199
- its(:bill_id_group) { should == [5, 53, 10, 20] }
209
+ its(:bill_id_group) { should == { client_id: 5, x: 53, type: 10, y: 20 } }
200
210
  its(:unique_bill_id) { should == unique_id }
201
211
  end
202
212
  end
203
213
 
204
- context "with bits" do
214
+ context "utility bill" do
205
215
  let(:klass) { utility_klass }
206
216
  subject { utility_bill }
207
217
 
208
- let(:tempered_group) { [8 % 16, 853 % 256, 11 % 2, 40 % 32] }
218
+ let(:tempered_group) { { client_id: 8 % 16, x: 853 % 256, type: 11 % 2, y: 40 % 32 } }
209
219
  let(:group_value) { ((8 % 16) << 14) + ((853 % 256) << 6) + ((11 % 2) << 5) + (40 % 32) }
210
220
  let(:unique_id) { (9428 << 18) + group_value }
211
221
 
212
222
  describe "class methods" do
213
- specify { expect(utility_klass.bill_id_group_value_from([8, 853, 11, 40])).to eq(group_value) }
214
- specify { expect(utility_klass.unique_bill_id_from(9428, [8, 853, 11, 40])).to eq(unique_id) }
223
+ specify { expect(utility_klass.bill_id_group_value_from(client_id: 8, x: 853, type: 11, y: 40)).to eq(group_value) }
224
+ specify { expect(utility_klass.unique_bill_id_from(9428, client_id: 8, x: 853, type: 11, y: 40)).to eq(unique_id) }
215
225
  specify { expect(utility_klass.bill_id_from(unique_id)).to eq(9428) }
216
226
  specify { expect(utility_klass.bill_id_group_from(unique_id)).to eq(tempered_group) }
217
227
 
@@ -219,35 +229,93 @@ describe UniqueBy::Generator do
219
229
  end
220
230
 
221
231
  describe "instance methods" do
222
- its(:bill_id_group) { should == [8, 853, 11, 40] }
232
+ its(:bill_id_group) { should == { client_id: 8, x: 853, type: 11, y: 40 } }
223
233
  its(:unique_bill_id) { should == unique_id }
224
234
  end
225
235
  end
226
236
  end
227
237
 
228
238
  context "errors" do
229
- let(:klass) do
230
- Class.new do
231
- include BaseBill
239
+ describe "#unique_by" do
240
+ let(:klass) do
241
+ Class.new do
242
+ include BaseBill
243
+ end
232
244
  end
233
- end
234
245
 
235
- describe "#unique_by" do
236
- specify { expect { klass.unique_by(:x) }.to raise_error(ArgumentError, "must pass either total or bits to #unique_by") }
237
- specify { expect { klass.unique_by(:x, total: [5, 4], bits: [2, 7]) }.to raise_error(ArgumentError, "both total ([5, 4]) and bits ([2, 7]) passed to #unique_by") }
238
- specify { expect { klass.unique_by(total: 5) }.to raise_error(ArgumentError, "must pass a group generator block") }
239
- specify { expect { klass.unique_by(:x, :y, total: 5) }.to raise_error(ArgumentError, "amount of group names (2) doesn't match total/bits (1)") }
240
- specify { expect { klass.unique_by(:x, :y, total: [3, 2, 5]) }.to raise_error(ArgumentError, "amount of group names (2) doesn't match total/bits (3)") }
241
- specify { expect { klass.unique_by(:x, :y, total: [3, 2, 5]) { } }.not_to raise_error }
242
- specify { expect { klass.unique_by(:x, :y, :z, bits: [3, 2]) { } }.to raise_error(ArgumentError, "amount of group names (3) doesn't match total/bits (2)") }
246
+ specify { expect { klass.unique_by }.to raise_error(ArgumentError, "must pass a group definition (Hash of name => total)") }
247
+ specify { expect { klass.unique_by(x: :a) }.to raise_error(ArgumentError, "group definition must be a Hash of name => Fixnum, {:x=>:a} given") }
243
248
  end
244
249
 
245
250
  describe "class methods" do
246
- pending
251
+ let(:klass) do
252
+ Class.new(Struct.new(:bill_id, :client_id)) do
253
+ include BaseBill
254
+ unique_by client_id: 10
255
+ end
256
+ end
257
+
258
+ specify { expect { klass.bill_id_group_value_from(x: 2) }.to raise_error(ArgumentError, "unknown bill_id group keys: [:x]") }
259
+ specify { expect { klass.bill_id_group_value_from(client_id: 5, x: 2) }.to raise_error(ArgumentError, "unknown bill_id group keys: [:x]") }
260
+ specify { expect { klass.bill_id_group_value_from() }.to raise_error(ArgumentError, "missing bill_id group keys: [:client_id]") }
261
+ specify { expect { klass.bill_id_group_value_from(client_id: nil) }.to raise_error(TypeError, "bill_id group client_id must not be nil") }
262
+ specify { expect { klass.bill_id_group_value_from(client_id: :a) }.to raise_error(TypeError, "bill_id group client_id must implement #to_i, :a given") }
263
+ specify { expect { klass.unique_bill_id_from(431, client_id: nil) }.to raise_error(TypeError, "bill_id group client_id must not be nil") }
264
+ specify { expect { klass.unique_bill_id_from(431, client_id: :a) }.to raise_error(TypeError, "bill_id group client_id must implement #to_i, :a given") }
265
+ specify { expect { klass.unique_bill_id_from(:a, client_id: 5) }.to raise_error(TypeError, "bill_id must implement #to_i, :a given") }
266
+ specify { expect { klass.bill_id_from(:a) }.to raise_error(TypeError, "unique_bill_id must implement #to_i, :a given") }
267
+ specify { expect { klass.bill_id_group_from(:a) }.to raise_error(TypeError, "unique_bill_id must implement #to_i, :a given") }
247
268
  end
248
269
 
249
270
  describe "instance methods" do
250
- pending
271
+ let(:klass) do
272
+ Class.new(Struct.new(:bill_id, :client_id, :block)) do
273
+ include BaseBill
274
+ unique_by(client_id: 10, x: 5) { block }
275
+ end
276
+ end
277
+
278
+ context "nil group" do
279
+ let(:bill) { klass.new(431, nil, { x: 2 }) }
280
+ specify { expect { bill.unique_bill_id }.to raise_error(TypeError, "bill_id group client_id must not be nil") }
281
+ end
282
+
283
+ context "invalid group" do
284
+ let(:bill) { klass.new(431, :a, { x: 2 }) }
285
+ specify { expect { bill.unique_bill_id }.to raise_error(TypeError, "bill_id group client_id must implement #to_i, :a given") }
286
+ end
287
+
288
+ context "nil block group" do
289
+ let(:bill) { klass.new(431, 5, { x: nil }) }
290
+ specify { expect { bill.unique_bill_id }.to raise_error(TypeError, "bill_id group x must not be nil") }
291
+ end
292
+
293
+ context "invalid block group" do
294
+ let(:bill) { klass.new(431, 5, :a) }
295
+ specify { expect { bill.unique_bill_id }.to raise_error(TypeError, "bill_id group block must return a Hash with any of the following keys: [:client_id, :x], :a given") }
296
+ end
297
+
298
+ context "invalid block group value" do
299
+ let(:bill) { klass.new(431, 5, { x: :a }) }
300
+ specify { expect { bill.unique_bill_id }.to raise_error(TypeError, "bill_id group x must implement #to_i, :a given") }
301
+ end
302
+
303
+ context "unknown block group keys" do
304
+ context "too few" do
305
+ let(:bill) { klass.new(431, 5, { }) }
306
+ specify { expect { bill.unique_bill_id }.to raise_error(NameError, "undefined method `x' for #<struct bill_id=431, client_id=5, block={}>") }
307
+ end
308
+
309
+ context "too many" do
310
+ let(:bill) { klass.new(431, 5, { x: 2, y: 12 }) }
311
+ specify { expect { bill.unique_bill_id }.to raise_error(ArgumentError, "unknown bill_id group passed to block: [:y]") }
312
+ end
313
+
314
+ context "different" do
315
+ let(:bill) { klass.new(431, 5, { y: 12 }) }
316
+ specify { expect { bill.unique_bill_id }.to raise_error(ArgumentError, "unknown bill_id group passed to block: [:y]") }
317
+ end
318
+ end
251
319
  end
252
320
  end
253
321
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unique_by
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oded Niv
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-17 00:00:00.000000000 Z
11
+ date: 2014-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler