og 0.23.0 → 0.24.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 (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>