lib/polyfill.rb in polyfill-0.8.0 vs lib/polyfill.rb in polyfill-0.9.0

- old
+ new

@@ -1,110 +1,69 @@ -require 'ipaddr' -require 'stringio' require 'polyfill/version' -require 'polyfill/utils' +require 'polyfill/internal_utils' module Polyfill - module Parcel; end + module Module; end def get(module_name, methods, options = {}) - if Object.const_get(module_name.to_s).is_a?(Class) + if Object.const_get(module_name.to_s, false).is_a?(Class) raise ArgumentError, "#{module_name} is a class not a module" end # # parse options # - versions = { - '2.2' => Polyfill::V2_2, - '2.3' => Polyfill::V2_3, - '2.4' => Polyfill::V2_4 - } - desired_version = options.delete(:version) || versions.keys.max - unless versions.keys.include?(desired_version) - raise ArgumentError, "invalid value for keyword version: #{desired_version}" - end - versions.reject! do |version_number, _| - version_number > desired_version - end + versions = InternalUtils.polyfill_versions_to_use(options.delete(:version)) unless options.empty? raise ArgumentError, "unknown keyword: #{options.first[0]}" end # # find all polyfills for the module across all versions # - module_names = module_name.to_s.split('::') - current_ruby_version = RUBY_VERSION[/\A(\d+\.\d+)/, 1] + modules_with_updates, modules = InternalUtils.modules_to_use(module_name, versions) - modules_with_updates = [] - modules = [] - versions.each do |version_number, version_module| - begin - final_module = module_names - .reduce(version_module) do |current_mod, name| - current_mod.const_get(name, false) - end - - modules_with_updates << final_module - - next if version_number <= current_ruby_version - - modules << final_module.clone - rescue NameError - nil - end - end - - if modules_with_updates.empty? - raise ArgumentError, %Q("#{module_name}" has no updates) - end - # # remove methods that were not requested # - methods_with_updates = modules_with_updates.flat_map(&:instance_methods).uniq - requested_methods = methods == :all ? methods_with_updates : methods + requested_methods = InternalUtils.methods_to_keep(modules_with_updates, methods, '#', module_name) - unless (leftovers = (requested_methods - methods_with_updates)).empty? - raise ArgumentError, %Q("##{leftovers.first}" is not a valid method on #{module_name} or has no updates) - end - modules.each do |instance_module| - instance_module.instance_methods.each do |name| - instance_module.send(:remove_method, name) unless requested_methods.include?(name) - end + InternalUtils.keep_only_these_methods!(instance_module, requested_methods) end # # build the module to return # - mod = Module.new + InternalUtils.create_module do |mod| + # make sure the methods get added if this module is included + mod.singleton_class.send(:define_method, :included) do |base| + modules.each do |module_to_add| + base.include module_to_add unless module_to_add.instance_methods.empty? + end + end - # make sure the methods get added if this module is included - mod.singleton_class.send(:define_method, :included) do |base| - modules.each do |module_to_add| - base.include module_to_add unless module_to_add.instance_methods.empty? + # make sure the methods get added if this module is extended + mod.singleton_class.send(:define_method, :extended) do |base| + modules.each do |module_to_add| + base.extend module_to_add unless module_to_add.instance_methods.empty? + end end - end - # make sure the methods get added if this module is extended - mod.singleton_class.send(:define_method, :extended) do |base| - modules.each do |module_to_add| - base.extend module_to_add unless module_to_add.instance_methods.empty? + # make sure the methods get added if this module is prepended + mod.singleton_class.send(:define_method, :prepended) do |base| + modules.each do |module_to_add| + base.prepend module_to_add unless module_to_add.instance_methods.empty? + end end end - - Polyfill::Parcel.const_set("O#{mod.object_id}", mod) end module_function :get end def Polyfill(options = {}) # rubocop:disable Style/MethodName - mod = Module.new - # # parse options # objects, others = options.partition { |key,| key[/\A[A-Z]/] } objects.sort! do |a, b| @@ -114,194 +73,159 @@ 1 else 0 end end + objects.each do |object_name, _| + Object.const_get(object_name.to_s, false) + end others = others.to_h - versions = { - '2.2' => Polyfill::V2_2, - '2.3' => Polyfill::V2_3, - '2.4' => Polyfill::V2_4 - } - desired_version = others.delete(:version) || versions.keys.max - unless versions.keys.include?(desired_version) - raise ArgumentError, "invalid value for keyword version: #{desired_version}" - end - versions.reject! do |version_number, _| - version_number > desired_version - end - + versions = Polyfill::InternalUtils.polyfill_versions_to_use(others.delete(:version)) native = others.delete(:native) { false } unless others.empty? raise ArgumentError, "unknown keyword: #{others.first[0]}" end # - # useful var + # build the module to return # - current_ruby_version = RUBY_VERSION[/\A(\d+\.\d+)/, 1] + Polyfill::InternalUtils.create_module do |mod| + objects.each do |object_name, methods| + # + # find all polyfills for the object across all versions + # + modules_with_updates, instance_modules = Polyfill::InternalUtils.modules_to_use(object_name, versions) - objects.each do |full_name, methods| - # - # find all polyfills for the object across all versions - # - object_module_names = full_name.to_s.split('::') - - object_modules = versions - .map do |version_number, version_module| + class_modules_with_updates = modules_with_updates.map do |module_with_updates| begin - final_module = object_module_names - .reduce(version_module) do |current_mod, name| - current_mod.const_get(name, false) - end - - [version_number, final_module] + module_with_updates.const_get(:ClassMethods, false) rescue NameError nil end - end - .compact + end.compact + class_modules = instance_modules.map do |module_with_updates| + begin + module_with_updates.const_get(:ClassMethods, false).clone + rescue NameError + nil + end + end.compact - if object_modules.empty? - raise ArgumentError, %Q("#{full_name}" is not a valid object or has no updates) - end - - # - # get all class modules and instance modules from the polyfills - # - class_modules = object_modules.map do |version_number, object_module| - begin - [version_number, object_module.const_get(:ClassMethods, false).clone] - rescue NameError - nil + # + # get all requested class and instance methods + # + if methods != :all && (method_name = methods.find { |method| method !~ /\A[.#]/ }) + raise ArgumentError, %Q("#{method_name}" must start with a "." if it's a class method or "#" if it's an instance method) end - end.compact - instance_modules = object_modules.map do |version_number, object_module| - [version_number, object_module.clone] - end - # - # get all requested class and instance methods - # - if methods != :all && (method_name = methods.find { |method| method !~ /\A[.#]/ }) - raise ArgumentError, %Q("#{method_name}" must start with a "." if it's a class method or "#" if it's an instance method) - end + instance_methods, class_methods = + if methods == :all + [:all, :all] + else + methods + .partition { |m| m.start_with?('#') } + .map { |method_list| method_list.map { |name| name[1..-1].to_sym } } + end - all_methods_for = lambda do |modules| - modules.flat_map { |_, m| m.instance_methods }.uniq - end - available_class_methods = all_methods_for.call(class_modules) - available_instance_methods = all_methods_for.call(instance_modules) + requested_instance_methods = + Polyfill::InternalUtils.methods_to_keep(modules_with_updates, instance_methods, '#', object_name) + requested_class_methods = + Polyfill::InternalUtils.methods_to_keep(class_modules_with_updates, class_methods, '.', object_name) - select_and_clean = lambda do |leader| - methods.select { |method| method.start_with?(leader) }.map { |method| method[1..-1].to_sym } - end - requested_class_methods = methods == :all ? available_class_methods : select_and_clean.call('.') - requested_instance_methods = methods == :all ? available_instance_methods : select_and_clean.call('#') - - unless (leftovers = (requested_class_methods - available_class_methods)).empty? - raise ArgumentError, %Q(".#{leftovers.first}" is not a valid method on #{full_name} or has no updates) - end - unless (leftovers = (requested_instance_methods - available_instance_methods)).empty? - raise ArgumentError, %Q("##{leftovers.first}" is not a valid method on #{full_name} or has no updates) - end - - # - # get the class(es) to refine - # - base_class = object_modules.first.last.name.sub(/\APolyfill::V\d_\d::/, '') - base_classes = - case base_class - when 'Comparable' - %w[Numeric String Time] - when 'Enumerable' - %w[Array Dir Enumerator Hash IO Range StringIO Struct] - when 'Kernel' - %w[Object] - else - [base_class] + # + # get the class(es) to refine + # + base_object = object_name.to_s + base_objects = + case base_object + when 'Comparable' + %w[Numeric String Time] + when 'Enumerable' + %w[Array Dir Enumerator Hash IO Matrix Range StringIO Struct Vector] + when 'Kernel' + %w[Object] + else + [base_object] + end + base_objects.select! do |klass| + begin + Object.const_get(klass, false) + rescue NameError + false + end end - # - # refine in class methods - # - class_modules.each do |version_number, class_module| - next if version_number <= current_ruby_version + # + # refine in class methods + # + class_modules.each do |class_module| + Polyfill::InternalUtils.keep_only_these_methods!(class_module, requested_class_methods) - class_module.instance_methods.each do |name| - class_module.send(:remove_method, name) unless requested_class_methods.include?(name) - end + next if class_module.instance_methods.empty? - next if class_module.instance_methods.empty? + mod.module_exec(requested_class_methods) do |methods_added| + base_objects.each do |klass| + refine Object.const_get(klass, false).singleton_class do + include class_module - mod.module_exec(requested_class_methods) do |methods_added| - base_classes.each do |klass| - refine Object.const_get(klass).singleton_class do - include class_module + if native + Polyfill::InternalUtils.ignore_warnings do + define_method :respond_to? do |name, include_all = false| + return true if methods_added.include?(name) - if native - Polyfill::Utils.ignore_warnings do - define_method :respond_to? do |name, include_all = false| - return true if methods_added.include?(name) + super(name, include_all) + end - super(name, include_all) - end + define_method :__send__ do |name, *args, &block| + return super(name, *args, &block) unless methods_added.include?(name) - define_method :__send__ do |name, *args, &block| - return super(name, *args, &block) unless methods_added.include?(name) - - class_module.instance_method(name).bind(self).call(*args, &block) + class_module.instance_method(name).bind(self).call(*args, &block) + end + alias_method :send, :__send__ end - alias_method :send, :__send__ end end end end end - end - # - # refine in instance methods - # - instance_modules.each do |version_number, instance_module| - next if version_number <= current_ruby_version + # + # refine in instance methods + # + instance_modules.each do |instance_module| + Polyfill::InternalUtils.keep_only_these_methods!(instance_module, requested_instance_methods) - instance_module.instance_methods.each do |name| - instance_module.send(:remove_method, name) unless requested_instance_methods.include?(name) - end + next if instance_module.instance_methods.empty? - next if instance_module.instance_methods.empty? + mod.module_exec(requested_instance_methods) do |methods_added| + base_objects.each do |klass| + refine Object.const_get(klass, false) do + include instance_module - mod.module_exec(requested_instance_methods) do |methods_added| - base_classes.each do |klass| - refine Object.const_get(klass) do - include instance_module + if native + Polyfill::InternalUtils.ignore_warnings do + define_method :respond_to? do |name, include_all = false| + return super(name, include_all) unless methods_added.include?(name) - if native - Polyfill::Utils.ignore_warnings do - define_method :respond_to? do |name, include_all = false| - return super(name, include_all) unless methods_added.include?(name) + true + end - true - end + define_method :__send__ do |name, *args, &block| + return super(name, *args, &block) unless methods_added.include?(name) - define_method :__send__ do |name, *args, &block| - return super(name, *args, &block) unless methods_added.include?(name) - - instance_module.instance_method(name).bind(self).call(*args, &block) + instance_module.instance_method(name).bind(self).call(*args, &block) + end + alias_method :send, :__send__ end - alias_method :send, :__send__ end end end end end end end - - Polyfill::Parcel.const_set("O#{mod.object_id}", mod) end require 'polyfill/v2_2' require 'polyfill/v2_3' require 'polyfill/v2_4'