unique_by 2.1.0 → 3.0.0

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: 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: []