2013-09-24 78 views
0

我有紅寶石的非數據庫支持類:Ruby類與數組屬性

class User 
    attr_accessor :countries 
end 

我想國家的根本是ISO國家代碼(美國,英國,加拿大,澳洲等)的數組我不想建立一個單獨的模型來保存每個模型。是否有一種神奇的方法讓Ruby瞭解:countries是一個數組並對其進行相應處理,或者是否需要編寫countriescountries=方法?

我試着用user.countries = ['US']設置國家數組,我得到一個NoMethodError。

+5

這是一個訪問器;不管它是什麼類型。 –

+0

如果你使用'attr_accessor'這個,你將獲得免費的'countries'和'countries =':) :) –

+0

這個類是如何「相應地對待它」的,這與'user.countries = [:gb ,:au]'? – Gareth

回答

3

Ruby中的變量無關緊要。

attr_accessor只是創建getter和setter方法來設置和返回實例變量; @countries在這種情況下。您可以將實例變量設置爲你的陣列,或使用二傳手:

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留下。我也覺得這很醜。

如果國家不會在實例間改變,爲什麼不是一個常數?

編輯/澄清

Tadman的點是良好的服用,例如,this diatribe on state。我不在乎的這些限制只限於小型的,自我控制的,獨立的班級。做出這些假設存在固有的風險,這些風險的水平取決於項目。

+0

當你擁有'attr_accessor'時使用實例變量,如果你重寫了getter或setter方法中的任何一個,就會導致一些錯誤。如果你在絕對需要''這個''的地方做了很多JavaScript,那麼加入'self'的紀律並不是什麼大問題。 – tadman

+0

@tadman正確。如果你使用'attr_accessor'並覆蓋其中一個,我會說你做錯了。 IMO'attr_accessor'傳達它是一個訪問器,而不是執行其他邏輯的方法。 –

+0

那麼,它可以在子類中重寫,但不能在父類中重寫。做出假設是有風險的。如果你聲明瞭任何類型的存取器,即使實例變量*可能相同,也應該使用它。 – tadman

3

貌似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 

freeze阻止修改:

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

更新

這裏的問題是,你的國陣必須被某種方式持久。你提到has_many,所以Rails似乎參與其中。您可以使用ActiveRecordserialize方法:

class User < ActiveRecord::Base 
    serialize :countries 
end 

這將countries屬性的數據庫保存爲一個對象,並檢索它是這樣:

u = User.new 
u.countries = ["US", "CA"] 
u.save 

u = User.last 
u.countries 
#=> ["US", "CA"] 

它的轉換和從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) 
+1

你也可以做一個類方法來包裝它:'def self.countries;國家; end'。這似乎毫無意義,但它允許子類根據需要覆蓋該方法,並避免將此常量的結構暴露給需要訪問數組的其他部分。 – tadman

+0

查看上面的評論。用戶應該擁有ISO代碼完整列表子集的所有權。 Has_many對於我的應用程序來說是過分的。 –

+0

@kid_drew我已經更新了我的答案。 – Stefan