og 0.13.0 → 0.14.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 +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
|