unique_by 2.1.0 → 3.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: 5403c00bf28bbe7858fa14d407bdbda99527de7e
4
- data.tar.gz: e9e0dcd9a442febb74ffd046edda31edfc9f4f9d
3
+ metadata.gz: 3318e49786c29b9bc711ccd7cedf5b62b0795ddf
4
+ data.tar.gz: 33d06afb8219b74f89614b0bf30f2d2d1f65aadb
5
5
  SHA512:
6
- metadata.gz: 2d4d75f32cde147d62e1c6e0a51580c0e4d339fa964d0675413562415ae58b49c650cf5752428d1d164d16e1576d88950b7d7e04d260ef0d2b6be16b8a9a1bce
7
- data.tar.gz: ec47fa855292e7e207ada64eb92f54852c114578297a68adedf5895edb0b35d7f5b68171a6a2c94b1a894f77e70484e14af4ed5304d61dcb56b3e9cdf4dc1375
6
+ metadata.gz: 30fa8eeaf072a6b026a68a9b12868eb9d8069419f586b0521941756071cb7b9a5b4c940954ddfea4ef6dcf8ce57dcebe5253d8620e13aa8be35e83f1a165288f
7
+ data.tar.gz: 0b27d32f656175a51bd363f85f741bf8b3bf3e094b98db695d75c128170e57429d0cce02429c770a09322fc3504c59511efd4618b0253f6407b984dd6e5bb272
@@ -0,0 +1,38 @@
1
+ **3.0.0**
2
+ * Calculate unique value by multiplying in exact total rather closest power of
3
+ 2 (bitwise).
4
+
5
+ ***INCOMPATIBLE:*** when your totals are not powers of 2. If you already depend
6
+ on previous values of this gem, change your values to the
7
+ closest power of 2.
8
+
9
+ For example:
10
+
11
+ ```ruby
12
+ unique_by client_id: 500
13
+ ```
14
+
15
+ Should be:
16
+
17
+ ```ruby
18
+ unique_by client_id: 512
19
+ ```
20
+
21
+ * Allow passing `:primary_key` explicitly.
22
+
23
+ *MINOR INCOMPATIBLE:* if your group was named `primary_key`.
24
+
25
+ **2.1.0**
26
+ * Remove useless finder methods, you should create your own based on the group.
27
+
28
+ *MINOR INCOMPATIBLE:* if you used the finder methods.
29
+ * Using `generate_method` gem, which means generated methods are 'inherited'
30
+ instead of defined in the class itself.
31
+
32
+ *MINOR INCOMPATIBLE:* when expected to override your methods.
33
+
34
+ **2.0.0**
35
+ * *MINOR INCOMPATIBLE:* key-value arguments instead of totals/bits.
36
+
37
+ **1.0.0**
38
+ * First stable version.
data/README.md CHANGED
@@ -56,7 +56,7 @@ bill2.unique_id
56
56
  => 7874
57
57
  ```
58
58
 
59
- You can use the internal methods:
59
+ You can use the singleton methods:
60
60
 
61
61
  ```ruby
62
62
  MedicalBill.unique_id_from(123, client_id: 1) # gives the unique_id
@@ -96,22 +96,39 @@ You can supply a block to give a custom mechanism for determining the group:
96
96
 
97
97
  ```ruby
98
98
  class MedicalBill < ActiveRecord::Base
99
- unique_by(type: 2) { { type: 1 } }
99
+ unique_by(type_index: 2) { { type_index: 0 } }
100
100
  end
101
101
  class UtilityBill < ActiveRecord::Base
102
- unique_by(type: 2) { { type: 2 } }
102
+ unique_by(type_index: 2) { { type_index: 1 } }
103
103
  end
104
104
  ```
105
105
 
106
- You can supply both group attributes and a block, and the block can also
107
- return more than one field:
106
+ Groups can also be served as a singleton method:
108
107
 
