保存する前に、ユーザーモデルの入力をサニタイズしたいと思います。今のところ、いくつかの単純な空白文字の除去が行われます。たとえば、「ハリー」に登録して「ハリー」のふりをする人を避けるためです。
Validates_uniqueness_ofが偶発的な重複を避けることができるように、検証の前にこのストリッピングを行うことは良い考えだと思います。
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
ただし、このコードにはエラーArgumentError:引数の数が間違っています(1の場合は0)。コールバックに値が渡されると想定しました。
また、このストリッピングは実際には良いアイデアですか?または、スペースで検証し、「Harry」に無効なスペースが含まれていることをユーザーに伝える必要があります(「Harry\s\sPotter」ではなく「Harry Potter」を許可したい)。
編集:コメントで指摘されているように、私のコードは間違っています(これが質問をa.o.に尋ねていた理由です)。正しいコードについての私の質問に加えて、受け入れられた答えを読んで、私が犯した同じ間違いを避けるようにしてください。
before_validation
がそのように機能するとは思わない。代わりに、次のようなメソッドを書きたいと思うでしょう。
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
self.columns
のようなものを使用したい場合は、より動的にすることができますが、それが要点です。
これを自動的に行うにはいくつかの宝石があります。これらのgemは、before_validationでコールバックを作成する同様の方法で機能します。良い宝石の1つは 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
多くの場合、ストリッピングは良い考えです。特に先頭と末尾の空白の場合。ユーザーは、フォームに値をコピー/貼り付けするときに末尾スペースを作成することがよくあります。名前やその他の識別文字列を使用して、文字列をスキッシュすることもできます。そのため、「Harry Potter」は「Harry Potter」になります(gemのスキッシュオプション)。
チャーリーの答えは良いですが、少し冗長です。これはより厳密なバージョンです。
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
使用する理由
self.foo = "bar"
の代わりに
foo = "bar"
activeRecordオブジェクトのコンテキストでは、Rubyは後者をローカル変数の割り当てとして解釈します。オブジェクト。
しかし、メソッドを呼び出す場合、あいまいさはありません。インタプリタは、fooというローカル変数がないため、これを参照していないことを知っています。したがって、たとえば:
self.foo = foo + 1
割り当てには「自己」を使用する必要がありますが、現在の値を読み取る必要はありません。
上記の「before_validations」ソリューションで発生する可能性のある落とし穴を1つ追加します。次の例をご覧ください。
u = User.new(name: " lala")
u.name # => " lala"
u.save
u.name # => "lala"
これは、オブジェクトが保存されたかどうかに基づいて一貫性のない動作が発生することを意味します。これに対処したい場合は、問題の別の解決策を提案します。対応するセッターメソッドを上書きします。
class User < ActiveRecord::Base
def name=(name)
write_attribute(:name, name.try(:strip))
end
end
また、前述のattribute_names.each
とは異なり、それをサポートするすべての属性に対してストリッピングを有効にする必要がないため、このアプローチも気に入っています。また、コールバックは必要ありません。
私はカールの答えが好きですが、名前で各属性を参照せずにそれを行う方法はありますか?つまり、モデル属性をただ実行し、各属性でストリップを呼び出す方法はありますか(メソッドに応答する場合)?
これは望ましいので、モデルを変更するたびにremove_whitespaceメソッドを更新する必要はありません。
[〜#〜] update [〜#〜]
カールは、あなたがこの種のことをしたいと思うかもしれないと暗示しているようです。私はそれがどのように行われるかをすぐには知りませんでしたが、上記のように私のために働くものがあります。おそらくもっと良い方法がありますが、これは機能します:
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
終わり
代わりに、オブジェクトの属性のタイプに関係なく、より一般的なより良いメソッドを書くことができます(3つの文字列タイプフィールド、少数のブール値、少数の数値を持つ可能性があります)
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
それが誰かを助けることを願っています!
ActiveSupportにアクセスできる場合は、stripの代わりにsquishを使用してください。
http://api.rubyonrails.org/classes/String.html#method-i-squish
strip_attributes を使用しました。本当に素晴らしく、簡単に実装できます。
class DrunkPokerPlayer < ActiveRecord::Base
strip_attributes
end
デフォルトでは、これは先頭と末尾の空白のみを取り除き、モデルのすべての属性に作用します。これは破壊的ではなく、ストライプ化する必要がある属性を指定する必要がないため、理想的です。
except
を使用する# all attributes will be stripped except :boxers
class SoberPokerPlayer < ActiveRecord::Base
strip_attributes :except => :boxers
end
only
を使用する# only :shoe, :sock, and :glove attributes will be stripped
class ConservativePokerPlayer < ActiveRecord::Base
strip_attributes :only => [:shoe, :sock, :glove]
end
allow_empty
を使用# Empty attributes will not be converted to nil
class BrokePokerPlayer < ActiveRecord::Base
strip_attributes :allow_empty => true
end
collapse_spaces
を使用# Sequential spaces in attributes will be collapsed to one space
class EloquentPokerPlayer < ActiveRecord::Base
strip_attributes :collapse_spaces => true
end
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
属性書き込みメソッドをオーバーライドすることも別の良い方法です。例えば:
class MyModel
def email=(value)
super(value.try(:strip))
end
end
次に、assign_attributesなどを含む、値を設定するアプリケーションのすべての部分が削除されます。
ユーザーがフロントエンドフォームにデータを誤って入力することを主に懸念している場合の代替アプローチを次に示します。
# app/assets/javascripts/trim.inputs.js.coffee
$(document).on "change", "input", ->
$(this).val $(this).val().trim()
ツリー全体をまだ含めていない場合は、application.jsにファイルを含めます。
これにより、すべての入力がRailsによって保存される前に、先頭および末尾の空白が確実に削除されます。 document
にバインドされ、入力に委任されているため、後でページに追加された入力もすべて処理されます。
長所:
短所:
私はカールの答えに同様のアプローチを取るかもしれませんが、私はより少ない割り当てでより簡潔な構文を好む:
def strip_whitespace
self.name.try(:strip!)
self.email.try(:strip!)
self.nick.try(:strip!)
end
まだコメントできないので、ここで質問する必要があります。どのメソッドがArgumentErrorを提供していますか? strip
、またはresponds_to?
また、.strip
は、先頭と末尾の空白のみを削除します。 2つのスペースを持つ「ハリーポッター」を受け入れない場合は、正規表現を使用するか、より単純に.splitを呼び出してスペースを削除し、単一のスペースで文字列を再連結する必要があります。
ストリッピングが良いアイデアである限り、それが単に先頭/末尾の空白である場合、問題は表示されません。ただし、単語間に複数のスペースがある場合、余分なスペースを自動的に削除して、送信したものではないログインをユーザーに与える代わりに、ユーザーに通知します。
もう1つのgemオプションは attribute_normalizer です。
# By default it will strip leading and trailing whitespace
# and set to nil if blank.
normalize_attributes :author, :publisher
:strip先頭と末尾の空白を削除します。
normalize_attribute :author, :with => :strip
Ruby 2.3.0からは、Safe Navigation Operator(&。)を使用できます
before_validation :strip_whitespace
def strip_whitespace
self.name&.strip!
self.email&.strip!
self.nick&.strip!
end
GEMS:
https://github.com/rmm5t/strip_attributes/
https://github.com/holli/auto_strip_attributes/