print comparison

M

Mario Ruiz

is it possible to print the comparison given as parameter in a method?

something like this:

mi(4==3)
def mi(param)
I want to know here that the param was 4==3
end


I know that I could pass mi("4==3") and in the method use eval(param)
but that's not possible in my case, I need to give a real comparison.
 
K

Kirk Haines

is it possible to print the comparison given as parameter in a method?

something like this:

mi(4=3D=3D3)
def mi(param)
=A0I want to know here that the param was 4=3D=3D3
end


I know that I could pass mi("4=3D=3D3") and in the method use eval(param)
but that's not possible in my case, I need to give a real comparison.

mi(4=3D=3D3) sees the 4=3D=3D3 evaluated before #mi is called. So, the met=
hod
call ends up being mi(false).

Consider the following:

def mi(bool)
puts bool
end

mi(4=3D=3D3)


If you look at the AST that is generated from that code, this is what you g=
et:


s:)block,
s:)defn,
:mi,
s:)args, :bool),
s:)scope, s:)block, s:)call, nil, :puts, s:)arglist, s:)lvar, :bool))))))=
,
s:)call,
nil,
:mi,
s:)arglist, s:)call, s:)lit, 4), :=3D=3D, s:)arglist, s:)lit, 3))))))


Pay particular attention to the second part. I have reformatted it to
emphasize a few things:


s:)call, nil, :mi,
s:)arglist,
s:)call, s:)lit, 4), :=3D=3D,
s:)arglist, s:)lit, 3))))))


Look at the 3rd and 4th lines there. The #=3D=3D method is being called on
4, with an arglist composed of 3. That method call happens first, and
the result of it is the argument placed into the #mi method call's
arglist.

Depending on your actual use case, and on the ruby version that you
are using, you may be about to use ParseTree (gem install ParseTree)
to get the information that you want, but you might get better
guidance if you explain what your goal in wanting to do this actually
is.


Kirk Haines
Software Engineer
Engine Yard
 
M

Mario Ruiz

Well what I need to do is simple I need to store the comparison in a log
file and use the result of the comparison to return true or false on
that method.

Kirk Haines wrote in post #980846:
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

is it possible to print the comparison given as parameter in a method?

something like this:

mi(4==3)
def mi(param)
I want to know here that the param was 4==3
end


I know that I could pass mi("4==3") and in the method use eval(param)
but that's not possible in my case, I need to give a real comparison.
Why isn't it possible? I'll assume because you aren't dealing with literals,
so you need to pass the references to the actual objects. If so, you could
also pass the binding:



Person = Struct.new :name , :age do
def <=>(person)
age <=> person.age
end
include Comparable
end

bill = Person.new 'Bill' , 25
jenn = Person.new 'Jenn' , 17
carl = Person.new 'Carl' , 25



def mi( comparison , binding )
log comparison
eval comparison , binding
end

alias :log :puts


# binding: http://ruby-doc.org/core/classes/Kernel.html#M001448
mi "bill == jenn" , binding # => false
mi "bill == carl" , binding # => true
mi "jenn == carl" , binding # => false

# logged:
# >> bill == jenn
# >> bill == carl
# >> jenn == carl




And as always, with eval, be careful: know where your input code is coming
from, and be sure you can trust that source.
 
M

Mario Ruiz

I have to deal directly with the comparison and not with literals
because a third party is calling my log method and they are not going to
change the calls and they will be passing comparisons all time instead
of literals.

Josh Cheek wrote in post #980886:
 
K

Kirk Haines

I have to deal directly with the comparison and not with literals
because a third party is calling my log method and they are not going to
change the calls and they will be passing comparisons all time instead
of literals.

I'm repeating myself, but....

a == b

can be rewritten as

a.==(b)

It's just calling the #== method on 'a'.

mi(4==3)

is the same thing as

mi(4.==(3))

When your #mi method is called, it ends up looking something like this:

s:)call, nil, :mi, s:)arglist, s:)false)))

i.e. it gets 'false' as it's sole argument.

There is no good solution to what you want to do.

A very bad solution might be something like this:

irb(main):001:0> class Fixnum
irb(main):002:1> def ==(val)
irb(main):003:2> $last_comparison = [self, :==, val]
irb(main):004:2> super
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> 1 == 2
=> false
irb(main):008:0> $last_comparison
=> [1, :==, 2]

i.e. override each of the methods on the objects that will be involved
in the comparisons to record their calls.

Then in your #mi method, you can use that record.

It's bad because you should always be very reluctant to start changing
the operation of core things like comparison methods on Fixnum, but it
would achieve your goal.


Kirk Haines
Software Engineer
Engine Yard
 
B

Brian Candler

Something you could consider is passing a block:

def mi(&blk)
res = blk.call
end

mi { 4 == 3}

But that would only help if you were running in a Ruby environment where
you could convert the block back into its AST.

Or have a look at how rspec does it:

4.should == 3

That is, define Object#should which returns a wrapper object which
implements the comparison plus whatever side effects you want.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,815
Latest member
treekmostly22

Latest Threads

Top