og 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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,117 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: filesys.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- require 'fileutils'
6
-
7
- require 'og/adapters/base'
8
-
9
- module Og
10
-
11
- # The Filesys adapter. This adapter stores Og objectes in
12
- # the filesystem. This adapter is a proof of concept and
13
- # at the moment severely limited. For extra documentation
14
- # see lib/og/adapter.rb
15
-
16
- class FilesysAdapter < Adapter
17
-
18
- # Creates the name of the database root dir.
19
-
20
- def self.dir(database)
21
- "#{database}_db"
22
- end
23
-
24
- def create_db(database, user = nil, password = nil)
25
- begin
26
- FileUtils.mkdir_p(self.class.dir(database))
27
- rescue
28
- Logger.error "Cannot create '#{database}'!"
29
- end
30
- end
31
-
32
- def drop_db(database, user = nil, password = nil)
33
- begin
34
- FileUtils.rmdir(self.class.dir(database))
35
- super
36
- rescue
37
- Logger.error "Cannot drop '#{database}'!"
38
- end
39
- end
40
-
41
- def insert_code(klass, db)
42
- props = props_for_insert(klass)
43
- values = props.collect { |p| write_prop(p) }.join(',')
44
-
45
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values})"
46
-
47
- %{
48
- conn.store.query("#{sql}").close
49
- @oid = conn.store.last_insert_row_id
50
- }
51
- end
52
-
53
- def new_connection(db)
54
- return FilesysConnection.new(db)
55
- end
56
-
57
- # A 'table' is emulated by using a directory to store
58
- # one file per table row. Each file contains an object
59
- # marshaled in YAML format. A special file called
60
- # '_sequence' stores the sequence for this 'table'.
61
-
62
- def create_table(klass, db)
63
- class_dir = File.join(self.class.dir(db.config[:database]), klass::DBTABLE)
64
- FileUtils.mkdir_p(class_dir)
65
-
66
- seq_file = File.join(class_dir, 'seq')
67
- File.open(seq_file, 'w') { |f| f << '1' }
68
- rescue => ex
69
- Logger.error "Cannot create directory to store '#{klass}' classes!"
70
- end
71
-
72
- end
73
-
74
- # The Filesys adapter connection.
75
-
76
- class FilesysConnection < Connection
77
-
78
- def initialize(db)
79
- super
80
- config = db.config
81
-
82
- begin
83
- raise unless File.directory?(FilesysAdapter.dir(config[:database]))
84
- rescue => ex
85
- if true # ex.to_s =~ /database .* does not exist/i
86
- Logger.info "Database '#{config[:database]}' not found!"
87
- @db.adapter.create_db(config[:database], config[:user])
88
- retry
89
- end
90
- raise
91
- end
92
- end
93
-
94
- def save(obj)
95
- seq = nil
96
- class_dir = File.join(FilesysAdapter.dir(@db.config[:database]), obj.class::DBTABLE)
97
- File.open(File.join(class_dir, 'seq'), 'r') { |f| seq = f.read.to_i }
98
- obj.oid = seq
99
- File.open(File.join(class_dir, "#{seq}.yml"), 'w') { |f| f << obj.to_yaml }
100
- seq += 1
101
- File.open(File.join(class_dir, 'seq'), 'w') { |f| f << seq }
102
- end
103
-
104
- def load(oid, klass)
105
- obj = nil
106
- class_dir = File.join(FilesysAdapter.dir(@db.config[:database]), klass::DBTABLE)
107
- File.open(File.join(class_dir, "#{oid}.yml"), 'r') { |f| obj = YAML::load(f.read) }
108
- return obj
109
- end
110
-
111
- def close
112
- Logger.debug "Closed DB connection." if $DBG
113
- end
114
-
115
- end
116
-
117
- end
@@ -1,350 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: mysql.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- begin
6
- require 'mysql'
7
- rescue Object => ex
8
- Logger.error 'Ruby-Mysql bindings are not installed!'
9
- Logger.error ex
10
- end
11
-
12
- require 'og/adapters/base'
13
-
14
- module Og
15
-
16
- # The MySQL adapter. This adapter communicates with
17
- # an MySQL rdbms. For extra documentation see
18
- # lib/og/adapter.rb
19
-
20
- class MysqlAdapter < Adapter
21
-
22
- def initialize
23
- super
24
- @typemap.update(TrueClass => 'tinyint')
25
- @typecast.update(TrueClass => "#\{:s: ? \"1\" : 'NULL' \}")
26
- end
27
-
28
- def self.escape(str)
29
- return nil unless str
30
- return Mysql.quote(str)
31
- end
32
-
33
- def self.timestamp(time = Time.now)
34
- return nil unless time
35
- return time.strftime("%Y%m%d%H%M%S")
36
- end
37
-
38
- def self.date(date)
39
- return nil unless date
40
- return "#{date.year}-#{date.month}-#{date.mday}"
41
- end
42
-
43
- def write_prop(p)
44
- if p.klass.ancestors.include?(Integer)
45
- return "#\{@#{p.symbol} || 'NULL'\}"
46
- elsif p.klass.ancestors.include?(Float)
47
- return "#\{@#{p.symbol} || 'NULL'\}"
48
- elsif p.klass.ancestors.include?(String)
49
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol})\}'" : 'NULL'\}|
50
- elsif p.klass.ancestors.include?(Time)
51
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
52
- elsif p.klass.ancestors.include?(Date)
53
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
54
- elsif p.klass.ancestors.include?(TrueClass)
55
- return "#\{@#{p.symbol} ? 1 : 0 \}"
56
- else
57
- # gmosx: keep the '' for nil symbols.
58
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
59
- end
60
- end
61
-
62
- def read_prop(p, idx)
63
- if p.klass.ancestors.include?(Integer)
64
- return "res[#{idx}].to_i"
65
- elsif p.klass.ancestors.include?(Float)
66
- return "res[#{idx}].to_f"
67
- elsif p.klass.ancestors.include?(String)
68
- return "res[#{idx}]"
69
- elsif p.klass.ancestors.include?(Time)
70
- return "#{self.class}.parse_timestamp(res[#{idx}])"
71
- elsif p.klass.ancestors.include?(Date)
72
- return "#{self.class}.parse_date(res[#{idx}])"
73
- elsif p.klass.ancestors.include?(TrueClass)
74
- return "('0' != res[#{idx}])"
75
- else
76
- return "YAML::load(res[#{idx}])"
77
- end
78
- end
79
-
80
- def create_db(database, user = nil, password = nil)
81
- # gmosx: system is used to avoid shell expansion.
82
- system 'mysqladmin', '-f', "--user=#{user}", "--password=#{password}", 'create', database
83
- super
84
- end
85
-
86
- def drop_db(database, user = nil, password = nil)
87
- system 'mysqladmin', '-f', "--user=#{user}", "--password=#{password}", 'drop', database
88
- super
89
- end
90
-
91
- def props_for_insert(klass)
92
- klass.__props.reject { |p| :oid == p.symbol }
93
- end
94
-
95
- def insert_code(klass, db)
96
- props = props_for_insert(klass)
97
- values = props.collect { |p| write_prop(p) }.join(',')
98
-
99
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values})"
100
-
101
- %{
102
- conn.store.query_with_result = false
103
- conn.store.query "#{sql}"
104
- @oid = conn.store.insert_id()
105
- }
106
- end
107
-
108
- def new_connection(db)
109
- return MysqlConnection.new(db)
110
- end
111
-
112
- def calc_field_index(klass, db)
113
- res = db.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
114
- meta = db.managed_classes[klass]
115
-
116
- for idx in (0...res.num_fields)
117
- meta.field_index[res.fetch_field.name] = idx
118
- end
119
-
120
- ensure
121
- res.free if res
122
- end
123
-
124
- def create_fields(klass)
125
- fields = []
126
-
127
- klass.__props.each do |p|
128
- klass.sql_index(p.symbol) if p.meta[:sql_index]
129
-
130
- field = "#{p.symbol}"
131
-
132
- if p.meta and p.meta[:sql]
133
- field << " #{p.meta[:sql]}"
134
- else
135
- field << " #{@typemap[p.klass]}"
136
-
137
- if p.meta
138
- # set default value (gmosx: not that useful in the
139
- # current implementation).
140
- if default = p.meta[:default]
141
- field << " DEFAULT #{default.inspect} NOT NULL"
142
- end
143
-
144
- # set unique
145
- # FIXME: correctly handle UNIQUE constrain.
146
- # field << " UNIQUE" if p.meta[:unique]
147
-
148
- # attach extra sql
149
- if extra_sql = p.meta[:extra_sql]
150
- field << " #{extra_sql}"
151
- end
152
- end
153
- end
154
-
155
- fields << field
156
- end
157
-
158
- return fields
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
- conn.store.query_with_result = true
169
-
170
- # Create table constrains
171
-
172
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
173
- sql << ", #{constrains.join(', ')}"
174
- end
175
-
176
- sql << ');'
177
-
178
- begin
179
- conn.store.query(sql)
180
- Logger.info "Created table '#{klass::DBTABLE}'."
181
- rescue => ex
182
- if ex.errno == 1050 # table already exists.
183
- Logger.debug "Table already exists" if $DBG
184
- return
185
- else
186
- raise
187
- end
188
- end
189
-
190
- # Create indices
191
-
192
- if klass.__meta and indices = klass.__meta[:sql_index]
193
- for data in indices
194
- idx, options = *data
195
- idx = idx.to_s
196
- pre_sql, post_sql = options[:pre], options[:post]
197
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
198
- conn.store.query("CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx})")
199
- end
200
- end
201
-
202
- # Create join tables if needed. Join tables are used in
203
- # 'many_to_many' relations.
204
-
205
- if klass.__meta and joins = klass.__meta[:sql_join]
206
- for data in joins
207
- # the class to join to and some options.
208
- join_name, join_class, options = *data
209
-
210
- # gmosx: dont use DBTABLE here, perhaps the join class
211
- # is not managed yet.
212
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
213
- join_src = "#{self.class.encode(klass)}_oid"
214
- join_dst = "#{self.class.encode(join_class)}_oid"
215
- begin
216
- conn.store.query("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )")
217
- conn.store.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)")
218
- conn.store.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)")
219
- rescue => ex
220
- if ex.errno == 1050 # table already exists.
221
- Logger.debug "Join table already exists" if $DBG
222
- else
223
- raise
224
- end
225
- end
226
- end
227
- end
228
-
229
- ensure
230
- db.put_connection
231
- end
232
-
233
- def eval_og_oid(klass)
234
- klass.class_eval %{
235
- prop_accessor :oid, Fixnum, :sql => 'integer AUTO_INCREMENT PRIMARY KEY'
236
- }
237
- end
238
- end
239
-
240
- # The MySQL connection.
241
-
242
- class MysqlConnection < Connection
243
-
244
- def initialize(db)
245
- super
246
-
247
- config = db.config
248
-
249
- @store = Mysql.connect(
250
- config[:address] || 'localhost',
251
- config[:user],
252
- config[:password],
253
- config[:database]
254
- )
255
- rescue => ex
256
- if ex.errno == 1049 # database does not exist.
257
- Logger.info "Database '#{config[:database]}' not found!"
258
- @db.adapter.create_db(config[:database], config[:user], config[:password])
259
- retry
260
- end
261
- raise
262
- end
263
-
264
- def close
265
- @store.close
266
- super
267
- end
268
-
269
- def prepare(sql)
270
- raise 'Not implemented!'
271
- end
272
-
273
- def query(sql)
274
- Logger.debug sql if $DBG
275
- begin
276
- @store.query_with_result = true
277
- return @store.query(sql)
278
- rescue => ex
279
- handle_db_exception(ex, sql)
280
- end
281
- end
282
-
283
- def exec(sql)
284
- Logger.debug sql if $DBG
285
- begin
286
- @store.query_with_result = false
287
- @store.query(sql)
288
- rescue => ex
289
- handle_db_exception(ex, sql)
290
- end
291
- end
292
-
293
- def start
294
- # @store.transaction
295
- end
296
-
297
- def commit
298
- # @store.commit
299
- end
300
-
301
- def rollback
302
- # @store.rollback
303
- end
304
-
305
- def valid_res?(res)
306
- return !(res.nil? or 0 == res.num_rows)
307
- end
308
-
309
- def read_one(res, klass)
310
- return nil unless valid_res?(res)
311
-
312
- row = res.fetch_row
313
- obj = klass.allocate
314
- obj.og_read(row)
315
-
316
- res.free
317
- return obj
318
- end
319
-
320
- def read_all(res, klass)
321
- return [] unless valid_res?(res)
322
-
323
- objects = []
324
-
325
- for tuple in (0...res.num_rows)
326
- row = res.fetch_row
327
-
328
- obj = klass.allocate
329
- obj.og_read(row)
330
-
331
- objects << obj
332
- end
333
-
334
- res.free
335
- return objects
336
- end
337
-
338
- def read_int(res, idx = 0)
339
- val = res.fetch_row[idx].to_i
340
- res.free
341
- return val
342
- end
343
-
344
- def get_row(res)
345
- res.fetch_row
346
- end
347
-
348
- end
349
-
350
- end