Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Agile Web Development With Rails, 2nd Edition (2006).pdf
Скачиваний:
30
Добавлен:
17.08.2013
Размер:
6.23 Mб
Скачать

EXTENSIONS TO NUMBERS 251

Our goose/geese pair are an irregular plural, so we could tell the inflector about them using

Inflector.inflections do |inflect| inflect.irregular "goose", "geese"

end

Now Rails gets it right.

depot> ruby script/console

Loading development environment.

>>

"goose".pluralize

#=>

"geese"

>>

"geese".singularize

#=>

"goose"

Perhaps surprisingly, defining an irregular plural actually defines plurals for all words that end with the given pattern.

>>

"canadagoose".pluralize

#=>

"canadageese"

>>

"wildgeese".singularize

#=>

"wildgoose"

For families of plurals, define pattern-based rules for forming singular and plural forms. For example, the plural of father-in-law is fathers-in-law, mother- in-law becomes mothers-in-law, and so on. You can tell Rails about this by defining the mappings using regular expressions. In this case, you have to tell it both how to make the plural from the singular form and vice versa.

Inflector.inflections do |inflect|

 

inflect.plural(/-in-law$/, "s-in-law")

 

inflect.singular(/s-in-law$/, "-in-law")

end

 

 

>>

"sister-in-law".pluralize

#=>

"sisters-in-law"

>>

"brothers-in-law".singularize

#=>

"brother-in-law"

Some words are uncountable (like bugs in my programs). You tell the inflector using the uncountable method.

Inflector.inflections do |inflect| inflect.uncountable("air", "information", "water")

end

 

 

>>

"water".pluralize

#=>

"water"

>>

"water".singularize

#=>

"water"

In a Rails application, these changes can go in the file environment.rb in the config directory.

15.4Extensions to Numbers

Integers gain the two instance methods even? and odd?. You can also get the ordinal form of an integer using ordinalize.

puts

3.ordinalize

#=>

"3rd"

puts

321.ordinalize

#=>

"321st"

Report erratum

TIME AND DATE EXTENSIONS 252

All numeric objects gain a set of scaling methods. Singular and plural forms are supported.

puts 20.bytes

#=> 20

puts 20.kilobytes

#=> 20480

puts 20.megabytes

#=> 20971520

puts 20.gigabytes

#=> 21474836480

puts 20.terabytes

#=> 21990232555520

puts 20.petabytes

#=> 22517998136852480

puts 1.exabyte

#=> 1152921504606846976

There are also time-based scaling methods. These convert their receiver into the equivalent number of seconds. The months and years methods are not accurate—months are assumed to be 30 days long, years 365 days long. However, the Time class has been extended with methods that give you accurate relative dates (see the description in the section that follows this one). Again, both singular and plural forms are supported.

puts 20.seconds

#=> 20

puts 20.minutes

#=> 1200

puts 20.hours

#=> 72000

puts 20.days

#=> 1728000

puts 20.weeks

#=> 12096000

puts 20.fortnights

#=> 24192000

puts 20.months

#=> 51840000

puts 20.years

#=> 630720000

You can also calculate times relative to some time (by default Time.now) using the methods ago and from_now (or their aliases until and since, respectively).

puts Time.now

#=> Thu May 18 23:29:14

CDT 2006

puts 20.minutes.ago

#=> Thu May 18 23:09:14

CDT 2006

puts 20.hours.from_now

#=> Fri May 19 19:29:14

CDT 2006

puts 20.weeks.from_now

#=> Thu Oct 05

23:29:14

CDT 2006

puts 20.months.ago

#=> Sat Sep 25

23:29:16

CDT 2004

puts 20.minutes.until("2006-12-25 12:00:00".to_time)

 

#=> Mon Dec 25

11:40:00 UTC 2006

puts 20.minutes.since("2006-12-25 12:00:00".to_time)

 

#=> Mon Dec 25

12:20:00

UTC 2006

How cool is that? And it gets even cooler....

15.5Time and Date Extensions

The Time class gains a number of useful methods, helping you calculate relative times and dates and format time strings. Many of these methods have aliases: see the API documentation for details.

now = Time.now

 

 

 

 

puts now

#=> Thu

May 18

23:36:10 CDT 2006

puts now.to_date

#=> 2006-05-18

 

puts

now.to_s

#=>

Thu

May 18

23:36:10 CDT 2006

puts

now.to_s(:short)

#=>

18 May 23:36

Report erratum

 

 

 

TIME AND DATE EXTENSIONS

253

puts now.to_s(:long)

#=> May 18,

2006 23:36

 

puts now.to_s(:db)

#=> 2006-05-18 23:36:10

 

puts now.to_s(:rfc822)

#=> Thu, 18

May 2006 23:36:10 -0500

 

