og 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/CHANGELOG +796 -664
  2. data/INSTALL +24 -24
  3. data/README +39 -32
  4. data/Rakefile +41 -42
  5. data/benchmark/bench.rb +36 -36
  6. data/doc/AUTHORS +15 -12
  7. data/doc/LICENSE +3 -3
  8. data/doc/RELEASES +311 -243
  9. data/doc/config.txt +1 -1
  10. data/examples/mysql_to_psql.rb +15 -15
  11. data/examples/run.rb +92 -92
  12. data/install.rb +7 -17
  13. data/lib/og.rb +76 -75
  14. data/lib/og/collection.rb +203 -160
  15. data/lib/og/entity.rb +168 -169
  16. data/lib/og/errors.rb +5 -5
  17. data/lib/og/manager.rb +179 -178
  18. data/lib/og/mixin/hierarchical.rb +107 -107
  19. data/lib/og/mixin/optimistic_locking.rb +36 -36
  20. data/lib/og/mixin/orderable.rb +148 -148
  21. data/lib/og/mixin/timestamped.rb +8 -8
  22. data/lib/og/mixin/tree.rb +124 -124
  23. data/lib/og/relation.rb +237 -213
  24. data/lib/og/relation/belongs_to.rb +5 -5
  25. data/lib/og/relation/has_many.rb +60 -58
  26. data/lib/og/relation/joins_many.rb +93 -47
  27. data/lib/og/relation/refers_to.rb +25 -21
  28. data/lib/og/store.rb +210 -207
  29. data/lib/og/store/filesys.rb +79 -79
  30. data/lib/og/store/kirby.rb +263 -258
  31. data/lib/og/store/memory.rb +261 -261
  32. data/lib/og/store/mysql.rb +288 -284
  33. data/lib/og/store/psql.rb +261 -244
  34. data/lib/og/store/sql.rb +873 -720
  35. data/lib/og/store/sqlite.rb +177 -175
  36. data/lib/og/store/sqlserver.rb +204 -214
  37. data/lib/og/types.rb +1 -1
  38. data/lib/og/validation.rb +57 -57
  39. data/lib/vendor/mysql.rb +376 -376
  40. data/lib/vendor/mysql411.rb +10 -10
  41. data/test/og/mixin/tc_hierarchical.rb +59 -59
  42. data/test/og/mixin/tc_optimistic_locking.rb +40 -40
  43. data/test/og/mixin/tc_orderable.rb +67 -67
  44. data/test/og/mixin/tc_timestamped.rb +19 -19
  45. data/test/og/store/tc_filesys.rb +46 -46
  46. data/test/og/tc_inheritance.rb +81 -81
  47. data/test/og/tc_join.rb +67 -0
  48. data/test/og/tc_polymorphic.rb +49 -49
  49. data/test/og/tc_relation.rb +57 -30
  50. data/test/og/tc_select.rb +49 -0
  51. data/test/og/tc_store.rb +345 -337
  52. data/test/og/tc_types.rb +11 -11
  53. metadata +11 -18
@@ -1,8 +1,8 @@
1
1
  begin
2
- require 'sqlite3'
2
+ require 'sqlite3'
3
3
  rescue Object => ex
4
- Logger.error 'Ruby-Sqlite3 bindings are not installed!'
5
- Logger.error ex
4
+ Logger.error 'Ruby-Sqlite3 bindings are not installed!'
5
+ Logger.error ex
6
6
  end
7
7
 
8
8
  require 'fileutils'
@@ -13,19 +13,21 @@ require 'og/store/sql'
13
13
  # more compatible with Og.
14
14
 
15
15
  class SQLite3::ResultSet
16
- alias_method :blank?, :eof?
17
-
18
- def each_row
19
- each do |row|
20
- yield(row, 0)
21
- end
22
- end
23
-
24
- def first_value
25
- val = self.next[0]
26
- close
27
- return val
28
- end
16
+ alias_method :blank?, :eof?
17
+
18
+ def each_row
19
+ each do |row|
20
+ yield(row, 0)
21
+ end
22
+ end
23
+
24
+ def first_value
25
+ val = self.next[0]
26
+ close
27
+ return val
28
+ end
29
+
30
+ alias_method :fields, :columns
29
31
  end
