Rails: Routes, Controllers, Views, Oh My(exclamation)

戏子无情 提交于 2019-12-09 22:45:05

问题


I'm failing to understand the correlation between routes, controllers, and views (and how they connect to each other).

So, I've got my controller with the index,show,new,create,destroy methods. And the corresponding

GET    /entries(.:format)                     entries#index
POST   /entries(.:format)                     entries#create
GET    /entries/new(.:format)                 entries#new
GET    /entries/:id/edit(.:format)            entries#edit
GET    /entries/:id(.:format)                 entries#show
PUT    /entries/:id(.:format)                 entries#update
DELETE /entries/:id(.:format)                 entries#destroy

How come if I add a new method vote_up or vote_down, for example, and the views with matching action names, it doesn't work.

1) What is the proper way to add new actions and connect them to views? 2) Bonus What is the proper way to make these methods ajax-compatible (render a partial with ajax)? What happens if the user doesn't have js enabled?

I may expand/evolve this question based on the answers I get.

I'm tired of googling things like custom action route rails and the like to hodge-podge my apps. It's draining and poor form and I'm finally getting to the level to comprehend the lingo--I've been self taught 100%...so please try to be understanding if you can for a young padawan.


回答1:


Here's how to think of it, from the beginning:

1) The one and only thing your app does is respond to HTTP REQUESTS.

The most typical kinds of requests are:

  • GET - the user types something into the URL bar of their browser and hits enter.

  • POST - the user submits a form.

There are also other kinds of HTTP requests, most importantly PUT, PATCH and DELETE. Rails follows the REST pattern, which means it assigns specific meanings to these HTTP verbs.

2) When any request comes into your app, it has to be routed to a Controller Action.

Your routes.rb file is a set of instructions for the Rails Router (ActionDispatch) that tells the router where to send requests. The "standard" rails resource is given as a shortcut, like this:

resources :things

This means the following:

GET /things => things#index

GET /things/:id => things#show

GET /things/new => things#new

GET /things/edit/:id => things#edit

POST /things => things#create

PUT /things/:id => things#update

DELETE /things/:id => things#destroy

These are considered the standard RESTful actions - nothing else is set by your resources :things declaration. So, if you want the controller to perform other non-standard actions, you have to add them manually.

If you want to perform an action on a specific record, the best way is to use:

resources :things do
  member do
    get 'vote_up'
  end
end

This tells the router that if someone makes a GET request to /things/123/vote_up that it should trigger the ThingsController vote_up action.

All of this is spelled out in great detail in the Rails Guide, you should read the whole thing.

3) Your controller's job is to send a response to the request.

Normally this means something like loading a record from the database and rendering the view for that record.

Each controller action ends by sending the response back to the incoming request. This response can be either a render call - which means send back some data in some format - or a redirect call - which basically makes a new request for you and therefore you get the response of that other request.

In Rails a redirect is effectively sending the request to a different controller action.

A Render call sends data as a response to the request.

When you call render :new, this is a shortcut to render :template => :new, which loads the app/views/things/new.html.erb (or whatever) template, sends it the data from the controller (normally your instance variables) and evaluates this using the template language (erb, haml, etc.) This results in a big string of HTML, which the controller then delivers to the browser.

Want to see what this for yourself? Try ending a controller with render :text => 'Hello World', or even:

render :inline => '<!DOCTYPE html><head><title>Inline Wow!</title></head><body>Mind blown.</body></html>'

See what happens.

When responding (rendering) you can send "normal" HTML templates, with a whole page worth of information in it (head, body, etc.), or a partial that is used by Ajax. You can also send raw data such as JSON or XML. It's all actually just text, and depending on the content of that text (and the HTTP headers that come with it) the browser, script, or client application handles it accordingly.

Again, see the Rails Guide.

4) When the request is made by a browser you probably want to send back HTML. If the request is made by Ajax you probably want to send back JSON.

In the case of a custom action like vote_up you might not want to show a template at all, but just redirect. So, you might have something like this:

ThingsController < ApplicationController

  def vote_up
    @thing = Thing.find(params[:id])
    @thing.vote_up
    redirect_to @thing
  end

end

