I'd just started toying around with Ruby on Rails and had come across an issue with linking to another action in a controller from a particular view. I am almost certain it's an issue (or lack of code) in my routes.rb file, but I think I'm misunderstanding exactly how this file works & what I have to do. I've got a solution but pretty sure it's not the "best way" to do it.
I have one controller called home with two actions, index (which is the default) and newbill. Inside index.html.erb I have:
<h1>Home View</h1>
<%= link_to "new", :controller => "home", :action => "newbill" %>
However I was getting a routing error:
No route matches {:controller=>"home", :action=>"newbill"}
Doing rake routes gives me the following:
root / {:controller=>"home", :action=>"index"}
I then (following some Googling) added this code to routes.rb
match 'home/newbill' => 'home#newbill', :as => :newbill
And then in my index.html.erb I've got this:
<%= link_to "Name", newbill_path %>
And now this works as expected. My questions however are:
Why does this work? What exactly is going on behind the scenes?
Surely this is not the best way to do it? Adding another match 'home/newbill'... for every controller / action I want to link to seems a rubbish way of doing things.
I really like Ruby, but struggling a bit with this aspect of Rails...routing in general is messing up my head a bit I think!
Any help is much appreciated :D
Thanks,
Jack
I guess the first time your code didn't work because your home controller is defined as a resource.
If you define a controller as a resource in routes.rb file it will support only 7 standard methods (according to REST architecture):
index
new
create
show
edit
update
destroy
If you need any more custom routes you should add them manually, say in your case 'newbill', may go as:
resources :home do
collection do
get :newbill
end
end
But as per my understanding, your newbill method should go to bills controllers new, method not in the home controller.
You are right, Rails routes are little bit confusing (at least for me), but once you understand you can do lots of cool stuff.
Read here for the Rails official routes documentation:
http://guides.rubyonrails.org/routing.html.
You should check out the Rails Routing guide. A read through will help you understand what is going on behind the scenes.
This works becuase rails filters every request through the router looking for a match. This enables you to define custom routes such as domain.com/post when the path is actually blog#post. Prior to rails 3, a catch-all route was the last route in the routes file. This allowed you to define a controller and action and it would just work. I'm on my iPad and not near any projects, so I can't verify it, but I think that route is still there in rails 3.1, it just needs to be umcommented.
Related
I am new to programming in general, someone referred me to railstutorial.org.
Specs: I am working on a cloud9 IDE, as suggested in the tutorial.
Information: I am on 1.3 of the rails tutorial, which is setting the root route.
The problem was initially my route did not effect the server launch (root page was still ruby default, not to 'application#hello'). Here are the files that the tutorial said to edit.
routes.rb
Rails.application.routes.draw do
root to: 'application#hello'
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def hello
render text: "Hello, world!"
end
end
There are a lot of comments that were defaulted into the files that I left out.
I have followed the instructions precisely. After I first had trouble, I thought I may have made an installation error, so I deleted my IDE and restarted, paying extreme attention to detail, especially versions.
I have tried $ rake routes, and my understanding it gives the message:
You don't have any routes defined!
That leads me to believe that the problem is the routes.rb file. I have tried changing the syntax to:
root to: 'application#hello'
I don't know a whole lot, such as how it would work using application, so I also tried:
root 'ApplicationController#hello'
and
root to: 'ApplicationController#hello'
These all result in the no routes defined message. I have no idea what is going on.
Thanks for any input or help!
You could try root 'application#hello' in your routes. Also, when starting out simple things like forgetting to save the file before trying things out on the browser can slip by; restarting the server takes care of a surprising number of foibles.
The rails documentation can also provide you a bit more information beyond the tutorial.
You probably want to move that action outside of the ApplicationController to another controller, but if you really insist, you can put this into your routes.rb:
get '/hello', to: 'application#hello', as: :hello
If you want the page to be the root, I would recommend creating a StaticPagesController and defining hello there, instead of putting it inside ApplicationController.
Here's what you can do:
Run rails g controller static_pages
Inside your StaticPagesController.rb, copy and paste your hello method that was inside ApplicationController.
Change the routes.rb to root 'static_pages#hello.
and you should have your desired result.
I've followed the same tutorial and I can say that the materials covered in the first two chapters are quite complicated at first if you are new to programming. It's only after you've done the entire tutorial that it will become clear to you how this routing thing (or any other details in these chapters for that matter) actually works.
However, since this idea of routing is very important, it's not a bad idea to understand how it works even if you are at an early stage in the tutorial.
The way you can create a route in rails is that you first specify a proper HTTP verb (GET, POST, PATCH, DELETE) with an appropriate path, the name of a controller, followed by a hash sign (#) and the name of an action defined in the controller.
Here controller is just a ruby class and an action a ruby method. (Since the basic principle of ruby on rails is "convention over configuration", it's important to get used to the terminology like controller, action, routing etc...)
When you say
get '/hello' => "application#hello"
(yes, you can use => in place of to:) as takeriho suggests, what happens is that a GET request to a URL of the type /hello(/ being "root path" as in www.example.com/(note the / at the end)) will get routed to the action, or method, named hello defined in a controller, or a class, named ApplicationController.
If you take a look at application_controller.rb, you can see that a method hello is defined within a class ApplicationController.
class ApplicationController < ActionController::Base
.....
def hello
render text: "Hello, world!"
end
end
Now if you want to specify a root route, which is your original question, you can just do root followed by the name of a controller, a hash sign (#), and the name of a class. So the code
root "application#hello"
means that a request to a url of the form /, or a root_path as it's called in rails convention, will get routed to the action (or method) named hello defined in a controller (or a class) named ApplicationController. You could accomplish the same result by doing
get '/' => "application#hello", as: :root
(you can name a route by adding as: :custom_name) but rails is smart enough to know that the two are equivalent. The task is made easier by following the rails conventions.
If you are completely new to Rails, I highly suggest you check out the Rails courses in Pragmatic Studio before going through the Ruby on Rails Tutorial which, as the author suggests, is not for a complete beginner. This approach worked perfectly for me. The rails courses offered by Pragmatic Studio assumes you have no prior knowledge about programming, and explains the basics in a manner much clearer than I did in this answer.
Happy coding :)
I am trying to learn ruby and learn how to handle a while request round trip.
On my index.html.erb page I added this line:
<%= link_to "Alex Link", test_path(#test) %>
but I got an error:
undefined method `test_path' for #<#<Class:0x4064e80>:0x3c0b5c8>
As I understand it, I need to add a record to routes.rb, and then a controller. Correct? How do I do that?
I read the explanation for this in the Rails Guides, but just finding it a bit confusing doing it the first time.
For your purposes (learning) resources tests if fine.
It also gives you other routes for free, see RESTful routes.
My app consists of two rails servers with mostly different concerns sitting behind a reverse proxy. Let's call them Server1 and Server2. Occasionally, Server1 needs to render a link to a url on Server2. Is there a good way to use Rails route helpers for this? Specifically in Rails 2? I came up with this:
ActionController::Routing::Routes.draw do |map|
# other routes omitted
map.with_options(:host => 'server2.example.com') do |add|
# create a named route for 'http://server2.example.com/thingies'
add.server2_thingies '/thingies', :controller => 'fake'
# create a named route for 'http://server2.example.com/thingies/combobulate'
add.enhance_server2_thingies '/thingies/combobulate', :controller => 'fake'
# create a named route for 'http://server2.example.com/mabobs/combobulate'
add.enhance_server2_mabobs '/mabobs/combobulate', :controller => 'fake'
# etc..
end
end
So then I can use server2_thingies_url and such in my views. This works, but it makes me feel like a bad person because there is no FakeController and I certainly have no intention of routing requests to it. I considered making helper methods and placing them in app/controllers/application_controller.rb, but a colleague made the argument that it is best to keep all route helpers in routes.rb, so things like rake routes will be accurate, for instance. Is there a better way?
I think I'd make a counter-argument to your colleague: if you're having to dirty up routes.rb with a "FakeController", then your rake routes is still not going to be accurate. In fact, I'd say that this is exactly the kind of thing that a helper was meant to help: it's taking logic that belongs purely in the view (link generation), and removing it from your view templates. Helpers are also easier to maintain and tweak.
Another advantage to the helper style is that if and when it's time to upgrade to Rails 3.x, the less hackish your routes.rb file is, the happier you will be - and that I can attest to from experience. :)
I'd just pass in :host to your foo_path or foo_url calls.
Seems cleaner than messing around with a FakeController
I'm new to Ruby on Rails and am doing my first tutorial and am running the latest version of rails 3 and ruby 1.9.2. After creating my controller and navigating to http://localhost:3000/say/hello I'm receiving a blank page. I do see the Welcome to Rails message when I just go to http://localhost:3000. I've done some Google searches and people have similar problems but there is no clear fix. I've never really worked with MVC before so the concept of routing is fairly new to me.
Below is my controller:
class SayController < ApplicationController
def hello
end
def goodbye
end
end
My view:
<h1>Say hello to Rails!</h1>
You should delete the public/index.html file as that will mess with your routing and display by default. Have you set up your routes already, and what is the exact location and filename of the template?
You will need something like in your config/routes.rb file to correctly route that url to your template/view:
match '/say/hello' => 'say#hello'
First delete the index.html file from your public folder. Then, go to the app/views and check the views for the say controller. You should have a hello.html.erb.
The answer to your particular question was answered already by Bitterzoet, but I thought you might want some alternative learning resources.
I'm not sure which tutorial you're starting with, but I find it odd that they're not using RESTful routes. You can find out what routes you have set up at the moment by going to the console and typing "rake routes". If you would like a different tutorial, I recommend the one here:
http://www.wiki.devchix.com/index.php?title=Rails_3_Curriculum
I'd also recommend http://railsforzombies.org/ as a good first-time rails experience.
A fun general line to add to config/routes is:
match ':controller(/:action(/:id(.:format)))'
While developing, this will allow you to display the controller/action in address bar for ALL controller/action/id.format etc.
Like Bitterzote wrote, if controller is "say" and action is "hello", http://localhost:3000/say/hello .
If you use controller "say" and action "move", http://localhost:3000/say/move .
I've found this route to be very useful during development, but change this if you launch your application! (Rails warns: "Note: This route will make all actions in every controller accessible via GET requests.")
I'm working with some legacy code with the last version of rails 1. Upgrading to a later version of rails isn't possible and as such map.namespace is a private method in this version, otherwise I'd be using it.
I have several resources which I have nested into a submenu for simplicity. Their controllers are all within the folder of that submenu and all inherit that controller's namespace.
I'm having major issues with the sub elements. I have mapped the resources and applied a prefix to them in the form of
map.resources :subitem, :path_prefix => "/sub_menu"
but I'm having major issues with using RESTFul methods on these nested objects. The paths that I'm trying to use, say to delete an object simply do not work.
I'm refactoring this current code base so there's very little I can do, and there's very little I can find on rails 1 routes online. It's a bit of a finicky issue but maybe I've missed something simple about routes.
Do I simply need to manually write out every particular route I can use and then manually prefix it, or is there a simpler way?
My other routes work fine, I simply cannot use the
subitem_path(subitem) :method => :delete
function in my link to
So I figured out how to layout the routes appropriately so I could access the paths
Add to the above route the specified controller you're using and the routes will map accordingly
map.resources :subitem, :path_prefix => "/sub_menu, :controller => "submenu/subitem"
Thanks!