Where Is the Best Place to Add Methods to the Integer Class in Rails

Where is the best place to add methods to the Integer class in Rails?

If you have your heart set on mucking with the Numeric (or integer, etc) class to get unit conversion, then at least do it logically and with some real value.

First, create a Unit class that stores the unit type (meters,feet, cubits, etc.) and the value on creation. Then add a bunch of methods to Numeric that correspond to the valid values unit can have: these methods will return a Unit object with it's type recorded as the method name. The Unit class would support a bunch of to_* methods that would convert to another unit type with the corresponding unit value. That way, you can do the following command:

>> x = 47.feet.to_meters
=> 14.3256
>> x.inspect
=> #<Unit 0xb795efb8 @value=14.3256, @type=:meter>

The best way to handle it would probably be a matrix of conversion types and expressions in the Unit class, then use method_missing to check if a given type can be converted to another type. In the numeric class, use method_missing to ask Unit if it supports the given method as a unit type, and if so, return a unit object of the requested type using the numeric as its value. You could then support adding units and conversions at runtime by adding a register_type and register_conversion class method to Unit that extended the conversion matrix and Numeric would "automagically" pick up the ability.

As for where to put it, create a lib/units.rb file, which would also contain the monkey_patch to Numeric, then initialize it in config/environment.rb bu requiring the lib/units.rb file.

Including our method to the integer class

You can simply do something like this, without using a module:

class Fixnum
def lcd(nr)
self * nr
end
end

puts 3.lcd(2) # -> 6

How to get integer value while adding new method to Integer class

Using self:

class Integer
def foo
'foo' * self
end
end
#It should work like:

p 3.foo
#=> 'foofoofoo'

You can also use Kernel#__method__ for a more general approach:

class Integer
def foo
__method__.to_s * self
end
end

p 3.foo
#=> 'foofoofoo'

Why can't I overwrite self in the Integer class?

You cannot change the value of self

An object is a class pointer and a set of instance methods (note that this link is an old version of Ruby, because its dramatically simpler, and thus better for explanatory purposes).

"Pointing" at an object means you have a variable which stores the object's location in memory. Then to do anything with the object, you first go to the location in memory (we might say "follow the pointer") to get the object, and then do the thing (e.g. invoke a method, set an ivar).

All Ruby code everywhere is executing in the context of some object. This is where your instance variables get saved, it's where Ruby looks for methods that don't have a receiver (e.g. $stdout is the receiver in $stdout.puts "hi", and the current object is the receiver in puts "hi"). Sometimes you need to do something with the current object. The way to work with objects is through variables, but what variable points at the current object? There isn't one. To fill this need, the keyword self is provided.

self acts like a variable in that it points at the location of the current object. But it is not like a variable, because you can't assign it new value. If you could, the code after that point would suddenly be operating on a different object, which is confusing and has no benefits over just using a variable.

