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
@@ -15,5 +15,3 @@ class BelongsTo < RefersTo
15
15
  end
16
16
 
17
17
  end
18
-
19
- # * George Moschovitis <gm@navel.gr>
@@ -18,6 +18,8 @@ end
18
18
  # article.comments.size
19
19
 
20
20
  class HasMany < Relation
21
+ ann self, :collection => true # marker
22
+
21
23
  def resolve_polymorphic
22
24
  unless target_class.relations.empty?
23
25
  unless target_class.relations.find { |r| r.is_a?(BelongsTo) and r.target_class == owner_class }
@@ -27,7 +29,12 @@ class HasMany < Relation
27
29
  end
28
30
 
29
31
  def enchant
30
- self[:owner_singular_name] = owner_class.to_s.demodulize.underscore.downcase
32
+ self[:owner_singular_name] = if owner_class.schema_inheritance?
33
+ owner_class.schema_inheritance_root_class
34
+ else
35
+ owner_class
36
+ end.to_s.demodulize.underscore.downcase
37
+
31
38
  self[:target_singular_name] = target_plural_name.to_s.singular
32
39
  self[:foreign_key] = "#{foreign_name || owner_singular_name}_#{owner_class.primary_key}"
33
40
  # gmosx: DON'T set self[:foreign_field]
@@ -53,6 +60,25 @@ class HasMany < Relation
53
60
  @#{target_plural_name}.reload(options) if options and options[:reload]
54
61
  @#{target_plural_name}
55
62
  end
63
+
64
+ def #{target_plural_name}=(*args)
65
+ options = args.last.is_a?(Hash) ? args.pop : nil
66
+
67
+ if args.size == 1 && args.first.is_a?(HasManyCollection)
68
+ @#{target_plural_name} = args.first
69
+ @#{target_plural_name}.find_options = options if options
70
+
71
+ return @#{target_plural_name}
72
+ end
73
+
74
+ args.flatten!
75
+
76
+ args.each do |obj|
77
+ add_#{target_singular_name}(obj, options)
78
+ end
79
+
80
+ return #{target_plural_name}
81
+ end
56
82
 
57
83
  def add_#{target_singular_name}(obj, options = nil)
58
84
  return unless obj
@@ -89,6 +115,3 @@ class HasMany < Relation
89
115
  end
90
116
 
91
117
  end
92
-
93
- # * George Moschovitis <gm@navel.gr>
94
- # * Guillaunne Pierronnet <guillaunne.pierronnet@laposte.net>
@@ -14,10 +14,23 @@ end
14
14
  #
15
15
  # === Examples
16
16
  #
17
+ # joins_many :categories
17
18
  # joins_many Category
18
- # joins_many :categories, Category
19
+ # joins_many :categories, MyCategory
19
20
 
20
21
  class JoinsMany < Relation
22
+ ann self, :collection => true # marker
23
+
24
+ #--
25
+ # FIXME: better enchant polymorphic parents and then add
26
+ # an alias to :objects.
27
+ #++
28
+
29
+ def resolve_polymorphic
30
+ target_class.module_eval %{
31
+ joins_many :#{owner_class.to_s.demodulize.underscore.pluralize}
32
+ }
33
+ end
21
34
 
22
35
  def enchant
23
36
  self[:owner_singular_name] = owner_class.to_s.demodulize.underscore.downcase
@@ -32,22 +45,44 @@ class JoinsMany < Relation
32
45
  join_class = sclass
33
46
  end
34
47
  join_table_info = store.join_table_info(self)
35
-
48
+
36
49
  if through = self[:through]
37
50
  # A custom class is used for the join. Use the class
38
51
  # table and don't create a new table.
39
52
  through = Relation.symbol_to_class(through, owner_class) if through.is_a?(Symbol)
40
53
  join_table = self[:join_table] = store.table(through)
54
+
55
+ tmp_join_class = through
41
56
  else
42
- # Calculate the name of the join table
57
+ # Calculate the name of the join table.
43
58
  join_table = self[:join_table] = join_table_info[:table]
44
59
  # Create a join table.
45
- owner_class.ann :self, :join_tables => [] if owner_class.ann.self.join_tables.nil?
60
+ owner_class.ann :self, :join_tables => [] unless owner_class.ann.self.join_tables?
46
61
  owner_class.ann.self.join_tables! << join_table_info
