og 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/ProjectInfo +58 -0
  2. data/README +5 -4
  3. data/Rakefile +2 -2
  4. data/doc/AUTHORS +10 -7
  5. data/doc/RELEASES +108 -0
  6. data/lib/og.rb +1 -3
  7. data/lib/og/collection.rb +4 -4
  8. data/lib/og/entity.rb +96 -27
  9. data/lib/og/evolution.rb +78 -0
  10. data/lib/og/manager.rb +29 -32
  11. data/lib/og/mixin/hierarchical.rb +1 -1
  12. data/lib/og/mixin/optimistic_locking.rb +5 -8
  13. data/lib/og/mixin/orderable.rb +15 -2
  14. data/lib/og/mixin/schema_inheritance_base.rb +12 -0
  15. data/lib/og/mixin/taggable.rb +29 -25
  16. data/lib/og/mixin/timestamped.rb +4 -2
  17. data/lib/og/mixin/tree.rb +0 -1
  18. data/lib/og/relation.rb +161 -116
  19. data/lib/og/relation/all.rb +6 -0
  20. data/lib/og/relation/belongs_to.rb +4 -1
  21. data/lib/og/relation/has_many.rb +6 -5
  22. data/lib/og/relation/joins_many.rb +13 -12
  23. data/lib/og/relation/refers_to.rb +3 -3
  24. data/lib/og/store.rb +9 -9
  25. data/lib/og/store/{filesys.rb → alpha/filesys.rb} +0 -0
  26. data/lib/og/store/alpha/kirby.rb +284 -0
  27. data/lib/og/store/{memory.rb → alpha/memory.rb} +2 -0
  28. data/lib/og/store/{sqlserver.rb → alpha/sqlserver.rb} +6 -6
  29. data/lib/og/store/kirby.rb +145 -162
  30. data/lib/og/store/mysql.rb +58 -27
  31. data/lib/og/store/psql.rb +15 -13
  32. data/lib/og/store/sql.rb +136 -135
  33. data/lib/og/store/sqlite.rb +13 -12
  34. data/lib/og/validation.rb +2 -2
  35. data/lib/vendor/kbserver.rb +20 -0
  36. data/lib/vendor/kirbybase.rb +2790 -1601
  37. data/test/og/CONFIG.rb +79 -0
  38. data/test/og/mixin/tc_hierarchical.rb +1 -1
  39. data/test/og/mixin/tc_optimistic_locking.rb +1 -3
  40. data/test/og/mixin/tc_orderable.rb +42 -1
  41. data/test/og/mixin/tc_taggable.rb +1 -1
  42. data/test/og/mixin/tc_timestamped.rb +1 -1
  43. data/test/og/store/tc_filesys.rb +1 -2
  44. data/test/og/tc_delete_all.rb +45 -0
  45. data/test/og/tc_inheritance.rb +10 -38
  46. data/test/og/tc_join.rb +2 -11
  47. data/test/og/tc_multiple.rb +3 -16
  48. data/test/og/tc_override.rb +3 -3
  49. data/test/og/tc_polymorphic.rb +3 -13
  50. data/test/og/tc_relation.rb +8 -6
  51. data/test/og/tc_reverse.rb +2 -11
  52. data/test/og/tc_select.rb +2 -15
  53. data/test/og/tc_store.rb +4 -63
  54. data/test/og/tc_types.rb +1 -2
  55. metadata +80 -77
