diff --git a/.travis.yml b/.travis.yml index 2c316cd..738ee9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ rvm: - "2.2" - ruby-head - jruby-head +before_install: gem install bundler -v ">=1.12" matrix: allow_failures: - rvm: ruby-head diff --git a/lib/more_core_extensions/core_ext/array/deletes.rb b/lib/more_core_extensions/core_ext/array/deletes.rb index d5b0d99..fa4e343 100644 --- a/lib/more_core_extensions/core_ext/array/deletes.rb +++ b/lib/more_core_extensions/core_ext/array/deletes.rb @@ -1,5 +1,4 @@ -require 'active_support' -require 'active_support/core_ext/object/blank' +require 'more_core_extensions/core_ext/object/blank' module MoreCoreExtensions module ArrayDeletes diff --git a/lib/more_core_extensions/core_ext/hash/deletes.rb b/lib/more_core_extensions/core_ext/hash/deletes.rb index 21b121b..6009920 100644 --- a/lib/more_core_extensions/core_ext/hash/deletes.rb +++ b/lib/more_core_extensions/core_ext/hash/deletes.rb @@ -1,5 +1,4 @@ -require 'active_support' -require 'active_support/core_ext/object/blank' +require 'more_core_extensions/core_ext/object/blank' module MoreCoreExtensions module HashDeletes diff --git a/lib/more_core_extensions/core_ext/object.rb b/lib/more_core_extensions/core_ext/object.rb index 28f42c0..9762236 100644 --- a/lib/more_core_extensions/core_ext/object.rb +++ b/lib/more_core_extensions/core_ext/object.rb @@ -1 +1,2 @@ +require 'more_core_extensions/core_ext/object/blank' require 'more_core_extensions/core_ext/object/namespace' diff --git a/lib/more_core_extensions/core_ext/object/blank.rb b/lib/more_core_extensions/core_ext/object/blank.rb new file mode 100644 index 0000000..84a9ae4 --- /dev/null +++ b/lib/more_core_extensions/core_ext/object/blank.rb @@ -0,0 +1,149 @@ +# Copied from https://github.com/rails/rails/blob/v5.0.0/activesupport/lib/active_support/core_ext/object/blank.rb +# With the release of ActiveSupport v5.0.0 a minimum required_ruby_version of '>= 2.2.2' was added. +# This code has been copied here so that we can continue to support ruby versions that don't meet that requirement. + +unless Object.respond_to?(:blank?) + class Object + # An object is blank if it's false, empty, or a whitespace string. + # For example, +false+, '', ' ', +nil+, [], and {} are all blank. + # + # This simplifies + # + # !address || address.empty? + # + # to + # + # address.blank? + # + # @return [true, false] + def blank? + respond_to?(:empty?) ? !!empty? : !self + end + + # An object is present if it's not blank. + # + # @return [true, false] + def present? + !blank? + end + + # Returns the receiver if it's present otherwise returns +nil+. + # object.presence is equivalent to + # + # object.present? ? object : nil + # + # For example, something like + # + # state = params[:state] if params[:state].present? + # country = params[:country] if params[:country].present? + # region = state || country || 'US' + # + # becomes + # + # region = params[:state].presence || params[:country].presence || 'US' + # + # @return [Object] + def presence + self if present? + end + end + + class NilClass + # +nil+ is blank: + # + # nil.blank? # => true + # + # @return [true] + def blank? + true + end + end + + class FalseClass + # +false+ is blank: + # + # false.blank? # => true + # + # @return [true] + def blank? + true + end + end + + class TrueClass + # +true+ is not blank: + # + # true.blank? # => false + # + # @return [false] + def blank? + false + end + end + + class Array + # An array is blank if it's empty: + # + # [].blank? # => true + # [1,2,3].blank? # => false + # + # @return [true, false] + alias_method :blank?, :empty? + end + + class Hash + # A hash is blank if it's empty: + # + # {}.blank? # => true + # { key: 'value' }.blank? # => false + # + # @return [true, false] + alias_method :blank?, :empty? + end + + class String + BLANK_RE = /\A[[:space:]]*\z/ + + # A string is blank if it's empty or contains whitespaces only: + # + # ''.blank? # => true + # ' '.blank? # => true + # "\t\n\r".blank? # => true + # ' blah '.blank? # => false + # + # Unicode whitespace is supported: + # + # "\u00a0".blank? # => true + # + # @return [true, false] + def blank? + # The regexp that matches blank strings is expensive. For the case of empty + # strings we can speed up this method (~3.5x) with an empty? call. The + # penalty for the rest of strings is marginal. + empty? || BLANK_RE === self + end + end + + class Numeric #:nodoc: + # No number is blank: + # + # 1.blank? # => false + # 0.blank? # => false + # + # @return [false] + def blank? + false + end + end + + class Time #:nodoc: + # No Time is blank: + # + # Time.now.blank? # => false + # + # @return [false] + def blank? + false + end + end +end diff --git a/more_core_extensions.gemspec b/more_core_extensions.gemspec index b8dd728..c70f0de 100644 --- a/more_core_extensions.gemspec +++ b/more_core_extensions.gemspec @@ -27,5 +27,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake" spec.add_development_dependency "rspec", ">= 3.0" - spec.add_dependency "activesupport", "> 3.2" + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.2") + spec.add_development_dependency 'activesupport' + end end diff --git a/spec/core_ext/hash/nested_spec.rb b/spec/core_ext/hash/nested_spec.rb index dae5d03..173c298 100644 --- a/spec/core_ext/hash/nested_spec.rb +++ b/spec/core_ext/hash/nested_spec.rb @@ -190,39 +190,43 @@ include_examples "core_ext/hash/nested" end -require 'active_support' -require 'active_support/core_ext/hash' -describe HashWithIndifferentAccess do - let(:hash) do - described_class.new.merge( - "a" => 1, - "b" => {}, - "c" => {"c1" => 2}, - "d" => {"d1" => {"d2" => {"d3" => 3}}}, - "e" => Hash.new(4), - "f" => described_class.new { |h, k| h[k] = described_class.new }, - nil => {nil => 7}, - ["h", "i"] => 8 - ) - - # NOTE: "f" has to be initialized in that way due to a bug in - # HashWithIndifferentAccess and assigning a Hash with a default proc. - # - # 1.9.3 :001 > h1 = Hash.new - # 1.9.3 :002 > h1[:a] = Hash.new { |h, k| h[k] = Hash.new } - # 1.9.3 :003 > h1[:a].class - # => Hash - # 1.9.3 :004 > h1[:a][:b].class - # => Hash - # - # 1.9.3 :005 > require 'active_support/all' - # 1.9.3 :006 > h2 = HashWithIndifferentAccess.new - # 1.9.3 :007 > h2[:a] = Hash.new { |h, k| h[k] = Hash.new } - # 1.9.3 :008 > h2[:a].class - # => ActiveSupport::HashWithIndifferentAccess - # 1.9.3 :009 > h2[:a][:b].class - # => NilClass +begin + require 'active_support' + require 'active_support/core_ext/hash' + describe HashWithIndifferentAccess do + let(:hash) do + described_class.new.merge( + "a" => 1, + "b" => {}, + "c" => {"c1" => 2}, + "d" => {"d1" => {"d2" => {"d3" => 3}}}, + "e" => Hash.new(4), + "f" => described_class.new { |h, k| h[k] = described_class.new }, + nil => {nil => 7}, + ["h", "i"] => 8 + ) + + # NOTE: "f" has to be initialized in that way due to a bug in + # HashWithIndifferentAccess and assigning a Hash with a default proc. + # + # 1.9.3 :001 > h1 = Hash.new + # 1.9.3 :002 > h1[:a] = Hash.new { |h, k| h[k] = Hash.new } + # 1.9.3 :003 > h1[:a].class + # => Hash + # 1.9.3 :004 > h1[:a][:b].class + # => Hash + # + # 1.9.3 :005 > require 'active_support/all' + # 1.9.3 :006 > h2 = HashWithIndifferentAccess.new + # 1.9.3 :007 > h2[:a] = Hash.new { |h, k| h[k] = Hash.new } + # 1.9.3 :008 > h2[:a].class + # => ActiveSupport::HashWithIndifferentAccess + # 1.9.3 :009 > h2[:a][:b].class + # => NilClass + end + + include_examples "core_ext/hash/nested" end - - include_examples "core_ext/hash/nested" +rescue LoadError + # ActiveSupport v5.0.0 requires ruby '>=2.2.2', skip these tests on older rubies. end diff --git a/spec/core_ext/object/blank_spec.rb b/spec/core_ext/object/blank_spec.rb new file mode 100644 index 0000000..7b6f49f --- /dev/null +++ b/spec/core_ext/object/blank_spec.rb @@ -0,0 +1,46 @@ +describe "blank" do + class EmptyTrue + def empty? + 0 + end + end + + class EmptyFalse + def empty? + nil + end + end + + BLANK = [EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", ' ', "\u00a0", [], {}].freeze + PRESENT = [EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], {nil => 0}].freeze + + describe "#blank?" do + context "true" do + BLANK.each { |v| it(v.inspect) { expect(v).to be_blank } } + end + + context "false" do + PRESENT.each { |v| it(v.inspect) { expect(v).to_not be_blank } } + end + end + + describe "#present?" do + context "true" do + PRESENT.each { |v| it(v.inspect) { expect(v).to be_present } } + end + + context "false" do + BLANK.each { |v| it(v.inspect) { expect(v).to_not be_present } } + end + end + + describe "#presence" do + context "object" do + PRESENT.each { |v| it(v.inspect) { expect(v.presence).to eq(v) } } + end + + context "nil" do + BLANK.each { |v| it(v.inspect) { expect(v.presence).to be_nil } } + end + end +end