Rails STI Mystery - Why does type change from Class to String in view? - ruby-on-rails-3

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.

Related

How can I create an array of objects from a list of associated checkboxes?

First of all, I've done a fair amount of looking around, and while questions get around answers, I have a problem I think is somewhat unique. I have a list of checkboxes generated with the following code:
<% for student in Student.find(:all) %>
<div>
<%= check_box_tag "user[student_ids][]", student.id, current_user.students.include (student) %>
<%= student.name %>
</div>
<% end %>
After clicking the 'update' button at the bottom, I need each of the checked boxes to be placed into an array. I then plan on iterating over the array and doing some work on each of the checked names. I am having a hard time, however, with the process of getting these names all into an array. I really am not sure which of the standard web actions this kind of work should be (i.e, post, get, etc.), so I don't know how to set up a route. Even if I could set up a route to a controller, how would I get the checked students into an array of Student objects?
Thanks ahead of time for your help!
The full answer to your question depends on a variety of things, for example, what you are trying to do with the submitted array, etc (which would determine whether POST, GET, PUT or DELETE should be used.) Without knowing more information with respect to your code base, if you throw the following code into a form_for in one of your controller's already restful routes, you should be able to see the array of checked names:
<%= current_user.students.include(student).each do |student| %>
<div>
<%= check_box_tag "student_names[]", student.name %> <%= label_tag student.name %>
</div>
<% end %>
Then, when the user hits submit, the params hash will show student_names = [].
And make sure your attributes are accessible as needed.
On a side note, check out Railscasts pro episode from last week. Pretty much exactly explains what you are trying to do. It's a subscription service, though.
I managed to solve my problem in a less-than-satisfying way. Here is the code I ended up using:
current_user.students.delete_all
if(params.has_key? :user)
params[:user][:student_ids].each do |i|
current_user.students<<(Student.find(i))
end
end
Because the number of students I'm managing is not ever larger than 100, this operation isn't as bad as it looks. I'm deleting all of the associations already present, and then cycling through all passed parameters. I then find the student object with the passed parameter id and add it to the current_user's User-Student join table.
I hope this helps someone down the line!

Rails ERB -- get the generated HTML content to a variable I can access in a controller

I am doing an online contract agreement which is generated dynamically based on the current state of our contract language (stored in an ERB partial).
When the user clicks "I Agree" we save the variable data, but I also want to store the contents of the rendered partial (html fragment) in the database.
I tried using ActionView::Helpers::CaptureHelper.capture -- wrapping the whole partial in a block and rendering at the end, like
<% #saved_output = capture do %>
...
The rest of the erb with instance variable <%= #my_class.name %> and so on
...
<% end %>
<%= #saved_output %>
<% logger.debug "Rendered output is: #{#saved_output}" %>
And this produced the same result and also sent the correct text to the log. But it appears to go out of scope -- even if I declare #saved_output = nil prior to render it's nil when I end the block.
I tried using content_for as well ... which makes some sense to me, but ... huh, just not getting it. Any helpful pointers appreciated.
Have you tried render_to_string in your controller when you're creating the record? That may allow you to just go ahead and "snapshot" the page.

ActiveRecord "select" results of model method

I have a Rails app that pulls in music from Soundcloud. This data contains a title, which I save as mix.sc_title but it's not always properly formatted. I have added an additional attribute on my Mix model which I call mix.override_title
For display on my site, I want to use the override title if available, and the sc_title in all other cases.
I have a Mix model method to do this for me
def display_title
override_title.blank? sc_title : override_title
end
Mixes#index grabs #mixes = Mix.where(:active => true) and mixes/index.html.erb looks like this:
<ul>
<% #mixes.each do |mix| %>
<li><%= link_to mix.display_title, mix %></li>
<% end %>
</ul>
As you can see, I'm not directly using any mix attributes, and so I take a huge hit when I go to the DB, and I don't actually benefit from it.
Is there a leaner way to get just the information I need? (mix.display_title)
I have tried Mix.select("display_title").where(:active => true) but it fails because display_title is not a real DB column
You can do Mix.select("sc_title, override_title").where(:active => true) and it will work, since those are the actual fields that the method uses. I don't really think getting the additional attributes gives you that much of a DB hit but sometimes selecting only what you need can be beneficial.
As you start chaining on more Arel commands, consider putting the select into a model method:
def select_active_titles
select("sc_title, override_title").where(:active => true)
end
Edit: Your link_to helper also secretly calls mix.id to link to the right mix, so make sure it's working and if not add id to the list of selected attributes.

Checking if a nested model attribute is true

So I have one model called Project, for which there is a nested model called Proposal (so every project has multiple proposals, and each proposal only belongs to one Project).
I have a column for Proposal called "winning" which just checks if one of the Proposals has won for the Project. I'd like to reference this on the Show page of the Project, but a little perplexed by the code.
What I really want to do is check if any of the proposals have status "winning"
This is what I'm trying for the Show view for Projects, but it isn't working:
<% if #project.proposals.winning %>
SUCCESSFUL
<% end %>
I feel like this should be pretty rudimentary but I'm having trouble figuring it out, thanks!
That's ideal candidate for:
<% if #idea.proposals.any? {|proposal| proposal.winning? } %>
Enumerable.any? returns true if for any array element the block returns true.
Use it instead:
<% if #idea.proposals.count{|a| a.winning } > 0 %>
Or even better to create a method for it in the Idea model:
def has_winning?
proposals.count{|a| a.winning } > 0
end
Okay, found this code on another post and it seems to be working, not sure if it's the best way to go about it though:
<% if #idea.proposals.map(&:winning).flatten %>

How can I use the 'unless' keyword in a view?

Can anyone tell me if there's a Rail3 replacement for something like this:
<%= unless #page.new_record? || !#page.background_image? %>
bla
<% end %>
I'm trying to display a checkbox on a form only when a user edits. Not when they create.
I think the statement is ok, but should not be included in your view. Instead, create a model method, probably named is_editable? or something, that includes this statement. Then get an instance variable in your controller and use that instead.
Logic in views is a very bad idea :)
Your mistake is including the = in the ruby code.
<% unless #page.new_record? || !#page.background_image? %>
bla
<% end %>
However, as other users have stated, it is probably better to hide this logic in the model rather than in the view. Additionally it considered a best practice to only use unless if there is only one boolean statement. It starts to get harder and harder to read when you have ors and nots included there.
<% if #page.is_editable %>
blah
<% end %>
This would be a nicer version, and even better than that (depending on how complicated 'blah' is) would be to hide the whole thing in a helper method.
<%= some_special_checkbox(f) %>
The parameter f would be the form object so that your helper can render the checkbox for the form.
You can write it like this:
<%= "bla" unless #page.new_record? || !#page.background_image? %>
May be you should write separate method with more human-readable name and replace this condition with one method. Try to keep your view as much cleaner as possible