og 0.20.0 → 0.21.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.
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