mm_uses_uuid 0.0.16 → 0.0.18
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.
- data/lib/mm_uses_uuid.rb +192 -84
- data/lib/mm_uses_uuid/bson_binary_mixin.rb +5 -1
- data/lib/mm_uses_uuid/version.rb +1 -1
- data/spec/mm_uses_uuid_spec.rb +10 -3
- metadata +25 -5
data/lib/mm_uses_uuid.rb
CHANGED
@@ -24,7 +24,7 @@ class MongoMapper::Plugins::Associations::InArrayProxy
|
|
24
24
|
def find_target
|
25
25
|
return [] if ids.blank?
|
26
26
|
if klass == UuidModel
|
27
|
-
UuidModel.find(*criteria[:_id])
|
27
|
+
out = *UuidModel.find(*criteria[:_id])
|
28
28
|
else
|
29
29
|
all
|
30
30
|
end
|
@@ -32,93 +32,82 @@ class MongoMapper::Plugins::Associations::InArrayProxy
|
|
32
32
|
|
33
33
|
end
|
34
34
|
|
35
|
-
class UuidModel
|
36
|
-
|
37
|
-
include MongoMapper::Document
|
38
|
-
|
39
|
-
@@lsn_class_lookup ||= {}
|
40
|
-
|
41
|
-
def self.add_lsn_mapping(ind, klass)
|
42
|
-
@@lsn_class_lookup[ind] = klass
|
43
|
-
@@class_lsn_lookup = @@lsn_class_lookup.invert
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.lsn_class_lookup
|
47
|
-
@@lsn_class_lookup
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.class_lsn_lookup
|
51
|
-
@@class_lsn_lookup
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.find(*ids)
|
55
|
-
ids = ids.flatten.uniq
|
56
|
-
ids_by_class = ids.each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |id, hsh|
|
57
|
-
lsn = id.to_s[-1].hex
|
58
|
-
klass = @@lsn_class_lookup[lsn]
|
59
|
-
if klass.nil?
|
60
|
-
raise "expected to find a class in @@lsn_class_lookup[#{lsn}] of the MongoMapper module but there was no entry. You need to set uuid_lsn in your class."
|
61
|
-
end
|
62
|
-
hsh[klass] << id
|
63
|
-
end
|
64
|
-
result = ids_by_class.map {|klass, ids| klass.find(ids)} .flatten
|
65
|
-
ids.length == 1 ? result.first : result
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.find!(*ids)
|
69
|
-
ids = ids.flatten.uniq
|
70
|
-
raise MongoMapper::DocumentNotFound, "Couldn't find without an ID" if ids.size == 0
|
71
|
-
find(*ids).tap do |result|
|
72
|
-
if result.nil? || ids.size != Array(result).size
|
73
|
-
raise MongoMapper::DocumentNotFound, "Couldn't find all of the ids (#{ids.join(',')}). Found #{Array(result).size}, but was expecting #{ids.size}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
35
|
module MmUsesUuid
|
81
36
|
extend ActiveSupport::Concern
|
82
37
|
|
83
38
|
included do
|
84
|
-
key :_id, BsonUuid
|
39
|
+
key :_id, BsonUuid, :default => lambda { make_uuid }
|
85
40
|
end
|
86
41
|
|
87
42
|
module ClassMethods
|
88
43
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
44
|
+
def serialize_id(object)
|
45
|
+
case object
|
46
|
+
when MongoMapper::Document, MongoMapper::EmbeddedDocument
|
47
|
+
object.id.to_s
|
48
|
+
else
|
49
|
+
object.to_s
|
50
|
+
end
|
92
51
|
end
|
93
52
|
|
94
|
-
def
|
95
|
-
|
96
|
-
super(args)
|
53
|
+
def deserialize_id(id)
|
54
|
+
BSON::Binary.new(id, BSON::Binary::SUBTYPE_UUID)
|
97
55
|
end
|
98
56
|
|
99
|
-
def
|
100
|
-
|
101
|
-
if
|
102
|
-
|
57
|
+
def make_uuid
|
58
|
+
uuid = SecureRandom.uuid.gsub!('-', '')
|
59
|
+
if single_collection_inherited? and not embeddable?
|
60
|
+
lookup_class_name = collection_name.singularize.camelize
|
103
61
|
else
|
104
|
-
|
62
|
+
lookup_class_name = name
|
105
63
|
end
|
106
|
-
|
64
|
+
replacement_lsn = UuidModel.class_lsn_lookup[lookup_class_name] || 0x00
|
65
|
+
uuid[-2..-1] = replacement_lsn.to_s(16).rjust(2,'0')
|
66
|
+
BSON::Binary.new(uuid, BSON::Binary::SUBTYPE_UUID)
|
67
|
+
|
68
|
+
rescue => e
|
69
|
+
binding.pry
|
70
|
+
raise e
|
107
71
|
end
|
108
72
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
73
|
+
def find(*ids)
|
74
|
+
batch_mode = ids.first.is_a?(Array) || ids.length > 1
|
75
|
+
ids.flatten!
|
76
|
+
ids = convert_ids_to_BSON(ids)
|
77
|
+
results = super(ids)
|
78
|
+
batch_mode ? results : results.first
|
79
|
+
end
|
80
|
+
|
81
|
+
def find!(*ids)
|
82
|
+
batch_mode = ids.first.is_a?(Array) || ids.length > 1
|
83
|
+
ids.flatten!
|
84
|
+
ids = convert_ids_to_BSON(ids)
|
85
|
+
results = super(ids)
|
86
|
+
batch_mode ? results : results.first
|
118
87
|
end
|
88
|
+
|
89
|
+
def convert_ids_to_BSON(ids)
|
90
|
+
ids.map {|id| BsonUuid.to_mongo(id)}
|
91
|
+
end
|
92
|
+
|
93
|
+
# def new(params = {})
|
94
|
+
# passed_id = params.delete(:id) || params.delete(:_id) || params.delete('id') || params.delete('_id')
|
95
|
+
# new_object = super(params)
|
96
|
+
# if passed_id
|
97
|
+
# if passed_id.is_a?(BSON::Binary) and passed_id.subtype == BSON::Binary::SUBTYPE_UUID
|
98
|
+
# new_object.id = passed_id
|
99
|
+
# else
|
100
|
+
# raise ArgumentError, "if you pass an explicit :id parameter it must be a valid BSON::Binary::SUBTYPE_UUID"
|
101
|
+
# end
|
102
|
+
# else
|
103
|
+
# new_object.find_new_uuid
|
104
|
+
# end
|
105
|
+
# new_object
|
106
|
+
# end
|
119
107
|
|
120
108
|
def uuid_lsn(lsn_integer)
|
121
|
-
|
109
|
+
raise "lsn_integer must be from 0-255" unless (0..255).cover?(lsn_integer)
|
110
|
+
UuidModel.add_lsn_mapping(lsn_integer, self.name)
|
122
111
|
end
|
123
112
|
|
124
113
|
end
|
@@ -129,8 +118,8 @@ module MmUsesUuid
|
|
129
118
|
options = {force_safe: false}.merge(options)
|
130
119
|
|
131
120
|
if not options[:ensure_unique_in]
|
132
|
-
@_id
|
133
|
-
#puts "assuming
|
121
|
+
@_id = make_uuid
|
122
|
+
#puts "assuming UUID #{@_id} is available"
|
134
123
|
return
|
135
124
|
else
|
136
125
|
find_new_uuid_safely(options[:ensure_unique_in])
|
@@ -142,8 +131,8 @@ module MmUsesUuid
|
|
142
131
|
|
143
132
|
@_id = nil
|
144
133
|
begin
|
145
|
-
trial_id
|
146
|
-
#puts "CHECKING #{coll} collection for availability of
|
134
|
+
trial_id = make_uuid
|
135
|
+
#puts "CHECKING #{coll} collection for availability of UUID: #{trial_id}"
|
147
136
|
if coll.where(:_id => trial_id).fields(:_id).first.nil?
|
148
137
|
@_id = trial_id
|
149
138
|
end
|
@@ -152,17 +141,7 @@ module MmUsesUuid
|
|
152
141
|
end
|
153
142
|
|
154
143
|
def make_uuid
|
155
|
-
|
156
|
-
if self.class.single_collection_inherited?
|
157
|
-
lookup_class = self.class.collection_name.singularize.camelize.constantize
|
158
|
-
else
|
159
|
-
lookup_class = self.class
|
160
|
-
end
|
161
|
-
if replacement_lsn = UuidModel.class_lsn_lookup[lookup_class]
|
162
|
-
uuid[-1] = replacement_lsn.to_s(16)
|
163
|
-
end
|
164
|
-
bson_encoded_uuid = BSON::Binary.new(uuid, BSON::Binary::SUBTYPE_UUID)
|
165
|
-
return bson_encoded_uuid, 'random'
|
144
|
+
self.class.make_uuid
|
166
145
|
end
|
167
146
|
|
168
147
|
def id_to_s!
|
@@ -177,3 +156,132 @@ module MmUsesUuid
|
|
177
156
|
end
|
178
157
|
|
179
158
|
end
|
159
|
+
|
160
|
+
class UuidModel
|
161
|
+
|
162
|
+
include MongoMapper::Document
|
163
|
+
plugin MmUsesUuid
|
164
|
+
|
165
|
+
@@lsn_class_lookup ||= {}
|
166
|
+
|
167
|
+
class << self
|
168
|
+
|
169
|
+
def add_lsn_mapping(ind, class_name)
|
170
|
+
class_name = class_name.to_s
|
171
|
+
if current_class_name = @@lsn_class_lookup[ind]
|
172
|
+
raise "cannont assign #{class_name} to #{ind} as #{current_class_name} is already assigned to that LSN"
|
173
|
+
end
|
174
|
+
@@lsn_class_lookup[ind] = class_name
|
175
|
+
@@class_lsn_lookup = @@lsn_class_lookup.invert
|
176
|
+
end
|
177
|
+
|
178
|
+
def lsn_class_lookup
|
179
|
+
@@lsn_class_lookup
|
180
|
+
end
|
181
|
+
|
182
|
+
def class_lsn_lookup
|
183
|
+
@@class_lsn_lookup
|
184
|
+
end
|
185
|
+
|
186
|
+
def class_name_from_id(id, options = {})
|
187
|
+
lsn = id.to_s[-2..-1].hex
|
188
|
+
class_name = @@lsn_class_lookup[lsn]
|
189
|
+
if class_name.nil? and options[:error_if_no_lsn_match]
|
190
|
+
raise "expected to find a class name in @@lsn_class_lookup[#{lsn}] of the MongoMapper module but there was no entry. You need to set uuid_lsn in your class."
|
191
|
+
end
|
192
|
+
class_name
|
193
|
+
end
|
194
|
+
|
195
|
+
def find_by_id(id)
|
196
|
+
find id
|
197
|
+
end
|
198
|
+
|
199
|
+
def find(*args)
|
200
|
+
|
201
|
+
# raise "foo"
|
202
|
+
|
203
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
204
|
+
fields = *options[:fields]
|
205
|
+
fields = nil if fields.empty?
|
206
|
+
batch_mode = args.first.is_a?(Array) || args.length > 1
|
207
|
+
ids = args.flatten.uniq
|
208
|
+
ids.map! {|id| BsonUuid.to_mongo(id)}
|
209
|
+
|
210
|
+
ids_by_model = ids.each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |id, hsh|
|
211
|
+
model_name = class_name_from_id(id, options)
|
212
|
+
hsh[model_name.constantize] << id if model_name
|
213
|
+
end
|
214
|
+
|
215
|
+
if defined? Celluloid
|
216
|
+
|
217
|
+
#NOTE: because IdentityMap is in the current thread only...
|
218
|
+
#we have to manage it ourselves if using Celluloid
|
219
|
+
|
220
|
+
im_results = []
|
221
|
+
|
222
|
+
unless fields
|
223
|
+
ids_by_model.clone.each do |model, model_ids|
|
224
|
+
model_ids.each do |model_id|
|
225
|
+
doc = model.get_from_identity_map(model_id)
|
226
|
+
if doc
|
227
|
+
im_results << doc
|
228
|
+
ids_by_model[model].delete model_id
|
229
|
+
end
|
230
|
+
end
|
231
|
+
ids_by_model.delete(model) if ids_by_model[model].empty?
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
future_db_results = ids_by_model.map do |model, model_ids|
|
236
|
+
query = model.where(:id => model_ids)
|
237
|
+
query = query.fields(fields) if fields
|
238
|
+
Celluloid::Future.new { query.all }
|
239
|
+
end
|
240
|
+
|
241
|
+
db_results = future_db_results.map(&:value).flatten
|
242
|
+
|
243
|
+
if fields
|
244
|
+
db_results.each(&:remove_from_identity_map)
|
245
|
+
else
|
246
|
+
db_results.each(&:add_to_identity_map)
|
247
|
+
end
|
248
|
+
|
249
|
+
results = im_results + db_results
|
250
|
+
|
251
|
+
else
|
252
|
+
|
253
|
+
#NOTE: as this is in the current thread, IdentityMap management is normal
|
254
|
+
|
255
|
+
results = ids_by_model.map do |model, model_ids|
|
256
|
+
if fields
|
257
|
+
model.where(:id => model_ids).fields(fields).all #models will be removed from the map
|
258
|
+
else
|
259
|
+
model.find model_ids #we use this so that we read and write to the identity map
|
260
|
+
end
|
261
|
+
end.flatten
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
batch_mode ? results : results.first
|
266
|
+
|
267
|
+
# rescue => e
|
268
|
+
# binding.pry
|
269
|
+
end
|
270
|
+
alias_method :find_with_fields, :find
|
271
|
+
|
272
|
+
def find!(*args)
|
273
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
274
|
+
options.merge(:error_if_no_lsn_match => true)
|
275
|
+
ids = args.flatten.uniq
|
276
|
+
raise MongoMapper::DocumentNotFound, "Couldn't find without an ID" if ids.size == 0
|
277
|
+
find(*ids, options).tap do |result|
|
278
|
+
if result.nil? || ids.size != Array(result).size
|
279
|
+
raise MongoMapper::DocumentNotFound, "Couldn't find all of the ids (#{ids.join(',')}). Found #{Array(result).size}, but was expecting #{ids.size}"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
alias_method :find_with_fields!, :find!
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
data/lib/mm_uses_uuid/version.rb
CHANGED
data/spec/mm_uses_uuid_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.expand_path('../../lib/mm_uses_uuid', __FILE__)
|
2
2
|
require 'ruby-debug'
|
3
|
+
require 'pry'
|
3
4
|
|
4
5
|
describe MmUsesUuid do
|
5
6
|
|
@@ -10,7 +11,7 @@ describe MmUsesUuid do
|
|
10
11
|
|
11
12
|
key :name, String
|
12
13
|
belongs_to :owner, :class_name => 'UuidModel', :required => true
|
13
|
-
|
14
|
+
has_many :people, :class_name => 'Person'
|
14
15
|
|
15
16
|
uuid_lsn 0
|
16
17
|
|
@@ -23,8 +24,8 @@ describe MmUsesUuid do
|
|
23
24
|
key :name
|
24
25
|
key :age
|
25
26
|
|
26
|
-
key :interest_ids,
|
27
|
-
|
27
|
+
key :interest_ids, Set
|
28
|
+
has_many :interests, :in => :interest_ids, :class_name => 'UuidModel' # this allows many-to-many polymorphic interests without the need for groups and people to be stored in a single collection
|
28
29
|
|
29
30
|
belongs_to :group
|
30
31
|
|
@@ -79,6 +80,12 @@ describe MmUsesUuid do
|
|
79
80
|
person = Person.find_by_name 'Jon'
|
80
81
|
person.interests.map(&:name).should include(@person.name, @group.name)
|
81
82
|
end
|
83
|
+
|
84
|
+
it "should return an array for many to many associations when there is only one associated item" do
|
85
|
+
person = Person.new
|
86
|
+
person.interests << @group
|
87
|
+
person.interests.should be_an_instance_of(Array)
|
88
|
+
end
|
82
89
|
|
83
90
|
it "should not set a new uuid if one as passed as a param" do
|
84
91
|
group_with_passed_id = Group.new(:id => BSON::Binary.new("3333333333334333y333333333333330", BSON::Binary::SUBTYPE_UUID))
|
metadata
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mm_uses_uuid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 18
|
9
|
+
version: 0.0.18
|
6
10
|
platform: ruby
|
7
11
|
authors:
|
8
12
|
- Jonathan Chambers
|
@@ -10,7 +14,8 @@ autorequire:
|
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
16
|
|
13
|
-
date: 2012-
|
17
|
+
date: 2012-12-21 00:00:00 +00:00
|
18
|
+
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rspec
|
@@ -20,6 +25,9 @@ dependencies:
|
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 7
|
23
31
|
version: "2.7"
|
24
32
|
type: :development
|
25
33
|
version_requirements: *id001
|
@@ -31,6 +39,10 @@ dependencies:
|
|
31
39
|
requirements:
|
32
40
|
- - ">="
|
33
41
|
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 1
|
44
|
+
- 5
|
45
|
+
- 0
|
34
46
|
version: 1.5.0
|
35
47
|
type: :development
|
36
48
|
version_requirements: *id002
|
@@ -42,6 +54,10 @@ dependencies:
|
|
42
54
|
requirements:
|
43
55
|
- - ">="
|
44
56
|
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
- 10
|
60
|
+
- 1
|
45
61
|
version: 0.10.1
|
46
62
|
type: :runtime
|
47
63
|
version_requirements: *id003
|
@@ -65,6 +81,7 @@ files:
|
|
65
81
|
- lib/mm_uses_uuid/version.rb
|
66
82
|
- mm_uses_uuid.gemspec
|
67
83
|
- spec/mm_uses_uuid_spec.rb
|
84
|
+
has_rdoc: true
|
68
85
|
homepage: https://github.com/jmchambers/mm_uses_uuid
|
69
86
|
licenses:
|
70
87
|
- MIT
|
@@ -78,20 +95,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
95
|
requirements:
|
79
96
|
- - ">="
|
80
97
|
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
81
100
|
version: "0"
|
82
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
102
|
none: false
|
84
103
|
requirements:
|
85
104
|
- - ">="
|
86
105
|
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
87
108
|
version: "0"
|
88
109
|
requirements: []
|
89
110
|
|
90
111
|
rubyforge_project:
|
91
|
-
rubygems_version: 1.
|
112
|
+
rubygems_version: 1.3.7
|
92
113
|
signing_key:
|
93
114
|
specification_version: 3
|
94
115
|
summary: UUIDs for MM
|
95
116
|
test_files: []
|
96
117
|
|
97
|
-
has_rdoc:
|