Rails: accessing the values of columns selected with column_names - ruby-on-rails-3

Any ideas what it takes to get this to work? I can't for the life of me figure it out.
def get_prices(c)
#print_prices = {}
Billing.where(:name => c).column_names.each do |d|
if d.match(/^print_/)
#print_prices[d] = d.value
end
end
return #print_prices
end
I've got no idea what to substitute d.value for.
Cheers for any help.

The following code will perform the query, returned in the form of a relation, and reject all items in the attribute key-value hash which do not match the given regex, which, in this case, is /^print_/.
def get_prices(c)
Billing.where(:name => c).first.attributes.reject{ |i| !i.match(/^print_/) }
end
Alternatively, it can also be written as:
def get_prices(c)
Billing.where(:name => c).first.attributes.select{ |i| i.match(/^print_/) }
end

Related

Rails ActiveRecord where clause

I want to select Cars from database with where clause looking for best DRY approach for my issue.
for example I have this two parameters
params[:car_model_id] (int)
params[:transmission_id] (int)
params[:from_date]
params[:to_date]
but I dont know which one will be null
if params[:car_model_id].nil? && !params[:transmission_id].nil?
if params[:from_date].nil? && params[:from_date].nil?
return Car.where(:transmission_id => params[:transmission_id])
else
return Car.where(:transmission_id => params[:transmission_id], :date => params[:from_date]..params[:to_date])
end
elseif !params[:car_model_id].nil? && params[:transmission_id].nil?
if params[:from_date].nil? && params[:from_date].nil?
return Car.where(:car_model_id=> params[:car_model_id])
else
return Car.where(:car_model_id=> params[:car_model_id], :date => params[:from_date]..params[:to_date])
end
else
return Car.where(:car_model_id=> params[:car_model_id], :transmission_id => params[:transmission_id], :date => params[:from_date]..params[:to_date])
end
what is best approach to avoid such bad code and check if parameter is nil inline(in where)
You can do:
car_params = params.slice(:car_model_id, :transmission_id).reject{|k, v| v.nil? }
and then:
Car.where(car_params)
Explanation: Since, you're checking if the particular key i.e.: :car_model_id and transmission_id exists in params. The above code would be something like this when you have just :transimission_id in params:
Car.where(:transmission_id => '1')
or this when you have :car_model_id in params:
Car.where(:car_model_id => '3')
or this when you'll have both:
Car.where(:transmission_id => '1', :car_model_id => '3')
NOTE: This will work only when you have params keys as the column names for which you're trying to run queries for. If you intend to have a different key in params which doesn't match with the column name then I'd suggest you change it's key to the column name in controller itself before slice.
UPDATE: Since, OP has edited his question and introduced more if.. else conditions now. One way to go about solving that and to always keep one thing in mind is to have your user_params correct values for which you want to run your queries on the model class, here it's Car. So, in this case:
car_params = params.slice(:car_model_id, :transmission_id).reject{|k, v| v.nil? }
if params[:from_date].present? && params[:from_date].present?
car_params.merge!(date: params[:from_date]..params[:to_date])
end
and then:
Car.where(car_params)
what is best approach to avoid such bad code and check if parameter is
nil inline(in where)
Good Question !
I will make implementation with two extra boolean variables (transmission_id_is_valid and
car_model_id_is_valid)
transmission_id_is_valid = params[:car_model_id].nil? && !params[:transmission_id].nil?
car_model_id_is_valid = !params[:car_model_id].nil? && params[:transmission_id].nil?
if transmission_id_is_valid
return Car.where(:transmission_id => params[:transmission_id])
elseif car_model_id_is_valid
return Car.where(:car_model_id=> params[:car_model_id])
....
end
I think now is more human readable.
First, I would change this code to Car model, and I think there is no need to check if params doesn't exists.
# using Rails 4 methods
class Car < ActiveRecord::Base
def self.find_by_transmission_id_or_model_id(trasmission_id, model_id)
if transmission_id
find_by trasmission_id: trasmission_id
elsif model_id
find_by model_id: model_id
end
end
end
In controller:
def action
car = Car.find_by_transmission_id_or_model_id params[:trasmission_id], params[:car_model_id]
end
edit:
This code is fine while you have only two parameters. For many conditional parameters, look at ransack gem.