Now, one of the benefits of the router is it will give you URL helpers. If you've created the route and action as shown before, on your "show thing" page you could have a URL like this:

link_to 'Vote up this thing!', vote_up_thing_path(@thing)

That would create a link to things/123/vote_up, and if someone clicked on it it would run the code in the vote_up action on the ThingsController, and then redirect back to the show thing view.

5) Your templates send messages to the controllers using links and forms. Links make GET requests, forms make POST requests.

If you want to start having AJAX requests, that's fine. In that case, you just need to make the request in Javascript, and handle the response. So, for instance, you could put something like this in your template:

= link_to 'Vote up this thing', vote_up_thing_path(@thing), :id => 'vote-up-button'

Then in Javascript (with jQuery) you could have a function like this:

$(function(){
  $('a#vote-up-button').click( function(event){
    event.preventDefault();
    $.ajax({
      url: this.attr('href'),
      type: 'GET',
      success: function(){...},
      error: function(){...}
    });
  });
});

In this case the jQuery Ajax method is just making a get request, and then running a callback function based on the response it got.

6) The structure of your controller/routes does not affect what kind of requests you can make, only what action will respond to what HTTP method on what URL.

What you do INSIDE your controller action determines whether you are ready to respond to javascript or html requests etc.

While rails is certainly able to handle multiple request formats in a single controller action, using the respond_to block, as a matter of practicality I find things work much more smoothly when you choose to have routes only respond to one format or another.

IE: I would make your normal page load requests (index, show, new, edit) just HTML requests, and then I would make any additional AJAX actions you want to add be Javascript only -- ie. they respond with JSON instead of HTML. You don't have to do this, of course, but your life will be easier if you do.

I hope this gives you a clearer sense of what is happening in your app. Welcome to Rails, you're joining a great community!




回答2:


Welcome to the ROR world. :)

Routes, controllers, and views work together to translate a HTTP request into a response of some kind (be it HTML, JSON, or XML). Each attacks a different piece of the problem.

Starting from the end, views are the templates in the rails world and they typically are ERB. ERB is just one templating system, others can be used as well, like haml. Their job is to take some data given to them by the controller and produce formatted output, again typically HTML, JSON, or XML.

But how do you know which view to render for a particular request? How do you get data into your view so that it can do all the fancy dynamic stuff you need? This is where controllers come in. Controllers are ruby classes with the job of examining the parsed HTTP request and any related parameters, fetching data from the database (or wherever), and then passing that data to a view. A controller will typically have several different methods, each corresponding to a different task (e.g. create, show, update, etc).

Lastly, Routes are a DSL for parsing HTTP requests and dispatching a parsed HTTP request to a particular controller method. They are the glue that Rails uses to route URLs to methods, hence the name, routes.

Regarding your specific questions:

1) To create a new action, you have to add a route, a controller method, and a view. So for instance if you wanted to get a count of your entries, you could add a route like:

get '/entries/count' => "entries#count"

This tells ROR to call the count method in the entries controller when that URL is received. Your controller method in this case would be something simple like:

def count
   @cnt = Entries.count
end

Then last, you'd make a view in app/views/entries/count.html.erb that had something like:

<p>Count: <%= @cnt %></p>

2) Ajax compatible code really just a way of asking "what if the requested format for this request is JSON?". For this, you'll want to use respond_to method. In a way, respond_to is a formal way of specifying a different view to handle the formatting of the request. To continue with the example above, you could do:

def count
  @cnt = Entries.count
  respond_to do |fmt|
    fmt.html # This just renders the default template
    fmt.json { render json: { count: @cnt } }
  end
end

N.b. - All code above freehanded. :)




回答3:


If you have the RESTful controller:

resources :entries do
  collection do
    get :vote_down
    get :vote_up
  end
end

this will generate 2 additional routes:

GET    /entries/:id/vote_up(.:format)                 entries#vote_up
GET    /entries/:id/vote_down(.:format)               entries#vote_down

You can easily change HTTP method to GET, POST, PUT and DELETE.

Also, check "Adding More RESTful Actions" in the routes documentation.



来源:https://stackoverflow.com/questions/16115644/rails-routes-controllers-views-oh-myexclamation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!