Reputation: 103
I have a Measure
model with a value
attribute who needs to be able to handle these kind of values: 1
, 0.11111
, >=1
, <=0.11111
, >1
, <1
. The logic here is that sometimes I have a discrete value and sometimes a range, depending of what is measured.
I would like to find a solution to store the values while keeping the information related to their relational operator. Also, I would prefer a solution where calculations are made at the database level instead than at the application level.
Upvotes: 0
Views: 339
Reputation: 32943
Add a new field to your measure model to store the relational type as a string (eg called 'comparison_method'), like ">=", "<" etc. You could add validation to make sure it's in the acceptable list of comparison operators. Store value
as a float.
When it comes to testing, bear in mind that "<=", "<", ">=", ">", "==" and "!=" are just methods, like anything else in ruby. There's some special syntactic sugar which allows you to say
2 >= 1.2
but this is actually equivalent to calling a method called ">=" on 2, and passing it 1.2 - like doing this, in other words:
2.>=(1.2)
Another way to call methods in ruby is to use the "send" method. For example instead of saying
@user.name
you can say
@user.send("name")
and instead of saying
@user.name = "foo"
you can say
@user.send("name=", "foo")
So, in our case that means that we can say that
2.>=(1.2)
is equivalent to
2.send(">=", 1.2)
So, with your saved value and comparison_method, you can test these against another number like so: let's say that @measure has value = "1.111" and comparison_method = ">=". Let's say you have some other number @foo (equal to 2, for example) and you want to test if @foo passes the test in @measure. You would say, for example in the controller:
if @foo.send(@measure.comparison_method, @measure.value)
which is like saying
if @foo >= 1.1111
but in a data-driven way.
You could put the above code into an instance method in Measure, like so:
def passes(x)
x.send(self.comparison_method, self.value)
end
Now you can say, in the controller,
if @measure.passes(@foo)
which looks cleaner and is more DRY.
EDIT - incorporating the above into a database search, to do the test at the db level
#load a Measure, eg via params[:id]
@measure = Measure.find(params[:id])
#let's say that @measure.value = "1.111" and @measure.comparison_method = ">="
records = Foo.find(:all, :conditions => ["bar #{@measure.comparison_method} #{@measure.value}"])
#which is like saying
#@foos = Foo.find(:all, :conditions => ["bar >= 1.111"])
Upvotes: 1