Module: DattsRight::InstanceMethods

Defined in:
lib/datts_right/instance_methods.rb

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *args, &body) ⇒ Object (private)



307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/datts_right/instance_methods.rb', line 307

def method_missing(method_id, *args, &body)
  begin
    super(method_id, *args, &body)
  rescue NoMethodError => e
    attr_name = method_id.to_s.sub(/\=$/, '')
    if dynamic_attribute?(attr_name)
      method_id.to_s =~ /\=$/ ? write_dynamic_attribute(attr_name, args[0]) : read_dynamic_attribute(attr_name)
    else
      raise NoMethodError
    end
  end
end

Instance Method Details

#add_definition(key, value) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/datts_right/instance_methods.rb', line 125

def add_definition(key, value)
  if key
    key = key.to_sym
    #puts "add_definition(:#{key}, #{value.inspect}). current definition: #{definition.nil?}"
    if dynamic_attributes_options[:defines].nil? || dynamic_attributes_options[:defines].empty?
      raise NoDefinitionError
    else
      #puts ":definition is true, so let's see if definition[:#{key}] already exists"
      # Checking definition straight seems to cause a problem. Is this a bug with Rails?
      # The problem was like this (assuming definition was actually nil):
      # definition.nil? # false
      # definition.inspect # nil
      # definition.class.name # NilClass
      # 
      # How in the world can .nil? return false when it was nil??
      dynamic_attribute_definition.definition ||= {}

      if definition[key]
        raise AlreadyDefined, "#{key} is already defined"
      else
        definition.merge!({key => value})
      end
    end
  else
    raise AttributeKeyRequired
  end
end

#add_definitions(*args) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/datts_right/instance_methods.rb', line 153

def add_definitions(*args)
  attributes = args
  attributes.compact! if attributes#remove the nil items
  #puts "args after compacting: #{attributes.inspect}"
  attributes.flatten! if args.first.is_a?(Array) && attributes
  #puts "args after flattening: #{attributes.inspect}"
  attributes.each do |item|
    #puts "Working on #{item.inspect} is is a hash? (#{item.is_a?(Hash)}) or something else?"
    item.each do |k, v|
      #puts "Working on this k,v pair: #{k.inspect} => #{v.inspect}"
      if v.is_a?(Hash) # item is like :robot => {:object_type => "text"}, :robot@ => {:object_type => "text"}
        #puts "#{v} IS a hash"
        add_definition k, v
      else # v is not a hash; item is like {"name"=>"A key", "attr_key"=>"a_key"}, {"name"=>"B key", "attr_key"=>"b_key"}
        # Sometimes the item is a ActiveRecord::HashWithIndifferentAccess, which doesn't have the method symbolize_keys!, so we do it manually
        #item = item.symbolize_keys # {:name=>"A key", :description=>"asd", :attr_key=>"a_key"}
        #puts "item is symbolized: #{item.inspect}"
        attr_key = item.delete("attr_key")
        #puts "This is the attr_key: #{attr_key}"
        if attr_key # we only want to work on it if there's an attr_key
          attr_key = attr_key.to_sym
          #puts "Adding: :#{attr_key}, #{item.inspect}"
          add_definition(attr_key, item)
        end
      end
    end
  end
end

#add_dynamic_attribute(name, object_type, value = nil) ⇒ Object Also known as: add_datt



3
4
5
6
7
8
9
10
11
12
# File 'lib/datts_right/instance_methods.rb', line 3

def add_dynamic_attribute(name, object_type, value=nil)
  key = name.to_s.underscore
  return false if self.class.columns_hash[key] # if key already exists as a normal attribute
  unless dynamic_attribute?(key)
    new_dynamic_attribute = dynamic_attributes.new :attr_key => key, :object_type => object_type, "#{object_type}_value".to_sym => value
    @dynamic_attributes_cache[key.to_sym] = new_dynamic_attribute
    return new_dynamic_attribute
  end
  return false
end

#add_dynamic_attribute!(name, object_type, value = nil) ⇒ Object Also known as: add_datt!



14
15
16
17
18
# File 'lib/datts_right/instance_methods.rb', line 14

def add_dynamic_attribute!(name, object_type, value=nil)
  dynamic_attribute = add_dynamic_attribute(name, object_type, value)
  dynamic_attribute.save if dynamic_attribute
  key = name.to_s.underscore
end

#attributes=(new_attributes, guard_protected_attributes = true) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/datts_right/instance_methods.rb', line 82

