Skip to content

Instantly share code, notes, and snippets.

@matheussilvasantos
Created September 12, 2019 12:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matheussilvasantos/a42fd23136162a16d4bb89839c4ad6b2 to your computer and use it in GitHub Desktop.
Save matheussilvasantos/a42fd23136162a16d4bb89839c4ad6b2 to your computer and use it in GitHub Desktop.
Rest
module REST
def self.OAuth(client)
REST::OAuth.new(client)
end
class OAuth < Module
def initialize(client)
@client = client
freeze
end
private
def included(descendant)
super
descendant.send(:include, Methods)
end
def define_methods
define_cmp_method
define_hash_method
define_inspect_method if inspect
end
# Define an #cmp? method based on the instance's values identified by #keys
#
# @return [undefined]
#
# @api private
def define_cmp_method
keys = @keys
define_method(:cmp?) do |comparator, other|
keys.all? do |key|
__send__(key).public_send(comparator, other.__send__(key))
end
end
private :cmp?
end
# Define a #hash method based on the instance's values identified by #keys
#
# @return [undefined]
#
# @api private
def define_hash_method
keys = @keys
define_method(:hash) do | |
keys.map(&method(:send)).push(self.class).hash
end
end
# Define an inspect method that reports the values of the instance's keys
#
# @return [undefined]
#
# @api private
def define_inspect_method
keys = @keys
define_method(:inspect) do | |
klass = self.class
name = klass.name || klass.inspect
"#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>"
end
end
# The comparison methods
module Methods
# Compare the object with other object for equality
#
# @example
# object.eql?(other) # => true or false
#
# @param [Object] other
# the other object to compare with
#
# @return [Boolean]
#
# @api public
def eql?(other)
instance_of?(other.class) && cmp?(__method__, other)
end
# Compare the object with other object for equivalency
#
# @example
# object == other # => true or false
#
# @param [Object] other
# the other object to compare with
#
# @return [Boolean]
#
# @api public
def ==(other)
other.is_a?(self.class) && cmp?(__method__, other)
end
end # module Methods
end # class Equalizer
end
@ivopt
Copy link

ivopt commented Sep 12, 2019

I've warped your example a little bit but this should be understandable.
I added a few comments on the code to explain the need for certain things

module Rest
  def self.OAuth(client)
    Rest::OAuth.new(client)
  end
  class OAuth < Module
    def initialize(client)
      @client = client
      define_methods # This is needed to define the so-called dynamic methods. You may rename this whatever you like obviously
    end

    private
    
    def included(base)
      super
      base.send :include, Methods # This is what makes all methods in Methods available to the target class as if it was a plain old mixin module.
    end

    def define_methods # here you define all dynamic methods you need. in this case we only have one and thus this is cumbersome, but as you add more and more...
      define_client_method
    end

    def define_client_method
      client = @client # This is very important, you might be tempted to do a @client on the next line.. that would fail as that would be fetched from the target classe's scope, not what you want
      define_method :rest_oauth_client do # I usually use these "funny/long" names. This is to avoid name clashes. My general rule of thumb here is "<module>_<class>_<accessor>" hence rest oauth client.
        client
      end
    end

    module Methods
      def say
        puts rest_oauth_client
      end
    end
  end
end

class Banana
  include Rest::OAuth("apple")

end

b = Banana.new
b.say

@ivopt
Copy link

ivopt commented Sep 12, 2019

PS: I think it would be possible to build a facilitator that would get you rid of all this boilerplate (the define_methods, the base.send :include, Methods, and possibly even the define_XXX_methods...

Sounds like a nice weekend challenge

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