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



305
306
307
308
309
310
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
# File 'lib/zena/use/relations.rb', line 305

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



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

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’



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

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



248
249
250
# File 'lib/zena/use/relations.rb', line 248

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

#l_commentObject

comment defined through loading link



139
140
141
142
# File 'lib/zena/use/relations.rb', line 139

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

#l_comment=(v) ⇒ Object



227
228
229
230
231
232
# File 'lib/zena/use/relations.rb', line 227

def l_comment=(v)
  @l_comment = v.blank? ? nil : v
  if rel = relation_proxy_from_link
    rel.other_comment = @l_comment
  end
end

#l_dateObject

date defined through loading link



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

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



241
242
243
244
245
246
# File 'lib/zena/use/relations.rb', line 241

def l_date=(v)
  @l_date = v.blank? ? nil : v
  if rel = relation_proxy_from_link
    rel.other_date = @l_date
  end
end

#l_statusObject

status defined through loading link



130
131
132
133
134
# File 'lib/zena/use/relations.rb', line 130

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

#l_status=(v) ⇒ Object



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

def l_status=(v)
  @l_status = v.blank? ? nil : v.to_i
  if rel = relation_proxy_from_link
    rel.other_status = @l_status
  end
end


150
151
152
# File 'lib/zena/use/relations.rb', line 150

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


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

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



125
126
127
# File 'lib/zena/use/relations.rb', line 125

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



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

def linked_node=(node)
  @linked_node = node
end

#relObject



217
218
219
# File 'lib/zena/use/relations.rb', line 217

def rel
  ProxyLoader.new(self)
end

#rel=(hash) ⇒ Object

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



223
224
225
# File 'lib/zena/use/relations.rb', line 223

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

#rel_attributes=(hash) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/zena/use/relations.rb', line 197

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.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/zena/use/relations.rb', line 100

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



257
258
259
260
261
262
263
264
265
266
267
# File 'lib/zena/use/relations.rb', line 257

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

#relation_proxy(role) ⇒ Object

Find relation proxy for the given role.



270
271
272
273
274
# File 'lib/zena/use/relations.rb', line 270

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


276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/zena/use/relations.rb', line 276

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



252
253
254
# File 'lib/zena/use/relations.rb', line 252

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


184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/zena/use/relations.rb', line 184

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