Ruby-on-rails Rails before_validation 去除空白最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3268053/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:54:41  来源:igfitidea点击:

Rails before_validation strip whitespace best practices

ruby-on-railsvalidationmodel

提问by berkes

I would like my User model to sanitize some input before before save. For now some simple whitespace stripping will do. So to avoid people registering with "Harry " and pretend to be "Harry", for example.

我希望我的用户模型在保存之前清理一些输入。现在可以做一些简单的空白剥离。因此,为了避免人们注册“Harry”并假装“Harry”,例如。

I assume it is a good idea to do this stripping before validation, so that the validates_uniqueness_of can avoid accidental duplicates.

我认为在验证之前进行剥离是个好主意,这样 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

However, this code comes with an error ArgumentError: wrong number of arguments (0 for 1). I assumed the callback would be passed the values.

但是,此代码带有错误 ArgumentError: wrong number of arguments (0 for 1)。我假设回调将传递值。

Also: is this stripping actually a good idea? Or should I rather validate on space and tell the user that "Harry " contains invalid spacess (I want to allow "Harry Potter" but not "Harry\s\sPotter").

另外:这种剥离实际上是个好主意吗?或者我应该在空间上进行验证并告诉用户“Harry”包含无效空格(我想允许“Harry Potter”但不允许“Harry\s\sPotter”)。

Edit: As pointed out in a comment, my code is wrong (which is why I was asking the question a.o.). Please make sure you read the accepted answer in addition to my question for the correct code and to avoid the same mistakes I made.

编辑:正如评论中所指出的,我的代码是错误的(这就是我问这个问题的原因)。除了我的问题之外,请确保您阅读了已接受的答案以获得正确的代码,并避免我犯的同样错误。

采纳答案by Karl

I don't believe before_validationworks like that. You probably want to write your method like this instead:

我不相信这样的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

You could make it more dynamic if you want using something like self.columns, but that's the gist of it.

如果你想使用类似的东西self.columns,你可以让它更动态,但这就是它的要点。

回答by holli

There are several gems to do this automatically. Those gems work in the similar way of creating callback in before_validation. One good gem is at https://github.com/holli/auto_strip_attributes

有几个 gem 可以自动执行此操作。这些 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

Stripping is often a good idea. Especially for leading and trailing whitespaces. User often creates trailing spaces when copy/pasting value to a form. With names and other identifying strings you also might want squish the string. So that "Harry    Potter" will become "Harry Potter" (squish option in the gem).

剥离通常是个好主意。特别是对于前导和尾随空格。用户在将值复制/粘贴到表单时通常会创建尾随空格。对于名称和其他标识字符串,您可能还想压缩该字符串。这样“哈利波特”将成为“哈利波特”(宝石中的挤压选项)。

回答by Erik

Charlie's answer is good, but there's a little verbosity. Here's a tighter version:

查理的回答很好,但有点冗长。这是一个更紧凑的版本:

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

The reason we use

我们使用的原因

self.foo = "bar"

instead of

代替

foo = "bar"

in the context of ActiveRecord objects is that Ruby interprets the latter as a local variable assignment. It will just set the foo variable in your method scope, instead of calling the "foo=" method of your object.

在 ActiveRecord 对象的上下文中,Ruby 将后者解释为局部变量赋值。它只会在您的方法范围内设置 foo 变量,而不是调用对象的“foo=”方法。

But if you are calling a method, there is no ambiguity. The interpreter knows you're not referring to a local variable called foo because there is none. So for example with:

但是,如果您正在调用一个方法,则没有歧义。解释器知道你不是指一个名为 foo 的局部变量,因为没有。因此,例如:

self.foo = foo + 1

you need to use "self" for the assignment, but not to read the current value.

您需要使用“self”进行赋值,而不是读取当前值。

回答by emrass

I'd like to add one pitfall that you might experience with the "before_validations" solutions above. Take this example:

我想添加一个您可能会在使用上述“before_validations”解决方案时遇到的陷阱。拿这个例子:

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

This means you have an inconsistent behavior based on whether your object was saved or not. If you want to address this, I suggest another solution to your problem: overwriting the corresponding setter methods.

这意味着根据您的对象是否被保存,您的行为不一致。如果你想解决这个问题,我建议你的问题的另一种解决方案:覆盖相应的 setter 方法。

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

I also like this approach because it does not force you to enable stripping for all attributes that support it - unlike the attribute_names.eachmentioned earlier. Also, no callbacks required.

我也喜欢这种方法,因为它不会强制您为支持它的所有属性启用剥离 - 与attribute_names.each前面提到的不同。此外,不需要回调。

回答by Ajay

Instead we can write a better method more generic regardless whatever may be the type of attributes with the object(might have 3 string type fields, few booleans, few numeric)

相反,我们可以编写一个更通用的更好的方法,无论对象的属性类型是什么(可能有 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

Hope that will helps someone!

希望这会帮助某人!

回答by emptywalls

If you have access to ActiveSupport, use squish instead of strip.

如果您有权访问 ActiveSupport,请使用 squish 而不是 strip。

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

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

回答by CharlieMezak

I like Karl's answer, but is there a way to do it without referencing each of the attributes by name? That is, is there a way to just run through the model attributes and call strip on each one (if it responds to that method)?

我喜欢 Karl 的回答,但有没有办法不按名称引用每个属性?也就是说,有没有办法只运行模型属性并调用每个属性(如果它响应该方法)?

This would be desirable so I don't have to update the remove_whitespace method whenever I change the model.

这将是可取的,因此每当我更改模型时,我都不必更新 remove_whitespace 方法。

UPDATE

更新

I see that Karl implied that you might want to do this sort of thing. I didn't immediately know how it could be done, but here's something that works for me as described above. There' probably a better way to do it, but this works:

我看到 Karl 暗示你可能想做这种事情。我没有立即知道如何做到这一点,但如上所述,这里有一些对我有用的东西。可能有更好的方法来做到这一点,但这有效:

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

结尾

回答by Rameshwar Vyevhare

StripAttributes Gem

StripAttributes 宝石

I used strip_attributes. It's really awesome and easy to implement.

我使用了strip_attributes。这真的很棒而且很容易实现。

Default Behavior

默认行为

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

使用 except

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

Using only

使用 only

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

Using allow_empty

使用 allow_empty

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

Using collapse_spaces

使用 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

回答by Matt Connolly

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.

然后设置该值的应用程序的任何部分都将被剥离,包括assign_attributes等。

回答by brookr

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.

如果您还没有包含整个树,则将该文件包含在您的 application.js 中。

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.

这将确保每个输入在提交以供 Rails 保存之前都被移除前导和尾随空格。它绑定在 上document,并委托给输入,因此稍后添加到页面的任何输入也将被处理。

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?)
  • 以表格以外的任何其他方式(例如,通过 API)提交的数据将不会被修剪
  • 没有像 squish 这样的高级功能(但你可以自己添加)
  • 正如评论中提到的,如果 JS 被禁用则不起作用(但谁为此编码?)