109
108
  ```ruby
110
109
  class MedicalBill < ActiveRecord::Base
111
- unique_by(client_id: 50, client_part: 5, xy: 10, halfz: 20) do
112
- { xy: self.x * self.y, halfz: self.z / 2 }
110
+ unique_by type_index: 2
111
+ def self.type_index
112
+ 0
113
113
  end
114
114
  end
115
+ class UtilityBill < ActiveRecord::Base
116
+ unique_by type_index: 2
117
+ def self.type_index
118
+ 1
119
+ end
120
+ end
121
+ ```
122
+
123
+ In either case, if all the groups are specified as singleton methods or served
124
+ within the block WITHOUT using the instance (`self`), you can use the singleton
125
+ methods without explicitly specifying the groups for this class:
126
+
127
+ ```ruby
128
+ MedicalBill.unique_id_from(123)
129
+ => 247
130
+ UtilityBill.unique_id_from(123)
131
+ => 246
115
132
  ```
116
133
 
117
134
  It is recommended to create finder methods that will find records according to
@@ -121,14 +138,25 @@ their id group, like so:
121
138
  module Bill
122
139
  module_function
123
140
  def find_by_unique_id(unique_id)
124
- case MedicalBill.id_group_from(unique_id)[:type]
125
- when 1 then MedicalBill.find(MedicalBill.id_from(unique_id))
126
- when 2 then UtilityBill.find(UtilityBill.id_from(unique_id))
141
+ case MedicalBill.id_group_from(unique_id)[:type_index]
142
+ when MedicalBill.type_index then MedicalBill.find(MedicalBill.id_from(unique_id))
143
+ when UtilityBill.type_index then UtilityBill.find(UtilityBill.id_from(unique_id))
127
144
  end
128
145
  end
129
146
  end
130
147
  ```
131
148
 
149
+ You can supply both group attributes and a block, and the block can also
150
+ return more than one field:
151
+
152
+ ```ruby
153
+ class MedicalBill < ActiveRecord::Base
154
+ unique_by(client_id: 50, client_part: 5, xy: 10, halfz: 20) do
155
+ { xy: self.x * self.y, halfz: self.z / 2 }
156
+ end
157
+ end
158
+ ```
159
+
132
160
  ## Not ActiveRecord
133
161
 
134
162
  The generator module is already included in `ActiveRecord::Base`, but if
@@ -138,6 +166,10 @@ you want the above methods in another class you can extend it:
138
166
  class MyClass
139
167
  extend UniqueBy::Generator
140
168
 
169
+ unique_by ..., primary_key: :id
170
+ #
171
+ # OR
172
+ #
141
173
  def self.primary_key
142
174
  :id # or 'id'
143
175
  end
@@ -152,7 +184,7 @@ fix that:
152
184
 
153
185
  ```ruby
154
186
  class MedicalBill < ActiveRecord::Base
155
- unique_by client_id: 500
187
+ unique_by client_id: 32**2 # two base32 letters
156
188
  rebase_attr :unique_id, to: 32, readable: true # digits and leters, without '0', 'o', '1' and 'l'
157
189
  end
158
190
 
@@ -12,60 +12,79 @@ module UniqueBy
12
12
  # #unique_id => unique_id
13
13
  # ::find_by_unique_id(unique_id) => find_by_id(id_from(unique_id))
14
14
  # ::find_by_unique_id!(unique_id) => find_by_id!(id_from(unique_id))
15
- def unique_by(**group_totals, &group_block)
15
+ def unique_by(primary_key: self.primary_key, **group_totals, &group_block)
16
16
  raise ArgumentError, "must pass a group definition (Hash of name => total)" if group_totals.empty?
17
17
  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) }
18
18
 
