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