How can i refactor this Ruby, Rails code? - ruby-on-rails

I'm new at Rails... Is there a better way to refactor this code:
def get_product_price_minimum
Product.minimum(:price).to_i
end
def get_product_price_maximum
Product.maximum(:price).to_i
end

You can define something like "prices" (a vague method name, to avoid using get_ or set_ prefixes) to expect an argument, which would be the maximum or minimum for which to query your model:
def prices(what)
Product.public_send(what, :price).to_i
end
Then you can use it by passing the minimum or maximum as a symbol or a string.

I'm not sure your function works
but you can try (if price is column and price is a int not a string)
def get_product_price_minimum
Product.order('price').last
end

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 to create method that converts attribute's value to new value

I have a form in which users input a number for the attribute :bytesize, which has an integer datatype. The number represents the amount of bytes for my object #catcher.
I'd like to have a method that will convert the value of :bytesize to megabytes. That is, I'd like to be able to run #catcher.mbsize, and that will display the number of megabytes for that object.
I'm pretty new to Rails, so my apologies if this seems obvious.
Conversion methods are pretty straight-forward:
class Catcher
def mbsize
self.bytesize / (1 << 20)
end
end
Remember that attributes are internally stored as instance variables, so attr_accessor :bytesize is stored in #bytesize.
You need to add mbsize column to your db.
In controller:
def create
#other code
def mbsize
#bytesize / (1 << 20)
end
#catcher.mbsize=mbsize
#cather.save
end
EDIT:
If you don't need related DB record, you can simply define this method in Catcher model:
def mbsize
bytesize=self.bytesize
mbsize=#your method of converting
end
By some reason your bytesize is sting. You can convert it into integer by .to_i method

In a Rails helper method, is it possible to specify a column name as a helper method variable and then use it?

I want to create a helper method that can turn the results of a Rails find into a sentence, where I specify the the results, and the column to use for making the sentence. For example:
def items_to_sentence(items, label_column)
items.map { |u| u.(label_column) }.to_sentence
end
I'm just not sure how to tell Rails to use my specified column.
Thanks for looking.
If items contains ActiveRecord objects (or any other objects that have accessor methods that match up with your column names), then you could use send:
def items_to_sentence(items, label_column)
items.map { |u| u.send(label_column) }.to_sentence
end
Or equivalently:
def items_to_sentence(items, label_column)
items.map(&(label_column.to_sym)).to_sentence
end
Or, if that's too noisy:
def items_to_sentence(items, label_column)
sym = label_column.to_sym
items.map(&sym).to_sentence
end

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

How do I convert a string to a class method?

This is how to convert a string to a class in Rails/Ruby:
p = "Post"
Kernel.const_get(p)
eval(p)
p.constantize
But what if I am retrieving a method from an array/active record object like:
Post.description
but it could be
Post.anything
where anything is a string like anything = "description".
This is helpful since I want to refactor a very large class and reduce lines of code and repetition. How can I make it work?
Post.send(anything)
While eval can be a useful tool for this sort of thing, and those from other backgrounds may take to using it as often as one might a can opener, it's actually dangerous to use so casually. Eval implies that anything can happen if you're not careful.
A safer method is this:
on_class = "Post"
on_class.constantize.send("method_name")
on_class.constantize.send("method_name", arg1)
Object#send will call whatever method you want. You can send either a Symbol or a String and provided the method isn't private or protected, should work.
Since this is taged as a Ruby on Rails question, I'll elaborate just a little.
In Rails 3, assuming title is the name of a field on an ActiveRecord object, then the following is also valid:
#post = Post.new
method = "title"
#post.send(method) # => #post.title
#post.send("#{method}=","New Name") # => #post.title = "New Name"
Try this:
class Test
def method_missing(id, *args)
puts "#{id} - get your method name"
puts "#{args} - get values"
end
end
a = Test.new
a.name('123')
So the general syntax would be a.<anything>(<any argument>).

Resources