19
- bits = Hash[group_totals.map { |k, t| [k, Math.log2(t).ceil] }]
20
- totals = Hash[bits.map { |k, b| [k, 2 ** b] }] # real total
19
+ generate_singleton_methods do
20
+ define_method :"#{primary_key}_group" do |**group|
21
+ raise ArgumentError, "unknown #{primary_key} group keys: #{group.keys - group_totals.keys}" if (group.keys - group_totals.keys).any?
22
+ next group if (group_totals.keys - group.keys).none?
21
23
 
22
- pk = primary_key # converting to a local variable
24
+ group_from_block = group_block ? instance_eval(&group_block) : {}
25
+ raise TypeError, "#{primary_key} 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)
26
+ raise ArgumentError, "unknown #{primary_key} group passed to block: #{group_from_block.keys - group_totals.keys}" if (group_from_block.keys - group_totals.keys).any?
27
+ group.update(group_from_block)
28
+ next group if (group_totals.keys - group.keys).none?
23
29
 
24
- generate_singleton_methods do
25
- define_method :"#{pk}_group_value_from" do |**group|
26
- raise ArgumentError, "unknown #{pk} group keys: #{group.keys - group_totals.keys}" if (group.keys - group_totals.keys).any?
27
- raise ArgumentError, "missing #{pk} group keys: #{group_totals.keys - group.keys}" if (group_totals.keys - group.keys).any?
30
+ group.update(Hash[(group_totals.keys - group.keys).map { |group_name| [group_name, send(group_name)] }])
31
+
32
+ group
33
+ end
34
+
35
+ define_method :"#{primary_key}_group_value_from" do |**group|
36
+ group = send(:"#{primary_key}_group", **group)
28
37
  group_totals.keys.reduce(0) do |group_value, group_name|
29
38
  g = group[group_name]
30
- raise TypeError, "#{pk} group #{group_name} must not be nil" if g.nil?
31
- raise TypeError, "#{pk} group #{group_name} must implement #to_i, #{g.inspect} given" unless g.respond_to?(:to_i)
32
- (group_value << bits[group_name]) + (g.to_i % totals[group_name])
39
+ raise TypeError, "#{primary_key} group #{group_name} must not be nil" if g.nil?
40
+ raise TypeError, "#{primary_key} group #{group_name} must implement #to_i, #{g.inspect} given" unless g.respond_to?(:to_i)
41
+ (group_value * group_totals[group_name]) + (g.to_i % group_totals[group_name])
33
42
  end
34
43
  end
35
44
 
36
- define_method :"unique_#{pk}_from" do |id, **group|
45
+ define_method :"unique_#{primary_key}_from" do |id, **group|
37
46
  break nil if id.nil?
38
- raise TypeError, "#{pk} must implement #to_i, #{id.inspect} given" unless id.respond_to?(:to_i)
39
- (id.to_i << bits.values.inject(&:+)) + send(:"#{pk}_group_value_from", **group)
47
+ raise TypeError, "#{primary_key} must implement #to_i, #{id.inspect} given" unless id.respond_to?(:to_i)
48
+ (id.to_i * group_totals.values.inject(&:*)) + send(:"#{primary_key}_group_value_from", **group)
40
49
  end
41
50
 
42
- define_method :"#{pk}_from" do |unique_id|
51
+ define_method :"#{primary_key}_from" do |unique_id|
43
52
  break nil if unique_id.nil?
44
- raise TypeError, "unique_#{pk} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
45
- unique_id.to_i >> bits.values.inject(&:+)
53
+ raise TypeError, "unique_#{primary_key} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
54
+ unique_id.to_i / group_totals.values.inject(&:*)
46
55
  end
47
56
 
48
- define_method :"#{pk}_group_from" do |unique_id|
57
+ define_method :"#{primary_key}_group_from" do |unique_id|
49
58
  break nil if unique_id.nil?