@@ -0,0 +1,78 @@
1
+ require 'fileutils'
2
+
3
+ require 'nano/kernel/constant'
4
+ require 'nano/kernel/assign_with'
5
+
6
+ # $DBG = true
7
+
8
+ module Og
9
+
10
+ class Manager
11
+
12
+ # Dump Og managed objects to the filesystem.
13
+
14
+ def dump(options = {})
15
+ classes = options[:classes] || options[:class] || manageable_classes
16
+ basedir = options[:basedir] || 'ogdump'
17
+
18
+ FileUtils.makedirs(basedir)
19
+
20
+ for c in [ classes ].flatten
21
+ Logger.info "Dumping class '#{c}'"
22
+ all = c.all.map { |obj| obj.properties_to_hash }
23
+ File.open("#{basedir}/#{c}.yml", 'w') { |f| f << all.to_yaml }
24
+ end
25
+ end
26
+ alias_method :export, :dump
27
+
28
+ # Load Og managed objects from the filesystem. This method can apply
29
+ # optional transformation rules in order to evolve a schema.
30
+
31
+ def load(options = {})
32
+ classes = options[:classes] || manageable_classes
33
+ basedir = options[:basedir] || 'ogdump'
34
+ rules = options[:rules] || rules[:evolution] || {}
35
+
36
+ classes.each { |c| c.destroy if managed?(c) }
37
+ unmanage_classes(classes)
38
+ manage_classes
39
+
40
+ for f in Dir["#{basedir}/*.yml"]
41
+ all = YAML.load(File.read(f))
42
+
43
+ unless all.empty?
44
+ klass = f.split(/\/|\./)[1]
45
+
46
+ Logger.info "Loading class '#{klass}'"
47
+
48
+ if krules = rules[klass.to_sym]
49
+ if krules[:self]
50
+ # Class name changed.
51
+ Logger.info "Renaming class '#{klass}' to '#{krules[:self]}'"
52
+ klass = krules[:self]
53
+ end
54
+
55
+ Logger.info "Evolution transformation will be applied!"
56
+ end
57
+
58
+ klass = constant(klass)
59
+
60
+ for h in all
61
+ obj = klass.allocate
62
+ obj.assign_with(h)
63
+ if krules
64
+ krules.each do |old, new|
65
+ obj.instance_variable_set "@#{new}", h[old]
66
+ end
67
+ end
68
+ obj.insert
69
+ end
70
+ end
71
+ end
72
+ end
73
+ alias_method :import, :load
74
+ alias_method :evolve, :load
75
+
76
+ end
77
+
78
+ end
@@ -1,4 +1,5 @@
1
1
  require 'mega/pool'
2
+ require 'nano/class/descendents'
2
3
 
3
4
  require 'og/entity'
4
5
  require 'og/store'
@@ -86,17 +87,6 @@ class Manager
86
87
  end
87
88
  end
88
89
 
89
- # Resolves the inheritance for a class.
90
-
91
- def resolve_inheritance(klass)
92
- if has_super?(klass)
93
- sclass = klass.superclass
94
- klass.meta :superclass, sclass
95
- klass.meta :schema_inheritance
96
- sclass.meta :subclasses, klass
97
- end
98
- end
99
-
100
90
  # Resolve polymorphic relations.
101
91
 
102
92
  def resolve_polymorphic(klass)
@@ -107,25 +97,25 @@ class Manager
107
97
 
108
98
  def manage(klass)
109
99
  return if managed?(klass) or klass.ancestors.include?(Unmanageable)
110
-
100
+
111
101
  info = EntityInfo.new(klass)
112
102
 
113
103
  klass.module_eval %{
114
104
  def ==(other)
115
- other ? @#{klass.primary_key.first} == other.#{klass.primary_key.first} : false
105
+ other ? @#{klass.primary_key.symbol} == other.#{klass.primary_key.symbol} : false
116
106
  end
117
107
  }
118
108
 
119
- klass.instance_variable_set '@ogmanager', self
120
109
  klass.class.send(:attr_accessor, :ogmanager)
121
-
110
+ klass.instance_variable_set '@ogmanager', self
111
+
122
112
  Relation.enchant(klass)
123
-
113
+
124
114
  # ensure that the superclass is managed before the
125
115
  # subclass.
126
116
 
127
- manage(klass.superclass) if has_super?(klass)
128
-
117
+ manage(klass.superclass) if manageable?(klass.superclass)
118
+
129
119
  # FIXME: uggly!
130
120
  store.enchant(klass, self); put_store
131
121
 
@@ -139,7 +129,7 @@ class Manager
139
129
  # Is this class manageable by Og?
140
130
 
141
131
  def manageable?(klass)
142
- klass.respond_to?(:__props) and (!klass.__props.empty?)
132
+ klass.respond_to?(:properties) and (!klass.properties.empty?) # and klass.ann.this.polymorphic.nil?
143
133
  end
144
134
 
145
135
  # Is the class managed by Og?
@@ -149,13 +139,6 @@ class Manager
149
139
  end
150
140
  alias_method :entity?, :managed?
151
141
 
