Filter controller result based on params - ruby-on-rails

I'm trying to filter the results that are returned from my index view based on optional params. My code is working for the first param, sinceDate. But for the second param, searchQeury, nothing is filtered out.
_controller.rb
def index
since = params[:sinceDate]
query = params[:searchQuery]
#articles = Comfy::Cms::Page.published.all
if since
#articles = #articles.reject{ |a| a[:created_at] < Date.parse(since) }
end
if query
#article = #articles.select{ |a| a[:label].match(/#{query}/i) }
end
end

Is it possible that the problem is a typo?
In the line after "if query", it should be perhaps #articles instead of #article.

Related

How a set a "search" value to a variable in a controller

I had a value that is inserted in a input at index and led me to a show page.
But, when I insert and submit, I gain a Blank Page! However, if I type in the URL the path to the show item, it appears!
Print from the input on client side:
https://imgur.com/a/ZSnacmM
I tried and get the following result with biding pry
https://imgur.com/a/d3H9VAi
The "entrega" really comes with the input that is the cient_number, but #entrega does'nt
So, When goes to if, all the values are nil. I really don't understand
I've tried the following code at Controller:
def sac_index
#binding.pry
#objectives = DeliveryObjective.all
#search = params["search"]
if #search.present?
#entrega = #search["client_number"]
#objectives = DeliveryObjective.where(client_number: #entrega)
end
##search_uol = DeliveryObjective.where(client_number: params[:id])
end
def sac_show
##delivery_objective = DeliveryObjective.where(client_number: #search)
#delivery_objective = DeliveryObjective.where(client_number: params[:id])
binding.pry
end
When I input the client_number, I want to be rendered to the show page from the item.
Based in an earlier response, I resolved the conflict, changing the Controller:
def index
#objectives = DeliveryObjective.all
#search = params["search"]
if #search.present?
#entrega = #search["client_number"]
#objective = DeliveryObjective.where(client_number: #entrega)
end
end
def show
#search = params["search"]
if #search.present?
#entrega = #search["client_number"]
#delivery_objective = DeliveryObjective.where(client_number: #entrega)
end
end
It's a little bit dirty, but can help someone.

Too many checks for empty params. How to optimize queries to ActiveRecord in Rails5?

I'm doing checks for empty parameters before do the query.
There is only 1 check for params[:car_model_id]. I can imagine if I will add more checks for other params, then there will be a mess of if-else statements. It doesn't look nice and I think it can be optimized. But how? Here is the code of controller:
class CarsController < ApplicationController
def search
if params[:car_model_id].empty?
#cars = Car.where(
used: ActiveRecord::Type::Boolean.new.cast(params[:used]),
year: params[:year_from]..params[:year_to],
price: params[:price_from]..params[:price_to],
condition: params[:condition]
)
else
#cars = Car.where(
used: ActiveRecord::Type::Boolean.new.cast(params[:used]),
car_model_id: params[:car_model_id],
year: params[:year_from]..params[:year_to],
price: params[:price_from]..params[:price_to],
condition: params[:condition]
)
end
if #cars
render json: #cars
else
render json: #cars.errors, status: :unprocessable_entity
end
end
end
The trick would be to remove the blank values, do a little bit of pre-processing (and possibly validation) of the data, and then pass the params to the where clause.
To help with the processing of the date ranges, you can create a method that checks both dates are provided and are converted to a range:
def convert_to_range(start_date, end_date)
if start_date && end_date
price_from = Date.parse(price_from)
price_to = Date.parse(price_to)
price_from..price_to
end
rescue ArgumentError => e
# If you're code reaches here then the user has invalid date and you
# need to work out how to handle this.
end
Then your controller action could look something like this:
# select only the params that are need
car_params = params.slice(:car_model_id, :used, :year_from, :year_to, :price_from, :price_to, :condition)
# do some processing of the data
year_from = car_params.delete(:year_from).presence
year_to = car_params.delete(:year_to).presence
car_params[:price] = convert_to_range(year_from, year_to)
price_from = car_params.delete(:price_from).presence
price_to = car_params.delete(:price_to).presence
car_params[:price] = convert_to_range(price_from, price_to)
# select only params that are present
car_params = car_params.select {|k, v| v.present? }
# search for the cars
#cars = Car.where(car_params)
Also, I'm pretty sure that the used value will automatically get cast to boolean for you when its provided to the where.
Also, #cars is an ActiveRecord::Relation which does not have an errors method. Perhaps you mean to give different results based on whether there are any cars returned?
E.g: #cars.any? (or #cars.load.any? if you don't want to execute two queries to fetch the cars and check if cars exist)
Edit:
As mentioned by mu is too short you can also clean up your code by chaining where conditions and scopes. Scopes help to move functionality out of the controller and into the model which increases re-usability of functionality.
E.g.
class Car > ActiveRecord::Base
scope :year_between, ->(from, to) { where(year: from..to) }
scope :price_between, ->(from, to) { where(price: from..to) }
scope :used, ->(value = true) { where(used: used) }
end
Then in your controller:
# initial condition is all cars
cars = Cars.all
# refine results with params provided by user
cars = cars.where(car_model_id: params[:car_model_id]) if params[:car_model_id].present?
cars = cars.year_between(params[:year_from], params[:year_to])
cars = cars.price_between(params[:price_from], params[:price_to])
cars = cars.used(params[:used])
cars = cars.where(condition: params[:condition]) if params[:condition].present?
#cars = cars