50
- raise TypeError, "unique_#{pk} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
59
+ raise TypeError, "unique_#{primary_key} must implement #to_i, #{unique_id.inspect} given" unless unique_id.respond_to?(:to_i)
51
60
  Hash[group_totals.keys.reverse.map do |group_name|
52
- g = unique_id & (totals[group_name] - 1)
53
- unique_id >>= bits[group_name]
61
+ g = unique_id % group_totals[group_name]
62
+ unique_id /= group_totals[group_name]
54
63
  [group_name, g]
55
64
  end.reverse]
56
65
  end
57
66
  end
58
67
 
59
68
  generate_methods do
60
- define_method :"#{pk}_group" do
69
+ define_method :"#{primary_key}_group" do
61
70
  group_from_block = group_block ? instance_eval(&group_block) : {}
62
- 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)
63
- raise ArgumentError, "unknown #{pk} group passed to block: #{group_from_block.keys - group_totals.keys}" if (group_from_block.keys - group_totals.keys).any?
64
- Hash[(group_totals.keys - group_from_block.keys).map { |group_name| [group_name, send(group_name)] }].merge(group_from_block)
71
+ raise TypeError, "#{primary_key} 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)
72
+ raise ArgumentError, "unknown #{primary_key} group passed to block: #{group_from_block.keys - group_totals.keys}" if (group_from_block.keys - group_totals.keys).any?
73
+ group = group_from_block.merge( # like reverse_merge, as we don't set keys that were in the block
74
+ Hash[
75
+ (group_totals.keys - group_from_block.keys).map do |group_name|
76
+ [
77
+ group_name,
78
+ (respond_to?(group_name) or not self.class.respond_to?(group_name)) ? send(group_name) : self.class.send(group_name) # would rather crash on instance method missing
79
+ ]
80
+ end
81
+ ]
82
+ )
83
+ group
65
84
  end
66
85
 
67
- define_method :"unique_#{pk}" do
68
- self.class.send(:"unique_#{pk}_from", send(pk), **send(:"#{pk}_group"))
86
+ define_method :"unique_#{primary_key}" do
87
+ self.class.send(:"unique_#{primary_key}_from", send(primary_key), **send(:"#{primary_key}_group"))
69
88
  end
70
89
  end
71
90
  end
@@ -1,3 +1,3 @@
1
1
  module UniqueBy
2
- VERSION = "2.1.0"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -23,6 +23,31 @@ class ShardedTablesBase < Struct.new(:bill_id, :client_id, :x)
23
23
  end
24
24
 
25
25
  describe UniqueBy::Generator do
26
+ shared_examples_for "unique by" do |id:, group:, tempered_group:, group_value:, unique_id:|
27
+ describe "class methods" do
28
+ specify { expect(klass.bill_id_group_value_from(group)).to eq(group_value) }
29
+ specify { expect(klass.unique_bill_id_from(id, **group)).to eq(unique_id) }
30
+ specify { expect(klass.bill_id_from(unique_id)).to eq(id) }
31
+ specify { expect(klass.bill_id_group_from(unique_id)).to eq(tempered_group) }
32
+
33
+ context "nil id" do
34
+ specify { expect(klass.unique_bill_id_from(nil, client_id: 2)).to be_nil }
35
+ specify { expect(klass.bill_id_from(nil)).to be_nil }
36
+ specify { expect(klass.bill_id_group_from(nil)).to be_nil }
37
+ end
38
+ end
39
+
40
+ describe "instance methods" do
41
+ its(:bill_id_group) { should == group }
42
+ its(:unique_bill_id) { should == unique_id }
43
+
44
+ context "nil id" do
45
+ before { bill.bill_id = nil }
46
+ its(:unique_bill_id) { should be_nil }
47
+ end
48
+ end
49
+ end
50
+
26
51
  context "shards" do
27
52
  let(:klass) do
28
53
  Class.new(Struct.new(:bill_id, :client_id)) do
@@ -33,51 +58,29 @@ describe UniqueBy::Generator do
33
58
 
34
59
  let(:bill1) { klass.new(431, 2) }
35
60
  let(:bill2) { klass.new(431, 7) }