Also remember that the object is tracked by variables which store memory addresses. What is self = 2 supposed to mean? Does it only mean that the current code operates as if it were invoked 2? Or does it mean that all variables pointing at the old object now have their values updated to point at the new one? It isn't really clear, but the former unnecessarily introduces an identity crisis, and the latter is prohibitively expensive and introduce situations where it's unclear what is correct (I'll go into that a bit more below).

You cannot mutate Fixnums

Some objects are special at the C level in Ruby (false, true, nil, fixnums, and symbols).

Variables pointing at them don't actually store a memory location. Instead, the address itself stores the type and identity of the object. Wherever it matters, Ruby checks to see if it's a special object (e.g. when looking up an instance variable), and then extracts the value from it.

So there isn't a spot in memory where the object 123 is stored. Which means self contains the idea of Fixnum 123 rather than a memory address like usual. As with variables, it will get checked for and handled specially when necessary.

Because of this, you cannot mutate the object itself (though it appears they keep a special global variable to allow you to set instance variables on things like Symbols).

Why are they doing all of this? To improve performance, I assume. A number stored in a register is just a series of bits (typically 32 or 64), which means there are hardware instructions for things like addition and multiplication. That is to say the ALU, is wired to perform these operations in a single clock cycle, rather than writing the algorithms with software, which would take many orders of magnitude longer. By storing them like this, they avoid the cost of storing and looking the object in memory, and they gain the advantage that they can directly add the two pointers using hardware. Note, however, that there are still some additional costs in Ruby, that you don't have in C (e.g. checking for overflow and converting result to Bignum).

Bang methods

You can put a bang at the end of any method. It doesn't require the object to change, it's just that people usually try to warn you when you're doing something that could have unexpected side-effects.

class C
def initialize(val)
@val = val # => 12
end # => :initialize

def bang_method!
"My val is: #{@val}" # => "My val is: 12"
end # => :bang_method!
end # => :bang_method!

c = C.new 12 # => #<C:0x007fdac48a7428 @val=12>
c.bang_method! # => "My val is: 12"
c # => #<C:0x007fdac48a7428 @val=12>

Also, there are no bang methods on integers, It wouldn't fit with the paradigm

Fixnum.instance_methods.grep(/!$/)  # => [:!]

# Okay, there's one, but it's actually a boolean negation
1.! # => false

# And it's not a Fixnum method, it's an inherited boolean operator
1.method(:!).owner # => BasicObject

# In really, you call it this way, the interpreter translates it
!1 # => false

Alternatives

  • Make a wrapper object: I'm not going to advocate this one, but it's the closest to what you're trying to do. Basically create your own class, which is mutable, and then make it look like an integer. There's a great blog post walking through this at http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html it will get you 95% of the way there
  • Don't depend directly on the value of a Fixnum: I can't give better advice than this without knowing what you're trying to do / why you feel this is a need.

Also, you should show your code when you ask questions like this. I misunderstood how you were approaching it for a long time.

Rails apply a method to all integers in model

If your Product model has a value attribute, for example, just write a class method and then you can call it wherever and for any object of that type.

class Product < ActiveRecord::Base
value_with_weight weight
value*weight
end
end

then use @product.value_with_weight(1.25) for example, wherever you want..

EDIT: What you're actually asking for sounds like you want to override the Integer class just in the scope of your particular model, which is a pretty horrible way to achieve that. However, sounds kinda like what Refinements are meant to do in Ruby 2.0

Adding numbers in succession

For reference, this problem comes from CodeWars "A Chain Adding Function".

The easiest way to achieve this is to define Integer#call:

class Integer
def call(n)
self + n
end
end

def add(n)
n
end

puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6

The returned objects are plain Ruby Integers, not Proxy objects.

Note that it's probably not a good idea to modify such an important class. It's possible to use refinements in order to avoid breaking other Ruby parts:

module ChainAdding
refine Integer do
def call(n)
self + n
end
end
end

def add(n)
n
end

using ChainAdding

puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6

If you're learning Ruby, this kind of metaprogramming trick isn't what you should concentrate on first. It's cool to know that we can do this, it doesn't mean that we should, though.

How to add methods to the Enumerable module?

In Ruby, you can use monkey-patching, which incorporates the concept of open classes. This means that classes in Ruby can be modified at any time. For example, you could create a double method in the number class.

class Integer < Numeric
def self.double
self * 2
end
end

Now you could call a double method on a number

4.double
=> 8

Hope this helps

Add the same method to multiple classes

The relevant part of the class hierarchy looks like this:

  • Numeric
    • Integer
      • Older versions had Fixnum and Bignum as subclasses of Integer.
    • Float
    • Rational

So patch your change into Numeric to cover them all at once:

class Numeric
def root(pow)
return self ** (1/pow.to_f)
end
end

Then you can do these things:

>> 11.root(2) # Integer
=> 3.3166247903554
>> 2.18.root(3) # Float
=> 1.296638256974172
>> Rational(23, 42).root(6) # Rational
=> 0.9045094132598528
>> 2**1000.root(42) # Integer
=> 2.2638347236157763


Related Topics



Leave a reply



Submit