2016-01-20 2 views
2

Есть ли лучший способ сделать код ниже?Массовое назначение объекта Ruby

user.name = "abc" 
user.email = "[email protected]" 
user.mobile = "12312312" 

Что-то, как это будет делать:

user.prepare do |u| 
    u.name = "abc" 
    u.email = "[email protected]" 
    u.mobile = "12312312" 
end 
+2

Возможный дубликат [DRY способ присвоить значения хэша объекту] (http://stackoverflow.com/questions/3669801/dry-way-to-assign-hash-values-to-an-object) –

+0

Если 'user' является' ActiveRecord', тогда ['attributes ='] (http://apidock.com/rails/ActiveRecord/AttributeAssignment/assign_attributes) может быть вариантом. – spickermann

+0

Почему вам нравится ваш второй пример лучше первого? – spickermann

ответ

0

Альтернативный вариант, когда ваши атрибуты приходят в виде хэша:

attrs = { 
    name: "abc", 
    email: "[email protected]", 
    mobile: "12312312" 
} 

attrs.each { |key, value| user.send("#{key}=", value) } 
+1

Это не будет работать, поскольку 'user.send (key)' будет вызывать getter - не метод setter. Вам нужно сделать 'user.отправить ("# {ключ} =") '. – max

+0

Спасибо за улов! – fylooi

0

С ActiveRecord объектов вы можете использовать .assign_attributes или методы обновления :

user.assign_attributes(name: "abc", email: "[email protected]", mobile: "12312312") 
# attributes= is a shorter alias for assign_attributes 
user.attributes = { name: "abc", email: "[email protected]", mobile: "12312312" } 

# this will update the record in the database 
user.update(name: "abc", email: "[email protected]", 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: "[email protected]", mobile: "12312312") 
u.attributes = { email: "[email protected]", name: "joe" } 
u.assign_attributes(name: 'bob') do |u| 
    u.email = "#{u.name}@example.com" 
end 

# etc. 
0

Вы можете сделать следующее а также:

user.instance_eval do 
    @name = "abc" 
    @email = "[email protected]" 
    @mobile = "12312312" 
end 

Вы можете получить доступ к переменным экземпляра из user внутри блок дано instance_eval


Вы можете использовать следующий код, если вы хотите, чтобы вызвать методы аксессоров вместо непосредственного манипулирования переменного экземпляра.

user.instance_eval do 
    self.name = "xyz" 
    self.email = "[email protected]" 
    self.mobile = "12312312" 
end 

или

user.instance_eval do |o| 
    o.name = "xyz" 
    o.email = "[email protected]" 
    o.mobile = "12312312" 
end 
+0

Я бы не рекомендовал этого, но поскольку 'instance_eval' нарушает правила инкапсуляции. Лучше использовать '.tap' или' .send', как уже было рекомендовано. – max

+0

@max 'send' также разрушает инкапсуляцию ;-) Инкапсуляция Ruby предназначена только для хорошо продуманных программистов. –

+0

Ты прямо здесь. Однако 'public_send' делает. И можно было бы пожелать этого. – max

0

Если предположить, что «пользователь» является классом, который вы контролируете, то вы можете просто определить метод, чтобы сделать то, что вы хотите. Например:

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

А потом в остальной части кода:

user.set_all(name: "abc", email: "[email protected]", mobile: "12312312") 

Если «пользователь» является экземпляром, скажем, модели ActiveRecord, то я немного шаткий на детали того, как вы могли бы заставить это работать. Но основной принцип по-прежнему применяется: DRY-код, перенося ответственность за сложность получателя.

Смежные вопросы