Question

I have a model with serialized data and I want to edit this data using the best_in_place gem. This isn't possible by default when using the best_in_place gem. How can this be done?

Was it helpful?

Solution

It can be done by extending method_missing and respond_to_missing? to forward the requests to the serialized data. Lets say you have your serialized Hash in data. In the class that's containing the serialized data you can for example use this code:

def method_missing(method_name, *arguments, &block) # forewards the arguments to the correct methods
  if method_name.to_s =~ /data_(.+)\=/
    key = method_name.to_s.match(/data_(.+)=/)[1]
    self.send('data_setter=', key, arguments.first)
  elsif method_name.to_s =~ /data_(.+)/
    key = method_name.to_s.match(/data_(.+)/)[1]
    self.send('data_getter', column_number)
  else
    super
  end
end

def respond_to_missing?(method_name, include_private = false) # prevents giving UndefinedMethod error
  method_name.to_s.start_with?('data_') || super
end

def data_getter(key)
  self.data[key.to_i] if self.data.kind_of?(Array)
  self.data[key.to_sym] if self.data.kind_of?(Hash)
end

def data_setter(key, value)
  self.data[key.to_i] = value if self.data.kind_of?(Array)
  self.data[key.to_sym] = value if self.data.kind_of?(Hash)
  value # the method returns value because best_in_place sets the returned value as text
end

Now you can access the object.data[:name] using the getter object.data_name and set value using the setter object.data_name="test". But to get this working using best_in_place you need to dynamicly add it to the attr_accessible list. To do this you need to change the behavior of the mass_assignment_authorizer and make the object respond to accessable_methods with an array of method names that should be allowed to be edited like this:

def accessable_methods # returns a list of all the methods that are responded dynamicly
  self.data.keys.map{|x| "data_#{x.to_s}".to_sym }
end

private
  def mass_assignment_authorizer(user) # adds the list to the accessible list.
    super + self.accessable_methods
  end

So in the View you can now call

  best_in_place @object, :data_name

To edit the serialized data of @object.data[:name]

// You can also do this for an array using the element index instead of the attribute name:

<% @object.data.count.times do |index| %>
  <%= best_in_place @object, "data_#{index}".to_sym %>
<% end %>

You dont need to change the rest of the code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top