62
+
63
+ # Create temporary classes to be able to use ann.descendants to cascade
64
+ # delete joins_many relations when one side of the relation is removed.
65
+
66
+ tmp_join_class_name = "OgTempJ_#{join_table}"
67
+
68
+ unless self.class.constants.include?(tmp_join_class_name)
69
+ tmp_join_class = self.class.const_set(tmp_join_class_name, Class.new)
70
+ tmp_join_class.const_set('OGTABLE', join_table)
71
+ else
72
+ tmp_join_class = self.class.const_get(tmp_join_class_name)
73
+ end
74
+
47
75
  end
48
76
 
49
77
  owner_key = join_table_info[:owner_key]
50
78
  target_key = join_table_info[:target_key]
79
+
80
+ # Add join class to ann.descendants to be able to use cascade deletes.
81
+
82
+ unless owner_class.ann.self[:descendants]
83
+ owner_class.ann(:self, :descendants => [])
84
+ end
85
+ owner_class.ann.self.descendants! << [tmp_join_class, owner_key]
51
86
 
52
87
  owner_class.module_eval %{
53
88
  attr_accessor :#{target_plural_name}
@@ -96,17 +131,13 @@ class JoinsMany < Relation
96
131
  #{target_class}.find(find_options)
97
132
  end
98
133
 
99
- #--
100
- # FIXME/TODO: optimize this!! use count.
101
- #++
102
-
103
134
  def count_#{target_plural_name}
104
135
  find_options = {
105
136
  :join_table => "#{join_table}",
106
137
  :join_condition => "#{join_table}.#{target_key}=\#{#{target_class}::OGTABLE}.oid",
107
- :condition => "#{join_table}.#{owner_key}=\#\{#{owner_class.primary_key}\}"
138
+ :condition => "#{join_table}.#{owner_key}=\#\{#{owner_class.primary_key}\}"
108
139
  }
109
- #{target_class}.find(find_options).size
140
+ #{target_class}.count(find_options)
110
141
  end
111
142
  }
112
143
 
@@ -122,7 +153,3 @@ class JoinsMany < Relation
122
153
  end
123
154
 
124
155
  end
125
-
126
- # * George Moschovitis <gm@navel.gr>
127
- # * Ysabel <deb@ysabel.org>
128
- # * Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
@@ -12,6 +12,16 @@ module Og
12
12
  # many_to_many :categories, Category
13
13
 
14
14
  class ManyToMany < JoinsMany
15
+ #--
16
+ # FIXME: better enchant polymorphic parents and then add
17
+ # an alias to :objects.
18
+ #++
19
+
20
+ def resolve_polymorphic
21
+ target_class.module_eval %{
22
+ many_to_many :#{owner_class.to_s.demodulize.underscore.pluralize}
23
+ }
24
+ end
15
25
  end
16
26
 
17
27
  end
@@ -2,6 +2,15 @@ require 'og/relation'
2
2
 
3
3
  module Og
4
4
 
5
+ # A refers to relation. Stores the foreign key in the object.
6
+ #--
7
+ # TODO: No need to save user (should only call save aspects, for
8
+ # example sweepers)
9
+ #
10
+ # THINK: Investigate if we should only store the key in the
11
+ # foreign object.
12
+ #++
13
+
5
14
  class RefersTo < Relation
6
15
 
7
16
  def self.foreign_key(rel)
@@ -17,9 +26,14 @@ class RefersTo < Relation
17
26
  field = ", :field => :#{self[:field]}"
18
27
  end
19
28
 
29
+ target_primary_key_class = target_class.ann(target_class.primary_key).class
30
+
31
+ join_table_info = owner_class.ogmanager.store.join_table_info(self)
32
+ owner_key = join_table_info[:owner_key]
33
+
20
34
  owner_class.module_eval %{
21
35
  attr_accessor :#{target_singular_name}
22
- prop_accessor :#{foreign_key}, #{target_class.primary_key.klass}#{field}, :relation => true
36
+ attr_accessor :#{foreign_key}, #{target_primary_key_class}#{field}, :relation => true
23
37
 
24
38
  def #{target_singular_name}(reload = false)
25
39
  return nil if @#{foreign_key}.nil?
@@ -32,7 +46,13 @@ class RefersTo < Relation
32
46
  end
33
47
 
34
48
  def #{target_singular_name}=(obj)
35
- @#{foreign_key} = obj.#{target_class.primary_key} if obj
49
+ if obj
50
+ @#{foreign_key} = obj.#{target_class.primary_key}
51
+ obj.#{owner_key} = pk if obj.respond_to?(:#{owner_key}=)
52
+ # obj.save
53
+ save unless self.unsaved?
54
+ end
55
+ return obj
36
56
  end
37
57
  }