36
- let(:id) { 431 }
37
61
 
38
62
  context "bill1" do
39
- let(:unique_id) { (431 << 4) + (2 % 16) }
40
- subject { bill1 }
41
-
42
- describe "class methods" do
43
- specify { expect(klass.bill_id_group_value_from(client_id: 2)).to eq(2 % 16) }
44
- specify { expect(klass.unique_bill_id_from(431, client_id: 2)).to eq(unique_id) }
45
- specify { expect(klass.bill_id_from(unique_id)).to eq(431) }
46
- specify { expect(klass.bill_id_group_from(unique_id)).to eq(client_id: 2 % 16) }
47
-
48
- context "nil id" do
49
- specify { expect(klass.unique_bill_id_from(nil, client_id: 2)).to be_nil }
50
- specify { expect(klass.bill_id_from(nil)).to be_nil }
51
- specify { expect(klass.bill_id_group_from(nil)).to be_nil }
52
- end
53
- end
54
-
55
- describe "instance methods" do
56
- its(:bill_id_group) { should == { client_id: 2 } }
57
- its(:unique_bill_id) { should == unique_id }
58
-
59
- context "nil id" do
60
- before { bill1.bill_id = nil }
61
- its(:unique_bill_id) { should be_nil }
62
- end
63
- end
63
+ subject(:bill) { bill1 }
64
+
65
+ it_behaves_like "unique by", {
66
+ id: 431,
67
+ group: { client_id: 2 },
68
+ tempered_group: { client_id: 2 % 10 },
69
+ group_value: group_value = 2 % 10,
70
+ unique_id: (431 * 10) + group_value,
71
+ }
64
72
  end
65
73
 
66
74
  context "bill2" do
67
- let(:unique_id) { (431 << 4) + (7 % 16) }
68
- subject { bill2 }
69
-
70
- describe "class methods" do
71
- specify { expect(klass.bill_id_group_value_from(client_id: 7)).to eq(7 % 16) }
72
- specify { expect(klass.unique_bill_id_from(431, client_id: 7)).to eq(unique_id) }
73
- specify { expect(klass.bill_id_from(unique_id)).to eq(431) }
74
- specify { expect(klass.bill_id_group_from(unique_id)).to eq(client_id: 7 % 16) }
75
- end
76
-
77
- describe "instance methods" do
78
- its(:bill_id_group) { should == { client_id: 7 } }
79
- its(:unique_bill_id) { should == unique_id }
80
- end
75
+ subject(:bill) { bill2 }
76
+
77
+ it_behaves_like "unique by", {
78
+ id: 431,
79
+ group: { client_id: 7 },
80
+ tempered_group: { client_id: 7 % 10 },
81
+ group_value: group_value = 7 % 10,
82
+ unique_id: (431 * 10) + group_value,
83
+ }
81
84
  end
82
85
  end
83
86
 
@@ -95,49 +98,38 @@ describe UniqueBy::Generator do
95
98
 
96
99
  let(:medical_bill) { medical_klass.new(839) }
97
100
  let(:utility_bill) { utility_klass.new(839) }
98
- let(:id) { 839 }
99
101
 
100
102
  context "medical bill" do
101
103
  let(:klass) { medical_klass }
102
- let(:unique_id) { (839 << 1) + (10 % 2) }
103
- subject { medical_bill }
104
-
105
- describe "class methods" do
106
- specify { expect(medical_klass.bill_id_group_value_from(type: 10)).to eq(10 % 2) }
107
- specify { expect(medical_klass.unique_bill_id_from(839, type: 10)).to eq(unique_id) }
108
- specify { expect(medical_klass.bill_id_from(unique_id)).to eq(839) }
109
- specify { expect(medical_klass.bill_id_group_from(unique_id)).to eq(type: 10 % 2) }
110
- end
111
-
112
- describe "instance methods" do
113
- its(:bill_id_group) { should == { type: 10 } }
114
- its(:unique_bill_id) { should == unique_id }
115
- end
104
+ subject(:bill) { medical_bill }
105
+
106
+ it_behaves_like "unique by", {
107
+ id: 839,
108
+ group: { type: 10 },
109
+ tempered_group: { type: 10 % 2 },
110
+ group_value: group_value = (10 % 2),
111
+ unique_id: (839 * 2) + group_value,
112
+ }
116
113
  end
