Frank Levering
Frank Levering

Reputation: 401

Map from one object to another in Ruby

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

Answers (3)

Jordan Running
Jordan Running

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_accessors in your User class, if you wanted:

class User
  attr_accessor *UserData.attribute_names
end

Upvotes: 3

Albin
Albin

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

Hieu Pham
Hieu Pham

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

Related Questions