在Ruby中从一个对象映射到另一个对象

时间:2016-03-14 07:19:11

标签: ruby-on-rails ruby

我有两个对象, UserData 对象和用户对象。目前他们看起来像这样:

user_data.rb

class UserData < ActiveRecord::Base
  self.table = "users"
end

user.rb

class User
  attr_accessor :first_name, :last_name, :email, :password
end

我现在要做的是将 UserData 对象映射到用户对象,反之亦然。对于第一个场景,我有以下代码:

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

然而,问题是每当我向users表添加一个新列时,我也必须在mapper中添加代码。当我的代码中有大量的对象需要映射时,它会变得非常混乱。我的问题是:这样做有更好/更好的方式吗?理想的情况是我不再需要触摸Mapper代码了。

3 个答案:

答案 0 :(得分:2)

你可以使用一点ruby元编程来完成这个:

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

然后你只需修改ATTRS,一切都会自动生效!

答案 1 :(得分:2)

您可以通过调用attribute_names类方法从ActiveRecord模型中获取属性名称列表。有了它,可以很容易地迭代它们并依次获取和分配每个属性:

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

如果您愿意,可以使用相同的方法在User类中动态创建attr_accessor

class User
  attr_accessor *UserData.attribute_names
end

答案 2 :(得分:1)

您可以根据此问题执行某些操作: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