117
114
 
118
115
  context "utility bill" do
119
116
  let(:klass) { utility_klass }
120
- let(:unique_id) { (839 << 1) + (11 % 2) }
121
- subject { utility_bill }
122
-
123
- describe "class methods" do
124
- specify { expect(utility_klass.bill_id_group_value_from(type: 11)).to eq(11 % 2) }
125
- specify { expect(utility_klass.unique_bill_id_from(839, type: 11)).to eq(unique_id) }
126
- specify { expect(utility_klass.bill_id_from(unique_id)).to eq(839) }
127
- specify { expect(utility_klass.bill_id_group_from(unique_id)).to eq(type: 11 % 2) }
128
- end
129
-
130
- describe "instance methods" do
131
- its(:bill_id_group) { should == { type: 11 } }
132
- its(:unique_bill_id) { should == unique_id }
133
- end
117
+ subject(:bill) { utility_bill }
118
+
119
+ it_behaves_like "unique by", {
120
+ id: 839,
121
+ group: { type: 11 },
122
+ tempered_group: { type: 11 % 2 },
123
+ group_value: group_value = (11 % 2),
124
+ unique_id: (839 * 2) + group_value,
125
+ }
134
126
  end
135
127
  end
136
128
 
137
129
  context "sharded tables" do
138
130
  let(:medical_klass) do
139
131
  Class.new(ShardedTablesBase) do
140
- unique_by(client_id: 10, x: 200, type: 2, y: 20) { { type: 10, y: y } }
132
+ unique_by(client_id: 10, x: 200, type: 2, y: 30) { { type: 10, y: y } }
141
133
  def x
142
134
  53
143
135
  end
@@ -149,7 +141,7 @@ describe UniqueBy::Generator do
149
141
 
150
142
  let(:utility_klass) do
151
143
  Class.new(ShardedTablesBase) do
152
- unique_by(client_id: 2**4, x: 2**8, type: 2**1, y: 2**5) { { type: 11, y: y } }
144
+ unique_by(client_id: 10, x: 200, type: 2, y: 30) { { type: 11, y: y } }
153
145
  def x
154
146
  853
155
147
  end
@@ -161,48 +153,92 @@ describe UniqueBy::Generator do
161
153
 
162
154
  let(:medical_bill) { medical_klass.new(9428, 5, 128) }
163
155
  let(:utility_bill) { utility_klass.new(9428, 8, 255) }
164
- let(:id) { 9428 }
165
156
 
166
157
  context "medical bill" do
167
158
  let(:klass) { medical_klass }
