We have a simple app which has a horizontal layout (left hand side panel and content on the right hand side), with a header and footer. So if you click on a certain object on the left hand side, the view is rendered on the right hand side with navigation panel in the header and footer links. The layout actually renders content on the same page itself for any action on the left hand side and the contents of the left hand side will differ based on the section chosen in the header. How should we go about designing the routes in these cases, which differs from the basic navigation where every action is rendered on a different page.
My routes looks like this..
resources :foos do
resources :foo_bars do
end
end
I would need to show all foos on the left hand side panel and if the user selects a foo it needs to show properties of foo and foo_bars in a table on the right hand side panel. How will the view look for me and how will the URL at the browser look for me? We will have several tabs at the top and based on that you will show foos or similar top level objects
The routes remain the same. You would need to ajaxify your calls.
If your question is:
how should we go about designing routes
The way you have it is just fine if you want to utilize nested resources, and in your case it seems logical.
http://guides.rubyonrails.org/routing.html#nested-resources
Currently, your url will be as follows: /foos/:foos_id/foo_bars/some_action
Let me rename these so things make more sense. Lets say foos is categories, and foo_bars is actions.
Personally, I would override the to_param in the categories model file.
to_param
#return a more readable attribute here
name
end
In this way, your URL would be more closely tied with the names of all the categories on the left side of your page.
So now, if you had a record in the categories table which had the name animal, your URL would look like this: /categories/animal/actions/some_action
That seems pretty logical to me. Make sure in your controller you fetch the record via the proper attribute if you use to_param.
I would apply the same principal to the nested resource as well, then your whole URL would be accurately representing what tab is selected on the page. If you had a record in actions with the name "running", and you had things setup properly, then you could have your url look similar to: categories/animal/actions/running.
You could play around with all the options in your routes file, then use rake routes in terminal to see what changes and what your urls will look like before you even touch the browser.
Here are some extra resources for you.
http://apidock.com/rails/ActiveRecord/Integration/to_param
http://guides.rubyonrails.org/action_controller_overview.html
Hope this helps.
There is no good answer to your question - it all significantly depends on the layout of your application. Besides, there are valid answers here about to_param, and using AJAX, that add important details. But, to give you a head start.
For your views/foos, rewrite your index.html.erb as:
<%= render partial: "show_foos", locals: { foos: #foos, selected_foo: nil }%>
And your show.html.erb as:
<%= render partial: "show_foos", locals: { foos: #foos, selected_foo: #foo }%>
In your foos_controller.rb in show method you need to obtain both #foos and #foo, e.g.:
#foos = Foo.all
#foo = Foo.find(params[:id])
Now, to the fun part. Back to views/foos directory. Create a partial called "_show_foos.erb" (the one that we called both from #index and #show). Do something like:
<table>
<tr>
<td>
<%= render partial: "show_foos_list", locals: { foos: foos, selected_foo: selected_foo }%>
</td>
<td>
<%= render partial: "show_foo_props", locals: { selected_foo: selected_foo }%>
</td>
</tr>
</table>
Please note that's an extremely brute & ugly example that creates a table with two columns: one for the list of foos in the left "panel", the other for displaying the results for the selected foos in the right "panel". In real life use divs and styling. Also, consider pushing the layout to where it belongs - to the appropriate layout file - and using named yields there. But, as I said, a headstart - simple table.
Now, just define the two partials mentioned here. First, the "_show_foos_list.erb" that lists the foos on the left. Assuming each foo has a 'title' attribute, something like:
<% foos.each do |foo| %>
<%= link_to_unless selected_foo && (foo.id == selected_foo.id), foo.title, foo %><br />
<% end %>
Second, the foo & foo_bars on the right - "_show_foo_props.erb":
<% if selected_foo %>
# Here display the Foo attributes
<h2> Foo: <%= selected_foo.title %> </h2>
<% selected_foo.foo_bars.each do |foo_bar| %>
# Here display each FooBar that belongs to Foo
<h3>FooBar <%= foo_bar.title %></h3>
<%= foo_bar.description %>
<% end %>
<% end %>
Again, very crude example. Replace 'title', 'description' with the right sets of parameters, use partials to display FooBars. Do the styling with CSS. Etc, etc, ... Refactor as you see fit.
Talking about the routes. What you get is when you go to your "www.yourapp.com/foos" url is the list of all foos on the left, nothing on the right. Once you press on any foo in the left column, you go to "www.yourapp.com/foos/:id", where :id is the ID of the selected foo (and consider to_param from the other answer here or more advanced techniques to make this part meaningful) and get the list of foos on the left, and the properties of the selected foo and all foo_bars belonging to it on the right.
Hope that helps to start laying out your own implementation based on the rough idea presented here.
If I understand your question correctly, the answers saying Ajax is required are not correct. I have an ancient Perl app (written in 1999) that does this. Am currently re-implementing in Rails, and it's working fine. Frames make it particularly easy to allow the data to scroll while the menu stays fixed.
You do need to use HTML4 frames, which are deprecated in HTML5, It's possible to use an IFRAME for the data rendering frame and be HTML5 compliant, but the result is less usable than the FRAME solution in HTML4, at least with some browsers.
As others have said, your routes are fine.
The trick is to use the target field in the form to direct the Submit response to the rendering frame. My haml code for the "command" frame is
= form_tag admin_menu_path, :method => :put, :target => 'data_frame' do
...
The rest is just a normal form. This form remains constant in (my case) the left frame while responses replace each other in the right data_frame.
The matching frame HTML is:
<frameset cols="360,*">
<frame name="menu_frame" src="...">
<frame name="data_frame" src="admin.htm">
</frameset>
You would have to use an outer frameset to get the header and footer, but this should be straightforward.
I am ready for comments saying frames are far from best practice. But for this particular application, they are perfect: simple, understandable, and extremely browser independent. E.g. my 1999 Perl generated code ran fine on IE 2.0 and Netscape (the ancestor of Firefox, friends). And it's still perfect on every modern browser I can find. Wish Ajax could say the same...
If I've misunderstood your question, I'll happily delete this response.
Related
Imagine a Rails project that looks up animal celeberties based on their names. This Rails app is backed by an external service that does the actual lookup. The service returns back results based on a key. For example, if I make a request to this external api like [GET] /animal?name=benji, I would get back something like {"type":"dog", "legs":"4", "tail-length":"short", "collar":"blue"}. However, if I pass in ...?name=flipper to the animal endpoint, I would get back {"type":"dolphin", "color":"gray", "food":"fish"}. (The data is returned in actual JSON or XML. I am just using pseudo code here to communicate the point.)
My first question is this... Given that the attributes of the return call vary based on data which is passed in, when unmarshaling a response (for lack of a better term) into a "model" object, does it make sense to implement some type of factory pattern (ala Design Patterns in Ruby, by Russ Olsen, Chapter 13) to create objects of an appropriate class? Are there other approaches that would make sense?
My next question is this, lets say that I want to display a list of all animals on a web page (using ERB templates.) Does it make sense to create different partial templates (eg _dolphin.html.erb and _dog.html.erb) and then put a case in the main list view that can deligate rendering each list item to an appropriate template.
For example:
list.html.erb...
<ul>
<% for animal in #animals.each %>
<li>
<% if animal.type == 'dog' %>
<%= render :partial => 'dog', :locals => {:animal => animal} %>
<% elsif item.type == 'dolphin' %>
<%= render :partial => 'dolphin', :locals => {:animal => animal} %>
<% else %>
<%= render :partial => 'generic_animal', :locals => {:animal => animal} %>
<% end %>
</li>
<% end %>
</ul>
(Here animal.type=='dog' is intentional. I am not using a symbol (:dog) because the data returned back from the API is a string value, and it is used to populate the animal.type attribute. Bad, I know.)
The project that I am working on is using this approach right now. (Obivously, I have changed the elements/domain.) I am wondering if this is a valid approach, and/or if others have dealt with similar problems and how they went about it.
Thanks!
I'd say create a single model and a single view which contains all possible attributes (can't be an infinite number ;) ).
And then you have an
if attribute_x exists then
display it
end
if attribute_y exists then
display it
end
for each attribute.
If you create a view for each animal this wouldn't be DRY at all, 'cause you'll repeat yourself sooo many times, just knowing that each animal has favorite food and a color, etc.. Another reason: If the API changes a bit, and an animal gathers or looses an attribute you would have to adapt this change.
With just one view, it would be all fine for all time.
If you want to be super-sure that you gather all attributes, you could place an array of all known attributes inside your controller and if there's something unknown: write it to a log file.
I'd only choose the way of 'one view per animal' if you want to be able to display things completely different for some animals. But then you could also tell your controller that it should choose another view if name = 'Donkey Kong'. you know what I mean.
I would like to have a right side bar with content changes for each page.
For example, when I am in Friends page, the side bar should display New Friends.
When I am in Account page, the side bar should display Recent Activities.
How should I go about this to respect Rails design patterns? I heard about Cells gem, but I am not sure if I use it.
here is one way, in your layout add a named yield section
<div id="main-content">
<%= yield %>
</div>
<div id="side-content">
<%= yield(:side_bar) %>
</div>
Then in your views put content into the named yield using content_for
# friends view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/new_friends" %>
<% end %>
# account view ....
<% content_for(:side_bar) do %>
<%= render :partial => "shared/recent_activity" %>
<% end %>
this requires you to be explicit about what content appears in the side bar for every view,
maybe having it do it dynamically is better? probably depends on the specific situation and your preference
see also - http://guides.rubyonrails.org/layouts_and_rendering.html#understanding-yield
I came by this question in a moment of a big design change in our views. After thinking about the sidebar problem a bit, I realized that there's no best solution (as always). There are better solutions for each case.
I'll compare 3 solutions here:
using content_for(:sidebar) and yield(:sidebar)
using the partials approach
using the Cells gem
1. Using content_for(:sidebar) and yield(:sidebar)
This is good for cases when each link (each controller action) you access renders a different sidebar. In this case, each view you access will have the content_for(:sidebar) part.
If your sidebar view depends only on the state of some variable in the session, for example, the sidebar should not be rendered for every link you access.
Then you should use a good caching system like turbolinks, to avoid rendering many times the same thing, or use something like the Cells gem with a javascript to render only the main part of the layout.
2. Using partials
Using partials is always good to eliminate duplication. If your sidebar is very simple and is changed for every controller, you can render it as a partial. But if you're rendering different partials in the same controller, according to some state, it may be an indication that you have business logic in your views, which should be avoided.
3. Using the Cells gem
Very good design pattern when you have to render your sidebar from a different controller than the rest of the view each time.
It takes a lot of business logic out of the view, which sure is a good practice.
Here you have an action calling a view. Inside that view, there is a statement render_cell(:sidebar, params). This statement will do some business logic and render the view of the sidebar. It's as if the first action called other controller actions to render specific parts of your view (called cells)
If you make changes to the sidebar only, you may have to create other simple action, so that a javascript will request it. This action will call the render_cell(:sidebar) method again to respond with the view.
It's a very interesting approach.
Other ideas:
Your sidebar could be rendered only with javascript from the same
action.
Your sidebar could be rendered by an angular controller, and rails sends jsons with the sidebar objects. (look for "One page apps")
try something like this
<div class="sidebar">
<% if current_page?(controller => "friends", :action => "show") %>
<h4>New Friends</h4>
<% elseif current_page?(controller => "accounts", :action => "show") %>
<h4>Recent Activities</h4>
<% end %>
</div>
If the above code fits what you are trying to do(looks like this is what you want to achieve), then stick with it, else it may be beneficial to go with some gems. Also checkout helper page on how to use current_page? method. Hope it helps
I've had a problem with the following issue in Rails and ASP.Net MVC. Often there are multiple widgets of functionality on a page, yet one controller action is supposed to render the page. Let me illustrate:
Let's say I have a normal e-commerce site, and the menu is made of categories, while the page is to display an group of products.
For the products, let's say I have an action on a controller that looks something like:
def product_list
#products = Products.find_by_category(:name => 'lawnmowers')
end
And I have a layout with something like
<div id="menu"><%= render :partial => 'menu' %></div>
<div id="content"><%= yield %></div>
The products have a view...
<%= render :partial => 'product', :collection => #products %>
(note I've ommited the product view as irrelevant)
And the menu has a partial...
<% Category.each {|c| %>
<%= render :partial => 'menu_node', :locals => { :category => c } %>
<% } %>
The line I have a problem with is the "Category.each.do" in the view. I'm fetching data in the view, as opposed to using variables that were set and bound in the controller. And it could easily be a more complex method call that produces the menu.
The solutions I've considered are:
-A view model base class that knows how to get various pieces of data. But you could end up with one of these for each conceptual "section" of the site.
-a local variable that populates at the top of each method (violates DRY)
-the same thing, but in a before_filter call
None of these seem very elegant to me. I can't help but look at this problem and think that a MVP presenter per view (not screen) is a more elegant solution.
ASP.Net MVC has render action (different from rails render :action), which does address this, but I'm not sure what I think of that solution.
Thoughts? Solution suggestions?
Added Note:
The answers provided so far are good suggestions. And they apply to the example I gave, where a menu is likely present in every layout, and is clearly secondary to the product data.
However, what if there is clearly no second class citizen? Portal type sites commonly have multiple unrelated widgets, in which each is important.
For example, What if this page was displaying weather trends, with widgets for temperature, humidity, and precipitation (and each is a different model and view type).
In rails we like to have a concept of thin-controllers, thick-models. So I think you're right to not want to have variables set in the controller.
Also, in order to enable a more-complex method later on, I recommend doing something like:
/app/controllers/application_controller.rb
before_filter :add_menu_nodes
def add_menu_nodes
#menu_nodes = Category.menu_nodes(current_user)
end
/app/views/layouts/application.html.erb
<%= render :partial=>:menu, :locals=>{:categories=>#menu_nodes} %>
/app/models/category.rb
def self.menu_nodes(current_user)
Category.all.order(:name)
end
That way in the future you could update Category.menu_nodes with a more complicated solution, based on the current user, if you need.
Forgive me if I butcher the Ruby (or misunderstand your question), but what's wrong with
class section_helper
def menu( section )
// ...
menuBuiltAbove
end
end
in the view
<%= section_helper.menu( 'section' ) %>
?
This question relates to cleaning up the view and giving the controller more of the work.
I have many cases in my project where I have nested variables being displayed in my view. For example:
# controller
#customers = Customer.find_all_by_active(true)
render :layout => 'forms'
# view
<% #customers.each do |c| %>
<%= c.name %>
<% #orders = c.orders %> # I often end up defining nested variables inside the view
<% #orders.each do |o| %>
...
<% end %>
<% end %>
I am fairly new to RoR but it seems that what I'm doing here is at odds with the 'intelligent controller, dumb view' mentality. Where I have many customers, each with many orders, how can I define these variables properly inside my controller and then access them inside the view?
If you could provide an example of how the controller would look and then how I would relate to that in the view it would be incredibly helpful. Thank you very much!
I don't think there is anything drastically wrong with what you're doing. Looping through the customers and outputting some of their attributes and for each customer, looping through their orders and outputting some attributes is very much a view-oriented operation.
In the MVC architecture, the controller has responsibility for interacting with the model, selecting the view and (certainly in the case of Rails) providing the view with the information it needs to render the model.
You might consider extracting the code into a view helper though, if you have that exact code repeated more than once. You could even genericize it, passing in the name of a model and association. I haven't tested it, but you should be able to do something like this:
def display_attributes(models, association, attribute, association_attribute)
content = ''
models.each do |m|
content << "<p>#{m.attribute}</p>"
associated_models = m.association
associated_models.each do |am|
content << "<p>#{am.association_attribute}</p>"
end
end
content
end
Then in the view, you could use the helper like this:
<%= display_attributes(#customers, orders, name, name) %>
Obviously you would change the HTML markup within the helper method to suit your requirements. Note that if you're not using Rails 3 then you'll want to escape the output of the attribute names in the helper method.
I don't think there's anything wrong with your code. I'd just suggest for you to use a :include in your find
#customers = Customer.find_all_by_active(true, :include => :orders)
to reduce the number of queries.
I see nothing wrong with the code as you showed.
You are mixed up about the "intelligent controller, dumb view" approach though, i tend to prefer the "skinny controller, fat model", so indeed the view should be dumb, but you put the intelligence inside your model, and your helpers (or use a presenter), but definitely not in the controller.
Warning: Noob here.
I know this is a trivial subject but I'm having a lot of difficulty in figuring out how exactly I can simplify my views by moving parts of them into helpers. For example, I've always read that conditionals in your views are prime candidates for extraction into helpers, but I couldn't really find examples of this, and my attempts to achieve this failed.
For example, suppose I have:
#index.html.erb
<% for beast in #beasts do -%>
<% if beast.dead? -%>
<%= beast.body %>
<%= link_to "bury", bury_beast_path( :id => beast.id ) %>
<% else -%>
<%= beast.body %>
<%= link_to "kill!", kill_beast_path( :id => beast.id ) %>
<% end -%>
<% end -%>
It annoys me a little to have this in my view, but how exactly could I move this to a helper instead? And further simplify it, if possible. (I've read somewhere that conditionals are bad but it's just beyond me how you could program anything without them.)
Another example: I need to id my body tags with the format controller_action. The best I've got so far is this:
#index.html.erb
<body id="<%= controller_action %>">
…and…
#application_helper.rb
def controller_action
#id = #controller.controller_name + "_" + #controller.action_name
end
I'm no expert, but that's still ugly even to me.
To make things more complicated, Ryan Singer said something I liked: to treat ERB like an image tag, using helpers to "reveal intention". Then in the next breath saying that you should have no HTML in helpers for that is the way to hell. WTF? How are both things compatible? If it's come to the point where you can just declare behaviors in the view, surely there should be a lot of HTML to be rendered behind the scenes? I can't grasp it.
So, that's basically it. I'd appreciate if anyone could share some thoughts on this, or point me to some good in depth reading on the subject – which I've found to have a really weak coverage on the web. I've already googled it to exhaustion but who knows.
Refactoring makes your views easier to maintain. The problem is choosing where the refactored code goes.
Your two choices are partials and helpers. There's no stone-set rules dictating which should be used where. There are a couple of guidelines floating around like the one stating that helpers should not contain HTML.
Generally partials are better suited for refactoring sections that are more HTML/ERB/HAML than ruby. Helpers on the other hand are used for chunks of ruby code with minimal HTML or generating simple HTML from parameters.
However, I don't agree with the sentiment that helpers should contain no HTML at all. A little is ok, just don't over do it. The way helpers are processed hinder their use for producing large amounts of HTML. Which is why it's suggested that your helpers contain minimal amounts of HTML. If you look at the source the helpers that ship with rails you will notice that most of them generate html. The few that don't, are mainly used to generate parameters and evaluate common conditions.
For example, any of the form helpers or link_to variants fit the first form of helpers. While things like url_for and logged_in? as supplied by various authentication models are of the second kind.
This is the decision chain I use to determine whether to factor code from a view into a partial or helper.
Repeating or nearly identical statements producing a single shallow html tag? => helper.
Common expression used as an argument for another helper? => helper.
Long expression (more than 4 terms) used as an argument for another helper? => helper.
4 or more lines of ruby (that is not evaluated into HTML)? => helper.
Pretty much everything else => partial.
I'm going to use the code you're looking to refactor as an example:
I would refactor the view in the question this way:
app/helpers/beast_helper.rb:
def beast_action(beast)
if beast.dead?
link_to "bury", bury_beast_path(beast)
else
link_to "kill!", kill_beast_path(beast)
end
end
app/views/beasts/_beast.html.erb:
<%= beast.body %>
<%= beast_action(beast) %>
app/views/beasts/index.html.erb:
<%= render :partial => "beast", :collection => #beasts %>
It's technically more complicated, because it's 3 files, and 10 lines total as opposed to 1 file and 10 lines. The views are now only 3 lines combined spread over 2 files. The end result is your code is much more DRY. Allowing you to reuse parts or all of it in other controllers/actions/views with minimal added complexity.
As for your body tag id. You should really be using content_for/yield. For that kind of thing.
app/views/layouts/application.html.erb
...
<body id="<%= yield(:body_id) %>">
...
app/views/beasts/index.html.erb
<% content_for :body_id, controller_action %>
...
This will allow you to override the id of the body in any view that requires it. Eg:
app/views/users/preferences.html.erb
<% content_for :body_id, "my_preferences" %>
The first thing I'd do would be this:
#index.html.erb
<%= render #beasts %>
#_beast.html.erb
<%= beast.body %>
<%= link_to_next_beast_action(beast) %>
#beast_helper.rb
def link_to_next_beast_action(beast)
if beast.dead?
link_to "bury", bury_beast_path( :id => beast.id )
else
link_to "kill!", kill_beast_path( :id => beast.id )
end
end
What I've done is separate out the rendering of the beast into a partial which uses collection semantics.
Then I've moved the logic for showing the kill/bury links into a beast helper. This way if you decide to add another action (for example, 'bring back from dead'), you'll only have to change your helper.
Does this help?
A third choice is to use a view model from the Cells gem. This is a very popular framework that brings object-orientation to the view layer in Rails.
# app/cells/beast/cell.rb
class Beast::Cell < Cell::Concept
def show
return dead if model.dead?
kill
end
private
def dead
link_to "bury", bury_beast_path( :id => model.id )
# you could render a view here, too!
end
def kill
link_to "kill!", kill_beast_path( :id => model.id )
end
end
You then render a view model using a helper (in the view or controller).
# app/views/beasts/index.erb
<%= concept(:beast, #beast).call %>
<%-# this returns the link content %>
That's all! You can test this cell isolated in a separate test. Cells also give you view rendering, view inheritance and many more things.
As an example, you could use a view for the kill link.
# app/cells/beast/cell.rb
class Beast::Cell < Cell::Concept
# ..
def kill
render :kill
end
end
This renders the cell's killer view.
# app/cells/beast/views/index.erb
<%= link_to "kill!", kill_beast_path( :id => model.id ) %>
Note the location of the view, it's nicely packaged into the cell directory.
And, yes, cells can do HAML and any other template engine supported by AbstractController.
Another startegy would be to not use templates and helpers at all.
For rendering you could :
render your views directly from your controllers using render(:inline => ). If you still want to keep Views and Controllers formally separated you can create modules / mixins that you include into the controllers.
or create your own view classes and use them to render your response.
The idea behind this is that helpers and rails erb templating system don't take advantage of OOP, so that at the end of the day you can't define general behaviours that you'll specialize according to each controller's/request's needs; more often than not one ends up rewriting very similar looking chunks of code, which is not very nice from a maintenance standpoint.
Then if you still need some helper methods (eg. form_tag, h, raw, ...) you only have to include them in your controller / dedicated view class.
See this : rails-misapprehensions-helpers-are-shit for a fun but useful article.
EDIT: to not sound like a complete douche, I'd say implementing this depends on how big your application is supposed to be, and how often you're going to have to update your code. Plus, if you're delegating the design to a non-programmer, he/she may well be in for some programming courses before digging into your code, which admittedly would be less directly understandable than with templates syntax.