152
- # Has this class a superclass?
153
-
154
- def has_super?(klass)
155
- manageable?(sclass = klass.superclass) and
156
- (klass.metadata.schema_inheritance || sclass.metadata.schema_inheritance)
157
- end
158
-
159
142
  # Use Ruby's advanced reflection features to find
160
143
  # all manageable classes. Managable are all classes that
161
144
  # define Properties.
@@ -168,25 +151,39 @@ class Manager
168
151
  classes << c
169
152
  end
170
153
  end
171
-
154
+
172
155
  return classes
173
156
  end
174
157
 
175
158
  # Manage a collection of classes.
176
159
 
177
160
  def manage_classes(*classes)
178
- classes = manageable_classes.flatten if classes.empty?
161
+ classes = manageable_classes.flatten # if classes.empty? FIXME!
162
+
163
+ classes.each { |c| Relation.resolve_targets(c) }
164
+ classes.each { |c| Relation.resolve_polymorphic_markers(c) }
165
+ classes.each { |c| Relation.resolve_polymorphic_relations(c) }
166
+
167
+ # The polymorpic resolution step creates more manageable classes.
168
+
169
+ classes = manageable_classes.flatten # if classes.empty? FIXME!
179
170
 
180
171
  Logger.debug "Og manageable classes: #{classes.inspect}" if $DBG
181
172
 
182
- classes.each { |c| resolve_inheritance(c) }
183
- classes.each { |c| Relation.resolve(c, :resolve_target) }
184
- classes.each { |c| Relation.resolve(c, :resolve_polymorphic) }
185
- classes.each { |c| Relation.resolve(c, :resolve_options) }
173
+ classes.each { |c| Relation.resolve_targets(c) }
174
+ classes.each { |c| Relation.resolve_names(c) }
186
175
  classes.each { |c| manage(c) }
187
176
  end
188
177
  alias_method :manage_class, :manage_classes
189
178
 
179
+ def unmanage_classes(*classes)
180
+ classes = manageable_classes.flatten if classes.empty?
181
+
182
+ for c in classes
183
+ @entities.delete_if { |k, v| v.klass == c }
184
+ end
185
+ end
186
+ alias_method :unmanage_class, :unmanage_classes
190
187
  end
191
188
 
192
189
  end
@@ -1,4 +1,4 @@
1
- require 'mega/macro'
1
+ require 'mega/dynamod'
2
2
  require 'mega/orm_support'
3
3
 
4
4
  module Og
@@ -1,4 +1,5 @@
1
- require 'mega/macro'
1
+ require 'mega/dynamod'
2
+ require 'glue/on_included'
2
3
 
3
4
  module Og
4
5
 
@@ -18,12 +19,8 @@ end
18
19
  module Locking
19
20
  property :lock_version, Fixnum, :default => 0
20
21
  pre "@lock_version = 0", :on => :og_insert
21
-
22
- def self.append_features(base) #:nodoc:
23
- PropertyUtils.copy_features(self, base)
24
-
25
- super
26
-
22
+
23
+ on_included %{
27
24
  base.module_eval do
28
25
  def self.enchant
29
26
  self.send :alias_method, :update_without_lock, :update
@@ -32,7 +29,7 @@ module Locking
32
29
  self.send :alias_method, :save, :save_with_lock
33
30
  end
34
31
  end
35
- end
32
+ }
36
33
 
37
34
  def update_with_lock
38
35
  lock = @lock_version
@@ -1,4 +1,4 @@
1
- require 'mega/macro'
1
+ require 'mega/dynamod'
2
2
 
3
3
  module Og
4
4
 
@@ -70,7 +70,20 @@ module Orderable
70
70
  end
71
71
  end
72
72
 
