og 0.31.0 → 0.40.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 (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,218 +0,0 @@
1
- # NOT WORKING YET !!!
2
-
3
- raise 'This is not working yet, do not require this file.'
4
-
5
- # A useful encapsulation of the nested intervals pattern for
6
- # hierarchical SQL queries. Slightly adapted from the original
7
- # article (http://www.dbazine.com/tropashko4.shtml)
8
-
9
- module TreeTraversal
10
-
11
- # The default prefix for the tree traversal helpers.
12
-
13
- cattr_accessor :prefix, 'tree'
14
-
15
- def self.child(sum, n)
16
- power = 2 ** n
17
- return sum * power
18
- end
19
-
20
- end
21
-
22
- __END__
23
-
24
- def xcoord(numer, denom)
25
- num = numer + 1
26
- den = denom * 2
27
-
28
- while (num / 2).floor == (num / 2)
29
- num /= 2
30
- den /= 2
31
- end
32
-
33
- return num, den
34
- end
35
-
36
- #--
37
- # TODO: optimize this
38
- #++
39
-
40
- def ycoord(numer, denom)
41
- num, den = xcoord(numer, denom)
42
-
43
- while den < denom
44
- num *= 2
45
- den *= 2
46
- end
47
-
48
- num = numer - num
49
-
50
- while (num / 2).floor == (num / 2)
51
- num /= 2
52
- den /= 2
53
- end
54
-
55
- return num, den
56
- end
57
-
58
- def parent(numer, denom)
59
- return nil if numer == 3
60
-
61
- num = (numer - 1) / 2
62
- den = denom / 2
63
-
64
- while ((num-1)/4).floor == ((num-1)/4)
65
- num = (num + 1) / 2
66
- den = den / 2
67
- end
68
-
69
- return num, den
70
- end
71
-
72
- def sibling(numer, denom)
73
- return nil if numer == 3
74
-
75
- num = (numer - 1) / 2
76
- den = denom / 2
77
- sib = 1
78
-
79
- while ((num-1)/4).floor == ((num-1)/4)
80
- return sib if num == 1 and den == 1
81
- num = (num + 1) / 2
82
- den /= 2
83
- sib += 1
84
- end
85
-
86
- return sib
87
- end
88
-
89
- def child(numer, denom, n)
90
- power = 2 ** n
91
-
92
- num = (numer * power) + 3 - power
93
- den = denom * power
94
-
95
- return num, den
96
- end
97
-
98
- def path(numer, denom)
99
- return '' if numer == nil
100
- n, d = parent(numer, denom)
101
- return "#{path(n, d)}.#{sibling(numer, denom)}"
102
- end
103
-
104
- def encode(path)
105
- num = den = 1
106
- postfix = ".#{path}."
107
-
108
- while postfix.length > 1
109
- sibling, postfix = postfix.split('.', 2)
110
- num, den = child(num, den, sibling.to_i)
111
- end
112
-
113
- return num, den
114
- end
115
-
116
-
117
- require 'og'
118
-
119
- class Comment
120
- property :path, String
121
- property :x, :y, Float
122
-
123
- def initialize(path = nil, x = nil, y = nil)
124
- @path, @x, @y = path, x, y
125
- end
126
- end
127
-
128
- Og::Database.new(
129
- :database => 'test',
130
- :adapter => 'psql',
131
- :user => 'postgres',
132
- :password => 'navelrulez',
133
- :connection_count => 1,
134
- :drop => true
135
- )
136
-
137
- def dp(path)
138
- n, d = encode(path)
139
- n, d = Float(n), Float(d)
140
- # puts "#{path} -> n: #{n} d: #{d} c: #{n/d}"
141
- p = path(n, d)
142
- # puts "=== #{p}"
143
- xn, xd = xcoord(n, d)
144
- yn, yd = ycoord(n, d)
145
- Comment.create(path, xn/xd, yn/yd)
146
- end
147
-
148
- dp '1.1'
149
- dp '1.2'
150
- dp '1.3'
151
- dp '1.1.2.1'
152
- dp '1.5.2.1'
153
- dp '1.2.1.1.1'
154
- dp '1.4.1.1'
155
- dp '1.4.3'
156
- dp '1.4.1'
157
- dp '1.2.1'
158
- dp '1.2.1.2'
159
- dp '1.5'
160
- dp '1.5.1'
161
-
162
- for c in Comment.all('ORDER BY x DESC, y ASC')
163
- puts "#{c.path.ljust(16)}#{c.inspect}"
164
- end
165
-
166
- class Article
167
- property :title, :body, String
168
- has_many: :comments, Comment, :tree => true
169
- end
170
-
171
- class Comment
172
- property :body, String
173
- belongs_to :article, Article
174
- belongs_to :parent, Comment
175
- has_many :children, Comment, :tree => true
176
- has_many :roles, Role, :list => true
177
- end
178
-
179
-
180
- article.comments_tree
181
- comment.add_child(Comment.new('hello'))
182
- comment.children_tree
183
- comment.children
184
-
185
- if options[:tree]
186
- code << %{
187
- property :#{prefix}_x, Fixnum
188
- property :#{prefix}_y, Fixnum
189
- sql_index '#{prefix}_x, #{prefix}_y'
190
- }
191
- end
192
-
193
- if options[:tree]
194
-
195
- code << %{
196
- def #{name}_tree(extrasql = nil)
197
- Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\} ORDER BY #{prefix}_x DESC, #{prefix}_y ASC", #{klass})
198
- end
199
- }
200
-
201
- elsif options[:list]
202
-
203
- else
204
-
205
- end
206
-
207
- }
208
-
209
- if options[:tree]
210
- code << %{
211
- n = Og.db.count("#{linkback}=\#\@oid", #{klass})
212
- ptx = @#{prefix}_x || 0
213
- pty = @#{prefix}_y || 0
214
- obj.#{prefix}_x, obj.#{prefix}_y = TreeTraversal.child(ptx + pty, n)
215
- }
216
- end
217
-
218
- code << %{
@@ -1,110 +0,0 @@
1
- require 'yaml'
2
- require 'fileutils'
3
-
4
- require 'og/store'
5
-
6
- module Og
7
-
8
- # A Store that saves the object on the Filesystem.
9
- # An extension of the Memory store.
10
-
11
- class FilesysStore < Store
12
-
13
- # :section: Store methods.
14
-
15
- def self.dir(name)
16
- "#{name}_db"
17
- end
18
-
19
- def self.create(options)
20
- name = dir(options[:name])
21
- FileUtils.mkdir_p(name)
22
- rescue
23
- Logger.error "Cannot create '#{name}'"
24
- end
25
-
26
- def self.destroy(options)
27
- name = dir(options[:name])
28
- FileUtils.rm_rf(name)
29
- rescue
30
- Logger.error "Cannot destroy '#{name}'"
31
- end
32
-
33
- # Initialize a connection to this store.
34
-
35
- def initialize(options)
36
- super
37
-
38
- @base_dir = self.class.dir(options[:name])
39
-
40
- unless File.directory?(@base_dir)
41
- Logger.info "Database '#{options[:name]}' not found!"
42
- self.class.create(options)
43
- end
44
- end
45
-
46
- def enchant(klass, manager)
47
- super
48
-
49
- klass.const_set 'OGNAME', klass.to_s.gsub(/::/, "/").downcase
50
- klass.property :oid, Fixnum
51
-
52
- FileUtils.mkdir_p(path(klass))
53
- File.open(spath(klass), 'w') { |f| f << '1' }
54
- rescue => ex
55
- Logger.error "Cannot create directory to store '#{klass}' classes!"
56
- end
57
-
58
- # :section: Lifecycle methods.
59
-
60
- def load(oid, klass)
61
- obj = nil
62
- File.open(path(klass, oid), 'r') { |f| obj = YAML::load(f.read) }
63
- return obj
64
- rescue
65
- return nil
66
- end
67
-
68
- def reload(obj)
69
- end
70
-
71
- def save(obj)
72
- seq = nil
73
- File.open(spath(obj.class), 'r') { |f| seq = f.read.to_i }
74
- obj.oid = seq
75
- File.open(path(obj.class, obj.oid), 'w') { |f| f << obj.to_yaml }
76
- seq += 1
77
- File.open(spath(obj.class), 'w') { |f| f << seq }
78
- end
79
-
80
- def delete(obj_or_pk, klass = nil, cascade = true)
81
- pk = obj_or_pk.is_a?(Fixnum) ? obj_or_pk : obj_or_pk.oid
82
- klass ||= obj_or_pk.class
83
- FileUtils.rm(path(klass, pk))
84
- end
85
-
86
- def find
87
- end
88
-
89
- def count
90
- end
91
-
92
- private
93
-
94
- # Path helper.
95
-
96
- def path(klass, pk = nil)
97
- class_dir = File.join(@base_dir, klass::OGNAME)
98
- pk ? File.join(class_dir, "#{pk}.yml") : class_dir
99
- end
100
-
101
- # Path to sequence helper.
102
-
103
- def spath(klass)
104
- File.join(path(klass), 'seq')
105
- end
106
-
107
- end
108
-
109
- end
110
-
@@ -1,295 +0,0 @@
1
- require 'yaml'
2
- require 'fileutils'
3
-
4
- module Og
5
-
6
- # A collection of utilities. Mainly to stay compatible with the
7
- # SQL based backends.
8
- #
9
- # WARNING: This store does not yet support all Og features.
10
-
11
- module MemoryUtils
12
-
13
- # FIXME: find a neutral name.
14
-
15
- def table(klass)
16
- klass.to_s
17
- end
18
-
19
- # FIXME: find a neutral name.
20
-
21
- def join_table(class1, class2, postfix = nil)
22
- "#{class1}#{class2}"
23
- end
24
-
25
- def quote(val)
26
- val.inspect
27
- end
28
-
29
- end
30
-
31
- # A Store that 'persists' objects into (RAM) memory.
32
- #--
33
- # TODO: fully working query.
34
- # TODO: arbitrary pk.
35
- #++
36
-
37
- class MemoryStore < Store
38
- extend MemoryUtils; include MemoryUtils
39
-
40
- class ObjectHash < Hash
41
- def [](key)
42
- super(key.to_s)
43
- end
44
-
45
- def []=(key, value)
46
- super(key.to_s, value)
47
- end
48
- end
49
-
50
- # This hash implements an in-memory database.
51
-
52
- @objects = ObjectHash.new
53
-
54
- def self.objects
55
- @objects
56
- end
57
-
58
- def self.objects=(val)
59
- @objects = val
60
- end
61
-
62
- # A pseudo-connection to the actual object store.
63
-
64
- attr_accessor :conn
65
-
66
- def self.destroy(options)
67
- FileUtils.rm_rf("#{options[:name]}.db")
68
- rescue
69
- Logger.info "Cannot destroy '#{name}'"
70
- end
71
-
72
- def initialize(options)
73
- super
74
- begin
75
- @conn = self.class.objects = YAML.load_file("#{@options[:name]}.db")
76
- rescue
77
- @conn = self.class.objects = ObjectHash.new
78
- end
79
- end
80
-
81
- def close
82
- File.open("#{@options[:name]}.db", 'w') { |f| f << @conn.to_yaml }
83
- end
84
-
85
- # Enchants a class.
86
-
87
- def enchant(klass, manager)
88
- klass.property :oid, Fixnum
89
-
90
- super
91
-
92
- @conn[klass] ||= {}
93
-
94
- eval_og_insert(klass)
95
- eval_og_update(klass)
96
- eval_og_read(klass)
97
- eval_og_delete(klass)
98
- end
99
-
100
- # :section: Lifecycle methods.
101
-
102
- # Loads an object from the store using the primary key.
103
-
104
- def load(pk, klass)
105
- @conn[klass][pk]
106
- end
107
-
108
- # Update selected properties of an object or class of
109
- # objects.
110
-
111
- def update_properties(target, set, options = nil)
112
- set = set.gsub(/,/, ';')
113
- if target.is_a?(Class)
114
- if options
115
- condition = options[:condition] || options[:where]
116
- else
117
- condition = 'true'
118
- end
119
- for obj in @conn[target].values
120
- obj.instance_eval %{
121
- if #{condition.gsub(/=/, '==')}
122
- #{set}
123
- end
124
- }
125
- end
126
- else
127
- target.instance_eval(set)
128
- end
129
- end
130
- alias_method :pupdate, :update_properties
131
- alias_method :update_property, :update_properties
132
-
133
- # Find a collection of objects.
134
- #
135
- # === Examples
136
- #
137
- # User.find(:condition => 'age > 15', :order => 'score ASC', :offet => 10, :limit =>10)
138
- # Comment.find(:include => :entry)
139
- # store.find(:class => User, :where => 'age > 15')
140
-
141
- def find(options)
142
- query(options)
143
- end
144
-
145
- # Find one object.
146
-
147
- def find_one(options)
148
- query(options).first
149
- end
150
-
151
- # Reloads an object from the store.
152
-
153
- def reload(obj, pk)
154
- # Nop, the memory store is always synchronized.
155
- end
156
-
157
- # Count results.
158
-
159
- def count(options)
160
- objects = 0
161
-
162
- if condition = options[:condition] || options[:where]
163
- condition = "obj." + condition.gsub(/=/, '==')
164
- else
165
- condition = true
166
- end
167
-
168
- eval %{
169
- for obj in @conn[options[:class]].values
170
- objects += 1 if #{condition}
171
- end
172
- }
173
-
174
- return objects
175
- end
176
-
177
- # Relate two objects through an intermediate join table.
178
- # Typically used in joins_many and many_to_many relations.
179
-
180
- def join(obj1, obj2, table)
181
- # nop
182
- end
183
-
184
- # Query.
185
-
186
- def query(options)
187
- objects = []
188
-
189
- if condition = options[:condition] || options[:where]
190
- condition = "obj." + condition.gsub(/=/, '==')
191
- else
192
- condition = true
193
- end
194
-
195
- eval %{
196
- for obj in @conn[options[:class]].values
197
- objects << obj if #{condition}
198
- end
199
- }
200
-
201
- if order = options[:order]
202
- desc = (order =~ /DESC/)
203
- order = order.gsub(/DESC/, '').gsub(/ASC/, '')
204
- eval "objects.sort { |x, y| x.#{order} <=> y.#{order} }"
205
- objects.reverse! if desc
206
- end
207
-
208
- return objects
209
- end
210
-
211
- # :section: Transaction methods.
212
-
213
- # Start a new transaction.
214
-
215
- def start
216
- end
217
-
218
- # Commit a transaction.
219
-
220
- def commit
221
- end
222
-
223
- # Rollback a transaction.
224
-
225
- def rollback
226
- end
227
-
228
- private
229
-
230
- # :section: Lifecycle method compilers.
231
-
232
- # Compile the og_update method for the class.
233
-
234
- def eval_og_insert(klass)
235
- pk = klass.primary_key.first
236
-
237
- klass.class_eval %{
238
- def og_insert(store)
239
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
240
- @#{pk} = store.conn[#{klass}].size + 1
241
- store.conn[#{klass}][@#{pk}] = self
242
- #{::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
243
- end
244
- }
245
- end
246
-
247
- # Compile the og_update method for the class.
248
-
249
- def eval_og_update(klass)
250
- pk = klass.primary_key.first
251
-
252
- klass.class_eval %{
253
- def og_update(store, options)
254
- #{::Aspects.gen_advice_code(:og_update, klass.advices, :pre) if klass.respond_to?(:advices)}
255
- store.conn[#{klass}][@#{pk}] = self
256
- #{::Aspects.gen_advice_code(:og_update, klass.advices, :post) if klass.respond_to?(:advices)}
257
- end
258
- }
259
- end
260
-
261
- # Not really useful in this store, kept for compatibility,
262
- # just to call the aspects.
263
-
264
- def eval_og_read(klass)
265
- klass.class_eval %{
266
- def og_read
267
- #{::Aspects.gen_advice_code(:og_read, klass.advices, :pre) if klass.respond_to?(:advices)}
268
- #{::Aspects.gen_advice_code(:og_read, klass.advices, :post) if klass.respond_to?(:advices)}
269
- end
270
- }
271
- end
272
-
273
- def eval_og_delete(klass)
274
- klass.module_eval %{
275
- def og_delete(store, pk, cascade = true)
276
- #{::Aspects.gen_advice_code(:og_delete, klass.advices, :pre) if klass.respond_to?(:advices)}
277
- pk ||= @#{klass.primary_key.first}
278
- transaction do |tx|
279
- tx.conn[#{klass}].delete(pk)
280
- if cascade and #{klass}.__meta[:descendants]
281
- #{klass}.__meta[:descendants].each do |dclass, foreign_key|
282
- eval "tx.conn[dclass].delete_if { |dobj| dobj.\#{foreign_key} == \#{pk} }"
283
- end
284
- end
285
- end
286
- #{::Aspects.gen_advice_code(:og_delete, klass.advices, :post) if klass.respond_to?(:advices)}
287
- end
288
- }
289
- end
290
-
291
- end
292
-
293
- end
294
-
295
- # * George Moschovitis <gm@navel.gr>