ninjudd-active_document 0.0.2 → 0.0.3
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/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
|