30
32
 
31
33
  module Og
@@ -36,171 +38,171 @@ module Og
36
38
 
37
39
  class SqliteStore < SqlStore
38
40
 
39
- # Override if needed.
40
-
41
- def self.db_filename(options)
42
- "#{options[:name]}.db"
43
- end
44
-
45
- def self.destroy(options)
46
- begin
47
- FileUtils.rm(db_filename(options))
48
- super
49
- rescue Object
50
- Logger.info "Cannot drop '#{options[:name]}'!"
51
- end
52
- end
53
-
54
- def initialize(options)
55
- super
56
- @conn = SQLite3::Database.new(self.class.db_filename(options))
57
- end
58
-
59
- def close
60
- @conn.close
61
- super
62
- end
63
-
64
- def enchant(klass, manager)
65
- klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
66
- super
67
- end
68
-
69
- def query(sql)
70
- Logger.debug sql if $DBG
71
- return @conn.query(sql)
72
- rescue => ex
73
- handle_sql_exception(ex, sql)
74
- end
75
-
76
- def exec(sql)
77
- Logger.debug sql if $DBG
78
- @conn.query(sql).close
79
- rescue => ex
80
- handle_sql_exception(ex, sql)
81
- end
82
-
83
- def start
84
- @conn.transaction if @transaction_nesting < 1
85
- @transaction_nesting += 1
86
- end
87
-
88
- def commit
89
- @transaction_nesting -= 1
90
- @conn.commit if @transaction_nesting < 1
91
- end
92
-
93
- def rollback
94
- @transaction_nesting -= 1
95
- @conn.rollback if @transaction_nesting < 1
96
- end
41
+ # Override if needed.
42
+
43
+ def self.db_filename(options)
44
+ "#{options[:name]}.db"
45
+ end
46
+
47
+ def self.destroy(options)
48
+ begin
49
+ FileUtils.rm(db_filename(options))
50
+ super
51
+ rescue Object
52
+ Logger.info "Cannot drop '#{options[:name]}'!"
53
+ end
54
+ end
55
+
56
+ def initialize(options)
57
+ super
58
+ @conn = SQLite3::Database.new(self.class.db_filename(options))
59
+ end
60
+
61
+ def close
62
+ @conn.close
63
+ super
64
+ end
65
+
66
+ def enchant(klass, manager)
67
+ klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
68
+ super
69
+ end
70
+
71
+ def query(sql)
72
+ Logger.debug sql if $DBG
73
+ return @conn.query(sql)
74
+ rescue => ex
75
+ handle_sql_exception(ex, sql)
76
+ end
77
+
78
+ def exec(sql)
79
+ Logger.debug sql if $DBG
80
+ @conn.query(sql).close
81
+ rescue => ex
82
+ handle_sql_exception(ex, sql)
83
+ end
84
+
85
+ def start
86
+ @conn.transaction if @transaction_nesting < 1
87
+ @transaction_nesting += 1
88
+ end
89
+
90
+ def commit
91
+ @transaction_nesting -= 1
92
+ @conn.commit if @transaction_nesting < 1
93
+ end
94
+
95
+ def rollback
96
+ @transaction_nesting -= 1
97
+ @conn.rollback if @transaction_nesting < 1
98
+ end
97
99
 
98
100
  def last_insert_rowid
99
- conn.query("SELECT last_insert_rowid()").first_value.to_i
100
- end
101
+ conn.query("SELECT last_insert_rowid()").first_value.to_i
102
+ end
101
103
 
102
- def sql_update(sql)
103
- exec(sql)
104
- @conn.changes
105
- end
104
+ def sql_update(sql)
105
+ exec(sql)
106
+ @conn.changes
107
+ end
106
108
 
107
109
  private
108
110
 
