ActiveRecord "select" results of model method - ruby-on-rails-3

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.

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!

Ordering records when using has_and_belongs_to_many relation in Rails 3?

I am, for the first time, trying to use a HABTM relationship in my Rails application. The following image shows the models and their relations to each other:
Currently I am displaying all members within a region with the following:
<% #region.members.each do |member| %>
<%= link_to member.name, member %>
<% end %>
I am trying to sort/order the list of members by their respective level. I then would like to have the members ordered in descending alphabetical order.
So, the members_controller code is currently the default:
#members = Member.all
I can order the results into alphabetical order:
#members = Member.order("name DESC").all
but I can't figure out how to use a related model's data to order the member records. There are likely going to be three levels; Charity, Basic and Subscription. I am planning on having a block at the top of the page which only shows subscription members. The subscription members should not then be shown again on the page.
#subscribed_members = Member.where(:level == 1).order("name DESC").all
but, as you can see, I have no idea how to filter that block by level.
Can anyone point me in the right direction, or maybe to a tutorial for this? I've read through the Rails guide for HABTM relationships but it only seems to cover the models.
EDIT
After trying the suggestion, I now have the following:
#members = Member.all
#subscribedmembers = Member.include(:levels)
.where("levels.name == 'subscriber'")
How do I use that method in the regions view?
<% #region.subscribedmembers.each do |member| %>
<%= member.name %>
<% end %>
That won't work because it's looking for a related model called subscrivedmembers which doesn't exist.
For this kind of task, i recommend you to take a look at joining models with ActiveRecord. The methods are include and joins. Lemme demonstrate with a piece of code:
#subscribed_members = Member.include(:levels)
.where("members.url == 'test_url'")
.order("levels.name DESC")
If you check your console after this query is run, you can see the include makes a SQL join with the two tables. Here i am assuming member :has_many :levels, so you include the :levels table and use a prefixed column name on your order clause.
It is not that hard once you get the idea, so i encourage you to try these two methods on the console and check the results.
EDIT
Create a scope with you query code, then use it on any place. Example:
# On your model
scope :ultimate_level, include(:levels).where("name = 'test'").order("levels.name DESC")
# On your controller
#subscribed = Member.ultimate_level
# On your view
<% #subscribed.each do |s| %>
You can create different scopes too, each one that makes a single operation, and then reuse on our controllers, etc.
Obs.: Check the syntax, i didn't test the code myself.

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.

How to add column filter options in view (doing it "the rails way")

I search for a working solution for a rather simple problem, but could not find a good explanation.
What I currently have (working) is an index view which contains:
a form to enter a new element and
a paginated list of existing elements (using will_paginate).
For the list I am interested in only part of the data, thus I am trying to add a form with filter options and I would like to store the forms content in a cookie (which should be replaced with an per user object stored in the database, but i do not have users yet). What I cannot figure out is how to get the values from the form stored in a cookie (and vice versa) and how to use it together with will_paginated.
What I currently tried to do as a first step is to create an #filter object in my controller and adding the filter form for this object, setting the form options to use the index controller again. This leads to selected filter parameters passed in the params hash to the index controller (visible in the url). But this solution has some drawbacks. first the filters are gone as soon as I change the view (e.g. by creating a new element) and second the #filter object should be the cookie instead.
Here is the code I have so far:
View-partial for filter:
<%= form_for(#filter, :url => {:action => "index"}, :html => {:method => :get}) do |f| %>
<div class="field">
<%= f.label :german %><br />
<%= f.check_box :german %>
</div>
<div class="actions">
<%= f.submit "Filter" %>
</div>
<% end %>
Controller:
def index
#word = Word.new
#filter = Word.new(params[:word])
#words = Word.paginate(:page => params[:page]).order('word')
# ....
Can anybody help me? How is such a functionality (filtering results) done in other applications?
So the answer to the question is, use a where clause to include only the matching records in your result.
#words = Word
.where("german = ?", params[:word][:german] != 0")
.order('word')
.paginate(:page => params[:page])
This is a new Rails syntax called Active Relation (AREL for short), which should generally replace the older find and find_by methods. It has several benefits that can improve performance, notably that the SQL it executes (which you can see in your logs) only occurs when it is referenced, not when it is declared. This give you neat ways of defining partial relations (even as named scopes) that you can build up to create simpler statements that combine together.
The order of the various clauses doesn't matter -- AREL will generate the same SQL, but generally I like to follow the order of the underlying SQL,
where
joins
group
order
limit
offset
(limit and offset are handled in your case by the pagination tool).