og 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +183 -0
- data/ChangeLog.1 +2344 -0
- data/README.og +4 -3
- data/RELEASES.og +15 -0
- data/Rakefile +135 -0
- data/examples/og/README +7 -0
- data/examples/og/mock_example.rb +58 -0
- data/examples/og/run.rb +9 -5
- data/lib/glue/property.rb +166 -107
- data/lib/glue/property.rb.old +307 -0
- data/lib/og.rb +17 -6
- data/lib/og/backend.rb +34 -4
- data/lib/og/backends/mysql.rb +3 -17
- data/lib/og/backends/psql.rb +5 -17
- data/lib/og/meta.rb +41 -26
- data/lib/og/mock.rb +223 -0
- data/lib/og/version.rb +2 -2
- data/test/og/tc_lifecycle.rb +107 -0
- data/test/tc_og.rb +31 -4
- metadata +22 -14
@@ -0,0 +1,307 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
# design:
|
4
|
+
# * Anastastios Koutoumanos <ak@navel.gr>
|
5
|
+
# * Elias Karakoulakis <ekarak@ktismata.com>
|
6
|
+
#
|
7
|
+
# (c) 2004 Navel, all rights reserved.
|
8
|
+
# $Id: property.rb 185 2004-12-10 13:29:09Z gmosx $
|
9
|
+
|
10
|
+
require "glue/array"
|
11
|
+
require "glue/hash"
|
12
|
+
|
13
|
+
module G
|
14
|
+
|
15
|
+
# = Property
|
16
|
+
#
|
17
|
+
# Ruby attributes are typeless and generally this is good. Some times
|
18
|
+
# we need extra metadata though, for example in relational mapping,
|
19
|
+
# or web form population.
|
20
|
+
#
|
21
|
+
# Only Fixnums, Strings, Floats, Times, Booleans are converted.
|
22
|
+
#
|
23
|
+
# The default = methods do not force the types. A special
|
24
|
+
# __force_set method should be used instead.
|
25
|
+
#
|
26
|
+
#--
|
27
|
+
# TODO:
|
28
|
+
# Inject only the really needd methods into Module.
|
29
|
+
# Perhaps a sync is needed in evals (!!!!)
|
30
|
+
#++
|
31
|
+
#
|
32
|
+
class Property
|
33
|
+
# the symbol of the property
|
34
|
+
attr_accessor :symbol
|
35
|
+
# the string representation of the symbol
|
36
|
+
attr_accessor :name
|
37
|
+
# the class of the property
|
38
|
+
attr_accessor :klass
|
39
|
+
# additional metadata (like sql declaratio, sql index, etc)
|
40
|
+
attr_accessor :meta
|
41
|
+
|
42
|
+
def initialize(symbol, klass, meta = {})
|
43
|
+
@symbol, @klass = symbol, klass
|
44
|
+
@meta = meta
|
45
|
+
@name = @symbol.to_s()
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
return @symbol == other.symbol
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
return name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end # module
|
58
|
+
|
59
|
+
class Module
|
60
|
+
|
61
|
+
# Define a property (== typed attribute)
|
62
|
+
# This works like Ruby's standard attr method, ie creates
|
63
|
+
# only one property.
|
64
|
+
#
|
65
|
+
# Use the prop_reader, prop_writer, prop_accessor methods
|
66
|
+
# for multiple properties.
|
67
|
+
#
|
68
|
+
# Examples:
|
69
|
+
# prop String, :name, :sql => "char(32), :sql_index => "name(32)"
|
70
|
+
# --> creates only writer.
|
71
|
+
# prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
|
72
|
+
# --> creates reader and writer.
|
73
|
+
#
|
74
|
+
def prop(*params)
|
75
|
+
meta = {}
|
76
|
+
klass = Object
|
77
|
+
|
78
|
+
for param in params
|
79
|
+
if param.is_a?(Class)
|
80
|
+
klass = param
|
81
|
+
elsif param.is_a?(Symbol)
|
82
|
+
symbol = param
|
83
|
+
elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
|
84
|
+
writer = param
|
85
|
+
elsif param.is_a?(Hash)
|
86
|
+
# the meta hash.
|
87
|
+
meta = param
|
88
|
+
else
|
89
|
+
raise "Error when defining property!"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
unless self.methods.include?(:__props)
|
94
|
+
eval %{
|
95
|
+
# Properties
|
96
|
+
# An array is used to enforce order.
|
97
|
+
def __props
|
98
|
+
@__props
|
99
|
+
end
|
100
|
+
|
101
|
+
def __props=(props)
|
102
|
+
@__props = props
|
103
|
+
end
|
104
|
+
|
105
|
+
def __meta
|
106
|
+
@__meta
|
107
|
+
end
|
108
|
+
|
109
|
+
def __meta=(meta)
|
110
|
+
@__meta = meta
|
111
|
+
end
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
@__props = G::SafeArray.new() unless @__props
|
116
|
+
|
117
|
+
property = G::Property.new(symbol, klass, meta)
|
118
|
+
|
119
|
+
reader = meta[:reader] || true
|
120
|
+
writer = writer || meta[:writer] || false
|
121
|
+
|
122
|
+
__add_prop(property, reader, writer)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Helper method. Accepts a collection of symbols and generates
|
126
|
+
# properties. Only generates reader.
|
127
|
+
#
|
128
|
+
# Example:
|
129
|
+
# prop_reader String, :name, :title, :body, :sql => "char(32)"
|
130
|
+
#
|
131
|
+
def prop_reader(*params)
|
132
|
+
meta = {}
|
133
|
+
klass = Object
|
134
|
+
symbols = []
|
135
|
+
|
136
|
+
for param in params
|
137
|
+
if param.is_a?(Class)
|
138
|
+
klass = param
|
139
|
+
elsif param.is_a?(Symbol)
|
140
|
+
symbols << param
|
141
|
+
elsif param.is_a?(Hash)
|
142
|
+
# the meta hash.
|
143
|
+
meta = param
|
144
|
+
else
|
145
|
+
raise "Error when defining property!"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
meta[:reader] = true
|
150
|
+
meta[:writer] = false
|
151
|
+
|
152
|
+
for symbol in symbols
|
153
|
+
prop(klass, symbol, meta)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Helper method. Accepts a collection of symbols and generates
|
158
|
+
# properties. Only generates writer.
|
159
|
+
#
|
160
|
+
# Example:
|
161
|
+
# prop_writer String, :name, :title, :body, :sql => "char(32)"
|
162
|
+
#
|
163
|
+
def prop_writer(*params)
|
164
|
+
meta = {}
|
165
|
+
klass = Object
|
166
|
+
symbols = []
|
167
|
+
|
168
|
+
for param in params
|
169
|
+
if param.is_a?(Class)
|
170
|
+
klass = param
|
171
|
+
elsif param.is_a?(Symbol)
|
172
|
+
symbols << param
|
173
|
+
elsif param.is_a?(Hash)
|
174
|
+
# the meta hash.
|
175
|
+
meta = param
|
176
|
+
else
|
177
|
+
raise "Error when defining property!"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
meta[:reader] = false
|
182
|
+
meta[:writer] = true
|
183
|
+
|
184
|
+
for symbol in symbols
|
185
|
+
prop(klass, symbol, meta)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Helper method. Accepts a collection of symbols and generates
|
190
|
+
# properties. Generates reader and writer.
|
191
|
+
#
|
192
|
+
# Example:
|
193
|
+
# prop_accessor String, :name, :title, :body, :sql => "char(32)"
|
194
|
+
#
|
195
|
+
def prop_accessor(*params)
|
196
|
+
meta = {}
|
197
|
+
klass = Object
|
198
|
+
symbols = []
|
199
|
+
|
200
|
+
for param in params
|
201
|
+
if param.is_a?(Class)
|
202
|
+
klass = param
|
203
|
+
elsif param.is_a?(Symbol)
|
204
|
+
symbols << param
|
205
|
+
elsif param.is_a?(Hash)
|
206
|
+
# the meta hash.
|
207
|
+
meta = param
|
208
|
+
else
|
209
|
+
raise "Error when defining property!"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
meta[:reader] = true
|
214
|
+
meta[:writer] = true
|
215
|
+
|
216
|
+
for symbol in symbols
|
217
|
+
prop(klass, symbol, meta)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Add the property
|
222
|
+
#
|
223
|
+
def __add_prop(prop, reader = true, writer = true)
|
224
|
+
if idx = @__props.index(prop)
|
225
|
+
# override in case of duplicates. Keep the order of the props.
|
226
|
+
@__props[idx] = prop
|
227
|
+
else
|
228
|
+
@__props << prop
|
229
|
+
end
|
230
|
+
|
231
|
+
# Precompile the property read/write methods
|
232
|
+
|
233
|
+
s, klass = prop.symbol, prop.klass
|
234
|
+
|
235
|
+
if reader
|
236
|
+
module_eval %{
|
237
|
+
def #{s}
|
238
|
+
return @#{s}
|
239
|
+
end
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
# gmosx: __force_xxx reuses xxx= to allow for easier
|
244
|
+
# overrides.
|
245
|
+
if writer
|
246
|
+
module_eval %{
|
247
|
+
def #{s}=(val)
|
248
|
+
@#{s} = val
|
249
|
+
end
|
250
|
+
|
251
|
+
def __force_#{s}(val)
|
252
|
+
self.#{s}=(} + case klass.name
|
253
|
+
when Fixnum.name
|
254
|
+
"val.to_i()"
|
255
|
+
when String.name
|
256
|
+
"val.to_s()"
|
257
|
+
when Float.name
|
258
|
+
"val.to_f()"
|
259
|
+
when Time.name
|
260
|
+
"Time.parse(val.to_s())"
|
261
|
+
when TrueClass.name, FalseClass.name
|
262
|
+
"val.to_i() > 0"
|
263
|
+
else
|
264
|
+
"val"
|
265
|
+
end + %{)
|
266
|
+
end
|
267
|
+
}
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Attach metadata
|
272
|
+
#
|
273
|
+
def meta(key, val)
|
274
|
+
@__meta = G::SafeHash.new unless @__meta
|
275
|
+
|
276
|
+
@__meta[key] = [] unless @__meta[key]
|
277
|
+
|
278
|
+
# guard against duplicates, no need to keep order.
|
279
|
+
@__meta[key].delete_if { |v| val == v }
|
280
|
+
@__meta[key] << val
|
281
|
+
end
|
282
|
+
|
283
|
+
# This method is typically called before including other
|
284
|
+
# modules to preserve properties order.
|
285
|
+
#
|
286
|
+
def inherit_meta(mod = superclass)
|
287
|
+
# concat props.
|
288
|
+
if mod.__props
|
289
|
+
@__props = G::SafeArray.new unless @__props
|
290
|
+
|
291
|
+
mod.__props.each { |p|
|
292
|
+
__add_prop(p)
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
# concat metadata
|
297
|
+
if mod.__meta
|
298
|
+
mod.__meta.each { |k, val|
|
299
|
+
val.each { |v|
|
300
|
+
meta(k, v)
|
301
|
+
} if val
|
302
|
+
}
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
data/lib/og.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# * George Moschovitis <gm@navel.gr>
|
3
3
|
#
|
4
4
|
# (c) 2004 Navel, all rights reserved.
|
5
|
-
# $Id: og.rb
|
5
|
+
# $Id: og.rb 197 2004-12-21 13:50:17Z gmosx $
|
6
6
|
|
7
7
|
require "glue/property"
|
8
8
|
require "glue/array"
|
@@ -28,6 +28,11 @@ $og_auto_manage_classes = true
|
|
28
28
|
# If true, automatically include the Og meta-language into Module.
|
29
29
|
$og_include_meta_language = true
|
30
30
|
|
31
|
+
# Attach the following prefix to all generated SQL table names.
|
32
|
+
# Usefull on hosting scenarios where you have to run multiple
|
33
|
+
# web applications/sites on a single database.
|
34
|
+
$og_table_prefix = nil
|
35
|
+
|
31
36
|
require "og/meta"
|
32
37
|
|
33
38
|
# = Og
|
@@ -169,7 +174,10 @@ class Database
|
|
169
174
|
@config[:connection_count].times do
|
170
175
|
@connection_pool << Og::Connection.new(self)
|
171
176
|
end
|
172
|
-
|
177
|
+
|
178
|
+
# gmosx, FIXME: this automanage code is not elegant and slow
|
179
|
+
# should probably recode this, along with glue/property.rb
|
180
|
+
#
|
173
181
|
if $og_auto_manage_classes
|
174
182
|
# automatically manage classes with properties and metadata.
|
175
183
|
# gmosx: Any idea how to optimize this?
|
@@ -286,8 +294,6 @@ class Database
|
|
286
294
|
Og::Utils.eval_og_oid(klass) unless klass.instance_methods.include?(:oid)
|
287
295
|
|
288
296
|
klass.class_eval %{
|
289
|
-
inherit_meta(superclass) if superclass
|
290
|
-
|
291
297
|
DBTABLE = "#{Og::Utils.table(klass)}"
|
292
298
|
DBSEQ = "#{Og::Utils.table(klass)}_oids_seq"
|
293
299
|
|
@@ -311,7 +317,12 @@ class Database
|
|
311
317
|
# class and its instances.
|
312
318
|
#
|
313
319
|
def enchant(klass)
|
314
|
-
klass.
|
320
|
+
klass.module_eval <<-"end_eval", __FILE__, __LINE__
|
321
|
+
def self.create(*params)
|
322
|
+
obj = #{klass}.new(*params)
|
323
|
+
obj.save!
|
324
|
+
end
|
325
|
+
|
315
326
|
def self.save(obj)
|
316
327
|
$og << obj
|
317
328
|
end
|
@@ -362,7 +373,7 @@ class Database
|
|
362
373
|
def delete!
|
363
374
|
$og.delete(@oid, #{klass})
|
364
375
|
end
|
365
|
-
|
376
|
+
end_eval
|
366
377
|
end
|
367
378
|
|
368
379
|
# Automatically wrap connection methods.
|
data/lib/og/backend.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# * George Moschovitis <gm@navel.gr>
|
3
3
|
#
|
4
4
|
# (c) 2004 Navel, all rights reserved.
|
5
|
-
# $Id: backend.rb
|
5
|
+
# $Id: backend.rb 197 2004-12-21 13:50:17Z gmosx $
|
6
6
|
|
7
7
|
require "yaml"
|
8
8
|
|
@@ -28,13 +28,13 @@ module Utils
|
|
28
28
|
# The name of the SQL table where objects of this class are stored.
|
29
29
|
#
|
30
30
|
def self.table(klass)
|
31
|
-
"_#{encode(klass)}"
|
31
|
+
"_#{$og_table_prefix}#{encode(klass)}"
|
32
32
|
end
|
33
33
|
|
34
34
|
# The name of the join table for the two given classes.
|
35
35
|
#
|
36
36
|
def self.join_table(klass1, klass2)
|
37
|
-
"
|
37
|
+
"_#{$og_table_prefix}j_#{Og::Utils.encode(klass1)}_#{Og::Utils.encode(klass2)}"
|
38
38
|
end
|
39
39
|
|
40
40
|
# Returns the props that will be included in the insert query.
|
@@ -232,7 +232,37 @@ class Backend
|
|
232
232
|
def rollback
|
233
233
|
exec "ROLLBACK"
|
234
234
|
end
|
235
|
-
|
235
|
+
|
236
|
+
# Create the fields that correpsond to the klass properties.
|
237
|
+
# The generated fields array is used in create_table.
|
238
|
+
# If the property has an :sql metadata this overrides the default mapping.
|
239
|
+
# If the property has an :extra_sql metadata the extra sql is appended
|
240
|
+
# after the default mapping.
|
241
|
+
#
|
242
|
+
def create_fields(klass, typemap)
|
243
|
+
fields = []
|
244
|
+
|
245
|
+
klass.__props.each do |p|
|
246
|
+
klass.sql_index(p.symbol) if p.meta[:sql_index]
|
247
|
+
|
248
|
+
field = "#{p.symbol}"
|
249
|
+
|
250
|
+
if p.meta and p.meta[:sql]
|
251
|
+
field << " #{p.meta[:sql]}"
|
252
|
+
else
|
253
|
+
field << " #{typemap[p.klass]}"
|
254
|
+
# attach extra sql
|
255
|
+
if p.meta and extra_sql = p.meta[:extra_sql]
|
256
|
+
field << " #{extra_sql}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
fields << field
|
261
|
+
end
|
262
|
+
|
263
|
+
return fields
|
264
|
+
end
|
265
|
+
|
236
266
|
# Create the managed object table. The properties of the
|
237
267
|
# object are mapped to the table columns. Additional sql relations
|
238
268
|
# and constrains are created (indicices, sequences, etc).
|
data/lib/og/backends/mysql.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# * Elias Athanasopoulos <elathan@navel.gr>
|
4
4
|
#
|
5
5
|
# (c) 2004 Navel, all rights reserved.
|
6
|
-
# $Id: mysql.rb
|
6
|
+
# $Id: mysql.rb 194 2004-12-20 20:23:57Z gmosx $
|
7
7
|
|
8
8
|
require "mysql"
|
9
9
|
|
@@ -263,21 +263,7 @@ class MysqlBackend < Og::Backend
|
|
263
263
|
# and constrains are created (indicices, sequences, etc).
|
264
264
|
#
|
265
265
|
def create_table(klass)
|
266
|
-
fields =
|
267
|
-
|
268
|
-
klass.__props.each do |p|
|
269
|
-
klass.sql_index(p.symbol) if p.meta[:sql_index]
|
270
|
-
|
271
|
-
field = "#{p.symbol}"
|
272
|
-
|
273
|
-
if p.meta and p.meta[:sql]
|
274
|
-
field << " #{p.meta[:sql]}"
|
275
|
-
else
|
276
|
-
field << " #{TYPEMAP[p.klass]}"
|
277
|
-
end
|
278
|
-
|
279
|
-
fields << field
|
280
|
-
end
|
266
|
+
fields = create_fields(klass, TYPEMAP)
|
281
267
|
|
282
268
|
sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
|
283
269
|
|
@@ -326,7 +312,7 @@ class MysqlBackend < Og::Backend
|
|
326
312
|
join_src = "#{Og::Utils.encode(klass)}_oid"
|
327
313
|
join_dst = "#{Og::Utils.encode(join_class)}_oid"
|
328
314
|
begin
|
329
|
-
exec "CREATE TABLE #{join_table} ( key1 integer, key2 integer )"
|
315
|
+
exec "CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )"
|
330
316
|
exec "CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)"
|
331
317
|
exec "CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)"
|
332
318
|
rescue => ex
|