38
58
  end
@@ -40,6 +60,3 @@ class RefersTo < Relation
40
60
  end
41
61
 
42
62
  end
43
-
44
- # * George Moschovitis <gm@navel.gr>
45
- # * Michael Neumann <mneumann@ntecs.de>
@@ -1,161 +1,164 @@
1
1
  module Og
2
2
 
3
- # A Store is responsible for the peristance of the ObjectGraph.
3
+ # A Store is a backend of Og used to persist objects. An
4
+ # adapter specializes the Store. For example the SQL Store
5
+ # has the MySQL, PostgreSQL, Sqlite3 etc adapters as
6
+ # specializations.
4
7
 
5
8
  class Store
6
9
 
7
- # Options.
8
-
9
- attr_accessor :options
10
-
11
- # Transaction nesting.
12
-
13
- attr_accessor :transaction_nesting
14
-
15
- # :section: Store methods.
16
-
17
- # Return the store for the given name.
18
-
19
- def self.for_name(name)
20
- Logger.info "Og uses the #{name.to_s.capitalize} store."
21
- # gmosx: to keep RDoc happy.
22
- require('og/store/' + name.to_s)
23
- return Og.const_get("#{name.to_s.capitalize}Store")
24
- end
25
-
26
- # Creates a store.
27
-
28
- def self.create(options)
29
- end
30
-
31
- # Destroys a store.
32
-
33
- def self.destroy(options)
34
- end
35
-
36
- # :section: Misc methods.
10
+ attr_accessor :ogmanager
37
11
 
38
12
  # Create a session to the store.
39
-
40
- def initialize(options)
41
- @options = options
13
+ #
14
+ # === Default options
15
+ #
16
+ # * :evolve_schema => :warn
17
+
18
+ def initialize options
19
+ @options = {
20
+ :evolve_schema => :warn
21
+ }.merge(options)
42
22
  @transaction_nesting = 0
43
23
  end
44
24
 
45
25
  # Close the session to the store.
46
26
 
47
27
  def close
28
+ raise 'Not implemented'
48
29
  end
49
-
30
+
50
31
  # Enchants a class.
51
32
 
52
33
  def enchant(klass, manager)
53
- # gmosx, FIXME:
54
- # ARGH, have to update this!
55
- #
56
- # klass.class.send(:define_method, :index) do |arg|
57
- # meta :index, arg
58
- # end
59
-
60
- pk = klass.primary_key.symbol
61
-
34
+ pk = klass.primary_key
62
35
  klass.module_eval %{
36
+
37
+ # A managed object is considered saved if it has
38
+ # a valid primary key.
39
+
63
40
  def saved?
64
- return #{klass.primary_key.symbol}
41
+ return #{pk}
65
42
  end
66
43
 
44
+ # The inverse of saved.
45
+
67
46
  def unsaved?
68
- return !#{klass.primary_key.symbol}
47
+ return !#{pk}
69
48
  end
70
49
 
71
50
  # Evaluate an alias for the primary key.
72
51
 
73
52
  alias_method :pk, :#{pk}
74
53
  alias_method :pk=, :#{pk}=
75
-
76
- def self.pk_symbol
77
- :#{klass.primary_key.symbol}
78
- end
79
54
  }
80
-
81
55
  end
82
-
56
+
83
57
  # :section: Lifecycle methods.
84
58
 
85
59
  # Loads an object from the store using the primary key.
86
60
 
87
61
  def load(pk, klass)
62
+ raise 'Not implemented'
88
63
  end
64
+ alias_method :exist?, :load
89
65
 
90
66
  # Reloads an object from the store.
91
67
 
92
68
  def reload(obj)
69
+ raise 'Not implemented'
93
70
  end
94
71
 
95
72
  # Save an object to store. Insert if this is a new object or
96
- # update if this is already inserted in the database.
73
+ # update if this is already inserted in the database. Checks
74
+ # if the object is valid before saving. Returns false if the
75
+ # object is invalid and populates obj.errors.
97
76
 
98
77
  def save(obj, options = nil)
99
78
  return false unless obj.valid?
100
79
 
101
80
  if obj.saved?
