【问题标题】:Ruby object mass assignmentRuby 对象质量分配
【发布时间】:2016-01-20 17:30:46
【问题描述】:

有没有更好的方法来做下面的代码?

user.name = "abc"
user.email = "abc@test.com"
user.mobile = "12312312"

这样就可以了:

user.prepare do |u|
  u.name = "abc"
  u.email = "abc@test.com"
  u.mobile = "12312312"
end

【问题讨论】:

标签: ruby accessor


【解决方案1】:

tap 让你做到这一点:

user.tap do |u|
  u.name = "abc"
  u.email = "abc@test.com"
  u.mobile = "12312312"
end

【讨论】:

    【解决方案2】:

    当您的属性以散列形式出现时的替代选项:

    attrs = {
      name: "abc",
      email: "abc@test.com",
      mobile: "12312312"
    }
    
    attrs.each { |key, value| user.send("#{key}=", value) }
    

    【讨论】:

    • 这不起作用,因为user.send(key) 将调用getter - 而不是setter 方法。你需要做user.send("#{key}=")
    • 感谢您的收获!
    【解决方案3】:

    您也可以执行以下操作:

    user.instance_eval do 
        @name = "abc"
        @email = "abc@test.com"
        @mobile = "12312312"
    end
    

    您可以在给instance_eval的块内访问user的实例变量


    如果您希望调用访问器方法而不是直接操作实例变量,可以使用以下代码。

    user.instance_eval do
        self.name = "xyz"
        self.email = "abc@test.com"
        self.mobile = "12312312"
    end
    

    user.instance_eval do |o|
        o.name = "xyz"
        o.email = "abc@test.com"
        o.mobile = "12312312"
    end
    

    【讨论】:

    • 虽然instance_eval 违反了封装规则,但我不建议这样做。最好按照已经推荐的方式使用.tap.send
    • @max send 也破坏了封装 ;-) Ruby 的封装只适用于表现良好的程序员。
    • 你就在那儿。 public_send 确实如此。人们可能希望.send 做到了。
    【解决方案4】:

    使用 ActiveRecord 对象,您可以使用 .assign_attributes 或更新 方法:

    user.assign_attributes( name: "abc", email: "abc@test.com", mobile: "12312312")
    # attributes= is a shorter alias for assign_attributes
    user.attributes = { name: "abc", email: "abc@test.com", mobile: "12312312" }
    
    # this will update the record in the database
    user.update( name: "abc", email: "abc@test.com", mobile: "12312312" )
    
    # or with a block
    user.update( name: "abc", mobile: "12312312" ) do |u|
      u.email = "#{u.name}@test.com" 
    end
    

    .update 接受一个块,而 assign_attributes 不接受。如果您只是分配文字值的哈希值 - 例如用户在参数中传递的值,则无需使用块。

    如果你有一个普通的旧红宝石对象,你想通过批量赋值来增加趣味,你可以这样做:

    class User
    
      attr_accessor :name, :email, :mobile
    
      def initialize(params = {}, &block)
        self.mass_assign(params) if params
        yield self if block_given?
      end
    
      def assign_attributes(params = {}, &block)
        self.mass_assign(params) if params
        yield self if block_given?
      end
    
      def attributes=(params)
        assign_attributes(params)
      end
    
      private
        def mass_assign(attrs)
          attrs.each do |key, value|
            self.public_send("#{key}=", value)
          end
        end
    end
    

    这会让你做:

    u = User.new(name: "abc", email: "abc@test.com", mobile: "12312312")
    u.attributes = { email: "abc@example.com", name: "joe" }
    u.assign_attributes(name: 'bob') do |u|
      u.email = "#{u.name}@example.com"
    end
    
    # etc.
    

    【讨论】:

      【解决方案5】:

      假设“用户”是您控制的类,那么您可以定义一个方法来做您想做的事。例如:

      def set_all(hash)
        @name, @email, @mobile = hash[:name], hash[:email], hash[:mobile]
      end
      

      然后在您的其余代码中:

      user.set_all(name: "abc", email: "abc@test.com", mobile: "12312312")
      

      如果“用户”是 ActiveRecord 模型的一个实例,那么我对如何让它工作的细节有点犹豫。但原则仍然适用:通过将复杂性的责任转移给接收者来干燥代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-25
        • 1970-01-01
        • 2012-01-10
        • 2018-04-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多