Reputation: 401
I have two objects, a UserData object and a User object. At the moment they look like this:
user_data.rb
class UserData < ActiveRecord::Base
self.table = "users"
end
user.rb
class User
attr_accessor :first_name, :last_name, :email, :password
end
What I'm trying to do now is map the UserData object to the User object and the other way around. For the first scenario I have the following code:
module Mappers
module UserMapper
def map user
@user = UserData.new
@user.first_name = user.first_name
@user.last_name = user.last_name
@user.password = user.password
@user.email = user.email
end
end
end
However, the problem is that whenever I add a new column to the users table, I have to add the code in the mapper as well. And when I have tons of objects inside my code that need to be mapped, it's going to be really messy. My question is: Is there a better / dynamic way for doing this? The ideal situation would be when I don't have to touch the Mapper code anymore.
Upvotes: 3
Views: 2075
Reputation: 106027
You can get a list of attribute names from an ActiveRecord model by calling the attribute_names
class method. Armed with that, it's easy to iterate over them and fetch and assign each attribute in turn:
module UserMapper
def map(user)
@user = UserData.new do |user_data|
UserData.attribute_names.each do |attr_name|
next unless user.respond_to?(attr_name)
user_data[attr_name] = user.public_send(attr_name)
end
end
end
end
You could use the same method to dynamically create attr_accessor
s in your User class, if you wanted:
class User
attr_accessor *UserData.attribute_names
end
Upvotes: 3
Reputation: 3012
You could do something based on this question: Fastest/One-liner way to list attr_accessors in Ruby?
module Mappers
module UserMapper
def map user
@user = UserData.new
user.attributes.inspect.each do |attr|
@user.public_send("#{attr}=", user.public_send(attr))
end
end
end
end
Upvotes: 1
Reputation: 6707
You can use a bit ruby meta programming to complete this:
class User
ATTRS = [:first_name, :last_name, :email, :password]
attr_accessor *ATTRS
end
module Mappers
module UserMapper
def map(user)
@user = UserData.new
User::ATTRS.each do |attr|
@user.send("#{attr}=", user.send(attr))
end
end
end
end
Then you just modify ATTRS
and everything will work automatically!
Upvotes: 2