og 0.16.0 → 0.17.0
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/CHANGELOG +485 -0
- data/README +35 -12
- data/Rakefile +4 -7
- data/benchmark/bench.rb +1 -1
- data/doc/AUTHORS +3 -3
- data/doc/RELEASES +153 -2
- data/doc/config.txt +0 -7
- data/doc/tutorial.txt +7 -0
- data/examples/README +5 -0
- data/examples/mysql_to_psql.rb +25 -50
- data/examples/run.rb +62 -77
- data/install.rb +1 -1
- data/lib/og.rb +45 -106
- data/lib/og/collection.rb +156 -0
- data/lib/og/entity.rb +131 -0
- data/lib/og/errors.rb +10 -15
- data/lib/og/manager.rb +115 -0
- data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
- data/lib/og/{mixins → mixin}/orderable.rb +35 -35
- data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
- data/lib/og/{mixins → mixin}/tree.rb +0 -4
- data/lib/og/relation.rb +178 -0
- data/lib/og/relation/belongs_to.rb +14 -0
- data/lib/og/relation/has_many.rb +62 -0
- data/lib/og/relation/has_one.rb +17 -0
- data/lib/og/relation/joins_many.rb +69 -0
- data/lib/og/relation/many_to_many.rb +17 -0
- data/lib/og/relation/refers_to.rb +31 -0
- data/lib/og/store.rb +223 -0
- data/lib/og/store/filesys.rb +113 -0
- data/lib/og/store/madeleine.rb +4 -0
- data/lib/og/store/memory.rb +291 -0
- data/lib/og/store/mysql.rb +283 -0
- data/lib/og/store/psql.rb +238 -0
- data/lib/og/store/sql.rb +599 -0
- data/lib/og/store/sqlite.rb +190 -0
- data/lib/og/store/sqlserver.rb +262 -0
- data/lib/og/types.rb +19 -0
- data/lib/og/validation.rb +0 -4
- data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
- data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
- data/test/og/mixin/tc_timestamped.rb +38 -0
- data/test/og/store/tc_filesys.rb +71 -0
- data/test/og/tc_relation.rb +36 -0
- data/test/og/tc_store.rb +290 -0
- data/test/og/tc_types.rb +21 -0
- metadata +54 -40
- data/examples/mock_example.rb +0 -50
- data/lib/og/adapters/base.rb +0 -706
- data/lib/og/adapters/filesys.rb +0 -117
- data/lib/og/adapters/mysql.rb +0 -350
- data/lib/og/adapters/oracle.rb +0 -368
- data/lib/og/adapters/psql.rb +0 -272
- data/lib/og/adapters/sqlite.rb +0 -265
- data/lib/og/adapters/sqlserver.rb +0 -356
- data/lib/og/database.rb +0 -290
- data/lib/og/enchant.rb +0 -149
- data/lib/og/meta.rb +0 -407
- data/lib/og/testing/mock.rb +0 -165
- data/lib/og/typemacros.rb +0 -24
- data/test/og/adapters/tc_filesys.rb +0 -83
- data/test/og/adapters/tc_sqlite.rb +0 -86
- data/test/og/adapters/tc_sqlserver.rb +0 -96
- data/test/og/tc_automanage.rb +0 -46
- data/test/og/tc_lifecycle.rb +0 -105
- data/test/og/tc_many_to_many.rb +0 -61
- data/test/og/tc_meta.rb +0 -55
- data/test/og/tc_validation.rb +0 -89
- data/test/tc_og.rb +0 -364
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'og/relation/joins_many'
|
2
|
+
|
3
|
+
module Og
|
4
|
+
|
5
|
+
# A 'many_to_many' relation.
|
6
|
+
# This objects is associated with an other using an intermediate
|
7
|
+
# join table. Just an alias for 'joins_many'.
|
8
|
+
#
|
9
|
+
# === Examples
|
10
|
+
#
|
11
|
+
# many_to_many Category
|
12
|
+
# many_to_many :categories, Category
|
13
|
+
|
14
|
+
class ManyToMany < JoinsMany
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'og/relation'
|
2
|
+
|
3
|
+
module Og
|
4
|
+
|
5
|
+
class RefersTo < Relation
|
6
|
+
|
7
|
+
def enchant
|
8
|
+
self[:foreign_key] = "#{foreign_name || target_singular_name}_#{target_pk}"
|
9
|
+
|
10
|
+
owner_class.module_eval %{
|
11
|
+
attr_accessor :#{target_singular_name}
|
12
|
+
prop_accessor :#{foreign_key}, #{target_pkclass}
|
13
|
+
|
14
|
+
def #{target_singular_name}(reload = false)
|
15
|
+
unless reload
|
16
|
+
@#{target_singular_name} = #{target_class}[@#{foreign_key}] unless @#{target_singular_name}
|
17
|
+
return @#{target_singular_name}
|
18
|
+
else
|
19
|
+
return #{target_class}[@#{foreign_key}]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def #{target_singular_name}=(obj)
|
24
|
+
@#{foreign_key} = obj.#{target_pk}
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/og/store.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
module Og
|
2
|
+
|
3
|
+
# A Store is responsible for the peristance of the ObjectGraph.
|
4
|
+
|
5
|
+
class Store
|
6
|
+
|
7
|
+
# Options.
|
8
|
+
|
9
|
+
attr_accessor :options
|
10
|
+
|
11
|
+
# Transaction nesting.
|
12
|
+
|
13
|
+
attr_accessor :transaction_nesting
|
14
|
+
|
15
|
+
# :section: Store methods.
|
16
|
+
|
17
|
+
# Return the store for the given name.
|
18
|
+
|
19
|
+
def self.for_name(name)
|
20
|
+
# gmosx: to keep RDoc happy.
|
21
|
+
eval %{
|
22
|
+
require 'og/store/#{name}'
|
23
|
+
return #{name.to_s.capitalize}Store
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Creates a store.
|
28
|
+
|
29
|
+
def self.create(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Destroys a store.
|
33
|
+
|
34
|
+
def self.destroy(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# :section: Misc methods.
|
38
|
+
|
39
|
+
# Create a session to the store.
|
40
|
+
|
41
|
+
def initialize(options)
|
42
|
+
@options = options
|
43
|
+
@transaction_nesting = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
# Close the session to the store.
|
47
|
+
|
48
|
+
def close
|
49
|
+
end
|
50
|
+
|
51
|
+
# Enchants a class.
|
52
|
+
|
53
|
+
def enchant(klass, manager)
|
54
|
+
klass.class.send(:define_method, :index) do |arg|
|
55
|
+
meta :index, arg
|
56
|
+
end
|
57
|
+
|
58
|
+
pk = klass.primary_key.first
|
59
|
+
|
60
|
+
klass.module_eval %{
|
61
|
+
def saved?
|
62
|
+
return @#{klass.primary_key.first}
|
63
|
+
end
|
64
|
+
|
65
|
+
def unsaved?
|
66
|
+
return !@#{klass.primary_key.first}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Evaluate an alias for the primary key.
|
70
|
+
|
71
|
+
alias_method :pk, :#{pk}
|
72
|
+
alias_method :pk=, :#{pk}=
|
73
|
+
|
74
|
+
def self.pk_symbol
|
75
|
+
:#{klass.primary_key.first}
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
# Generate finder methods.
|
80
|
+
|
81
|
+
code = ''
|
82
|
+
|
83
|
+
for p in klass.properties
|
84
|
+
finder = p.meta[:unique] ? 'find_one' : 'find'
|
85
|
+
|
86
|
+
code << %{
|
87
|
+
def self.find_by_#{p.symbol}(val, operator = '=', options = {})
|
88
|
+
options.update(
|
89
|
+
:class => #{klass},
|
90
|
+
:condition => "#{p.symbol}\#{operator}\#{ogmanager.store.quote(val)}"
|
91
|
+
)
|
92
|
+
ogmanager.store.#{finder}(options)
|
93
|
+
end;
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
klass.module_eval(code)
|
98
|
+
end
|
99
|
+
|
100
|
+
# :section: Lifecycle methods.
|
101
|
+
|
102
|
+
# Loads an object from the store using the primary key.
|
103
|
+
|
104
|
+
def load(pk, klass)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Reloads an object from the store.
|
108
|
+
|
109
|
+
def reload(obj)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Save an object to store. Insert if this is a new object or
|
113
|
+
# update if this is already inserted in the database.
|
114
|
+
|
115
|
+
def save(obj)
|
116
|
+
if obj.saved?
|
117
|
+
obj.og_update(self)
|
118
|
+
else
|
119
|
+
obj.og_insert(self)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
alias_method :<<, :save
|
123
|
+
|
124
|
+
# Insert an object in the store.
|
125
|
+
|
126
|
+
def insert(obj)
|
127
|
+
obj.og_insert(self)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Update an object in the store.
|
131
|
+
|
132
|
+
def update(obj, properties = nil)
|
133
|
+
obj.og_update(self)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Update selected properties of an object or class of
|
137
|
+
# objects.
|
138
|
+
|
139
|
+
def update_properties(obj_or_class, props, options = nil)
|
140
|
+
end
|
141
|
+
alias_method :pupdate, :update_properties
|
142
|
+
alias_method :update_property, :update_properties
|
143
|
+
|
144
|
+
# Permanently delete an object from the store.
|
145
|
+
|
146
|
+
def delete(obj_or_pk, klass = nil, cascade = true)
|
147
|
+
unless obj_or_pk.is_a?(EntityMixin)
|
148
|
+
# create a dummy instance to keep the og_delete
|
149
|
+
# method as an instance method like the other lifecycle
|
150
|
+
# methods.
|
151
|
+
klass.allocate.og_delete(self, obj_or_pk, cascade)
|
152
|
+
else
|
153
|
+
obj_or_pk.og_delete(self, nil, cascade)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Perform a query.
|
158
|
+
|
159
|
+
def find(klass, options)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Count the results returned by the query.
|
163
|
+
|
164
|
+
def count(options)
|
165
|
+
end
|
166
|
+
|
167
|
+
# :section: Transaction methods.
|
168
|
+
|
169
|
+
# Start a new transaction.
|
170
|
+
|
171
|
+
def start
|
172
|
+
raise 'Not implemented'
|
173
|
+
true if @transaction_nesting < 1
|
174
|
+
@transaction_nesting += 1
|
175
|
+
end
|
176
|
+
|
177
|
+
# Commit a transaction.
|
178
|
+
|
179
|
+
def commit
|
180
|
+
raise 'Not implemented'
|
181
|
+
@transaction_nesting -= 1
|
182
|
+
true if @transaction_nesting < 1
|
183
|
+
end
|
184
|
+
|
185
|
+
# Rollback a transaction.
|
186
|
+
|
187
|
+
def rollback
|
188
|
+
@transaction_nesting -= 1
|
189
|
+
true if @transaction_nesting < 1
|
190
|
+
end
|
191
|
+
|
192
|
+
# Transaction helper. In the transaction block use
|
193
|
+
# the db pointer to the backend.
|
194
|
+
|
195
|
+
def transaction(&block)
|
196
|
+
begin
|
197
|
+
start
|
198
|
+
yield(self)
|
199
|
+
commit
|
200
|
+
rescue => ex
|
201
|
+
Logger.error 'Error in transaction'
|
202
|
+
Logger.error ex
|
203
|
+
Logger.error ex.backtrace
|
204
|
+
rollback
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def eval_og_insert(klass)
|
211
|
+
end
|
212
|
+
|
213
|
+
def eval_og_update(klass)
|
214
|
+
end
|
215
|
+
|
216
|
+
def eval_og_read(klass)
|
217
|
+
end
|
218
|
+
|
219
|
+
def eval_og_delete(klass)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'og/store'
|
5
|
+
|
6
|
+
module Og
|
7
|
+
|
8
|
+
# A Store that saves the object on the Filesystem.
|
9
|
+
# You can create multiple instances that point to the same store.
|
10
|
+
|
11
|
+
class FilesysStore < Store
|
12
|
+
|
13
|
+
# :section: Store methods.
|
14
|
+
|
15
|
+
def self.dir(name)
|
16
|
+
"#{name}_db"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create(options)
|
20
|
+
name = dir(options[:name])
|
21
|
+
FileUtils.mkdir_p(name)
|
22
|
+
rescue
|
23
|
+
Logger.error "Cannot create '#{name}'"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.destroy(options)
|
27
|
+
name = dir(options[:name])
|
28
|
+
FileUtils.rm_rf(name)
|
29
|
+
rescue
|
30
|
+
Logger.error "Cannot destroy '#{name}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
# :section: Misc methods.
|
34
|
+
|
35
|
+
# Initialize a connection to this store.
|
36
|
+
|
37
|
+
def initialize(options)
|
38
|
+
super
|
39
|
+
|
40
|
+
@base_dir = self.class.dir(options[:name])
|
41
|
+
|
42
|
+
unless File.directory?(@base_dir)
|
43
|
+
Logger.info "Database '#{options[:name]}' not found!"
|
44
|
+
self.class.create(options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def enchant(klass, manager)
|
49
|
+
super
|
50
|
+
|
51
|
+
klass.const_set 'OGNAME', klass.to_s.gsub(/::/, "/").downcase
|
52
|
+
klass.property :oid, Fixnum
|
53
|
+
|
54
|
+
FileUtils.mkdir_p(path(klass))
|
55
|
+
File.open(spath(klass), 'w') { |f| f << '1' }
|
56
|
+
rescue => ex
|
57
|
+
p ex
|
58
|
+
Logger.error "Cannot create directory to store '#{klass}' classes!"
|
59
|
+
end
|
60
|
+
|
61
|
+
# :section: Lifecycle methods.
|
62
|
+
|
63
|
+
def load(oid, klass)
|
64
|
+
obj = nil
|
65
|
+
File.open(path(klass, oid), 'r') { |f| obj = YAML::load(f.read) }
|
66
|
+
return obj
|
67
|
+
rescue
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def reload(obj)
|
72
|
+
end
|
73
|
+
|
74
|
+
def save(obj)
|
75
|
+
seq = nil
|
76
|
+
File.open(spath(obj.class), 'r') { |f| seq = f.read.to_i }
|
77
|
+
obj.oid = seq
|
78
|
+
File.open(path(obj.class, obj.oid), 'w') { |f| f << obj.to_yaml }
|
79
|
+
seq += 1
|
80
|
+
File.open(spath(obj.class), 'w') { |f| f << seq }
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete(obj_or_pk, klass = nil, cascade = true)
|
84
|
+
pk = obj_or_pk.is_a?(Fixnum) ? obj_or_pk : obj_or_pk.oid
|
85
|
+
klass ||= obj_or_pk.class
|
86
|
+
FileUtils.rm(path(klass, pk))
|
87
|
+
end
|
88
|
+
|
89
|
+
def find
|
90
|
+
end
|
91
|
+
|
92
|
+
def count
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Path helper.
|
98
|
+
|
99
|
+
def path(klass, pk = nil)
|
100
|
+
class_dir = File.join(@base_dir, klass::OGNAME)
|
101
|
+
pk ? File.join(class_dir, "#{pk}.yml") : class_dir
|
102
|
+
end
|
103
|
+
|
104
|
+
# Path to sequence helper.
|
105
|
+
|
106
|
+
def spath(klass)
|
107
|
+
File.join(path(klass), 'seq')
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Og
|
5
|
+
|
6
|
+
# A collection of utilities. Mainly to stay compatible with the
|
7
|
+
# SQL based backends.
|
8
|
+
|
9
|
+
module MemoryUtils
|
10
|
+
|
11
|
+
# FIXME: find a neutral name.
|
12
|
+
|
13
|
+
def table(klass)
|
14
|
+
klass.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
# FIXME: find a neutral name.
|
18
|
+
|
19
|
+
def join_table(class1, class2, postfix = nil)
|
20
|
+
"#{class1}#{class2}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def quote(val)
|
24
|
+
val.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
# A Store that 'persists' objects into (RAM) memory.
|
30
|
+
#--
|
31
|
+
# TODO: fully working query.
|
32
|
+
# TODO: arbitrary pk.
|
33
|
+
#++
|
34
|
+
|
35
|
+
class MemoryStore < Store
|
36
|
+
extend MemoryUtils; include MemoryUtils
|
37
|
+
|
38
|
+
class ObjectHash < Hash
|
39
|
+
def [](key)
|
40
|
+
super(key.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
def []=(key, value)
|
44
|
+
super(key.to_s, value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# This hash implements an in-memory database.
|
49
|
+
|
50
|
+
@objects = ObjectHash.new
|
51
|
+
|
52
|
+
def self.objects
|
53
|
+
@objects
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.objects=(val)
|
57
|
+
@objects = val
|
58
|
+
end
|
59
|
+
|
60
|
+
# A pseudo-connection to the actual object store.
|
61
|
+
|
62
|
+
attr_accessor :conn
|
63
|
+
|
64
|
+
def self.destroy(options)
|
65
|
+
FileUtils.rm_rf("#{options[:name]}.db")
|
66
|
+
rescue
|
67
|
+
Logger.info "Cannot destroy '#{name}'"
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(options)
|
71
|
+
super
|
72
|
+
begin
|
73
|
+
@conn = self.class.objects = YAML.load_file("#{@options[:name]}.db")
|
74
|
+
rescue
|
75
|
+
@conn = self.class.objects = ObjectHash.new
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def close
|
80
|
+
File.open("#{@options[:name]}.db", 'w') { |f| f << @conn.to_yaml }
|
81
|
+
end
|
82
|
+
|
83
|
+
# Enchants a class.
|
84
|
+
|
85
|
+
def enchant(klass, manager)
|
86
|
+
klass.property :oid, Fixnum
|
87
|
+
|
88
|
+
super
|
89
|
+
|
90
|
+
@conn[klass] ||= {}
|
91
|
+
|
92
|
+
eval_og_insert(klass)
|
93
|
+
eval_og_update(klass)
|
94
|
+
eval_og_read(klass)
|
95
|
+
eval_og_delete(klass)
|
96
|
+
end
|
97
|
+
|
98
|
+
# :section: Lifecycle methods.
|
99
|
+
|
100
|
+
# Loads an object from the store using the primary key.
|
101
|
+
|
102
|
+
def load(pk, klass)
|
103
|
+
@conn[klass][pk]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Update selected properties of an object or class of
|
107
|
+
# objects.
|
108
|
+
|
109
|
+
def update_properties(target, set, options = nil)
|
110
|
+
set = set.gsub(/,/, ';')
|
111
|
+
if target.is_a?(Class)
|
112
|
+
if options
|
113
|
+
condition = options[:condition] || options[:where]
|
114
|
+
else
|
115
|
+
condition = 'true'
|
116
|
+
end
|
117
|
+
for obj in @conn[target].values
|
118
|
+
obj.instance_eval %{
|
119
|
+
if #{condition.gsub(/=/, '==')}
|
120
|
+
#{set}
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
else
|
125
|
+
target.instance_eval(set)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
alias_method :pupdate, :update_properties
|
129
|
+
alias_method :update_property, :update_properties
|
130
|
+
|
131
|
+
# Find a collection of objects.
|
132
|
+
#
|
133
|
+
# === Examples
|
134
|
+
#
|
135
|
+
# User.find(:condition => 'age > 15', :order => 'score ASC', :offet => 10, :limit =>10)
|
136
|
+
# Comment.find(:include => :entry)
|
137
|
+
# store.find(:class => User, :where => 'age > 15')
|
138
|
+
|
139
|
+
def find(options)
|
140
|
+
query(options)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Find one object.
|
144
|
+
|
145
|
+
def find_one(options)
|
146
|
+
query(options).first
|
147
|
+
end
|
148
|
+
|
149
|
+
# Reloads an object from the store.
|
150
|
+
|
151
|
+
def reload(obj, pk)
|
152
|
+
# Nop, the memory store is always synchronized.
|
153
|
+
end
|
154
|
+
|
155
|
+
# Count results.
|
156
|
+
|
157
|
+
def count(options)
|
158
|
+
objects = 0
|
159
|
+
|
160
|
+
if condition = options[:condition] || options[:where]
|
161
|
+
condition = "obj." + condition.gsub(/=/, '==')
|
162
|
+
else
|
163
|
+
condition = true
|
164
|
+
end
|
165
|
+
|
166
|
+
eval %{
|
167
|
+
for obj in @conn[options[:class]].values
|
168
|
+
objects += 1 if #{condition}
|
169
|
+
end
|
170
|
+
}
|
171
|
+
|
172
|
+
return objects
|
173
|
+
end
|
174
|
+
|
175
|
+
# Relate two objects through an intermediate join table.
|
176
|
+
# Typically used in joins_many and many_to_many relations.
|
177
|
+
|
178
|
+
def join(obj1, obj2, table)
|
179
|
+
# nop
|
180
|
+
end
|
181
|
+
|
182
|
+
# Query.
|
183
|
+
|
184
|
+
def query(options)
|
185
|
+
objects = []
|
186
|
+
|
187
|
+
if condition = options[:condition] || options[:where]
|
188
|
+
condition = "obj." + condition.gsub(/=/, '==')
|
189
|
+
else
|
190
|
+
condition = true
|
191
|
+
end
|
192
|
+
|
193
|
+
eval %{
|
194
|
+
for obj in @conn[options[:class]].values
|
195
|
+
objects << obj if #{condition}
|
196
|
+
end
|
197
|
+
}
|
198
|
+
|
199
|
+
if order = options[:order]
|
200
|
+
desc = (order =~ /DESC/)
|
201
|
+
order = order.gsub(/DESC/, '').gsub(/ASC/, '')
|
202
|
+
eval "objects.sort { |x, y| x.#{order} <=> y.#{order} }"
|
203
|
+
objects.reverse! if desc
|
204
|
+
end
|
205
|
+
|
206
|
+
return objects
|
207
|
+
end
|
208
|
+
|
209
|
+
# :section: Transaction methods.
|
210
|
+
|
211
|
+
# Start a new transaction.
|
212
|
+
|
213
|
+
def start
|
214
|
+
end
|
215
|
+
|
216
|
+
# Commit a transaction.
|
217
|
+
|
218
|
+
def commit
|
219
|
+
end
|
220
|
+
|
221
|
+
# Rollback a transaction.
|
222
|
+
|
223
|
+
def rollback
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
# :section: Lifecycle method compilers.
|
229
|
+
|
230
|
+
# Compile the og_update method for the class.
|
231
|
+
|
232
|
+
def eval_og_insert(klass)
|
233
|
+
pk = klass.primary_key.first
|
234
|
+
|
235
|
+
klass.class_eval %{
|
236
|
+
def og_insert(store)
|
237
|
+
#{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
|
238
|
+
@#{pk} = store.conn[#{klass}].size + 1
|
239
|
+
store.conn[#{klass}][@#{pk}] = self
|
240
|
+
#{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
|
241
|
+
end
|
242
|
+
}
|
243
|
+
end
|
244
|
+
|
245
|
+
# Compile the og_update method for the class.
|
246
|
+
|
247
|
+
def eval_og_update(klass)
|
248
|
+
pk = klass.primary_key.first
|
249
|
+
|
250
|
+
klass.class_eval %{
|
251
|
+
def og_update(store)
|
252
|
+
#{Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
|
253
|
+
store.conn[#{klass}][@#{pk}] = self
|
254
|
+
#{Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
|
255
|
+
end
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
# Not really useful in this store, kept for compatibility,
|
260
|
+
# just to call the aspects.
|
261
|
+
|
262
|
+
def eval_og_read(klass)
|
263
|
+
klass.class_eval %{
|
264
|
+
def og_read
|
265
|
+
#{Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
|
266
|
+
#{Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
|
267
|
+
end
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
def eval_og_delete(klass)
|
272
|
+
klass.module_eval %{
|
273
|
+
def og_delete(store, pk, cascade = true)
|
274
|
+
#{Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
|
275
|
+
pk ||= @#{klass.primary_key.first}
|
276
|
+
transaction do |tx|
|
277
|
+
tx.conn[#{klass}].delete(pk)
|
278
|
+
if cascade and #{klass}.__meta[:descendants]
|
279
|
+
#{klass}.__meta[:descendants].each do |dclass, foreign_key|
|
280
|
+
eval "tx.conn[dclass].delete_if { |dobj| dobj.\#{foreign_key} == \#{pk} }"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
#{Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
|
285
|
+
end
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|