Looking for partial in wrong location? - ruby-on-rails-3

Maybe it's my brain taking some time to warm up after the long weekend but my head is about to explode. I'm trying to include a shared partial, which I have in
/app/views/shared/_invitee_fields.html.erb
In my main page view /app/views/pages/_form.html.erb I'm including the partial like so:
<%# Render the partial of the invitee fields. %>
<%= render :partial => "/shared/invitee_fields", :locals => { :form_builder => i } %>
However, this results in an error being thrown, for a missing partial, apparently looking in the wrong location.
Missing partial pages/invitee_fields, application/invitee_fields with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}.
It's as if the framework is ignoring my explicite choice to look in /shared and falling back to the convention. I'm sure that I'm making some form of glaringly obvious mistake, but I can't spot it.
Can anyone help?

Related

Middleman sitemap.where doesn't exist

I'm trying to automatically generate a list of links to pages that have certain frontmatter in them, but every time I try to use sitemap.where(), I get a NoMethodError. For example, the following line:
<%= sitemap.where(:title=>"about") %>
produces this output:
NoMethodError at /
undefined method `where' for #<Middleman::Sitemap::Store:0x007f9b95c7d890>
Ruby layouts/layout.erb: in block in singleton class, line 20
Web GET localhost/
I was wondering if I accidentally messed something up in my project, so I generated a new Middleman project, but I had the same problem when I tried to use sitemap.where. Is there a solution to this or another way that I can query all of the pages?
The where method is part of ActiveRecord and might not work in Middleman.
To get only those pages in the sitemap which have a particular property, you can use Ruby's select:
<% sitemap.resources.select{|p| p.data.title == 'about'}.each do |page| %>
<%= page.url %>
<% end %>
This code will print a (very basic) list of the URLs of pages that match your criteria.

Correct way to share a view in the index page

I'm a Ruby-on-Rails newbie, just starting out.
I have an MVC called "account_types", generated via scaffold to produce:
controllers/account_types_controller.rb
helpers/account_types_helper.rb
models/account_type.rb
views/account_types/_form, edit, index etc...
Going to localhost:3000/account_types gives me the index view.
What I'd like to do is display the same data as selected from the account_types index method in the application index page as a list.
I wrote a new view called account_types/_list.html_erb as follows:
<ul>
<% #account_types.each do |account| %>
<li><% account.label %></li>
<% end %>
</ul>
I then edited home/index.html.erb (This is based on examples given in other questions on SO):
<%= render :partial => 'account_types/list', :module_types => #module_types %>
However I get
undefined method `each' for nil:NilClass
and the error displays the code from account_types/_list.html.erb where I've written
<% #account_types.each do |account| %>
The scaffolded views work fine, why aren't mine?
Are partials the right thing to use here?
Thanks in advance.
What is the correct way to define an application-wide partial and its variables in rails says to use a before_filter on ApplicationController.
You pass :module_types to partial, but use account_types. As I can see you just need to change your index.html.erb to:
<%= render :partial => 'account_types/list', :account_types => #module_types %>
You can use partials for this if you want, though it would be unnecessary in this case as far as I can tell (they are for sharing chunks of code between several views). In order to get this code to work you'll need to define #account_types in your controller with something like
#account_types = AccountType.all
You can see exact line in your account_types_controller.rb under index action. :module_types => #module_types is not necessary here, since I doubt you defined #module_types either and you don't use module_types in your partial at all.
It's obvious, that you don't understand how Rails works, so I suggest reading through a good tutorial (like this one) before you proceed with whatever you have in mind.

Rails STI Mystery - Why does type change from Class to String in view?

