Sub-classing Fixnum in ruby
You can pretty easily set up a quick forwarding implementation yourself:
class MyNum
def initialize(number)
@number = number
end
def method_missing(name, *args, &blk)
ret = @number.send(name, *args, &blk)
ret.is_a?(Numeric) ? MyNum.new(ret) : ret
end
end
Then you can add whatever methods you want on MyNum, but you'll need to operate on @number in those methods, rather than being able to call super directly.
12345.class returning 'Integer' not 'Fixnum' in Ruby
It depends on the Ruby version. From Ruby 2.4.0 we have just Integer
s, no more Fixnum
s and Bignum
s distinction
https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/
Mimic another Ruby class so the object passes the === type check
This is a hackish solution that I warned against in my question:
Fixnum === page #=> false
Numeric.extend Module.new {
def ===(obj)
obj.instance_of?(PageNumber) or super
end
}
Fixnum === page #=> true
It solves the problem but raises a question is it safe to do? I can't think of any drawbacks of this method from the top of my mind but since we're messing with a very important method here it might not be something we'd want to do.
Why can't I extend the Fixnum class in a module and use it?
There are three problems with your code:
You are creating a new class
EanControl::Fixnum
, but you actually want to change the existing builtin::Fixnum
. Solution: explicitly start the constant lookup from the top-level, or, more idiomatically, just drop the module.module EanControl
class ::Fixnum
# …
end
end
# although it would be much simpler to just do this:
class Fixnum
# …
endYou define
roundup
as a singleton method of the objectFixnum
, but you call it as an instance method of instances ofFixnum
. Solution: makeroundup
an instance method:class Fixnum
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
endThe Ruby Language Specification does not actually guarantee that there even is a
Fixnum
class. It only guarantees that there is anInteger
class, and it allows that different implementations may provide implementation-specific subclasses. (E.g. YARV hasFixnum
andBignum
subclasses ofInteger
.) Since you only add the method toFixnum
, it won't work for otherInteger
s, which aren'tFixnum
s. And since the range ofFixnum
s is different for different implementations of architectures (e.g. on YARV on 32 bit systems,Fixnum
s are 31 bit, on 64 bit systems, they are 63 bit, on JRuby, they are always 64 bit), you don't even know for sure what numbers your method will work on and when it will fail. (E.g.:9223372036854775808.roundup # NoMethodError: undefined method 'roundup' for 9223372036854775808:Bignum
.) Solution: make the method an instance method ofInteger
:class Integer
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
Lastly, I want to suggest at least using a mixin here:
module IntegerWithRoundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
class Integer
include IntegerWithRoundup
end
Now, if someone else debugs your code, and wonders where this roundup
method comes from, there is a clear trace in the ancestry chain:
12.method(:roundup).owner
# => IntegerWithRoundup
Even better would be to use a Refinement, that way your monkeypatch doesn't pollute the global namespace:
module IntegerWithRoundup
module Roundup
def roundup
return self if (self % 10).zero? # already a factor of 10
self + 10 - (self % 10) # go to nearest factor 10
end
end
refine Integer do
include Roundup
end
end
12.roundup
# NoMethodError: undefined method `roundup' for 12:Fixnum
using IntegerWithRoundup
12.roundup
# => 20
What is the difference between Integer and Fixnum?
You never "use" Integer
. It is an abstract class whose job is to endow its children (Fixnum
and Bignum
) with methods. Under effectively no circumstances will you ever ask for an object's class and be told that it is an Integer
.
How to override Ruby + method?
It seems you use ruby < 2.4. If so you want to patch Fixnum
and not Integer
. Be careful as the system itself uses numbers as well.
class Fixnum
alias_method :add, :+
def +(other)
puts 'plus method overridden'
add(other)
end
end
puts 5 + 9
Case statement for class objects
Something more functional?
is_descendant = lambda { |sample, main| main <= sample }
not_a_class = lambda { |x| !x.kind_of?(Class) }
mine = Fixnum
case mine
when not_a_class then raise 'Not a class' # Credits to @brymck
when is_descendant.curry[Float] then puts 'Float'
when is_descendant.curry[Integer] then puts 'Integer'
else raise 'Shit happens!'
end
# ⇒ Integer
Besides Float and Fixnum, are there any other objects that can't be created with *.new?
I guess immediate values are like that: true
, false
, nil
.
Related Topics
Sprockets::Circulardependencyerror in Store#Index
How to Use Watir::Waiter::Wait_Until to Force Chrome to Wait
Case-Insensitive Array#Include
Ruby: Sum Corresponding Members of Two or More Arrays
Class Method VS Constant in Ruby/Rails
Rspec: How to Stub an Instance Method Called by Constructor
Conditional Key/Value in a Ruby Hash
In Ruby What Does "=>" Mean and How Does It Work
Get Absolute (Base) Url in Sinatra
Retrieving a Braintree Customer's Subscriptions
Git Deployment + Configuration Files + Heroku
Rails 6.1, Ruby 3.0.0: Tests Error as They Cannot Load Rexml