og 0.31.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/doc/{AUTHORS → CONTRIBUTORS} +26 -10
  2. data/doc/LICENSE +2 -3
  3. data/doc/RELEASES +56 -7
  4. data/doc/tutorial.txt +15 -15
  5. data/lib/glue/cacheable.rb +2 -5
  6. data/lib/glue/hierarchical.rb +1 -4
  7. data/lib/glue/optimistic_locking.rb +0 -2
  8. data/lib/glue/orderable.rb +79 -75
  9. data/lib/glue/revisable.rb +19 -24
  10. data/lib/glue/searchable.rb +0 -2
  11. data/lib/glue/taggable.rb +31 -29
  12. data/lib/glue/timestamped.rb +4 -2
  13. data/lib/og.rb +50 -29
  14. data/lib/og/adapter.rb +19 -0
  15. data/lib/og/adapter/mysql.rb +212 -0
  16. data/lib/og/adapter/mysql/override.rb +34 -0
  17. data/lib/og/adapter/mysql/script.rb +15 -0
  18. data/lib/og/adapter/mysql/utils.rb +40 -0
  19. data/lib/og/adapter/postgresql.rb +231 -0
  20. data/lib/og/adapter/postgresql/override.rb +117 -0
  21. data/lib/og/adapter/postgresql/script.rb +15 -0
  22. data/lib/og/adapter/postgresql/utils.rb +35 -0
  23. data/lib/og/adapter/sqlite.rb +132 -0
  24. data/lib/og/adapter/sqlite/override.rb +33 -0
  25. data/lib/og/adapter/sqlite/script.rb +15 -0
  26. data/lib/og/collection.rb +35 -7
  27. data/lib/og/{evolution.rb → dump.rb} +4 -5
  28. data/lib/og/entity.rb +102 -173
  29. data/lib/og/entity/clone.rb +119 -0
  30. data/lib/og/errors.rb +0 -2
  31. data/lib/og/manager.rb +85 -37
  32. data/lib/og/relation.rb +52 -34
  33. data/lib/og/relation/belongs_to.rb +0 -2
  34. data/lib/og/relation/has_many.rb +27 -4
  35. data/lib/og/relation/joins_many.rb +41 -14
  36. data/lib/og/relation/many_to_many.rb +10 -0
  37. data/lib/og/relation/refers_to.rb +22 -5
  38. data/lib/og/store.rb +80 -86
  39. data/lib/og/store/sql.rb +710 -713
  40. data/lib/og/store/sql/evolution.rb +119 -0
  41. data/lib/og/store/sql/join.rb +155 -0
  42. data/lib/og/store/sql/utils.rb +149 -0
  43. data/lib/og/test/assertions.rb +1 -3
  44. data/lib/og/test/testcase.rb +0 -2
  45. data/lib/og/types.rb +2 -5
  46. data/lib/og/validation.rb +6 -9
  47. data/test/{og/mixin → glue}/tc_hierarchical.rb +3 -13
  48. data/test/glue/tc_og_paginate.rb +47 -0
  49. data/test/{og/mixin → glue}/tc_optimistic_locking.rb +2 -12
  50. data/test/{og/mixin → glue}/tc_orderable.rb +15 -23
  51. data/test/glue/tc_orderable2.rb +47 -0
  52. data/test/glue/tc_revisable.rb +3 -3
  53. data/test/{og/mixin → glue}/tc_taggable.rb +20 -10
  54. data/test/{og/mixin → glue}/tc_timestamped.rb +2 -12
  55. data/test/glue/tc_webfile.rb +36 -0
  56. data/test/og/CONFIG.rb +8 -11
  57. data/test/og/multi_validations_model.rb +14 -0
  58. data/test/og/store/tc_filesys.rb +3 -1
  59. data/test/og/store/tc_kirby.rb +16 -13
  60. data/test/og/store/tc_sti.rb +11 -11
  61. data/test/og/store/tc_sti2.rb +79 -0
  62. data/test/og/tc_build.rb +41 -0
  63. data/test/og/tc_cacheable.rb +3 -2
  64. data/test/og/tc_has_many.rb +96 -0
  65. data/test/og/tc_inheritance.rb +6 -4
  66. data/test/og/tc_joins_many.rb +93 -0
  67. data/test/og/tc_multi_validations.rb +5 -7
  68. data/test/og/tc_multiple.rb +7 -6
  69. data/test/og/tc_override.rb +13 -7
  70. data/test/og/tc_primary_key.rb +30 -0
  71. data/test/og/tc_relation.rb +8 -14
  72. data/test/og/tc_reldelete.rb +163 -0
  73. data/test/og/tc_reverse.rb +17 -14
  74. data/test/og/tc_scoped.rb +3 -11
  75. data/test/og/tc_setup.rb +13 -11
  76. data/test/og/tc_store.rb +21 -28
  77. data/test/og/tc_validation2.rb +2 -2
  78. data/test/og/tc_validation_loop.rb +17 -15
  79. metadata +109 -103
  80. data/INSTALL +0 -91
  81. data/ProjectInfo +0 -51
  82. data/README +0 -177
  83. data/doc/config.txt +0 -28
  84. data/examples/README +0 -23
  85. data/examples/mysql_to_psql.rb +0 -71
  86. data/examples/run.rb +0 -271
  87. data/lib/glue/tree.rb +0 -218
  88. data/lib/og/store/alpha/filesys.rb +0 -110
  89. data/lib/og/store/alpha/memory.rb +0 -295
  90. data/lib/og/store/alpha/sqlserver.rb +0 -256
  91. data/lib/og/store/kirby.rb +0 -490
  92. data/lib/og/store/mysql.rb +0 -415
  93. data/lib/og/store/psql.rb +0 -875
  94. data/lib/og/store/sqlite.rb +0 -348
  95. data/lib/og/store/sqlite2.rb +0 -241
  96. data/setup.rb +0 -1585
  97. data/test/og/tc_sti_find.rb +0 -35