This is long so I hope you'll bear with me...
I have a model called Update with two subclasses, MrUpdate and TriggeredUpdate. Using single-table inheritance, added type field as a string to Update.
In my view I'm checking which type it is to decide what to display. I assumed since type is a string, I should do
<% if #update.type == 'MrUpdate' %>
This failed, i.e., it evaluated to false when the update was an MrUpdate. I noticed that at this point, #update.type.type is Class. OK, whatever, thought I, so I changed it to:
<% if #update.type == MrUpdate %>
and it worked, i.e., the comparison evaluated to true when the update was an MrUdpate. Then I did it again lower down in my view and it failed again (i.e., it evaluated to false when the update was an MrUpdate.)
Turns out the culprit is a couple of <%= link_to ... %> calls I use and make into buttons with jQuery. If I put this code in my view:
<br>
<%= #update.type.type %><br>
<%= #update.type %><br>
<%= link_to 'New Note', new_note_path(:update_id => #update.id), :class => "ui-button" %>
<br>
<%= #update.type.type %><br>
<%= #update.type %><br>
What I see is:
Class
MrUpdate
(the New Note button)
String
MrUpdate
It's changing from a class to a string! So what the heck am I doing wrong or missing here? Why should a link_to do that? First I'm not clear why it's not a string in the first place, but then really confused as to why it would change...?!? Any help or explanation would be helpful. I can just code it one way at the top and another way at the bottom, but that way madness lies. I need to understand why this is happening.
I figured out what the issue is here. Thanks to fl00r for pointing the way.
Yes, type is a reserved in Ruby 1.8.7 which tells you the class of the object you call it from. But it's also true that it is the name of the field used in Rails to indicate single-table inheriance and to store the name of the class of each instance of the subclass.
So I naively tried to access the value of the type field using #update.type. But what this was doing at the top of the view was calling the type method of the Object class in Ruby. For whatever reason, after the link_to calls, it was then access the value of the type field of the updates table.
While trying to figure this out I called #update.type in the Rails console and saw this message: "warning: Object#type is deprecated; use Object#class". Finally it registered what I was doing. When I changed my calls to:
<% if #update.class == MrUpdate %>
everything works as expected. I never saw a call to determine the type in any of the pages I found via Google about STI. This despite the fact that they all recommended using only one controller, wherein sometimes you must need to determine the class of the instance you have.
So, dumb mistake--pilot error. But maybe this will help someone else who gets tripped up on this.

Rails Routing Error for nested form_for

I now this has been asked a thousand times but that doesn't help me heh :) I've been at this an hour. My form:
= form_for #comment, :url_for => { :action => "create", :controller => "comments"}, :method => :post
my rake routes:
POST /t/:trunk_id/r/:root_id/comments(.:format) {:action=>"create", :controller=>"comments"}
trunk_root_comment GET /t/:trunk_id/r/:root_id/comments/:id(.:format) {:action=>"show", :controller=>"comments"}
The error:
undefined method `comments_path' for #<#<Class:0x007fed2c713128>:0x007fed2c71cc78>
If I name space the form to:
= form_for [:trunk_root, #comment], :url_for => { :action => "create", :controller => "comments"}, :method => :post do |f|
which should make the route trunk_root_comments_path.. which is correct according to the rake routes.. I get:
No route matches {:controller=>"comments", :format=>nil}
Help is very much appreciated.. been looking at this for hours..
UPDATE:
Thank you Ryan for such a great answer! A very clear explanation of something I was just sort of 'throwing things' at, now at least I understand better. I actually already had 'trunk_root_comments_path' available in my rake routes, and I had tried a couple of the combinations you mentioned, but I wasn't really grocking what I was missing, so you helped. I'm using Mongo and I don't actually have a Trunk model, I just have an attribute on roots called #root.trunk, though I have a trunk controller and therefore its a part of my routes(maybe a bad idea idk).
So I tried your TLDR and it said error:
Undefined method 'root_comments_path'
.. cause no Trunk model exists, I assume?.. so I made #trunk just equal the correct id with
= form_for [#trunk, #root, #comment] do |f|
<- and I got 'undefined method `politics_root_comments_path''.. I figured well.. that probably makes sense.. since I'm failing I must as well try your most explicit version:
= form_for #comment, :url => (trunk_root_comments_path(:trunk_id => #root.trunk, :root_id => #root.id)) do |f|
and sure enough that worked... so I'm not quite sure how to do it shorter then this.. the odd thing for me is I have another nested resource "photos" at the same level of depth in the routes and I was able to get that to work with = form_for [:trunk_root, #photo], :html => { :class => 'root_form' } do |f|.. but here for some reason I couldn't.. anyways I'd say you gave me enough to understand 100% but I think I went from 20% understanding to 50% understanding.. I know now that id's ARE important to routes, and the named helpers need access to them. I got an introduction to how the url_helper works, but would need to read more on it to really grock it fully I think. I'm also now able to construct proper routes in their longer form at least to get through tricky situations like this. So thank you :)
TL;DR You need to specify both a :trunk_id and a root_id in your URL or use form_for like this:
<%= form_for [#trunk, #root, #comment] do |f| %>
Rails is attempting to build a URL from the hash you're giving it, but that hash doesn't match anything in its routing table. You could do this:
{ :controller => "comments", :action => "create", :trunk_id => trunk.id, :root_id => root.id }
But that's really a bit tl;dr.
The cooler way to do it is this:
trunk_root_comments_path(trunk, root)
Where trunk and root are Trunk and Root instances respectively.
Now, if you want to be super-wicked-cool, do it like this:
<%= form_for [trunk, root, comment] do |f| %>
Science!
So how does this work? Elementary, my dear:
Rails first recognises that we're using form_for using an Array and that we mean business. Rails uses this array passed in and builds a URL out of it. It does this by using the routing helpers that are defined by the routes. Unfortunately, you've defined your routes in a funny way that don't play nice with this, but don't fear! We can fix this.
The way you can do it is this where you have this in config/routes.rb:
post '/t/:trunk_id/r/:root_id/comments'
Instead put this:
post '/t/:trunk_id/r/:root_id/comments', :as => "trunk_root_comments"
You may alternatively already have this:
match '/t/:trunk_id/r/:root_id/comments', :via => :post
Which should become this:
match '/t/:trunk_id/r/:root_id/comments', :via => :post, :as => "trunk_root_comments"
Either way, you've now got not one, but two(!!) path helpers defined by the routes. These aretrunk_root_comments_path and trunk_root_comments_url respectively. The names of these methods are super important for what I am about to explain to you. Pay attention!
So, back to our little form_for call:
<%= form_for [trunk, root, comment] do |f| %>
Rails knows that we're using an Array because it can see it. What it does with this Array may seem like magic, but isn't really.
Rails will take each element of this array and build a routing helper method name up from the different parts. This isn't actually part of form_for, but another method called url_for that you can use by itself:
url_for([trunk, root, comment])
In the beginning, this routing helper method name generated by url_for is simply an empty array ([]). Nothing special at all.
But then what happens is special!
The first element is going to be a persisted instance of the Trunk class. By "persisted" I mean that it's an object that maps directly to a record in the database. Yay ORMs!
Rails will know this, and so will turn the routing helper into this: [:trunk].
The second element is going to be a persisted instance of the Root class. Rails also knows this (damn, Rails is smart!) and will then append this to the array, turning it into [:trunk, :root]. Awesome.
The third (and final) element is then checked by Rails. It sees that (in this case) it's a non-persisted element, i.e. it's not been saved to the database.. yet. Rails treats this differently and will instead append [:comments] to the array, turning it into this:
[:trunk, :root, :comments]
See where I'm going with this now?
Now that Rails has done it's thing (or thang, if you like) it will join these three parts together like this: trunk_root_comments, and just for good measure it'll put _path on the end of it, turning it into the final trunk_root_comments_path helper.
And then! Man, and then... Rails calls this method and passes it arguments! Just like this:
trunk_root_comments_path(:trunk_id => trunk.id, :root_id => root_id)
This generates a full path to the resource like this:
/t/:trunk_id/r/:root_id/comments
And bam! Full circle! That's how Rails will know to generate the URL and you don't have to use ugly hashes anymore.
Success!
Not sure if you have this route set up but try:
= form_for #comment, :url => trunk_root_comments_path

Beginner struggling with update_attribute command

I am in the process of trying to use the update_attribute command, but struggling to get it working (at all) and hoped someone could point me in the right direction?
I have previously posted a question about this issue, it was very useful in terms of giving a feel for the mechanics of what is going on, but unfortunately it didn't actually get it working.
I have a database of items (Items), which among other things contains ':item_name', ':click_count' and ':external_url'.
Currently I have a view (Showselecteditems) in which there is a list of all the items, when a user clicks on an item name, they are directed to the appropriate external url. This works using the code:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url %>
I would now like to add the ability to count the number of times a particular item name has been clicked on (i.e. in total for all users, not individual users) and therefore the number of times each external url has been visited in order to work out which is most popular.
Reading around, I believe i need to modify the code above to something of the form:
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And need to define this function somewhere - it seems to only be found if located in 'application_helper'?
def clickcountplusone
clickeditem = Items.find(params[:identifier])
clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end
Needless to say, I cannot get this to work... My question is therefore, how can I set things up correctly so that when the link is clicked on the count is incremented? The other common problem people seem to report is that the number will be incremented each time the page is refreshed, which I would like to avod if possible.
Previously people have suggested adding to the 'show' section of the 'Items' controller, however, i don't know how this would work as the links are being clicked on the Showselecteditems view page, not the database itself where you get the show, edit, destroy commands. Any advice greatly appreciated.
This
<%= link_to selecteditem.item_name.to_s, selecteditem.external_url, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
will not point user to the some_controller#clickcountplusone, because you already specified an external link.
The easiest way to do this job is to modify your link_to like:
<%= link_to selecteditem.item_name.to_s, {:controller => params[:controller], :action => clickcountplusone, :identifier => selecteditem.item_name} %>
And then to modify your actions source:
def clickcountplusone
clickeditem = Items.find(params[:identifier])
redirect_to clickeditem.external_url if clickeditem.update_attribute(:click_count, clickeditem.click_count + 1)
rescue ActiveRecord::RecordNotFound # to avoid error if no identifier value
end