Rails3 How can I use :params in named scope? - ruby-on-rails

I'm trying to display a list of milestones for a particular order. (Orders have many milestones.)
In my orders model, I have this:
scope :open, lambda {
joins("join milestones on milestones.order_id = orders.id").
where("order_id = ? AND milestone_status = ?", :params[:order_id], true).
group("orders.id")
}
The problem I'm having is getting the current order ID to work - :params[:order_id] is clearly wrong.
In my routes I have this:
resources :orders do
resources :milestones
end
And my url is as follows:
http://127.0.0.1/orders/2/milestones
How is this possible? I have tested the scope by replacing with an order ID manually.
-- EDIT --
As per advice below, I've put the following in my milestones controller:
#orders = Order.open( params[:order_id] )
And in my view, I have this:
<% #orders.each do |open| %>
But I get an error:
wrong number of arguments (1 for 0)
The full stacktrace is here: http://pastie.org/2442518

Define it like this:
scope :open, lambda { |order_id|
joins("join milestones on milestones.order_id = orders.id").
where("order_id = ? AND milestone_status = ?", order_id, true).
group("orders.id")
}
And call it on your controller like this:
def index
#orders = Order.open( params[:order_id] )
end

Related

Rails 7 Advanced Search Chain Where

I'd like to setup an advanced search of a Rails resource (i.e Product) where I can perform both positive and negative searches. For example:
Product is a phone
Product is not made by Apple
Product was made in the last 16 months
I can pass multiple parameters to a page but is there a way to chain queries?
#results = Product.where("lower(type) LIKE ?", "%#{search_term.downcase}%").where(....
I'd like to use a combination of where and where.not:
def search
word1 = params[:word_1]
word2 = params[:word_2]
if word1.starts_with?('not')
chain1 = where.not("lower(tags) LIKE ?", "%#{word1.downcase}%")
else
chain1 = where("lower(tags) LIKE ?", "%#{word1.downcase}%")
end
if word2.starts_with?('not')
chain2 = where.not("lower(tags) LIKE ?", "%#{word2.downcase}%")
else
chain2 = where("lower(tags) LIKE ?", "%#{word2.downcase}%")
end
#products = Product.chain1.chain2
end
but I get the following error:
undefined method where' for #ProductsController:0x0000000000ac58`
You can chain where like this
Product.
where(type: "phone").
where.not(factory: "Apple").
where(manufactered_at: 16.months.ago..)
Also rails 7 introduces invert_where (it inverts all condtions before it) so you can
Product.
where(factory: "Apple").invert_where.
where(type: "phone").
where(manufactered_at: 16.months.ago..)
You can use scopes
class Product < ApplicationRecord
scope :phone, -> where(type: "phone")
scope :apple, -> where(factory: "Apple")
scope :manufacatured_last, ->(period) { where(manufactered_at: period.ago..) }
end
Product.apple.invert_where.phone.manufacatured_last(16.months)

Rails Searchkick with scopes in controller