@@ -1,348 +0,0 @@
1
- begin
2
- require 'sqlite3'
3
- rescue Object => ex
4
- Logger.error 'Ruby-Sqlite3 bindings are not installed!'
5
- Logger.error ex
6
- end
7
-
8
- require 'fileutils'
9
- require 'set'
10
-
11
- require 'og/store/sql'
12
-
13
- #--
14
- # Customize the standard Sqlite3 resultset to make
15
- # more compatible with Og.
16
- #++
17
-
18
- class SQLite3::ResultSet # :nodoc: all
19
- alias_method :blank?, :eof?
20
-
21
- def each_row
22
- each do |row|
23
- yield(row, 0)
24
- end
25
- end
26
-
27
- def first_value
28
- val = self.next[0]
29
- close
30
- return val
31
- end
32
-
33
- alias_method :fields, :columns
34
- end
35
-
36
- module Og
37
-
38
- # A Store that persists objects into an Sqlite3 database.
39
- # To read documentation about the methods, consult the documentation
40
- # for SqlStore and Store.
41
-
42
- class SqliteStore < SqlStore
43
-
44
- # Override if needed.
45
-
46
- def self.db_filename(options)
47
- options[:name] ||= 'data'
48
- "#{options[:name]}.db"
49
- end
50
-
51
- def self.destroy(options)
52
- begin
53
- FileUtils.rm(db_filename(options))
54
- super
55
- rescue Object
56
- Logger.info "Cannot drop '#{options[:name]}'!"
57
- end
58
- end
59
-
60
- # Initialize the Sqlite store.
61
- # This store provides a default name.
62
-
63
- def initialize(options)
64
- super
65
- @conn = SQLite3::Database.new(self.class.db_filename(options))
66
- end
67
-
68
- def close
69
- @conn.close
70
- super
71
- end
72
-
73
- def enchant(klass, manager)
74
- if klass.ann.self.primary_key.symbol == :oid
75
- unless klass.properties.include? :oid
76
- klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
77
- end
78
- end
79
-
80
- super
81
- end
82
-
83
- def query(sql)
84
- Logger.debug sql if $DBG
85
- return @conn.query(sql)
86
- rescue Exception => ex
87
- handle_sql_exception(ex, sql)
88
- end
89
-
90
- def exec(sql)
91
- Logger.debug sql if $DBG
92
- @conn.query(sql).close
93
- rescue => ex
94
- handle_sql_exception(ex, sql)
95
- end
96
-
97
- def start
98
- @conn.transaction if @transaction_nesting < 1
99
- @transaction_nesting += 1
100
- end
101
-
102
- def commit
103
- @transaction_nesting -= 1
104
- @conn.commit if @transaction_nesting < 1
105
- end
106
-
107
- def rollback
108
- @transaction_nesting -= 1
109
- @conn.rollback if @transaction_nesting < 1
110
- end
111
-
112
- def last_insert_rowid
113
- conn.query("SELECT last_insert_rowid()").first_value.to_i
114
- end
115
-
116
- def sql_update(sql)
117
- exec(sql)
118
- @conn.changes
119
- end
120
-
121
- def list_tables
122
- rset = @conn.query("select name from sqlite_master where type = 'table'")
123
- rset.map { |r| r[0] }
124
- end
125
-
126
- def list_fields(table)
127
- rset = @conn.query("pragma table_info(#{table})")
128
- columns = rset.columns
129
- rows = rset.entries
130
- idx = columns.index('name')
131
- rows.map { |r| r[idx] }
132
- end
133
-
134
- # Deserialize one object from the ResultSet.
135
-
136
- def read_one(res, klass, options = nil)
137
- return nil if res.blank?
138
-
139
- if options and join_relations = options[:include]
140
- join_relations = [join_relations].flatten.collect do |n|
141
- klass.relation(n)
142
- end
143
- end
144
-
145
- res_row = res.next
146
-
147
- # causes STI classes to come back as the correct child class
148
- # if accessed from the superclass.
149
-
150
- klass = Og::Entity::entity_from_string(res_row[0]) if klass.schema_inheritance?
151
- obj = klass.og_allocate(res_row, 0)
152
-
153
- if options and options[:select]
154
- read_row(obj, res, res_row, 0)
155
- else
156
- obj.og_read(res_row)
157
- read_join_relations(obj, res_row, 0, join_relations) if join_relations
158
- end
159
-
160
- return obj
161
-
162
- ensure
163
- res.close
164
- end
165
-
166
- private
167
-
168
- def property_to_field(klass, p)
169
- field = []
170
-
171
- klass.index(p.symbol) if p.index
172
-
173
- field = field_for_property(p)
174
-
175
- if p.sql
176
- field << " #{p.sql}"
177
- else
178
- field << " #{type_for_class(p.klass)}"
179
- field << " UNIQUE" if p.unique
180
- field << " DEFAULT #{p.default.inspect} NOT NULL" if p.default
181
- field << " #{p.extra_sql}" if p.extra_sql
182
- end
183
-
184
- field
185
- end
186
-
187
- def create_table_ddl(klass, type = nil, table_name = nil)
188
- fields = fields_for_class(klass)
189
- type ||= ''
190
- table_name ||= klass::OGTABLE
191
-
192
- sql = "CREATE #{type} TABLE #{table_name} (#{fields.join(', ')}"
193
-
194
- # Create table constraints.
195
-
196
- if constraints = klass.ann.self[:sql_constraint]
197
- sql << ", #{constraints.join(', ')}"
198
- end
199
-
200
- sql << ")"
201
-
202
- # Create indices.
203
-
204
- if indices = klass.ann.self[:index]
205
- for data in indices
206
- idx, options = *data
207
- idx = idx.to_s
208
- pre_sql, post_sql = options[:pre], options[:post]
209
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
210
- sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
211
- end
212
- end
213
-
214
- sql
215
- end
216
-
217
- def create_table(klass)
218
- unless list_tables.include?(klass::OGTABLE)
219
- sql = create_table_ddl(klass)
220
-
221
- @conn.query(sql).close
222
- Logger.info "Created table '#{klass::OGTABLE}'."
223
- else
224
- Logger.debug "Table '#{klass::OGTABLE}' already exists" if $DBG
225
-
226
- actual_fields = Set.new(list_fields(klass::OGTABLE))
227
- defined_fields = Set.new(fields_for_class(klass).map { |f| f[0, f.index(' ')] } )
228
-
229
- unless defined_fields == actual_fields
230
- Logger.debug "Field mismatch in '#{klass::OGTABLE}'. Attempting to correct..." if $DBG
231
-
232
- fields_to_add = defined_fields - actual_fields
233
- fields_to_delete = actual_fields - defined_fields
234
-
235
- fields_to_add.each do |field|
236
- if @options[:evolve_schema] == true
237
- Logger.debug "Adding field '#{field}' to '#{klass::OGTABLE}'" if $DBG
238
- sql = "ALTER TABLE #{klass::OGTABLE} ADD COLUMN #{property_to_field(klass, klass.properties[field.to_sym])}"
239
- @conn.query(sql)
240
- @conn.query("VACUUM #{klass::OGTABLE}")
241
- else
242
- Logger.warn "Table '#{klass::OGTABLE} is missing field '#{field}' and :evolve_schema is not set to true or :cautious!"
243
- end
244
- end
245
-
246
- # Sqlite3 does _NOT_ support removing columns via alter table. You have to drop the table.
247
-
248
- unless fields_to_delete.empty?
249
- if (@options[:evolve_schema] == true) and (@options[:evolve_schema_cautious] == false)
250
- Logger.warn "Removing obsolete fields '#{fields_to_delete.to_a.join(', ')}' from '#{klass::OGTABLE}'!"
251
-
252
- field_list = klass.properties.values.map { |f| field_for_property(f) }.join(', ')
253
- backup_table = "#{klass::OGTABLE}_backup"
254
-
255
- sql_transaction = [
256
- "BEGIN TRANSACTION",
257
- "#{create_table_ddl(klass, 'temporary', backup_table)}",
258
- "INSERT INTO #{backup_table} SELECT #{field_list} FROM #{klass::OGTABLE}",
259
- "DROP TABLE #{klass::OGTABLE}",
260
- "#{create_table_ddl(klass)}",
261
- "INSERT INTO #{klass::OGTABLE} SELECT #{field_list} FROM #{backup_table}",
262
- "DROP TABLE #{backup_table}",
263
- "COMMIT"
264
- ]
265
-
266
- sql_transaction.each do |s|
267
- Logger.debug s if $DBG
268
- @conn.query(s)
269
- end
270
- else
271
- Logger.warn "Table '#{klass::OGTABLE}' contains obsolete fields '#{fields_to_delete.to_a.join(', ')}' " +
272
- "and :evolve_schema is not set to true or is in cautious mode!"
273
- end
274
- end
275
- end
276
- end
277
-
278
- # Create join tables if needed. Join tables are used in
279
- # 'many_to_many' relations.
280
-
281
- if join_tables = klass.ann.self[:join_tables]
282
- for info in join_tables
283
- begin
284
- create_join_table_sql(info).each do |sql|
285
- @conn.query(sql).close
286
- end
287
- Logger.debug "Created jointable '#{info[:table]}'." if $DBG
288
- rescue Object => ex
289
- # gmosx: any idea how to better test this?
290
- if ex.to_s =~ /table .* already exists/i
291
- Logger.debug 'Join table already exists' if $DBG
292
- else
293
- raise
294
- end
295
- end
296
- end
297
- end
298
- end
299
-
300
- def create_field_map(klass)
301
- res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
302
- map = {}
303
-
304
- fields = res.columns
305
-
306
- # Check if the field should be ignored.
307
- ignore = klass.ann[:self][:ignore_field] || klass.ann[:self][:ignore_fields] || klass.ann[:self][:ignore_columns]
308
-
309
- res.fields.size.times do |i|
310
- field_name = fields[i].to_sym
311
- unless (ignore and ignore.include?(field_name))
312
- map[field_name] = i
313
- end
314
- end
315
-
316
- return map
317
- ensure
318
- res.close if res
319
- end
320
-
321
- def eval_og_insert(klass)
322
- pk = klass.primary_key.symbol
323
- props = klass.properties.values.dup
324
- values = props.collect { |p| write_prop(p) }.join(',')
325
-
326
- if klass.schema_inheritance?
327
- props << Property.new(:symbol => :ogtype, :klass => String)
328
- values << ", '#{klass}'"
329
- end
330
-
331
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
332
-
333
- klass.class_eval %{
334
- def og_insert(store)
335
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
336
- store.query("#{sql}").close
337
- @#{pk} = store.last_insert_rowid
338
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
339
- end
340
- }
341
- end
342
-
343
- end
344
-
345
- end
346
-
347
- # * George Moschovitis <gm@navel.gr>
348
- # * Ghislain Mary
@@ -1,241 +0,0 @@
1
- #--
2
- # TODO: extend from sqlite.rb to avoid code duplication.
3
- #++
4
-
5
- begin
6
- require 'sqlite'
7
- rescue Object => ex
8
- Logger.error 'Ruby-Sqlite bindings are not installed!'
9
- Logger.error ex
10
- end
11
-
12
- require 'fileutils'
13
-
14
- require 'og/store/sql'
15
-
16
- #--
17
- # Customize the standard Sqlite2 resultset to make
18
- # more compatible with Og.
19
- #++
20
-
21
- class SQLite::ResultSet # :nodoc: all
22
- alias_method :blank?, :eof?
23
-
24
- def each_row
25
- each do |row|
26
- yield(row, 0)
27
- end
28
- end
29
-
30
- def first_value
31
- val = self.next[0]
32
- close
33
- return val
34
- end
35
-
36
- alias_method :fields, :columns
37
- end
38
-
39
- module Og
40
-
41
- # A Store that persists objects into an Sqlite2 database.
42
- # To read documentation about the methods, consult the documentation
43
- # for SqlStore and Store.
44
-
45
- class Sqlite2Store < SqlStore
46
-
47
- # Override if needed.
48
-
49
- def self.db_filename(options)
50
- options[:name] ||= 'data'
51
- "#{options[:name]}.db"
52
- end
53
-
54
- def self.destroy(options)
55
- begin
56
- FileUtils.rm(db_filename(options))
57
- super
58
- rescue Object
59
- Logger.info "Cannot drop '#{options[:name]}'!"
60
- end
61
- end
62
-
63
- # Initialize the Sqlite store.
64
- # This store provides a default name.
65
-
66
- def initialize(options)
67
- super
68
- @conn = SQLite::Database.new(self.class.db_filename(options))
69
- end
70
-
71
- def close
72
- @conn.close
73
- super
74
- end
75
-
76
- def enchant(klass, manager)
77
- if klass.ann.self.primary_key.symbol == :oid
78
- unless klass.properties.include? :oid
79
- klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
80
- end
81
- end
82
-
83
- super
84
- end
85
-
86
- def query(sql)
87
- Logger.debug sql if $DBG
88
- return @conn.query(sql)
89
- rescue Exception => ex
90
- handle_sql_exception(ex, sql)
91
- end
92
-
93
- def exec(sql)
94
- Logger.debug sql if $DBG
95
- @conn.query(sql).close
96
- rescue => ex
97
- handle_sql_exception(ex, sql)
98
- end
99
-
100
- def start
101
- @conn.transaction if @transaction_nesting < 1
102
- @transaction_nesting += 1
103
- end
104
-
105
- def commit
106
- @transaction_nesting -= 1
107
- @conn.commit if @transaction_nesting < 1
108
- end
109
-
110
- def rollback
111
- @transaction_nesting -= 1
112
- @conn.rollback if @transaction_nesting < 1
113
- end
114
-
115
- def last_insert_rowid
116
- conn.query("SELECT last_insert_rowid()").first_value.to_i
117
- end
118
-
119
- def sql_update(sql)
120
- exec(sql)
121
- @conn.changes
122
- end
123
-
124
- private
125
-
126
- def create_table(klass)
127
- fields = fields_for_class(klass)
128
-
129
- sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
130
-
131
- # Create table constraints.
132
-
133
- if constraints = klass.ann.self[:sql_constraint]
134
- sql << ", #{constraints.join(', ')}"
135
- end
136
-
137
- sql << ");"
138
-
139
- # Create indices.
140
-
141
- if indices = klass.ann.self[:index]
142
- for data in indices
143
- idx, options = *data
144
- idx = idx.to_s
145
- pre_sql, post_sql = options[:pre], options[:post]
146
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
147
- sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
148
- end
149
- end
150
-
151
- begin
152
- @conn.query(sql).close
153
- Logger.info "Created table '#{klass::OGTABLE}'."
154
- rescue Object => ex
155
- # gmosx: any idea how to better test this?
156
- if ex.to_s =~ /table .* already exists/i
157
- Logger.debug 'Table already exists' if $DBG
158
- return
159
- else
160
- raise
161
- end
162
- end
163
-
164
- # Create join tables if needed. Join tables are used in
165
- # 'many_to_many' relations.
166
-
167
- if join_tables = klass.ann.self[:join_tables]
168
- for info in join_tables
169
- begin
170
- create_join_table_sql(info).each do |sql|
171
- @conn.query(sql).close
172
- end
173
- Logger.debug "Created jointable '#{info[:table]}'." if $DBG
174
- rescue Object => ex
175
- # gmosx: any idea how to better test this?
176
- if ex.to_s =~ /table .* already exists/i
177
- Logger.debug 'Join table already exists' if $DBG
178
- else
179
- raise
180
- end
181
- end
182
- end
183
- end
184
- end
185
-
186
- def create_field_map(klass)
187
- res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
188
- map = {}
189
-
190
- fields = res.columns
191
-
192
- # Check if the field should be ignored.
193
- ignore = klass.ann[:self][:ignore_field] || klass.ann[:self][:ignore_fields] || klass.ann[:self][:ignore_columns]
194
-
195
- res.fields.size.times do |i|
196
- field_name = fields[i].to_sym
197
- unless (ignore and ignore.include?(field_name))
198
- map[field_name] = i
199
- end
200
- end
201
-
202
- return map
203
- ensure
204
- res.close if res
205
- end
206
-
207
- def eval_og_insert(klass)
208
- pk = klass.primary_key.symbol
209
- props = klass.properties.values.dup
210
- values = props.collect { |p| write_prop(p) }.join(',')
211
-
212
- if klass.schema_inheritance?
213
- props << Property.new(:symbol => :ogtype, :klass => String)
214
- values << ", '#{klass}'"
215
- end
216
-
217
- sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
218
-
219
- klass.class_eval %{
220
- def og_insert(store)
221
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
222
- store.query("#{sql}").close
223
- @#{pk} = store.last_insert_rowid
224
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
225
- end
226
- }
227
- end
228
-
229
- end
230
-
231
- end
232
-
233
- # * George Moschovitis <gm@navel.gr>
234
- # * Ghislain Mary
235
- # * Mitchell Foral
236
-
237
-
238
-
239
-
240
-
241
-