73
- def move_to
73
+ def move_to(dest_position)
74
+ return if @#{position} == dest_position
75
+
76
+ #{base}.transaction do
77
+ if @#{position} < dest_position
78
+ #{base}.update("#{position}=(#{position} - 1)", #{cond_and}"#{position} > \#\{@#{position}\} AND #{position} <= \#\{dest_position\}")
79
+ else
80
+ #{base}.update("#{position}=(#{position} + 1)", #{cond_and}"#{position} >= \#\{dest_position\} AND #{position} < \#\{@#{position}\} ")
81
+ end
82
+ @#{position} = dest_position
83
+ update_property(:#{position})
84
+ end
85
+
86
+ self
74
87
  end
75
88
 
76
89
  def add_to_top
@@ -0,0 +1,12 @@
1
+ module Og
2
+
3
+ # This is a marker module that denotes that the
4
+ # base class follows the SingleTableInheritance
5
+ # pattern. Ie, all the subclasses of the base
6
+ # class are stored in the same schema (table).
7
+
8
+ module SchemaInheritanceBase; end
9
+
10
+ end
11
+
12
+ # * George Moschovitis <gm@navel.gr>
@@ -1,5 +1,7 @@
1
- require 'mega/macro'
2
- require 'nano/object/assign_with'
1
+ require 'mega/dynamod'
2
+ require 'nano/kernel/assign_with'
3
+
4
+ require 'og/relation'
3
5
 
4
6
  #--
5
7
  # gmosx: make polymorphic work with many_to_many.
@@ -45,15 +47,15 @@ module Taggable
45
47
  # Helper.
46
48
 
47
49
  def self.tags_to_names(the_tags, separator = ' ')
48
- if the_tags.is_a? Array
50
+ if the_tags.is_a? Array
49
51
  names = the_tags
50
- elsif the_tags.is_a? String
51
- names = the_tags.split(separator)
52
+ elsif the_tags.is_a? String
53
+ names = the_tags.split(separator)
52
54
  end
53
-
55
+
54
56
  names = names.flatten.uniq.compact
55
57
 
56
- return names
58
+ return names
57
59
  end
58
60
 
59
61
  def self.append_dynamic_features(base, options = nil)
@@ -63,8 +65,10 @@ module Taggable
63
65
  }
64
66
  o.update(options) if options
65
67
 
68
+ base.send :include, Og::RelationDSL
69
+
66
70
  code = ''
67
-
71
+
68
72
  unless o[:tag_class]
69
73
  o[:tag_class] = 'Tag'