109
- def create_table(klass)
110
- fields = fields_for_class(klass)
111
-
112
- sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
113
-
114
- # Create table constrains.
115
-
116
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
117
- sql << ", #{constrains.join(', ')}"
118
- end
119
-
120
- sql << ");"
121
-
122
- # Create indices.
123
-
124
- if klass.__meta and indices = klass.__meta[:index]
125
- for data in indices
126
- idx, options = *data
127
- idx = idx.to_s
128
- pre_sql, post_sql = options[:pre], options[:post]
129
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
130
- sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
131
- end
132
- end
133
-
134
- begin
135
- @conn.query(sql).close
136
- Logger.info "Created table '#{klass::OGTABLE}'."
137
- rescue Object => ex
138
- # gmosx: any idea how to better test this?
139
- if ex.to_s =~ /table .* already exists/i
140
- Logger.debug 'Table already exists' if $DBG
141
- return
142
- else
143
- raise
144
- end
145
- end
146
-
147
- # Create join tables if needed. Join tables are used in
148
- # 'many_to_many' relations.
149
-
150
- if klass.__meta and join_tables = klass.__meta[:join_tables]
151
- for join_table in join_tables
152
- begin
153
- @conn.query("CREATE TABLE #{join_table} (key1 integer NOT NULL, key2 integer NOT NULL)").close
154
- @conn.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").close
155
- @conn.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").close
156
- rescue Object => ex
157
- # gmosx: any idea how to better test this?
158
- if ex.to_s =~ /table .* already exists/i
159
- Logger.debug 'Join table already exists' if $DBG
160
- else
161
- raise
162
- end
163
- end
164
- end
165
- end
166
- end
167
-
168
- def create_field_map(klass)
169
- res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
170
- map = {}
171
-
172
- fields = res.columns
173
-
174
- fields.size.times do |i|
175
- map[fields[i].intern] = i
176
- end
177
-
178
- return map
179
- ensure
180
- res.close if res
181
- end
182
-
183
- def eval_og_insert(klass)
184
- pk = klass.primary_key.first
185
- props = klass.properties
186
- values = props.collect { |p| write_prop(p) }.join(',')
187
-
188
- if klass.metadata.superclass or klass.metadata.subclasses
189
- props << Property.new(:ogtype, String)
190
- values << ", '#{klass}'"
191
- end
192
-
193
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
194
-
195
- klass.class_eval %{
196
- def og_insert(store)
197
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
198
- store.query("#{sql}").close
199
- @#{pk} = store.last_insert_rowid
200
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
201
- end
202
- }
203
- end
111
+ def create_table(klass)
112
+ fields = fields_for_class(klass)
113
+
114
+ sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
115
+
116
+ # Create table constrains.
117
+
118
+ if klass.__meta and constrains = klass.__meta[:sql_constrain]
119
+ sql << ", #{constrains.join(', ')}"
120
+ end
121
+
122
+ sql << ");"
123
+
124
+ # Create indices.
125
+
126
+ if klass.__meta and indices = klass.__meta[:index]
127
+ for data in indices
128
+ idx, options = *data
129
+ idx = idx.to_s
130
+ pre_sql, post_sql = options[:pre], options[:post]
131
+ idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
132
+ sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
133
+ end
134
+ end
135
+
136
+ begin
137
+ @conn.query(sql).close
138
+ Logger.info "Created table '#{klass::OGTABLE}'."
139
+ rescue Object => ex
140
+ # gmosx: any idea how to better test this?
141
+ if ex.to_s =~ /table .* already exists/i
142
+ Logger.debug 'Table already exists' if $DBG
143
+ return
144
+ else
145
+ raise
146
+ end
147
+ end
148
+
149
+ # Create join tables if needed. Join tables are used in
150
+ # 'many_to_many' relations.
151
+
152
+ if klass.__meta and join_tables = klass.__meta[:join_tables]
153
+ for info in join_tables
154
+ begin
155
+ create_join_table_sql(info).each do |sql|
156
+ @conn.query(sql).close
157
+ end
158
+ rescue Object => ex
159
+ # gmosx: any idea how to better test this?
160
+ if ex.to_s =~ /table .* already exists/i
161
+ Logger.debug 'Join table already exists' if $DBG
162
+ else
163
+ raise
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ def create_field_map(klass)
171
+ res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
172
+ map = {}
173
+
174
+ fields = res.columns
175
+
176
+ res.fields.size.times do |i|
177
+ map[fields[i].intern] = i
178
+ end
179
+
180
+ return map
181
+ ensure
182
+ res.close if res
183
+ end
184
+
185
+ def eval_og_insert(klass)
186
+ pk = klass.primary_key.first
187
+ props = klass.properties
188
+ values = props.collect { |p| write_prop(p) }.join(',')
189
+
190
+ if klass.metadata.superclass or klass.metadata.subclasses
191
+ props << Property.new(:ogtype, String)
192
+ values << ", '#{klass}'"
193
+ end
194
+
195
+ sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
196
+
197
+ klass.class_eval %{
198
+ def og_insert(store)
199
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
200
+ store.query("#{sql}").close
201
+ @#{pk} = store.last_insert_rowid
202
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
203
+ end
204
+ }
205
+ end
204
206
 
