mingo 0.1.3 → 0.1.5

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.
Files changed (3) hide show
  1. data/lib/mingo.rb +25 -2
  2. data/lib/mingo/many_proxy.rb +98 -40
  3. metadata +35 -81
data/lib/mingo.rb CHANGED
@@ -2,6 +2,10 @@ require 'active_support/core_ext/hash/conversions'
2
2
  require 'mongo'
3
3
  require 'active_model'
4
4
  require 'hashie/dash'
5
+ # for ObjectId:
6
+ require 'thread'
7
+ require 'socket'
8
+ require 'digest/md5'
5
9
 
6
10
  BSON::ObjectId.class_eval do
7
11
  def self.[](id)
@@ -11,6 +15,25 @@ BSON::ObjectId.class_eval do
11
15
  def id
12
16
  self
13
17
  end
18
+
19
+ def initialize(time_or_data = nil)
20
+ if Time === time_or_data or Date === time_or_data
21
+ @data = generate_from_time(time_or_data)
22
+ else
23
+ @data = time_or_data || generate
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def generate_from_time(time)
30
+ oid = ''
31
+ oid += [time.to_i].pack("N")
32
+ oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
33
+ oid += [Process.pid % 0xFFFF].pack("n")
34
+ oid += [get_inc].pack("N")[1, 3]
35
+ oid.unpack("C12")
36
+ end
14
37
  end
15
38
 
16
39
  class Mingo < Hashie::Dash
@@ -85,13 +108,13 @@ class Mingo < Hashie::Dash
85
108
  end
86
109
  end
87
110
 
88
- def many(property, model, &block)
111
+ def many(property, *args, &block)
89
112
  proxy_class = block_given?? Class.new(ManyProxy, &block) : ManyProxy
90
113
  ivar = "@#{property}"
91
114
 
92
115
  define_method(property) {
93
116
  (instance_variable_defined?(ivar) && instance_variable_get(ivar)) ||
94
- instance_variable_set(ivar, proxy_class.new(self, property, model))
117
+ instance_variable_set(ivar, proxy_class.new(self, property, *args))
95
118
  }
96
119
  end
97
120
  end
@@ -16,65 +16,63 @@ class Mingo
16
16
  end
17
17
  end
18
18
 
19
- def initialize(parent, property, model)
19
+ def initialize(parent, property, mapping)
20
20
  @parent = parent
21
21
  @property = property
22
- @model = model
23
- @collection = nil
24
- @embedded = (@parent[@property] ||= [])
25
- @parent.changes.delete(@property)
26
- end
27
-
28
- def find_options
29
- @find_options ||= begin
30
- decorator = self.class.decorate_with
31
- decorate_block = self.class.decorate_each
32
-
33
- if decorator or decorate_block
34
- {:convert => lambda { |doc|
35
- @model.new(doc).tap do |obj|
36
- obj.extend decorator if decorator
37
- decorate_block.call(obj, @embedded) if decorate_block
38
- end
39
- }}
40
- else
41
- {}
42
- end
43
- end
22
+ # TODO: ugh, improve naming
23
+ @model, @self_referencing_key, @forward_referencing_key = analyze_mapping mapping
24
+ @counter_cache_field = "#{@property}_count"
25
+ @join_loaded = nil
26
+ @loaded = nil
44
27
  end
45
28
 
46
29
  undef :inspect
47
30
  undef :to_a if instance_methods.include? 'to_a'
48
31
 
49
32
  def object_ids
50
- @embedded
33
+ join_docs = load_join
34
+ join_docs = join_docs.select(&Proc.new) if block_given?
35
+ join_docs.map { |doc| doc[@forward_referencing_key] }
36
+ end
37
+
38
+ def size
39
+ (counter_cache? && counter_cache) || (@join_loaded && @join_loaded.size) || join_cursor.count
51
40
  end
52
41
 
53
42
  def include?(doc)
54
- object_ids.include? convert(doc)
43
+ !!find_join_doc(doc.id)
55
44
  end
56
45
 
57
46
  def convert(doc)
58
- doc.id
47
+ {@self_referencing_key => @parent.id, @forward_referencing_key => doc.id}
59
48
  end
60
49
 
61
50
  def <<(doc)
62
51
  doc = convert(doc)
63
- @parent.update '$addToSet' => { @property => doc }
52
+ doc['_id'] = join_collection.save doc
53
+ change_counter_cache(1)
54
+ load_join << doc if @join_loaded
64
55
  unload_collection
65
- @embedded << doc
66
56
  self
67
57
  end
68
58
 
69
59
  def delete(doc)
70
60
  doc = convert(doc)
71
- @parent.update '$pull' => { @property => doc }
72
- unload_collection
73
- @embedded.delete doc
61
+ if join_doc = find_join_doc(doc[@forward_referencing_key])
62
+ join_collection.remove :_id => join_doc['_id']
63
+ change_counter_cache(-1)
64
+ unload_collection
65
+ @join_loaded.delete join_doc
66
+ end
67
+ doc
74
68
  end
75
69
 
76
70
  def loaded?
77
- !!@collection
71
+ !!@loaded
72
+ end
73
+
74
+ def find_by_ids(ids)
75
+ @model.find_by_ids(ids, {}, find_options)
78
76
  end
79
77
 
80
78
  def respond_to?(method, priv = false)
@@ -85,21 +83,81 @@ class Mingo
85
83
 
86
84
  def method_missing(method, *args, &block)
87
85
  load_collection
