mongo_record 0.4.2
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.
- data/LICENSE +202 -0
- data/README.rdoc +85 -0
- data/Rakefile +38 -0
- data/examples/tracks.rb +109 -0
- data/lib/mongo_record/base.rb +988 -0
- data/lib/mongo_record/convert.rb +64 -0
- data/lib/mongo_record/log_device.rb +111 -0
- data/lib/mongo_record/sql.rb +235 -0
- data/lib/mongo_record/subobject.rb +109 -0
- data/lib/mongo_record.rb +21 -0
- data/mongo-activerecord-ruby.gemspec +38 -0
- data/tests/address.rb +12 -0
- data/tests/class_in_module.rb +11 -0
- data/tests/course.rb +10 -0
- data/tests/student.rb +34 -0
- data/tests/test_log_device.rb +82 -0
- data/tests/test_mongo.rb +774 -0
- data/tests/test_sql.rb +176 -0
- data/tests/track2.rb +9 -0
- data/tests/track3.rb +9 -0
- metadata +85 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright 2009 10gen, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# Mongo stores trees of JSON-like documents. These +to_mongo_value+ methods
|
16
|
+
# covert objects to Hash values, which are converted by the Mongo driver
|
17
|
+
# to the proper types.
|
18
|
+
|
19
|
+
class Object
|
20
|
+
# Convert an Object to a Mongo value. Used by MongoRecord::Base when saving
|
21
|
+
# data to Mongo.
|
22
|
+
def to_mongo_value
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
# From Rails
|
27
|
+
def instance_values #:nodoc:
|
28
|
+
instance_variables.inject({}) do |values, name|
|
29
|
+
values[name.to_s[1..-1]] = instance_variable_get(name)
|
30
|
+
values
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class Array
|
37
|
+
# Convert an Array to a Mongo value. Used by MongoRecord::Base when saving
|
38
|
+
# data to Mongo.
|
39
|
+
def to_mongo_value
|
40
|
+
self.collect {|v| v.to_mongo_value}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Hash
|
45
|
+
# Convert an Hash to a Mongo value. Used by MongoRecord::Base when saving
|
46
|
+
# data to Mongo.
|
47
|
+
def to_mongo_value
|
48
|
+
h = {}
|
49
|
+
self.each {|k,v| h[k] = v.to_mongo_value}
|
50
|
+
h
|
51
|
+
end
|
52
|
+
|
53
|
+
# Same symbolize_keys method used in Rails
|
54
|
+
def symbolize_keys
|
55
|
+
inject({}) do |options, (key, value)|
|
56
|
+
options[(key.to_sym rescue key) || key] = value
|
57
|
+
options
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def symbolize_keys!
|
62
|
+
self.replace(self.symbolize_keys)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Copyright 2009 10gen, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module MongoRecord
|
16
|
+
|
17
|
+
# A destination for Ruby's built-in Logger class. It writes log messages
|
18
|
+
# to a Mongo database collection. Each item in the collection consists of
|
19
|
+
# two fields (besides the _id): +time+ and +msg+. +time+ is automatically
|
20
|
+
# generated when +write+ is called.
|
21
|
+
#
|
22
|
+
# If we are running outside of the cloud, all log messages are echoed to
|
23
|
+
# $stderr.
|
24
|
+
#
|
25
|
+
# The collection is capped, which means after the limit is reached old
|
26
|
+
# records are deleted when new ones are inserted. See the new method and
|
27
|
+
# the Mongo documentation for details.
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
#
|
31
|
+
# logger = Logger.new(MongoRecord::LogDevice('my_log_name'))
|
32
|
+
#
|
33
|
+
# The database connection defaults to the global $db. You can set the
|
34
|
+
# connection using MongoRecord::LogDevice.connection= and read it with
|
35
|
+
# MongoRecord::LogDevice.connection.
|
36
|
+
#
|
37
|
+
# # Set the connection to something besides $db
|
38
|
+
# MongoRecord::LogDevice.connection = connect('my-database')
|
39
|
+
class LogDevice
|
40
|
+
|
41
|
+
DEFAULT_CAP_SIZE = (10 * 1024 * 1024)
|
42
|
+
|
43
|
+
@@connection = nil
|
44
|
+
|
45
|
+
class << self # Class methods
|
46
|
+
|
47
|
+
# Return the database connection. The default value is
|
48
|
+
# <code>$db</code>.
|
49
|
+
def connection
|
50
|
+
conn = @@connection || $db
|
51
|
+
raise "connection not defined" unless conn
|
52
|
+
conn
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set the database connection. If the connection is set to +nil+, then
|
56
|
+
# <code>$db</code> will be used.
|
57
|
+
def connection=(val)
|
58
|
+
@@connection = val
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# +name+ is the name of the Mongo database collection that will hold all
|
64
|
+
# log messages. +options+ is a hash that may have the following entries:
|
65
|
+
#
|
66
|
+
# <code>:size</code> - Optional. The max size of the collection, in
|
67
|
+
# bytes. If it is nil or negative then +DEFAULT_CAP_SIZE+ is used.
|
68
|
+
#
|
69
|
+
# <code>:max</code> - Optional. Specifies the maximum number of log
|
70
|
+
# records, after which the oldest items are deleted as new ones are
|
71
|
+
# inserted.
|
72
|
+
#
|
73
|
+
# <code>:stderr</code> - Optional. If not +nil+ then all log messages will
|
74
|
+
# be copied to $stderr.
|
75
|
+
#
|
76
|
+
# Note: a non-nil :max requires a :size value. The collection will never
|
77
|
+
# grow above :size. If you leave :size nil then it will be
|
78
|
+
# +DEFAULT_CAP_SIZE+.
|
79
|
+
#
|
80
|
+
# Note: once a capped collection has been created, you can't redefine
|
81
|
+
# the size or max falues for that collection. To do so, you must drop
|
82
|
+
# and recreate (or let a LogDevice object recreate) the collection.
|
83
|
+
def initialize(name, options = {})
|
84
|
+
@collection_name = name
|
85
|
+
options[:capped] = true
|
86
|
+
options[:size] ||= DEFAULT_CAP_SIZE
|
87
|
+
options[:size] = DEFAULT_CAP_SIZE if options[:size] <= 0
|
88
|
+
|
89
|
+
# It's OK to call createCollection if the collection already exists.
|
90
|
+
# Size and max won't change, though.
|
91
|
+
#
|
92
|
+
# Note we can't use the name "create_collection" because a DB JSObject
|
93
|
+
# does not have normal keys and returns collection objects as the
|
94
|
+
# value of all unknown names.
|
95
|
+
self.class.connection.create_collection(@collection_name, options)
|
96
|
+
|
97
|
+
@console = options[:stderr]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Write a log message to the database. We save the message and a timestamp.
|
101
|
+
def write(str)
|
102
|
+
$stderr.puts str if @console
|
103
|
+
self.class.connection.collection(@collection_name).insert({:time => Time.now, :msg => str})
|
104
|
+
end
|
105
|
+
|
106
|
+
# Close the log. This method is a sham. Nothing happens. You may
|
107
|
+
# continue to use this LogDevice.
|
108
|
+
def close
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# Copyright 2009 10gen, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module MongoRecord
|
16
|
+
|
17
|
+
module SQL
|
18
|
+
|
19
|
+
# A simple tokenizer for SQL.
|
20
|
+
class Tokenizer
|
21
|
+
|
22
|
+
attr_reader :sql
|
23
|
+
|
24
|
+
def initialize(sql)
|
25
|
+
@sql = sql
|
26
|
+
@length = sql.length
|
27
|
+
@pos = 0
|
28
|
+
@extra_tokens = []
|
29
|
+
end
|
30
|
+
|
31
|
+
# Push +tok+ onto the stack.
|
32
|
+
def add_extra_token(tok)
|
33
|
+
@extra_tokens.push(tok)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Skips whitespace, setting @pos to the position of the next
|
37
|
+
# non-whitespace character. If there is none, @pos will == @length.
|
38
|
+
def skip_whitespace
|
39
|
+
while @pos < @length && [" ", "\n", "\r", "\t"].include?(@sql[@pos,1])
|
40
|
+
@pos += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return +true+ if there are more non-whitespace characters.
|
45
|
+
def more?
|
46
|
+
skip_whitespace
|
47
|
+
@pos < @length
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return the next string without its surrounding quotes. Assumes we have
|
51
|
+
# already seen a quote character.
|
52
|
+
def next_string(c)
|
53
|
+
q = c
|
54
|
+
@pos += 1
|
55
|
+
t = ''
|
56
|
+
while @pos < @length
|
57
|
+
c = @sql[@pos, 1]
|
58
|
+
case c
|
59
|
+
when q
|
60
|
+
if @pos + 1 < @length && @sql[@pos + 1, 1] == q # double quote
|
61
|
+
t += q
|
62
|
+
@pos += 1
|
63
|
+
else
|
64
|
+
@pos += 1
|
65
|
+
return t
|
66
|
+
end
|
67
|
+
when '\\'
|
68
|
+
@pos += 1
|
69
|
+
return t if @pos >= @length
|
70
|
+
t << @sql[@pos, 1]
|
71
|
+
else
|
72
|
+
t << c
|
73
|
+
end
|
74
|
+
@pos += 1
|
75
|
+
end
|
76
|
+
raise "unterminated string in SQL: #{@sql}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return +true+ if the next character is a legal starting identifier
|
80
|
+
# character.
|
81
|
+
def identifier_char?(c)
|
82
|
+
c =~ /[\.a-zA-Z0-9_]/ ? true : false
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return +true+ if +c+ is a single or double quote character.
|
86
|
+
def quote?(c)
|
87
|
+
c == '"' || c == "'"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return the next token, or +nil+ if there are no more.
|
91
|
+
def next_token
|
92
|
+
return @extra_tokens.pop unless @extra_tokens.empty?
|
93
|
+
|
94
|
+
skip_whitespace
|
95
|
+
c = @sql[@pos, 1]
|
96
|
+
return next_string(c) if quote?(c)
|
97
|
+
|
98
|
+
first_is_identifier_char = identifier_char?(c)
|
99
|
+
t = c
|
100
|
+
@pos += 1
|
101
|
+
while @pos < @length
|
102
|
+
c = @sql[@pos, 1]
|
103
|
+
break if c == ' '
|
104
|
+
|
105
|
+
this_is_identifier_char = identifier_char?(c)
|
106
|
+
break if first_is_identifier_char != this_is_identifier_char && @length > 0
|
107
|
+
break if !this_is_identifier_char && quote?(c)
|
108
|
+
|
109
|
+
t << c
|
110
|
+
@pos += 1
|
111
|
+
end
|
112
|
+
|
113
|
+
case t
|
114
|
+
when ''
|
115
|
+
nil
|
116
|
+
when /^\d+$/
|
117
|
+
t.to_i
|
118
|
+
else
|
119
|
+
t
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
# Only parses simple WHERE clauses right now. The parser returns a query
|
126
|
+
# Hash suitable for use by Mongo.
|
127
|
+
class Parser
|
128
|
+
|
129
|
+
# Parse a WHERE clause (without the "WHERE") ane return a query Hash
|
130
|
+
# suitable for use by Mongo.
|
131
|
+
def self.parse_where(sql, remove_table_names=false)
|
132
|
+
Parser.new(Tokenizer.new(sql)).parse_where(remove_table_names)
|
133
|
+
end
|
134
|
+
|
135
|
+
def initialize(tokenizer)
|
136
|
+
@tokenizer = tokenizer
|
137
|
+
end
|
138
|
+
|
139
|
+
# Given a regexp string like '%foo%', return a Regexp object. We set
|
140
|
+
# Regexp::IGNORECASE so that all regex matches are case-insensitive.
|
141
|
+
def regexp_from_string(str)
|
142
|
+
if str[0,1] == '%'
|
143
|
+
str = str[1..-1]
|
144
|
+
else
|
145
|
+
str = '^' + str
|
146
|
+
end
|
147
|
+
|
148
|
+
if str[-1,1] == '%'
|
149
|
+
str = str[0..-2]
|
150
|
+
else
|
151
|
+
str = str + '$'
|
152
|
+
end
|
153
|
+
Regexp.new(str, Regexp::IGNORECASE)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Parse a WHERE clause (without the "WHERE") and return a query Hash
|
157
|
+
# suitable for use by Mongo.
|
158
|
+
def parse_where(remove_table_names=false)
|
159
|
+
filters = {}
|
160
|
+
done = false
|
161
|
+
while !done && @tokenizer.more?
|
162
|
+
name = @tokenizer.next_token
|
163
|
+
raise "sql parser can't handle nested stuff yet: #{@tokenizer.sql}" if name == '('
|
164
|
+
name.sub!(/.*\./, '') if remove_table_names # Remove "schema.table." from "schema.table.col"
|
165
|
+
|
166
|
+
op = @tokenizer.next_token
|
167
|
+
op += (' ' + @tokenizer.next_token) if op.downcase == 'not'
|
168
|
+
op = op.downcase
|
169
|
+
|
170
|
+
val = @tokenizer.next_token
|
171
|
+
|
172
|
+
case op
|
173
|
+
when "="
|
174
|
+
filters[name] = val
|
175
|
+
when "<"
|
176
|
+
filters[name] = { :$lt => val }
|
177
|
+
when "<="
|
178
|
+
filters[name] = { :$lte => val }
|
179
|
+
when ">"
|
180
|
+
filters[name] = { :$gt => val }
|
181
|
+
when ">="
|
182
|
+
filters[name] = { :$gte => val }
|
183
|
+
when "<>", "!="
|
184
|
+
filters[name] = { :$ne => val }
|
185
|
+
when "like"
|
186
|
+
filters[name] = regexp_from_string(val)
|
187
|
+
when "in"
|
188
|
+
raise "'in' must be followed by a list of values: #{@tokenizer.sql}" unless val == '('
|
189
|
+
filters[name] = { :$in => read_array }
|
190
|
+
when "between"
|
191
|
+
conjunction = @tokenizer.next_token.downcase
|
192
|
+
raise "syntax error: expected 'between X and Y', but saw '" + conjunction + "' instead of 'and'" unless conjunction == 'and'
|
193
|
+
val2 = @tokenizer.next_token
|
194
|
+
val2, val = val, val2 if val > val2 # Make sure val <= val2
|
195
|
+
filters[name] = { :$gte => val, :$lte => val2 }
|
196
|
+
else
|
197
|
+
raise "can't handle sql operator [#{op}] yet: #{@tokenizer.sql}"
|
198
|
+
end
|
199
|
+
|
200
|
+
break unless @tokenizer.more?
|
201
|
+
|
202
|
+
tok = @tokenizer.next_token.downcase
|
203
|
+
case tok
|
204
|
+
when 'and'
|
205
|
+
next
|
206
|
+
when 'or'
|
207
|
+
raise "sql parser can't handle ors yet: #{@tokenizer.sql}"
|
208
|
+
when 'order', 'group', 'limit'
|
209
|
+
@tokenizer.add_extra_token(tok)
|
210
|
+
done = true
|
211
|
+
else
|
212
|
+
raise "can't handle [#{tok}] yet"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
filters
|
216
|
+
end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
# Read and return an array of values from a clause like "('a', 'b',
|
221
|
+
# 'c')". We have already read the first '('.
|
222
|
+
def read_array
|
223
|
+
vals = []
|
224
|
+
while @tokenizer.more?
|
225
|
+
vals.push(@tokenizer.next_token)
|
226
|
+
sep = @tokenizer.next_token
|
227
|
+
return vals if sep == ')'
|
228
|
+
raise "missing ',' in 'in' list of values: #{@tokenizer.sql}" unless sep == ','
|
229
|
+
end
|
230
|
+
raise "missing ')' at end of 'in' list of values: #{@tokenizer.sql}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Copyright 2009 10gen, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'mongo_record/base'
|
16
|
+
|
17
|
+
module MongoRecord
|
18
|
+
|
19
|
+
# A MongoRecord::Subobject is an MongoRecord::Base subclass that disallows
|
20
|
+
# many operations. Subobjects are those that are contained within and
|
21
|
+
# saved with some other object.
|
22
|
+
#
|
23
|
+
# Using MongoRecord::Subobject is completely optional.
|
24
|
+
#
|
25
|
+
# As an example, say a Student object contains an Address. You might want
|
26
|
+
# to make Address a subclass of Subobject so that you don't accidentally
|
27
|
+
# try to save an address to a collection by itself.
|
28
|
+
class Subobject < Base
|
29
|
+
|
30
|
+
class << self # Class methods
|
31
|
+
|
32
|
+
# Subobjects ignore the collection name.
|
33
|
+
def collection_name(coll_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Disallow find.
|
37
|
+
def find(*args)
|
38
|
+
complain("found")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Disallow count.
|
42
|
+
def count(*args)
|
43
|
+
complain("counted")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Disallow delete.
|
47
|
+
def delete(id)
|
48
|
+
complain("deleted")
|
49
|
+
end
|
50
|
+
alias_method :remove, :delete
|
51
|
+
|
52
|
+
# Disallow destroy.
|
53
|
+
def destroy(id)
|
54
|
+
complain("destroyed")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Disallow destroy_all.
|
58
|
+
def destroy_all(conditions = nil)
|
59
|
+
complain("destroyed")
|
60
|
+
end
|
61
|
+
|
62
|
+
# Disallow delete_all.
|
63
|
+
def delete_all(conditions=nil)
|
64
|
+
complain("deleted")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Disallow create.
|
68
|
+
def create(values_hash)
|
69
|
+
complain("created")
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def complain(cant_do_this)
|
75
|
+
raise "Subobjects can't be #{cant_do_this} by themselves. Use a subobject query."
|
76
|
+
end
|
77
|
+
|
78
|
+
end # End of class methods
|
79
|
+
|
80
|
+
public
|
81
|
+
|
82
|
+
# Subobjects do not have their own ids.
|
83
|
+
def id=(val); raise "Subobjects don't have ids"; end
|
84
|
+
|
85
|
+
# Subobjects do not have their own ids.
|
86
|
+
# You'll get a deprecation warning if you call this outside of Rails.
|
87
|
+
def id; raise "Subobjects don't have ids"; end
|
88
|
+
|
89
|
+
# to_param normally returns the id of an object. Since subobjects don't
|
90
|
+
# have ids, this is disallowed.
|
91
|
+
def to_param; raise "Subobjects don't have ids"; end
|
92
|
+
|
93
|
+
# Disallow new_record?
|
94
|
+
def new_record?; raise "Subobjects don't have ids"; end
|
95
|
+
|
96
|
+
# Disallow udpate.
|
97
|
+
def update
|
98
|
+
self.class.complain("updated")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Disallow delete and remove.
|
102
|
+
def delete
|
103
|
+
self.class.complain("deleted")
|
104
|
+
end
|
105
|
+
alias_method :remove, :delete
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
data/lib/mongo_record.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2009 10gen, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# Include files for using Mongo, MongoRecord::Base, and a database logger.
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'mongo'
|
19
|
+
require 'mongo_record/base'
|
20
|
+
require 'mongo_record/subobject'
|
21
|
+
require 'mongo_record/log_device'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'mongo_record'
|
3
|
+
s.version = '0.4.2'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.summary = 'ActiveRecord-like models for the 10gen Mongo DB'
|
6
|
+
s.description = 'MongoRecord is an ActiveRecord-like framework for the 10gen Mongo database. For more information about Mongo, see http://www.mongodb.org.'
|
7
|
+
|
8
|
+
s.add_dependency('mongo', ['>= 0.15'])
|
9
|
+
|
10
|
+
s.require_paths = ['lib']
|
11
|
+
|
12
|
+
s.files = ['examples/tracks.rb', 'lib/mongo_record.rb',
|
13
|
+
'lib/mongo_record/base.rb',
|
14
|
+
'lib/mongo_record/convert.rb',
|
15
|
+
'lib/mongo_record/log_device.rb',
|
16
|
+
'lib/mongo_record/sql.rb',
|
17
|
+
'lib/mongo_record/subobject.rb',
|
18
|
+
'README.rdoc', 'Rakefile',
|
19
|
+
'mongo-activerecord-ruby.gemspec',
|
20
|
+
'LICENSE']
|
21
|
+
s.test_files = ['tests/address.rb',
|
22
|
+
'tests/course.rb',
|
23
|
+
'tests/student.rb',
|
24
|
+
'tests/class_in_module.rb',
|
25
|
+
'tests/test_log_device.rb',
|
26
|
+
'tests/test_mongo.rb',
|
27
|
+
'tests/test_sql.rb',
|
28
|
+
'tests/track2.rb',
|
29
|
+
'tests/track3.rb']
|
30
|
+
|
31
|
+
s.has_rdoc = true
|
32
|
+
s.rdoc_options = ['--main', 'README.rdoc', '--inline-source']
|
33
|
+
s.extra_rdoc_files = ['README.rdoc']
|
34
|
+
|
35
|
+
s.authors = ['Jim Menard', 'Mike Dirolf']
|
36
|
+
s.email = 'mongodb-user@googlegroups.com'
|
37
|
+
s.homepage = 'http://www.mongodb.org'
|
38
|
+
end
|
data/tests/address.rb
ADDED
data/tests/course.rb
ADDED
data/tests/student.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'mongo_record'
|
2
|
+
require File.join(File.dirname(__FILE__), 'address')
|
3
|
+
|
4
|
+
class Score < MongoRecord::Base
|
5
|
+
|
6
|
+
field :grade
|
7
|
+
has_one :for_course, :class_name => 'Course' # Mongo will store course db reference, not duplicate object
|
8
|
+
|
9
|
+
def passed?
|
10
|
+
@grade >= 2.0
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"#@for_course: #@grade"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class Student < MongoRecord::Base
|
20
|
+
|
21
|
+
collection_name :students
|
22
|
+
fields :name, :email, :num_array, :created_at, :created_on, :updated_on
|
23
|
+
has_one :address
|
24
|
+
has_many :scores, :class_name => "Score"
|
25
|
+
|
26
|
+
def initialize(row=nil)
|
27
|
+
super
|
28
|
+
@address ||= Address.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_score(course_id, grade)
|
32
|
+
@scores << Score.new(:for_course => Course.find(course_id), :grade => grade)
|
33
|
+
end
|
34
|
+
end
|