mm_uses_uuid 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -31,7 +31,12 @@ class Group
31
31
  end
32
32
  ```
33
33
 
34
- The newly instantiated model will have a randomly generated UUID. If you want to make sure that the UUID hasn't already been used
34
+ The newly instantiated model will have a randomly generated UUID.
35
+
36
+ Ensuring truly unique UUIDs
37
+ ---------------------------
38
+
39
+ If you want to make sure that the UUID hasn't already been used
35
40
  you can generate a new one like this:
36
41
 
37
42
  ```
@@ -43,4 +48,28 @@ This will generate random UUIDs until it finds one that isn't in the passed coll
43
48
  Obviously, the whole idea of random (type 4) UUIDs is that there is a tiny probability of generating duplicates.
44
49
  For this reason, you should only consider using `:ensure_unique_in` if a duplicate UUID would be a disaster for you.
45
50
 
51
+ Encoding class in the LSN
52
+ -------------------------
53
+
54
+ It is possible to encode the class of an object in its UUID by forcing the least significant nibble (the rightmost hex character) of its UUIDs to be a particular value.
55
+ To do this, add the `uuid_lsn` method to you model and pass it a single hex character like this:
56
+
57
+ ```
58
+ class Group
59
+ include MongoMapper::Document
60
+ plugin MmUsesUuid
61
+
62
+ many :people, :class_name => 'Person'
63
+
64
+ uuid_lsn 0xf
65
+ end
66
+ ```
67
+
68
+ Once this value is set you can use `MongoMapper.find_by_uuid(...)` to find by id (or a list of ids) and it will automatically detect the class by inspecting
69
+ the last character of the UUIDs you pass. So for the example above, all UUIDs generated for new Group objects will end in 'f'
70
+ and, if you pass a UUID ending in 'f' to `MongoMapper.find`, it will pass that request on to `Group.find()`.
71
+
72
+ This method can be useful if you need to store long lists of ids, but don't want to incur any additional complexity and storage by storing
73
+ the collection name as well. However, because we only use one nibble, this method can only be used for 16 distinct collections.
74
+
46
75
  Copyright (c) 2011 PeepAll Ltd, released under the MIT license
@@ -1,3 +1,3 @@
1
1
  module MmUsesUuid
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
data/lib/mm_uses_uuid.rb CHANGED
@@ -2,6 +2,42 @@ require 'mongo_mapper'
2
2
  require_relative "mm_uses_uuid/version"
3
3
  require_relative "mm_uses_uuid/bson_binary_mixin"
4
4
 
5
+ class BsonUuid
6
+ def self.to_mongo(value)
7
+ case value
8
+ when String
9
+ BSON::Binary.new(value, BSON::Binary::SUBTYPE_UUID)
10
+ when BSON::Binary, NilClass
11
+ value
12
+ else
13
+ raise "BsonUuid cannot be of type #{value.class}. String, BSON::Binary and NilClass are the only permitted types"
14
+ end
15
+ end
16
+
17
+ def self.from_mongo(value)
18
+ value
19
+ end
20
+ end
21
+
22
+ module MongoMapper
23
+
24
+ @@lsn_class ||= []
25
+
26
+ def self.find_by_uuid(*args)
27
+ args.flatten!
28
+ ids_by_class = {}
29
+ args.each do |id|
30
+ lsn = id.to_s[-1].hex
31
+ klass = @@lsn_class[lsn]
32
+ raise "expected to find a class in @@lsn_class[#{lsn}] of the MongoMapper module but there was no entry. You need to set uuid_lsn in you class." if klass.nil?
33
+ ids_by_class[klass] ||= []
34
+ ids_by_class[klass] << id
35
+ end
36
+ ids_by_class.map {|klass, ids| klass.find(ids)} .flatten
37
+ end
38
+
39
+ end
40
+
5
41
  module MmUsesUuid
6
42
  extend ActiveSupport::Concern
7
43
 
@@ -9,23 +45,6 @@ module MmUsesUuid
9
45
  key :_id, BsonUuid
10
46
  end
11
47
 
12
- class BsonUuid
13
- def self.to_mongo(value)
14
- case value
15
- when String
16
- BSON::Binary.new(value, BSON::Binary::SUBTYPE_UUID)
17
- when BSON::Binary, NilClass
18
- value
19
- else
20
- raise "BsonUuid cannot be of type #{value.class}. String, BSON::Binary and NilClass are the only permitted types"
21
- end
22
- end
23
-
24
- def self.from_mongo(value)
25
- value
26
- end
27
- end
28
-
29
48
  module ClassMethods
30
49
 
31
50
  def find(*args)
@@ -36,8 +55,8 @@ module MmUsesUuid
36
55
  else
37
56
  args = BsonUuid.to_mongo(args.first)
38
57
  end
39
-
40
58
  super(args)
59
+
41
60
  end
42
61
 
43
62
  def new(params = {})
@@ -51,6 +70,14 @@ module MmUsesUuid
51
70
  new_object
52
71
  end
53
72
 
73
+ def uuid_lsn(lsn_integer)
74
+ add_class_lsn(self, lsn_integer)
75
+ end
76
+
77
+ def add_class_lsn(klass, lsn_integer)
78
+ MongoMapper.class_eval "@@lsn_class[#{lsn_integer}] = #{klass}"
79
+ end
80
+
54
81
  end
55
82
 