205
207
  end
206
208
 
@@ -3,10 +3,10 @@
3
3
  # DO NOT USE YET!
4
4
 
5
5
  begin
6
- require 'dbi'
6
+ require 'dbi'
7
7
  rescue Object => ex
8
- Logger.error 'Ruby-DBI bindings not present or ADO support not available.'
9
- Logger.error ex
8
+ Logger.error 'Ruby-DBI bindings not present or ADO support not available.'
9
+ Logger.error ex
10
10
  end
11
11
 
12
12
  require 'og/store/sql'
@@ -15,55 +15,55 @@ require 'og/store/sql'
15
15
  # more compatible with Og.
16
16
  =begin
17
17
  class Sqlserver::Result
18
- def blank?
19
- 0 == num_rows
20
- end
18
+ def blank?
19
+ 0 == num_rows
20
+ end
21
21
 
22
- alias_method :next, :fetch_row
22
+ alias_method :next, :fetch_row
23
23
 
24
- def each_row
25
- each do |row|
26
- yield(row, 0)
27
- end
28
- end
29
-
30
- def first_value
31
- val = fetch_row[0]
32
- free
33
- return val
34
- end
35
-
36
- alias_method :close, :free
24
+ def each_row
25
+ each do |row|
26
+ yield(row, 0)
27
+ end
28
+ end
29
+
30
+ def first_value
31
+ val = fetch_row[0]
32
+ free
33
+ return val
34
+ end
35
+
36
+ alias_method :close, :free
37
37
  end
38
38
  =end
39
39
 
40
40
  module Og
41
41
 
42
42
  module SqlserverUtils
43
- include SqlUtils
44
-
45
- def escape(str)
46
- return nil unless str
47
- return Sqlserver.quote(str)
48
- end
49
-
50
- def quote(val)
51
- case val
52
- when Fixnum, Integer, Float
53
- val ? val.to_s : 'NULL'
54
- when String
55
- val ? "'#{escape(val)}'" : 'NULL'
56
- when Time
57
- val ? "'#{timestamp(val)}'" : 'NULL'
58
- when Date
59
- val ? "'#{date(val)}'" : 'NULL'
60
- when TrueClass
61
- val ? "'1'" : 'NULL'
62
- else
63
- # gmosx: keep the '' for nil symbols.
64
- val ? escape(val.to_yaml) : ''
65
- end
66
- end
43
+ include SqlUtils
44
+
45
+ def escape(str)
46
+ return nil unless str
47
+ return Sqlserver.quote(str)
48
+ end
49
+
50
+ def quote(val)
51
+ case val
52
+ when Fixnum, Integer, Float
53
+ val ? val.to_s : 'NULL'
54
+ when String
55
+ val ? "'#{escape(val)}'" : 'NULL'
56
+ when Time
57
+ val ? "'#{timestamp(val)}'" : 'NULL'
58
+ when Date
59
+ val ? "'#{date(val)}'" : 'NULL'
60
+ when TrueClass
61
+ val ? "'1'" : 'NULL'
62
+ else
63
+ # gmosx: keep the '' for nil symbols.
64
+ val ? escape(val.to_yaml) : ''
65
+ end
66
+ end
67
67
  end
