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



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
356
# File 'lib/zena/use/relations.rb', line 312

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’



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

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



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

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

#l_commentObject

comment defined through loading link



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

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

#l_comment=(v) ⇒ Object



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

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



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

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



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

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



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

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



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

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


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

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


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

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



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

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”.



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

def linked_node=(node)
  @linked_node = node
end

#relObject



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

def rel
  ProxyLoader.new(self)
end

#rel=(hash) ⇒ Object

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



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

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

#rel_attributes=(hash) ⇒ Object



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

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
113
# File 'lib/zena/use/relations.rb', line 96

def relation_alias(match)
  return nil if respond_to?("#{match[0]}=") # native method
  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



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

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.



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

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


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

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



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

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


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

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