def attributes=(new_attributes, guard_protected_attributes = true)
  return unless new_attributes.is_a?(Hash)
  attributes = new_attributes.stringify_keys

  multi_parameter_attributes = []
  attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes

  attributes.each do |k, v|
    if k.include?("(")
      multi_parameter_attributes << [ k, v ]
    else
      #respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
      begin
        #puts "Attempt to set super #{k} to #{v}"
        #puts "Checking to see if #{self.inspect} responds to #{k}= ........... #{self.class.name}##{self.class.respond_to?(:"#{k}=")}, or the record itself: #{respond_to?(:"#{k}=")}"
        respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(ActiveRecord::UnknownAttributeError, "unknown attribute: #{k}")
        #send("#{k}=", v)
        #puts "Set super #{k} to #{v}"
      rescue ActiveRecord::UnknownAttributeError => e
        #puts "ActiveRecord::UnknownAttributeError was raised: #{e}, so we now check to see if '#{k}' is dynamic_attribute"
        if dynamic_attribute?(k)
          write_dynamic_attribute("#{k}", v)
        else
          raise ActiveRecord::UnknownAttributeError, "unknown attribute: #{k}"
        end
      end
    end
  end

  assign_multiparameter_attributes(multi_parameter_attributes)
end

#create_dynamic_attribute_definition_if_neededObject



119
120
121
122
123
# File 'lib/datts_right/instance_methods.rb', line 119

def create_dynamic_attribute_definition_if_needed
  if self.defines?
    DynamicAttributeDefinition.create :attribute_defineable_id => self.id, :attribute_defineable_type => self.class.name, :definition => {}
  end
end

#defined?Boolean

Returns true if this is defined by any other class

Returns:

  • (Boolean)


269
270
271
# File 'lib/datts_right/instance_methods.rb', line 269

def defined?
  self.class.defined?
end

#defined_by?(arg) ⇒ Boolean

Returns true if the calling instance is defined by the argument

Returns:

  • (Boolean)


274
275
276
# File 'lib/datts_right/instance_methods.rb', line 274

def defined_by?(arg)
  self.class.defined_by?(arg)
end

#definesObject

Returns an array of symbols of the classes that this defines



279
280
281
# File 'lib/datts_right/instance_methods.rb', line 279

def defines
  self.class.defines
end

#defines?(klass = nil) ⇒ Boolean

Returns true if this defines any other class

Returns:

  • (Boolean)


264
265
266
# File 'lib/datts_right/instance_methods.rb', line 264

def defines?(klass=nil)
  self.class.defines?(klass)
end

#defining_recordObject



114
115
116
117
# File 'lib/datts_right/instance_methods.rb', line 114

def defining_record
  return nil if dynamic_attributes_options[:of].nil?
  send dynamic_attributes_options[:of].to_s
end

#dynamic_attribute?(attr) ⇒ Boolean

Determines if the given attribute is a dynamic attribute.

Returns:

  • (Boolean)


35
36
37
# File 'lib/datts_right/instance_methods.rb', line 35

def dynamic_attribute?(attr)
  !@dynamic_attributes_cache[attr.to_sym].nil?
end

#dynamic_attribute_details(key) ⇒ Object Also known as: datt_details

Give users access to the cache



30
31
32
# File 'lib/datts_right/instance_methods.rb', line 30

def dynamic_attribute_details(key)
  @dynamic_attributes_cache[key]
end

#inherit_definition!Object

Adds dynamic attributes to inheriting model, based on the definition of the defining model.

class InheritingModel < AR::Base
  has_dynamic_attributes :of => :defining_model
end

The DefiningModel should have the code:

class DefiningModel < AR::Base
  has_dynamic_attributes :defines => [:inheriting_model]
end

Example:

@defining_model.add_definitions(:name => {:object_type => "string"}, :body => {:object_type => "text"})
@defining_model.save
InheritingModel.create # creates an instance with dynamic attributes: name and body, that are "string" and "text", respectively

Calling this method manually only adds to the inheriting instance you call it on. If you remove some definitions from the defining model, then you call @inheriting_model.inherit_definition!@, the new definitions will be added. If there are no new definitions, nothing will happen.



239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/datts_right/instance_methods.rb', line 239

def inherit_definition!
  #puts "In inherit_definition"
  #puts "------- #{dynamic_attributes_options[:of]}"
  #dynamic_attribute_definition.create if dynamic_attributes_options[:definition]
  if self.defined? && defining_record
    defining_record.definition.each do |k, v|
      datt = add_dynamic_attribute(k, v[:object_type])
      datt.dynamic_attribute_definition
      #puts "Added #{datt.inspect}"
    end
  end
end

#read_dynamic_attribute(attr_name) ⇒ Object Also known as: read_datt

Like AR::Base#read_attribute



60
61
62
63
64
65
66
# File 'lib/datts_right/instance_methods.rb', line 60

def read_dynamic_attribute(attr_name)
  attr_name = attr_name.to_sym
  if dynamic_attribute?(attr_name)
    #puts "Reading #{attr_name}. The whole cache: #{@dynamic_attributes_cache.inspect}"
    @dynamic_attributes_cache[attr_name].value
  end
end

#remove_definition(key) ⇒ Object



206
207
208
209
210
211
212
213
# File 'lib/datts_right/instance_methods.rb', line 206

def remove_definition(key)
  if key
    key = key.to_sym
    raise NoDefinitionError unless self.defines?
    raise NotDefinedError, "#{key} is not defined" unless definition && definition[key]
    definition.delete(key)
  end
end

#remove_definitions(*array) ⇒ Object



215
216
217
218
219
220
221
# File 'lib/datts_right/instance_methods.rb', line 215

def remove_definitions(*array)
  if array
    array.each do |a|
      remove_definition a
    end
  end
end

#remove_dynamic_attribute(name) ⇒ Object Also known as: remove_datt



20
21
22
23
24
25
26
27
# File 'lib/datts_right/instance_methods.rb', line 20

def remove_dynamic_attribute(name)
  # Remove from the cache
  @dynamic_attributes_cache.delete(name.to_sym)

  # Then remove from the db
  dynamic_attribute = dynamic_attributes.find_by_attr_key(name.to_s)
  dynamic_attribute.destroy if dynamic_attribute
end

#sync_definition!Object

Adds and removes dynamic attributes based on the defining record



253
254
255
256
257
258
259
260
261
# File 'lib/datts_right/instance_methods.rb', line 253

def sync_definition!
  defining_record.definition.each do |k, v|
    add_dynamic_attribute(k, v[:object_type]) if dynamic_attribute_details(k).nil?
  end
  @dynamic_attributes_cache.each do |k, v|
    #puts "Remove #{k}?"
    remove_dynamic_attribute(k) if v.definer.nil?
  end
end

#update_definition(key, new_values = {}) ⇒ Object

Raises:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/datts_right/instance_methods.rb', line 182

def update_definition(key, new_values={})
  raise NoDefinitionError unless self.defines?
  raise NotDefinedError, "#{key} is not defined" unless definition && definition[key]

  attr_key = new_values.symbolize_keys[:attr_key]
  new_values.each do |k, v|
    definition[key][k] = v unless k.to_s == "attr_key"
  end

  #puts "attr_key is #{attr_key}, key is #{key}"
  if attr_key && attr_key != key.to_s
    #puts "Adding definition: #{attr_key} => #{definition[key]}"
    add_definition(attr_key, definition[key])
    #puts "Removing definition: #{key} => #{definition[key]}"
    remove_definition(key)
  end
end

#update_definitions(hash = {}) ⇒ Object



200
201
202
203
204
# File 'lib/datts_right/instance_methods.rb', line 200

def update_definitions(hash={})
  hash.each do |k, v|
    update_definition k, v
  end
end

#update_dynamic_attributes(attributes) ⇒ Object



39
40
41
42
43
44
45
46
47
48
# File 'lib/datts_right/instance_methods.rb', line 39

def update_dynamic_attributes(attributes)
  # The following transaction covers any possible database side-effects of the
  # attributes assignment. For example, setting the IDs of a child collection.
  with_transaction_returning_status do
    attributes.symbolize_keys.each do |k, v|
      self.write_dynamic_attribute(k, v)
    end
    save
  end
end

#update_dynamic_attributes!(attributes) ⇒ Object



50
51
52
53
54
55
56
57
# File 'lib/datts_right/instance_methods.rb', line 50

def update_dynamic_attributes!(attributes)
  with_transaction_returning_status do
    attributes.symbolize_keys.each do |k, v|
      self.write_dynamic_attribute(k, v)
    end
    save!
  end
end

#write_dynamic_attribute(attr_name, value) ⇒ Object Also known as: write_datt, update_dynamic_attribute

Like AR::Base#write_attribute



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/datts_right/instance_methods.rb', line 69

def write_dynamic_attribute(attr_name, value)
  #puts "attempting to write: #{attr_name} = #{value}"
  attr_name = attr_name.to_sym
  if dynamic_attribute?(attr_name)
    #puts "#{attr_name} is a dynamic_attribute"
    #puts "Writing @dynamic_attributes_cache[:#{attr_name}].value = #{value}"
    dynamic_attribute = @dynamic_attributes_cache[attr_name]
    dynamic_attribute.value = value
    #puts "In write_dynamic_attribute. Full cache: #{@dynamic_attributes_cache.inspect}"
    return dynamic_attribute.value
  end
end