168
- subject { medical_bill }
159
+ subject(:bill) { medical_bill }
160
+
161
+ it_behaves_like "unique by", {
162
+ id: 9428,
163
+ group: { client_id: 5, x: 53, type: 10, y: 20 },
164
+ tempered_group: { client_id: 5 % 10, x: 53 % 200, type: 10 % 2, y: 20 % 30 },
165
+ group_value: group_value = ((5 % 10) * 200*2*30) + ((53 % 200) * 2*30) + ((10 % 2) * 30) + (20 % 30),
166
+ unique_id: (9428 * 10*200*2*30) + group_value,
167
+ }
168
+ end
169
+
170
+ context "utility bill" do
171
+ let(:klass) { utility_klass }
172
+ subject(:bill) { utility_bill }
173
+
174
+ it_behaves_like "unique by", {
175
+ id: 9428,
176
+ group: { client_id: 8, x: 853, type: 11, y: 40 },
177
+ tempered_group: { client_id: 8 % 10, x: 853 % 200, type: 11 % 2, y: 40 % 30 },
178
+ group_value: group_value = ((8 % 10) * 200*2*30) + ((853 % 200) * 2*30) + ((11 % 2) * 30) + (40 % 30),
179
+ unique_id: (9428 * 10*200*2*30) + group_value,
180
+ }
181
+ end
182
+ end
183
+
184
+ context "override primary key" do
185
+ let(:klass) do
186
+ Class.new(Struct.new(:bill_id, :client_id)) do
187
+ extend UniqueBy::Generator
188
+ unique_by(client_id: 10, primary_key: :bill_id)
189
+ end
190
+ end
191
+ subject(:bill) { klass.new(431, 2) }
192
+
193
+ it_behaves_like "unique by", {
194
+ id: 431,
195
+ group: { client_id: 2 },
196
+ tempered_group: { client_id: 2 % 10 },
197
+ group_value: group_value = 2 % 10,
198
+ unique_id: (431 * 10) + group_value,
199
+ }
200
+ end
169
201
 
170
- let(:tempered_group) { { client_id: 5 % 16, x: 53 % 256, type: 10 % 2, y: 20 % 32 } }
171
- let(:group_value) { ((5 % 16) << 14) + ((53 % 256) << 6) + ((10 % 2) << 5) + (20 % 32) }
172
- let(:unique_id) { (9428 << 18) + group_value }
202
+ context "constant groups" do
203
+ let(:instance) { klass.new(49, 5) }
173
204
 
205
+ shared_examples_for "unique by" do
174
206
  describe "class methods" do
175
- specify { expect(medical_klass.bill_id_group_value_from(client_id: 5, x: 53, type: 10, y: 20)).to eq(group_value) }
176
- specify { expect(medical_klass.unique_bill_id_from(9428, client_id: 5, x: 53, type: 10, y: 20)).to eq(unique_id) }
177
- specify { expect(medical_klass.bill_id_from(unique_id)).to eq(9428) }
178
- specify { expect(medical_klass.bill_id_group_from(unique_id)).to eq(tempered_group) }
207
+ subject { klass }
208
+ its(:id_group_value_from) { should == 5 }
209
+ specify { expect(klass.unique_id_from(49)).to eq(495) }
179
210
  end
180
211
 
181
212
  describe "instance methods" do
182
- its(:bill_id_group) { should == { client_id: 5, x: 53, type: 10, y: 20 } }
183
- its(:unique_bill_id) { should == unique_id }
213
+ subject { instance }
214
+ its(:id_group) { should == { type: 5 } }
215
+ its(:unique_id) { should == 495 }
184
216
  end
185
217
  end
186
218
 
187
- context "utility bill" do
188
- let(:klass) { utility_klass }
189
- subject { utility_bill }
219
+ context "in static method" do
220
+ let(:klass) do
221
+ Class.new(Struct.new(:id, :type)) do
222
+ extend UniqueBy::Generator
223
+ unique_by(type: 10, primary_key: :id)
224
+ def self.type
225
+ 5
226
+ end
227
+ end
228
+ end
190
229
 
191
- let(:tempered_group) { { client_id: 8 % 16, x: 853 % 256, type: 11 % 2, y: 40 % 32 } }
192
- let(:group_value) { ((8 % 16) << 14) + ((853 % 256) << 6) + ((11 % 2) << 5) + (40 % 32) }
193
- let(:unique_id) { (9428 << 18) + group_value }
230
+ it_behaves_like "unique by"
231
+ end
194
232
 
