lib/duck_puncher.rb in duck_puncher-3.0.0 vs lib/duck_puncher.rb in duck_puncher-4.0.0

- old
+ new

@@ -1,65 +1,92 @@ +# Standard lib require 'pathname' require 'fileutils' require 'logger' +require 'set' +require 'delegate' + +# Gems require 'usable' + +# Our stuff require 'duck_puncher/version' +require 'duck_puncher/registration' +require 'duck_puncher/decoration' module DuckPuncher autoload :JSONStorage, 'duck_puncher/json_storage' autoload :GemInstaller, 'duck_puncher/gem_installer' autoload :Duck, 'duck_puncher/duck' autoload :Ducks, 'duck_puncher/ducks' class << self + include Registration, Decoration + attr_accessor :log + alias_method :logger, :log - def classes - @classes ||= {} + def punch!(*classes) + options = classes.last.is_a?(Hash) ? classes.pop : {} + classes.each do |klass| + klass = lookup_constant(klass) + Ducks[klass].sort.each do |duck| + punches = options[:only] || Ducks::Module.instance_method(:local_methods).bind(duck.mod).call + log.info %Q(#{duck.target}#{" <-- #{punches}" if Array(punches).any?}) + options[:target] = klass + unless duck.punch(options) + log.error %Q(Failed to punch #{name}) + end + end + end + nil end - def duck_class(name) - classes[name] ||= const_set "#{name}Duck", Ducks[name].dup.classify + def punch_all! + punch! *Ducks.list.keys end - # @description Extends functionality to a copy of the specified class - def punch(*names) - singular = names.size == 1 - punched_ducks = names.map(&method(:duck_class)).compact - if singular - punched_ducks.first + def lookup_constant(const) + return const if Module === const + if const.to_s.respond_to?(:constantize) + const.to_s.constantize else - punched_ducks + const.to_s.split('::').inject(Object) { |k, part| k.const_get(part) } end + rescue NameError => e + log.error "#{e.class}: #{e.message}" + nil end - def punch!(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - names.each do |name| - duck = Ducks[name] - log.warn %Q(Punching#{" #{options[:only]} onto" if Array(options[:only]).any?} #{options.fetch(:target, name)}) - unless duck.punch(options) - log.error %Q(Failed to punch #{name}!) - end + def redefine_constant(name, const) + if const_defined? name + remove_const name end - nil + const_set name, const end - def punch_all! - log.warn 'Punching all ducks!' - Ducks.list.each &:punch + def ancestral_hash + Hash.new { |me, klass| me[klass.superclass] if klass.respond_to?(:superclass) } end - - def register(*args) - Array(args.shift).each do |name| - Ducks.list << Duck.new(name, *args) - end - end end self.log = Logger.new(STDOUT).tap do |config| config.level = Logger::INFO config.formatter = proc { |*args| "#{args.first}: #{args.last.to_s}\n" } end log.level = Logger::ERROR + + ducks = [ + [String, Ducks::String], + [Array, Ducks::Array], + [Numeric, Ducks::Numeric], + [Hash, Ducks::Hash], + [Object, Ducks::Object], + [Module, Ducks::Module], + [Method, Ducks::Method, { before: ->(*) { DuckPuncher::GemInstaller.initialize! } }], + ] + ducks << ['ActiveRecord::Base', Ducks::ActiveRecord] if defined? ::ActiveRecord + ducks.each do |duck| + register *duck + end end