在form_for中了解Rails日期

时间:2015-07-30 16:39:33

标签: ruby-on-rails ruby

我正在尝试从用户那里获取日期,并将其作为纯文本发送到电子邮件中,格式如下:“2015年7月30日”。

为了做到这一点,如果我得到的输出是一个字符串,我可以这样做:

Date.parse("2015-07-30").strftime("%m/%d/%Y")

问题是,我得到了一个FixNum。

问题很多:

  • 如果我尝试转换为字符串以使用Date.parse对其进行解析,则会变为“2001”。
  • 如果我应用我刚写的代码,Date.parse ...它会抛出'无效日期'。

例如:

(2016-02-13).to_s #=> "2001"

(2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum

Date.parse("2001").strftime("%m/%d/%Y") #=> invalid date

因此,如果我可以将2015-07-30转换为"2015-07-30",那么它会起作用:

Date.parse("2015-07-30").strftime("%m/%d/%Y") #=> "07/30/2015"

然后我尝试使用date_select代替date_field,但现在消息到达时这些字段为空。

有什么建议吗?

这是我的表格:

= form_for @contact do |f|
    = f.text_field :product_name
    = f.date_field :purchase_date
    = f.submit

这是我的代码:

<%= message.subject %>

<% @resource.mail_form_attributes.each do |attribute, value|
    if attribute == "mail_subject" 
        next
    end
%>

<%= "#{@resource.class.human_attribute_name(attribute)}: #{Date.parse(value).class == Date ? Date.parse(value).strftime("%m/%d/%Y") : value}" %>

<% end %>

我的控制器:

class ContactsController < ApplicationController
    before_action :send_email, except: [:create]

    def create
       @contact = Contact.new(params[:contact])
       @contact.request = request
       if @contact.deliver
           @thank   = "Thank you for your message!"
           @message = "We have received your inquiry and we'll be in touch shortly."
       else
           @error   = "Cannot send message. Please, try again."
       end
    end

    def contact_page
    end

    def product_complaint
        @the_subject = "Product Complaint Form"
    end


    private
        def send_email
            @contact = Contact.new
        end
end

我的模特:

class Contact < MailForm::Base

   # all forms
   attribute :mail_subject
   attribute :first_name,             validate: true
   attribute :last_name,              validate: true

   # product-complaint
   attribute :best_by,                validate: true, allow_blank: true # date
   attribute :bag_code,               validate: true, allow_blank: true
   attribute :purchase_date,          validate: true, allow_blank: true # date
   attribute :bag_opened,             validate: true, allow_blank: true # date
   attribute :problem_noticed,        validate: true, allow_blank: true # date

   # all forms
   attribute :message,                validate: true
   attribute :nickname,               captcha:  true

   def headers
       {
           content_type: "text/plain",
           subject: %(#{mail_subject}),
           to:      "xxxxx@xxxxxxx.com",
           # from:    %("#{first_name.capitalize} #{last_name.capitalize}" <#{email.downcase}>)
           from: "xxx@xxxxx.com"

       }
   end
 end

2 个答案:

答案 0 :(得分:3)

(2016-02-13).to_date #=> NoMethodError: undefined method `to_date' for 2001:Fixnum

您收到此错误,因为您没有围绕该值的引号。即它不是一个字符串,它是一个应用减法的数字。这被解释为

2016 - 2
2014 - 13
2001.to_date

需要('2016-02-13').to_date

如果您无法将其作为字符串获取,您是否可以发布从用户开始获取它的方式? (日期字段应该向您的控制器发送一个字符串,而不是一系列数字)

答案 1 :(得分:2)

您不了解从表单接收值的内容:您不能接收整数,fixnum或除字符串之外的任何其他内容。所以,你不能收到2016-02-13。相反,您可以获得"2016-02-13""2016""02""2""13",具体取决于表单。如果你在Rails下运行,那么它就得到了字符串,并且通过它的元数据理解你想要一个整数(它实际上应该被定义为一个字符串),然后它将它转换为一个整数。

无论哪种方式,当你写:

(2016-02-13).to_s
(2016-02-13).to_date

你正在将这种误解传播到你的测试中。这就是 MUST 的写法,因为你需要使用字符串:

require 'active_support/core_ext/string/conversions'

("2016-02-13").to_s # => "2016-02-13"
("2016-02-13").to_date # => #<Date: 2016-02-13 ((2457432j,0s,0n),+0s,2299161j)>

您可以创建日期而不使用字符串:Ruby的Date初始化程序允许我们传递年,月和日值并接收新的Date对象:

year, month, day = 2001, 1, 2
date = Date.new(year, month, day) # => #<Date: 2001-01-02 ((2451912j,0s,0n),+0s,2299161j)>
date.year # => 2001
date.month # => 1
date.day # => 2

继续......

在Ruby中解析日期很快就证明它不是以美国为中心的语言。美国人认为01/01/2001的所有日期都是“MM / DD / YYYY”,但这是一个不好的假设,因为世界其他地方的大部分都使用“DD / MM / YYYY”。不知道这意味着在该假设下编写的代码做错了。考虑一下:

require 'date'

date = Date.parse('01/02/2001') 
date.month # => 2
date.day # => 1

显然有些“错误”正在发生,至少对于'mericans来说。这一点非常明显:

date = Date.parse('01/31/2001') 
# ~> -:3:in `parse': invalid date (ArgumentError)

这是因为没有月份“31”。在上一个'01/02/2001'的例子中,这种误解意味着程序员认为它应该是“1月2日”,但是代码认为它是“2月1日”,并且使用它。这可能会对企业系统造成严重破坏,或者涉及财务计算,产品调度,运输或其他任何与日期有关的事情。

因为代码假定为这种字符串的DD / MM / YYYY格式,所以要做的事情是:

  • 了解您的用户将发送日期的格式。不要假设,永远。问他们并让你的代码能够处理交替,或告诉他们他们必须使用什么,并在实际将其提交到你的系统之前审查他们的数据。或者,提供一个GUI,强制他们从弹出窗口中选择日期,并且永远不允许他们手动输入日期。
  • 强制日期解析器使用明确的日期格式,以便始终做正确的事情:

    Date.strptime('01/31/2001', '%m/%d/%Y') # => #<Date: 2001-01-31 ((2451941j,0s,0n),+0s,2299161j)>
    Date.strptime('31/01/2001', '%d/%m/%Y') # => #<Date: 2001-01-31 ((2451941j,0s,0n),+0s,2299161j)>
    

最后一点是编写代码的关键:我们告诉语言该做什么,而不是让自己和我们的雇主接受猜测的代码。给代码一半机会它会做错误的事情,所以你控制它。这就是为什么编程很难的原因。