kongo 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +15 -0
- data/README.md +204 -0
- data/Rakefile +1 -0
- data/kongo.gemspec +21 -0
- data/lib/kongo.rb +348 -0
- data/lib/kongo/version.rb +3 -0
- metadata +70 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
The Azure License
|
2
|
+
|
3
|
+
Copyright (c) 2011 Kenneth Ballenegger
|
4
|
+
|
5
|
+
Attribute to Kenneth Ballenegger - http://kswizz.com/
|
6
|
+
|
7
|
+
You (the licensee) are hereby granted permission, free of charge, to deal in this software or source code (this "Software") without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sublicense this Software, subject to the following conditions:
|
8
|
+
|
9
|
+
You must give attribution to the party mentioned above, by name and by hyperlink, in the about box, credits document and/or documentation of any derivative work using a substantial portion of this Software.
|
10
|
+
|
11
|
+
You may not use the name of the copyright holder(s) to endorse or promote products derived from this Software without specific prior written permission.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER DEALINGS IN THIS SOFTWARE.
|
14
|
+
|
15
|
+
http://license.azuretalon.com/
|
data/README.md
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
# Kongo
|
2
|
+
|
3
|
+
Kongo is a lightweight and generic library for accessing data from Mongo.
|
4
|
+
|
5
|
+
## Rationale
|
6
|
+
|
7
|
+
Kongo is not your typical ORM. Traditionally, according to MVC architecture best practices, you would create a model class to represent data from each collection in your application. However, while it is a good abstraction early on, as an application grows and scales, it is not uncommon to see models grow to thousands of lines of code, grouping together many different pieces of unrelated business logic. Not only that, but dependencies and tight coupling arises between the various related models.
|
8
|
+
|
9
|
+
Kongo takes a different approach. Kongo does not have models, it simply provides basic data-access functionality and object-oriented wrapping of the Mongo driver. It also provides support for extending collections and models with libraries. Kongo believes logic belongs in libraries, and related code belongs in the same file, not divided amongst multiple models and mixed with other logic.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Using Kongo is fairly straight-forward.
|
14
|
+
|
15
|
+
First, however, Kongo must know how to connect to Mongo (put this in a init / config type file):
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
db = Mongo::Connection.new(...)['dbname']
|
19
|
+
Kongo::Collection.fetch_collections_using do |collection_name|
|
20
|
+
# In larger applications you would insert logic here to figure out
|
21
|
+
# how to connect to Mongo, when you have multiple replicated or
|
22
|
+
# sharded clusters...
|
23
|
+
db[collection_name]
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Then, simply define your collections as you use them. (It's okay to do this in multiple contexts).
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
Posts = Kongo::Collection.new(:posts)
|
31
|
+
Comments = Kongo::Collection.new(:comments)
|
32
|
+
```
|
33
|
+
|
34
|
+
You can use a collection to read data:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
post = Posts.find_by_id(id)
|
38
|
+
|
39
|
+
# this returns a cursor
|
40
|
+
comments = Comments.find_many(post: post._id)
|
41
|
+
|
42
|
+
# get a json of the top ten comments
|
43
|
+
top = comments.sort(score: -1)
|
44
|
+
json = top.to_enum.take(10).map(:&to_hash).to_json
|
45
|
+
```
|
46
|
+
|
47
|
+
All Kongo methods yield `Kongo::Model` objects whenever possible. These objects simply wrap around a `Hash` and provide some helper methods for dealing with the object.
|
48
|
+
|
49
|
+
Perhaps the most useful of these helpers is `update!`:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Kongo encourages only performing atomic updates:
|
53
|
+
|
54
|
+
# simple setters change the data and record the appropriate delta
|
55
|
+
post.title = 'New title!'
|
56
|
+
post.date = Time.now.to_i
|
57
|
+
# more advanced deltas can be set explicitly. see mongo update syntax for documentation.
|
58
|
+
post.delta('$inc', edit_count: 1)
|
59
|
+
|
60
|
+
# the update! method commits deltas.
|
61
|
+
post.update!
|
62
|
+
```
|
63
|
+
|
64
|
+
This is just the tip of the iceberg. See the documentation for the Kongo classes to see everything that Kongo can do.
|
65
|
+
|
66
|
+
### Writing an extension
|
67
|
+
|
68
|
+
Imagine you have three models, `user`, `account`, and `transaction`. You want to make a library that will let you transfer money between users and their accounts. Typically you'd add code to your three existing model classes:
|
69
|
+
|
70
|
+
models/user.rb:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
require 'models/account'
|
74
|
+
|
75
|
+
class User < Model
|
76
|
+
|
77
|
+
# a whole bunch of random crap
|
78
|
+
# ...
|
79
|
+
|
80
|
+
def earnings
|
81
|
+
Account::find_by_id(self['earnings_account'])
|
82
|
+
end
|
83
|
+
def spend
|
84
|
+
Account::find_by_id(sefl['spend_account'])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
models/account.rb:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
require 'models/transaction'
|
93
|
+
class Account < Model
|
94
|
+
# more random crap
|
95
|
+
def deposit; ...; end
|
96
|
+
|
97
|
+
def transfer_to(other_account, amount)
|
98
|
+
if Transaction.create(self, other_account, amount})
|
99
|
+
self.update!('$inc', amount: (-1 * amount))
|
100
|
+
other_account('$inc', amount: amount)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
models/transaction.rb:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class Transaction < Model
|
109
|
+
def self.create(from, to, amount)
|
110
|
+
if from.balance >= amount
|
111
|
+
insert!({form: from['_id'], to: to['_id'], amount: amount})
|
112
|
+
true
|
113
|
+
else
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
Now we have code related to the same thing in three different model files, and it's all mixed up with the other functionality of these models (such as analytics for transactions or authentication for the user model).
|
121
|
+
|
122
|
+
Instead, if we have the possibility of abstracting this into a library, we might have something much cleaner like a single `lib/finance.rb` file:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
module Finance
|
126
|
+
|
127
|
+
# other finance functionality that does not belong directly to a
|
128
|
+
# model, eg something like inance::convert_currency
|
129
|
+
# our finance extensions to the models:
|
130
|
+
|
131
|
+
module Extensions
|
132
|
+
module User
|
133
|
+
def earnings
|
134
|
+
Account::find_by_id(self['earnings_account'])
|
135
|
+
end
|
136
|
+
def spend
|
137
|
+
Account::find_by_id(sefl['spend_account'])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
Kongo::Model.add_extension(:users, User)
|
141
|
+
|
142
|
+
module Transactions
|
143
|
+
def create(from, to, amount)
|
144
|
+
if from.balance >= amount
|
145
|
+
insert!({form: from['_id'], to: to['_id'], amount: amount})
|
146
|
+
true
|
147
|
+
else
|
148
|
+
false
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
Kongo::Collection.add_extension(:transactions, Transactions)
|
153
|
+
|
154
|
+
TransactionsCollection = Kongo::Collection.new(:transactions)
|
155
|
+
module Account
|
156
|
+
def deposit; ...; end
|
157
|
+
def transfer_to(other_account, amount)
|
158
|
+
if TransactionsCollection.create(self, other_account, amount})
|
159
|
+
self.update!('$inc', amount: (-1 * amount))
|
160
|
+
other_account('$inc', amount: amount)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
Kongo::Model.add_extension(:accounts, Account)
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
# The Account and Transaction "models" belong to the finance library,
|
168
|
+
# in that it is their primary function. So we provide constants for them:
|
169
|
+
Acounts = Kongo::Collection.new(:accounts)
|
170
|
+
Transactions = Kongo::Collection.new(:transactions)
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
Using this extension is now straight-forward:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
require 'lib/authentication' # imagine this lib provides user-related functionality
|
178
|
+
require 'lib/finance'
|
179
|
+
user = Authentication.current_user
|
180
|
+
kenneth = Authentication::Users.find_one(email: 'kenneth@ballenegger.com')
|
181
|
+
user.transfer_to(kenneth, 100)
|
182
|
+
```
|
183
|
+
|
184
|
+
## Installation
|
185
|
+
|
186
|
+
Add this line to your application's Gemfile:
|
187
|
+
|
188
|
+
gem 'kongo'
|
189
|
+
|
190
|
+
And then execute:
|
191
|
+
|
192
|
+
$ bundle
|
193
|
+
|
194
|
+
Or install it yourself as:
|
195
|
+
|
196
|
+
$ gem install kongo
|
197
|
+
|
198
|
+
## Contributing
|
199
|
+
|
200
|
+
1. Fork it
|
201
|
+
2. Create your feature branch (```git checkout -b my-new-feature```)
|
202
|
+
3. Commit your changes (```git commit -am 'Add some feature'```)
|
203
|
+
4. Push to the branch (```git push origin my-new-feature```)
|
204
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/kongo.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'kongo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'kongo'
|
8
|
+
gem.version = Kongo::VERSION
|
9
|
+
gem.authors = ['Kenneth Ballenegger']
|
10
|
+
gem.email = ['kenneth@ballenegger.com']
|
11
|
+
gem.description = %q{Kongo is a lightweight and generic library for accessing data from Mongo.}
|
12
|
+
gem.summary = %q{Kongo is a lightweight and generic library for accessing data from Mongo.}
|
13
|
+
gem.homepage = 'https://github.com/kballenegger/Kongo'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency('mongo', '>= 1.7.0')
|
21
|
+
end
|
data/lib/kongo.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
|
2
|
+
require 'mongo'
|
3
|
+
|
4
|
+
|
5
|
+
module Kongo
|
6
|
+
|
7
|
+
class Collection
|
8
|
+
|
9
|
+
# Initialize with a collection name (as symbol), will use that collection
|
10
|
+
# name to generate the connection lazily when it is first used, thru the
|
11
|
+
# collection callback (*must* be defined).
|
12
|
+
#
|
13
|
+
def initialize(name)
|
14
|
+
@coll_name = name
|
15
|
+
@visible_ivars = []
|
16
|
+
@@extensions ||= {}
|
17
|
+
|
18
|
+
if @@extensions[name.to_sym]
|
19
|
+
@@extensions[name.to_sym].each do |const|
|
20
|
+
extend(const)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# `find_one` and `find_by_id` are the same method. When passed a native
|
26
|
+
# BSON::ObjectId, it will act as a find_by_id, otherwise, expects a regular
|
27
|
+
# query hash. Will return a Kongo::Model.
|
28
|
+
#
|
29
|
+
def find_one(*args)
|
30
|
+
(r = coll.find_one(*args)) ?
|
31
|
+
Model.new(r, coll) : r
|
32
|
+
end
|
33
|
+
alias :find_by_id :find_one
|
34
|
+
|
35
|
+
# `find`, aka. `find_many` returns a Kongo::Cursor wrapping the Mongo
|
36
|
+
# cursor.
|
37
|
+
#
|
38
|
+
def find(*args)
|
39
|
+
(c = coll.find(*args)).is_a?(::Mongo::Cursor) ?
|
40
|
+
Cursor.new(c, coll) : c
|
41
|
+
end
|
42
|
+
alias :find_many :find
|
43
|
+
|
44
|
+
# Count, just forwards to driver.
|
45
|
+
#
|
46
|
+
def count(*args)
|
47
|
+
coll.count(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Verify existence of record by id.
|
51
|
+
#
|
52
|
+
def has?(id)
|
53
|
+
count(:query => {_id: id}) == 1
|
54
|
+
end
|
55
|
+
|
56
|
+
# Insert a record, returns a Model object.
|
57
|
+
#
|
58
|
+
def insert!(hash)
|
59
|
+
coll.insert(hash)
|
60
|
+
Model.new(hash, coll)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Collection#extend adds the option of extending with a symbol, which
|
64
|
+
# will automatically use that constant from Extensions::Collections,
|
65
|
+
# calls super in every other case.
|
66
|
+
#
|
67
|
+
def extend(arg)
|
68
|
+
super(arg.is_a?(Symbol) ? Extensions::Collections.const_get(arg) : arg)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# This method must be called statically on the ORM once before being used,
|
73
|
+
# so that the ORM knows how to connect to mongo and fetch collections.
|
74
|
+
#
|
75
|
+
# A simple usage example might look like this:
|
76
|
+
#
|
77
|
+
# class MongoCollectionFetcher
|
78
|
+
# def self.fetch(name)
|
79
|
+
# unless @mongo
|
80
|
+
# @mongo = Mongo::Connection.new
|
81
|
+
# end
|
82
|
+
# return @mongo['database'][name.to_s]
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# Kongo::Collection.fetch_collections_using do |n|
|
87
|
+
# MongoCollectionFetcher.fetch(n)
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
def self.fetch_collections_using(&block)
|
91
|
+
@@collection_fetcher = block
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# This method returns the Mongo::Collection object for this collection.
|
96
|
+
#
|
97
|
+
def coll
|
98
|
+
return @coll if @coll
|
99
|
+
|
100
|
+
raise 'Kongo has not been initialized with a collection fetcher.' unless @@collection_fetcher
|
101
|
+
@coll = @@collection_fetcher.call(@coll_name)
|
102
|
+
@coll
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
# Inspecting a Mongo Model attempts to only show *useful* information,
|
108
|
+
# such as what its extensions are as well as certain ivars.
|
109
|
+
#
|
110
|
+
def inspect
|
111
|
+
proxy = Object.new
|
112
|
+
@visible_ivars.each do |ivar|
|
113
|
+
val = instance_variable_get(ivar)
|
114
|
+
proxy.instance_variable_set(ivar, val) unless val.nil?
|
115
|
+
end
|
116
|
+
string = proxy.inspect
|
117
|
+
ext_info = @extensions ? '(+ '+@extensions.join(', ')+')' : ''
|
118
|
+
string.gsub(/Object:0x[0-9a-f]+/, "Kongo::Collection#{ext_info}:0x#{object_id}")
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# Declare extensions using this method:
|
123
|
+
#
|
124
|
+
# Kongo::Collection.add_extension(:collection_name, module)
|
125
|
+
#
|
126
|
+
def self.add_extension(collection_name, mod)
|
127
|
+
((@@extensions ||= {})[collection_name.to_sym] ||= []) << mod
|
128
|
+
end
|
129
|
+
|
130
|
+
# This method just adds the extension to the list of extension, for the
|
131
|
+
# sake of inspect, and call super:
|
132
|
+
#
|
133
|
+
def extend(const)
|
134
|
+
(@extensions ||= []) << const.to_s
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Cursor is an object that wraps around a Mongo::Cursor, wrapping objects it
|
142
|
+
# returns in Kongo::Model objects.
|
143
|
+
#
|
144
|
+
class Cursor
|
145
|
+
|
146
|
+
# `initialize` is typically only used internally by Kongo::Collection when
|
147
|
+
# it returns cursors.
|
148
|
+
#
|
149
|
+
def initialize(cursor, coll)
|
150
|
+
@coll = coll
|
151
|
+
@cursor = cursor
|
152
|
+
end
|
153
|
+
|
154
|
+
# Any method is forwarded to its wrapped cursor.
|
155
|
+
#
|
156
|
+
def method_missing(*args, &block)
|
157
|
+
@cursor.send(*args, &block)
|
158
|
+
end
|
159
|
+
|
160
|
+
# `next` wraps responses in Kongo::Model.
|
161
|
+
#
|
162
|
+
def next
|
163
|
+
(e = @cursor.next).is_a?(Hash) ?
|
164
|
+
Model.new(e, @coll) : e
|
165
|
+
end
|
166
|
+
|
167
|
+
# `each` yields Kongo::Model objects.
|
168
|
+
#
|
169
|
+
def each
|
170
|
+
@cursor.each { |e| yield Model.new(e, @coll) }
|
171
|
+
end
|
172
|
+
|
173
|
+
# `to_enum` returns an Enumerator which yields the results of the cursor.
|
174
|
+
#
|
175
|
+
def to_enum
|
176
|
+
Enumerator.new do |yielder|
|
177
|
+
while @cursor.has_next?
|
178
|
+
yielder.yield(self.next)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# `to_a` returns an array of Kongo::Model.
|
184
|
+
#
|
185
|
+
def to_a
|
186
|
+
arr = []
|
187
|
+
each { |e| arr << e }
|
188
|
+
arr
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Kongo::Model is the most important class of Kongo, it wraps around hashes
|
194
|
+
# representing Mongo records, provides a collection of useful methods, and
|
195
|
+
# allows itself to be extended by third party libraries.
|
196
|
+
#
|
197
|
+
class Model
|
198
|
+
|
199
|
+
# Typically, you would not call Model#new directly, but rather get
|
200
|
+
# a model from a method on Collections, such as a finder or #insert.
|
201
|
+
#
|
202
|
+
def initialize(hash, coll)
|
203
|
+
@coll = coll
|
204
|
+
@hash = hash
|
205
|
+
@deltas = {}
|
206
|
+
@visible_ivars = [:@hash, :@deltas]
|
207
|
+
@@extensions ||= {}
|
208
|
+
|
209
|
+
if @@extensions[coll.name.to_sym]
|
210
|
+
@@extensions[coll.name.to_sym].each do |const|
|
211
|
+
extend(const)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
attr_reader :hash
|
217
|
+
attr_reader :deltas
|
218
|
+
|
219
|
+
# Record fields can be accessed using [] syntax.
|
220
|
+
#
|
221
|
+
def [](k); @hash[k]; end
|
222
|
+
|
223
|
+
# This adds to the list of deltas, so that we may update with no
|
224
|
+
# arguments below
|
225
|
+
#
|
226
|
+
def []=(k,v)
|
227
|
+
@hash[k.to_s]=v
|
228
|
+
|
229
|
+
delta('$set', k => v)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Add a delta
|
233
|
+
#
|
234
|
+
# delta '$inc',
|
235
|
+
# total: 3,
|
236
|
+
# unique: 1
|
237
|
+
#
|
238
|
+
def delta(type, fields = {})
|
239
|
+
fields.each do |k,v|
|
240
|
+
@deltas[type.to_s] ||= {}
|
241
|
+
@deltas[type.to_s][k.to_s] = v
|
242
|
+
end
|
243
|
+
self
|
244
|
+
end
|
245
|
+
|
246
|
+
# `unset` lets you remove a key from the hash, as well as adding it to
|
247
|
+
# the deltas so that the next update will unset that key in Mongo.
|
248
|
+
#
|
249
|
+
def unset(key)
|
250
|
+
@hash.delete(key.to_s)
|
251
|
+
delta('$unset', key => 1)
|
252
|
+
end
|
253
|
+
|
254
|
+
# This method_missing provides accessors for keys as proprieties of the
|
255
|
+
# model object, so you may do:
|
256
|
+
#
|
257
|
+
# object.key = :value
|
258
|
+
# object.key #=> :value
|
259
|
+
#
|
260
|
+
def method_missing(key, *args, &block)
|
261
|
+
key = key.to_s
|
262
|
+
if matches = /^(.+)=$/.match(key)
|
263
|
+
raise ArgumentError.new 'Unexpected argument count.' if args.count != 1
|
264
|
+
self[matches[1]] = args.first
|
265
|
+
else
|
266
|
+
raise ArgumentError.new 'Unexpected argument count.' if args.count != 0
|
267
|
+
return self[key]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# @deprecated
|
272
|
+
# Do not use saves, they're dirty.
|
273
|
+
#
|
274
|
+
def save!(options = {})
|
275
|
+
warn("#{Kernel.caller.first}: `save` is deprecated, use `update` instead.")
|
276
|
+
raise if @stale unless options[:ignore_stale] # TODO: custom exception
|
277
|
+
@coll.save(@hash, :safe => true)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Issues an update on the database, for this record, with the provided
|
281
|
+
# deltas. WARNING: the record will become stale, and should no longer be
|
282
|
+
# saved after an update has been issued.
|
283
|
+
#
|
284
|
+
def update!(deltas = {})
|
285
|
+
return if @deltas.empty?
|
286
|
+
|
287
|
+
id = @hash['_id']
|
288
|
+
raise unless id # TODO: custom exception
|
289
|
+
|
290
|
+
if @deltas
|
291
|
+
deltas = @deltas.merge(deltas)
|
292
|
+
@deltas = {}
|
293
|
+
end
|
294
|
+
|
295
|
+
@stale = true
|
296
|
+
|
297
|
+
@coll.update({_id: id}, deltas, :safe => true)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Deletes this record from the database.
|
301
|
+
#
|
302
|
+
def delete!
|
303
|
+
id = @hash['_id']
|
304
|
+
raise unless id # TODO: custom exception
|
305
|
+
@coll.remove({_id: id})
|
306
|
+
end
|
307
|
+
|
308
|
+
# Returns the hash of the record itself.
|
309
|
+
#
|
310
|
+
def to_hash
|
311
|
+
@hash
|
312
|
+
end
|
313
|
+
|
314
|
+
# Inspecting a Mongo Model attempts to only show *useful* information,
|
315
|
+
# such as what its extensions are as well as certain ivars.
|
316
|
+
#
|
317
|
+
def inspect
|
318
|
+
proxy = Object.new
|
319
|
+
@visible_ivars.each do |ivar|
|
320
|
+
val = instance_variable_get(ivar)
|
321
|
+
proxy.instance_variable_set(ivar, val) unless val.nil?
|
322
|
+
end
|
323
|
+
string = proxy.inspect
|
324
|
+
ext_info = @extensions ? '(+ '+@extensions.join(', ')+')' : ''
|
325
|
+
string.gsub(/Object:0x[0-9a-f]+/, "Kongo::Model#{ext_info}:0x#{object_id}")
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
|
330
|
+
# Declare extensions using this method:
|
331
|
+
#
|
332
|
+
# Kongo::Model.add_extension(:collection_name, module)
|
333
|
+
#
|
334
|
+
def self.add_extension(collection_name, mod)
|
335
|
+
((@@extensions ||= {})[collection_name.to_sym] ||= []) << mod
|
336
|
+
end
|
337
|
+
|
338
|
+
# This method just adds the extension to the list of extension, for the
|
339
|
+
# sake of inspect, and call super:
|
340
|
+
#
|
341
|
+
def extend(const)
|
342
|
+
(@extensions ||= []) << const.to_s
|
343
|
+
super
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kongo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kenneth Ballenegger
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mongo
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.7.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.7.0
|
30
|
+
description: Kongo is a lightweight and generic library for accessing data from Mongo.
|
31
|
+
email:
|
32
|
+
- kenneth@ballenegger.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- .gitignore
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- kongo.gemspec
|
43
|
+
- lib/kongo.rb
|
44
|
+
- lib/kongo/version.rb
|
45
|
+
homepage: https://github.com/kballenegger/Kongo
|
46
|
+
licenses: []
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.24
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Kongo is a lightweight and generic library for accessing data from Mongo.
|
69
|
+
test_files: []
|
70
|
+
has_rdoc:
|