具有数组属性的Ruby类

具有数组属性的Ruby类,ruby,Ruby,我在Ruby中有一个非数据库支持的类: class User attr_accessor :countries end 我希望国家仅仅是一组ISO国家代码(美国、GB、CA、AU等),我不想构建一个单独的模型来容纳每个国家。有没有一种神奇的方法可以让Ruby理解:countries是一个数组并相应地处理它,或者我需要编写countries和countries=方法 我试着用user.countries=['US']设置countries数组,得到了一个NoMethodError。看起来co

我在Ruby中有一个非数据库支持的类:

class User
  attr_accessor :countries
end
我希望国家仅仅是一组ISO国家代码(美国、GB、CA、AU等),我不想构建一个单独的模型来容纳每个国家。有没有一种神奇的方法可以让Ruby理解
:countries
是一个数组并相应地处理它,或者我需要编写
countries
countries=
方法


我试着用user.countries=['US']设置countries数组,得到了一个NoMethodError。

看起来
countries
应该是一个常量:

class User
  COUNTRIES = %w(
    AF AX AL DZ AS AD AO AI AQ AG AR AM AW AU AT AZ BS BH BD BB BY BE BZ BJ BM
    BT BO BQ BA BW BV BR IO BN BG BF BI KH CM CA CV KY CF TD CL CN CX CC CO KM
    CG CD CK CR CI HR CU CW CY CZ DK DJ DM DO EC EG SV GQ ER EE ET FK FO FJ FI
    FR GF PF TF GA GM GE DE GH GI GR GL GD GP GU GT GG GN GW GY HT HM VA HN HK
    HU IS IN ID IR IQ IE IM IL IT JM JP JE JO KZ KE KI KP KR KW KG LA LV LB LS
    LR LY LI LT LU MO MK MG MW MY MV ML MT MH MQ MR MU YT MX FM MD MC MN ME MS
    MA MZ MM NA NR NP NL NC NZ NI NE NG NU NF MP NO OM PK PW PS PA PG PY PE PH
    PN PL PT PR QA RE RO RU RW BL SH KN LC MF PM VC WS SM ST SA SN RS SC SL SG
    SX SK SI SB SO ZA GS SS ES LK SD SR SJ SZ SE CH SY TW TJ TZ TH TL TG TK TO
    TT TN TR TM TC TV UG UA AE GB US UM UY UZ VU VE VN VG VI WF EH YE ZM ZW
  ).freeze
end

User::COUNTRIES.include? "US" #=> true
冻结
防止修改:

User::COUNTRIES.delete "US"   #=> RuntimeError: can't modify frozen Array
更新

这里的问题是,您的国家阵列必须以某种方式保持。您提到的
有很多
,因此Rails似乎参与其中。您可以使用
ActiveRecord
的方法:

它在内部与YAML进行转换,因此
users
表如下所示:

mysql> SELECT * FROM users;
+----+-------------------+---------------------+---------------------+
| id | countries         | created_at          | updated_at          |
+----+-------------------+---------------------+---------------------+
|  1 | ---\n- US\n- CA\n | 2013-09-24 18:24:03 | 2013-09-24 18:24:03 |
+----+-------------------+---------------------+---------------------+
1 row in set (0,00 sec)
在Ruby中,变量的类型并不重要

attr\u accessor
只创建设置和返回实例变量的getter和setter方法<代码>@countries在这种情况下。您可以将实例变量设置为数组,或使用setter:

class User
  attr_accessor :countries

  def initialize
    @countries = %w[Foo Bar Baz]
    # Or...
    self.countries = %w[Foo Bar Baz]
  end
end

> puts User.new.countries
=> ["Foo", "Bar", "Baz"]
就个人而言,我更喜欢使用实例变量而不是
self.xxx
;很容易忘记
self.
位,最后设置了一个局部变量,而实例变量
为nil
。我也觉得很难看

如果这些国家不会在不同的情况下发生变化,为什么不是一个常数呢

编辑/澄清


塔德曼的观点被很好地采纳了,例如。我不关心的环境仅限于小型的、自我控制的、独立的类。做出这些假设存在内在风险,这些风险的水平取决于项目。

它是一个访问者;不管它是什么类型。如果你使用
attr\u accessor
这个,你将得到免费的
countries
countries=
:):)这个类需要如何“相应地对待它”,这与仅仅
user.countries=[:gb,:au]不同
attr\u accessor
提供了
countries
countries=
方法,但是没有什么可以阻止您将
@countries
设置为数组以外的其他对象。如果您想执行数组操作,比如
,您的问题似乎已经或多或少得到了回答。但是您指出的这个用例似乎更适合常量而不是访问器方法imho。如果您有
attr\u访问器
时使用实例变量,那么如果您重写了getter或setter方法,可能会导致一些错误。如果你大量使用JavaScript,而
这是绝对必要的,那么严格遵守添加
self
的规则并不是什么大问题。@tadman正确。如果您使用的是
attr_accessor
并覆盖了其中一个,我会说您做得不对。IMO
attr_accessor
表明它是一个访问器,而不是一个执行其他逻辑的方法。它可以在子类中重写,但不能在父类中重写。做假设有点冒险。如果声明任何类型的访问器,即使实例变量可能相同,也应该使用它<代码>self.countries.push(“bla”)
不会work@TheRookierLearner我不知道你为什么这么想。你也可以制作一个类方法来总结:
def self.countries;国家;结束
。这似乎毫无意义,但它允许子类在需要时重写该方法,并避免将此常量的结构暴露给需要访问数组的代码的其他部分。请参阅上面的注释。用户实际上应该拥有ISO代码完整列表的一个子集的所有权。对我的申请来说,有很多是多余的。
mysql> SELECT * FROM users;
+----+-------------------+---------------------+---------------------+
| id | countries         | created_at          | updated_at          |
+----+-------------------+---------------------+---------------------+
|  1 | ---\n- US\n- CA\n | 2013-09-24 18:24:03 | 2013-09-24 18:24:03 |
+----+-------------------+---------------------+---------------------+
1 row in set (0,00 sec)
class User
  attr_accessor :countries

  def initialize
    @countries = %w[Foo Bar Baz]
    # Or...
    self.countries = %w[Foo Bar Baz]
  end
end

> puts User.new.countries
=> ["Foo", "Bar", "Baz"]