- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •Models, Views, and Controllers
- •Installing Rails
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Unix/Linux
- •Rails and Databases
- •Keeping Up-to-Date
- •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 A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B2: Add Page Decorations
- •Task C: Cart Creation
- •Sessions
- •More Tables, More Models
- •Iteration C1: Creating a Cart
- •Iteration C3: Finishing the Cart
- •Task D: Checkout!
- •Iteration D2: Show Cart Contents on Checkout
- •Task E: Shipping
- •Iteration E1: Basic Shipping
- •Task F: Administrivia
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Finishing Up
- •More Icing on the Cake
- •Task T: Testing
- •Tests Baked Right In
- •Testing Models
- •Testing Controllers
- •Using Mock Objects
- •Test-Driven Development
- •Running Tests with Rake
- •Performance Testing
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Active Support
- •Logging in Rails
- •Debugging Hints
- •Active Record Basics
- •Tables and Classes
- •Primary Keys and IDs
- •Connecting to the Database
- •Relationships between Tables
- •Transactions
- •More Active Record
- •Acts As
- •Aggregation
- •Single Table Inheritance
- •Validation
- •Callbacks
- •Advanced Attributes
- •Miscellany
- •Action Controller and Rails
- •Context and Dependencies
- •The Basics
- •Routing Requests
- •Action Methods
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Builder templates
- •RHTML Templates
- •Helpers
- •Formatting Helpers
- •Linking to Other Pages and Resources
- •Pagination
- •Form Helpers
- •Layouts and Components
- •Adding New Templating Systems
- •Introducing AJAX
- •The Rails Way
- •Advanced Techniques
- •Action Mailer
- •Sending E-mail
- •Receiving E-mail
- •Testing E-mail
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Securing Your Rails Application
- •SQL Injection
- •Cross-Site Scripting (CSS/XSS)
- •Avoid Session Fixation Attacks
- •Creating Records Directly from Form Parameters
- •Knowing That It Works
- •Deployment and Scaling
- •Picking a Production Platform
- •A Trinity of Environments
- •Iterating in the Wild
- •Maintenance
- •Finding and Dealing with Bottlenecks
- •Case Studies: Rails Running Daily
- •Appendices
- •Introduction to Ruby
- •Ruby Names
- •Regular Expressions
- •Source Code
- •Cross-Reference of Code Samples
- •Resources
- •Index
DISPATCHING MODES 417
:float
A floating-point number.
:time
A timestamp value, containing both date and time. Coerced into the Ruby Time type.
:datetime
A timestamp value, containing both date and time. Coerced into the Ruby DateTime type.
:date
A date value, containing just the date. Coerced into the Ruby Date type.
Structured Parameter Types
In addition to the base types, AWS lets us use the Class objects of ActionWebService::Struct or ActiveRecord::Base in signatures.
Using these lets external developers work with the native structured types for their platform when accessing our web services.
So what gets put into the structured type seen by remote callers? For
ActionWebService::Struct, all the members defined with member( ).
class Person < ActionWebService::Struct member :id, :int
member :name, :string end
An ActiveRecord::Base derivative exposes the columns defined in its corresponding database table.
20.3 Dispatching Modes
Remote callers send their invocation requests to endpoint URLs. (See Sec- |
endpoint URLs |
tion 20.6, External Client Applications (XML-RPC), on page 424, for the for- |
|
mats of endpoint URLs.) Dispatching is the process by which AWS maps |
|
these incoming requests to methods in objects that implement the ser- |
|
vices. |
|
The default dispatching mode is direct dispatching and requires no addi- |
direct dispatching |
tional configuration to set up. This is the mode we used for the example |
|
on page 412. |
|
Prepared exclusively for Rida Al Barazi
Report erratum
DISPATCHING MODES 418
NewPost(post)
Remote Caller
Controller
new_post(post)
Figure 20.2: Overview of Direct Dispatching
Direct Dispatching
With direct dispatching, the API definition is attached directly to the controller, and the API method implementations are placed in the controller as public instance methods.
The advantage of this approach is its simplicity. The drawback is that only one API definition can be attached to the controller; therefore, we can have only one API implementation for a unique endpoint URL. It also blurs the separation of model and controller code. It is shown in Figure 20.2 .
Layered Dispatching
Layered dispatching allows us to implement multiple APIs with one controller, with one unique endpoint URL for all the APIs. This works well for overlapping XML-RPC–based APIs (such as the various blogging APIs), which have desktop client applications supporting only one endpoint URL. This is shown in Figure 20.3, on the following page.
|
Delegated Dispatching |
|
Delegated dispatching is identical to layered dispatching except that it |
|
uses a unique endpoint URL per contained API. Instead of embedding API |
|
identifiers in the method invocation messages, remote callers send the |
|
messages for a specific API to its associated endpoint URI. |
|
We use the web_service_dispatching_mode( ) method in a controller to select |
|
that controller’s dispatching mode. |
File 220 |
class RpcController < ActionController::Base |
|
web_service_dispatching_mode :layered |
|
end |
|
The valid modes are :direct, :layered, and :delegated. |
Prepared exclusively for Rida Al Barazi
Report erratum
DISPATCHING MODES 419
Remote Caller
FindProductById(id)
ProductService
find_product_by_id(id)
Controller
OrderService
is_order_shipped(id)
IsOrderShipped(id)
Remote Caller
Figure 20.3: Overview of Layered Dispatching
Layered Dispatching from a Remote Caller’s Perspective
Method invocation requests from remote callers differentiate between the APIs by sending an identifier indicating which API the method call should go to.
In the case of XML-RPC, remote callers use the standard XML-RPC serviceName.methodName convention, serviceName being the identifier. For example, an XML-RPC method with a name in the XML-RPC message of blogger.newPost would be sent to a newPost( ) method in whichever object is declared to implement the blogger service.
In the case of SOAP, this information is encoded in the SOAPAction HTTP header as declared by the generated WSDL. This has the implication that remote callers behind a proxy stripping off this HTTP header will not be able to call web services that use layered dispatching.
Prepared exclusively for Rida Al Barazi
Report erratum
USING ALTERNATE DISPATCHING |
420 |
|
20.4 Using Alternate Dispatching |
|
As we’ve already used direct dispatching in our first example web service, |
|
let’s implement the same web service in one of the other modes. |
|
Layered Dispatching Mode |
|
Since layered dispatching implements multiple APIs with one controller, it |
|
needs to create mappings for incoming method calls to the objects imple- |
|
menting them. We do this mapping using the web_service( ) declaration in |
|
the controller. |
File 134 |
class LayeredBackendController < ApplicationController |
|
web_service_dispatching_mode :layered |
|
web_service_scaffold :invoke |
|
web_service :product, ProductService.new |
|
web_service(:order) { OrderService.new } |
|
end |
|
You’ll notice that we no longer attach the API definition to the controller, as |
|
it no longer contains the API methods. Also notice the two different ways |
|
we called web_service( ). |
|
The first call to web_service( ) passed it a ProductService instance directly. |
|
This is sufficient if our web service doesn’t need to have anything to do |
|
with the controller. As the instance is created at class definition time, |
|
though, it has no access to the instance variables of the controller, so it |
|
effectively operates in isolation from it. |
|
The second call to web_service( ) passes a block parameter. This has the |
|
effect of deferring OrderService instantiation to request time. The block |
|
we give it will be evaluated in controller instance context, so it will have |
|
access to all the instance variables and methods of the controller. This |
|
can be useful if we need to use helper methods such as url_for( ) in our web |
|
service methods. |
|
Here’s the rest of our code. First, here’s the implementation of our product |
|
searching service. |
File 130 |
class ProductService < ActionWebService::Base |
|
web_service_api ProductApi |
|
def find_all_products |
|
Product.find(:all).map{ |product| product.id } |
|
end |
|
def find_product_by_id(id) |
|
Product.find(id) |
|
end |
|
end |
Prepared exclusively for Rida Al Barazi
Report erratum