Last active
December 16, 2017 00:20
-
-
Save EdmundLeex/e42711cfb16d66a2f29356f3e6e246bd to your computer and use it in GitHub Desktop.
FigLeaf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Tools for making inherited interfaces private to a class. | |
module FigLeaf | |
module Macros | |
# Given a list of classes, modules, strings, and symbols, compile | |
# a combined list of methods. Classes and modules will be queried | |
# for their instance methods; strings and symbols will be treated | |
# as method names. | |
# | |
# Once the list is compiled, make all of the methods private. | |
# | |
# Takes an optional options hash, which can include the following options: | |
# | |
# - :ancestors is a boolean determining whether to consider | |
# ancestors classes and modules. | |
# | |
# - :except is a list of classes, modules, and method names which | |
# will be excluded from treatment. | |
def hide(*stuff) | |
hide_methods(self, [Object], *stuff) | |
end | |
# Like #hide, only hides methods at the class/module level. | |
def hide_singletons(*stuff) | |
hide_methods(singleton_class, [Class], *stuff) | |
end | |
# The shared bits of #hide and #hide_singletons | |
def hide_methods(mod, except_defaults, *stuff) | |
options = stuff.last.is_a?(Hash) ? stuff.pop : {} | |
include_ancestors = options.fetch(:ancestors){false} | |
except = Array(options.fetch(:except){except_defaults}) | |
protect = Array(options[:protect]) | |
except_methods = collect_methods(true, *except) | |
protect_methods = collect_methods(true, *protect) | |
methods_to_hide = collect_methods(include_ancestors, *stuff) | |
(methods_to_hide - except_methods).each do |method_name| | |
mod.module_eval do | |
next unless method_defined?(method_name) | |
if protect_methods.include?(method_name) | |
protected method_name | |
else | |
private method_name | |
end | |
end | |
end | |
end | |
# Given a list of classes, modules, strings, and symbols, compile | |
# a combined list of methods. Classes and modules will be queried | |
# for their instance methods; strings and symbols will be treated | |
# as methods names. +include_ancestors+ determines whether to | |
# include methods defined by class/module ancestors. | |
def collect_methods(include_ancestors, *methods_or_modules) | |
methods_or_modules.inject([]) {|methods, method_or_module| | |
case method_or_module | |
when Symbol, String | |
methods << method_or_module.to_sym | |
when Module # also includes classes | |
methods.concat(method_or_module.instance_methods(include_ancestors)) | |
when Array | |
methods.concat(method_or_module) | |
else | |
raise ArgumentError, "Bad argument: #{method_or_module.inspect}" | |
end | |
} | |
end | |
end | |
def self.clothe(other) | |
other.extend(Macros) | |
end | |
def self.included(other) | |
clothe(other) | |
other.singleton_class.extend(Macros) | |
end | |
def self.extended(object) | |
clothe(object.singleton_class) | |
end | |
end | |
class Post < ActiveRecord::Base | |
include FigLeaf | |
hide ActiveRecord::Base, ancestors: true, | |
except: [Object, :init_with, :new_record?, | |
:errors, :valid?, :save] | |
hide_singletons ActiveRecord::Calculations, | |
ActiveRecord::FinderMethods, | |
ActiveRecord::Relation | |
# ... | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment