How to do method binding (or re-binding) in Ruby
…or how to decorate object so that it can be undecorated:
class Account attr_reader :state def initialize(state) @state = state end end module Debit def transaction(amount) @state = @state - amount end end module Credit def transaction(amount) @state = @state + amount end end account = Account.new(100) account.state # => 100 puts account.public_methods(false) # => [:state] debit = Debit.instance_method(:transaction) credit = Credit.instance_method(:transaction) # Lets do debit transactions transaction = debit.bind(account) transaction.call(3) transaction.call(3) account.state # => 94 puts account.public_methods(false) # => [:state] puts account.public_methods(false) # => [:state] # Lets do credit transactions transaction = credit.bind(account) transaction.call(7) transaction.call(7) puts account.state # => 108 puts account.public_methods(false) # => [:state]
Note! this is still experimental feature in Ruby and too fast.
So when you think about it, we manage to decorate the object with a method, called some logic that changed the state and then we were able to work with the original object (without that method)
Note! This is still quite slow feature in Ruby. I personally wouldn’t use it in a production code. But it’s cool that something like this exist.
extend, inheritance this is reversable => you can
work with original object without that logic. With include/extend once
you go that way you cannot come back:
module Foo def foo 123 end end a = Object.new b = a a.extend(Foo) a.foo # => 123 b.foo # => 123
Well yeah you could, like with Refinements but that’s not my point. You should be able to access the original (undecorated) object.
And although you can do similar transformations with decorator objects (e.g. like
the point is that you have to define class where you pass object in order to do
transformation. You are not directly injecting / removing functions from
Discussion to this article:
Entire blog website and all the articles can be forked from this Github Repo