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 +4 -4
- data/README.md +85 -55
- data/lib/unique_by/version.rb +1 -1
- data/lib/unique_by.rb +38 -42
- data/spec/unique_by_spec.rb +114 -46
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4839ad7e8c93eabad52a34ae43774c3a4fa31ead
|
4
|
+
data.tar.gz: d205bd87ab905c57bbc620df6400a36fe9c95fba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
46
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
81
|
-
group:
|
84
|
+
You can supply a block to give a custom mechanism for determining the group:
|
82
85
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
96
|
+
return more than one field:
|
92
97
|
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
|
data/lib/unique_by/version.rb
CHANGED
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(
|
15
|
-
|
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
|
-
|
18
|
-
|
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
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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 |
|
45
|
-
|
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 |
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
g
|
53
|
-
|
54
|
-
|
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
|
-
|
59
|
-
group.
|
60
|
-
raise ArgumentError, "
|
61
|
-
|
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
|
-
|
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 |
|
72
|
-
send(:"find_by_#{pk}", send(:"#{pk}_from",
|
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 |
|
76
|
-
send(:"find_by_#{pk}!", send(:"#{pk}_from",
|
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
|
data/spec/unique_by_spec.rb
CHANGED
@@ -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(
|
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(
|
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(
|
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(:
|
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(:
|
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 "
|
191
|
+
context "medical bill" do
|
182
192
|
let(:klass) { medical_klass }
|
183
193
|
subject { medical_bill }
|
184
194
|
|
185
|
-
let(:tempered_group) {
|
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(
|
191
|
-
specify { expect(medical_klass.unique_bill_id_from(9428,
|
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 ==
|
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 "
|
214
|
+
context "utility bill" do
|
205
215
|
let(:klass) { utility_klass }
|
206
216
|
subject { utility_bill }
|
207
217
|
|
208
|
-
let(:tempered_group) {
|
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(
|
214
|
-
specify { expect(utility_klass.unique_bill_id_from(9428,
|
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 ==
|
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
|
-
|
230
|
-
|
231
|
-
|
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
|
-
|
236
|
-
specify { expect { klass.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
|
-
|
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
|
-
|
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:
|
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-
|
11
|
+
date: 2014-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|