Skip to content

Instantly share code, notes, and snippets.

@dbernheisel
Created November 29, 2018 14:51
Show Gist options
  • Select an option

  • Save dbernheisel/a7acc84ed6a996f57e45959d9a1b10ed to your computer and use it in GitHub Desktop.

Select an option

Save dbernheisel/a7acc84ed6a996f57e45959d9a1b10ed to your computer and use it in GitHub Desktop.
Interfaces in Ruby
module Interface
# For this module to be useful, it'll need to be prepended. For example, if
# you create a CardInterface:
#
# module CardInterface
# extend Interface
# method :photo, returns: [Photo, NilClass]
# end
#
# prepend it to the class:
#
# class MyClass
# prepend CardInterface
# end
#
# Prepending ensures that the methods are executed in the correct order.
def method(name, opts = {})
define_method(name) do |*args|
if respond_to?(name)
Interface.validate_and_return(super(*args), opts[:returns])
else
raise Interface::NotImplementedError,
"#{self.class} method #{name} not implemented"
end
end
end
def self.validate_and_return(value, return_type)
return value unless return_type
return_types = Array(return_type)
if return_types.include?(value.class)
value
else
raise Interface::WrongReturnTypeError,
"Method did not return one of: #{return_type.map(&:to_s)}\n" \
"Returned: #{value.class}"
end
end
class NotImplementedError < NoMethodError
end
class WrongReturnTypeError < NoMethodError
end
end
module CardInterface
# It's important to use this interace as:
# prepend CardInterface
# so that the methods are executed in the correct order.
extend Interface
method :photo, returns: [Photo, NilClass]
method :title, returns: [String, NilClass]
end
class Animal < ApplicationRecord
prepend CardInterface
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment