Module: Zena::Use::Relations::ModelMethods

Included in:
Node
Defined in:
lib/zena/use/relations.rb

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object (private)

Used to create / destroy / update links through pseudo methods ‘icon_id=’, ‘icon_status=’, … Pseudo methods created for a many-to-one relation (icon_for — icon):

icon_id=

set icon

icon_status=

set status field for link to icon

icon_comment=

set comment field for link to icon

icon_for_ids=

set all nodes for which the image is an icon (replaces old values)

icon_for_id=

add a node for which the image is an icon (adds a new value)

icon_id

get icon id

icon_zip

get icon zip

icon_status

get status field for link to icon

icon_comment

get comment field for link to icon

icon_for_ids

get all node ids for which the image is an icon

icon_for_zips

get all node zips for which the image is an icon



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/zena/use/relations.rb', line 311

def method_missing(meth, *args, &block)
  # first try rails' version of method missing
  super(meth, *args, &block)
rescue NoMethodError => err
  # 1. is this a method related to a relation ?
  if meth.to_s =~ LINK_REGEXP
    role  = $1
    field = $2
    mode  = $3
    # 2. is this a valid role ?
    if rel = relation_proxy(role)
      if mode == '='
        # set
        rel.send("other_#{field}=", args[0])
      else
        # get
        if field != 'ids' && field != 'zips' && !rel.unique?
          # ask for a single value in a ..-to-many relation
          # 1. try to use focus
          if @link
            rel.other_link = @link
          elsif self.link_id
            @link = Link.find_through(self, self.link_id)
            rel.other_link = @link
          else
            return nil
          end
        end
        rel.send("other_#{field}")
      end
    else
      # invalid relation
      if mode == '='
        errors.add(role, "invalid relation") unless args[0].blank?
        return args[0]
      else
        # ignore
        return nil
      end
    end
  else
    # not related to relations
    raise err
  end
end

Class Method Details

.included(base) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/zena/use/relations.rb', line 67

def self.included(base)
  base.extend Zena::Use::Relations::ClassMethods
  base.validate      :relations_valid
  base.after_save    :update_relations
  base.after_destroy :destroy_links
  base.safe_method   :rel => ProxyLoader

  base.safe_method :l_status  => {:class => Number, :nil => true}
  base.safe_method :l_comment => {:class => String, :nil => true}
  base.safe_method :l_date    => {:class => Time,   :nil => true}
  base.safe_method :link_id   => {:class => Number, :nil => true}

  base.nested_attributes_alias LINK_REGEXP => Proc.new {|obj, m| obj.relation_alias(m) }
  base.class_eval <<-END
    attr_accessor :link
    class << self
      include Zena::Use::Relations::ClassMethods
    end

    def relation_base_class
      #{base}
    end

    HAS_RELATIONS = true
  END
end

Instance Method Details

FIXME: this method does an ‘update’ not only ‘add’



152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/zena/use/relations.rb', line 152

def add_link(role, hash)
  if rel = relation_proxy(role)
    rel.qb         = hash[:qb]         if hash.has_key?(:qb)
    rel.other_id   = hash[:other_id]   if hash.has_key?(:other_id)
    rel.other_ids  = hash[:other_ids]  if hash.has_key?(:other_ids)
    rel.other_zip  = hash[:other_zip]  if hash.has_key?(:other_zip)
    rel.other_zips = hash[:other_zips] if hash.has_key?(:other_zips)
    LINK_ATTRIBUTES.each do |k|
      rel.send("other_#{k}=", hash[k]) if hash.has_key?(k)
    end
  else
    errors.add(role, 'invalid relation')
  end
end

#all_relationsObject



254
255
256
# File 'lib/zena/use/relations.rb', line 254

def all_relations
  @all_relations ||= self.vclass.all_relations(self)
end

#l_commentObject

comment defined through loading link



135
136
137
138
# File 'lib/zena/use/relations.rb', line 135

def l_comment
  return @l_comment if defined? @l_comment
  @link ? @link[:comment] : self['l_comment']
end

#l_comment=(v) ⇒ Object



210
211
212
213
214
215
216
217
218
# File 'lib/zena/use/relations.rb', line 210

def l_comment=(v)
  @l_comment = v.blank? ? nil : v
  if rel = relation_proxy_from_link
    rel.other_comment = @l_comment
  else
    l = @link_attributes_to_update ||= {}
    l[:comment] = @l_comment
  end
end

#l_dateObject

date defined through loading link



141
142
143
144
# File 'lib/zena/use/relations.rb', line 141

def l_date
  return @l_date if defined? @l_date
  @l_date = @link ? @link[:date] : (self['l_date'] ? Time.parse(self['l_date']) : nil)
end

#l_date=(v) ⇒ Object



230
231
232
233
234
235
236
237
238
# File 'lib/zena/use/relations.rb', line 230

def l_date=(v)
  @l_date = v.blank? ? nil : v
  if rel = relation_proxy_from_link
    rel.other_date = @l_date
  else
    l = @link_attributes_to_update ||= {}
    l[:date] = @l_date
  end
end

#l_statusObject

status defined through loading link



126
127
128
129
130
# File 'lib/zena/use/relations.rb', line 126

def l_status
  return @l_status if defined? @l_status
  val = @link ? @link[:status] : self['l_status']
  val ? val.to_f : nil
end

#l_status=(v) ⇒ Object



220
221
222
223
224
225
226
227
228
# File 'lib/zena/use/relations.rb', line 220

def l_status=(v)
  @l_status = v.blank? ? nil : v
  if rel = relation_proxy_from_link
    rel.other_status = @l_status
  else
    l = @link_attributes_to_update ||= {}
    l[:status] = @l_status
  end
end


146
147
148
# File 'lib/zena/use/relations.rb', line 146

def link_id
  @link ? @link[:id] : (self[:link_id] == -1 ? nil : self[:link_id]) # -1 == dummy link
end


240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/zena/use/relations.rb', line 240

def link_id=(v)
  if @link && @link[:id].to_i != v.to_i
    @link = nil
  end
  self[:link_id] = v.to_i
  if @link_attributes_to_update
    if rel = relation_proxy_from_link
      @link_attributes_to_update.each do |k,v|
        rel.send("other_#{k}=",v)
      end
    end
  end
end

#linked_nodeObject



121
122
123
# File 'lib/zena/use/relations.rb', line 121

def linked_node
  @linked_node ||= @relation_proxies ? @relation_proxies[@relation_proxies.keys.first].last_target : nil
end

#linked_node=(node) ⇒ Object

Linked_node is a way to store a linked node during calendar display or ajax return calls so the template knows which “couple” has just been formed or removed. The linked_node “node” must respond to “l_date”.



117
118
119
# File 'lib/zena/use/relations.rb', line 117

def linked_node=(node)
  @linked_node = node
end

#relObject



200
201
202
# File 'lib/zena/use/relations.rb', line 200

def rel
  ProxyLoader.new(self)
end

#rel=(hash) ⇒ Object

This accessor is used when the data arrives with the syntax rel => { friend => … }



206
207
208
# File 'lib/zena/use/relations.rb', line 206

def rel=(hash)
  self.rel_attributes = hash
end

#rel_attributes=(hash) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/zena/use/relations.rb', line 180

def rel_attributes=(hash)
  return unless hash.kind_of?(Hash)
  hash.each do |role, definition|
    if role =~ /\A\d+\Z/
      # key used as array
    elsif role =~ /^(.+)_attributes$/
      # key used as role
      definition['role'] ||= $1
    elsif definition.kind_of?(Hash)
      # key used as role, without the '_attributes'
      definition['role'] ||= role
    else
      # qb
      definition = {'role' => role, 'qb' => definition}
    end
    # TODO: only use string keys
    add_link(definition.delete('role'), definition.symbolize_keys)
  end
end

#relation_alias(match) ⇒ Object

Return an array of accessor methods for the matched relation alias.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/zena/use/relations.rb', line 96

def relation_alias(match)
  return nil if match[0] == 'parent_id'
  role     = match[1]
  field    = match[2]

  if relation = relation_proxy(role)
    # We use 'links' so that we can keep the old @link accessor.
    # FIXME: rename 'link' when we refactor the @link part.
    if field =~ /^ids?|zips?/
      ['rel', role, "other_#{field}"]
    else
      ['rel', role, field]
    end
  else
    nil
  end
end

List the links, grouped by role



263
264
265
266
267
268
269
270
271
272
273
# File 'lib/zena/use/relations.rb', line 263

def relation_links
  res = []
  all_relations.each do |rel|
    #if relation.record_count > 5
    #  # FIXME: show message ?
    #end
    links = rel.records(:limit => 25, :order => "link_id DESC")
    res << [rel, links] if links
  end
  res
end

#relation_proxy(role) ⇒ Object

Find relation proxy for the given role.



276
277
278
279
280
# File 'lib/zena/use/relations.rb', line 276

def relation_proxy(role)
  @relation_proxies ||= {}
  return @relation_proxies[role] if @relation_proxies.has_key?(role)
  @relation_proxies[role] = RelationProxy.get_proxy(self, role.singularize.underscore)
end


282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/zena/use/relations.rb', line 282

def relation_proxy_from_link(link = nil)
  unless link
    if @link
      link = @link
    elsif self.link_id
      link = @link = Link.find_through(self, self.link_id)
    end
    return nil unless link
  end
  @relation_proxies ||= {}
  return @relation_proxies[link.role] if @relation_proxies.has_key?(link.role)
  @relation_proxies[link.role] = link.relation_proxy(self)
end

#relations_for_formObject



258
259
260
# File 'lib/zena/use/relations.rb', line 258

def relations_for_form
  all_relations.map {|r| [r.other_role.singularize, r.other_role]}
end


167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/zena/use/relations.rb', line 167

def remove_link(link)
  if link[:source_id] != self[:id] && link[:target_id] != self[:id]
    errors.add('link', "not related to this node")
    return false
  end
  # find proxy
  if rel = relation_proxy_from_link(link)
    rel.remove_link(link)
  else
    errors.add('link', "cannot remove (relation proxy not found).")
  end
end