68
68
 
69
69
  # A Store that persists objects into a Sqlserver database.
@@ -71,195 +71,185 @@ end
71
71
  # for SqlStore and Store.
72
72
 
73
73
  class SqlserverStore < SqlStore
74
- extend SqlserverUtils
75
- include SqlserverUtils
74
+ extend SqlserverUtils
75
+ include SqlserverUtils
76
76
 
77
- def self.create(options)
78
- raise 'Not implemented'
79
- end
77
+ def self.create(options)
78
+ raise 'Not implemented'
79
+ end
80
80
 
81
- def self.destroy(options)
82
- raise 'Not implemented'
83
- end
81
+ def self.destroy(options)
82
+ raise 'Not implemented'
83
+ end
84
84
 
85
- def initialize(options)
86
- super
85
+ def initialize(options)
86
+ super
87
87
 
88
- begin
89
- @conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{options[:address]};Initial Catalog=#{options[:name]};User Id=#{options[:user]};Password=#{options[:password]};")
90
- rescue => ex
91
- # gmosx, FIXME: drak, fix this!
92
- if ex.to_s =~ /database .* does not exist/i
93
- Logger.info "Database '#{options[:name]}' not found!"
94
- self.class.create(options)
95
- retry
96
- end
97
- raise
98
- end
99
- end
88
+ begin
89
+ @conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{options[:address]};Initial Catalog=#{options[:name]};User Id=#{options[:user]};Password=#{options[:password]};")
90
+ rescue => ex
91
+ # gmosx, FIXME: drak, fix this!
92
+ if ex.to_s =~ /database .* does not exist/i
93
+ Logger.info "Database '#{options[:name]}' not found!"
94
+ self.class.create(options)
95
+ retry
96
+ end
97
+ raise
98
+ end
99
+ end
100
100
 
101
- def close
102
- @conn.disconnect
103
- super
104
- end
101
+ def close
102
+ @conn.disconnect
103
+ super
104
+ end
105
105
 
106
- def enchant(klass, manager)
107
- klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
108
- super
109
- end
110
-
111
- def query(sql)
112
- # Logger.debug sql if $DBG
113
- return @conn.select_all(sql)
114
- rescue => ex
115
- handle_sql_exception(ex, sql)
116
- end
106
+ def enchant(klass, manager)
107
+ klass.property :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
108
+ super
109
+ end
110
+
111
+ def query(sql)
112
+ # Logger.debug sql if $DBG
113
+ return @conn.select_all(sql)
114
+ rescue => ex
115
+ handle_sql_exception(ex, sql)
116
+ end
117
117
 
118
- def exec(sql)
119
- # Logger.debug sql if $DBG
120
- @conn.execute(sql).finish
121
- rescue => ex
122
- handle_sql_exception(ex, sql)
123
- end
118
+ def exec(sql)
119
+ # Logger.debug sql if $DBG
120
+ @conn.execute(sql).finish
121
+ rescue => ex
122
+ handle_sql_exception(ex, sql)
123
+ end
124
124
 
125
125
  private
126
126
 
127
- def create_table(klass)
128
- fields = fields_for_class(klass)
127
+ def create_table(klass)
128
+ fields = fields_for_class(klass)
129
129
 
