og 0.16.0 → 0.17.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 (69) hide show
  1. data/CHANGELOG +485 -0
  2. data/README +35 -12
  3. data/Rakefile +4 -7
  4. data/benchmark/bench.rb +1 -1
  5. data/doc/AUTHORS +3 -3
  6. data/doc/RELEASES +153 -2
  7. data/doc/config.txt +0 -7
  8. data/doc/tutorial.txt +7 -0
  9. data/examples/README +5 -0
  10. data/examples/mysql_to_psql.rb +25 -50
  11. data/examples/run.rb +62 -77
  12. data/install.rb +1 -1
  13. data/lib/og.rb +45 -106
  14. data/lib/og/collection.rb +156 -0
  15. data/lib/og/entity.rb +131 -0
  16. data/lib/og/errors.rb +10 -15
  17. data/lib/og/manager.rb +115 -0
  18. data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
  19. data/lib/og/{mixins → mixin}/orderable.rb +35 -35
  20. data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
  21. data/lib/og/{mixins → mixin}/tree.rb +0 -4
  22. data/lib/og/relation.rb +178 -0
  23. data/lib/og/relation/belongs_to.rb +14 -0
  24. data/lib/og/relation/has_many.rb +62 -0
  25. data/lib/og/relation/has_one.rb +17 -0
  26. data/lib/og/relation/joins_many.rb +69 -0
  27. data/lib/og/relation/many_to_many.rb +17 -0
  28. data/lib/og/relation/refers_to.rb +31 -0
  29. data/lib/og/store.rb +223 -0
  30. data/lib/og/store/filesys.rb +113 -0
  31. data/lib/og/store/madeleine.rb +4 -0
  32. data/lib/og/store/memory.rb +291 -0
  33. data/lib/og/store/mysql.rb +283 -0
  34. data/lib/og/store/psql.rb +238 -0
  35. data/lib/og/store/sql.rb +599 -0
  36. data/lib/og/store/sqlite.rb +190 -0
  37. data/lib/og/store/sqlserver.rb +262 -0
  38. data/lib/og/types.rb +19 -0
  39. data/lib/og/validation.rb +0 -4
  40. data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
  41. data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
  42. data/test/og/mixin/tc_timestamped.rb +38 -0
  43. data/test/og/store/tc_filesys.rb +71 -0
  44. data/test/og/tc_relation.rb +36 -0
  45. data/test/og/tc_store.rb +290 -0
  46. data/test/og/tc_types.rb +21 -0
  47. metadata +54 -40
  48. data/examples/mock_example.rb +0 -50
  49. data/lib/og/adapters/base.rb +0 -706
  50. data/lib/og/adapters/filesys.rb +0 -117
  51. data/lib/og/adapters/mysql.rb +0 -350
  52. data/lib/og/adapters/oracle.rb +0 -368
  53. data/lib/og/adapters/psql.rb +0 -272
  54. data/lib/og/adapters/sqlite.rb +0 -265
  55. data/lib/og/adapters/sqlserver.rb +0 -356
  56. data/lib/og/database.rb +0 -290
  57. data/lib/og/enchant.rb +0 -149
  58. data/lib/og/meta.rb +0 -407
  59. data/lib/og/testing/mock.rb +0 -165
  60. data/lib/og/typemacros.rb +0 -24
  61. data/test/og/adapters/tc_filesys.rb +0 -83
  62. data/test/og/adapters/tc_sqlite.rb +0 -86
  63. data/test/og/adapters/tc_sqlserver.rb +0 -96
  64. data/test/og/tc_automanage.rb +0 -46
  65. data/test/og/tc_lifecycle.rb +0 -105
  66. data/test/og/tc_many_to_many.rb +0 -61
  67. data/test/og/tc_meta.rb +0 -55
  68. data/test/og/tc_validation.rb +0 -89
  69. data/test/tc_og.rb +0 -364