102
- obj.og_update(self, options)
81
+ update_count = obj.og_update(self, options)
103
82
  else
104
- obj.og_insert(self)
83
+ update_count = 1 if obj.og_insert(self)
105
84
  end
85
+
86
+ # Save building collections if any.
87
+ obj.save_building_collections
88
+
89
+ return update_count
106
90
  end
107
91
  alias_method :<<, :save
108
92
  alias_method :validate_and_save, :save
109
93
 
94
+ # Force the persistence of an Object. Ignore any validation
95
+ # and/or other errors.
96
+
110
97
  def force_save!(obj, options)
111
98
  if obj.saved?
112
- obj.og_update(self, options)
99
+ obj.og_update self, options
113
100
  else
114
- obj.og_insert(self)
101
+ obj.og_insert self
115
102
  end
116
103
  end
117
104
 
118
105
  # Insert an object in the store.
119
106
 
120
107
  def insert(obj)
121
- obj.og_insert(self)
108
+ obj.og_insert self
122
109
  end
123
110
 
124
111
  # Update an object in the store.
125
112
 
126
- def update(obj, options = nil)
127
- obj.og_update(self, options)
113
+ def update obj, options = nil
114
+ obj.og_update self, options
128
115
  end
129
116
 
130
- # Update selected properties of an object or class of
117
+ # Update selected attributes of an object or class of
131
118
  # objects.
132
-
133
- def update_properties(obj_or_class, props, options = nil)
119
+
120
+ def update_attributes target, *attributes
121
+ update(target, :only => attributes)
134
122
  end
135
- alias_method :pupdate, :update_properties
136
- alias_method :update_property, :update_properties
123
+ alias_method :aupdate, :update_attributes
124
+ alias_method :update_attribute, :update_attributes
137
125
 
138
126
  # Permanently delete an object from the store.
139
127
 
140
128
  def delete(obj_or_pk, klass = nil, cascade = true)
141
- unless obj_or_pk.is_a?(EntityMixin)
142
- # create a dummy instance to keep the og_delete
129
+ unless obj_or_pk.is_a? EntityMixin
130
+ # create an instance to keep the og_delete
143
131
  # method as an instance method like the other lifecycle
144
- # methods.
145
- klass.allocate.og_delete(self, obj_or_pk, cascade)
132
+ # methods. This even allows to call og_delete aspects
133
+ # that use instance variable (for example, sophisticated
134
+ # cache sweepers).
135
+ #
136
+ # gmosx: the following is not enough!
137
+ # obj = klass.allocate
138
+ # obj.pk = obj_or_pk
139
+ obj = klass[obj_or_pk]
140
+ obj.og_delete(self, cascade)
146
141
  else
147
- obj_or_pk.og_delete(self, nil, cascade)
142
+ obj_or_pk.og_delete(self, cascade)
148
143
  end
149
144
  end
150
145
 
146
+ # Delete all instances of the given class.
147
+
148
+ def delete_all(klass)
149
+ raise 'Not implemented'
150
+ end
151
+
151
152
  # Perform a query.
152
153
 
153
154
  def find(klass, options)
155
+ raise 'Not implemented'
154
156
  end
155
157
 
156
158
  # Count the results returned by the query.
157
159
 
158
160
  def count(options)
161
+ raise 'Not implemented'
159
162
  end
160
163
 
161
164
  # :section: Transaction methods.
@@ -191,7 +194,7 @@ class Store
191
194
  start
192
195
  yield(self)
193
196
  commit
194
- rescue => ex
197
+ rescue Object => ex
195
198
  rollback
196
199
  raise ex
197
200
  end
@@ -199,22 +202,13 @@ class Store
199
202
 
200
203
  private
201
204
 
202
- def eval_og_insert(klass)
203
- end
204
-
205
- def eval_og_update(klass)
206
- end
207
-
208
- def eval_og_read(klass)
209
- end
210
-
211
- def eval_og_delete(klass)
212
- end
213
-
214
- def eval_og_create_schema(klass)
205
+ #--
206
+ # A helper for inserting advices in the precompiled methods.
207
+ #++
208
+
209
+ def insert_advices where, target, klass, advices # :nodoc: all
210
+ Aspects.gen_advice_code(target, klass.send(advices), where) if klass.respond_to?(advices)
215
211
  end
216
212
  end
217
213
 
218
214
  end
219
-
220
- # * George Moschovitis <gm@navel.gr>