130
- sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
131
-
132
- # Create table constrains.
133
-
134
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
135
- sql << ", #{constrains.join(', ')}"
136
- end
137
-
138
- sql << ");"
139
-
140
- # Create indices.
141
-
142
- if klass.__meta and indices = klass.__meta[:index]
143
- for data in indices
144
- idx, options = *data
145
- idx = idx.to_s
146
- pre_sql, post_sql = options[:pre], options[:post]
147
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
148
- sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
149
- end
150
- end
151
-
152
- conn.query_with_result = false
130
+ sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
131
+
132
+ # Create table constrains.
133
+
134
+ if klass.__meta and constrains = klass.__meta[:sql_constrain]
135
+ sql << ", #{constrains.join(', ')}"
136
+ end
137
+
138
+ sql << ");"
139
+
140
+ # Create indices.
141
+
142
+ if klass.__meta and indices = klass.__meta[:index]
143
+ for data in indices
144
+ idx, options = *data
145
+ idx = idx.to_s
146
+ pre_sql, post_sql = options[:pre], options[:post]
147
+ idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
148
+ sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
149
+ end
150
+ end
151
+
152
+ conn.query_with_result = false
153
153
 
154
- begin
155
- conn.query(sql)
156
- Logger.info "Created table '#{klass::OGTABLE}'."
157
- rescue => ex
158
- # gmosx: any idea how to better test this?
159
- if ex.errno == 1050 # table already exists.
160
- Logger.debug 'Table already exists' if $DBG
161
- return
162
- else
163
- raise
164
- end
165
- end
166
- =begin
167
- # Create join tables if needed. Join tables are used in
168
- # 'many_to_many' relations.
169
-
170
- if klass.__meta and joins = klass.__meta[:sql_join]
171
- for data in joins
172
- # the class to join to and some options.
173
- join_name, join_class, options = *data
174
-
175
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
176
- join_src = "#{self.class.encode(klass)}_oid"
177
- join_dst = "#{self.class.encode(join_class)}_oid"
154
+ begin
155
+ conn.query(sql)
156
+ Logger.info "Created table '#{klass::OGTABLE}'."
157
+ rescue => ex
158
+ # gmosx: any idea how to better test this?
159
+ if ex.errno == 1050 # table already exists.
160
+ Logger.debug 'Table already exists' if $DBG
161
+ return
162
+ else
163
+ raise
164
+ end
165
+ end
178
166
 
179
- begin
180
- conn.exec("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").clear
181
- conn.exec("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").clear
182
- conn.exec("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").clear
183
- rescue => ex
184
- # gmosx: any idea how to better test this?
185
- if ex.errno == 1050 # table already exists.
186
- Logger.debug 'Join table already exists'
187
- else
188
- raise
189
- end
190
- end
191
- end
192
- end
193
- =end
194
- end
167
+ # Create join tables if needed. Join tables are used in
168
+ # 'many_to_many' relations.
169
+
170
+ if klass.__meta and join_tables = klass.__meta[:join_tables]
171
+ for join_table in join_tables
172
+ begin
173
+ conn.query(create_join_table_sql(join_table))
174
+ rescue => ex
175
+ # gmosx: any idea how to better test this?
176
+ if ex.errno == 1050 # table already exists.
177
+ Logger.debug 'Join table already exists'
178
+ else
179
+ raise
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
195
185
 
196
- def create_field_map(klass)
197
- conn.query_with_result = true
198
- res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
199
- map = {}
186
+ def create_field_map(klass)
187
+ conn.query_with_result = true
188
+ res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
189
+ map = {}
200
190
 
201
- res.num_fields.times do |i|
202
- map[res.fetch_field.name.intern] = i
203
- end
191
+ res.num_fields.times do |i|
192
+ map[res.fetch_field.name.intern] = i
193
+ end
204
194
 
205
- return map
206
- ensure
207
- res.close if res
208
- end
195
+ return map
196
+ ensure
197
+ res.close if res
198
+ end
209
199
 
