Rails before validation strip whitespace najlepsze praktyki
Chciałbym, aby mój model użytkownika wyczyścił niektóre dane wejściowe przed zapisem. Na razie wystarczy proste usuwanie spacji. Więc aby uniknąć ludzi rejestrujących się z "Harry" i udawać, że jest "Harry", na przykład.
Zakładam, że dobrym pomysłem jest zrobienie tego przed walidacją, aby validates_uniqueness_of mógł uniknąć przypadkowych duplikatów.
class User < ActiveRecord::Base
has_many :open_ids
validates_presence_of :name
validates_presence_of :email
validates_uniqueness_of :name
validates_uniqueness_of :email
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
before_validation :strip_whitespace, :only => [:name, :email, :nick]
private
def strip_whitespace(value)
value.responds_to?('strip') ? value.strip : value
end
end
Jednak ten kod zawiera błąd ArgumentError: wrong number of arguments (0 for 1). Założyłem, że oddzwanianie zostaną przekazane wartości.
Również: czy to rozbieranie to dobry pomysł? A może powinienem raczej zweryfikować spację i powiedzieć użytkownikowi, że "Harry" zawiera nieprawidłową spację (chcę zezwolić na "Harry Potter", ale nie "Harry\s\sPotter").
Edit: jak zaznaczono w komentarzu, mój kod jest zły (dlatego pytałem o. o.). Upewnij się, że przeczytałeś zaakceptowaną odpowiedź oprócz mojego pytania o poprawny kod i aby uniknąć tych samych błędów, które popełniłem.
13 answers
Nie wierzę, że to działa. Prawdopodobnie chcesz zamiast tego napisać swoją metodę w ten sposób:
def strip_whitespace
self.name = self.name.strip unless self.name.nil?
self.email = self.email.strip unless self.email.nil?
self.nick = self.nick.strip unless self.nick.nil?
end
Możesz uczynić go bardziej dynamicznym, jeśli chcesz użyć czegoś podobnego self.columns
, ale w tym tkwi sedno sprawy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-18 01:27:21
Istnieje kilka klejnotów, które robią to automatycznie. Te perełki działają w podobny sposób do tworzenia wywołania zwrotnego w before_validation. Jeden dobry klejnot jest na https://github.com/holli/auto_strip_attributes
gem "auto_strip_attributes", "~> 2.2"
class User < ActiveRecord::Base
auto_strip_attributes :name, :nick, nullify: false, squish: true
auto_strip_attributes :email
end
Stripping jest często dobrym pomysłem. Szczególnie w przypadku wiodących i końcowych spacji. Użytkownik często tworzy spacje końcowe podczas kopiowania / wklejania wartości do formularza. Z nazwami i innymi ciągami identyfikującymi możesz również chcieć wycisnąć ciąg. Aby "Harry Potter" stał się "Harry Potter "(opcja squish w klejnocie).Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-06 13:50:55
Odpowiedź Charliego jest dobra, ale jest trochę gadatliwości. Tu jest wersja ciaśniejsza:
def clean_data
# trim whitespace from beginning and end of string attributes
attribute_names.each do |name|
if send(name).respond_to?(:strip)
send("#{name}=", send(name).strip)
end
end
end
Powód, dla którego używamy
self.foo = "bar"
Zamiast
foo = "bar"
W kontekście obiektów ActiveRecord jest to, że Ruby interpretuje te ostatnie jako przypisanie zmiennej lokalnej. Po prostu ustawi zmienną foo w zakresie metody, zamiast wywoływać metodę " foo="obiektu.
Ale jeśli wywołujesz metodę, nie ma niejednoznaczności. Tłumacz wie, że nie jesteś odnosi się do zmiennej lokalnej o nazwie foo, ponieważ jej nie ma. Więc na przykład z:
self.foo = foo + 1
Musisz użyć "self" do przypisania, ale nie do odczytu bieżącej wartości.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-27 10:24:08
Chciałbym dodać jedną pułapkę, którą możesz doświadczyć w powyższych rozwiązaniach "before_validations". Weźmy ten przykład:
u = User.new(name: " lala")
u.name # => " lala"
u.save
u.name # => "lala"
Oznacza to, że masz niespójne zachowanie oparte na tym, czy twój obiekt został zapisany, czy nie. Jeśli chcesz rozwiązać ten problem, proponuję inne rozwiązanie twojego problemu: nadpisanie odpowiednich metod settera.
class User < ActiveRecord::Base
def name=(name)
write_attribute(:name, name.try(:strip))
end
end
Podoba mi się również to podejście, ponieważ nie zmusza do włączania usuwania wszystkich atrybutów, które go obsługują-w przeciwieństwie do attribute_names.each
wspomniany wcześniej. Ponadto, nie wymaga wywołań zwrotnych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-17 13:20:15
Podoba mi się odpowiedź Karla, ale czy jest sposób, aby to zrobić bez odwoływania się do każdego z atrybutów Po nazwie? To znaczy, czy istnieje sposób, aby po prostu uruchomić przez atrybuty modelu i wywołać pasek na każdym z nich (jeśli reaguje na tę metodę)?
Byłoby to pożądane, więc nie muszę aktualizować metody remove_whitespace za każdym razem, gdy zmieniam model.
UPDATE
Widzę, że Karl zasugerował, że możesz chcieć robić takie rzeczy. Nie wiedziałem od razu, jak to można to zrobić, ale tutaj jest coś, co działa dla mnie, jak opisano powyżej. Jest chyba lepszy sposób, ale to działa:def clean_data
# trim whitespace from beginning and end of string attributes
attribute_names().each do |name|
if self.send(name.to_sym).respond_to?(:strip)
self.send("#{name}=".to_sym, self.send(name).strip)
end
end
End
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-06 01:03:07
Jeśli masz dostęp do ActiveSupport, użyj squish zamiast strip.
Http://api.rubyonrails.org/classes/String.html#method-i-squish
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-20 04:00:30
Zamiast tego możemy napisać lepszą metodę bardziej ogólną niezależnie od rodzaju atrybutów z obiektem (może mieć 3 pola typu string, kilka wartości logicznych, kilka liczbowych)
before_validation :strip_input_fields
def strip_input_fields
self.attributes.each do |key, value|
self[key] = value.strip if value.respond_to?("strip")
end
end
Mam nadzieję, że to komuś pomoże!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-25 19:54:12
StripAttributes Gem
Użyłem strip_attributes. To naprawdę niesamowite i łatwe do wdrożenia.
Domyślne Zachowanie
class DrunkPokerPlayer < ActiveRecord::Base
strip_attributes
end
Domyślnie spowoduje to usunięcie tylko początkowych i końcowych spacji i będzie działać na wszystkich atrybutach modelu. Jest to idealne rozwiązanie, ponieważ nie jest destrukcyjne i nie wymaga określania, które atrybuty mają być pasowane.
Za pomocą except
# all attributes will be stripped except :boxers
class SoberPokerPlayer < ActiveRecord::Base
strip_attributes :except => :boxers
end
Za pomocą only
# only :shoe, :sock, and :glove attributes will be stripped
class ConservativePokerPlayer < ActiveRecord::Base
strip_attributes :only => [:shoe, :sock, :glove]
end
Za pomocą allow_empty
# Empty attributes will not be converted to nil
class BrokePokerPlayer < ActiveRecord::Base
strip_attributes :allow_empty => true
end
Użycie collapse_spaces
# Sequential spaces in attributes will be collapsed to one space
class EloquentPokerPlayer < ActiveRecord::Base
strip_attributes :collapse_spaces => true
end
Using regex
class User < ActiveRecord::Base
# Strip off characters defined by RegEx
strip_attributes :only => [:first_name, :last_name], :regex => /[^[:alpha:]\s]/
# Strip off non-integers
strip_attributes :only => [:phone], :regex => /[^0-9]/
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-20 17:49:36
Oto alternatywne podejście, jeśli chodzi głównie o użytkowników błędnie wprowadzających dane w formularzach front-end...
# app/assets/javascripts/trim.inputs.js.coffee
$(document).on "change", "input", ->
$(this).val $(this).val().trim()
Następnie dołącz plik do aplikacji.js, jeśli nie jesteś jeszcze w tym całe drzewo.
Zapewni to, że każde wejście zostanie usunięte przed wysłaniem do zapisania przez Rails. Jest przypisany do document
i delegowany do wejść, więc wszelkie wejścia dodane do strony później będą przetwarzane jako cóż.
Plusy:
- nie wymaga wymieniania poszczególnych atrybutów po nazwie
- nie wymaga żadnego metaprogramowania
- nie wymaga zewnętrznych zależności bibliotek
Wady:
- dane przesyłane w inny sposób niż formularze (np. za pośrednictwem API) nie będą przycinane
- nie ma zaawansowanych funkcji, takich jak squish (ale możesz to dodać samemu)
- Jak wspomniano w komentarzach, nie działa, jeśli JS jest wyłączone (ale kto do tego koduje?)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-30 08:14:21
Nadpisanie metod zapisu atrybutów jest kolejnym dobrym sposobem. Na przykład:
class MyModel
def email=(value)
super(value.try(:strip))
end
end
Wtedy jakakolwiek część aplikacji, która ustawia wartość, zostanie usunięta, w tym assign_attributes i tak dalej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-05 04:13:31
Chociaż mogę przyjąć podobne podejście do odpowiedzi Karla, wolę bardziej zwięzłą składnię z mniejszą liczbą zadań:
def strip_whitespace
self.name.try(:strip!)
self.email.try(:strip!)
self.nick.try(:strip!)
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-22 19:37:21
Ponieważ nie mogę jeszcze skomentować, muszę zapytać tutaj: Która metoda daje ArgumentError? strip
, LUB responds_to?
Ponadto, .strip
usuwa tylko początkowe i końcowe białe znaki. Jeśli chcesz, aby "Harry Potter" z dwoma spacjami nie był akceptowany, musisz użyć wyrażenia regularnego lub, prościej, możesz zadzwonić .split, który Usuwa spacje i ponownie łączy łańcuch z pojedynczą spacją.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-14 07:48:22
Inną opcją klejnotu jest attribute_normalizer :
# By default it will strip leading and trailing whitespace
# and set to nil if blank.
normalize_attributes :author, :publisher
:Pasek usuwa wiodące i końcowe białe spacje.
normalize_attribute :author, :with => :strip
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-08 16:10:14