70
74
  code << %{
@@ -96,28 +100,28 @@ module Taggable
96
100
  options = {
97
101
  :clear => true
98
102
  }.merge(options)
99
-
103
+
100
104
  names = Taggable.tags_to_names(the_tags, #{separator})
101
105
 
102
106
  tags.load_members
103
107
 
104
108
  # clear the collection if needed.
105
-
106
- self.tags.clear if options[:clear]
107
-
109
+
110
+ self.tags.clear if options[:clear]
111
+
108
112
  # append the tag names to the collection
109
-
110
- names.each do |name|
113
+
114
+ names.each do |name|
111
115
  name = name.strip
112
- unless tagged_with?(name)
116
+ unless tagged_with?(name)
113
117
  unless tag_obj = #{tag_class}.find_by_name(name)
114
118
  tag_obj = #{tag_class}.create(name)
115
119
  end
116
120
  tags << tag_obj
117
- end
121
+ end
118
122
  end
119
123
 
120
- self.save
124
+ self.save
121
125
  end
122
126
  }
123
127
 
@@ -130,18 +134,18 @@ module Taggable
130
134
  # Returns an array of strings containing the tags applied to
131
135
  # this object.
132
136
 
133
- code << %{
134
- def tag_names
135
- tags.map { |t| t.name }
136
- end
137
- }
137
+ code << %{
138
+ def tag_names
139
+ tags.map { |t| t.name }
140
+ end
141
+ }
138
142
 
139
143
  # Checks to see if this object has been tagged
140
144
  # with +tag_name+.
141
145
 
142
- code << %{
143
- def tagged_with?(tag_name)
144
- tag_names.include?(tag_name)
146
+ code << %{
147
+ def tagged_with?(tag_name)
148
+ tag_names.include?(tag_name)
145
149
  end
146
150
  alias_method :tagged_by?, :tagged_with?
147
151
  }
@@ -5,9 +5,11 @@ module Og
5
5
  # Adds timestamping functionality.
6
6
 
7
7
  module Timestamped
8
+ include Glue::Aspects
9
+
8
10
  property :create_time, Time
9
- property :update_time, Time
10
- property :access_time, Time
11
+ property :update_time, Time, :editor => :none
12
+ property :access_time, Time, :editor => :none
11
13
 
12
14
  before "@create_time = @update_time = Time.now", :on => :og_insert
13
15
  before "@update_time = Time.now", :on => :og_update
@@ -18,7 +18,6 @@ module TreeTraversal
18
18
 
19
19
  def self.child(sum, n)
20
20
  power = 2 ** n
21
-
22
21
  return sum * power
23
22
  end
24
23
 
@@ -1,11 +1,16 @@
1
- require 'nano/object/constant'
2
- require 'nano/string/capitalized%3F'
3
-
1
+ require 'nano/kernel/constant'
2
+ require 'nano/string/capitalized'
4
3
  require 'mega/orm_support'
4
+ require 'mega/inheritor'
5
+ require 'mega/annotation'
5
6
 
6
7
  module Og
7
8
 
8
9
  # A relation between Entities.
10
+ #--
11
+ # Relations are resolved in multiple passes. First
12
+ # the relations are logged in :relations...
13
+ #++
9
14
 
10
15
  class Relation
11
16
 
@@ -13,10 +18,6 @@ class Relation
13
18
 
14
19
  attr_accessor :options
15
20
 
16
- # Is this a polymorphic relation?
17
-
18
- attr_accessor :is_polymorphic
19
-
20
21
  # A generalized initialize method for all relations.
21
22
  # Contains common setup code.
22
23
 
@@ -53,74 +54,59 @@ class Relation
53
54
 
54
55
  @options[:target_class] ||= @options[target_name].to_s.singular.camelize.intern
55
56
 
56
- setup()
57
- end
58
-
59
- def setup
60
- if target_class == Object
61
- # If the target class is just an Object mark that class
62
- # as a polymorphic parent class.
63
- # This class acts as template
64
- # to generate customized versions of this class.
65
- owner_class.meta(:polymorphic, owner_class)
66
- elsif target_class.respond_to?(:metadata) and target_class.metadata.polymorphic
67
- # If the target class is polymorphic, create a specialized
68
- # version of the class enclosed in the owner namespace.
69
-
70
- target_dm = target_class.to_s.demodulize
71
- owner_class.module_eval %{
72
- class #{owner_class}::#{target_dm} < #{target_class}
73
- end
74
- }
75
- eval %{
76
- @options[:target_class] = #{owner_class}::#{target_dm}
77
- }
78
- end
79
-
80
- target_name = if collection
81
- :target_plural_name
82
- else
83
- :target_singular_name
84
- end
85
-
86
- # Inflect the relation name.
87
-
88
- unless @options[target_name]
89
- @options[target_name] = if collection
90
- target_class.to_s.demodulize.underscore.downcase.plural.intern
91
- else
92
- target_class.to_s.demodulize.underscore.downcase.intern
93
- end
94
- end
95
-
96
- @options[:name] = options[target_name]
57
+ # FIXME: this is a hack!
58
+ # setup() rescue nil
97
59
  end
98
60
 
61
+ # Get an option.
62
+
99
63
  def [](key)
100
64
  @options[key]
101
65
  end
102
66
 
67
+ # Set an option.
68
+
103
69
  def []=(key, val)
104
70
  @options[key] = val
105
71
  end
106
72
 
107
- # Is the relation polymorphic?
73
+ # Is this a polymorphic marker?
108
74
 
109
- def polymorphic?
75
+ def polymorphic_marker?
110
76
  target_class == Object
111
77
  end
112
-
113
- #--
114
- # gmosx, TODO: remove, this is not really needed.
115
- #++
116
78
 
117
- def resolve_options
118
- @options[:owner_pk], @options[:owner_pkclass] = owner_class.primary_key
119
- if target_class.respond_to?(:primary_key)
120
- @options[:target_pk], @options[:target_pkclass] = target_class.primary_key
121
- end
79
+ # Is this a polymorphic relation ?
80
+
81
+ def polymorphic?
82
+ target_class.ann.this[:polymorphic]
83
+ end
84
+
85
+ # Resolve a polymorphic target class.
86
+ # Overrided in subclasses.
87
+
88
+ def resolve_polymorphic
89
+ end
90
+
91
+ # This method is implemented in subclasses.
92
+
93
+ def enchant
122
94
  end
123
95
 
96
+ # Access the hash values as methods.
97
+
98
+ def method_missing(sym, *args)
99
+ return @options[sym]
100
+ end
101
+
102
+ end
103
+
104
+ # A collection of helper methods and resolvers
105
+ # for relations.
106
+
107
+ class Relation
108
+ class << self
109
+
124
110
  # To avoid forward declarations, references to undefined
125
111
  # (at the time of the creation of the relation) classes are
126
112
  # stored as symbols. These symbols are resolved by this
@@ -129,7 +115,7 @@ class Relation
129
115
  # FIXME: do something more elegant here.
130
116
  #++
131
117
 
132
- def resolve_symbol(sym, owner_class)
118
+ def symbol_to_class(sym, owner_class)
133
119
  c = owner_class.name.dup
134
120
  c = "::" + c unless c =~ /::/
135
121
  c.gsub!(/::.*$/, '::')
@@ -143,69 +129,131 @@ class Relation
143
129
  end
144
130
  end
145
131
  end
132
+ alias_method :resolve_symbol, :symbol_to_class
146
133
 
147
- def resolve_target
148
- if target_class.is_a?(Symbol)
149
- klass = resolve_symbol(target_class, owner_class)
150
- @options[:target_class] = klass
134
+ def resolve_targets(klass)
135
+ for r in klass.relations
136
+ if r.target_class.is_a? Symbol
137
+ klass = symbol_to_class(r.target_class, r.owner_class)
138
+ r.options[:target_class] = klass
139
+ end
151
140
  end
152
141
  end
153
-
154
- # Resolve a polymorphic target class.
155
- # Overrided in subclasses.
142
+
143
+ # If the target class is just an Object mark this class
144
+ # (self) as a polymorphic parent class. This class acts
145
+ # as template to generate customized versions of this class.
146
+ #
147
+ # For example:
148
+ #
149
+ # class Comment
150
+ # belongs_to :parent, Object # <= polymorphic
151
+ # ...
152
+ # end
153
+
154
+ def resolve_polymorphic_markers(klass)
155
+ for r in klass.relations
156
+ if r.polymorphic_marker?
157
+ r.owner_class.ann :this, :polymorphic => r.owner_class
158
+ end
159
+ end
160
+ end
161
+
162
+ # Resolve polymorphic relations.
163
+ # If the target class is polymorphic, create a specialized
164
+ # version of that class (the target) enclosed in the
165
+ # owner namespace.
166
+ #
167
+ # For example:
168
+ #
169
+ # class Article
170
+ # has_many Comment
171
+ # ...
172
+ # end
173
+
174
+ def resolve_polymorphic_relations(klass)
175
+ for r in klass.relations
176
+ if r.polymorphic?
177
+
178
+ target_dm = r.target_class.to_s.demodulize
179
+ r.owner_class.module_eval %{
180
+ class #{r.owner_class}::#{target_dm} < #{r.target_class}
181
+ end
182
+ }
183
+
184
+ # Replace the target class.
185
+
186
+ r[:target_class] = eval("#{r.owner_class}::#{target_dm}")
187
+ end
156
188
 
157
- def resolve_polymorphic
189
+ r.resolve_polymorphic
190
+ end
158
191
  end
159
192
 
160
- # This method is implemented in subclasses.
193
+ # Resolve the names of the relations.
161
194
 
162
- def enchant
195
+ def resolve_names(klass)
196
+ for r in klass.relations
197
+ target_name = if r.collection
198
+ :target_plural_name
199
+ else
200
+ :target_singular_name
201
+ end
202
+
203
+ # Inflect the relation name.
204
+
205
+ unless r[target_name]
206
+ r[target_name] = if r.collection
207
+ r.target_class.to_s.demodulize.underscore.downcase.plural.intern
208
+ else
209
+ r.target_class.to_s.demodulize.underscore.downcase.intern
210
+ end
211
+ end
212
+
213
+ r[:name] = r[target_name]
214
+ end
163
215
  end
164
-
165
- # Access the hash values as methods.
166
-
167
- def method_missing(sym, *args)
168
- return @options[sym]
216
+
217
+ # General resovle method.
218
+
219
+ def resolve(klass, action = :resolve_polymorphic)
220
+ for r in klass.relations
221
+ r.send(action)
222
+ end
169
223
  end
170
224
 
171
- class << self
172
-
173
- def resolve(klass, action)
174
- if relations = klass.metadata.relations
175
- for relation in klass.metadata.relations
176
- relation.send(action)
177
- end
178
- end
225
+ # Perform relation enchanting on this class.
226
+
227
+ def enchant(klass)
228
+ # update inherited relations.
229
+
230
+ for r in klass.relations
231
+ r[:owner_class] = klass
179
232
  end
180
233
 
181
- def enchant(klass)
182
- if relations = klass.metadata.relations
183
- # update inherited relations.
184
- # gmosx, FIXME: implement a better fix/rethink this!
185
- for relation in relations
186
- relation[:owner_class] = klass
187
- unless relation.target_class == Object or relation.target_class.metadata.polymorphic
188
- relation.setup
189
- end
190
- end
191
-
192
- # enchant.
193
- for relation in relations
194
- relation.enchant unless relation.target_class == Object
195
- end
196
- end
234
+ # enchant.
235
+
236
+ for r in klass.relations
237
+ # p "=== #{klass} : #{r.class} : #{r.name}"
238
+ r.enchant() unless r.polymorphic_marker?
197
239
  end
198
-
240
+
241
+ end
242
+
199
243
  end
200
-
201
244
  end
202
245
 
203
- # Relation macros. These macros are used to define Entity
204
- # relations.
246
+ # Relations domain specific language (DSL). This
247
+ # language defines macros that are used to define Entity
248
+ # relations. Additional macros allow for relation
249
+ # inspection.
205
250
 
206
- module RelationMacros
251
+ module RelationDSL
207
252
  def self.append_features(base)
208
253
  super
254
+ base.module_eval do
255
+ inheritor(:relations, [], :+) unless @relations
256
+ end
209
257
  base.extend(ClassMethods)
210
258
  end
211
259
 
@@ -220,7 +268,7 @@ module RelationMacros
220
268
 
221
269
  def belongs_to(*args)
222
270
  require 'og/relation/belongs_to'
223
- meta :relations, Og::BelongsTo.new(args, :owner_class => self)
271
+ relations! << Og::BelongsTo.new(args, :owner_class => self)
224
272
  end
225
273
 
226
274
  # === Examples
@@ -230,7 +278,7 @@ module RelationMacros
230
278
 
231
279
  def refers_to(*args)
232
280
  require 'og/relation/refers_to'
233
- meta :relations, Og::RefersTo.new(args, :owner_class => self)
281
+ relations! << Og::RefersTo.new(args, :owner_class => self)
234
282
  end
235
283
 
236
284
  # === Examples
@@ -239,7 +287,7 @@ module RelationMacros
239
287
 
240
288
  def has_one(*args)
241
289
  require 'og/relation/has_one'
242
- meta :relations, Og::HasOne.new(args, :owner_class => self)
290
+ relations! << Og::HasOne.new(args, :owner_class => self)
243
291
  end
244
292
 
245
293
  # === Examples
@@ -249,30 +297,25 @@ module RelationMacros
249
297
 
250
298
  def has_many(*args)
251
299
  require 'og/relation/has_many'
252
- meta :relations, Og::HasMany.new(args, :owner_class => self, :collection => true)
300
+ relations! << Og::HasMany.new(args, :owner_class => self, :collection => true)
253
301
  end
254
302
 
255
303
  # ..
256
304
 
257
305
  def joins_many(*args)
258
306
  require 'og/relation/joins_many'
259
- meta :relations, Og::JoinsMany.new(args, :owner_class => self, :collection => true)
307
+ relations! << Og::JoinsMany.new(args, :owner_class => self, :collection => true)
260
308
  end
261
309
 
262
310
  # ..
263
311
 
264
312
  def many_to_many(*args)
265
313
  require 'og/relation/many_to_many'
266
- meta :relations, Og::ManyToMany.new(args, :owner_class => self, :collection => true)
314
+ relations! << Og::ManyToMany.new(args, :owner_class => self, :collection => true)
267
315
  end
268
316
 
269
- def inspect_relations
270
- __meta[:relations]
271
- end
272
- alias_method :relations, :inspect_relations
273
-
274
317
  def inspect_relation(name)
275
- __meta[:relations].find { |r| r[:name] == name }
318
+ relations.find { |r| r[:name] == name }
276
319
  end
277
320
  alias_method :relation, :inspect_relation
278
321
 
@@ -280,3 +323,5 @@ module RelationMacros
280
323
  end
281
324
 
282
325
  end
326
+
327
+ # * George Moschovitis <gm@navel.gr>