How to Create an Object Who Act as a False in Ruby

How to create an Object who act as a False in Ruby

Unfortunately, this is not possible.

This is one of those annoying cases where Ruby is not object-oriented. In OO, it must be possible for one object to simulate another (in fact, depending on whom you ask, this is the very definition of OO – remember that OO came out of simulation), but it is not possible to build an object which simulates false.

This is because, in Ruby, conditional control structures are baked into the language and don't translate into message sends, whereas in other OO languages they are just regular message sends (or at least translate into message sends, just like for in Ruby translates into each). For example, in Smalltalk, Booleans are actually implemented using the Church encoding of Booleans you know from Lambda Calculus, and translated to Ruby they look a bit like this:

class FalseClass
def if(&block)
# do nothing
end

def if_then_else(then_lambda, else_lambda)
else_lambda.()
end

def not
true
end

def and(&block)
self
end

def or(&block)
block.()
end
end

And TrueClass is just the mirror image:

class TrueClass
def if(&block)
block.()
end

def if_then_else(then_lambda, else_lambda)
then_lambda.()
end

def not
false
end

def and(&block)
block.()
end

def or(&block)
self
end
end

And then, instead of something like

if 2 < 3 then foo end
if 2 < 3 then bar else baz end

You would have

(2 < 3).if { foo }
(2 < 3).if_then_else(-> { bar }, -> { baz })

# using the new keyword arguments in Ruby 2.0, it almost reads like Smalltalk:
class FalseClass
def if(then: -> {}, else: -> {})
else.()
end
end

class TrueClass
def if(then: -> {}, else: -> {})
then.()
end
end

(2 < 3).if(then: -> { bar }, else: { baz })

That way, you can easily create an object which simulates false simply by implementing the respective methods.

In other cases, where some object really absolutely must be an instance of a specific class and not just speak the correct protocol, Ruby provides an escape hatch. For example, if a method really requires an Array as an argument, then it will first try to call to_ary to at least give you a chance to convert your object into an Array. The same goes for to_str, to_int, to_proc, to_float etc. But there is no equivalent to_bool protocol.

Return true/false on creation of object

You could try

@object = Blah.create()
@object.persisted? # true or false

also if you want to throw an error instead you could use the bang version, be sure to use rescue

@object = Blah.create!()

Ruby's 'is_a?' is returning false for built-in types, what's happening?

I wrote another answer, but one major question is - why are you doing this? Why not, instead, just call flatten on the array so you just get the entries? Or, check for the behavior of the objects instead. You might need to give more detail about what you require.

Ruby nil-like object

This is a pretty long answer with a bunch of ideas and code samples of how to approach the problem.

try

Rails has a try method that let's you program like that. This is kind of how it's implemented:

class Object
def try(*args, &b)
__send__(*a, &b)
end
end

class NilClass # NilClass is the class of the nil singleton object
def try(*args)
nil
end
end

You can program with it like this:

fizz.try(:buzz).try(:foo).try(:bar)

You could conceivably modify this to work a little differently to support a more elegant API:

class Object
def try(*args)
if args.length > 0
method = args.shift # get the first method
__send__(method).try(*args) # Call `try` recursively on the result method
else
self # No more methods in chain return result
end
end
end
# And keep NilClass same as above

Then you could do:

fizz.try(:buzz, :foo, :bar)

andand

andand uses a more nefarious technique, hacking the fact that you can't directly instantiate NilClass subclasses:

class Object
def andand
if self
self
else # this branch is chosen if `self.nil? or self == false`
Mock.new(self) # might want to modify if you have useful methods on false
end
end
end

class Mock < BasicObject
def initialize(me)
super()
@me = me
end
def method_missing(*args) # if any method is called return the original object
@me
end
end

This allows you to program this way:

fizz.andand.buzz.andand.foo.andand.bar

Combine with some fancy rewriting

Again you could expand on this technique:

class Object
def method_missing(m, *args, &blk) # `m` is the name of the method
if m[0] == '_' and respond_to? m[1..-1] # if it starts with '_' and the object
Mock.new(self.send(m[1..-1])) # responds to the rest wrap it.
else # otherwise throw exception or use
super # object specific method_missing
end
end
end

class Mock < BasicObject
def initialize(me)
super()
@me = me
end
def method_missing(m, *args, &blk)
if m[-1] == '_' # If method ends with '_'
# If @me isn't nil call m without final '_' and return its result.
# If @me is nil then return `nil`.
@me.send(m[0...-1], *args, &blk) if @me
else
@me = @me.send(m, *args, &blk) if @me # Otherwise call method on `@me` and
self # store result then return mock.
end
end
end

To explain what's going on: when you call an underscored method you trigger mock mode, the result of _meth is wrapped automatically in a Mock object. Anytime you call a method on that mock it checks whether its not holding a nil and then forwards your method to that object (here stored in the @me variable). The mock then replaces the original object with the result of your function call. When you call meth_ it ends mock mode and returns the actual return value of meth.

This allows for an api like this (I used underscores, but you could use really anything):

fizz._buzz.foo.bum.yum.bar_

Brutal monkey-patching approach

This is really quite nasty, but it allows for an elegant API and doesn't necessarily screw up error reporting in your whole app:

class NilClass
attr_accessor :complain
def method_missing(*args)
if @complain
super
else
self
end
end
end
nil.complain = true

Use like this:

nil.complain = false
fizz.buzz.foo.bar
nil.complain = true

Why do Ruby object_ids for true, false, and nil seem to differ in Windows vs. Ubuntu?

As the accepted answer on the question you linked to says, 0, 20, and 8 are the Object IDs that are used when using flonums on YARV.

So, obviously, your Windows implementation is not using flonums. The most likely reason is that either you are not using YARV (e.g. using JRuby or Rubinius), or you are using YARV, but a 32 bit version.

Note: Object IDs are a private internal implementation detail of the particular Ruby implementation. The version of Ruby you are using is completely irrelevant. What is relevant is which implementation you are using, which version, which commandline options, how you compiled it, what your environment is and so on. It has nothing to do with the language.

Note also: you should never ever rely on the specific value of Object IDs. Object IDs guarantee two things, and only these two things:

  1. An object does not change its Object ID during its lifetime.
  2. No two objects have the same Object ID at the same time.

Here are some things that are not guaranteed:

  • It is not guaranteed that an object will have the same Object ID during different runs of the program.
  • It is not guaranteed that an Object ID is unique during the runtime of the program, it may be re-used for different objects as long as those objects don't live at the same time.
  • It is not guaranteed that the Object ID will follow a certain pattern, e.g. that it is always the memory address of the object (not true on JRuby, for example) or that it is always a specific value (not true for nil and false, as you have just discovered), or that it is always related to the object's value in some way (due to the specific way that YARV optimizes Integers, the Object ID for small integers will always be 2n+1, but that is only true for small integers (where the definition of "small" depends on whether you run a 64 bit or 32 bit version of YARV) and it is only an implementation detail that may change at any moment without notice).

How does Ruby evaluate the class of an object when evaluating the truthiness in a condition?

There are only two objects which evaluate falsy. Ruby simply checks whether the object is one of those two. Period.

Note that one of the foundations of OO is simulation: an object can simulate another object. This is not true here: it is impossible to simulate nil or false. This is one of those annoying cases where Ruby violates OO, very similar to Object#equal?.

What evaluates to false in Ruby?

false and nil are the only ones:

http://www.ruby-doc.org/core-2.1.1/FalseClass.html

Rails provides present? which also includes empty strings and empty arrays: http://api.rubyonrails.org/classes/Object.html#method-i-present-3F



Related Topics



Leave a reply



Submit