I'm making a search page where I have a couple of filters on the side and I'm trying to integrate them with Searchkick to query products.
These are my scopes I'm using for the products
models/product.rb
scope :in_price_range, ->(range) { where("price <= ?", range.first) }
scope :in_ratings_range, -> (range) { where("average_rating >= ?", range.first) }
def self.with_all_categories(category_ids)
select(:id).distinct.
joins(:categories).
where("categories.id" => category_ids)
end
This is where I'm actually calling the scopes
controllers/search_controller.rb
#results = Product.search(#query)
#results = #results.with_all_categories(params[:category_ids]) if params[:category_ids].present?
#results = #results.in_price_range(params[:price]) if params[:price].present?
#results = #results.in_ratings_range(params[:rating]) if params[:rating].present?
After running it, I get an error saying the searchkick model doesn't have any methods with the name of my scope.
undefined method `with_all_categories' for #Searchkick::Results:0x00007f4521074c30>
How do I use scopes with my search query?
You can apply scopes to Searchkick results with:
Product.search "milk", scope_results: ->(r) { in_price_range(params[:price]) }
See "Run additional scopes on results" in the readme.
However, if you apply ActiveRecord where filters, it will throw off pagination. For pagination to work correctly, you need to use Searchkick's where option:
Product.search(query, where: {price_range: 10..20})
The error (unknown to me at the time of writing this answer) might be because you defined with_all_categories as a class method on Product, but in your controller you call it on #results which must be an ActiveRecord::Relation.
Turning it into a scope should fix the issue:
Change this:
def self.with_all_categories(category_ids)
select(:id).distinct.
joins(:categories).
where("categories.id" => category_ids)
end
to:
scope :with_all_categories, -> (category_ids) { select(:id).distinct.joins(:categories).where("categories.id" => category_ids) }

Method to return a collection - Ruby

Say, I have a method called posted_listings, which is supposed to run an ActiveRecord query and return a collection of User.listings where posted: true, with posted? being a Listing class method. So far I have been doing:
class Bar < ActiveRecord::Base
def posted_listings
posted_listings = []
listings.each { |listing| posted_listings << listing if listing.posted? }
posted_listing
end
end
but each time this query runs I start feeling really bad about my skills (or lack of thereof). What is the most efficient way to return a collection of posted listings?
Edit:
posted? is not an attribute, its a class method:
class Listing < ActiveRecord::Base
def posted?
true if quantity >= 1 && has_sellers?
end
end
def has_sellers?
sellers.count >=1 #association to Seller
end
I would recommend adding a scope to your Listing model like this:
scope :posted, -> { where(posted: true) }
Then you can get all posted listings like this:
#user.listings.posted
You can learn more about scopes here if you are interested.
UPDATE
Try this scope instead:
def self.posted
joins(:sellers)
.where('posted = ? AND quantity > ?', true, 0)
.group('listings.id')
.having('COUNT(sellers.id) > ?', 0)
end
Your question is not so clear for me.
You may try:
User.listings.where(posted: true)
to get all users' posted Listings.
Or, saying #useris an User instance:
#user.listings.where(posted: true)
to get posted Listings from an specific user.

Next previous article based on order

I've got a list of all my articles, ordered by client name.
articles_controller.rb - index
#articles = Article.order('client')
In my show page, how do I get the next and previous article in that order?
If you already have the article for the show action, you could use 'index' to get the position of this article in the Array returned by 'Article.order('client').all'
Somehow like this:
#article = Article.find(params[:id])
#article_list = Article.order(:client).all
index = #article_list.index(#article)
Based on this you can calculate the previous and next article simple enough. Not sure if this is the smartest solution, but it should work.
have two scopes in your model
class Article < ActiveRecord::Base
scope :order_by_id, lambda {
{:order => "id desc"}
}
scope :order_by_client, lambda {|client_id|
{:order => "client_id desc"}
}
end
and
#articles = Article.order_by_client(1).order_by_id
with validations you could do something like
next article (say the current article index is 1)
#articles(current_index + 1) unless #articles(current_index + 1).nil?
previous article (say the current article index is 2)
#articles(current_index - 1) unless #articles(current_index - 1).nil?
** havent tried the code but should work

Rails 3 displaying tasks from partials

My Tasks belongs to different models but are always assigned to a company and/or a user. I am trying to narrow what gets displayed by grouping them by there due_at date without doing to many queries.
Have a application helper
def current_tasks
if user_signed_in? && !current_company.blank?
#tasks = Task.where("assigned_company = ? OR assigned_to = ?", current_company, current_user)
#current_tasks = #tasks
else
#current_tasks = nil
end
end
Then in my Main view I have
<%= render :partial => "common/tasks_show", :locals => { :tasks => current_tasks }%>
My problem is that in my task class I have what you see below. I have the same as a scope just named due_today. when I try current_tasks.due_today it works if I try current_tasks.select_due_today I get a undefined method "select_due_tomorrow" for #<ActiveRecord::Relation:0x66a7ee8>
def select_due_today
self.to_a.select{|task|task.due_at < Time.now.midnight || !task.due_at.blank?}
end
If you want to call current_tasks.select_due_today then it'll have to be a class method, something like this (translating your Ruby into SQL):
def self.select_due_today
select( 'due_at < ? OR due_at IS NOT NULL', Time.now.midnight )
end
Or, you could have pretty much the same thing as a scope - but put it in a lambda so that Time.now.midnight is called when you call the scope, not when you define it.
[edited to switch IS NULL to IS NOT NULL - this mirrors the Ruby in the question, but makes no sense because it will negate the left of the ORs meaning]

Resources