og 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 185 2004-12-10 13:29:09Z gmosx $
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.class_eval %{
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 185 2004-12-10 13:29:09Z gmosx $
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
- "_j_#{Og::Utils.encode(klass1)}_#{Og::Utils.encode(klass2)}"
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).
@@ -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 185 2004-12-10 13:29:09Z gmosx $
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