is_a? returns false, though I think it should return true

64 views
Skip to first unread message

Ronald Fischer

unread,
Aug 30, 2014, 9:21:48 AM8/30/14
to rubyonra...@googlegroups.com
Rails 4, Ruby 2

Model has Card and Idiom
and Idiom belongs_to :card

In my Rails console I have an Idiom i and get the card from it:

c=i.card

=> #<Card id: 19, ....>

Now I do:

c.is_a?(Card)
=> false

c.instance_of?(Card)
=> false

Ooops! Why false in both cases?

c.class.name
=> "Card"
c.class.name==Card.name
=> true

The class name is the same, but the class isn't? Please explain....

And now an explanation why I want to do it:

I have a function which is "overloaded", in that it's single argument
can be either of a certain clas (in my case, Card (or a subclass of
it)), or a Fixnum, and I need to distinguish between these cases.

I know that it is not the best style of querying the type of a variable
at runtime, but the only alternatives I could think of, would be to
either provide two differently named functions, or have an hash argument
with two types of keywords, and I don't like both of these solutions.
Aside from this, I'm really curious to know why is_a? doesn't work
here.

--
Posted via http://www.ruby-forum.com/.

Colin Law

unread,
Aug 30, 2014, 10:54:07 AM8/30/14
to rubyonra...@googlegroups.com
I *think* it is because it is actually an ActiveRecord Association,
which is converted for you into a Card when you use it (and this may
involve querying the database for example). So in the console it sees
you need to display c.card so it fetches it from the database (if it
has not already been fetched) and displays it as a Card. Apparently
(and I am postulating here) when you do c.class it becomes a card in
order to call the method class on it, but when you pass it to is_a? it
does not (since it does not know until inside is_a? whether it needs
to evaluate it). I may be talking rubbish here in which case someone
more knowledgeable about the internal workings of Rails will jump in.

Colin

Ronald Fischer

unread,
Aug 30, 2014, 12:52:54 PM8/30/14
to rubyonra...@googlegroups.com
This certainly sounds reasonable, not rubbish at all.

I think I was begging for trouble when I wanted to test the class
membership, which certainly is not in spirit with ActiveRecord and/or
Duck Typing OOP....

I guess the solution I'm trying is "too dirty", and I just was bitten by
it.

Ronald

Richard Wilson

unread,
Sep 1, 2014, 1:27:47 AM9/1/14
to rubyonra...@googlegroups.com
As colin mentioned, this is an active record relation that looks like a card. I believe it's actually an association proxy (I dont have the rails code open here)

Also, in rails, best practice is to operate on duck types, not strict types. You should never have to is_a? anything. This hints at design flaws within your application.

Remember, .class is simply a method defined on an object and can be overriden too.

Ronald Fischer

unread,
Sep 1, 2014, 7:11:16 AM9/1/14
to rubyonra...@googlegroups.com
> This hints at design flaws within your application.

I suspect this too, though I can't see a *convincing* better solution.

I try to sketch my problem in an abstract way:

I have a function f which, when receiving a Card object, can find a
certain integer number which it then uses for further calculation:

def f(a,b,c)
x=g(c) # c is a Card, x is a positive integer
... # do something with a, b, x
end

Occasionally, I am calling f in a loop, and I know from the context (I
could actually make this an assertion) that g(c) MUST be the same in
each iteration, even though the c is sometimes different. Hence I want
to precompute the value of g(c) and pass the integer to f.

If it were C++, I would implement this providing to overloaded
definitions of f.

As for Ruby, I see so far four possibilities:

(1) Query the type of c at runtime
(2) Require, that c is an integer, and calculate g(c) always at the
calling site, i.e. f(a,b,g(c))
(3) Provide two differently named functions, one expecting a Card and
one expecting an integer.
(4) Make g a method of Card, and add a g method to Fixnum, which just
returns the number unchanged.

I agree with you, that (1) is ugly, because we don't want to do runtime
tests.

I don't like (2) and (3) either, because it makes f more inconvenient to
use.

(4) has the advantage, that we can write inside f simply:
x=c.g
This would be duck typing at work, but extending such a basic class as
Fixnum by a function, which doesn't have any semantic on its own,
doesn't look like good design either.

What approach would you suggest?

Ronald

Iazel

unread,
Sep 6, 2014, 3:54:59 PM9/6/14
to rubyonra...@googlegroups.com
I think the cleanest solution is to use two function, one that use Fixnum and another for the Card.
Obviously, the latter will internally use the first:

def fn(a, b, n)
  # do stuff
end

def fc(a, b, c)
  fn(a, b, g(c))
end

However, if you still don't like this, you can just check if the last parameter is a Fixnum (duck typing style) and act accordingly:

 x = n.respond_to?(:to_int) ? n.to_int : g(c)

When you have a lot of case, is better to use others technique, like a module whose only responsibility is to convert this or that thing; you can go even further, using a module for each different case

Ronald Fischer

unread,
Sep 7, 2014, 4:06:45 AM9/7/14
to rubyonra...@googlegroups.com
Iazel wrote in post #1157000:
> However, if you still don't like this, you can just check if the last
> parameter is a Fixnum (duck typing style) and act accordingly:
>
> x = n.respond_to?(:to_int) ? n.to_int : g(c)

Isn't this a bit risky? After all, Card the class of n derives from
ActiveRecord::Base, i.e. a class, which I can't control. Now imagine
that in a new version of Rails, this class would receive a to_int method
(for example, to implicitly convert a model object into the id of the
object). This would break my code.
Reply all
Reply to author
Forward
0 new messages