@@ -1,265 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: sqlite.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- begin
6
- require 'sqlite3'
7
- rescue Object => ex
8
- Logger.error 'Ruby-Sqlite3 bindings are not installed!'
9
- Logger.error ex
10
- end
11
-
12
- require 'fileutils'
13
-
14
- require 'og/adapters/base'
15
-
16
- module Og
17
-
18
- # The SQLite adapter. This adapter communicates with
19
- # an SQLite3 rdbms. For extra documentation see
20
- # lib/og/adapter.rb
21
-
22
- class SqliteAdapter < Adapter
23
-
24
- def drop_db(database, user = nil, password = nil)
25
- begin
26
- FileUtils.rm("#{database}.db")
27
- super
28
- rescue
29
- Logger.error "Cannot drop '#{database}'!"
30
- end
31
- end
32
- =begin
33
- def write_prop(p)
34
- if p.klass.ancestors.include?(Integer)
35
- return "@#{p.symbol}"
36
- elsif p.klass.ancestors.include?(Float)
37
- return "@#{p.symbol}"
38
- elsif p.klass.ancestors.include?(String)
39
- return "#{self.class}.escape(@#{p.symbol})"
40
- elsif p.klass.ancestors.include?(Time)
41
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
42
- elsif p.klass.ancestors.include?(Date)
43
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
44
- elsif p.klass.ancestors.include?(TrueClass)
45
- return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
46
- else
47
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
48
- end
49
- end
50
- =end
51
- def insert_code(klass, db)
52
- props = props_for_insert(klass)
53
- values = props.collect { |p| write_prop(p) }.join(',')
54
-
55
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values})"
56
-
57
- %{
58
- conn.store.query("#{sql}").close
59
- @oid = conn.store.last_insert_row_id
60
- }
61
- =begin
62
- props = props_for_insert(klass)
63
-
64
- placeholders = Array.new(props.size, '?').join(',')
65
- values = props.collect { |p| write_prop(p) }.join(',')
66
-
67
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{placeholders})"
68
- klass.class_eval %{
69
- cattr_accessor :og_insert_statement
70
- }
71
-
72
- klass.og_insert_statement = db.prepare(sql)
73
-
74
- %{
75
- #{pre_cb}
76
- @@og_insert_statement.execute(#{values})
77
- @oid = conn.store.last_insert_row_id
78
- #{post_cb}
79
- }
80
- =end
81
- end
82
-
83
- def new_connection(db)
84
- return SqliteConnection.new(db)
85
- end
86
-
87
- def calc_field_index(klass, db)
88
- res = db.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
89
- meta = db.managed_classes[klass]
90
-
91
- columns = res.columns
92
-
93
- for idx in (0...columns.size)
94
- meta.field_index[columns[idx]] = idx
95
- end
96
-
97
- ensure
98
- res.close
99
- end
100
-
101
- def create_table(klass, db)
102
- conn = db.get_connection
103
-
104
- fields = create_fields(klass)
105
-
106
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
107
-
108
- # Create table constrains
109
-
110
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
111
- sql << ", #{constrains.join(', ')}"
112
- end
113
-
114
- sql << ");"
115
-
116
- # Create indices
117
-
118
- if klass.__meta and indices = klass.__meta[:sql_index]
119
- for data in indices
120
- idx, options = *data
121
- idx = idx.to_s
122
- pre_sql, post_sql = options[:pre], options[:post]
123
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
124
- sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
125
- end
126
- end
127
-
128
- begin
129
- conn.store.query(sql).close
130
- Logger.info "Created table '#{klass::DBTABLE}'."
131
- rescue Exception => ex
132
- # gmosx: any idea how to better test this?
133
- if ex.to_s =~ /table .* already exists/i
134
- Logger.debug "Table already exists" if $DBG
135
- return
136
- else
137
- raise
138
- end
139
- end
140
-
141
- # Create join tables if needed. Join tables are used in
142
- # 'many_to_many' relations.
143
-
144
- if klass.__meta and joins = klass.__meta[:sql_join]
145
- for data in joins
146
- # the class to join to and some options.
147
- join_name, join_class, options = *data
148
-
149
- # gmosx: dont use DBTABLE here, perhaps the join class
150
- # is not managed yet.
151
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
152
- join_src = "#{self.class.encode(klass)}_oid"
153
- join_dst = "#{self.class.encode(join_class)}_oid"
154
- begin
155
- conn.store.query("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").close
156
- conn.store.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").close
157
- conn.store.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").close
158
- rescue Exception => 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
-
169
- ensure
170
- db.put_connection
171
- end
172
-
173
- end
174
-
175
- # The SQLite connection.
176
-
177
- class SqliteConnection < Connection
178
-
179
- def initialize(db)
180
- @store = SQLite3::Database.new("#{db.config[:database]}.db")
181
- super
182
- end
183
-
184
- def close
185
- @store.close
186
- super
187
- end
188
-
189
- def prepare(sql)
190
- @store.prepare(sql)
191
- end
192
-
193
- def query(sql)
194
- Logger.debug sql if $DBG
195
- begin
196
- return @store.query(sql)
197
- rescue => ex
198
- handle_db_exception(ex, sql)
199
- end
200
- end
201
-
202
- def exec(sql)
203
- Logger.debug sql if $DBG
204
- begin
205
- @store.query(sql).close
206
- rescue => ex
207
- handle_db_exception(ex, sql)
208
- end
209
- end
210
-
211
- def start
212
- @store.transaction
213
- end
214
-
215
- def commit
216
- @store.commit
217
- end
218
-
219
- def rollback
220
- @store.rollback
221
- end
222
-
223
- def valid_res?(res)
224
- return !(res.nil?)
225
- end
226
-
227
- def read_one(res, klass)
228
- return nil unless valid_res?(res)
229
- row = res.next
230
- return nil unless row
231
-
232
- obj = klass.allocate
233
- obj.og_read(row)
234
-
235
- res.close
236
- return obj
237
- end
238
-
239
- def read_all(res, klass)
240
- return [] unless valid_res?(res)
241
- objects = []
242
-
243
- res.each do |row|
244
- obj = klass.allocate
245
- obj.og_read(row)
246
- objects << obj
247
- end
248
-
249
- res.close
250
- return objects
251
- end
252
-
253
- def read_int(res, idx = 0)
254
- val = res.next[idx].to_i
255
- res.close
256
- return val
257
- end
258
-
259
- def get_row(res)
260
- res.next
261
- end
262
-
263
- end
264
-
265
- end
@@ -1,356 +0,0 @@
1
- # * Anastasios Koutoumanos <ak@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: sqlserver.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- begin
6
- require 'dbi'
7
- rescue Object => ex
8
- Logger.error 'Ruby-DBI bindings not present or ADO support not available.'
9
- Logger.error ex
10
- end
11
-
12
- require 'og/adapters/base'
13
-
14
- module Og
15
-
16
- # The Microsoft SQL Server Sql Server adapter. This adapter
17
- # communicates with a Microsoft SQL Server sql server rdbms.
18
- # This adapter will ONLY work on Windows systems, since it
19
- # relies on Win32OLE is apparently only available on Windows.
20
- # It relies on the ADO support in the DBI module. If you are using the
21
- # one-click installer of Ruby, then you already have DBI installed, but
22
- # the ADO module is *NOT* installed. You will need to get the latest
23
- # source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
24
- # unzip it, and copy the file <tt>src/lib/dbd_ado/ADO.rb</tt> to
25
- # <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
26
- # (you will need to create the ADO directory). Once you've installed
27
- # that file, you are ready to go.
28
- # Originally based on the sqlserver_adapter.rb -- the ActiveRecord
29
- # adapter for Microsoft SQL Server by Joey Gibson and DeLynn Berry.
30
- # Information related to the SQL Server datatypes:
31
- # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/architec/8_ar_da_4ucz.asp
32
- # For extra documentation see lib/og/adapter.rb
33
-
34
- class SqlserverAdapter < Adapter
35
-
36
- def initialize
37
- super
38
- @typemap.update(
39
- Integer => 'int', # drak: maybe smallint?
40
- Fixnum => 'int',
41
- Float => 'float',
42
- String => 'varchar(1024)', # drak: maybe nvarchar for unicode
43
- Time => 'datetime', # drak: maybe smalldatime?
44
- Date => 'smalldatetime',
45
- TrueClass => 'bit'
46
- )
47
-
48
- @typecast.update(TrueClass => "#\{:s: ? \'1\' : '0' \}")
49
- end
50
-
51
-
52
- def self.timestamp(time = Time.now)
53
- return nil unless time
54
- return time.strftime("%Y-%m-%d %H:%M:%S")
55
- end
56
-
57
- def self.date(date)
58
- return nil unless date
59
- return "#{date.year}-#{date.month}-#{date.mday}"
60
- end
61
-
62
- def read_prop(p, idx)
63
- if p.klass.ancestors.include?(Integer)
64
- return "res[tuple][#{idx}].to_i"
65
- elsif p.klass.ancestors.include?(Float)
66
- return "res[tuple][#{idx}].to_f"
67
- elsif p.klass.ancestors.include?(String)
68
- return "res[tuple][#{idx}]"
69
- elsif p.klass.ancestors.include?(Time)
70
- return "#{self.class}.parse_timestamp(res[tuple][#{idx}])"
71
- elsif p.klass.ancestors.include?(Date)
72
- return "#{self.class}.parse_date(res[tuple][#{idx}])"
73
- elsif p.klass.ancestors.include?(TrueClass)
74
- return "('0' != res[tuple][#{idx}])"
75
- else
76
- return "YAML::load(res[tuple][#{idx}])"
77
- end
78
- end
79
-
80
- def create_db(database, user = nil, password = nil)
81
- raise 'Not implemented!'
82
-
83
- ### how can we get a valid connection when the database does not already exist?
84
- begin
85
- conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=master;User Id=sa;Password=sa;")
86
- conn["AutoCommit"] = true
87
- conn.execute("CREATE DATABASE #{database}")
88
- conn.commit
89
- Logger.warn "Ignoring credentials for database creation (not implemented)" if user
90
- Logger.info "Created database '#{database}'."
91
- super
92
- rescue Exception => ex
93
- # gmosx: any idea how to better test this?
94
- if ex.to_s =~ /0x80020009/i
95
- Logger.warn "Cannot drop database '#{database}', it does not exist!"
96
- super
97
- else
98
- raise
99
- end
100
- end
101
- end
102
-
103
- def drop_db(database, user = nil, password = nil)
104
- raise 'Not implemented!'
105
-
106
- begin
107
- conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=master;User Id=sa;Password=sa;")
108
- conn["AutoCommit"] = true
109
- # FIXME: why no drop?
110
- # conn.execute("DROP DATABASE #{database}")
111
- Logger.warn "Ignoring credentials for database drop (not implemented)" if user
112
- Logger.info "Dropped database '#{database}'."
113
- super
114
- rescue Exception => ex
115
- # gmosx: any idea how to better test this?
116
- if ex.to_s =~ /0x80020009/i
117
- Logger.warn "Cannot drop database '#{database}', it does not exist!"
118
- super
119
- else
120
- raise
121
- end
122
- end
123
- end
124
-
125
- def props_for_insert(klass)
126
- klass.__props.reject { |p| :oid == p.symbol }
127
- end
128
-
129
- def insert_code(klass, db)
130
- props = props_for_insert(klass)
131
- values = props.collect { |p| write_prop(p) }.join(',')
132
-
133
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values});"
134
-
135
- %{
136
- conn.exec "#{sql}"
137
- conn.commit
138
- res = conn.query("SELECT IDENT_CURRENT('#{klass::DBTABLE}')")
139
- @oid = res[0][0].to_i
140
- #res.finish
141
- }
142
- end
143
-
144
- def new_connection(db)
145
- return SqlserverConnection.new(db)
146
- end
147
-
148
- def calc_field_index(klass, db)
149
- res = db.get_connection.store.execute("SELECT TOP 1 * FROM #{klass::DBTABLE}")
150
- meta = db.managed_classes[klass]
151
- columns = res.column_names
152
-
153
- for idx in (0...columns.size)
154
- meta.field_index[columns[idx]] = idx
155
- end
156
-
157
- ensure
158
- res.finish
159
- end
160
-
161
- def create_table(klass, db)
162
- conn = db.get_connection
163
-
164
- fields = create_fields(klass)
165
-
166
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
167
-
168
- # Create table constrains.
169
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
170
- sql << ", #{constrains.join(', ')}"
171
- end
172
- sql << ");"
173
-
174
- begin
175
- conn.store.execute(sql)
176
- conn.commit
177
- Logger.info "Created table '#{klass::DBTABLE}'."
178
- rescue => ex
179
- if ex.to_s =~ /80040E14/i
180
- Logger.debug 'Table already exists' if $DBG
181
- else
182
- raise
183
- end
184
- end
185
-
186
- # Create indices.
187
-
188
- if klass.__meta and indices = klass.__meta[:sql_index]
189
- for data in indices
190
- idx, options = *data
191
- idx = idx.to_s
192
- pre_sql, post_sql = options[:pre], options[:post]
193
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
194
- sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
195
- begin
196
- conn.store.execute(sql)
197
- conn.commit
198
- rescue => ex
199
- if ex.to_s =~ /80040E14/i
200
- Logger.debug 'Table already exists' if $DBG
201
- else
202
- raise
203
- end
204
- end
205
- end
206
- end
207
-
208
- # Create join tables if needed. Join tables are used in
209
- # 'many_to_many' relations.
210
-
211
- if klass.__meta and joins = klass.__meta[:sql_join]
212
- for data in joins
213
- # the class to join to and some options.
214
- join_name, join_class, options = *data
215
-
216
- # gmosx: dont use DBTABLE here, perhaps the join class
217
- # is not managed yet.
218
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
219
- join_src = "#{self.class.encode(klass)}_oid"
220
- join_dst = "#{self.class.encode(join_class)}_oid"
221
- begin
222
- conn.store.execute("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").finish
223
- conn.store.execute("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").finish
224
- conn.store.execute("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").finish
225
- conn.commit
226
- rescue => ex
227
- # gmosx: any idea how to better test this?
228
- if ex.to_s =~ /80040E14/i
229
- Logger.debug "Join table already exists" if $DBG
230
- else
231
- raise
232
- end
233
- end
234
- end
235
- end
236
-
237
- ensure
238
- db.put_connection
239
- end
240
-
241
- def drop_table(klass)
242
- super
243
- exec "DROP TABLE #{klass::DBTABLE}"
244
- end
245
-
246
- # Generate the property for oid.
247
-
248
- def eval_og_oid(klass)
249
- klass.class_eval %{
250
- prop_accessor :oid, Fixnum, :sql => 'int NOT NULL IDENTITY(1, 1) PRIMARY KEY'
251
- }
252
- end
253
-
254
- end
255
-
256
- # The SqlserverConnection connection.
257
-
258
- class SqlserverConnection < Connection
259
-
260
- def initialize(db)
261
- super
262
- config = db.config
263
-
264
- begin
265
- @store = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{config[:address]};Initial Catalog=#{config[:database]};User Id=#{config[:user].to_s};Password=#{config[:password].to_s};")
266
- rescue => ex
267
- # gmosx, FIXME: drak, fix this!
268
- if ex.to_s =~ /database .* does not exist/i
269
- Logger.info "Database '#{config[:database]}' not found!"
270
- @db.adapter.create_db(config[:database], config[:user])
271
- retry
272
- end
273
- raise
274
- end
275
- end
276
-
277
- def close
278
- @store.disconnect
279
- super
280
- end
281
-
282
- def prepare(sql)
283
- @store.prepare(sql)
284
- end
285
-
286
- def query(sql)
287
- Logger.debug sql if $DBG
288
- begin
289
- res = @store.select_all(sql)
290
- return res
291
- rescue => ex
292
- handle_db_exception(ex, sql)
293
- end
294
- end
295
-
296
- def exec(sql)
297
- Logger.debug sql if $DBG
298
- begin
299
- @store.execute(sql).finish
300
- rescue => ex
301
- handle_db_exception(ex, sql)
302
- end
303
- end
304
-
305
- def start
306
- # no need to do something.
307
- end
308
-
309
- def commit
310
- @store.commit
311
- end
312
-
313
- def rollback
314
- @store.rollback
315
- end
316
-
317
- def valid_res?(res)
318
- return !(res.nil?)
319
- end
320
-
321
- def read_one(res, klass)
322
- return nil unless valid_res?(res)
323
- return nil unless res
324
-
325
- obj = klass.allocate
326
- obj.og_read(res)
327
-
328
- return obj
329
- end
330
-
331
- def read_all(res, klass)
332
- return [] unless valid_res?(res)
333
- objects = []
334
-
335
- for tuple in (0...res.size)
336
- obj = klass.allocate
337
- obj.og_read(res, tuple)
338
- objects << obj
339
- end
340
-
341
- return objects
342
- end
343
-
344
- def read_int(res, idx = 0)
345
- val = res.next[idx].to_i
346
- res.finish
347
- return val
348
- end
349
-
350
- def get_row(res)
351
- res.next
352
- end
353
-
354
- end
355
-
356
- end