og 0.8.0 → 0.9.3

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.
@@ -1,8 +1,6 @@
1
- # code:
2
1
  # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
5
- # $Id: psql.rb 194 2004-12-20 20:23:57Z gmosx $
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: psql.rb 248 2005-01-31 13:38:34Z gmosx $
6
4
 
7
5
  require 'postgres'
8
6
 
@@ -10,16 +8,14 @@ require 'og/backend'
10
8
 
11
9
  class Og
12
10
 
13
- # = PsqlBackend
14
- #
15
11
  # Implements a PostgreSQL powered backend.
16
12
  # This backend is compatible with Michael Neumann's postgres-pr
17
13
  # pure ruby driver.
18
- #
14
+
19
15
  class PsqlBackend < Og::Backend
20
16
 
21
17
  # A mapping between Ruby and SQL types.
22
- #
18
+
23
19
  TYPEMAP = {
24
20
  Integer => 'integer',
25
21
  Fixnum => 'integer',
@@ -34,7 +30,7 @@ class PsqlBackend < Og::Backend
34
30
  }
35
31
 
36
32
  # Intitialize the connection to the RDBMS.
37
- #
33
+
38
34
  def initialize(config)
39
35
  begin
40
36
  @conn = PGconn.connect(nil, nil, nil, nil, config[:database],
@@ -55,7 +51,7 @@ class PsqlBackend < Og::Backend
55
51
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56
52
 
57
53
  # Escape an SQL string
58
- #
54
+
59
55
  def self.escape(str)
60
56
  return nil unless str
61
57
  return PGconn.escape(str)
@@ -63,7 +59,7 @@ class PsqlBackend < Og::Backend
63
59
 
64
60
  # Convert a ruby time to an sql timestamp.
65
61
  # TODO: Optimize this
66
- #
62
+
67
63
  def self.timestamp(time = Time.now)
68
64
  return nil unless time
69
65
  return time.strftime("%Y-%m-%d %H:%M:%S")
@@ -71,7 +67,7 @@ class PsqlBackend < Og::Backend
71
67
 
72
68
  # Output YYY-mm-dd
73
69
  # TODO: Optimize this
74
- #
70
+
75
71
  def self.date(date)
76
72
  return nil unless date
77
73
  return "#{date.year}-#{date.month}-#{date.mday}"
@@ -79,14 +75,14 @@ class PsqlBackend < Og::Backend
79
75
 
80
76
  # Parse sql datetime
81
77
  # TODO: Optimize this
82
- #
78
+
83
79
  def self.parse_timestamp(str)
84
80
  return Time.parse(str)
85
81
  end
86
82
 
87
83
  # Input YYYY-mm-dd
88
84
  # TODO: Optimize this
89
- #
85
+
90
86
  def self.parse_date(str)
91
87
  return nil unless str
92
88
  return Date.strptime(str)
@@ -98,7 +94,7 @@ class PsqlBackend < Og::Backend
98
94
  # portable.
99
95
  #
100
96
  # FIXME: add extra handling for float.
101
- #
97
+
102
98
  def self.write_prop(p)
103
99
  if p.klass.ancestors.include?(Integer)
104
100
  return "#\{@#{p.symbol} || 'NULL'\}"
@@ -111,7 +107,7 @@ class PsqlBackend < Og::Backend
111
107
  elsif p.klass.ancestors.include?(Date)
112
108
  return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.date(@#{p.symbol})\}'" : 'NULL'\}|
113
109
  elsif p.klass.ancestors.include?(TrueClass)
114
- return "#\{@#{p.symbol} || 'NULL'\}"
110
+ return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
115
111
  else
116
112
  return %|#\{@#{p.symbol} ? "'#\{Og::PsqlBackend.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
117
113
  end
@@ -119,7 +115,7 @@ class PsqlBackend < Og::Backend
119
115
 
120
116
  # Return an evaluator for reading the property.
121
117
  # No need to optimize this, used only to precalculate code.
122
- #
118
+
123
119
  def self.read_prop(p, idx)
124
120
  if p.klass.ancestors.include?(Integer)
125
121
  return "res.getvalue(tuple, #{idx}).to_i()"
@@ -132,7 +128,7 @@ class PsqlBackend < Og::Backend
132
128
  elsif p.klass.ancestors.include?(Date)
133
129
  return "Og::PsqlBackend.parse_date(res.getvalue(tuple, #{idx}))"
134
130
  elsif p.klass.ancestors.include?(TrueClass)
135
- return "('true' == res.getvalue(tuple, #{idx}))"
131
+ return %|('t' == res.getvalue(tuple, #{idx}))|
136
132
  else
137
133
  return "YAML::load(res.getvalue(tuple, #{idx}))"
138
134
  end
@@ -140,7 +136,7 @@ class PsqlBackend < Og::Backend
140
136
 
141
137
  # Returns the code that actually inserts the object into the
142
138
  # database. Returns the code as String.
143
- #
139
+
144
140
  def self.insert_code(klass, sql, pre_cb, post_cb)
145
141
  %{
146
142
  #{pre_cb}
@@ -153,7 +149,7 @@ class PsqlBackend < Og::Backend
153
149
 
154
150
  # generate the mapping of the database fields to the
155
151
  # object properties.
156
- #
152
+
157
153
  def self.calc_field_index(klass, og)
158
154
  res = og.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
159
155
  meta = og.managed_classes[klass]
@@ -164,7 +160,7 @@ class PsqlBackend < Og::Backend
164
160
  end
165
161
 
166
162
  # Generate the property for oid
167
- #
163
+
168
164
  def self.eval_og_oid(klass)
169
165
  klass.class_eval %{
170
166
  prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
@@ -176,28 +172,28 @@ class PsqlBackend < Og::Backend
176
172
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177
173
 
178
174
  # Create the database.
179
- #
175
+
180
176
  def self.create_db(database, user = nil, password = nil)
181
177
  Logger.info "Creating database '#{database}'."
182
178
  `createdb #{database} -U #{user}`
183
179
  end
184
180
 
185
181
  # Drop the database.
186
- #
182
+
187
183
  def self.drop_db(database, user = nil, password = nil)
188
184
  Logger.info "Dropping database '#{database}'."
189
185
  `dropdb #{database} -U #{user}`
190
186
  end
191
187
 
192
- # Execute an SQL query and return the result
193
- #
188
+ # Execute an SQL query and return the result.
189
+
194
190
  def query(sql)
195
191
  Logger.debug sql if $DBG
196
192
  return @conn.exec(sql)
197
193
  end
198
194
 
199
195
  # Execute an SQL query, no result returned.
200
- #
196
+
201
197
  def exec(sql)
202
198
  Logger.debug sql if $DBG
203
199
  res = @conn.exec(sql)
@@ -206,7 +202,7 @@ class PsqlBackend < Og::Backend
206
202
 
207
203
  # Execute an SQL query and return the result. Wrapped in a rescue
208
204
  # block.
209
- #
205
+
210
206
  def safe_query(sql)
211
207
  Logger.debug sql if $DBG
212
208
  begin
@@ -220,7 +216,7 @@ class PsqlBackend < Og::Backend
220
216
 
221
217
  # Execute an SQL query, no result returned. Wrapped in a rescue
222
218
  # block.
223
- #
219
+
224
220
  def safe_exec(sql)
225
221
  Logger.debug sql if $DBG
226
222
  begin
@@ -233,7 +229,7 @@ class PsqlBackend < Og::Backend
233
229
  end
234
230
 
235
231
  # Check if it is a valid resultset.
236
- #
232
+
237
233
  def valid?(res)
238
234
  return !(res.nil? or 0 == res.num_tuples)
239
235
  end
@@ -241,7 +237,7 @@ class PsqlBackend < Og::Backend
241
237
  # Create the managed object table. The properties of the
242
238
  # object are mapped to the table columns. Additional sql relations
243
239
  # and constrains are created (indicices, sequences, etc).
244
- #
240
+
245
241
  def create_table(klass)
246
242
  fields = create_fields(klass, TYPEMAP)
247
243
 
@@ -282,6 +278,7 @@ class PsqlBackend < Og::Backend
282
278
  # create the sequence for this table. Even if the table
283
279
  # uses the oids_seq, attempt to create it. This makes
284
280
  # the system more fault tolerant.
281
+
285
282
  begin
286
283
  exec "CREATE SEQUENCE #{klass::DBSEQ}"
287
284
  Logger.info "Created sequence '#{klass::DBSEQ}'."
@@ -336,15 +333,15 @@ class PsqlBackend < Og::Backend
336
333
 
337
334
  end
338
335
 
339
- # Drop the managed object table
340
- #
336
+ # Drop the managed object table.
337
+
341
338
  def drop_table(klass)
342
339
  super
343
340
  exec "DROP SEQUENCE #{klass::DBSEQ}"
344
341
  end
345
342
 
346
343
  # Deserialize one row of the resultset.
347
- #
344
+
348
345
  def deserialize_one(res, klass)
349
346
  return nil unless valid?(res)
350
347
 
@@ -359,9 +356,9 @@ class PsqlBackend < Og::Backend
359
356
  end
360
357
 
361
358
  # Deserialize all rows of the resultset.
362
- #
359
+
363
360
  def deserialize_all(res, klass)
364
- return nil unless valid?(res)
361
+ return [] unless valid?(res)
365
362
 
366
363
  entities = []
367
364
 
@@ -379,11 +376,11 @@ class PsqlBackend < Og::Backend
379
376
  end
380
377
 
381
378
  # Return a single integer value from the resultset.
382
- #
379
+
383
380
  def get_int(res, idx = 0)
384
381
  return res.getvalue(0, idx).to_i
385
382
  end
386
383
 
387
384
  end
388
385
 
389
- end # module
386
+ end
@@ -1,17 +1,15 @@
1
- # code:
2
- # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
5
- # $Id: connection.rb 167 2004-11-23 14:03:10Z gmosx $
1
+ #--
2
+ # George Moschovitis <gm@navel.gr>
3
+ # (c) 2004-2005 Navel, all rights reserved.
4
+ # $Id: connection.rb 248 2005-01-31 13:38:34Z gmosx $
5
+ #++
6
6
 
7
7
  class Og;
8
8
 
9
- require "glue/property"
10
- require "glue/array"
11
- require "glue/time"
9
+ require 'glue/property'
10
+ require 'glue/array'
11
+ require 'glue/time'
12
12
 
13
- # = Connection
14
- #
15
13
  # A Connection to the Database. This file defines the skeleton
16
14
  # functionality. A backend specific implementation file (driver)
17
15
  # implements all methods.
@@ -20,20 +18,23 @@ require "glue/time"
20
18
  #
21
19
  # - support caching.
22
20
  # - support prepared statements.
23
- #
21
+
24
22
  class Connection
25
23
  # The frontend (Og) contains useful strucutres.
24
+
26
25
  attr_reader :og
27
26
 
28
27
  # The backend
28
+
29
29
  attr_reader :db
30
30
 
31
31
  # If set to true, the select methods deserialize the
32
32
  # resultset to create entities.
33
+
33
34
  attr_accessor :deserialize
34
35
 
35
36
  # Initialize a connection to the database
36
- #
37
+
37
38
  def initialize(og)
38
39
  @og = og
39
40
  @db = @og.config[:backend].new(@og.config)
@@ -189,9 +190,8 @@ class Connection
189
190
  # TODO: implement this as stored procedure? naaah.
190
191
  transaction do |tx|
191
192
  tx.exec "DELETE FROM #{klass::DBTABLE} WHERE oid=#{oid}"
192
-
193
- if cascade and klass.respond_to?(:og_descendants)
194
- klass.og_descendants.each do |dclass, linkback|
193
+ if cascade and klass.__meta.include?(:has)
194
+ klass.__meta[:has].each do |dclass, linkback|
195
195
  tx.exec "DELETE FROM #{dclass::DBTABLE} WHERE #{linkback}=#{oid}"
196
196
  end
197
197
  end
@@ -1,20 +1,18 @@
1
- # code:
2
1
  # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
2
+ # (c) 2004-2005 Navel, all rights reserved.
5
3
  # $Id: meta.rb 198 2004-12-22 11:26:59Z gmosx $
6
4
 
7
5
  class Og
8
6
 
9
7
  module Enchant
10
8
 
11
- # Enchant a managed class. Add useful DB related methods to the
12
- # class and its instances.
13
- #
9
+ # Enchant a managed class. Add useful DB related methods
10
+ # to the class and its instances.
11
+
14
12
  def enchant(klass)
15
13
  klass.module_eval <<-"end_eval", __FILE__, __LINE__
16
- def self.create(*params)
17
- obj = #{klass}.new(*params)
14
+ def self.create(*params, &block)
15
+ obj = #{klass}.new(*params, &block)
18
16
  obj.save!
19
17
  end
20
18
 
@@ -26,6 +24,10 @@ module Enchant
26
24
  Og.db.load(oid_or_name, #{klass})
27
25
  end
28
26
 
27
+ def self.get(oid_or_name)
28
+ Og.db.load(oid_or_name, #{klass})
29
+ end
30
+
29
31
  def self.[](oid_or_name)
30
32
  Og.db.load(oid_or_name, #{klass})
31
33
  end
@@ -57,6 +59,11 @@ module Enchant
57
59
  def self.delete(obj_or_oid)
58
60
  Og.db.delete(obj_or_oid, #{klass})
59
61
  end
62
+
63
+ def each(&block)
64
+ all.each(&block)
65
+ end
66
+ include Enumerable
60
67
 
61
68
  def save
62
69
  Og.db << self
@@ -77,4 +84,4 @@ module Enchant
77
84
 
78
85
  end
79
86
 
80
- end # namespace
87
+ end
@@ -1,8 +1,6 @@
1
- # code:
2
1
  # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
5
- # $Id: meta.rb 198 2004-12-22 11:26:59Z gmosx $
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: meta.rb 248 2005-01-31 13:38:34Z gmosx $
6
4
 
7
5
  require 'og/backend'
8
6
  require 'glue/inflector'
@@ -12,18 +10,42 @@ class Og
12
10
  # = MetaUtils
13
11
  #
14
12
  # Some useful meta-language utilities.
15
- #
13
+
16
14
  module MetaUtils # :nodoc: all
17
15
 
18
- # Conver the klass to a string representation
16
+ # Convert the klass to a string representation
19
17
  # The leading module if available is removed.
20
18
  #--
21
19
  # gmosx, FIXME: unify with the ogutils.encode method?
22
20
  #++
21
+
23
22
  def self.expand(klass)
24
23
  return klass.name.gsub(/^.*::/, '').gsub(/::/, '_').downcase
25
24
  end
26
25
 
26
+ # Infer the target klass for a relation. When defining
27
+ # relations, tha target class is typically given. Some times
28
+ # in order to avoid forward declarations a symbol is given instead
29
+ # of a class. Other times on class is given at all, and
30
+ # the inflection mechanism is used to infer the class name.
31
+
32
+ def self.resolve_class(name, klass)
33
+ klass ||= N::Inflector.camelize(name)
34
+
35
+ return klass if klass.is_a?(Class)
36
+
37
+ unless klass.is_a?(String) or klass.is_a?(Symbol)
38
+ raise 'Invalid class definition'
39
+ end
40
+
41
+ unless Object.const_get(klass.intern)
42
+ # Forward declaration.
43
+ Object.class_eval("class #{klass}; end")
44
+ end
45
+
46
+ return Object.const_get(klass)
47
+ end
48
+
27
49
  end
28
50
 
29
51
  # = MetaLanguage
@@ -33,11 +55,12 @@ end
33
55
  # from the excellent ActiveRecord library.
34
56
  #
35
57
  # Many more useful relations will be available soon.
36
- #
58
+
37
59
  module MetaLanguage
38
60
 
39
- # Defines an SQL index.
40
- #
61
+ # Defines an SQL index. Useful for defining indiced
62
+ # over multiple columns.
63
+
41
64
  def sql_index(index, options = {})
42
65
  meta :sql_index, [index, options]
43
66
  end
@@ -56,12 +79,14 @@ module MetaLanguage
56
79
  # prop_accessor Fixnum, :parent_oid
57
80
  # def parent; ... end
58
81
  # def parent=(obj_or_oid); ... end
59
- #
82
+
60
83
  def belongs_to(name, klass, options = {})
61
84
  prop_eval = "prop_accessor Fixnum, :#{name}_oid"
62
85
  prop_eval << ", :sql => '#{options[:sql]}'" if options[:sql]
63
86
  prop_eval << ", :extra_sql => '#{options[:extra_sql]}'" if options[:extra_sql]
64
87
 
88
+ meta :belongs_to, klass
89
+
65
90
  module_eval %{
66
91
  #{prop_eval}
67
92
 
@@ -81,31 +106,31 @@ module MetaLanguage
81
106
  # Example:
82
107
  #
83
108
  # class MyObject
84
- # has_one AnotherObject
109
+ # has_one :child, TheClass
110
+ # has_one :article
85
111
  # end
86
112
  #
87
113
  # creates the code:
88
114
  #
89
115
  # ...
90
- #
91
- def has_one(name, klass, options = {})
116
+
117
+ def has_one(name, klass = nil, options = {})
118
+
92
119
  # linkback is the property of the child object that 'links back'
93
120
  # to this object.
121
+
94
122
  linkback = options[:linkback] || "#{MetaUtils.expand(self)}_oid"
95
123
 
124
+ meta :has, [klass, linkback]
125
+
96
126
  module_eval %{
97
- @@og_descendants ||= {}
98
- @@og_descendants[#{klass}] = :#{linkback}
99
-
100
- unless defined?(og_descendants)
101
- def self.og_descendants
102
- @@og_descendants
103
- end
104
- end
105
-
106
127
  def #{name}(extrasql = nil)
107
- Og.db.select_one("SELECT * FROM #{Og::Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}", #{klass})
128
+ Og.db.select_one("SELECT * FROM #{Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}", #{klass})
108
129
  end
130
+
131
+ def delete_#{name}(extrasql = nil)
132
+ Og.db.exec("DELETE FROM #{Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}")
133
+ end
109
134
  }
110
135
  end
111
136
 
@@ -121,36 +146,37 @@ module MetaLanguage
121
146
  # creates the code:
122
147
  #
123
148
  # def children; ... end
124
- #
149
+
125
150
  def has_many(name, klass, options = {})
126
151
  name_s = N::Inflector.singularize(name.to_s)
127
152
 
128
153
  # linkback is the property of the child object that 'links back'
129
154
  # to this object.
155
+
130
156
  linkback = options[:linkback] || "#{MetaUtils.expand(self)}_oid"
131
157
 
158
+ # keep belongs to metadata, useful for
159
+ # reflection/scaffolding.
160
+
161
+ meta :has, [klass, linkback]
162
+
132
163
  module_eval %{
133
- @@og_descendants ||= {}
134
- @@og_descendants[#{klass}] = :#{linkback}
135
-
136
- unless defined?(og_descendants)
137
- def self.og_descendants
138
- @@og_descendants
139
- end
140
- end
141
-
142
164
  def #{name}(extrasql = nil)
143
- Og.db.select("SELECT * FROM #{Og::Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}", #{klass})
165
+ Og.db.select("SELECT * FROM #{Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}", #{klass})
144
166
  end
145
167
 
146
168
  def #{name}_count(extrasql = nil)
147
- Og.db.count("SELECT COUNT(*) FROM #{Og::Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}")
169
+ Og.db.count("SELECT COUNT(*) FROM #{Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}")
148
170
  end
149
171
 
150
172
  def add_#{name_s}(obj, extra = nil)
151
173
  obj.#{linkback} = @oid
152
174
  obj.save!
153
175
  end
176
+
177
+ def delete_all_#{name}(extrasql = nil)
178
+ Og.db.exec("DELETE FROM #{Backend.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\}")
179
+ end
154
180
  }
155
181
  end
156
182
 
@@ -173,10 +199,10 @@ module MetaLanguage
173
199
  #
174
200
  # category.articles
175
201
  # ...
176
- #
177
202
  #--
178
203
  # FIXME: make more compatible with other enchant methods.
179
204
  #++
205
+
180
206
  def many_to_many(name, klass, options = {})
181
207
  list_o = name.to_s
182
208
  prop_o = N::Inflector.singularize(list_o)
@@ -185,36 +211,38 @@ module MetaLanguage
185
211
 
186
212
  # exit if the class is allready indirectly 'enchanted' from the
187
213
  # other class of the many_to_many relation.
214
+
188
215
  return if self.respond_to?(prop_m)
189
216
 
190
217
  # Add some metadata to the class to allow for automatic join table
191
218
  # calculation.
219
+
192
220
  meta :sql_join, [klass, options]
193
221
 
194
- # gmosx, FIXME: should I update descendants here ?
222
+ meta :many_to_many, klass
223
+ klass.meta :many_to_many, self
195
224
 
196
225
  # enchant this class
197
226
 
198
227
  module_eval %{
199
228
  def #{list_o}(extrasql = nil)
200
- Og.db.select("SELECT d.* FROM #{Og::Backend.table(klass)} AS d, #{Og::Backend.join_table(self, klass)} AS j WHERE j.key1=\#\@oid AND j.key2=d.oid \#\{extrasql\}", #{klass})
229
+ Og.db.select("SELECT d.* FROM #{Backend.table(klass)} AS d, #{Backend.join_table(self, klass)} AS j WHERE j.key1=\#\@oid AND j.key2=d.oid \#\{extrasql\}", #{klass})
201
230
  end
202
231
 
203
232
  def #{list_o}_count(extrasql = nil)
204
- Og.db.select("SELECT COUNT(*) FROM #{Og::Backend.table(klass)} AS d, #{Og::Backend.join_table(self, klass)} AS j WHERE j.key1=\#\@oid AND j.key2=d.oid \#\{extrasql\}", #{klass})
233
+ Og.db.select("SELECT COUNT(*) FROM #{Backend.table(klass)} AS d, #{Backend.join_table(self, klass)} AS j WHERE j.key1=\#\@oid AND j.key2=d.oid \#\{extrasql\}", #{klass})
205
234
  end
206
235
 
207
236
  def add_#{prop_o}(obj, extra = nil)
208
- Og.db.exec("INSERT INTO #{Og::Backend.join_table(self, klass)} (key1, key2) VALUES (\#\@oid, \#\{obj.oid\})")
237
+ Og.db.exec("INSERT INTO #{Backend.join_table(self, klass)} (key1, key2) VALUES (\#\@oid, \#\{obj.oid\})")
209
238
  end
210
239
 
211
- def del_#{prop_o}(obj_or_oid, extra = nil)
212
- Og.db.exec("DELETE FROM #{Og::Backend.join_table(self, klass)} WHERE key2=\#\{obj_or_oid.to_i\}")
240
+ def delete_#{prop_o}(obj_or_oid, extra = nil)
241
+ Og.db.exec("DELETE FROM #{Backend.join_table(self, klass)} WHERE key2=\#\{obj_or_oid.to_i\}")
213
242
  end
214
- alias_method :delete_#{prop_o}, :del_#{prop_o}
215
243
 
216
244
  def clear_#{list_o}
217
- Og.db.exec("DELETE FROM #{Og::Backend.join_table(self, klass)} WHERE key1=\#\@oid")
245
+ Og.db.exec("DELETE FROM #{Backend.join_table(self, klass)} WHERE key1=\#\@oid")
218
246
  end
219
247
  }
220
248
 
@@ -222,40 +250,85 @@ module MetaLanguage
222
250
 
223
251
  klass.module_eval %{
224
252
  def #{list_m}(extrasql = nil)
225
- Og.db.select("SELECT s.* FROM #{Og::Backend.table(self)} AS s, #{Og::Backend.join_table(self, klass)} AS j WHERE j.key2=\#\@oid AND j.key1=s.oid \#\{extrasql\}", #{self})
253
+ Og.db.select("SELECT s.* FROM #{Backend.table(self)} AS s, #{Backend.join_table(self, klass)} AS j WHERE j.key2=\#\@oid AND j.key1=s.oid \#\{extrasql\}", #{self})
226
254
  end
227
255
 
228
256
  def #{list_m}_count(extrasql = nil)
229
- Og.db.select("SELECT COUNT(*) FROM #{Og::Backend.table(self)} AS s, #{Og::Backend.join_table(self, klass)} AS j WHERE j.key2=\#\@oid AND j.key1=s.oid \#\{extrasql\}", #{self})
257
+ Og.db.select("SELECT COUNT(*) FROM #{Backend.table(self)} AS s, #{Backend.join_table(self, klass)} AS j WHERE j.key2=\#\@oid AND j.key1=s.oid \#\{extrasql\}", #{self})
230
258
  end
231
259
 
232
260
  def add_#{prop_m}(obj, extra = nil)
233
- Og.db.exec("INSERT INTO #{Og::Backend.join_table(self, klass)} (key1, key2) VALUES (\#\{obj.oid\}, \#\@oid)")
261
+ Og.db.exec("INSERT INTO #{Backend.join_table(self, klass)} (key1, key2) VALUES (\#\{obj.oid\}, \#\@oid)")
234
262
  end
235
263
 
236
- def del_#{prop_m}(obj_or_oid, extra = nil)
237
- Og.db.exec("DELETE FROM #{Og::Backend.join_table(self, klass)} WHERE key1=\#\{obj_or_oid.to_i\}")
264
+ def delete_#{prop_m}(obj_or_oid, extra = nil)
265
+ Og.db.exec("DELETE FROM #{Backend.join_table(self, klass)} WHERE key1=\#\{obj_or_oid.to_i\}")
238
266
  end
239
- alias_method :delete_#{prop_m}, :del_#{prop_m}
240
267
 
241
268
  def clear_#{list_m}
242
- Og.db.exec("DELETE FROM #{Og::Backend.join_table(self, klass)} WHERE key2=\#\@oid")
269
+ Og.db.exec("DELETE FROM #{Backend.join_table(self, klass)} WHERE key2=\#\@oid")
243
270
  end
244
271
  }
245
272
  end
246
273
  alias :has_and_belongs_to_many :many_to_many
247
274
 
275
+ # Implements a 'refers_to' relation.
276
+ # This is a one-way version of the 'has_one'/'belongs_to'
277
+ # relations. The target object cannot link back to the source
278
+ # object.
279
+ # This is in fact EXACTLY the same as belongs_to with a
280
+ # different name (!!!!)
281
+ #
282
+ # Automatically enchants the calling class with helper methods.
283
+ #
284
+ #
285
+ # Example:
286
+ #
287
+ # class MyObject
288
+ # refers_to article, Article
289
+ # end
290
+ #
291
+ # creates the code:
292
+ #
293
+ # prop_accessor Fixnum, :article_oid
294
+ # def article; ... end
295
+ # def article=(obj_or_oid); ... end
296
+
297
+ def refers_to(name, klass, options = {})
298
+ prop_eval = "prop_accessor Fixnum, :#{name}_oid"
299
+ prop_eval << ", :sql => '#{options[:sql]}'" if options[:sql]
300
+ prop_eval << ", :extra_sql => '#{options[:extra_sql]}'" if options[:extra_sql]
301
+
302
+ meta :refers_to, klass
303
+ klass.meta :has, [self, "#{name}_oid"]
304
+
305
+ module_eval %{
306
+ #{prop_eval}
307
+
308
+ def #{name}
309
+ Og.db.load_by_oid(@#{name}_oid, #{klass})
310
+ end
311
+
312
+ def #{name}=(obj_or_oid)
313
+ @#{name}_oid = obj_or_oid.to_i
314
+ end
315
+ }
316
+ end
317
+
248
318
  end
249
319
 
250
- end # namespace
320
+ end
251
321
 
252
322
  # Include the meta-language extensions into Module. If the flag is
253
323
  # false the developer is responsible for including the MetaLanguage
254
324
  # module where needed.
255
- #
325
+ #
326
+ # By default this is FALSE, to avoid polution of the Module object.
327
+ # However if you include a prop_accessor or a managed Mixin in your
328
+ # object MetaLanguage gets automatically extended in the class.
329
+
256
330
  if Og.include_meta_language
257
331
  class Module # :nodoc: all
258
332
  include Og::MetaLanguage
259
333
  end
260
334
  end
261
-