88
- @collection.send(method, *args, &block)
86
+ @loaded.send(method, *args, &block)
89
87
  end
90
-
91
- def unload_collection
92
- @collection = nil
88
+
89
+ def join_collection
90
+ @join_collection ||= @parent.class.collection[@property.to_s]
91
+ end
92
+
93
+ def counter_cache
94
+ @counter_cache ||= @parent[@counter_cache_field].to_i
95
+ end
96
+
97
+ def counter_cache?
98
+ !!@parent[@counter_cache_field]
99
+ end
100
+
101
+ def change_counter_cache(by)
102
+ @counter_cache = counter_cache + by
103
+ @parent.update '$inc' => { @counter_cache_field => by }
104
+ end
105
+
106
+ # Example: {self => 'user_id', 'movie_id' => Movie}
107
+ def analyze_mapping(mapping)
108
+ model = self_referencing_key = forward_referencing_key = nil
109
+ mapping.each do |key, value|
110
+ if key == @parent.class then self_referencing_key = value.to_s
111
+ elsif value < Mingo
112
+ forward_referencing_key = key.to_s
113
+ model = value
114
+ end
115
+ end
116
+ [model, self_referencing_key, forward_referencing_key]
117
+ end
118
+
119
+ def find_options
120
+ @find_options ||= begin
121
+ decorator = self.class.decorate_with
122
+ decorate_block = self.class.decorate_each
123
+
124
+ if decorator or decorate_block
125
+ {:convert => lambda { |doc|
126
+ @model.new(doc).tap do |obj|
127
+ obj.extend decorator if decorator
128
+ if decorate_block
129
+ join_doc = find_join_doc(doc['_id'])
130
+ decorate_block.call(obj, join_doc)
131
+ end
132
+ end
133
+ }}
134
+ else
135
+ {}
136
+ end
137
+ end
138
+ end
139
+
140
+ def load_join
141
+ @join_loaded ||= join_cursor.to_a
142
+ end
143
+
144
+ def find_join_doc(forward_id)
145
+ load_join.find { |d| d[@forward_referencing_key] == forward_id }
146
+ end
147
+
148
+ def join_cursor
149
+ # TODO: make options configurable
150
+ join_collection.find({@self_referencing_key => @parent.id}, :sort => '_id')
93
151
  end
94
152
 
95
153
  def load_collection
96
- @collection ||= if @embedded.empty? then []
154
+ @loaded ||= if self.object_ids.empty? then []
97
155
  else find_by_ids(self.object_ids)
98
156
  end
99
157
  end
100
-
101
- def find_by_ids(ids)
102
- @model.find_by_ids(ids, {}, find_options)
158
+
159
+ def unload_collection
160
+ @loaded = nil
103
161
  end
104
162
  end
105
163
  end
metadata CHANGED
@@ -1,81 +1,45 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: mingo
3
- version: !ruby/object:Gem::Version
4
- hash: 29
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 3
10
- version: 0.1.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
13
- - "Mislav Marohni\xC4\x87"
7
+ authors:
8
+ - Mislav Marohnić
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2010-10-23 00:00:00 +02:00
12
+ date: 2011-03-07 00:00:00.000000000 +01:00
19
13
  default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: mongo
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2157373800 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 15
30
- segments:
31
- - 1
32
- - 0
33
- version: "1.0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
34
23
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: hashie
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2157373800
26
+ - !ruby/object:Gem::Dependency
27
+ name: hashie
28
+ requirement: &2157373320 !ruby/object:Gem::Requirement
40
29
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 15
45
- segments:
46
- - 0
47
- - 4
48
- - 0
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
49
33
  version: 0.4.0
50
34
  type: :runtime
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: rspec
54
35
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
56
- none: false
57
- requirements:
58
- - - ~>
59
- - !ruby/object:Gem::Version
60
- hash: 62196427
61
- segments:
62
- - 2
63
- - 0
64
- - 0
65
- - beta
66
- - 20
67
- version: 2.0.0.beta.20
68
- type: :development
69
- version_requirements: *id003
36
+ version_requirements: *2157373320
70
37
  description: Mingo is a minimal document-object mapper for MongoDB.
71
38
  email: mislav.marohnic@gmail.com
72
39
  executables: []
73
-
74
40
  extensions: []
75
-
76
41
  extra_rdoc_files: []
77
-
78
- files:
42
+ files:
79
43
  - Rakefile
80
44
  - lib/mingo/cursor.rb
81
45
  - lib/mingo/many_proxy.rb
@@ -83,36 +47,26 @@ files:
83
47
  has_rdoc: false
84
48
  homepage: http://github.com/mislav/mingo
85
49
  licenses: []
86
-
87
50
  post_install_message:
88
51
  rdoc_options: []
89
-
90
- require_paths:
52
+ require_paths:
91
53
  - lib
92
- required_ruby_version: !ruby/object:Gem::Requirement
54
+ required_ruby_version: !ruby/object:Gem::Requirement
93
55
  none: false
94
- requirements:
95
- - - ">="
96
- - !ruby/object:Gem::Version
97
- hash: 3
98
- segments:
99
- - 0
100
- version: "0"
101
- required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
61
  none: false
103
- requirements:
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- hash: 3
107
- segments:
108
- - 0
109
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
110
66
  requirements: []
111
-
112
67
  rubyforge_project:
113
- rubygems_version: 1.3.7
68
+ rubygems_version: 1.5.3
114
69
  signing_key:
115
70
  specification_version: 3
116
71
  summary: Minimal Mongo
117
72
  test_files: []
118
-