210
- def write_prop(p)
211
- if p.klass.ancestors.include?(Integer)
212
- return "#\{@#{p.symbol} || 'NULL'\}"
213
- elsif p.klass.ancestors.include?(Float)
214
- return "#\{@#{p.symbol} || 'NULL'\}"
215
- elsif p.klass.ancestors.include?(String)
216
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
217
- elsif p.klass.ancestors.include?(Time)
218
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
219
- elsif p.klass.ancestors.include?(Date)
220
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
221
- elsif p.klass.ancestors.include?(TrueClass)
222
- return "#\{@#{p.symbol} ? \"'1'\" : 'NULL' \}"
223
- else
224
- # gmosx: keep the '' for nil symbols.
225
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
226
- end
227
- end
228
-
229
- def read_prop(p, col)
230
- if p.klass.ancestors.include?(Integer)
231
- return "res[#{col} + offset].to_i"
232
- elsif p.klass.ancestors.include?(Float)
233
- return "res[#{col} + offset].to_f"
234
- elsif p.klass.ancestors.include?(String)
235
- return "res[#{col} + offset]"
236
- elsif p.klass.ancestors.include?(Time)
237
- return "#{self.class}.parse_timestamp(res[#{col} + offset])"
238
- elsif p.klass.ancestors.include?(Date)
239
- return "#{self.class}.parse_date(res[#{col} + offset])"
240
- elsif p.klass.ancestors.include?(TrueClass)
241
- return "('0' != res[#{col} + offset])"
242
- else
243
- return "YAML.load(res[#{col} + offset])"
244
- end
245
- end
200
+ def write_prop(p)
201
+ if p.klass.ancestors.include?(Integer)
202
+ return "#\{@#{p.symbol} || 'NULL'\}"
203
+ elsif p.klass.ancestors.include?(Float)
204
+ return "#\{@#{p.symbol} || 'NULL'\}"
205
+ elsif p.klass.ancestors.include?(String)
206
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
207
+ elsif p.klass.ancestors.include?(Time)
208
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
209
+ elsif p.klass.ancestors.include?(Date)
210
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
211
+ elsif p.klass.ancestors.include?(TrueClass)
212
+ return "#\{@#{p.symbol} ? \"'1'\" : 'NULL' \}"
213
+ else
214
+ # gmosx: keep the '' for nil symbols.
215
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
216
+ end
217
+ end
218
+
219
+ def read_prop(p, col)
220
+ if p.klass.ancestors.include?(Integer)
221
+ return "res[#{col} + offset].to_i"
222
+ elsif p.klass.ancestors.include?(Float)
223
+ return "res[#{col} + offset].to_f"
224
+ elsif p.klass.ancestors.include?(String)
225
+ return "res[#{col} + offset]"
226
+ elsif p.klass.ancestors.include?(Time)
227
+ return "#{self.class}.parse_timestamp(res[#{col} + offset])"
228
+ elsif p.klass.ancestors.include?(Date)
229
+ return "#{self.class}.parse_date(res[#{col} + offset])"
230
+ elsif p.klass.ancestors.include?(TrueClass)
231
+ return "('0' != res[#{col} + offset])"
232
+ else
233
+ return "YAML.load(res[#{col} + offset])"
234
+ end
235
+ end
246
236
 
247
- def eval_og_insert(klass)
248
- props = klass.properties
249
- values = props.collect { |p| write_prop(p) }.join(',')
250
-
251
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
237
+ def eval_og_insert(klass)
238
+ props = klass.properties
239
+ values = props.collect { |p| write_prop(p) }.join(',')
240
+
241
+ sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| p.symbol.to_s}.join(',')}) VALUES (#{values})"
252
242
 
253
- klass.class_eval %{
254
- def og_insert(store)
255
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
256
- store.conn.query_with_result = false
257
- store.conn.query "#{sql}"
258
- @#{klass.pk_symbol} = store.conn.insert_id
259
- #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
260
- end
261
- }
262
- end
243
+ klass.class_eval %{
244
+ def og_insert(store)
245
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
246
+ store.conn.query_with_result = false
247
+ store.conn.query "#{sql}"
248
+ @#{klass.pk_symbol} = store.conn.insert_id
249
+ #{Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
250
+ end
251
+ }
252
+ end
263
253
 
264
254
  end
265
255