Module: RTM::AR::TraverseAssociations

Included in:
Sugar::Topic::PredefinedAssociations
Defined in:
lib/rtm/activerecord/traverse_associations.rb,
lib/rtm/activerecord.rb

Instance Method Summary collapse

Instance Method Details

#define_association(prop, options = {}) ⇒ Object

defines methods to traverse a specific assocation for all topics



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/rtm/activerecord/traverse_associations.rb', line 7

def define_association(prop, options={})
  r = options[:rule]
  module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION__)", 1)
    def direct_#{prop}_roles
      # prefetch typing topics
      rtype = @ips_#{prop}_rtype ||= self.topic_map.get("#{r[:role_type]}")
      atype = @ips_#{prop}_atype ||= self.topic_map.get("#{r[:association_type]}")
      otype = @ips_#{prop}_otype ||= self.topic_map.get("#{r[:other_role_type]}")
      # return nothing if any of the typing topics does not exist
      return [] unless rtype && atype && otype
      # we do that only here and not earlier because some might me nil
      rtype = rtype.id
      atype = atype.id
      otype = otype.id
      self.__getobj__.roles.map{|r|
        r.ttype_id != nil &&
          r.ttype_id == rtype &&
          r.parent.roles.size == #{r[:association_arity]} &&
          r.parent != nil &&
          r.parent.ttype_id == atype &&
          (r2 = r.parent.roles.select{|r2| r2.ttype_id != nil &&
            r2.ttype_id == otype}.first) &&
          r2}.select{|r2| r2}.map{|r2| AssociationRole.wrap(r2)}
    end
    def direct_#{prop}
      direct_#{prop}_roles.map{|r2| r2.player}.uniq
    end
  EOS
  
  if r[:transitive]
    module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION2__)", 1)
      def #{prop}
        d = todo = self.direct_#{prop}
        while todo.size > 0
          todo = todo.map{|dt| dt.direct_#{prop}}.flatten.uniq - d
          d += todo
        end
        d
        #d2 = self.direct_#{prop}.map {|dt| dt.#{prop}}.flatten
        #(d+d2).uniq
      end
    EOS
  else
    if r[:infer] && r[:infer_other] # this part is not testet at all!
      module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION3a__)", 1)
        def #{prop}
          dps = (self + self.#{r[:infer]}).flatten.uniq
          dcps = dps.map{|d2| d2.direct_#{prop}}
          (dcps + dcps.map{|d2| d2.#{r[:infer_other]}}).flatten.uniq
        end
      EOS
    elsif r[:infer]
      module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION3__)", 1)
        def #{prop}
          (self.direct_#{prop} + self.#{r[:infer]}.map{|d2| d2.direct_#{prop}}).flatten.uniq
        end
      EOS
    elsif r[:infer_other]
      module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION4__)", 1)
        def #{prop}
          d = self.direct_#{prop}
          (d + d.map{|d2| d2.#{r[:infer_other]}}).flatten.uniq
        end
      EOS
    end
  end
  if r[:add]
    module_eval(<<-EOS, "(__AR_DELEGATOR_DEFINE_ASSOCIATION_ADD_REMOVE__)", 1)
      def add_#{r[:add]}(t)
        a = self.topic_map.create_association("#{r[:association_type]}")
        a.create_role self, "#{r[:role_type]}"
        a.create_role t, "#{r[:other_role_type]}"
        a
        # TODO add_x in define_association needs to trigger reload of the topics somewhere
      end
      def remove_#{r[:add]}(t)
        direct_#{prop}_roles.select{|r| r.player == t}.each{|r| r.parent.remove}
        # TODO remove_x in define_association needs to trigger reload of the topics somewhere
      end
    EOS
  end
end