How to merge two objects in Ruby on Rails

I'm trying to retrieve from the database two contents: the first one with the field source equal to "imported" (which means that we import it from the excel spreadsheet), and the second one with source != imported (we create it from scratch). Attached is my code:
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
#page = params[:page]
#contents = #contents_not_imported << #contents_imported
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
And I want to combine the two results before showing it:
#contents = #contents_not_imported << #contents_imported
but it didn't work. How can I do that?
If both of them are arrays and are having same type of objects you can do Result = Arr1 | Arr1
That also removes the duplicates. Its like boolean UNION. In your case #contents = #contents_not_imported | #contents_imported
The problem is that you want to concatenate results, but you also want to continue treating the combined results as an ActiveRelation (call .search on it). Here's a simpler approach that avoids the need for concatenation in the first place. You will need a more complex ORDER BY clause to accomplish this, however:
#page = params[:page]
#contents = Content.of_project(#project).with_category(#category).
order('CASE WHEN source <> "imported" THEN contents.created_at END desc, CASE WHEN source = "imported" THEN contents.created_at END asc')
#q = #contents.search(params[:q])
Concatenating the arrays is done with the plus sign
You are getting undefined method search for Array because, concatenating will return you an array. And you can't call search method on that Array
EDIT
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
contents_imported_ids = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc').map(&:id)
contents_not_imported_ids = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc').map(&:id)
#page = params[:page]
contents_ids = contents_imported_ids + contents_not_imported_ids
contents = Content.where(content_ids)
#contents = content_ids.collect{|id| contents.detect{|c| c.id == id}}
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
Just create a new Relation with the conditions of imported or not imported, after that, order all the records (if order is important to #contents and #content):
def index
add_breadcrumb 'Projects', projects_path
add_breadcrumb #project.name, #project
add_breadcrumb "List #{#category.display_name} Content", project_category_contents_path(#project, #category)
#contents_imported = Content.of_project(#project).with_category(#category).imported.order('contents.created_at asc')
#contents_not_imported = Content.of_project(#project).with_category(#category).not_imported.order('contents.created_at desc')
#page = params[:page]
imported = #contents_imported.where_values.reduce(:and)
not_imported = #contents_not_imported.where_values.reduce(:and)
#contents = Content.where(imported.or(not_ipmorted)).order('CASE contents.imported WHEN true THEN contents.created_at asc ELSE contents.created_at desc END')
#q = #contents.search(params[:q])
#content = #q.result(distinct: true).page(#page).per(20)
end
Now you can call Ransack#search on #contents because it is an ActiveRecord::Relation. I assume that the imported scope take a field contents.imported with value true.
If I wrote this without errors, this must works.

Add extra filter parameters in RoR

Suppose I have a method in a controller:
def my_find(is_published, count)
items = Idea.where(published: is_published)
#......
end
Sometimes I want to pass some extra filter arguments
def my_find(is_published, count, some_extra_filter = nil)
items = Idea.where(published: is_published) #.where (some_extra_filter)
#......
end
where some_extra_filter can be lambda or just an plain sql "where" string and it can also be nil or "".
So how do I concatenate .where(published: is_published) with where (some_extra_filter) to get what I need?
This is actually very easy using scopes:
def my_find
#items = Idea.scoped
#items = #items.where(published: is_published) unless is_published.nil?
#items = #items.where(other: other_param) if other_params < 10
# etc, etc
end

Rails how to beautify this block

kinda new to Rails.
How would you simplify, beautify this method:
def find_index
index = 0
#ipn.each {|p|
index = 4 if p = "new" #this is just a dummy line
}
index
end
As you can see, it's ugly. How can I remove the index definition at the top and the index at the bottom to return - The Ruby way?
I think you want this method:
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-find_index
def find_index
#ipn.find_index{|p| p == "new" }
end
I also change the variable name '#ipn' to '#ipns', since it seems to be an enumerable.
And to avoid confusion, I might rename the method to 'find_ipn_index', so it looks like this:
def find_ipn_index
#ipns.find_index{|p| p == "new" }
end
Or, if self.ipns is available, don't define a new method at all for this, just call:
self.ipns.find_index{|p| p == "new" }

Resources