web-dev-qa-db-ja.com

Rails before_validation strip whitespace best practice

保存する前に、ユーザーモデルの入力をサニタイズしたいと思います。今のところ、いくつかの単純な空白文字の除去が行われます。たとえば、「ハリー」に登録して「ハリー」のふりをする人を避けるためです。

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.に尋ねていた理由です)。正しいコードについての私の質問に加えて、受け入れられた答えを読んで、私が犯した同じ間違いを避けるようにしてください。

54
berkes

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のようなものを使用したい場合は、より動的にすることができますが、それが要点です。

55
Karl

これを自動的に行うにはいくつかの宝石があります。これらの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のスキッシュオプション)。

43
holli

チャーリーの答えは良いですが、少し冗長です。これはより厳密なバージョンです。

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

割り当てには「自己」を使用する必要がありますが、現在の値を読み取る必要はありません。

27
Erik

上記の「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とは異なり、それをサポートするすべての属性に対してストリッピングを有効にする必要がないため、このアプローチも気に入っています。また、コールバックは必要ありません。

18
emrass

私はカールの答えが好きですが、名前で各属性を参照せずにそれを行う方法はありますか?つまり、モデル属性をただ実行し、各属性でストリップを呼び出す方法はありますか(メソッドに応答する場合)?

これは望ましいので、モデルを変更するたびに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

終わり

9
CharlieMezak

代わりに、オブジェクトの属性のタイプに関係なく、より一般的なより良いメソッドを書くことができます(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

それが誰かを助けることを願っています!

9
Ajay

ActiveSupportにアクセスできる場合は、stripの代わりにsquishを使用してください。

http://api.rubyonrails.org/classes/String.html#method-i-squish

8
emptywalls

StripAttributes Gem

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
6

属性書き込みメソッドをオーバーライドすることも別の良い方法です。例えば:

class MyModel
  def email=(value)
    super(value.try(:strip))
  end
end

次に、assign_attributesなどを含む、値を設定するアプリケーションのすべての部分が削除されます。

4
Matt Connolly

ユーザーがフロントエンドフォームにデータを誤って入力することを主に懸念している場合の代替アプローチを次に示します。

# app/assets/javascripts/trim.inputs.js.coffee
$(document).on "change", "input", ->
  $(this).val $(this).val().trim()

ツリー全体をまだ含めていない場合は、application.jsにファイルを含めます。

これにより、すべての入力がRailsによって保存される前に、先頭および末尾の空白が確実に削除されます。 documentにバインドされ、入力に委任されているため、後でページに追加された入力もすべて処理されます。

長所:

  • 名前ごとに個々の属性をリストする必要はありません
  • メタプログラミングを必要としません
  • 外部ライブラリの依存関係を必要としません

短所:

  • フォーム以外の方法(APIなど)で送信されたデータはトリミングされません。
  • スキッシュのような高度な機能はありません(ただし、自分で追加できます)
  • コメントで述べたように、JSが無効になっている場合は機能しません(しかし、だれがそれをコーディングしていますか?)
4
brookr

私はカールの答えに同様のアプローチを取るかもしれませんが、私はより少ない割り当てでより簡潔な構文を好む:

def strip_whitespace
  self.name.try(:strip!)
  self.email.try(:strip!)
  self.nick.try(:strip!)
end
3
chad_

まだコメントできないので、ここで質問する必要があります。どのメソッドがArgumentErrorを提供していますか? strip、またはresponds_to?

また、.stripは、先頭と末尾の空白のみを削除します。 2つのスペースを持つ「ハリーポッター」を受け入れない場合は、正規表現を使用するか、より単純に.splitを呼び出してスペースを削除し、単一のスペースで文字列を再連結する必要があります。

ストリッピングが良いアイデアである限り、それが単に先頭/末尾の空白である場合、問題は表示されません。ただし、単語間に複数のスペースがある場合、余分なスペースを自動的に削除して、送信したものではないログインをユーザーに与える代わりに、ユーザーに通知します。

2
davidcelis

もう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
1
cweston

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/

0
artamonovdev