2

我正在尝试编写一个 Rails 应用程序,但目前出生日期的显示与正常日期格式一样,但我更愿意在视图中显示年龄

我在控制器中的方法如下,我的数据库中有一个 DOb 列作为出生日期L

def age
  @user = user
  now = Time.now.utc.to_date
  now.year - @user.dob.year - (@user.dob.to_date.change(:year => now.year) > now ? 1 : 0)
end

它显示为 DOB: 23/5/2011,但我希望它以年为单位。

我如何还放置一个验证器来检查年龄是否低于 18 岁?

4

6 回答 6

7

对于验证器,您可以使用自定义方法

validate :over_18

def over_18
  if dob + 18.years >= Date.today
    errors.add(:dob, "can't be under 18")
  end
end
于 2012-05-05T16:00:31.337 回答
3

我遇到了这个问题,并认为我会发布一个更现代的答案,它利用 Rails 便捷方法语法和自定义验证器。

自定义年龄验证器

此验证器将为您要验证的字段名称和最低年龄要求提供一个选项哈希。

# Include somewhere such as the top of user.rb to make sure it gets loaded by Rails.
class AgeValidator < ActiveModel::Validator

  def initialize(options)
    super
    @field = options[:field] || :birthday
    @min_age = options[:min_age] || 18
    @allow_nil = options[:allow_nil] || false
  end

  def validate(record)
    date = record.send(@field)
    return if date.nil? || @allow_nil

    unless date <= @min_age.years.ago.to_date
      record.errors[@field] << "must be over #{@min_age} years ago."
    end
  end

end

示例用法

class User < ActiveRecord::Base
  validates_with AgeValidator, { min_age: 18, field: :dob }
end

User#age方便法

为了计算用户的显示年龄,您需要小心计算闰年。

class User < ActiveRecord::Base
  def age

    return nil unless dob.present?
    # We use Time.current because each user might be viewing from a
    # different location hours before or after their birthday.
    today = Time.current.to_date

    # If we haven't gotten to their birthday yet this year.
    # We use this method of calculation to catch leapyear issues as
    # Ruby's Date class is aware of valid dates.
    if today.month < dob.month || (today.month == dob.month && dob.day > today.day)
      today.year - dob.year - 1
    else
      today.year - dob.year
    end

  end
end

还有规格!

require 'rails_helper'
describe User do
  describe :age do
    let(:user) { subject.new(dob: 30.years.ago) }
    it "has the proper age" do
      expect(user.age).to eql(30)

      user.birthday += 1.day
      expect(user.age).to eql(29)
    end
  end
end
于 2015-08-05T20:51:31.477 回答
2

计算年龄时应该小心。这是一个正确的方法:

def age(as_at = Time.now)
  as_at = as_at.utc.to_date if as_at.respond_to?(:utc)
  as_at.year - dob.year - ((as_at.month > dob.month || (as_at.month == dob.month && as_at.day >= dob.day)) ? 0 : 1)
end

之后,根据@Baldrick:

validate :check_over_18

def check_over_18
  errors.add(:dob, "can't be under 18") if age < 18
end
于 2012-05-05T16:17:10.687 回答
1

您在这里有几个不同的问题。

  1. 年龄计算属于哪里?

    年龄计算应该是辅助方法或模型方法。我一直将其作为模型方法,但最近看到了在装饰器甚至辅助方法中使用这些显示元素的优势。在您的情况下,首先将其放入模型中,然后从那里开始:

    def age
        now = Time.now.utc.to_date
        now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
    end
    
  2. 您如何验证此人已超过 18 岁?

    如果某人未满 18 岁,您是否真的限制他们被保存到数据库中?或者您是否限制了观看能力?

    def is_over_18?
        age >= 18
    end
    

这编写了一个自定义的每个验证器或使用一个 Proc,但我真的质疑以这种方式验证的决定。

于 2012-05-05T16:05:16.060 回答
1

要查找年龄,您可以使用 gem adroit-age

age = AdroitAge.find_age("23/01/1990")
=> 23
于 2013-09-07T05:21:37.397 回答
0

我也不得不处理这个问题,但是几个月。变得太复杂了。我能想到的最简单的方法是:

def month_number(today = Date.today)
  n = 0
  while (dob >> n+1) <= today
    n += 1
  end
  n
end

你可以用 12 个月做同样的事情:

def age(today = Date.today)
  n = 0
  while (dob >> n+12) <= today
    n += 1
  end
  n
end

这将使用 Date 类来增加月份,这将处理 28 天和闰年等。

于 2012-11-02T13:57:06.897 回答