looping over columns in a model using Ruby - ruby-on-rails

I'm trying to loop through all the columns of a model and (1) set the value to lowercase and (2) trim it but I can't seem to get the syntax right. This is what i have so far:
#response.attributes.each do |attr_name, attr_value|
#response."#{attr_name}".downcase.strip!
end
I've searched around and can't seem to find an example of actually setting the value of the model column. All the examples I find deal with displaying the value or field name of each column. In other languages there is an evaluate or eval function to do this but I can't seem to find the equivalent in Ruby.

You can use the write_attribute method to alter an ActiveRecord attribute by name
#response.attributes.each do |attr_name, attr_value|
#response.write_attribute( attr_name, attr_value.downcase.strip )
end
Outside of ActiveRecord framework it is common to use the send method to call a bunch of accessors by name. That would work here, too:
#response.attributes.each do |attr_name, attr_value|
setter = "#{attr_name}="
#response.send( setter, attr_value.downcase.strip )
end
However, the authors of ActiveRecord have foreseen this need, and the write_attribute syntax would be my recommendation here.

You should try this code:
#response.attributes.each do |attr_name, attr_value|
#response[attr_name.to_sym] = attr_value.to_s.downcase.strip
end
Then check #response. It will assign all the values with downcase and stripped in #response variable.

Related

Rails dynamic attribute in where LIKE clause

I have a search method, which takes in a key value pair in argument and searches on an active record model via a LIKE query. But I am unable to get it to work. It doesn't take the key argument properly.
This is what my method looks like:
def search(key,value)
where('? LIKE ?',key,"%#{value}%")
end
The query it fires is ('name' LIKE '%air%') whereas it should fire (name LIKE '%air%')
Is there a way I could get this to work?
Warning: The solution proposed by #MKumar is very dangerous. If key is user-input, you just allowed SQL injection.
def search(key, value)
where("#{key} LIKE ?", "%#{value}%")
end
search("IS_ADMIN == 1 --", "")
Whoops!
The better way to do this would be to use Arel tables.
def search(key, value)
column = Model.arel_table[key.to_sym] # index into the columns, via a symbol
where(column.matches("%#{value}%"))
end
This cannot produce a SQL injection.
Try like this
def search(key,value)
where("#{key} LIKE ?","%#{value}%")
end

How do I get several attributes value and method value from an ActiveRecord model?

To solve my problem, I set fictitious Car Model below:
Car Model has
3 attributes(id, car_name, owner_name) and
2 methods which return integers(riders, passengers).
I want to get ONE HASH which has the values of 2 attributes and 2 methods from all of cars. To solve this problem, my temporary solution is below:
json_format = Car.all.to_json(only: [:car_name, :owner_name], methods: [:riders, :passengers])
final_hash = ActiveSupport::JSON.decode(json_format)
This works, but this is bad because I use 'to_json' method only for its optional function.
Is their any other choice to getting the one hash directly from Car Model via its own optional function?
Use as_json. It's what to_json uses under the hood, and accepts all the same options but returns a Hash.
I feel the same as you that using the json commands for a method unrelated to json is poor practice.
If you don't mind being verbose, you could do something like this:
formatted_cars = Array.new
Car.all.each do |c|
formatted_cars.push( car_name: c.car_name,
owner_name: c.owner_name,
riders: c.riders,
passengers: c.passengers )
end
I find when hitting both attributes and methods it's cleanest just to specify the assignments like this. This is also the technique I would use in passing an object with its virtual attributes to javascript.
I agree that turning it into Json and back is pretty stupid. I would do this like so.
Car.all.collect{|car| hash = {}; [:car_name, :owner_name, :riders, :passengers].each{|key| hash[key] = car.send(key)}; hash}
it would be cleaner to put this into a method in the object
#in Car class
def stats_hash
hash = {}
[:car_name, :owner_name, :riders, :passengers].each do |key|
hash[key] = self.send(key)
end
hash
end
then do
Car.all.collect(&:stats_hash)

Dynamically creating hash key name in Rails 4

