Nested views "the rails way" - ruby-on-rails

I am working on a rails 3.2 application that has a lot of static, deeply nested pages. The architecture is as follows:
So far, I have an information controller with these four methods:
about_the_liver
therapies
nutrition
liver_diseases
with the following routes:
get "information/therapies"
get "information/nutrition"
get "information/liver_diseases"
get "information/about_the_liver"
Here are my questions:
First of all, these routes, as generated by the rails generator, look clumsy. Is there any way to dry them out and group them together? E.g., so that the "information/" part is not repeated so often?
I have come up with this, but I am not sure whether this is the best approach:
[ :therapies, :nutrition, :liver_diseases, :about_the_liver ].each do |method|
get "information/#{method}"
end
I will need views for every single menu entry that you can see in the rightmost menu. My approach is to change liver_diseases to being a separate controller and have 8 different views - but that can't be dry. Is there a "rails way" to this?

Couldn't you simply put your static pages in /public and have them served directly from there? If there's no rails content this would be the quickest and most efficient approach.

Related

What is the "_path" method in ruby on rails?

I'm learning RoR, and I'm getting very confused by the "_path" method as it's used in Controllers and Routes. To be more specific, I'm referring to the many different calls that take the syntax "(something)_path". So far as I know, they all seem to either encode or manipulate a URL or link. I'm having a hard time mastering the use of this type of method because I can't figure out what it's core functionality is supposed to be.
For example, I could use the following code to redirect an old URL structure to a page of listed Tweet instances in my config/routes.rb file:
get '/all' => 'tweets#index', as: 'all_tweets'
Only now can I use the following in an .erb file. Notice the "_path" code at the end of the line.
<%= link_to "All Tweets", all_tweets_path %>
I could also use the following code to create a link to an edit page (and another action) in a different .erb file:
<p><%= link_to tweet.user.name, edit_tweet_path(#tweet) %></p>
I've tried reading through my study materials as well as the RoR documentation, but I always end up more lost than when I began. Does anybody know the low-level definition of this "_path" method?
Helper
It's called a route helper, which means that Rails will generate them to help provide you with resource-based routing structures. I'll explain more in a second
--
To explain properly - Rails is just a framework.
Like all software, it is a series of files loaded in a particular order. As such, Rails creates a series of helper methods in the booting process. These "helper" methods can then be used throughout your application to call functionality / information as you require:
The Rails framework provides a large number of helpers for working
with assets, dates, forms, numbers and model objects, to name a few.
These helpers are available to all templates by default.
In addition to using the standard template helpers provided, creating
custom helpers to extract complicated logic or reusable functionality
is strongly encouraged. By default, each controller will include all
helpers. These helpers are only accessible on the controller through
.helpers
The route helpers (which are generated from your config/routes.rb file give you the ability to call routes which are resourceful. These might seem strange to begin with, but once you understand them, will help you inexorably.
--
Resourceful
To give you more clarity - Rails routes are known as resourceful
This means they are constructed around resources. To give you a brief definition of this, you need to appreciate that the resources of your application are the pools of data you can add to, and pull
from.
To explain further, because Rails is object orientated. If you're new, this won't mean very much, but keep it in mind, as when you progress through the language / work, you'll begin to see why this is important.
Object orientated programming puts OBJECTS at the center of the flow. Typically, you'd put logic at the center, but with OOP, it's the objects. This is very important for us, as it means that everything you do in Rails is based around the objects you can create.
As per the MVC principle (which, again, is what Rails is built on), you'll create / invoke your objects from your Models:
This means that if you want to create a series of routes to "CRUD" (Create Read Update Destroy) your objects, Rails is able to create the routes necessary to do that. This is where the resources directives come from inside the routes file:
Hope this helps!
Actually, these paths are generated based on your routes.rb. If you run this command at your project, you would be able to see all available on your app
rake routes
For example, if I declare my resources in routes.rb like this
resources :posts
then I would automatically have following available paths
posts_path
post_path
new_post_path
edit_post_path
If you use some strange abc_path which has not been declared in routes.rb, then you will get errors.
Hope this is helpful, you will definitely need to work more with Rails and then eventually you will understand all of these things :)
you could find definition for these methods in rails repository:
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/route_set.rb#L127

Creating links using link_to for hierarchical model

Okay, so I am new to Rails and am creating a project management system with the framework. For a couple of my models, views, and controllers I used scaffolding and had no problems. For other parts, I coded all of the parts myself.
So as an overview of my project, at the root of it all you can have many projects. Within all of these projects you can create multiple to-do lists. Within each to-do list, you can have multiple tasks. This is what I meant by "hierarchical" in the title.
I just created my lists page, and when I go to the URL directly in my browser (ex: http://localhost:3000/projects/3/lists/20/tasks/1) the task is displayed correctly. However, I do not know how to format my link that is in one of my to-do list views (the tasks are usually shown below the to-do list but now I want them to show on their own view).
Here is the code I currently have:
<%= link_to "#{task.description}", project_list_tasks_url(#list.id,task.id) %>
I know that the link_to "#{task.description}" is correct because I tried using it with a static URL (Google or something), but project_list_tasks_url(#list.id,task.id) is where I'm having trouble.
Can anybody help me out? I can provide as much code as you'd like from my to-do list or task controllers and views.
A couple of tips to help make routing less confusing. It can be a bit unnerving to get used to.
Routing Rule #1
Always check the output of rake routes to be sure how to call your various routing methods. You might think you know how your routes will play out by looking at routes.rb but you won't know until you look at the compiled routes table.
In your case you're expecting a route with the format:
/projects/:project_id/lists/:list_id/tasks/:id
Be sure that's the case. If it is, your call should look like:
project_list_task_path(#project, #list, task)
Note that the arguments here are :project_id, :list_id and :id, so all three are required in this case. Any in brackets in the path specification can be ignored, like :format usually is.
Routing Rule #2
Use the _path methods unless you strictly require the full URL. They're shorter and the output is easier to read and debug. They also don't inadvertently flip the URL in the browser and cause session problems if you don't properly differentiate between www.mysite.com and site.com.
Routing Rule #3
Don't forget there's a huge difference between #project and #project.id when it's supplied to a routing path method.
The router will always call the to_param method if it's available and this can be over-ridden in your model to produce pretty or friendly URLs. id is for your database and your database alone. to_param is for routing but you shouldn't be calling it manually unless you're doing something exceptionally irregular.
You generally should not nest resources more than one level deep, but putting that aside, the link_to format should be:
link_to task.description, project_list_task_path(#project, #list, task)
i.e. project_link_tasks_url should be project_link_task_url, and you have to pass the #project as the first argument (I'm assuming that your project is named #project). I've switched _url to _path so you can just pass the objects themselves as arguments rather than their ids.
See the documentation on creating paths and URLs from objects for details.

Beginner with Rails 3.1 and "static" pages

I just started deploying a Rails website. In fact, I started programming on Rails 2 days ago.
I've created a new project, added some gems, etc. Everything is working properly, and I have some basic knowledge on how all works.
The thing is that what I want to create is a simple website with some sections (let's say, News, Contact, About, Products...). All this content is kinda static.
But I came in a problem. I don't really know what to do in order to create them. What I want, for example, is something like mypage.com/products/fashionableproduct, mypage.com/about, etc, or even mypage.com/page/products.
I thought about creating a Controller, then an action for each page... afterwards, I came up with other solution: scaffolding. Creating a resource called page, that has a title, etc...
I'm really a beginner on this topic, and I would like to hear your helpful voice.
Thanks!
Check out https://github.com/thoughtbot/high_voltage for static pages for Rails.
And check out http://railscasts.com/episodes/30-pretty-page-title for setting page titles.
The paths to your files are determined by your routes. The configuration file for routes is located at config/routes.rb. You can match a URL path, and then point to a given resource. More information about routes here: http://guides.rubyonrails.org/routing.html
If you generate a controller, you can process any dynamic data and then pass this data to these "kinda static" pages. Here is an example configuration that would match the path "mypage.com/about" and display the appropriate page:
# config/routes.rb
match "/about" => "example_controller#about"
# app/controllers/example_controller.rb
class ExampleController < ApplicationController
def about
# calculations
end
end
# app/views/example/about.html.erb
<!-- This is your HTML page -->
I think the title of your post might be a bit misleading. I have the feeling you don't want static pages but some database stored content. Just like Ben Simpson tells you to do, create a normal pages controller and make it work.
In the end you might want to customize some routes to get them to be exactly the way you want as in your examples.
Since you just started the app, I strongly recommend you start over and make a new app with Rails 3.1 which is the most current version and learn how to do the basics through http://guides.rubyonrails.org/ and a few other sources such as http://railscasts.com.
You will then learn Rails the right way from the beginning. Good luck and have fun in the process.

Scaffold default files are the best practice?

Hey, i have some experience with MVC. but I'm new to rails. I'm using the scaffold command to generate some default files. The model looks clean and nice, but the controller and the views aren't really dry. The contents of new.html.erb and edit.html.erb are almost the same and the methods new/edit and create/update are doing almost the same thing. In other frameworks i've used only one view for updating and creating new entries and also the same method in my controller by setting the id as an optional parameter. Do they use this structure to keep things RESTful (i have not much of a clue about rest :()? Is it the best practice to use this default stuff for crud?
The scaffold generator is a pretty good place to start. As you pointed out, there are some things which are not that great about it. I think most people take what the scaffold generates and then fix it up to their liking. For example, you can extract the form part from new.html.erb and edit.html.erb and place it in a partial _form.html.erb. Then update new.html.erb and edit.html.erb to include that partial to render the form. I think that for Rails 3, the scaffold generator has been changed to do this by default.
It does seem like new and edit, and create and update are pretty much the same, but you need to remember that they are mapped to different HTTP methods and URLs, which ties in to the whole RESTful resource idea. Check out the RailsGuides for routing, the section CRUD, Verbs, and Actions has a nice table of the seven different routes and the differences between them.
You should check out ryanb's nifty-generators:
http://github.com/ryanb/nifty-generators
The scaffolding creates a partial view called _form that then gets referenced from the new and edit views. It also comes with a bunch of other nice options -- like generating your views in haml or your tests in Shoulda or RSpec.
If they wanted you to keep it they wouldn't call it "scaffolding." It's just there to make everything work out of the box. If you put it into production you will more than likely be laughed at.

Why do I need to work harder to make my Rails application fit into a RESTful architecture?

I started a Rails project recently and decided to use RESTful controllers. I created controllers for my key entities (such as Country) and added index, new, edit, create, show, update and delete. I added my map.resources :country to my routes file and life was good.
After development progressed a little, I started to encounter problems. I sometimes needed extra actions in my controller. First there was the search action that returned the options for my fancy autocompleting search box. Then came the need to display the countries in two different ways in different places in the application (the data displayed was different too, so it wasn't just two views) - I added the index_full action. Then I wanted to show a country by name in the URL, not by id so I added the show_by_name action.
What do you do when you need actions beyond the standard index, new, edit, create, show, update, delete in a RESTful controller in Rails? Do I need to add (and maintain) manual routes in the routes.rb file (which is a pain), do they go in a different controller, do I become unRESTful or am I missing something fundamental?
I guess I am asking, do I need to work harder and add actions into my routes.rb file for the privilege of being RESTful? If I wasn't using map.resources to add the REST goodies, the standard :controller/:action, :controller/:action/:id routes would handle pretty much everything automatically.
I would treat search as a special case of index. Both actions return a collection of resources. The request parameters should specify things like page, limit, sort order, and search query.
For example:
/resources/index # normal index
/resources/index?query=foo # search for 'foo'
And in resources_controller:
before_filter :do_some_preprocessing_on_parameters
def index
#resources = Resource.find_by_param(#preprocessed_params)
end
As for index_full and search_by_name, you might look at splitting your current controller into two. There's a smell about what you've described.
Having said that, you're absolutely right that there's no point in forcing your app to user restful routes when it doesn't deliver anything over /:controller/:action/:id. To make the decision, look how frequently you're using the restful resource route helpers in forms and links. If you're not using them, I wouldn't bother with it.
If I go beyond the standard CRUD actions with my models, I normally just add the methods as required. Searching is something I add to many controllers, but not every one, so I add it and maintain the routes normally:
map.resources :events, :collection => { :search => :get }
Moving these actions to an entirely separate controller might keep some of your controllers RESTful, but I find that keeping them in context is far more useful.
REST does not specify that you can't have additional views. No real world application is going to be able use only the supplied actions; this is why you can add your own actions.
REST is about being able to make stateless calls to the server. Your search action is stateless each time as the data so far is supplied back, correct? Your alternate display action is also stateless, just a different view.
As to if they should be manual routes or a new controller, that depends on how distinct the activity is. Your alternate view, if it provides a full set of CRUD (create, read, update, delete) operations would do well to be in a new controller. If you only have an alternate view to the data, I would just add an alternate view action.
In other words, it doesn't sound like your application is failing to be RESTful, it is more an issue of realizing that the automatically generated feature set is a starting point, not a conclusion.
In my opinion they may have gone a bit off the rails here. What happened to DRY?
I'm just getting back into Rails not having done much development with it since beta and I'm still waiting for the light-bulb to come on here. I'm still giving it a chance but if it hasn't happened for me by the end of my current project I'll probably just drop-back to the old standard routes and define the methods as I actually need them for the next one.
I won't go on to explain more about REST since I think that has been answered in this question, however I will talk a little bit about the default route.
My main problem with the default route is that if you have multiple sites using the same Rails app it can look horrible.
For example there may be controllers that you don't want people to be able to see on one app:
http://example1.somesite.com/example_2/foo/bar/1
compare this to
/:controller/:action/:id
This would go to the controller example_2/foo, action bar and id 1
I consider this to be the main flaw of Rails' default route and this is something that RESTful routes (with subdomain extensions) or only named routes (map.connect 'foo' ... ) can fix.
To remain RESTful in your design, you need to rethink what you call a resource.
In your example a show action for a search controller, (search resource) is the direction to remain restful.
In mine, I have a dashboard controller (show) and controllers for single fields of in-place ecditors (show and update)

Resources