2016-03-04 6 views
4

У меня есть метод в моей модели пользователя, чтобы рассчитать возраст пользователя и вернуть человеческую удобочитаемую строку. Мой пользователь может быть от 1 месяца и старше, поэтому возвращаемая строка отличается в зависимости от того, является ли человек «2 месяца» или «1 год» или «2 года и 3 месяца».Возрастной метод Ruby с датой рождения, включая месяцы

Я рассмотрел несколько сообщений SO, чтобы прийти к этому решению. Есть что-то, чего я не вижу? Високосные годы? Спасибо!

def age 
    dob = self.date_of_birth 

    # if a date of birth is not nil 
    if dob != nil 

     # get current date 
     now = Date.current 

     # has person had their birthday yet this year 
     had_birthday = ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? true : false) 

     # if yes then subtract this year from birthday year, if not then also subtract 1 to get how many full years old they are 
     years = now.year - dob.year - (had_birthday ? 0 : 1) 

     # get the calendar month difference from birthdya calendar month and today's calendar month. if they have not had their birthdya yet then subtract the difference from 12 
     months = had_birthday ? now.month - dob.month : 12 - (now.month - dob.month) 

     # for under 1 year olds 
     if years == 0 
     return months > 1 ? months.to_s + " months old" : months.to_s + " month old" 

     # for 1 year olds 
     elsif years == 1 
     return months > 1 ? years.to_s + " year and " + months.to_s + " months old" : years.to_s + " year and " + months.to_s + " month old" 

     # for older than 1 
     else 
     return months > 1 ? years.to_s + " years and " + months.to_s + " months old" : years.to_s + " years and " + months.to_s + " month old" 
     end 

    # No date of birth saved so can not calculate age 
    else 
     return "No Date of Birth" 
    end 
    end 
+2

"Есть что-нибудь не хватает?", Это exaclty, где вы хотите использовать модульные тесты, даже если вы в противном случае они не используются. Вы можете использовать их для обеспечения того, чтобы правильная вещь произошла, когда кто-то родился на первом марше, а сегодня 29 февраля. Чтобы упростить тестирование, сделайте «now/today» также параметром. – Meier

ответ

5

Хотя это может быть лучше отправлено на сайт codereview, я все равно дам вам свои мысли.

Вы написали довольно длинный метод для того, что может быть несколько меньших.

Во-первых, я бы написал один метод, который занимает несколько месяцев в месяц и разделяет его на собственную функцию.

def readable_age(years, months) 
    # for under 1 year olds 
    if years == 0 
    return months > 1 ? months.to_s + " months old" : months.to_s + " month old" 

    # for 1 year olds 
    elsif years == 1 
    return months > 1 ? years.to_s + " year and " + months.to_s + " months old" : years.to_s + " year and " + months.to_s + " month old" 

    # for older than 1 
    else 
    return months > 1 ? years.to_s + " years and " + months.to_s + " months old" : years.to_s + " years and " + months.to_s + " month old" 
    end 
end 

Хотя, если вы не возражаете, добавив некоторые зависимости в ваш проект, вы можете воспользоваться actionview драгоценным камнем, вы можете воспользоваться функцией pluralize. Что-то вроде линий

def readable_age(years, months) 
    year_text = '' 
    if years == 0 
    year_text = "#{years} #{pluralize('year', years)} and " 
    end 

    "#{year_text}#{pluralize('month', months)} old" 
end 

Теперь для вашей функции рассчитано количество лет и месяцев.

def age(t) 
    dob = self.date_of_birth 

    months = (t.year * 12 + t.month) - (dob.year * 12 + dob.month) 

    # months/12 will give the number of years 
    # months % 12 will give the number of months 
    readable_age(months/12, 15 % 12) 
end 

EDIT

Причина Я передаю объект даты в функцию age, чтобы позволить вам вычислить возраст человека в течение определенного периода времени штампа. Это также облегчает проверку функции, если она дает тот же результат при тех же входах.

+0

Спасибо за все улучшения! – MicFin

4

Вы можете использовать time_ago_in_words:

Class.new.extend(ActionView::Helpers::DateHelper).time_ago_in_words(Time.parse("1981-11-20")) 
=> "over 34 years" 

Edit: она не имеет такую ​​же детализацию, как ваше решение, я знаю. Просто подумал, что это может быть хорошей ссылкой.

+0

Очень хорошо знать, havent подвергался воздействию time_ago_in_words – MicFin

1

Еще один вариант, основанный на классе Rational и некоторых расчетах дат, поддерживаемых Rails.

def age 
    return 'No Date of Birth' unless date_of_birth.present? 

    days_alive = Date.now - date_of_birth 
    years = (days_alive/365).to_i 
    months = ((days_alive % 365)/30).to_i 
    [pluralized(years, 'year'), pluralized(months, 'month')].compact.join(' ') 
end 

def pluralized(quantity, noun) 
    return nil if quantity.zero? 
    return noun.singularize if quantity == 1 
    noun.pluralize 
end 
+0

Спасибо! собираюсь познакомиться с классом Raionali – MicFin

+0

Хотя кажется, что это даст довольно близкий ответ, я считаю, что он не учитывает високосные годы. –

+0

С уровнем точности, установленным в месяцах, я считаю это разумной уступкой.Это будет означать, например, что ребенок, родившийся 1 марта 2015 года, будет «1 год» 29 февраля 2016 года, а человеку будет более ста лет, прежде чем возрастная функция сообщит о возрасте в поистине неточно. – AndyV