mongorilla 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Takeshi Morita
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,189 @@
1
+ = mongorilla
2
+
3
+ mongorilla is very small Object Document Mapper for mongo.
4
+ mongorilla can update atomic with condition.
5
+ mongorilla accept single server or Master/Slave or Replica Set
6
+
7
+ === sample
8
+ Mongorilla::Collection.build("/var/www/mongo_config.yml") #only one time when boot
9
+ class User
10
+ UserFields = [:_id,:name,:password,:logs,:log_count]
11
+ include Mongorilla::Document
12
+ end
13
+ user = User.create(:name => "morita",:password => "pass")
14
+ u = User.find(user.id) # find by id
15
+ u = User.find({:name => "morita"},:master=>true)[0] #find user named morita for master db (if you want avoid slave time lag)
16
+ u.password = "hello" #change attribute whithout update db yet
17
+ u.push("logs","ooooo") #$push whithout update db yet
18
+ u.inc("log_count",1) #$inc whithout update db yet
19
+ u.save #update above change to db
20
+ u.push("logs","bb") #$push whithout update db yet
21
+ u.push("logs","cc") #$push whithout update db yet
22
+ p u.logs # ["ooooo","bb","cc"]
23
+ u.inc("log_count",2) #$inc +2 whithout update db yet
24
+ p u.log_count # 3
25
+ ret = u.save({:log_count => {"$lt" => 1}},Mongorilla::Document::SYNC) #update failed because log_count was 1.
26
+ p ret # false
27
+ p u.logs # ["ooooo"]
28
+ p u.log_count # 1
29
+ u.pull("logs","ooooo") #$pull whithout update db yet
30
+ p u.logs #["ooooo"] $pull is not reflect object because pull can be complex when embedded or $gt etc.
31
+ ret = u.save(Mongorilla::Document::RELOAD) #update success and reflect object because reload was specified.
32
+ p ret #true
33
+ p user.logs # []
34
+
35
+ === config
36
+ #single server
37
+ host: localhost
38
+ port: 27017
39
+ database: dev
40
+
41
+ #Master/Slave
42
+ host: localhost
43
+ port: 27017
44
+ database: dev
45
+ slaves:
46
+ - host: localhost
47
+ port: 27018
48
+
49
+ #replica Set
50
+ hosts
51
+ - - localhost
52
+ - 27017
53
+ - - localhost
54
+ - 27018
55
+ database: dev
56
+
57
+ === syntax
58
+ 1. Connect Server below statement when boot application
59
+ Mongorilla::Collection.build("/var/www/mongo_config.yml")
60
+ 2. create document class and define constant class name + Fields with document fields and include Mongorilla::Document.
61
+ class Class
62
+ ClassNameFields = [:_id,...]
63
+ include Mongorilla::Document
64
+ end
65
+
66
+ ==== Class.create(attributes)
67
+ args:
68
+ attributes hash for document
69
+
70
+ description:
71
+ set attributes to db and return new Object
72
+
73
+ ==== Class.collection
74
+ description:
75
+ get mongodb collection
76
+
77
+ === Class.find_one(cond,opt)
78
+ args:
79
+ cond condition {:logcount => {"$gt" => 2}} etc.
80
+ opt option master => true then find master db other option see mongo driver {:master => true,:fields => [:name]} etc.
81
+ description:
82
+ find one match object. if not exitst,return nil
83
+
84
+ === Class.find(cond,opt)
85
+ args:
86
+ cond condition {:logcount => {"$gt" => 2}} etc. if cond is String or BSON::ObjectId then search _id and return one object.
87
+ opt option master => true then find master db other option see mongo driver {:master => true,:fields => [:name]} etc.
88
+ description:
89
+ return match objects array. if not exitst,return nil
90
+
91
+ === #set(k,v)
92
+ args:
93
+ k field name for $set
94
+ v value for $set
95
+ description:
96
+ modify object attribute and modify @change for save. this method does not reflect db.
97
+
98
+ === #inc(k,v)
99
+ args:
100
+ k field name for $inc
101
+ v amount for $inc. it is possible less than 0.
102
+ description:
103
+ modify object attribute and modify @change for save. this method does not reflect db.
104
+
105
+ === #push(k,v)
106
+ args:
107
+ k field name for $push
108
+ v value for $push
109
+ description:
110
+ modify object attribute and modify @change for save. this method does not reflect db.
111
+
112
+ === #unset(k)
113
+ args:
114
+ k field name for $unset
115
+ description:
116
+ modify object attribute and modify @change for save. this method does not reflect db.
117
+
118
+ === #push_all(k,v)
119
+ args:
120
+ k field name for $pushAll
121
+ v value for $pushAll
122
+ description:
123
+ modify object attribute and modify @change for save. this method does not reflect db.
124
+
125
+ === #add_to_set(k,v)
126
+ args:
127
+ k field name for $addToSet
128
+ v value for $addToSet
129
+ description:
130
+ modify object attribute and modify @change for save. this method does not reflect db.
131
+
132
+ === #pop(k,v)
133
+ args:
134
+ k field name for $pop
135
+ v value if less than 0 remove first else remove tail
136
+ description:
137
+ modify object attribute and modify @change for save. this method does not reflect db.
138
+
139
+ === #pull(k,v)
140
+ args:
141
+ k field name for $pull
142
+ v value for $pull
143
+ description:
144
+ modify @change for save. this method does not reflect object and db.
145
+
146
+ === #pull_all(k,v)
147
+ args:
148
+ k field name for $pullAll
149
+ v value for $pullAll
150
+ description:
151
+ modify @change for save. this method does not reflect object and db.
152
+
153
+ === #save(cond,opt,mode)
154
+ args:
155
+ cond condition(optional) {:count => {$lt => 20}} etc.
156
+ opt option(optional) see mongdb driver {:upsert => true} etc.
157
+ mode Mongorilla::Document::SYNC(default) or Mongorilla::Document::ASYNC or Mongorilla::Document::RELOAD
158
+ SYNC is specified,check result,RELOAD is specified,check result and fetch update record from db,
159
+ ASYNC is specified,does not check result.
160
+
161
+ description:
162
+ reflect @change to DB
163
+
164
+ === #delete
165
+ description:
166
+ remove object from db
167
+
168
+ === #reset
169
+ description:
170
+ discard @changes and reset object to original
171
+
172
+ === #reload
173
+ description:
174
+ fetch record from db.
175
+
176
+ == Contributing to mongorilla
177
+
178
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
179
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
180
+ * Fork the project
181
+ * Start a feature/bugfix branch
182
+ * Commit and push until you are happy with your contribution
183
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
184
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
185
+
186
+ == Copyright
187
+
188
+ Copyright (c) 2011 Takeshi Morita. See LICENSE.txt for
189
+ further details.
@@ -0,0 +1,132 @@
1
+ require 'yaml'
2
+ require 'mongo'
3
+ module Mongorilla
4
+ class Collection
5
+ @@master = nil
6
+ @@slaves = nil
7
+ @@slave_index = 0
8
+ @@config = nil
9
+
10
+ def self.master
11
+ @@master
12
+ end
13
+
14
+ def self.slaves
15
+ @@slaves
16
+ end
17
+
18
+ def self.config
19
+ @@config
20
+ end
21
+
22
+ def self.load_config(path)
23
+ @@config = YAML.load(File.read(path))
24
+ end
25
+
26
+ def self.build(path=File.expand_path("../config.yml",__FILE__))
27
+ load_config(path)
28
+ @@config["max_retries"] ||= 10
29
+ @@config["meantime"] ||= 0.5
30
+ if @@config["hosts"]
31
+ @@master = Mongo::ReplSetConnection.new(*@@config["hosts"]).db(@@config["database"])
32
+ elsif @@config["slaves"]
33
+ @@master = Mongo::Connection.new(@@config["host"],@@config["port"]).db(@@config["database"])
34
+ @@slaves = @@config["slaves"].map{|s| Mongo::Connection.new(s["host"],s["port"]).db(@@config["database"])}
35
+ else
36
+ host = @@config["host"] ? @@config["host"] : "localhost"
37
+ port = @@config["port"] ? @@config["port"].to_i : 27017
38
+ @@master = Mongo::Connection.new(host,port).db(@@config["database"])
39
+ end
40
+ end
41
+
42
+ def r_col
43
+ if @@slaves
44
+ @@slave_index += 1
45
+ @@slaves[@@slave_index % @@slaves.length][@name]
46
+ else
47
+ @@master[@name]
48
+ end
49
+ end
50
+
51
+ def w_col
52
+ @@master[@name]
53
+ end
54
+
55
+ def initialize(collection_name)
56
+ @name = collection_name
57
+ end
58
+
59
+ def find_one(cond={},opt={})
60
+ opt[:limit] = 1
61
+ if cond.is_a?(String) || cond.is_a?(BSON::ObjectId)
62
+ cond = BSON::ObjectId(cond) if cond.is_a?(String)
63
+ cond = {:_id => cond}
64
+ end
65
+ ret = find(cond,opt)
66
+ ret.first
67
+ end
68
+
69
+ def count(cond={},opt={})
70
+ find(cond,opt).count
71
+ end
72
+
73
+ def find(cond={},opt={})
74
+ if opt[:master] || opt["master"]
75
+ opt.delete(:master)
76
+ opt.delete("master")
77
+ if @@config["hosts"]
78
+ opt[:read] = :primary
79
+ end
80
+ rescue_connection_failure do
81
+ w_col.find(cond,opt)
82
+ end
83
+ else
84
+ if @@config["hosts"] && @@config["read_secondary"]
85
+ opt[:read] = :secondary
86
+ end
87
+ begin
88
+ rescue_connection_failure do
89
+ r_col.find(cond,opt)
90
+ end
91
+ rescue
92
+ w_col.find(cond,opt)
93
+ end
94
+ end
95
+ end
96
+
97
+ def insert(data,opt={})
98
+ rescue_connection_failure do
99
+ w_col.insert(data,opt)
100
+ end
101
+ end
102
+
103
+ def update(cond,data,opt)
104
+ rescue_connection_failure do
105
+ w_col.update(cond,data,opt)
106
+ end
107
+ end
108
+
109
+ def remove(cond={},opt={})
110
+ if cond.is_a? String
111
+ cond = {:_id => BSON::ObjectId(cond)}
112
+ elsif cond.is_a? BSON::ObjectId
113
+ cond = {:_id => cond}
114
+ end
115
+ rescue_connection_failure do
116
+ w_col.remove(cond,opt)
117
+ end
118
+ end
119
+
120
+ def rescue_connection_failure(max_retries=@@config["max_retries"])
121
+ retries = 0
122
+ begin
123
+ yield
124
+ rescue Mongo::ConnectionFailure => ex
125
+ retries += 1
126
+ raise ex if retries > max_retries
127
+ sleep(@@config["meantime"])
128
+ retry
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,217 @@
1
+ require File.expand_path("../collection.rb",__FILE__)
2
+ module Mongorilla
3
+ module Document
4
+ RELOAD = :reload
5
+ SYNC = :sync
6
+ ASYNC = :async
7
+ def self.pluralize(name)
8
+ underscore_name = name.gsub(/^[A-Z]/){|m| "_" + m.downcase}[1 .. -1]
9
+ case underscore_name
10
+ when /(s|ss|sh|ch|o|x)$/
11
+ underscore_name + "es"
12
+ when /[^aeiou]y$/
13
+ underscore_name[0 .. -2] + "ies"
14
+ when /(f|fe)$/
15
+ underscore_name.gsub(/(f|fe)$/){|m| "ves"}
16
+ when /child$/
17
+ underscore_name + "en"
18
+ when /foot$/
19
+ underscore_name.gsub(/foot$/){|m| "feet"}
20
+ when /tooth$/
21
+ underscore_name.gsub(/tooth$/){|m| "teeth"}
22
+ when /man$/
23
+ underscore_name.gsub(/man$/){|m| "men"}
24
+ when /woman$/
25
+ underscore_name.gsub(/woman$/){|m| "women"}
26
+ else
27
+ underscore_name + "s"
28
+ end
29
+ end
30
+
31
+ def self.included(c)
32
+ fields = c.const_get("#{c}Fields")
33
+ fields.each do |f|
34
+ define_method(f) {@doc[f.to_s] }
35
+ define_method(f.to_s + "="){|v| set(f.to_s,v);}
36
+ end
37
+ alias_method :id,:_id
38
+ col_name = pluralize(c.to_s)
39
+ @@col = Collection.new(col_name)
40
+
41
+ def initialize(doc)
42
+ @orig = doc.dup
43
+ @doc = doc
44
+ @changes={}
45
+ end
46
+
47
+ def changes
48
+ @changes
49
+ end
50
+
51
+ def set(f,v)
52
+ send(f)
53
+ @doc[f] = v
54
+ @changes["$set"] ||= {}
55
+ @changes["$set"][f] = v
56
+ end
57
+
58
+ def inc(f,v)
59
+ @doc[f] = 0 unless send(f)
60
+ @doc[f] += v
61
+ @changes["$inc"] ||= {}
62
+ @changes["$inc"][f] = v
63
+ end
64
+
65
+ def push(f,v)
66
+ @doc[f] = [] unless send(f)
67
+ @doc[f].push(v)
68
+ @changes["$push"] ||= {}
69
+ @changes["$push"][f] = v
70
+ end
71
+
72
+ def unset(f)
73
+ @doc.delete(f)
74
+ @changes["$unset"] ||= {}
75
+ @changes["$unset"][f] = 1
76
+ end
77
+
78
+ def push_all(f,v)
79
+ if send(f)
80
+ @doc[f] += v
81
+ else
82
+ @doc[f] = v
83
+ end
84
+ @changes["$pushAll"] ||= {}
85
+ @changes["$pushAll"][f] = v
86
+ end
87
+
88
+ def add_to_set(f,v)
89
+ if v.is_a? Hash
90
+ values = v["$each"]
91
+ else
92
+ values = [v]
93
+ end
94
+ values.each do|val|
95
+ if send(f)
96
+ @doc[f].push(val) unless @doc[f].find{|r| r == val}
97
+ else
98
+ @doc[f] = [val]
99
+ end
100
+ end
101
+ @changes["$addToSet"] ||= {}
102
+ @changes["$addToSet"][f] = v
103
+ end
104
+
105
+ def pop(f,v)
106
+ @changes["$pop"] ||= {}
107
+ @changes["$pop"][f] = v
108
+ if v > 0
109
+ send(f).pop
110
+ else
111
+ send(f).shift
112
+ end
113
+ end
114
+
115
+ def pull(f,v)
116
+ @changes["$pull"] ||= {}
117
+ @changes["$pull"][f] = v
118
+ end
119
+
120
+ def pull_all(f,v)
121
+ @changes["$pull_all"] ||= {}
122
+ @changes["$pull_all"][f] = v
123
+ end
124
+
125
+ def save(*args)
126
+ cond = {}
127
+ opt = {}
128
+ mode = SYNC
129
+ args.each do|arg|
130
+ if arg.is_a?(Symbol)
131
+ mode = arg
132
+ elsif cond == {}
133
+ cond = arg
134
+ else
135
+ opt = arg
136
+ end
137
+ end
138
+ opt[:safe] = true if [SYNC,RELOAD].include?(mode)
139
+ if @changes
140
+ ret = @@col.update(cond,@changes,opt)
141
+ if opt[:safe] && ret["n"] != 1
142
+ reset
143
+ return false
144
+ end
145
+ end
146
+ if mode == RELOAD
147
+ reload
148
+ end
149
+ @orig = @doc.dup
150
+ @changes={}
151
+ true
152
+ end
153
+
154
+ def reset
155
+ @changes={}
156
+ @doc = @orig.dup
157
+ end
158
+
159
+ def delete
160
+ self.class.remove(:_id => id)
161
+ end
162
+
163
+ def reload
164
+ @changes={}
165
+ @doc = @@col.find_one(id,:master => true)
166
+ @orig = @doc.dup
167
+ end
168
+
169
+ def c.collection
170
+ @@col
171
+ end
172
+
173
+ def c.create(data,opt={})
174
+ if opt == {}
175
+ opt[:safe] = true
176
+ end
177
+ ret = @@col.insert(data,opt)
178
+ if ret.is_a? Array
179
+ ret.map{|uid| find(uid,:master=>true)}
180
+ elsif ret
181
+ find(ret,:master=>true)
182
+ end
183
+ end
184
+
185
+ def c.find_one(cond,opt={})
186
+ ret = @@col.find_one(cond,opt)
187
+ if ret
188
+ return self.new(ret)
189
+ else
190
+ return nil
191
+ end
192
+ end
193
+
194
+ def c.count(cond={},opt={})
195
+ @@col.count(cond,opt)
196
+ end
197
+
198
+ def c.find(cond={},opt={})
199
+ if !cond.is_a? Hash
200
+ find_one(cond,opt)
201
+ else
202
+ ret = @@col.find(cond,opt)
203
+ return nil if ret.count == 0
204
+ ret.map{|r| self.new(r)}
205
+ end
206
+ end
207
+
208
+ def c.update(cond,data,opt={})
209
+ @@col.update(cond,data,opt)
210
+ end
211
+
212
+ def c.remove(cond={},opt={})
213
+ @@col.remove(cond,opt)
214
+ end
215
+ end
216
+ end
217
+ end
data/lib/mongorilla.rb ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'mongorilla/collection'
3
+ require 'mongorilla/document'
4
+ module Mongorilla
5
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongorilla
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Takeshi Morita
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongo
16
+ requirement: &16529460 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *16529460
25
+ - !ruby/object:Gem::Dependency
26
+ name: mongo_ext
27
+ requirement: &16528040 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *16528040
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &16526160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.3.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *16526160
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: &16540780 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *16540780
58
+ - !ruby/object:Gem::Dependency
59
+ name: jeweler
60
+ requirement: &16538560 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.6.4
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *16538560
69
+ - !ruby/object:Gem::Dependency
70
+ name: rcov
71
+ requirement: &16535020 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *16535020
80
+ description: Small Object Document Mapper for Mongo. you can update atomic with condition
81
+ email: laten@nifty.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files:
85
+ - LICENSE.txt
86
+ - README.rdoc
87
+ files:
88
+ - lib/mongorilla.rb
89
+ - lib/mongorilla/collection.rb
90
+ - lib/mongorilla/document.rb
91
+ - LICENSE.txt
92
+ - README.rdoc
93
+ homepage: http://github.com/takeshy/mongorilla
94
+ licenses:
95
+ - MIT
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ segments:
107
+ - 0
108
+ hash: 1915061905546845040
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.11
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Small Object Document Mapper for Mongo
121
+ test_files: []