mingo 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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
-