Is it possible to dynamically create key names of a hash? I'm passing the following hash parameters:
params[:store][:store_mon_open(5i)]
params[:store][:store_mon_closed(5i)]
params[:store][:store_tue_open(5i)]
params[:store][:store_tue_closed(5i)]
.
.
.
params[:store][:store_sun_open(5i)]
params[:store][:store_sun_closed(5i)]
To check if each parameter exists, I'm using two arrays:
days_of_week = [:mon, :tue, ..., :sun]
open_or_closed = [:open, :closed]
But, I can't seem to figure out how to dynamically create the params hash (the second key( with the array. Here's what I have so far:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
if !eval("params[:store][:store_#{day_of_week}_#{store_status}(5i)").nil
[DO SOMETHING]
end
end
end
I've tried a bunch of things including the eval method (as listed above) but rails seems to dislike the parentheses around the "5i". Any help is greatly appreciated!
You should be able to do
if params[:store]["store_#{day_of_week}_#{store_status}(5i)".to_sym]
Note that you were missing the ? on .nil? and that !object.nil? can be shortened to just object
Assuming this is a HashWithIndifferentAccess, you should be able to access it via string just as you could with a symbol. Thus:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
key = "store_#{day_of_week}_#{store_status}(5i)"
unless params[:store][key]
# DO SOMETHING
end
end
end
If it's not a HashWithIndifferentAccess then you should just be able to call key.to_sym to turn it into a symbol.

Getting the name of Ruby method for a literal hash query

In a rails application, I have a number of attributes for a model called Record. I want to design a method that when called on an attribute, returns the name of the attribute (which is essentially a method on the Record object). This name is then passed to an Hash, which returns a number (for the sake of this example, say the number is a percentage which is then multiplied by the original attribute value to get a new value).
For example, say my Record has four attributes: teachers, students, principals, and parents. The method would then look like the following:
def name
**something here**
end
and the corresponding new_value method and PRECENTAGE hash would look like this:
def new_value
self * PERCENTAGE[self.name]
end
PERCENTAGE = {
"teachers" => 0.40,
"students" => 0.53,
"principals" => 0.21,
"parents" => 0.87
}
Then, to execute this whole thing, I would do Record.students.new_value, which would return new number of students according to the percentage obtained in the hash.
I know that to get the name of a method that is currently executing, you can do something like this: (found on http://ryat.la/7RDk)
def this_method
__method__
end
but that won't work for me, because I need the name of the previously executed method.
If you have any suggestions as to an alternative approach to accomplishing my goal, I'd be happy to try something else.
Ryan, I'm struggling to understand your question, but I think this is what you want, for record.teachers_percent, for example:
["teachers", "students", "principals", "parents"].each do |attrib|
Record.class_eval <<-RUBY
def #{attrib}_percent
#{attrib} * PERCENTAGE[#{attrib.inspect}]
end
RUBY
end
Although this is probably a cleaner solution, giving record.percent(:teachers) or record.percent("teachers"):
class Record
def percent(attrib)
self.send(attrib) * PERCENTAGE[attrib.to_s]
end
end

Rails 3 check if attribute changed

Need to check if a block of attributes has changed before update in Rails 3.
street1, street2, city, state, zipcode
I know I could use something like
if #user.street1 != params[:user][:street1]
then do something....
end
But that piece of code will be REALLY long. Is there a cleaner way?
Check out ActiveModel::Dirty (available on all models by default). The documentation is really good, but it lets you do things such as:
#user.street1_changed? # => true/false
This is how I solved the problem of checking for changes in multiple attributes.
attrs = ["street1", "street2", "city", "state", "zipcode"]
if (#user.changed & attrs).any?
then do something....
end
The changed method returns an array of the attributes changed for that object.
Both #user.changed and attrs are arrays so I can get the intersection (see ary & other ary method). The result of the intersection is an array. By calling any? on the array, I get true if there is at least one intersection.
Also very useful, the changed_attributes method returns a hash of the attributes with their original values and the changes returns a hash of the attributes with their original and new values (in an array).
You can check APIDock for which versions supported these methods.
http://apidock.com/rails/ActiveModel/Dirty
For rails 5.1+ callbacks
As of Ruby on Rails 5.1, the attribute_changed? and attribute_was ActiveRecord methods will be deprecated
Use saved_change_to_attribute? instead of attribute_changed?
#user.saved_change_to_street1? # => true/false
More examples here
ActiveModel::Dirty didn't work for me because the #model.update_attributes() hid the changes. So this is how I detected changes it in an update method in a controller:
def update
#model = Model.find(params[:id])
detect_changes
if #model.update_attributes(params[:model])
do_stuff if attr_changed?
end
end
private
def detect_changes
#changed = []
#changed << :attr if #model.attr != params[:model][:attr]
end
def attr_changed?
#changed.include :attr
end
If you're trying to detect a lot of attribute changes it could get messy though. Probably shouldn't do this in a controller, but meh.
Above answers are better but yet for knowledge we have another approch as well,
Lets 'catagory' column value changed for an object (#design),
#design.changes.has_key?('catagory')
The .changes will return a hash with key as column's name and values as a array with two values [old_value, new_value] for each columns. For example catagory for above is changed from 'ABC' to 'XYZ' of #design,
#design.changes # => {}
#design.catagory = 'XYZ'
#design.changes # => { 'catagory' => ['ABC', 'XYZ'] }
For references change in ROR

Resources