og 0.6.0 → 0.7.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.
@@ -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