Skip to content

Instantly share code, notes, and snippets.

@tomstuart
Created July 29, 2011 12:50
Show Gist options
  • Save tomstuart/1113745 to your computer and use it in GitHub Desktop.
Save tomstuart/1113745 to your computer and use it in GitHub Desktop.
Parametric modules in Ruby
class Printable < Module
def initialize(string)
super()
define_method :print do
puts string
end
end
end
>> class A; include Printable.new('Hello'); end
>> class B; include Printable.new('Goodbye'); end
>> A.new.print
Hello
>> B.new.print
Goodbye
---
# @tomafro suggested something better:
module TimeoutThing
def do_something
puts timeout
end
def self.with(options = {})
Module.new do |m|
include TimeoutThing
options.each do |k, v|
define_method k do
v
end
end
end
end
end
TimeoutThing.with(:timeout => 20)
class A
include TimeoutThing.with(:timeout => 20)
end
class B
include TimeoutThing.with(:timeout => 40)
end
A.new.do_something
B.new.do_something
---
# I think I like this best:
module Parametric
def with(options)
base = self
Module.new do
include base
options.each do |k, v|
define_method(k) { v }
end
end
end
end
module Printable
extend Parametric
def print
puts string
end
end
>> class A; include Printable.with(:string => 'Hello'); end
>> class B; include Printable.with(:string => 'Goodbye'); end
>> A.new.print
Hello
>> B.new.print
Goodbye
@lazyatom
Copy link

I didn't realise that a module which contained an initialize method would gain a corresponding new - interesting. Regardless, I think any solution is going to have the same key characteristics that both yours and @tomafro's has - the creation of a new Module, with method(s) defined using define_method to get closure-like behaviour to bind the 'class'-level argument to the instance method implementation.

@tomstuart
Copy link
Author

To clarify: it's not a module, it's a class which subclasses Module and therefore produces a module when instantiated. Mmm, contrived.

I like @tomafro's solution because it gives you a nice place to put (static) method definitions. I'll probably go with slightly less magic, i.e. define_method :options { options } rather than options.each do … end, but otherwise it's what I'm going to use for now. I think you're right that there's no beautiful way. Maybe I'll put the definition of #with in its own module and use it to extend any modules that I want to make parametric?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment