I’d probably create a module and include it into the controllers that need that behavior. I like to name these ending in -able. It reads really nicely, but it’s not required, obviously. Maybe restorable here or something?
If you use an ActiveSupport::Concern, for your module, you can very easily add the restore_user_data... and save_user_data... calls as filters on the appropriate actions using the included block.
Because the filters will need access to the instance variable, I would add a template method to the module. Something like:
def resource
raise NotImplementedError, "Restorables must implement #resource`
end
Then your filters in the module use this resource method rther than an instance variable. The controllers that include your module would then have to override resource to provide the right variable.
@derekprior I like your suggestion because for me it is a Rails way. So I’ve implemented something like this:
module Restorable
private
def save_user_data
attributes_to_save.each do |attribute|
cookies[cookie_name(attribute)] = { value: resource.send(attribute), expires: 3.months.from_now }
end
end
def restore_user_data
attributes_to_save.each do |attribute|
resource.send("#{attribute}=", cookies[cookie_name(attribute)])
end
end
def resource
raise NotImplementedError, 'Restorables must implement #resource'
end
def attributes_to_save
raise NotImplementedError, 'Restorables must implement #attributes_to_save'
end
def cookie_name(attribute)
"#{resource.class.name.downcase}_#{attribute}"
end
end
Then in my controller I just need to include Restorable and define attributes_to_save and resource methods. Also I need to call restore_user_data in new action and save_user_data in create action.
I’ve wanted to use before_filter to do it automatically as you suggested, but it is not working because instance variable for resource is not set before action is called.
@benorenstein I definitely wanted to try your solution as first. But I was not able to do it. I was even not able to start with tests… Issue is that I have no idea how to access cookies in extracted class. I’ve tried different solutions which came to my mind, do some googling, but without any success.
Could you please point me to right direction? How would you test/implement it?
What does the code for the controllers that include restorable look like? You should be able to refactor such that the ivar gets set with memoization in its implementation of resource.
Well @derekprior. What seemed impossible for me before is now done! Thanks!
I’ve created version which is working with before filters.
Fo anyone interested, here it is:
module Restorable
extend ActiveSupport::Concern
included do
before_filter :restore_user_data, only: [:new]
before_filter :save_user_data, only: [:create, :update]
end
private
def save_user_data
if resource.valid?
attributes_to_save.each do |attribute|
cookies[cookie_name(attribute)] = { value: resource.send(attribute), expires: 3.months.from_now }
end
end
end
def restore_user_data
attributes_to_save.each do |attribute|
resource.send("#{attribute}=", cookies[cookie_name(attribute)])
end
end
def resource
raise NotImplementedError, 'Restorables must implement #resource'
end
def attributes_to_save
raise NotImplementedError, 'Restorables must implement #attributes_to_save'
end
def cookie_name(attribute)
"#{resource.class.name.downcase}_#{attribute}"
end
end
And I’ve implemented these two additional private methods in my controller:
def resource
if params[:solver]
@solver ||= issue.solvers.build(solver_params)
else
@solver ||= issue.solvers.build
end
end
def attributes_to_save
[:first_name, :last_name, :email]
end
Is there more elegant way how to implement resource? Because in solver_params I require some parameters and it is not working when they are empty…
Anyway I would also like to try out solution suggested by Ben. So If anyone of you can point me to right direction it would be great! Thanks.