diff --git a/lib/pact.rb b/lib/pact.rb index 01756361..75159569 100644 --- a/lib/pact.rb +++ b/lib/pact.rb @@ -5,6 +5,11 @@ require 'pact/railtie' if defined?(Rails::Railtie) +# Load core_ext polyfills (blank?/present?, deep_dup) before Zeitwerk eager-loads +# the rest of the gem. Each polyfill is skipped if already defined by active_support +# or any other library loaded by the host application. +require 'pact/support/core_ext' + module Pact class Error < StandardError; end @@ -41,6 +46,7 @@ def self.configuration loader.ignore("#{__dir__}/pact/version.rb") loader.ignore("#{__dir__}/pact/rspec.rb") loader.ignore("#{__dir__}/pact/rspec") +loader.ignore("#{__dir__}/pact/support/core_ext.rb") loader.ignore("#{__dir__}/pact/railtie.rb") unless defined?(Rails::Railtie) loader.setup diff --git a/lib/pact/rspec/support/webmock/webmock_helpers.rb b/lib/pact/rspec/support/webmock/webmock_helpers.rb index 57526ddd..615c3733 100644 --- a/lib/pact/rspec/support/webmock/webmock_helpers.rb +++ b/lib/pact/rspec/support/webmock/webmock_helpers.rb @@ -2,7 +2,7 @@ module WebmockHelpers def self.turned_off - yield unless defined?(::WebMock) + return yield unless defined?(::WebMock) allow_net_connect = WebMock::Config.instance.allow_net_connect allow_localhost = WebMock::Config.instance.allow_localhost diff --git a/lib/pact/support/core_ext.rb b/lib/pact/support/core_ext.rb new file mode 100644 index 00000000..5a83db3f --- /dev/null +++ b/lib/pact/support/core_ext.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +# Minimal active_support polyfills for blank?/present? and deep_dup. +# Each block is skipped entirely if the method is already defined +# (e.g. active_support is loaded by the host application). + +unless Object.method_defined?(:blank?) + class NilClass + def blank? = true + def present? = false + end + + class FalseClass + def blank? = true + def present? = false + end + + class TrueClass + def blank? = false + def present? = true + end + + class String + BLANK_RE = /\A[[:space:]]*\z/ + + def blank? + empty? || BLANK_RE.match?(self) + end + + def present? = !blank? + end + + class Symbol + alias_method :blank?, :empty? + def present? = !blank? + end + + class Array + alias_method :blank?, :empty? + def present? = !empty? + end + + class Hash + alias_method :blank?, :empty? + def present? = !empty? + end + + class Object + def blank? = respond_to?(:empty?) ? !!empty? : false + def present? = !blank? + + def presence + self if present? + end + end +end + +unless Object.method_defined?(:deep_dup) + class Object + def deep_dup + dup + rescue TypeError + self + end + end + + class Array + def deep_dup + map(&:deep_dup) + end + end + + class Hash + def deep_dup + hash = dup + each_pair do |key, value| + if ::String === key || ::Symbol === key + hash[key] = value.deep_dup + else + hash.delete(key) + hash[key.deep_dup] = value.deep_dup + end + end + hash + end + end + + class Module + def deep_dup + name.nil? ? super : self + end + end +end diff --git a/pact.gemspec b/pact.gemspec index d9a139ac..b43acdfb 100644 --- a/pact.gemspec +++ b/pact.gemspec @@ -27,6 +27,12 @@ Gem::Specification.new do |gem| 'documentation_uri' => 'https://github.com/pact-foundation/pact-ruby/blob/master/README.md' } + # Optional runtime dependencies (not required by pact itself): + # active_support — if already loaded, its blank?/present? take precedence over + # pact's built-in polyfill (lib/pact/support/blank.rb). + # webmock — only needed for WebMock integration via WebmockHelpers. + # Require it yourself before using that helper. + # Core dependencies (code loading) gem.add_dependency 'zeitwerk', '~> 2.3' # For Pact support via Pact Rust Core