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

Chapter 15

Active Support

Active Support is a set of libraries that are shared by all Rails components. Much of what’s in there is intended for Rails’ internal use. However, Active Support also extends some of Ruby’s built-in classes in interesting and useful ways. In this section we’ll quickly list the most popular of these extensions.

We’ll also end with a brief look at how Ruby and Rails can handle Unicode strings, making it possible to create web sites that correctly handle international text.

15.1Generally Available Extensions

As we’ll see when we look at AJAX on page 521, it’s sometimes useful to be able to convert Ruby objects into a neutral form to allow them to be sent to a remote program (often JavaScript running in the user’s browser). Rails extends Ruby objects with two methods, to_json and to_yaml. These convert objects into JavaScript Object Notation (JSON) and YAML (the same notation used in Rails configuration and fixture files).

#For demo purposes, create a Ruby structure with two attributes Rating = Struct.new(:name, :ratings)

rating = Rating.new("Rails" , [ 10, 10, 9.5, 10 ])

#and serialize an object of that structure two ways...

puts rating.to_json

#=> ["Rails", [10, 10, 9.5, 10]]

puts rating.to_yaml

#=> --- !ruby/struct:Rating

 

name: Rails

 

ratings:

 

- 10

 

- 10

 

- 9.5

 

- 10

In addition, all Active Record objects, and all hashes, support a to_xml method. We saw this in Section 12.1, Autogenerating the XML, on page 180.

ENUMERATIONS AND ARRAYS 248

David Says. . .

Why Extending Base Classes Doesn’t Lead to the Apocalypse

The awe that seeing 5.months + 30.minutes for the first time generates is usually replaced by a state of panic shortly thereafter. If everyone can just change how integers work, won’t that lead to an utterly unmaintainable spaghetti land of hell? Yes, if everyone did that all the time, it would. But they don’t, so it doesn’t.

Don’t think of Active Support as a collection of random extensions to the Ruby language that invites everyone and their brother to add their own pet feature to the string class. Think of it as a dialect of Ruby spoken universally by all Rails programmers. Because Active Support is a required part of Rails, you can always rely on the fact that 5.months will work in any Rails application. That negates the problem of having a thousand personal dialects of Ruby.

Active Support gives us the best of both worlds when it comes to language extensions. It’s contextual standardization.

To make it easier to tell whether something has no content, Rails extends all Ruby objects with the blank? method. It always returns true for nil and false, and it always returns false for numbers and for true. For all other objects, it returns true if that object is empty. (A string containing just spaces is considered to be empty.)

puts [ ].blank?

#=> true

puts { 1 => 2}.blank?

#=> false

puts "

cat ".blank?

#=> false

puts "".blank?

#=> true

puts "

".blank?

#=> true

puts nil.blank?

#=> true

15.2Enumerations and Arrays

Because our web applications spend a lot of time working with collections, Rails adds some magic to Ruby’s Enumerable mixin.

The group_by method partitions a collection into sets of values. It does this by calling a block once for each element in the collection and using the result returned by the block as the partitioning key. The result is a hash where each of the keys is associated with an array of elements from the original collection that share a common partitioning key. For example, the following splits a group of posts by author.

Report erratum

STRING EXTENSIONS 249

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

The variable groups will reference a hash where the keys are the author ids and the values are arrays of posts written by the corresponding author.

You could also write this as

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

The groupings will be the same in both cases, but in the second case entire Author objects will be used as the hash keys (which means that the author objects will be retrieved from the database for each post). Which form is correct depends on your application.

Rails also extends Enumerable with two other methods. The index_by method takes a collection and converts it into a hash where the values are the values from the original collection. The key referencing each value is determined by passing that element to the block.

us_states = State.find(:all)

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

The sum method sums a collection by passing each element to a block and accumulating the total of the values returned by that block. It assumes the initial value of the accumulator is the number 0; you can override this by passing a parameter to sum.

total_orders = Order.find(:all).sum {|order| order.value }

Rails also extends arrays with a couple of convenience methods.

puts [ "ant", "bat", "cat"].to_sentence #=> "ant, bat, and cat"

puts [ "ant", "bat", "cat"].to_sentence(:connector => "and not forgetting") #=> "ant, bat, and not forgetting cat"

puts [ "ant", "bat", "cat"].to_sentence(:skip_last_comma => true) #=> "ant, bat and cat"

[1,2,3,4,5,6,7].in_groups_of(3) {|slice| puts slice.inspect} #=> [1, 2, 3]

[4, 5, 6]

[7, nil, nil] [1,2,3,4,5,6,7].in_groups_of(3, "X") {|slice| puts slice.inspect}

#=> [1, 2, 3] [4, 5, 6]

[7, "X", "X"]

15.3String Extensions

Newcomers to Ruby are often surprised that indexing into a string using something like string[2] returns an integer, not a one-character string. Rails adds some helper methods to strings that give some more natural behavior.

Report erratum

STRING EXTENSIONS 250

string = "Now is the time"

 

puts string.at(2)

#=> "w"

 

puts string.from(8)

#=> "he time"

puts string.to(8)

#=> "Now is th"

puts string.first

#=> "N"

 

puts string.first(3)

#=> "Now"

puts string.last

#=> "e"

 

puts string.last(4)

#=> "time"

puts string.starts_with?("No" )

#=> true

puts string.ends_with?("ME" )

#=> false

count = Hash.new(0)

string.each_char {|ch| count[ch] += 1}

puts count.inspect

#=> {" "=>3, "w"=>1, "m"=>1, "N"=>1, "o"=>1,

 

"e"=>2, "h"=>1, "s"=>1, "t"=>2, "i"=>2}

Active Support adds methods to all strings to support the way Rails itself converts names from singular to plural, lowercase to mixed case, and so on. A few of these might be useful in the average application.

puts "cat".pluralize

#=> cats

puts "cats".pluralize

#=> cats

puts "erratum".pluralize

#=> errata

puts "cats".singularize

#=> cat

puts "errata".singularize

#=> erratum

puts "first_name".humanize

#=> "First name"

puts "now is the time".titleize

#=> "Now Is The Time"

Writing Your Rules for Inflections

Rails comes with a fairly decent set of rules for forming plurals for English words, but it doesn’t (yet) know every single irregular form. For example, if you’re writing a farming application and have a table for geese, Rails might not find it automatically.

depot> ruby script/console

Loading development environment. >> "goose".pluralize

=> "gooses"

Seems to me that gooses is a verb, not a plural noun.

As with everything in Rails, if you don’t like the defaults, you can change them. Changing the automatic inflections is easy. At the bottom of the file environment.rb in the config directory you’ll find a commented-out section that configures the Inflector module. This lets us define new rules for forming the plural and singular forms of words. We can tell it

The plural of a word or class of words given the singular form

The singular form of a word or class of words given the plural form

Which words have irregular plurals

Which words have no plurals

Report erratum