3. REST, Resources, and Rails - 3.12 The RESTful Rails Action Set

3.12 The RESTful Rails Action Set

3.12.1 Index

Typically, an index action provides a representation of a plural (or collection) resource. However, to be clear,not all resource collections are mapped to the index action. Your default index representations will usually be generic, although admittedly that has a lot to do with your application-specific needs. But in general, the index action shows the world the most neutral representation possible.

class AuctionsController < ApplicationController
  def index
    @auctions = Auction.all
  end
end

The associated view template will display information about each auction, with links to specific information about each one, and to profiles of the sellers.

You’ll certainly encounter situations where you want to display a representation of a collection in a restricted way. In our recurring example, users should be able to see a listing of all their bids, but maybe you don’t want users seeing other people’s bids.

There are a couple of ways to do this. One way is to test for the presence of a logged-in user and decide what to show based on that. But that’s not going to work here. For one thing, the logged-in user might want to see the more public view. For another, the more dependence on server-side state we can eliminate or consolidate, the better.
So let’s try looking at the two bid lists, not as public and private versions of the same resource, but as different index resources. The difference can be reflected in the routing like:

resources :auctions do 
  resources :bids do
    get :manage, :on => :collection 
  end
end

resources:bids

We can now organize the bids controller in such a way that access is nicely layered, using action callbacks
only where necessary and eliminating conditional branching in the actions themselves:

class BidsController < ApplicationController 
  before_action :check_authorization, only: :manage
  def index
    @bids = Bid.all
  end

  def manage
    @bids = auction.bids
  end

  protected

  def auction
    @auction ||= Auction.find(params[:auction_id])
  end

  def check_authorization 
    auction.authorized?(current_user)
  end 
end

There’s now a clear distinction between /bids and /auctions/1/bids/manage and the role that they play in your application.

On the named route side, we’ve now got bids_url and manage_auction_bids_url. We’ve thus preserved the public, stateless face of the /bids resource, and quarantined as much stateful behavior as possible into a discrete member resource, /auctions/1/bids/manage. Don’t fret if this mentality doesn’t come to you naturally. It’s part of the REST learning curve.

3.12.2 Show

The RESTful show action is the singular flavor of a resource. That generally translates to a representation ofinformation about one object, one member of a collection.

class AuctionController < ApplicationController
  def show
    @auction = Auction.find(params[:id])
  end
end

3.12.3 Destroy

Destroy actions are good candidates for administrative safeguarding, though of course it depends on whatyou’re destroying. You might want something like this to protect the destroy action.

class ProductsController < ApplicationController
  before_action :admin_required, only: :destroy
def destroy
  product.destroy
  redirect_to products_url, notice: "Product deleted!"
end

This approach might be reflected in a simple administrative interface like

%h1 Products 
- products.each do |product|
  %p= link_to product.name, product 
  - if current_user.admin?
    %p= link_to "delete", product, method: :delete

The Rails UJS (Unobtrusive JavaScript) API greatly simplifies the HTML emitted for a destroy action, using CSS selectors to bind JavaScript to (in this case) the “delete” link.

DELETE submissions are dangerous. Rails wants to make them as hard as possible to trigger accidentally—for instance, by a crawler or bot sending requests to your site. So when you specify the DELETE method, JavaScript that submits a form is bound to your “delete” link, along with a rel="nofollow" attribute on the link. Since bots don’t submit forms (and shouldn’t follow links marked “nofollow”), this gives a layer of protection to your code.

3.12.4 New and Create

As you’ve already seen, the new and create actions go together in RESTful Rails. A “new resource” is really just an entity waiting to be created. Accordingly, the new action customarily presents a form, and create creates a new record, based on the form input.

Let’s say you want a user to be able to create (that is, start) an auction. You’re going to need

  1. A new action, which will display a form
  2. A create action, which will create a new Auction object based on the form input, and proceed to a
    view (show action) of that auction.
protected

def auction
  @auction ||= current_user.auctions.build(params[:auction])
end
helper_method:auction

Once the information is filled out by a user, it’s time for the main event: the create action. Unlike new, this action has something to do.

def create
  if auction.save
    redirect_to auction_url(auction), notice: "Auction created!" 
  else
    render :new 
  end
end

3.12.5 Edit and Update

Like new and create, the edit and update actions go together: edit provides a form, and update processes the form input.

The form for editing a record appears similar to the form for creating one. (In fact, you can put much of it in a partial template and use it for both; that’s left as an exercise for the reader.)

The form_for method is smart enough to check whether the object you pass to it has been persisted or not. If it has, then it recognizes that you are doing an edit and specifies a PATCH method on the form.

你可能感兴趣的:(3. REST, Resources, and Rails - 3.12 The RESTful Rails Action Set)