2012-07-19 11 views
14

Mam model z niektórymi atrybutami i atrybutem wirtualnym. Ten wirtualny atrybut służy do zaznaczenia pola wyboru w formularzu tworzenia.Wpisz rzutowany atrybut wirtualny modelu ActiveRecord

class Thing < ActiveRecord::Base 
    attr_accessor :foo 
    attr_accessible :foo 
end 

ponieważ pole to pole w formularzu, atrybut foo otrzyma '0' lub '1' jako wartości. Chciałbym, że jest to logiczna z powodu następującego kodu:

class Thing < ActiveRecord::Base 
    attr_accessor :foo 
    attr_accessible :foo 

    before_validation :set_default_bar 

    private 

    def set_default_bar 
    self.bar = 'Hello' if foo 
    end 
end 

Problem polega na tym, że warunek będzie prawdziwa nawet gdy foo jest '0'. Chciałbym użyć ActiveRecord mechanizm typu odlewania ale tylko znalazłem to zrobić jest następujące:

class Thing < ActiveRecord::Base 
    attr_reader :foo 
    attr_accessible :foo 

    before_validation :set_default_bar 

    def foo=(value) 
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 


    private 

    def set_default_bar 
    self.bar = 'Hello' if foo 
    end 
end 

Ale czuję się brudny robi to w ten sposób. Czy istnieje lepsza alternatywa bez ponownego zapisywania metody konwersji?

Dzięki

Odpowiedz

14

Twoje rozwiązanie z oryginalnego posta wygląda jak najlepsze rozwiązanie dla mnie.

class Thing < ActiveRecord::Base 
    attr_reader :foo 
    def foo=(value) 
    @foo = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value) 
    end 
end 

Jeśli chciał oczyścić rzeczy się trochę, zawsze można utworzyć metody pomocnika, który definiuje swoją foo= metody pisarz dla używasz value_to_boolean.

Prawdopodobnie utworzyć moduł z metody zwanej bool_attr_accessor więc można uprościć model wyglądać następująco:

class Thing < ActiveRecord::Base 
    bool_attr_accessor :foo 
end 

Wydaje się ActiveModel powinno zapewnić coś takiego dla nas, tak że wirtualne atrybuty akt bardziej jak "rzeczywiste" (ActiveRecord-persisted) atrybuty. Oddanie tego typu jest niezbędne, gdy masz wirtualny atrybut boolowski, który zostanie przesłany z formularza.

Może powinniśmy złożyć patch do szyn ...

0

Dlaczego pan tego nie robi:

def foo=(value) 
    @foo = value 
    @bar = 'Hello' if value == "1" 
end 
+0

Ponieważ wartości można zmienić na "true" i "false", a one nadal powinny działać. Railsy to dobrze, ale w niewłaściwym miejscu IMHO. – Happynoff

+0

O ustawieniu @bar w 'foo =' Myślę, że to nie jest miejsce, w którym można to zrobić, zasada jednej odpowiedzialności i wszystko. I, w moim prawdziwym przypadku użycia, ma sens umieszczenie go w before_validation;) – Happynoff

+0

Masz rację. Masz takie rozwiązanie: https://gist.github.com/2000623, ale nie sądzę, że jest to lepsze rozwiązanie. To w zasadzie to samo, co szyny. Zobacz źródło: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb – Dougui

1

Spójrz na kod validates_acceptance_of (kliknij Pokaż źródło). Wdrożyli go w porównaniu do "0".

Używam go w dokumentach rejestracyjnych form, w ten sposób:

class User < ActiveRecord::Base 
    validates_acceptance_of :terms_of_service 
    attr_accessible :terms_of_service 
end 

Jeśli naprawdę chcesz rzucić z łańcucha etc można użyć to:

def foo=(value) 
    self.foo=(value == true || value==1 || value =~ (/(true|t|yes|y|1)$/i)) ? true:false 
end 

Albo dodać metodę typecast dla klasy String i użyj go w modelu:

class String 
def to_bool 
    return true if self == true || self =~ (/(true|t|yes|y|1)$/i) 
    return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i) 
    raise ArgumentError.new("invalid value for Boolean: \"#{self}\"") 
end 
end 
+0

Tak, może, ale uważam, że ten sposób jest trochę brudny. Wolałbym używać prawdziwej wartości boolowskiej. Co by się stało, gdyby w jakiejś innej części kodu napisałem 'Thing.new (foo: true)'? – Happynoff

+0

znalazłem ciąg # to_bool, aby był czysty i pomocny. Dzięki. – linojon

2

W Rails 5 można użyć attribute metody. Ta metoda definiuje atrybut z typem w tym modelu. W razie potrzeby zastąpi on typ istniejących atrybutów.

class Thing < ActiveRecord::Base 
    attribute :foo, :boolean 
end 

Uwaga: jest nieprawidłowe zachowanie tego attribute funkcji w szynach 5.0.0 modeli ładowanych z db. Dlatego używaj szyn 5.0.1 lub wyższych.

Powiązane problemy