A better way to do conditional ActiveRecord statements? - ruby-on-rails

I'm trying to figure out a better way to have one query here. I want to be able to send something to last where statement a wildcard so I can select all vendors. Right now if i don't include that line it doesn't filter by vendor so I essentially get all the purchase requests.
Any thoughts of a cleaner way to do these sorts of queries?
if #vendor == "0" #checks for vendor
#purchase_requests = PurchaseRequest.includes(:purchase_order)
.where(:created_at => #date_start..#date_end)
.where(:total_cost => #cost_beginning..#cost_end)
else
#purchase_requests = PurchaseRequest.includes(:purchase_order)
.where(:created_at => #date_start..#date_end)
.where(:total_cost => #cost_beginning..#cost_end)
.where("purchaseorder.VendorRef_ListID = ?", #vendor)
end

there must be some better solution, but try this
#purchase_requests = PurchaseRequest.includes(:purchase_order).where(created_at: #date_start..#date_end, total_cost: #cost_beginning..#cost_end)
#purchase_requests = #purchase_requests.where("purchaseorder.VendorRef_ListID = ?", #vendor) unless #vendor == "0"

Here is a simplified version:
#purchase_requests = PurchaseRequest
.includes(:purchase_order)
.where(created_at: #date_start..#date_end)
.where(total_cost: #cost_beginning..#cost_end)
#purchase_requests = #purchase_requests.where('purchase_orders.VendorRef_ListID = ?', #vendor) unless #vendor == '0'

Related

less than or greater than query in RoR

Is there a way to generate a less than or greater than query in rails like the between range query. I have multiple query params and hence I do not want to use string literal for comparison.
if params["end_time"]
if params["start_time"]
params["end_time"] = params["end_time"].to_datetime
query[:created_at] = ((params["start_time"])..params["end_time"])
else
query[:created_at] = #Need help with this
end
end
def with_duration
if(params['start_time'] && params['end_time'])
{created_at: params['start_time']..params['end_time']}
elsif(params['start_time'])
return ["created_at > ?", "#{params['start_time']}"]
elsif(params['end_time'])
return ["created_at < ?", "#{params['end_time']}"]
else
return {}
end
end
ModelName.where(with_duration)
As I understood you need to do nothing if params empty. So you can try this implementation.
def query_method(scope)
return scope unless params["end_time"] && params["start_time"]
scope.where.not(created_at: (params["start_time"]..params["end_time"]))
end

rails conditions based on attributes presence

I was wondering if there's a better way to do this:
def conditions(obj)
if self.setor.present?
obj = obj.joins(:negocios_setores).where("setor_id = ?", self.setor.id)
end
if self.uf.present?
obj = obj.joins(localizacao: [:uf]).where("uf_id = ?", self.uf_id)
end
if self.municipio.present?
obj = obj.joins(localizacao: [:municipio]).where("municipio_id = ?", self.municipio_id)
end
if !self.lucro_liquido_min.to_f.zero?
obj = obj.where("lucro_liquido_anual BETWEEN ? and ?", self.lucro_liquido_min, self.lucro_liquido_max)
end
if !self.faturamento_min.to_f.zero?
obj = obj.where("faturamento_bruto_anual BETWEEN ? and ?", self.faturamento_min, self.faturamento_max)
end
if !self.valor_min.to_f.zero?
obj = obj.where("valor BETWEEN ? and ?", self.valor_min, self.valor_max)
end
obj
end
Does rails 4 provide something to only do the condition if the value is present instead of placing it with a NULL value?
I don't believe that there is any way to do exactly what you mentioned. I've run into the same type of concatenation of queries.
To clean up a bit and make this tighter, you can use one-line if and unless. I think this is a bit cleaner and still readable.
def conditions(obj)
obj = obj.joins(:negocios_setores).where(setor: setor) if setor.present?
obj = obj.joins(localizacao: [:uf]).where("uf_id = ?", uf_id) if uf.present?
obj = obj.joins(localizacao: [:municipio]).where("municipio_id = ?", municipio_id) if municipio.present?
obj = obj.where("lucro_liquido_anual BETWEEN ? and ?", lucro_liquido_min, lucro_liquido_max) unless lucro_liquido_min.to_f.zero?
obj = obj.where("faturamento_bruto_anual BETWEEN ? and ?", faturamento_min, faturamento_max) unless faturamento_min.to_f.zero?
obj = obj.where("valor BETWEEN ? and ?", valor_min, valor_max) unless valor_min.to_f.zero?
obj
end
I also changed the first query to use Rails style queries rather than SQL in the where.

Rails 4 ActiveRecord append conditions (multiple .where)

I would like to append AND conditions depend on condition like this:
#flag = true || false;
#results = Model.where(conditions).where(conditions_depend_on_flag);
// The simple way:
if (#flag) {
#results = Model.where(conditions);
} else {
#results = Model.where(conditions).where(conditions_depend_on_flag);
}
Example for my expected:
#results = Model.where(conditions).where(conditions_depend_on_flag, #flag == true);
I don't know is it possible or not.
Could you give me some suggestion?
#results = Model.where(conditions)
#results = #results.where(conditions_depend_on_flag) if #flag
We can combine 2 conditions into 1 where statement:
To make it easy I assume:
conditions = created_at < 1.day.ago
conditions_depend_on_flag = updated_at > 1.day.ago
So the query will be:
Model.where(
'created_at < ? AND (? OR updated_at > ?)', 1.day.ago, !#flag, 1.day.ago
)
Beautiful SQL :)
use scopes for sql conditions
in model
scope :conditions_depend_on_flag, ->(flag) { where(....) if flag }
anywhere
#results = Model.where(conditions).conditions_depend_on_flag(#flag)

How to refactor complicated logic in create_unique method?

I would like to simplify this complicated logic for creating unique Track object.
def self.create_unique(p)
f = Track.find :first, :conditions => ['user_id = ? AND target_id = ? AND target_type = ?', p[:user_id], p[:target_id], p[:target_type]]
x = ((p[:target_type] == 'User') and (p[:user_id] == p[:target_id]))
Track.create(p) if (!f and !x)
end
Here's a rewrite of with a few simple extract methods:
def self.create_unique(attributes)
return if exists_for_user_and_target?(attributes)
return if user_is_target?(attributes)
create(attributes)
end
def self.exists_for_user_and_target?(attributes)
exists?(attributes.slice(:user_id, :target_id, :target_type))
end
def self.user_is_target?(attributes)
attributes[:target_type] == 'User' && attributes[:user_id] == attributes[:target_id]
end
This rewrite shows my preference for small, descriptive methods to help explain intent. I also like using guard clauses in cases like create_unique; the happy path is revealed in the last line (create(attributes)), but the guards clearly describe exceptional cases. I believe my use of exists? in exists_for_user_and_target? could be a good replacement for find :first, though it assumes Rails 3.
You could also consider using uniqueness active model validation instead.
##keys = [:user_id, :target_id, :target_type]
def self.create_unique(p)
return if Track.find :first, :conditions => [
##keys.map{|k| "#{k} = ?"}.join(" and "),
*##keys.map{|k| p[k]}
]
return if p[##keys[0]] == p[##keys[1]]
return if p[##keys[2]] == "User"
Track.create(p)
end

Ruby: compare hash to hash value trouble

I have such part of "ghost look like" code (but it so must be, as db is huge and have many tables):
def search_group
#search_trees = SearchTree.all
#designation = Designation.find(:all, :conditions => { :DES_ID => #search_trees.map(&:STR_DES_ID)})
#text = DesText.find(:all, :conditions => { :TEX_ID => #designation.map(&:DES_TEX_ID)})
#search_result = #text.find_all{|item| item.TEX_TEXT.include?(params[:search_group_text])}
#designation_back = #designation.find_all{|item| item.DES_TEX_ID == #search_result.TEX_ID}
#search_trees_back = #search_trees.find_all{|item| item.STR_DES_ID == #designation_back.DES_ID}
respond_to do |format|
format.html
end
end
I try to compare
#designation_back = #designation.find_all{|item| item.DES_TEX_ID == #search_result.TEX_ID}
but i get errors, something bad...undefined method `TEX_ID'. As i think, it's via i compare hash and hash in bad way... How can i do this?
#search_result = #text.find_all{|item| item.TEX_TEXT.include?(params[:search_group_text])}
#designation_back = #designation.find_all{|item| item.DES_TEX_ID == #search_result.TEX_ID}
it's because #search_result is an array and not an object where you can call that method on.
#search_results is an array. If you know it is returning just one result, you can do #search_results[0].Tex_Id, otherwise have to loop through for each value of #search_results.
try 'pry' gem to debug what results you are getting from each assignment.

Resources