puts now.ago(3600)

#=> Thu May

18

22:36:10 CDT 2006

 

puts now.at_beginning_of_day

#=> Thu May

18

00:00:00 CDT 2006

 

puts now.at_beginning_of_month

#=> Mon May

01

00:00:00 CDT 2006

 

puts now.at_beginning_of_week

#=> Mon May

15

00:00:00 CDT 2006

 

puts now.at_beginning_of_quarter

#=> Sat Apr

01

00:00:00 CST 2006

 

puts now.at_beginning_of_year

#=> Sun Jan

01

00:00:00 CST 2006

 

puts now.at_midnight

#=> Thu May

18

00:00:00 CDT 2006

 

puts now.change(:hour => 13)

#=> Thu May

18

13:00:00 CDT 2006

 

puts now.last_month

#=> Tue Apr

18

23:36:10 CDT 2006

 

puts now.last_year

#=> Wed May

18

23:36:10 CDT 2005

 

puts now.midnight

#=> Thu May

18

00:00:00 CDT 2006

 

puts now.monday

#=> Mon May

15

00:00:00 CDT 2006

 

puts now.months_ago(2)

#=> Sat Mar

18

23:36:10 CST 2006

 

puts now.months_since(2)

#=> Tue Jul

18

23:36:10 CDT 2006

 

puts now.next_week

#=> Mon May

22

00:00:00 CDT 2006

 

puts now.next_year

#=> Fri May

18

23:36:10 CDT 2007

 

puts now.seconds_since_midnight

#=> 84970.423472

 

puts now.since(7200)

#=> Fri May

19

01:36:10 CDT 2006

 

puts now.tomorrow

#=> Fri May

19

23:36:10 CDT 2006

 

puts now.years_ago(2)

#=> Tue May

18

23:36:10 CDT 2004

 

puts now.years_since(2)

#=> Sun May

18

23:36:10 CDT 2008

 

puts now.yesterday

#=> Wed May

17

23:36:10 CDT 2006

 

puts now.advance(:days => 30)

#=> Sat Jun

17

23:36:10 CDT 2006

 

puts Time.days_in_month(2)

#=> 28

 

 

 

puts Time.days_in_month(2, 2000) #=> 29

Date objects also pick up a few useful methods.

date = Date.today

 

puts date.to_s

#=> "2006-05-18"

puts date.to_time

#=> Thu May 18 00:00:00 CDT 2006

puts date.to_s(:short)

#=> "18 May"

puts date.to_s(:long)

#=> "May 18, 2006"

puts date.to_s(:db)

#=> "2006-05-18"

The last of these converts a date into a string that’s acceptable to the default database currently being used by your application. You may have noticed that the Time class has a similar extension for formatting datetime fields in a database-specific format.

You can add your own extensions to date and time formatting. For example, your application may need to display ordinal dates (the number of days into a year). The Ruby Date and Time libraries both support the strftime method for formatting dates, so you could use something like

Report erratum

AN EXTENSION TO RUBY SYMBOLS 254

>> d = Date.today

=> #<Date: 4907769/2,0,2299161> >> d.to_s

=> "2006-05-29"

>> d.strftime("%y-%j") => "06-149"

Instead, though, you might want to encapsulate this formatting by extending the to_s method of dates. In your environment.rb file, add a line like the following.

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!( :ordinal => "%Y-%j"

)

Now you can say

any_date.to_s(:ordinal)

#=> "2006-149"

You can extend the Time class string formatting as well.

ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(

:chatty => "It's %I:%M%p on %A, %B %d, %Y"

)

 

Time.now.to_s(:chatty)

#=> "It's 12:49PM on Monday, May 29, 2006"

There are also two useful time-related methods added to the String class. The methods to_time and to_date return Time and Date objects, respectively.

puts

"2006-12-25

12:34:56".to_time

#=>

Mon Dec 25 12:34:56 UTC 2006

puts

"2006-12-25

12:34:56".to_date

#=>

2006-12-25

Active Support also includes a TimeZone class. TimeZone objects encapsulate the names and offset of a time zone. The class contains a list of the world’s time zones. See the Active Support RDoc for details.

15.6An Extension to Ruby Symbols

(This section describes an advanced feature of Ruby and can be safely skipped on the first dozen or so readings....)

We often use iterators where all the block does argument. We did this in our earlier group_by and

is invoke a method on its index_by examples.

groups = posts.group_by {|post| post.author_id}

Rails has a shorthand notation for this. We could have written this code as

groups = posts.group_by(&:author_id)

Similarly, the code

us_states = State.find(:all)

state_lookup = us_states.index_by {|state| state.short_name}

could also be written

us_states = State.find(:all)

state_lookup = us_states.index_by(&:short_name)

Report erratum