56
83
  module InstanceMethods
@@ -83,9 +110,13 @@ module MmUsesUuid
83
110
  end
84
111
 
85
112
  def make_uuid
86
- uuid = SecureRandom.uuid.gsub!('-', '')
87
- bson_encoded_uuid = BSON::Binary.new(uuid, BSON::Binary::SUBTYPE_UUID)
88
- return bson_encoded_uuid, 'random'
113
+ uuid = SecureRandom.uuid.gsub!('-', '')
114
+ lsn_class = MongoMapper.class_variable_get('@@lsn_class')
115
+ if replacement_lsn = lsn_class.index(self.class)
116
+ uuid[-1] = replacement_lsn.to_s(16)
117
+ end
118
+ bson_encoded_uuid = BSON::Binary.new(uuid, BSON::Binary::SUBTYPE_UUID)
119
+ return bson_encoded_uuid, 'random'
89
120
  end
90
121
 
91
122
  def id_to_s!
@@ -7,8 +7,11 @@ describe MmUsesUuid do
7
7
  include MongoMapper::Document
8
8
  plugin MmUsesUuid
9
9
 
10
+ key :name, String
10
11
  many :people, :class_name => 'Person'
11
12
 
13
+ uuid_lsn 0
14
+
12
15
  end
13
16
 
14
17
  class Person
@@ -19,6 +22,9 @@ describe MmUsesUuid do
19
22
  key :age
20
23
 
21
24
  belongs_to :group
25
+
26
+ uuid_lsn 0xf
27
+
22
28
  end
23
29
 
24
30
  MongoMapper.connection = Mongo::Connection.new('localhost', 27017)
@@ -36,7 +42,7 @@ describe MmUsesUuid do
36
42
  "11111111-1111-4111-y111-111111111111",
37
43
  "33333333-3333-4333-y333-333333333333"
38
44
  )
39
- @group = Group.create
45
+ @group = Group.create(name: 'mongo_mapper fanclub')
40
46
  @person = Person.create(name: 'Jon', age: 33)
41
47
  end
42
48
 
@@ -45,12 +51,21 @@ describe MmUsesUuid do
45
51
  @group._id.subtype.should == BSON::Binary::SUBTYPE_UUID
46
52
  end
47
53
 
54
+ it "should replace the least significant bits with the integer specified using uuid_lsn" do
55
+ @group._id.to_s.slice(-1).should == '0'
56
+ @person._id.to_s.slice(-1).should == 'f'
57
+ end
58
+
59
+ it "should perform a find on the right collection if MongoMapper.find is used" do
60
+ MongoMapper.find_by_uuid(@person.id, @group.id).map(&:name).should include(@person.name, @group.name)
61
+ end
62
+
48
63
  it "should not set a new uuid if one as passed as a param" do
49
- group_with_passed_id = Group.new(:id => BSON::Binary.new("3333333333334333y333333333333333", BSON::Binary::SUBTYPE_UUID))
50
- group_with_passed_id.id.to_s.should == "3333333333334333y333333333333333"
64
+ group_with_passed_id = Group.new(:id => BSON::Binary.new("3333333333334333y333333333333330", BSON::Binary::SUBTYPE_UUID))
65
+ group_with_passed_id.id.to_s.should == "3333333333334333y333333333333330"
51
66
 
52
- group_with_passed_id = Group.new('_id' => BSON::Binary.new("3333333333334333y333333333333333", BSON::Binary::SUBTYPE_UUID))
53
- group_with_passed_id.id.to_s.should == "3333333333334333y333333333333333"
67
+ group_with_passed_id = Group.new('_id' => BSON::Binary.new("3333333333334333y333333333333330", BSON::Binary::SUBTYPE_UUID))
68
+ group_with_passed_id.id.to_s.should == "3333333333334333y333333333333330"
54
69
  end
55
70
 
56
71
  it "should have a useful inspect method that shows the uuid string" do
@@ -58,8 +73,8 @@ describe MmUsesUuid do
58
73
  end
59
74
 
60
75
  it "should report that uuid BSON::Binary objects are eql? if they encode the same string" do
61
- a = BSON::Binary.new("3333333333334333y333333333333333", BSON::Binary::SUBTYPE_UUID)
62
- b = BSON::Binary.new("3333333333334333y333333333333333", BSON::Binary::SUBTYPE_UUID)
76
+ a = BSON::Binary.new("3333333333334333y333333333333330", BSON::Binary::SUBTYPE_UUID)
77
+ b = BSON::Binary.new("3333333333334333y333333333333330", BSON::Binary::SUBTYPE_UUID)
63
78
  a.should eql b
64
79
  end
65
80
 
@@ -71,7 +86,7 @@ describe MmUsesUuid do
71
86
  it "should ensure that the uuid is unique if :ensure_unique_in is set" do
72
87
  safe_new_group = Group.new
73
88
  safe_new_group.find_new_uuid(:ensure_unique_in => Group)
74
- safe_new_group._id.to_s.should == "3333333333334333y333333333333333"
89
+ safe_new_group._id.to_s.should == "3333333333334333y333333333333330"
75
90
  end
76
91
 
77
92
  context 'finding by uuid' do
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 5
9
- version: 0.0.5
8
+ - 6
9
+ version: 0.0.6
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jonathan Chambers
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-12-23 00:00:00 +00:00
17
+ date: 2012-01-01 00:00:00 +00:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency