Using ".order(params[])" with a instance method? - ruby-on-rails

I have a Post, Reply and a Vote model (polymorphic):
create_table "posts", :force => true do |t|
t.text "content", :limit => 255
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "title"
t.integer "replies_count", :default => 0, :null => false
t.integer "category_id"
end
create_table "replies", :force => true do |t|
t.text "content"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "votes", :force => true do |t|
t.integer "votable_id"
t.string "votable_type"
t.integer "user_id"
t.integer "polarity"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I needed the total_votes of posts and replies so I created an instance method:
post.rb and reply.rb:
def total_votes
self.votes.map {|v| v.polarity }.sum
end
So I can use it to sort posts and replies:
homepage:
default_order = "created_at DESC"
params[:order_by] ||= default_order
#feed_items = #post.replies.paginate(page: params[:page],
per_page: 10).order(params[:order_by])
So now, I'm not very sure what do add after order_by in the view:
<span><%= link_to 'total_votes DESC', root_path(order_by: HERE) %></span>
I tried &:total_votes DESC and total_votes DESC but didn't work.
What's the right way of doing this? (I thought it was a bit of unnecessary to add a total_votes column to both posts and replies tables, but not sure if that is better for performance?)

I would start with using the database to do the summing for you, but this is a little bit tricky because you need to order by a value that is calculated (an "aggregate"), but let's set that aside.
Your view will display items in the order determined when the instance variable is set in the controller. So in the controller, you may detect that there's an order_by parameter (e.g. /feed?order_by=total_votes, and load the #feed_items accordingly, e.g.
def index
if params[:order_by] && params[:order_by] == 'total_votes'
sort_order = "total_votes DESC"
else
sort_order = "created_at DESC"
end
#feed_items = #post.replies.paginate(page: params[:page],
per_page: 10).order(sort_order)
end
then in your view, to change the sort order, create a link_to that adds the query string parameter (see the last example in the link_to api doc, e.g.
<%= link_to "Sort by Total Votes", root_path(:order_by => 'total_votes') %><br />
<%= link_to "Sort by Time", root_path %><br />
Your other question is about how to do the sorting by votes. This answer may provide a good start for that, and an answer to your performance question: Rails: Order by sum of two columns

Related

Sorting a recipe list by amount of ingredients on view page in Rails?

My schema is as follows:
create_table "ingredients", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "recipe_ingredients", force: :cascade do |t|
t.integer "recipe_id"
t.integer "ingredient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "recipes", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
On my index page, I would like to list all the recipes and then either have link or button that sorts the recipes by the amount of ingredients each recipe has. I've been toying with this for a couple hours now and getting closer but being a newb, my brain isn't quite making the leap. Right now I have some code in the RecipesController in #index that takes in some params from the view file:
<%= button_to "sort by ingredient amount", {:controller => "index", :action => "update", :order => true}, :method=>:post %>
the code in the controller so far is something like this:
def index
if params[:order] = true
Recipe.all.each do |r|
r.ingredients.each do |i|
???
end
end
else
#recipes = Recipe.all
end
end
I'm not sure how to use .order when accessing an association such as the recipes_ingredients. This could also be the totally bassackwards way to do it.
Yes you can find the data based on ingredients like these
def index
if params[:order] = true
Recipe.left_joins(:ingredients).group(:id).order('COUNT(ingredients.id) DESC')
else
#recipes = Recipe.all
end
end

Rails Includes - Association named 'albums' was not found on Song; perhaps you misspelled it?

I have the following Schema
create_table "songs", force: :cascade do |t|
t.string "title", null: false
t.text "lyrics", null: false
t.string "youtube_url"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "album_id"
t.integer "artist_id"
end
When i try to do the following
def index
#songs ||= find_songs
end
private
def find_songs
songs = Song.includes(:albums).order(updated_at: :desc).paginate(:page => params[:page], :per_page => 2)
songs = songs.order(:title) if params['sort_by'] == "title"
songs
end
I get the following error
Association named 'albums' was not found on Song; perhaps you
misspelled it?
Try this if you have an association as Album has_many songs and Song belongs_to album.
includes should take the association name.
songs = Song.includes(:album).order(updated_at: :desc).paginate(:page => params[:page], :per_page => 2)

Rails ajax form with join table associations

I am trying to build a 'ajaxy' form between two models in rails. here are the relevant files and code snippets...
my models
ActiveRecord::Schema.define(:version => 20140214134314) do
create_table "group_shots", :force => true do |t|
t.integer "shot_id"
t.integer "group_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "groups", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "shots", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
app/views/groups/show.html.erb
<b>Name:</b>
<%= #group.name %>
<%= link_to 'New Shot', new_shot_path, id: "new_shot", remote: true %></br>
app/views/shots/new.js.erb
$('#new_shot').hide().after('<%= j render("form") %>');
app/controllers/shots_controller.rb
def create
#shot = Shot.new(params[:shot])
respond_to do |format|
if #shot.save
GroupShot.create! :shot_id => #shot.id
def new
#shot = Shot.new
end
My end goals is when a user is viewing a group (show action/view) that they can add a shot that is associated with that group, and consequently when the user adds the shot, the correct entries are made in the group_shot table.
You can see in the shots controller, when the shot is saved I am creating a new entry in the group/shots table successfully. I am getting the shot_id, but not the group_id. So that is the question, how do I get the group_id in this view/controller. Any help appreciated.
My suggestion is to pass the group_id as a parameter to the call to new_shot_path in your groups_controller#show view:
new_shot_path(group_id: #group)
then include it as a hidden field in the form you're using in your shots_controller#new view, using hidden_field_tag:
<%= hidden_field_tag 'group_id', #group.id %>
and then include it in your call to GroupShot#create in the shots_controller#create method:
GroupShot.create! :shot_id => #shot.id, :group_id => params[:group_id]
Just keep in mind that you're doing two things in shots_controller#create -- both creating the shot and associating it back to the group, which is perhaps a little unorthodox. From your code, it appears that a Shot is never created without an associated Group. If your starting point is always from viewing groups (then adding shots to a selected group), you may want to consider moving this to the groups_controller as something like #add_shot, but that's going to be depend on how the rest of your application is architected.

Getting a Twilio 404 Error Rails Receiving SMS

I'm trying to create an app that accepts an SMS message through Twilio, and then creates a check-in/out transaction that is tied to both employee models and item models. A simple SMS-based item checkout/checkin tracker. I have the twilio app wired up to listen on tooler.herokuapp.com/twilio/twilio_create, but when I send messages to the number, nothing happens and I get a 404 error within twilio's logs. Not sure exactly what's going on, was hoping someone might be able to help. In this case, I'm taking the FROM from twilio and putting it into employee_id, and the BODY from twilio and putting it into item_id. Why won't it create new transactions?
db/schema.rb
ActiveRecord::Schema.define(:version => 20130516162824) do
create_table "employees", :force => true do |t|
t.string "phone"
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "items", :force => true do |t|
t.string "description"
t.string "assettag"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "transactions", :force => true do |t|
t.boolean "status"
t.integer "item_id"
t.integer "employee_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "transactions", ["employee_id"], :name => "index_transactions_on_employee_id"
add_index "transactions", ["item_id"], :name => "index_transactions_on_item_id"
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
end
app/controllers/twilio_controller.rb
class TwilioController < ApplicationController
def process_sms
#city = params[:FromCity].capitalize
#state = params[:FromState]
render 'process_sms.xml.erb', :content_type => 'text/xml'
end
def twilio_create
#transaction = Transaction.new(:item_id => params[:Body], :employee_id => params[:From])
#transaction.save
end
end
app/views/twilio/twilio_create.xml.erb
<Response>
<Sms>Received. You checked out <%= #body %>, <%= #from %> you lucky bastard.</Sms>
</Response>
I already got it working with the process_sms page, so I know that it's something with the twilio_create function. What am I doing wrong?
Should the URL be tooler.herokuapp.com/twilio/twilio_create.xml? You can check rake routes to see all URLs that conforms to your config/routes.rb.
Actually, Rails already has CRUD convention. Since you are creating a twilio resource, your config/routes.rb should be:
# config/routes.rb
resources :twilio do
collection do
get :process_sms
end
end
In the controller, you should use def create instead of def twilio_create.
class TwilioController < ApplicationController
def process_sms
#city = params[:FromCity].capitalize
#state = params[:FromState]
render 'process_sms.xml.erb', :content_type => 'text/xml'
end
def create
#transaction = Transaction.new(:item_id => params[:Body], :employee_id => params[:From])
#transaction.save
end
end
Lastly, rename app/views/twilio/twilio_create.xml.erb to app/views/twilio/create.xml.erb.
In order to create a new transaction, do a post request to tooler.herokuapp.com/twilio.xml. That URL will hit the def create in TwilioController and render app/views/twilio/create.xml.erb.
If it still doesn't work because of 404 error, you can check rake routes to see all URLs that conforms to your config/routes.rb.

Using association params in collections

I want to narrow down a collection using params accessible through association
class PostsController < ApplicationController
load_and_authorize_resource
def index
if params[:event_id] then
#event = Event.find(params[:event_id])
if params[:category_id] then
#category = Category.find(params[:category_id])
#posts = Post.where(:event_id => #event.id,
:product => {:category_id => #category.id })
end
end
end
Gives me the error
No attribute named 'category_id' exists for table 'product'
But the column 'category_id' does exist in the table 'product'. Searching for this error hasn't shown me anything helpful yet. I've also tried using 'delegate' to make the attribute accessible but that hasn't worked either. I'm stumped.
Here is the schema
create_table "posts", :force => true do |t|
t.float "quantity"
t.datetime "deadline"
t.integer "product_id"
t.integer "event_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", :force => true do |t|
t.string "title"
t.text "desc"
t.text "ingredients"
t.float "deposit"
t.float "cost"
t.string "units"
t.float "quantity"
t.float "deadline_hours"
t.boolean "presell_option"
t.integer "user_id"
t.integer "category_id"
t.integer "club_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Edit:
When I correct ':product' to ':products' I get this related error
SQLite3::SQLException: no such column: products.category_id: SELECT "posts".* FROM "posts" WHERE (("products"."category_id" = 2 AND "posts"."event_id" = 10))
This puzzles me further, the schema says I do have the category_id in the products table
You have to use the attribute name products instead of product. This is one one Rails exceptions to the rule.
#posts = Post.joins(:product).where(:event_id => #event.id,
:products => {:category_id => #category.id })
Try
#posts = Post.where(:event_id => #event.id,
:products => {:category_id => #category.id })
You can reduce your code and make your life easier by using MetaSearch gem. It's very nice tool! Video tutorial: here. Documentation: here.

Resources