program story

Rails before_validation 스트립 공백 모범 사례

inputbox 2021. 1. 9. 09:51
반응형

Rails before_validation 스트립 공백 모범 사례


저장하기 전에 사용자 모델이 일부 입력을 삭제하고 싶습니다. 지금은 간단한 공백 제거가 가능합니다. 예를 들어 "Harry"에 등록하는 사람을 피하고 "Harry"인 척하는 것입니다.

유효성 검사 전에이 스트리핑을 수행하여 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 Potter"는 허용하지만 "Harry \ s \ sPotter"는 허용하지 않음).

편집 : 코멘트에서 지적했듯이 내 코드가 잘못되었습니다 (이것이 내가 질문하는 이유입니다). 올바른 코드와 동일한 실수를 피하기 위해 내 질문과 함께 수락 된 답변을 읽으십시오.


나는 그렇게 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에서 콜백을 생성하는 것과 유사한 방식으로 작동합니다. 좋은 보석 중 하나는 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"(보석의 스 퀴시 옵션)가되도록합니다.


Charlie의 대답은 좋지만 약간의 장황함이 있습니다. 더 타이트한 버전은 다음과 같습니다.

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 ="메소드를 호출하는 대신 메소드 범위에서 foo 변수를 설정합니다.

그러나 메서드를 호출하는 경우에는 모호성이 없습니다. 인터프리터는 foo라는 지역 변수가 없기 때문에 참조하지 않는다는 것을 알고 있습니다. 예를 들면 다음과 같습니다.

self.foo = foo + 1

할당에는 "self"를 사용해야하지만 현재 값을 읽을 필요는 없습니다.


위의 "before_validations"솔루션에서 경험할 수있는 한 가지 함정을 추가하고 싶습니다. 이 예를 보자 :

u = User.new(name: " lala")
u.name # => " lala"
u.save
u.name # => "lala"

이것은 객체가 저장되었는지 여부에 따라 일관되지 않은 동작이 있음을 의미합니다. 이 문제를 해결하려면 문제에 대한 또 다른 해결책을 제안합니다. 해당하는 setter 메서드를 덮어 씁니다.

class User < ActiveRecord::Base
  def name=(name)
    write_attribute(:name, name.try(:strip))
  end
end

또한 attribute_names.each앞서 언급 한 것과 달리이를 지원하는 모든 속성에 대해 스트리핑을 사용하도록 강제하지 않기 때문에이 방법을 좋아합니다 . 또한 콜백이 필요하지 않습니다.


나는 Karl의 대답을 좋아하지만 각 속성을 이름으로 참조하지 않고 수행하는 방법이 있습니까? 즉, 모델 속성을 실행하고 각 속성에 대해 스트립을 호출하는 방법이 있습니까 (해당 메소드에 응답하는 경우)?

이것은 바람직 할 것이므로 모델을 변경할 때마다 remove_whitespace 메서드를 업데이트 할 필요가 없습니다.

최신 정보

나는 칼이 당신이 이런 종류의 일을하고 싶을 수도 있다는 것을 암시 한 것을 봅니다. 나는 그것이 어떻게 될 수 있는지 즉시 알지 못했지만 여기에 위에서 설명한대로 작동하는 것이 있습니다. 더 나은 방법이있을 수 있지만 다음과 같이 작동합니다.

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에 액세스 할 수있는 경우 스트립 대신 스 퀴시를 사용하십시오.

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


StripAttributes 보석

strip_attributes를 사용 했습니다 . 정말 멋지고 구현하기 쉽습니다.

기본 동작

class DrunkPokerPlayer < ActiveRecord::Base
  strip_attributes
end

By default, this will only strip the leading and trailing whitespaces and will act on all attributes of the model. This is ideal because it's not destructive and doesn't require you to specify which attributes need to be striped.

Using except

# all attributes will be stripped except :boxers
class SoberPokerPlayer < ActiveRecord::Base
  strip_attributes :except => :boxers
end

Using only

# only :shoe, :sock, and :glove attributes will be stripped
class ConservativePokerPlayer < ActiveRecord::Base
  strip_attributes :only => [:shoe, :sock, :glove]
end

Using allow_empty

# Empty attributes will not be converted to nil
class BrokePokerPlayer < ActiveRecord::Base
  strip_attributes :allow_empty => true
end

Using 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

Here's an alternative approach, if you are mostly concerned with users mis-entering data in your front-end forms...

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

Then include the file in your application.js if you aren't already including the whole tree.

This will ensure that every input gets leading & trailing whitespace removed before it is submitted to be saved by Rails. It's bound on document, and delegated to inputs, so any inputs added to the page later will be processed as well.

Pros:

  • Does not require listing individual attributes by name
  • Does not require any metaprogramming
  • Does not require external library dependencies

Cons:

  • Data submitted any other way than the forms (eg, via API) will not be trimmed
  • Does not have advanced features like squish (but you could add that yourself)
  • As mentioned in comments, does not work if JS is disabled (but who codes for that?)

Overriding the attribute write methods is another good way. For example:

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

Then any part of the application that sets the value will have it stripped, including assign_attributes and so on.


While I might take a similar approach to Karl's answer, I prefer a more terse syntax with fewer assignments:

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

Since I can't comment yet, I'll have to ask here: which method is giving the ArgumentError? strip, or responds_to?

Also, .strip removes only leading and trailing whitespace. If you want "Harry Potter" with two spaces to not be accepted, you would either have to use a regex or, more simply, you could call .split, which removes spaces, and re-concatenate the string with a single space.

As far as if stripping is a good idea, I don't see a problem when it is just the leading/trailing whitespace. If there are multiple spaces in between words though, I would notify the user instead of automatically removing the extra spaces and giving the user a login that is not what they submitted.


Another gem option is attribute_normalizer:

# By default it will strip leading and trailing whitespace
# and set to nil if blank.
normalize_attributes :author, :publisher

:strip Will strip leading and trailing whitespace.

normalize_attribute  :author, :with => :strip

Starting with Ruby 2.3.0 you can use the 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/

ReferenceURL : https://stackoverflow.com/questions/3268053/rails-before-validation-strip-whitespace-best-practices

반응형