Mongoid dynamic query

This must be an easy one, but I'm stuck...
So I'm using Rails#3 with Mongoid and want to dynamically build query that would depend upon passed parameters and then execute find().
Something like
def select_posts
query = :all # pseudo-code here
if (params.has_key?(:author))
query += where(:author => params[:author]) # this is pseudo-code again
end
if (params.has_key?(:post_date))
query += where(:create_date => params[:post_date]) # stay with me
end
#post_bodies = []
Post.find(query).each do |post| # last one
#post_bodies << post.body
end
respond_to do |format|
format.html
format.json { render :json => #post_bodies }
end
end
You have a few different options to go with here - depending on how complex your actual application is going to get. Using your example directly - you could end up with something like:
query = Post.all
query = query.where(:author => params[:author]) if params.has_key?(:author)
query = query.where(:create_date => params[:post_date]) if params.has_key?(:post_date)
#post_bodies = query.map{|post| post.body}
Which works because queries (Criteria) in Mongoid are chainable.
Alternatively, if you're going to have lots more fields that you wish to leverage, you could do the following:
query = Post.all
fields = {:author => :author, :post_date => :create_date}
fields.each do |params_field, model_field|
query = query.where(model_field => params[params_field]) if params.has_key?(params_field)
end
#post_bodies = query.map{|post| post.body}
And finally, you can take it one level further and properly nest your form parameters, and name the parameters so that they match with your model, so that your params object looks something like this:
params[:post] = {:author => "John Smith", :create_date => "1/1/1970", :another_field => "Lorem ipsum"}
Then you could just do:
#post_bodies = Post.where(params[:post]).map{|post| post.body}
Of course, with that final example, you'd want to sanitize the input fields - to prevent malicious users from tampering with the behaviour.

Rails: finding matches that start with a string and do not end in another

I've got this little thing here:
def get_articles
#articles = []
Doc.column_names.each do |a|
if a.match(/^article/)
#articles << a
end
end
end
But it returns a lot of unwanted results. How would I go about discarding results it returns that end in a specific string (say, _body)?
Cheers!
How about:
if a.match(/^article/) and !a.match(/_body$/)
Incidentally, your method can be rewritten (more compactly) as:
def get_articles
#articles = Doc.column_names.select { |a| a.match(/^article/) && !a.match(/_body$/) }
end
You can also replace the dual match with a single match containing a zero-width negative look-behind assertion, but it is less readable for the majority of people (though about 2x as fast in a quick-and-dirty test):
def get_articles
#articles = Doc.column_names.select { |a| a.match(/^article.*(?<!_body)$/) }
end

Rails: how can I write this so it won't fail if it returns nil?

I'm just not sure how best to write it. Cheers!
def get_prices(c)
#print_prices = Billing.where(:name => c).first.attributes.select{ |i| i.match(/^print_/) }
end
One way to do it:
def get_prices(c)
billings = Billing.where(:name => c)
#print_prices = billings.first.attributes.select{ |i| i.match(/^print_/) } unless billings.empty?
end
you should store the return value of Billing.where(:name => c).first and test it.

how do I get rid of sql injection in this case

I have a method like this in my RoR 3 app
def buscar
array = params[:query].split(' ')
array.each_with_index do |query, index|
array[index] = array[index].gsub(/<\/?[^>]*>/, "").downcase
end
#noticias = Noticia.where(:tags.all => array).paginate(:page => params[:page])
end
I'm using brakeman to scan for any problems, and he says this
Possible SQL injection near line 116: Noticia.where(:tags.all => (params[:query].split(" ")))
How can I change the query to evict this problem?
Oh, i'm using mongoid
Thanks in advance
This is untested, but something like this:
tag = params[:query].split(" ")
tag.each do |tag|
#noticias << Noticias.find_by_tag(tag)
end
#noticias.paginate(:page => params[:page])
You may have to mess with the <<. I'm not sure what paginate looks for in the #noticias object.