og 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +114 -0
- data/INSTALL +22 -0
- data/README +1 -1
- data/Rakefile +2 -2
- data/doc/RELEASES +17 -1
- data/lib/og.rb +9 -3
- data/lib/og/adapter.rb +13 -2
- data/lib/og/adapters/mysql.rb +13 -7
- data/lib/og/adapters/oracle.rb +12 -9
- data/lib/og/adapters/psql.rb +9 -7
- data/lib/og/adapters/sqlite.rb +14 -7
- data/lib/og/connection.rb +26 -5
- data/lib/og/database.rb +33 -27
- data/lib/og/enchant.rb +28 -4
- data/lib/og/meta.rb +30 -5
- data/lib/og/mixins/list.rb +158 -0
- data/lib/og/mixins/tree.rb +228 -0
- data/lib/og/{mock.rb → testing/mock.rb} +0 -0
- data/test/og/mixins/tc_list.rb +104 -0
- data/test/og/mixins/tc_tree.rb +59 -0
- data/test/og/tc_automanage.rb +45 -0
- data/test/tc_og.rb +29 -0
- metadata +13 -5
data/lib/og/connection.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: connection.rb
|
3
|
+
# $Id: connection.rb 326 2005-03-28 11:07:17Z gmosx $
|
4
4
|
|
5
5
|
module Og;
|
6
6
|
|
@@ -12,10 +12,10 @@ require 'glue/time'
|
|
12
12
|
# functionality. A backend specific implementation file (driver)
|
13
13
|
# implements all methods.
|
14
14
|
#
|
15
|
-
|
16
|
-
#
|
15
|
+
#--
|
17
16
|
# - support caching.
|
18
17
|
# - support prepared statements.
|
18
|
+
#++
|
19
19
|
|
20
20
|
class Connection
|
21
21
|
|
@@ -139,6 +139,12 @@ class Connection
|
|
139
139
|
raise 'Not implemented!'
|
140
140
|
end
|
141
141
|
|
142
|
+
# Get a row from the resultset.
|
143
|
+
|
144
|
+
def get_row(res)
|
145
|
+
return res
|
146
|
+
end
|
147
|
+
|
142
148
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
143
149
|
# :section: Managed object methods.
|
144
150
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
@@ -187,6 +193,7 @@ class Connection
|
|
187
193
|
exec "UPDATE #{klass::DBTABLE} SET #{update_sql} WHERE oid=#{oid}"
|
188
194
|
end
|
189
195
|
alias_method :pupdate, :update_properties
|
196
|
+
alias_method :update_propery, :update_properties
|
190
197
|
|
191
198
|
# Load an object from the database.
|
192
199
|
#
|
@@ -282,7 +289,7 @@ class Connection
|
|
282
289
|
# this is a class callback!
|
283
290
|
|
284
291
|
if klass.respond_to?(:og_pre_delete)
|
285
|
-
klass.og_pre_delete(self,
|
292
|
+
klass.og_pre_delete(self, obj_or_oid)
|
286
293
|
end
|
287
294
|
|
288
295
|
# TODO: implement this as stored procedure? naaah.
|
@@ -298,7 +305,21 @@ class Connection
|
|
298
305
|
end
|
299
306
|
end
|
300
307
|
alias_method :delete!, :delete
|
301
|
-
|
308
|
+
|
309
|
+
protected
|
310
|
+
|
311
|
+
# Handles an adapter exception.
|
312
|
+
|
313
|
+
def handle_db_exception(ex, sql = nil)
|
314
|
+
Logger.error "DB error #{ex}, [#{sql}]"
|
315
|
+
Logger.error ex.backtrace.join("\n")
|
316
|
+
raise SqlException.new(ex, sql) if Og.raise_db_exceptions
|
317
|
+
|
318
|
+
# FIXME: should return :error or something.
|
319
|
+
|
320
|
+
return nil
|
321
|
+
end
|
322
|
+
|
302
323
|
end
|
303
324
|
|
304
325
|
end
|
data/lib/og/database.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: database.rb
|
3
|
+
# $Id: database.rb 326 2005-03-28 11:07:17Z gmosx $
|
4
4
|
|
5
5
|
require 'glue/logger'
|
6
6
|
require 'glue/attribute'
|
@@ -65,6 +65,8 @@ class Database
|
|
65
65
|
|
66
66
|
@adapter = Adapter.for_name(@config[:adapter])
|
67
67
|
|
68
|
+
self.class.drop!(config) if config[:drop]
|
69
|
+
|
68
70
|
@connection_pool = N::Pool.new
|
69
71
|
@managed_classes = N::SafeHash.new
|
70
72
|
|
@@ -72,26 +74,7 @@ class Database
|
|
72
74
|
@connection_pool << @adapter.new_connection(self)
|
73
75
|
end
|
74
76
|
|
75
|
-
|
76
|
-
# should probably recode this, along with glue/property.rb
|
77
|
-
|
78
|
-
if Og.auto_manage_classes
|
79
|
-
# automatically manage classes with properties and metadata.
|
80
|
-
# gmosx: Any idea how to optimize this?
|
81
|
-
|
82
|
-
classes_to_manage = []
|
83
|
-
|
84
|
-
ObjectSpace.each_object(Class) do |c|
|
85
|
-
if c.respond_to?(:__props) and c.__props
|
86
|
-
classes_to_manage << c
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
Logger.debug "Og auto manages the following classes:"
|
91
|
-
Logger.debug "#{classes_to_manage.inspect}"
|
92
|
-
|
93
|
-
manage_classes(*classes_to_manage)
|
94
|
-
end
|
77
|
+
auto_manage_classes if Og.auto_manage_classes
|
95
78
|
|
96
79
|
# use the newly created database.
|
97
80
|
Og.use(self)
|
@@ -169,6 +152,32 @@ class Database
|
|
169
152
|
manage(klass)
|
170
153
|
end
|
171
154
|
end
|
155
|
+
|
156
|
+
# Use Ruby's advanced reflection features to find
|
157
|
+
# and manage all manageable classes. Manageable are all
|
158
|
+
# classes that include Properties or Metadata.
|
159
|
+
#--
|
160
|
+
# gmosx, FIXME: this automanage code is not elegant and slow
|
161
|
+
# should probably recode this, along with glue/property.rb
|
162
|
+
# FIXME: can this be optimized?
|
163
|
+
# TODO: find a better name.
|
164
|
+
#++
|
165
|
+
|
166
|
+
def auto_manage_classes
|
167
|
+
classes_to_manage = []
|
168
|
+
|
169
|
+
ObjectSpace.each_object(Class) do |c|
|
170
|
+
if c.respond_to?(:__props) and c.__props
|
171
|
+
classes_to_manage << c
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
Logger.debug "Og auto manages the following classes:"
|
176
|
+
Logger.debug "#{classes_to_manage.inspect}"
|
177
|
+
|
178
|
+
manage_classes(*classes_to_manage)
|
179
|
+
end
|
180
|
+
alias_method :auto_manage, :auto_manage_classes
|
172
181
|
|
173
182
|
# Stop managing a Ruby class
|
174
183
|
|
@@ -177,7 +186,7 @@ class Database
|
|
177
186
|
end
|
178
187
|
|
179
188
|
# Is this class managed?
|
180
|
-
|
189
|
+
|
181
190
|
def managed?(klass)
|
182
191
|
return @managed_classes.include?(klass)
|
183
192
|
end
|
@@ -212,11 +221,6 @@ class Database
|
|
212
221
|
# Create the schema for this class if not available.
|
213
222
|
|
214
223
|
@adapter.create_table(klass, self) if Og.create_schema
|
215
|
-
|
216
|
-
# @adapter.eval_og_insert(klass, self)
|
217
|
-
# @adapter.eval_og_update(klass, self)
|
218
|
-
# @adapter.eval_og_read(klass, self)
|
219
|
-
|
220
224
|
@adapter.eval_lifecycle_methods(klass, self)
|
221
225
|
end
|
222
226
|
|
@@ -257,6 +261,8 @@ class Database
|
|
257
261
|
wrap_method :prepare, 'sql'
|
258
262
|
wrap_method :query, 'sql'
|
259
263
|
wrap_method :exec, 'sql'
|
264
|
+
wrap_method :get_row, 'res'
|
265
|
+
wrap_method :transaction, '&block'
|
260
266
|
|
261
267
|
class << self
|
262
268
|
|
data/lib/og/enchant.rb
CHANGED
@@ -43,6 +43,14 @@ module Enchant
|
|
43
43
|
@@og_db.load_all(#{klass}, extra_sql)
|
44
44
|
end
|
45
45
|
|
46
|
+
def self.update_all(set_sql, extra_sql = nil)
|
47
|
+
@@og_db.exec("UPDATE #{klass::DBTABLE} SET \#\{set_sql\} \#\{extra_sql\}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.update(set_sql, extra_sql = nil)
|
51
|
+
@@og_db.exec("UPDATE #{klass::DBTABLE} SET \#\{set_sql\} \#\{extra_sql\}")
|
52
|
+
end
|
53
|
+
|
46
54
|
def self.count(sql = "SELECT COUNT(*) FROM #{klass::DBTABLE}")
|
47
55
|
@@og_db.count(sql, #{klass})
|
48
56
|
end
|
@@ -71,11 +79,15 @@ module Enchant
|
|
71
79
|
@@og_db.delete(obj_or_oid, #{klass})
|
72
80
|
end
|
73
81
|
|
82
|
+
def self.transaction(&block)
|
83
|
+
@@og_db.transaction(&block)
|
84
|
+
end
|
85
|
+
|
74
86
|
def self.properties_and_relations
|
75
87
|
@@__meta[:props_and_relations]
|
76
88
|
end
|
77
89
|
|
78
|
-
def each(&block)
|
90
|
+
def self.each(&block)
|
79
91
|
all.each(&block)
|
80
92
|
end
|
81
93
|
include Enumerable
|
@@ -85,14 +97,26 @@ module Enchant
|
|
85
97
|
return self
|
86
98
|
end
|
87
99
|
alias_method :save!, :save
|
88
|
-
|
100
|
+
|
101
|
+
def reload
|
102
|
+
raise 'Cannot reload unmanaged object' unless @oid
|
103
|
+
res = @@og_db.query "SELECT * FROM #{klass::DBTABLE} WHERE oid=\#\{@oid\}"
|
104
|
+
og_read(@@og_db.get_row(res))
|
105
|
+
end
|
106
|
+
alias_method :reload!, :reload
|
107
|
+
|
89
108
|
def update_properties(updatesql)
|
90
109
|
@@og_db.pupdate(updatesql, self.oid, #{klass})
|
91
110
|
end
|
92
|
-
alias_method :pupdate
|
111
|
+
alias_method :pupdate, :update_properties
|
112
|
+
alias_method :update_property, :update_properties
|
113
|
+
|
114
|
+
def set_property(prop, value)
|
115
|
+
@@og_db.pupdate("\#\{prop\}=\#\{value\}", self.oid, #{klass})
|
116
|
+
end
|
93
117
|
|
94
118
|
def delete!
|
95
|
-
@@og_db.delete(
|
119
|
+
@@og_db.delete(self, #{klass})
|
96
120
|
end
|
97
121
|
}
|
98
122
|
|
data/lib/og/meta.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
-
# $Id: meta.rb
|
3
|
+
# $Id: meta.rb 326 2005-03-28 11:07:17Z gmosx $
|
4
4
|
#--
|
5
5
|
# TODO:
|
6
6
|
# - precreate the meta sql statements as much as possible to
|
@@ -11,6 +11,8 @@ require 'glue/inflector'
|
|
11
11
|
require 'og/adapter'
|
12
12
|
require 'og/typemacros'
|
13
13
|
|
14
|
+
require 'og/mixins/list'
|
15
|
+
|
14
16
|
module Og
|
15
17
|
|
16
18
|
class Relation < N::Property
|
@@ -138,7 +140,7 @@ module MetaLanguage
|
|
138
140
|
linkback = (options[:linkback].to_s || "#{MetaUtils.expand(self)}_oid").to_s
|
139
141
|
|
140
142
|
meta :descendants, klass, linkback
|
141
|
-
meta :props_and_relations, HasOne.new(name, klass,
|
143
|
+
meta :props_and_relations, HasOne.new(name, klass, options)
|
142
144
|
|
143
145
|
module_eval %{
|
144
146
|
def #{name}(extrasql = nil)
|
@@ -150,10 +152,18 @@ module MetaLanguage
|
|
150
152
|
end
|
151
153
|
}
|
152
154
|
end
|
153
|
-
|
155
|
+
|
154
156
|
# Implements a 'has_many' relation.
|
155
157
|
# Automatically enchants the calling class with helper methods.
|
156
158
|
#
|
159
|
+
# Options:
|
160
|
+
#
|
161
|
+
# [+linkback+]
|
162
|
+
#
|
163
|
+
# [+order+]
|
164
|
+
#
|
165
|
+
# [+extrasql+]
|
166
|
+
#
|
157
167
|
# Example:
|
158
168
|
#
|
159
169
|
# class MyObject
|
@@ -176,16 +186,21 @@ module MetaLanguage
|
|
176
186
|
# to this object.
|
177
187
|
|
178
188
|
linkback = (options[:linkback] || "#{MetaUtils.expand(self)}_oid").to_s
|
189
|
+
|
190
|
+
if order = options[:order]
|
191
|
+
order = "ORDER BY #{order}"
|
192
|
+
end
|
193
|
+
default_extrasql = "#{order}#{options[:sql]}"
|
179
194
|
|
180
195
|
# keep belongs to metadata, useful for
|
181
196
|
# reflection/scaffolding.
|
182
197
|
|
183
198
|
meta :descendants, klass, linkback
|
184
|
-
meta :props_and_relations, HasMany.new(name, klass,
|
199
|
+
meta :props_and_relations, HasMany.new(name, klass, options)
|
185
200
|
|
186
201
|
code = %{
|
187
202
|
def #{name}(extrasql = nil)
|
188
|
-
Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}", #{klass})
|
203
|
+
Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid #{default_extrasql} \#\{extrasql\}", #{klass})
|
189
204
|
end
|
190
205
|
|
191
206
|
def #{name}_count(extrasql = nil)
|
@@ -380,5 +395,15 @@ end
|
|
380
395
|
if Og.include_meta_language
|
381
396
|
class Module # :nodoc: all
|
382
397
|
include Og::MetaLanguage
|
398
|
+
include Og::List
|
399
|
+
=begin
|
400
|
+
A hack to avoid forward references. Does not work
|
401
|
+
with namespave modules though. Any idea?
|
402
|
+
-> try TOPLEVEL_BINDING
|
403
|
+
|
404
|
+
def const_missing(sym)
|
405
|
+
eval "class ::#{sym}; end; return #{sym}"
|
406
|
+
end
|
407
|
+
=end
|
383
408
|
end
|
384
409
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id: list.rb 326 2005-03-28 11:07:17Z gmosx $
|
4
|
+
|
5
|
+
module Og
|
6
|
+
|
7
|
+
# Attach list/ordering methods to the enchanted class.
|
8
|
+
#--
|
9
|
+
# TODO:
|
10
|
+
# Convert to new Og filter system.
|
11
|
+
# Implement as super-mixin.
|
12
|
+
#++
|
13
|
+
|
14
|
+
module List
|
15
|
+
|
16
|
+
# The enchanted object acts as a list item.
|
17
|
+
|
18
|
+
def acts_as_list(options = {})
|
19
|
+
c = { :position => 'position', :type => Fixnum, :scope => '1 = 1' }
|
20
|
+
c.update(options) if options.is_a?(Hash)
|
21
|
+
c[:scope] = "#{c[:scope]}_oid".intern if c[:scope].is_a?(Symbol) && c[:scope].to_s !~ /_oid$/
|
22
|
+
|
23
|
+
position = c[:position]
|
24
|
+
scope = c[:scope]
|
25
|
+
|
26
|
+
if scope.is_a?(Symbol)
|
27
|
+
scope = %{(#{scope} ? "#{scope} = \#{@#{scope}}" : "#{scope} IS NULL")}
|
28
|
+
end
|
29
|
+
|
30
|
+
module_eval <<-EOE, __FILE__, __LINE__
|
31
|
+
property :#{position}, #{c[:type]}
|
32
|
+
|
33
|
+
def og_pre_insert(conn)
|
34
|
+
add_to_bottom
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.og_pre_delete(conn, obj)
|
38
|
+
obj.decrement_position_of_lower_items
|
39
|
+
end
|
40
|
+
|
41
|
+
def move_higher
|
42
|
+
if higher = higher_item
|
43
|
+
#{self}.transaction do
|
44
|
+
higher.increment_position
|
45
|
+
decrement_position
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def move_lower
|
51
|
+
if lower = lower_item
|
52
|
+
#{self}.transaction do
|
53
|
+
lower.decrement_position
|
54
|
+
increment_position
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def move_to_top
|
60
|
+
#{self}.transaction do
|
61
|
+
increment_position_of_higher_items
|
62
|
+
set_top_position
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def move_to_bottom
|
67
|
+
#{self}.transaction do
|
68
|
+
decrement_position_of_lower_items
|
69
|
+
set_bottom_position
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def move_to
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_to_top
|
77
|
+
increment_position_of_all_items
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_to_bottom
|
81
|
+
@#{position} = bottom_position + 1
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_to
|
85
|
+
end
|
86
|
+
|
87
|
+
def higher_item
|
88
|
+
#{self}.one(#{scope} + " AND #{position}=\#\{@#{position} - 1\}")
|
89
|
+
end
|
90
|
+
alias_method :previous_item, :higher_item
|
91
|
+
|
92
|
+
def lower_item
|
93
|
+
#{self}.one(#{scope} + " AND #{position}=\#\{@#{position} + 1\}")
|
94
|
+
end
|
95
|
+
alias_method :next_item, :lower_item
|
96
|
+
|
97
|
+
def top_item
|
98
|
+
end
|
99
|
+
alias_method :first_item, :top_item
|
100
|
+
|
101
|
+
def bottom_item
|
102
|
+
#{self}.one(#{scope} + " ORDER BY #{position} DESC")
|
103
|
+
end
|
104
|
+
alias_method :last_item, :last_item
|
105
|
+
|
106
|
+
def top?
|
107
|
+
@#{position} == 1
|
108
|
+
end
|
109
|
+
alias_method :first?, :top?
|
110
|
+
|
111
|
+
def bottom?
|
112
|
+
@#{position} == bottom_position
|
113
|
+
end
|
114
|
+
alias_method :last?, :bottom?
|
115
|
+
|
116
|
+
def increment_position
|
117
|
+
@#{position} += 1
|
118
|
+
set_property('#{position}', @#{position})
|
119
|
+
end
|
120
|
+
|
121
|
+
def decrement_position
|
122
|
+
@#{position} -= 1
|
123
|
+
set_property('#{position}', @#{position})
|
124
|
+
end
|
125
|
+
|
126
|
+
def bottom_position
|
127
|
+
item = bottom_item
|
128
|
+
item ? item.#{position} : 0
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_top_position
|
132
|
+
@#{position} = 1
|
133
|
+
set_property('#{position}', 1)
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_bottom_position
|
137
|
+
@#{position} = bottom_position + 1
|
138
|
+
set_property('#{position}', @#{position})
|
139
|
+
end
|
140
|
+
|
141
|
+
def increment_position_of_higher_items
|
142
|
+
#{self}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope} + " AND #{position} < \#\{@#{position}\}")
|
143
|
+
end
|
144
|
+
|
145
|
+
def increment_position_of_all_items
|
146
|
+
#{self}.update("#{position}=(#{position} + 1)", "WHERE " + #{scope})
|
147
|
+
end
|
148
|
+
|
149
|
+
def decrement_position_of_lower_items
|
150
|
+
#{self}.update("#{position}=(#{position} - 1)", "WHERE " + #{scope} + " AND #{position} > \#\{@#{position}\}")
|
151
|
+
end
|
152
|
+
EOE
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|