Rails:使用ActiveModel进行自定义验证

时间:2015-06-23 18:59:54

标签: ruby-on-rails ruby validation activemodel chronic

我目前正在尝试使用日期输入进行自定义验证,但不幸的是,它似乎无效。

应用程序内部有两个页面,索引页面和搜索页面。在索引页面内有一个带有日期的文本字段。我正在使用Chronic gem将文本解析为日期。如果日期无效,Chronic将返回nil。如果有效,它会重定向到搜索页面并显示日期。

到目前为止我写的代码似乎没有正常工作,但我想要实现的是......

1)验证Chronic不会返回nil

2)验证日期是否大于今天的日期

请注意我没有使用这个数据库,我只想在不使用ActiveRecord的情况下验证输入日期。如果有人可以帮助我,我们将非常感谢您的帮助。

视图/主/ index.html.erb

<%= form_tag({controller: "main", action: "search"}, method: "get") do %>
    <%= label_tag(:q, "Enter Date:") %>
    <%= text_field_tag(:q) %>
    <%= submit_tag "Explore", name: nil %>
<% end %>

视图/主/ search.html.erb

<%= @show_date %>

main_controller.rb

def search

    passed_info = Main.new

    if passed_info.valid_date?
        @show_date = passed_info
    else
        flash[:error] = "Please enter correct date!"
        render :index => 'new'
    end

end

模型/ main.rb的

class Main

  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  attr_accessor :q

  validates_presence_of :q

  def initialize(params={})
    params.each do |attr, value|
    self.public_send("#{attr}=", value)
    end if params
  end

  def persisted?
    false
  end

  def valid_date?
    require 'chronic'
    if Chronic.parse(q).nil? || Chronic.parse(q) < Time.today
        errors.add(:q, "is missing or invalid")
    end
  end

end

编辑:

这是出了什么问题......

本地主机:3000

enter image description here

然后重定向到..

本地主机:3000 /主/搜索UTF8 =%E2%9C%93&安培; Q =无效+日期+测试

enter image description here

没有验证,没有日期,没有......

2 个答案:

答案 0 :(得分:2)

问题

对返回值要更加小心。当您尝试使用if valid_date?保护控制器时,您正在执行的操作是检查valid_date?是否返回false。如果解析失败,则返回值为errors.add的输出,而nil的输出又是Array#<<的输出。相关地,输出不是falseif,因此它的计算结果为true,因此valid_date?子句通过并继续前进。

潜在解决方案

您可能想让Rails Validation Framework为您做更多工作。不要将valid?视为控制器调用的公共方法,而是调用由ActiveModel::Validations添加的valid?方法。根据是否所有模型验证都通过,if model_instance.valid? 返回一个布尔值。因此,您和Rails Way一样,会在控制器中调用class YourClass include ActiveModel::Validations validate :date_is_valid validate :date_not_before_today private def date_is_valid if Chronic.parse(q).nil? errors.add(:q, "Date is invalid") end end def date_not_before_today if Chronic.parse(q) < Date.today errors.add(:q, "Date cannot be before today") end end end

这使您可以在模型中编写验证器方法,表示您尝试编写的逻辑。现在,您可以在单个方法中拥有日期的所有验证逻辑,只有一条错误消息。相反,您可以放置​​两个方法,这些方法会添加更多描述性的单个错误方法。

{{1}}

答案 1 :(得分:0)

正如ABMagil正确建议的那样,我想将完整的解决方案发布到我的答案中。事实上,这个答案可以真正适用于任何想要使用ActiveModel进行验证的人,无论是否有慢性宝石或日期。它可以作为一个有效的模板。

坦率地说,我的大部分错误来自于当时非常贫穷,理解我实际上想要实现的目标。大多数代码需要进行重大重构,请参阅下面我必须进行的更新。我试图尽可能地保存代码。

解决方案:

<强>视图/主/ index.html.erb

<%= form_for @search, url: { action: "search" }, 
                      html: { method: :get } do |f| %>

  # Displays error messages if there are any.
  <% if @search.errors.any? %>
    The form contains <%= pluralize(@search.errors.count, "error") %>.<br />
    <% @search.errors.each do |attr, msg| %>
      <%= msg %><br />
    <% end %>
  <% end %>

  <%= f.label :q, "Enter Date:" %>
  <%= f.text_field :q %>
  <%= f.submit "Explore", :class => 'submit' %>
<% end %>

views / main / search.html.erb - 与之前相同

<%= @show_date %>

<强> main_controller.rb

def index
  # Initializes a string from the form to a variable.
  @search = Search.new
end

def search
  # Retrieves the input from the form.
  @search = Search.new(params[:search])

  # Checks for validity, 
  # If valid, converts a valid string into a date.
  # Redirects to search.html.erb 
  # If not valid, renders a new index.html.erb template.
  if @search.valid?
    @show_date = (Chronic.parse(params[:search][:q])).to_date   
  else
    render :action => 'index'   
  end
end

<强>模型/ main.rb的

class Main
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  # Accepts the passed attribute from the form.
  attr_accessor :q

  # If form is submitted blank, then output the prompting message.
  validates_presence_of :q, :message => "This text field can't be blank!"
  # Two custom validations that check if passed string converts to a valid date.
  validate :date_is_valid
  validate :date_not_before_today

  # Initializes the attributes from the form.
  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  # Checks for persistence, i.e. if it's a new record and it wasn't destroyed. 
  # Otherwise returns false.
  def persisted?
    false
  end

  # ABMagil's code used for custom date validations
  private
    require 'chronic'
    def date_is_valid
      if Chronic.parse(q).nil?
        errors.add(:base, "Date is invalid")
      end
    end
    def date_not_before_today
      if !Chronic.parse(q).nil?
        if Chronic.parse(q) < Date.today
          errors.add(:base, "Date cannot be before today")
        end
      end
    end
end

结果:

Result