- •Contents
- •Preface to the Second Edition
- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •The Architecture of Rails Applications
- •Models, Views, and Controllers
- •Active Record: Rails Model Support
- •Action Pack: The View and Controller
- •Installing Rails
- •Your Shopping List
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Linux
- •Development Environments
- •Rails and Databases
- •Rails and ISPs
- •Creating a New Application
- •Hello, Rails!
- •Linking Pages Together
- •What We Just Did
- •Building an Application
- •The Depot Application
- •Incremental Development
- •What Depot Does
- •Task A: Product Maintenance
- •Iteration A1: Get Something Running
- •Iteration A2: Add a Missing Column
- •Iteration A3: Validate!
- •Iteration A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B4: Linking to the Cart
- •Task C: Cart Creation
- •Sessions
- •Iteration C1: Creating a Cart
- •Iteration C2: A Smarter Cart
- •Iteration C3: Handling Errors
- •Iteration C4: Finishing the Cart
- •Task D: Add a Dash of AJAX
- •Iteration D1: Moving the Cart
- •Iteration D3: Highlighting Changes
- •Iteration D4: Hide an Empty Cart
- •Iteration D5: Degrading If Javascript Is Disabled
- •What We Just Did
- •Task E: Check Out!
- •Iteration E1: Capturing an Order
- •Task F: Administration
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Iteration F4: A Sidebar, More Administration
- •Task G: One Last Wafer-Thin Change
- •Generating the XML Feed
- •Finishing Up
- •Task T: Testing
- •Tests Baked Right In
- •Unit Testing of Models
- •Functional Testing of Controllers
- •Integration Testing of Applications
- •Performance Testing
- •Using Mock Objects
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Logging in Rails
- •Debugging Hints
- •Active Support
- •Generally Available Extensions
- •Enumerations and Arrays
- •String Extensions
- •Extensions to Numbers
- •Time and Date Extensions
- •An Extension to Ruby Symbols
- •with_options
- •Unicode Support
- •Migrations
- •Creating and Running Migrations
- •Anatomy of a Migration
- •Managing Tables
- •Data Migrations
- •Advanced Migrations
- •When Migrations Go Bad
- •Schema Manipulation Outside Migrations
- •Managing Migrations
- •Tables and Classes
- •Columns and Attributes
- •Primary Keys and IDs
- •Connecting to the Database
- •Aggregation and Structured Data
- •Miscellany
- •Creating Foreign Keys
- •Specifying Relationships in Models
- •belongs_to and has_xxx Declarations
- •Joining to Multiple Tables
- •Acts As
- •When Things Get Saved
- •Preloading Child Rows
- •Counters
- •Validation
- •Callbacks
- •Advanced Attributes
- •Transactions
- •Action Controller: Routing and URLs
- •The Basics
- •Routing Requests
- •Action Controller and Rails
- •Action Methods
- •Cookies and Sessions
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Using Helpers
- •How Forms Work
- •Forms That Wrap Model Objects
- •Custom Form Builders
- •Working with Nonmodel Fields
- •Uploading Files to Rails Applications
- •Layouts and Components
- •Caching, Part Two
- •Adding New Templating Systems
- •Prototype
- •Script.aculo.us
- •RJS Templates
- •Conclusion
- •Action Mailer
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Secure and Deploy Your Application
- •Securing Your Rails Application
- •SQL Injection
- •Creating Records Directly from Form Parameters
- •Avoid Session Fixation Attacks
- •File Uploads
- •Use SSL to Transmit Sensitive Information
- •Knowing That It Works
- •Deployment and Production
- •Starting Early
- •How a Production Server Works
- •Repeatable Deployments with Capistrano
- •Setting Up a Deployment Environment
- •Checking Up on a Deployed Application
- •Production Application Chores
- •Moving On to Launch and Beyond
- •Appendices
- •Introduction to Ruby
- •Classes
- •Source Code
- •Resources
- •Index
- •Symbols
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