ninjudd-active_document 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +43 -0
- data/VERSION.yml +1 -1
- data/lib/active_document/base.rb +119 -25
- data/lib/active_document/database.rb +60 -14
- data/lib/active_document/environment.rb +10 -8
- data/lib/active_document.rb +1 -0
- data/test/active_document_test.rb +101 -11
- metadata +3 -2
data/README.rdoc
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
= ActiveDocument
|
2
|
+
|
3
|
+
ActiveDocument is a persistent Model store built on Berkeley DB. It was inspired by
|
4
|
+
ActiveRecord, and in some cases, it can be used as a drop-in replacement. The performance
|
5
|
+
of ActiveDocument can exceed a traditional ORM for many applications, because the database
|
6
|
+
is stored locally and all lookups must use a predefined index. Also, attributes do not
|
7
|
+
have to be cast after they are read from the database like in ActiveRecord. Instead, Ruby
|
8
|
+
objects are stored directly in Berkeley DB and loaded using Marshal, which makes loading
|
9
|
+
objects much faster. For more information on the diffences between Berkeley DB and a
|
10
|
+
relational Database, see (http://www.oracle.com/database/docs/Berkeley-DB-v-Relational.pdf).
|
11
|
+
|
12
|
+
== Usage:
|
13
|
+
|
14
|
+
require 'active_document'
|
15
|
+
|
16
|
+
class User < ActiveDocument::Base
|
17
|
+
path '/data/bdb'
|
18
|
+
accessor :first_name, :last_name, :username, :email_address
|
19
|
+
|
20
|
+
primary_key :username
|
21
|
+
index_by [:last_name, :first_name]
|
22
|
+
index_by :email_address, :unique => true
|
23
|
+
end
|
24
|
+
|
25
|
+
User.create(
|
26
|
+
:first_name => 'John',
|
27
|
+
:last_name => 'Stewart',
|
28
|
+
:username => 'lefty',
|
29
|
+
:email_address => 'john@thedailyshow.com'
|
30
|
+
)
|
31
|
+
|
32
|
+
User.find('lefty').attributes
|
33
|
+
=> {:first_name=>"John", :last_name=>"Stewart", :username=>"lefty", :email_address=>"john@thedailyshow.com"}
|
34
|
+
|
35
|
+
== Install:
|
36
|
+
|
37
|
+
sudo gem install ninjudd-bdb -s http://gems.github.com
|
38
|
+
sudo gem install ninjudd-tuple -s http://gems.github.com
|
39
|
+
sudo gem install ninjudd-active_document -s http://gems.github.com
|
40
|
+
|
41
|
+
== License:
|
42
|
+
|
43
|
+
Copyright (c) 2009 Justin Balthrop, Geni.com; Published under The MIT License, see LICENSE
|
data/VERSION.yml
CHANGED
data/lib/active_document/base.rb
CHANGED
@@ -3,7 +3,7 @@ class ActiveDocument::Base
|
|
3
3
|
if path
|
4
4
|
@path = path
|
5
5
|
else
|
6
|
-
@path || (base_class? ? DEFAULT_PATH : super)
|
6
|
+
@path || (base_class? ? ActiveDocument::DEFAULT_PATH : super)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -13,7 +13,7 @@ class ActiveDocument::Base
|
|
13
13
|
@database_name = database_name
|
14
14
|
else
|
15
15
|
return nil if self == ActiveDocument::Base
|
16
|
-
@database_name ||= base_class? ? name.underscore.pluralize : super
|
16
|
+
@database_name ||= base_class? ? name.underscore.gsub('/', '-').pluralize : super
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -38,58 +38,118 @@ class ActiveDocument::Base
|
|
38
38
|
environment.transaction(&block)
|
39
39
|
end
|
40
40
|
|
41
|
+
def transaction(&block)
|
42
|
+
self.class.transaction(&block)
|
43
|
+
end
|
44
|
+
|
41
45
|
def self.create(*args)
|
42
46
|
model = new(*args)
|
43
47
|
model.save
|
44
48
|
model
|
45
49
|
end
|
46
50
|
|
47
|
-
def self.
|
48
|
-
field =
|
51
|
+
def self.primary_key(field_or_fields)
|
52
|
+
field = define_field_accessor(field_or_fields)
|
53
|
+
define_find_methods(field, :field => :primary_key) # find_by_field1_and_field2
|
54
|
+
|
55
|
+
define_field_accessor(field_or_fields, :primary_key)
|
56
|
+
define_find_methods(:primary_key) # find_by_primary_key
|
57
|
+
|
58
|
+
# Define shortcuts for partial keys.
|
59
|
+
if field_or_fields.kind_of?(Array) and not respond_to?(field_or_fields.first)
|
60
|
+
define_find_methods(field_or_fields.first, :field => :primary_key, :partial => true) # find_by_field1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.index_by(field_or_fields, opts = {})
|
65
|
+
field = define_field_accessor(field_or_fields)
|
49
66
|
raise "index on #{field} already exists" if databases[field]
|
50
67
|
databases[field] = ActiveDocument::Database.new(opts.merge(:field => field, :model_class => self))
|
68
|
+
define_find_methods(field) # find_by_field1_and_field2
|
69
|
+
|
70
|
+
# Define shortcuts for partial keys.
|
71
|
+
if field_or_fields.kind_of?(Array) and not respond_to?(field_or_fields.first)
|
72
|
+
define_find_methods(field_or_fields.first, :field => field, :partial => true) # find_by_field1
|
73
|
+
end
|
51
74
|
end
|
52
|
-
|
75
|
+
|
53
76
|
def self.databases
|
54
|
-
@databases ||= { :
|
77
|
+
@databases ||= { :primary_key => ActiveDocument::Database.new(:model_class => self, :unique => true) }
|
55
78
|
end
|
56
79
|
|
57
80
|
def self.open_database
|
58
|
-
|
59
|
-
|
60
|
-
|
81
|
+
unless @database_open
|
82
|
+
environment.open
|
83
|
+
databases[:primary_key].open # Must be opened first for associate to work.
|
84
|
+
databases.values.each {|database| database.open}
|
85
|
+
@database_open = true
|
86
|
+
at_exit { close_database }
|
87
|
+
end
|
61
88
|
end
|
62
89
|
|
63
90
|
def self.close_database
|
64
|
-
|
65
|
-
|
91
|
+
if @database_open
|
92
|
+
databases.values.each {|database| database.close}
|
93
|
+
environment.close
|
94
|
+
@database_open = false
|
95
|
+
end
|
66
96
|
end
|
67
97
|
|
68
|
-
def self.database(field =
|
98
|
+
def self.database(field = nil)
|
99
|
+
open_database # Make sure the database is open.
|
100
|
+
field ||= :primary_key
|
69
101
|
field = field.to_sym
|
70
102
|
database = databases[field]
|
71
103
|
database ||= base_class.database(field) unless base_class?
|
72
104
|
database
|
73
105
|
end
|
74
106
|
|
107
|
+
def database(field = nil)
|
108
|
+
self.class.database(field)
|
109
|
+
end
|
110
|
+
|
75
111
|
def self.find_by(field, *keys)
|
76
|
-
opts = keys
|
112
|
+
opts = extract_opts(keys)
|
113
|
+
keys << :all if keys.empty?
|
77
114
|
database(field).find(keys, opts)
|
78
115
|
end
|
79
116
|
|
80
117
|
def self.find(key, opts = {})
|
81
118
|
doc = database.find([key], opts).first
|
82
|
-
raise ActiveDocument::DocumentNotFound, "Couldn't find #{name} with
|
119
|
+
raise ActiveDocument::DocumentNotFound, "Couldn't find #{name} with id #{key.inspect}" unless doc
|
83
120
|
doc
|
84
121
|
end
|
122
|
+
|
123
|
+
def self.define_field_accessor(field_or_fields, field = nil)
|
124
|
+
if field_or_fields.kind_of?(Array)
|
125
|
+
field ||= field_or_fields.join('_and_').to_sym
|
126
|
+
define_method(field) do
|
127
|
+
field_or_fields.collect {|f| self.send(f)}.flatten
|
128
|
+
end
|
129
|
+
elsif field
|
130
|
+
define_method(field) do
|
131
|
+
self.send(field_or_fields)
|
132
|
+
end
|
133
|
+
else
|
134
|
+
field = field_or_fields.to_sym
|
135
|
+
end
|
136
|
+
field
|
137
|
+
end
|
85
138
|
|
86
|
-
def self.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
139
|
+
def self.define_find_methods(name, opts = {})
|
140
|
+
field = opts[:field] || name
|
141
|
+
|
142
|
+
(class << self; self; end).instance_eval do
|
143
|
+
define_method("find_by_#{name}") do |*args|
|
144
|
+
merge_opts(args, :limit => 1, :partial => opts[:partial])
|
145
|
+
find_by(field, *args).first
|
146
|
+
end
|
147
|
+
|
148
|
+
define_method("find_all_by_#{name}") do |*args|
|
149
|
+
merge_opts(args, :partial => opts[:partial])
|
150
|
+
find_by(field, *args)
|
151
|
+
end
|
91
152
|
end
|
92
|
-
raise NoMethodError, "undefined method `#{method_name}' for #{self}"
|
93
153
|
end
|
94
154
|
|
95
155
|
def self.timestamps
|
@@ -107,7 +167,6 @@ class ActiveDocument::Base
|
|
107
167
|
def self.writer(*attrs)
|
108
168
|
attrs.each do |attr|
|
109
169
|
define_method("#{attr}=") do |value|
|
110
|
-
raise 'cannot modify readonly document' if readonly?
|
111
170
|
attributes[attr] = value
|
112
171
|
end
|
113
172
|
end
|
@@ -117,7 +176,15 @@ class ActiveDocument::Base
|
|
117
176
|
reader(*attrs)
|
118
177
|
writer(*attrs)
|
119
178
|
end
|
120
|
-
|
179
|
+
|
180
|
+
def self.save_method(method_name)
|
181
|
+
define_method("#{method_name}!") do |*args|
|
182
|
+
value = send(method_name, *args)
|
183
|
+
save
|
184
|
+
value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
121
188
|
def initialize(attributes = {})
|
122
189
|
if attributes.kind_of?(String)
|
123
190
|
@attributes, @saved_attributes = Marshal.load(attributes)
|
@@ -145,20 +212,37 @@ class ActiveDocument::Base
|
|
145
212
|
return false unless @attributes and @saved_attributes
|
146
213
|
|
147
214
|
if field
|
148
|
-
|
215
|
+
send(field) != saved.send(field)
|
149
216
|
else
|
150
217
|
attributes != saved_attributes
|
151
218
|
end
|
152
219
|
end
|
153
220
|
|
221
|
+
def saved
|
222
|
+
raise 'no saved attributes for new record' if new_record?
|
223
|
+
@saved ||= self.class.new(saved_attributes)
|
224
|
+
end
|
225
|
+
|
154
226
|
def save
|
155
227
|
attributes[:updated_at] = Time.now if respond_to?(:updated_at)
|
156
228
|
attributes[:created_at] = Time.now if respond_to?(:created_at) and new_record?
|
229
|
+
|
230
|
+
opts = {}
|
231
|
+
if changed?(:primary_key)
|
232
|
+
opts[:create] = true
|
233
|
+
saved.destroy
|
234
|
+
else
|
235
|
+
opts[:create] = new_record?
|
236
|
+
end
|
237
|
+
|
157
238
|
@saved_attributes = attributes
|
158
239
|
@attributes = nil
|
159
|
-
|
240
|
+
@saved = nil
|
241
|
+
database.save(self, opts)
|
242
|
+
end
|
160
243
|
|
161
|
-
|
244
|
+
def destroy
|
245
|
+
database.delete(self)
|
162
246
|
end
|
163
247
|
|
164
248
|
def _dump(ignored)
|
@@ -168,4 +252,14 @@ class ActiveDocument::Base
|
|
168
252
|
def self._load(data)
|
169
253
|
new(data)
|
170
254
|
end
|
255
|
+
|
256
|
+
private
|
257
|
+
|
258
|
+
def self.extract_opts(args)
|
259
|
+
args.last.kind_of?(Hash) ? args.pop : {}
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.merge_opts(args, opts)
|
263
|
+
args << extract_opts(args).merge(opts)
|
264
|
+
end
|
171
265
|
end
|
@@ -4,7 +4,6 @@ class ActiveDocument::Database
|
|
4
4
|
@field = opts[:field]
|
5
5
|
@unique = opts[:unique]
|
6
6
|
@suffix = opts[:suffix] || (@field ? "by_#{@field}" : nil)
|
7
|
-
at_exit { close }
|
8
7
|
end
|
9
8
|
|
10
9
|
attr_accessor :model_class, :field, :db, :suffix
|
@@ -31,34 +30,73 @@ class ActiveDocument::Database
|
|
31
30
|
|
32
31
|
def find(keys, opts = {}, &block)
|
33
32
|
models = block_given? ? BlockArray.new(block) : []
|
33
|
+
flags = opts[:modify] ? Bdb::DB_RMW : 0
|
34
34
|
|
35
35
|
keys.uniq.each do |key|
|
36
|
-
if key.kind_of?(Range)
|
36
|
+
if opts[:partial] and not key.kind_of?(Range)
|
37
|
+
first = [*key]
|
38
|
+
last = first + [true]
|
39
|
+
key = first..last
|
40
|
+
end
|
41
|
+
|
42
|
+
if key == :all
|
43
|
+
cursor = db.cursor(transaction, 0)
|
44
|
+
if opts[:reverse]
|
45
|
+
k,v = cursor.get(nil, nil, Bdb::DB_LAST | flags) # Start at the last item.
|
46
|
+
iter = lambda {cursor.get(nil, nil, Bdb::DB_PREV | flags)} # Move backward.
|
47
|
+
else
|
48
|
+
k,v = cursor.get(nil, nil, Bdb::DB_FIRST | flags) # Start at the first item.
|
49
|
+
iter = lambda {cursor.get(nil, nil, Bdb::DB_NEXT | flags)} # Move forward.
|
50
|
+
end
|
51
|
+
|
52
|
+
while k
|
53
|
+
models << Marshal.load(v)
|
54
|
+
break if opts[:limit] and models.size == opts[:limit]
|
55
|
+
k,v = iter.call
|
56
|
+
end
|
57
|
+
cursor.close
|
58
|
+
elsif key.kind_of?(Range)
|
37
59
|
# Fetch a range of keys.
|
38
60
|
cursor = db.cursor(transaction, 0)
|
39
61
|
first = Tuple.dump(key.first)
|
40
|
-
last = Tuple.dump(key.last)
|
41
|
-
|
42
|
-
|
62
|
+
last = Tuple.dump(key.last)
|
63
|
+
|
64
|
+
# Return false once we pass the end of the range.
|
65
|
+
cond = key.exclude_end? ? lambda {|k| k < last} : lambda {|k| k <= last}
|
66
|
+
|
67
|
+
if opts[:reverse]
|
68
|
+
# Position the cursor at the end of the range.
|
69
|
+
k,v = cursor.get(last, nil, Bdb::DB_SET_RANGE | flags)
|
70
|
+
while k and not cond.call(k)
|
71
|
+
k,v = iter.call
|
72
|
+
end
|
73
|
+
|
74
|
+
iter = lambda {cursor.get(nil, nil, Bdb::DB_PREV | flags)} # Move backward.
|
75
|
+
cond = lambda {|k| k >= first} # Change the condition to stop when we move past the start.
|
76
|
+
else
|
77
|
+
k,v = cursor.get(first, nil, Bdb::DB_SET_RANGE | flags) # Start at the beginning of the range.
|
78
|
+
iter = lambda {cursor.get(nil, nil, Bdb::DB_NEXT | flags)} # Move forward.
|
79
|
+
end
|
80
|
+
|
81
|
+
while k and cond.call(k)
|
43
82
|
models << Marshal.load(v)
|
44
83
|
break if opts[:limit] and models.size == opts[:limit]
|
45
|
-
k,
|
46
|
-
break unless k
|
84
|
+
k,v = iter.call
|
47
85
|
end
|
48
86
|
cursor.close
|
49
87
|
else
|
50
88
|
if unique?
|
51
89
|
# There can only be one item for each key.
|
52
|
-
data = db.get(transaction, Tuple.dump(key), nil,
|
90
|
+
data = db.get(transaction, Tuple.dump(key), nil, flags)
|
53
91
|
models << Marshal.load(data) if data
|
54
92
|
else
|
55
93
|
# Have to use a cursor because there may be multiple items with each key.
|
56
94
|
cursor = db.cursor(transaction, 0)
|
57
|
-
k,v = cursor.get(Tuple.dump(key), nil, Bdb::DB_SET)
|
95
|
+
k,v = cursor.get(Tuple.dump(key), nil, Bdb::DB_SET | flags)
|
58
96
|
while k
|
59
97
|
models << Marshal.load(v)
|
60
98
|
break if opts[:limit] and models.size == opts[:limit]
|
61
|
-
k,v = cursor.get(nil, nil, Bdb::DB_NEXT_DUP)
|
99
|
+
k,v = cursor.get(nil, nil, Bdb::DB_NEXT_DUP | flags)
|
62
100
|
end
|
63
101
|
cursor.close
|
64
102
|
end
|
@@ -69,10 +107,18 @@ class ActiveDocument::Database
|
|
69
107
|
block_given? ? nil : models
|
70
108
|
end
|
71
109
|
|
72
|
-
def save(model)
|
73
|
-
|
74
|
-
data
|
75
|
-
|
110
|
+
def save(model, opts = {})
|
111
|
+
key = Tuple.dump(model.primary_key)
|
112
|
+
data = Marshal.dump(model)
|
113
|
+
flags = opts[:create] ? Bdb::DB_NOOVERWRITE : 0
|
114
|
+
db.put(transaction, key, data, flags)
|
115
|
+
rescue Bdb::DbError => e
|
116
|
+
raise ActiveDocument::DuplicatePrimaryKey, "primary key #{model.primary_key.inspect} already exists"
|
117
|
+
end
|
118
|
+
|
119
|
+
def delete(model)
|
120
|
+
key = Tuple.dump(model.primary_key)
|
121
|
+
db.del(transaction, key, 0)
|
76
122
|
end
|
77
123
|
|
78
124
|
def open
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class ActiveDocument::Environment
|
2
2
|
def initialize(path)
|
3
3
|
@path = path
|
4
|
-
at_exit { close }
|
5
4
|
end
|
6
5
|
|
7
6
|
attr_reader :path, :env
|
@@ -13,12 +12,14 @@ class ActiveDocument::Environment
|
|
13
12
|
def open
|
14
13
|
if @env.nil?
|
15
14
|
@env = Bdb::Env.new(0)
|
16
|
-
env_flags = Bdb::DB_CREATE
|
17
|
-
Bdb::DB_INIT_TXN
|
18
|
-
Bdb::DB_INIT_LOCK
|
19
|
-
Bdb::DB_INIT_LOG
|
20
|
-
Bdb::DB_INIT_MPOOL
|
21
|
-
@env.
|
15
|
+
env_flags = Bdb::DB_CREATE | # Create the environment if it does not already exist.
|
16
|
+
Bdb::DB_INIT_TXN | # Initialize transactions
|
17
|
+
Bdb::DB_INIT_LOCK | # Initialize locking.
|
18
|
+
Bdb::DB_INIT_LOG | # Initialize logging
|
19
|
+
Bdb::DB_INIT_MPOOL # Initialize the in-memory cache.
|
20
|
+
@env.cachesize = 10 * 1024 * 1024
|
21
|
+
@env.flags_on = Bdb::DB_TXN_WRITE_NOSYNC
|
22
|
+
@env.open(path, env_flags, 0)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -34,7 +35,7 @@ class ActiveDocument::Environment
|
|
34
35
|
parent = @transaction
|
35
36
|
@transaction = env.txn_begin(nil, 0)
|
36
37
|
begin
|
37
|
-
yield
|
38
|
+
value = yield
|
38
39
|
@transaction.commit(0)
|
39
40
|
rescue Exception => e
|
40
41
|
@transaction.abort
|
@@ -42,6 +43,7 @@ class ActiveDocument::Environment
|
|
42
43
|
ensure
|
43
44
|
@transaction = parent
|
44
45
|
end
|
46
|
+
value
|
45
47
|
else
|
46
48
|
@transaction
|
47
49
|
end
|
data/lib/active_document.rb
CHANGED
@@ -6,20 +6,29 @@ class Foo < ActiveDocument::Base
|
|
6
6
|
path BDB_PATH
|
7
7
|
accessor :foo, :bar, :id
|
8
8
|
|
9
|
+
primary_key :id
|
9
10
|
index_by :foo
|
10
11
|
index_by :bar, :unique => true
|
11
12
|
end
|
12
13
|
|
14
|
+
class Bar < ActiveDocument::Base
|
15
|
+
path BDB_PATH
|
16
|
+
accessor :foo, :bar
|
17
|
+
|
18
|
+
primary_key [:foo, :bar]
|
19
|
+
index_by :bar
|
20
|
+
end
|
21
|
+
|
13
22
|
class ActiveDocumentTest < Test::Unit::TestCase
|
14
|
-
context 'with db open' do
|
23
|
+
context 'with foo db open' do
|
15
24
|
setup do
|
16
25
|
FileUtils.mkdir BDB_PATH
|
17
26
|
Foo.open_database
|
18
27
|
end
|
19
|
-
|
28
|
+
|
20
29
|
teardown do
|
21
30
|
Foo.close_database
|
22
|
-
FileUtils.rmtree BDB_PATH
|
31
|
+
FileUtils.rmtree BDB_PATH
|
23
32
|
end
|
24
33
|
|
25
34
|
should 'find in database after save' do
|
@@ -35,11 +44,46 @@ class ActiveDocumentTest < Test::Unit::TestCase
|
|
35
44
|
end
|
36
45
|
end
|
37
46
|
|
38
|
-
should '
|
47
|
+
should 'find_by_primary_key' do
|
39
48
|
f = Foo.new(:foo => 'BAR', :id => 1)
|
40
49
|
f.save
|
41
50
|
|
42
|
-
assert_equal f, Foo.
|
51
|
+
assert_equal f, Foo.find_by_primary_key(1)
|
52
|
+
assert_equal f, Foo.find_by_id(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'destroy' do
|
56
|
+
f = Foo.new(:foo => 'BAR', :id => 1)
|
57
|
+
f.save
|
58
|
+
|
59
|
+
assert_equal f, Foo.find_by_id(1)
|
60
|
+
|
61
|
+
f.destroy
|
62
|
+
|
63
|
+
assert_equal nil, Foo.find_by_id(1)
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'change primary key' do
|
67
|
+
f = Foo.new(:foo => 'BAR', :id => 1)
|
68
|
+
f.save
|
69
|
+
|
70
|
+
assert_equal f, Foo.find_by_id(1)
|
71
|
+
|
72
|
+
f.id = 2
|
73
|
+
f.save
|
74
|
+
|
75
|
+
assert_equal nil, Foo.find_by_id(1)
|
76
|
+
assert_equal 2, Foo.find_by_id(2).id
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'not overwrite existing model' do
|
80
|
+
b1 = Bar.new(:foo => 'foo', :bar => 'bar')
|
81
|
+
b1.save
|
82
|
+
|
83
|
+
assert_raises(ActiveDocument::DuplicatePrimaryKey) do
|
84
|
+
b2 = Bar.new(:foo => 'foo', :bar => 'bar')
|
85
|
+
b2.save
|
86
|
+
end
|
43
87
|
end
|
44
88
|
|
45
89
|
should 'find by secondary indexes' do
|
@@ -49,9 +93,9 @@ class ActiveDocumentTest < Test::Unit::TestCase
|
|
49
93
|
f2 = Foo.new(:foo => 'BAR', :bar => 'FU', :id => 2)
|
50
94
|
f2.save
|
51
95
|
|
52
|
-
assert_equal f1,
|
53
|
-
assert_equal f2,
|
54
|
-
assert_equal [f1,f2], Foo.
|
96
|
+
assert_equal f1, Foo.find_by_bar('FOO')
|
97
|
+
assert_equal f2, Foo.find_by_bar('FU')
|
98
|
+
assert_equal [f1,f2], Foo.find_all_by_foo('BAR')
|
55
99
|
end
|
56
100
|
|
57
101
|
should 'find by range' do
|
@@ -59,11 +103,57 @@ class ActiveDocumentTest < Test::Unit::TestCase
|
|
59
103
|
Foo.new(:id => i, :foo => "foo-#{i}").save
|
60
104
|
end
|
61
105
|
|
62
|
-
assert_equal (5..17).to_a, Foo.
|
63
|
-
assert_equal (5..14).to_a, Foo.
|
106
|
+
assert_equal (5..17).to_a, Foo.find_all_by_id(5..17).collect {|f| f.id}
|
107
|
+
assert_equal (5..14).to_a, Foo.find_all_by_id(5..17, :limit => 10).collect {|f| f.id}
|
64
108
|
|
65
109
|
# Mixed keys and ranges.
|
66
|
-
assert_equal (1..4).to_a + (16..20).to_a, Foo.
|
110
|
+
assert_equal (1..4).to_a + (16..20).to_a, Foo.find_all_by_id(1..3, 4, 16..20).collect {|f| f.id}
|
111
|
+
end
|
112
|
+
|
113
|
+
should 'find all' do
|
114
|
+
(1..20).each do |i|
|
115
|
+
Foo.new(:id => i, :foo => "foo-#{i}").save
|
116
|
+
end
|
117
|
+
|
118
|
+
assert_equal (1..20).to_a, Foo.find_all_by_id.collect {|f| f.id}
|
119
|
+
assert_equal 1, Foo.find_by_id.id # First
|
120
|
+
end
|
121
|
+
|
122
|
+
should 'find with reverse' do
|
123
|
+
(1..20).each do |i|
|
124
|
+
Foo.new(:id => i, :foo => "foo-#{i}").save
|
125
|
+
end
|
126
|
+
|
127
|
+
assert_equal (1..20).to_a.reverse, Foo.find_all_by_id(:reverse => true).collect {|f| f.id}
|
128
|
+
assert_equal (5..17).to_a.reverse, Foo.find_all_by_id(5..17, :reverse => true).collect {|f| f.id}
|
129
|
+
assert_equal 20, Foo.find_by_id(:reverse => true).id # Last
|
67
130
|
end
|
68
131
|
end
|
132
|
+
|
133
|
+
context 'with bar db open' do
|
134
|
+
setup do
|
135
|
+
FileUtils.mkdir BDB_PATH
|
136
|
+
Bar.open_database
|
137
|
+
end
|
138
|
+
|
139
|
+
teardown do
|
140
|
+
Bar.close_database
|
141
|
+
FileUtils.rmtree BDB_PATH
|
142
|
+
end
|
143
|
+
|
144
|
+
should 'find_by_primary_key and find by id fields' do
|
145
|
+
100.times do |i|
|
146
|
+
100.times do |j|
|
147
|
+
b = Bar.new(:foo => i, :bar => j)
|
148
|
+
b.save
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
assert_equal [5, 5], Bar.find_by_primary_key([5, 5]).primary_key
|
153
|
+
assert_equal [52, 52], Bar.find_by_foo_and_bar([52, 52]).foo_and_bar
|
154
|
+
assert_equal (0..99).collect {|i| [42, i]}, Bar.find_all_by_foo(42).collect {|b| b.primary_key}
|
155
|
+
assert_equal (0..99).collect {|i| [i, 52]}, Bar.find_all_by_bar(52).collect {|b| b.primary_key}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
69
159
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ninjudd-active_document
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Balthrop
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-08-
|
12
|
+
date: 2009-08-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -22,6 +22,7 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
+
- README.rdoc
|
25
26
|
- VERSION.yml
|
26
27
|
- lib/active_document
|
27
28
|
- lib/active_document/base.rb
|