从Ruby中的字符串动态设置对象属性的更优雅的方法

时间:2018-07-28 22:35:41

标签: ruby-on-rails ruby

设置

说我有一个名为Theme的Rails模型(类)。

class Theme < ApplicationRecord
  attr_accessor :web,:mobile,:accessible
end

现在,我想像这样遍历每个属性并分配它们(效果很好):

["web","mobile","accessible"].each do |stylesheet|
      @theme.send("#{stylesheet}=", "some dynamic value"))
      #I also use the value somewhere below in loop using @theme.send(stylesheet)      
end

这实际上只是在做

@theme.web = "some dynamic value"
@theme.mobile = "some dynamic value"
@theme.accessible = "some dynamic value"

问题

现在虽然上面的方法工作得很好,但是我试图找出是否有一种更优雅的方式编写下面的代码而不使用自省api send

@theme.send("#{stylesheet}=", "some dynamic value"))

以上内容似乎并不干净且“红润”。关于如何使作业更好地阅读的任何想法?

  

更新

我现在在这里显示完整的方法,为了使问题简单起见,我在前面省略了它。万一看到完整的方法可以帮助您:

def edit
    ["web","mobile","accessible"].each do |stylesheet|
      @theme.send("#{stylesheet}=",File.read("#{@theme.user_dir}/#{stylesheet}.css"))
      if @map.stylesheet[stylesheet] !=  @theme.send(stylesheet)
        @map.stylesheet[stylesheet] = @theme.send(stylesheet)
        @map.stylesheet.save!
      end
    end
end

5 个答案:

答案 0 :(得分:2)

你可以

[:@web, :@mobile, :@accessible].each do |stylesheet|
  @theme.instance_variable_set(stylesheet, "some dynamic value"))      
end

这样可以节省字符串连接和string-> symbole转换,因此从理论上讲应该更快一些。

答案 1 :(得分:1)

如果您定义了attr_accessor,它将为您生成getter和setter方法,从而扩展了可以实现此方法的各种方式。

我很高兴看到您没有走eval的路线,因为那总是很糟糕的选择,几乎总有比eval所需要的性能/安全性更好的选择。否决任何建议的人。

您已经实现了send方式,这是一种常见的方式,尽管我从这篇文章中推测出,您和我在某些方面似乎很不雅,对此我感到相似。

大多数替代品非常相似。正如我已经说过的,attr_accessor创建方法,因此您也可以使用以下惯用法:

theme = Theme.new
theme.method("mobile=").call("some value")

如您所见,虽然您可以将方法存储到变量中以进行重复使用,但几乎是同一件事:

get_mobile = theme.method("mobile") # Or use symbol
set_mobile = theme.method("mobile=")

mobile = get_mobile.call
set_mobile.call 'foobar'

编辑: 附带说明一下,使用符号而不是字符串几乎总是更好。 Ruby始终会在内部对它们进行符号化,但是以符号开头可以防止每次调用额外调用rb_intern并增加查找现有ID的查找。在我看来,除了提高性能外,它们还使代码看起来不那么草率。

答案 2 :(得分:1)

如果您使用ActiveModel::Attributes而不是标准的attr_accessor,则可以执行以下操作:

class Theme < ApplicationRecord

  attribute :web
  attribute :mobile
  attribute :responsive

end 

@theme['web'] = 'some value'

[]语法适用于任何ActiveModel::Attributes属性-因此也适用于您的数据库列。

答案 3 :(得分:1)

正如miaout17所述,我认为instance_variable_set是必经之路。另外,我可以看到您使用send从实例变量中获取值,而当您执行File.read..

因此,基于@ miaout17的答案以及关注方式

["web","mobile","accessible"].each do |stylesheet|
  css = File.read("#{@theme.user_dir}/#{stylesheet}.css")
  @theme.instance_variable_set("@#{stylesheet}", css)
  @map.stylesheet[stylesheet] = css if (@map.stylesheet[stylesheet] !=  css)
end
@map.stylesheet.save!

但是,如果仅针对3个定义的变量,我将亲自定义3次,并使代码更明确。我知道使用元编程很酷,但是除非真正需要元编程的调试,否则我会避免使用它

答案 4 :(得分:0)

我不认为它不干净或不是红宝石。

Ruby是一种具有良好OOP模型的动态语言。因此,使用send并遍历方法名称是很自然的。

另外,请看一些ActiveRecord方法,例如:https://apidock.com/rails/ActiveRecord/Base/assign_attributes 他们可以帮助

P.S。我会用符号代替字符串btw。 %i [mobile ...]