195
- describe "class methods" do
196
- specify { expect(utility_klass.bill_id_group_value_from(client_id: 8, x: 853, type: 11, y: 40)).to eq(group_value) }
197
- specify { expect(utility_klass.unique_bill_id_from(9428, client_id: 8, x: 853, type: 11, y: 40)).to eq(unique_id) }
198
- specify { expect(utility_klass.bill_id_from(unique_id)).to eq(9428) }
199
- specify { expect(utility_klass.bill_id_group_from(unique_id)).to eq(tempered_group) }
233
+ context "in block" do
234
+ let(:klass) do
235
+ Class.new(Struct.new(:id, :type)) do
236
+ extend UniqueBy::Generator
237
+ unique_by(type: 10, primary_key: :id) { { type: 5 } }
238
+ end
200
239
  end
201
240
 
202
- describe "instance methods" do
203
- its(:bill_id_group) { should == { client_id: 8, x: 853, type: 11, y: 40 } }
204
- its(:unique_bill_id) { should == unique_id }
205
- end
241
+ it_behaves_like "unique by"
206
242
  end
207
243
  end
208
244
 
@@ -228,7 +264,7 @@ describe UniqueBy::Generator do
228
264
 
229
265
  specify { expect { klass.bill_id_group_value_from(x: 2) }.to raise_error(ArgumentError, "unknown bill_id group keys: [:x]") }
230
266
  specify { expect { klass.bill_id_group_value_from(client_id: 5, x: 2) }.to raise_error(ArgumentError, "unknown bill_id group keys: [:x]") }
231
- specify { expect { klass.bill_id_group_value_from() }.to raise_error(ArgumentError, "missing bill_id group keys: [:client_id]") }
267
+ specify { expect { klass.bill_id_group_value_from() }.to raise_error(NoMethodError, /^undefined method `client_id' for/) }
232
268
  specify { expect { klass.bill_id_group_value_from(client_id: nil) }.to raise_error(TypeError, "bill_id group client_id must not be nil") }
233
269
  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") }
234
270
  specify { expect { klass.unique_bill_id_from(431, client_id: nil) }.to raise_error(TypeError, "bill_id group client_id must not be nil") }
@@ -12,6 +12,10 @@ Gem::Specification.new do |spec|
12
12
  spec.description = %q{Allows uniqueness of a record when sharding (specifying the shard ID as the group) or span accross tables (receipts).}
13
13
  spec.homepage = "https://github.com/odedniv/unique_by"
14
14
  spec.license = "UNLICENSE"
15
+ spec.post_install_message = <<MSG
16
+ Upgrading from a previous major version could have destructive results.
17
+ Make sure you go through all INCOMPATIBLEs mentioned in the changelog!
18
+ MSG
15
19
 
16
20
  spec.files = `git ls-files -z`.split("\x0")
17
21
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unique_by
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.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-11-17 00:00:00.000000000 Z
11
+ date: 2014-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: generate_method
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.6'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '3.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec-its
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.0'
69
69
  description: Allows uniqueness of a record when sharding (specifying the shard ID
@@ -74,8 +74,9 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
- - .gitignore
78
- - .rspec
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - CHANGELOG.md
79
80
  - Gemfile
80
81
  - README.md
81
82
  - Rakefile
@@ -89,18 +90,20 @@ homepage: https://github.com/odedniv/unique_by
89
90
  licenses:
90
91
  - UNLICENSE
91
92
  metadata: {}
92
- post_install_message:
93
+ post_install_message: |
94
+ Upgrading from a previous major version could have destructive results.
95
+ Make sure you go through all INCOMPATIBLEs mentioned in the changelog!
93
96
  rdoc_options: []
94
97
  require_paths:
95
98
  - lib
96
99
  required_ruby_version: !ruby/object:Gem::Requirement
97
100
  requirements:
98
- - - '>='
101
+ - - ">="
99
102
  - !ruby/object:Gem::Version
100
103
  version: '0'
101
104
  required_rubygems_version: !ruby/object:Gem::Requirement
102
105
  requirements:
103
- - - '>='
106
+ - - ">="
104
107
  - !ruby/object:Gem::Version
105
108
  version: '0'
106
109
  requirements: []