Skip to content

Commit

Permalink
Further work on how refinements are loaded.
Browse files Browse the repository at this point in the history
  • Loading branch information
nomoon committed Mar 5, 2017
1 parent 375ee67 commit 8e52888
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 97 deletions.
50 changes: 29 additions & 21 deletions lib/ragabash/refinements.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
module Ragabash
# A set of useful refinements for base classes.
#
# Activate these by including the following in an appropriate lexical scope:
# using ::Ragabash
# or with the pattern:
# ::Ragabash::Refinements.activate! || using(::Ragabash::Refinements)
# for compatibility with versions of Ruby which don't support Refinements.
# Activate these by including the following in an appropriate
# lexical scope:
# using ::Ragabash::Refinements
# Or activate these refinements via monkey-patching with:
# ::Ragabash::Refinements.activate
# You may also use this pattern to monkey-patch only as a fall-back:
# ::Ragabash::Refinements.compat || using(::Ragabash::Refinements)

module Refinements
# rubocop:disable Style/Alias
Expand Down Expand Up @@ -66,14 +68,14 @@ module Refinements

# This section permits us to fall-back to monkey-patching if we're not on
# MRI 2.1+
unless RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.1"
@refinement_blocks = {}
class << self
private

def refine(klass, &refinement)
@refinement_blocks[klass] = refinement
end
@refinement_blocks = {}
class << self
private

def refine(klass, &refinement)
@refinement_blocks[klass] = refinement
super if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.1"
end
end

Expand Down Expand Up @@ -295,25 +297,31 @@ def present?
end

REFINEMENT_BLOCKS = IceNine.deep_freeze(@refinement_blocks) || {}
remove_instance_variable(:@refinement_blocks) if @refinement_blocks
remove_instance_variable(:@refinement_blocks)
private_constant :REFINEMENT_BLOCKS

# Activate the refinements as a monkey-patch if refinements aren't
# supported. Will only monkey-patch once.
#
# This allows for the pattern of:
# ::Ragabash::Refinements.activate! || using(::Ragabash::Refinements)
# Which should work on Ruby versions that do not support refinements.
# Activate the refinements as a monkey-patch.
# <b>Will only monkey-patch once.</b>
#
# @return [Boolean] +false+ if there is nothing to monkey-patch, or +true+
# if monkey-patching was successful now or before.
def self.activate!
# if monkey-patching was successful now or previously.
def self.activate
return false if REFINEMENT_BLOCKS.empty?
return true if @activated
REFINEMENT_BLOCKS.each do |klass, refinement|
klass.class_eval(&refinement)
end
@activated = true
end

# Activate the refinements as a monkey-patch only if refinements aren't
# supported.
#
# @return [Boolean] +false+ if refinements are supported, or the result of
# the {.activate} method.
def self.compat
return false if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.1"
activate
end
end
end
158 changes: 83 additions & 75 deletions spec/ragabash/refinements_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@
require "spec_helper"

describe Ragabash::Refinements do # rubocop:disable BlockLength
::Ragabash::Refinements.activate! || using(::Ragabash::Refinements)
if ::Ragabash::Refinements.compat
STDERR.puts "*** Refinements loaded through monkey-patching ***"
elsif using(::Ragabash::Refinements)
STDERR.puts "*** Refinements loaded through using() ***"
end

let(:flt) { 1.15 }
let(:big) { BigDecimal.new("1.5") }
let(:ary) { [1, 2, 3] }
let(:hsh) { { a: 1, b: 2 } }
let(:set) { Set.new([1, 2]) }

context "#deep_freeze" do
it "freezes nested objects" do
Expand All @@ -23,124 +31,124 @@
end

context "#try_dup" do
it("NilClass returns self") { expect(nil.try_dup).to equal(nil) }
it("FalseClass returns self") { expect(false.try_dup).to equal(false) }
it("TrueClass returns self") { expect(true.try_dup).to equal(true) }
it("Integer returns self") { expect(1.try_dup).to equal(1) }
it("Float returns self") { expect(flt.try_dup).to equal(flt) }
it("Symbol returns self") { expect(:symbol.try_dup).to equal(:symbol) }
it("BigDecimal duplicates") { expect(BigDecimal.new("1.5").try_dup).to not_equal(BigDecimal.new("1.5")) }
it("String duplicates") { expect("A string".try_dup).to not_equal("A string") }
it("Array duplicates") { expect([1, 2, 3].try_dup).to not_equal([1, 2, 3]) }
it("Hash duplicates") { expect({ a: 1, b: 2 }.try_dup).to not_equal(a: 1, b: 2) }
it("Set duplicates") { expect(Set.new([1, 2]).try_dup).to not_equal(Set.new([1, 2])) }
it("NilClass returns self") { expect(nil.try_dup).to be(nil) }
it("FalseClass returns self") { expect(false.try_dup).to be(false) }
it("TrueClass returns self") { expect(true.try_dup).to be(true) }
it("Integer returns self") { expect(1.try_dup).to be(1) }
it("Float returns self") { expect(flt.try_dup).to be(flt) }
it("Symbol returns self") { expect(:symbol.try_dup).to be(:symbol) }
it("BigDecimal duplicates") { expect(big.try_dup).to not_be(big) }
it("String duplicates") { expect("A string".try_dup).to not_be("A string") }
it("Array duplicates") { expect(ary.try_dup).to not_be(ary) }
it("Hash duplicates") { expect(hsh.try_dup).to not_be(hsh) }
it("Set duplicates") { expect(set.try_dup).to not_be(set) }
end

context "#deep_dup" do
it("NilClass returns self") { expect(nil.deep_dup).to equal(nil) }
it("FalseClass returns self") { expect(false.deep_dup).to equal(false) }
it("TrueClass returns self") { expect(true.deep_dup).to equal(true) }
it("Integer returns self") { expect(1.deep_dup).to equal(1) }
it("Float returns self") { expect(flt.deep_dup).to equal(flt) }
it("Symbol returns self") { expect(:symbol.deep_dup).to equal(:symbol) }
it("BigDecimal duplicates") { expect(BigDecimal.new("1.5").try_dup).to not_equal(BigDecimal.new("1.5")) }
it("String duplicates") { expect("A string".deep_dup).to not_equal("A string") }
it("NilClass returns self") { expect(nil.deep_dup).to be(nil) }
it("FalseClass returns self") { expect(false.deep_dup).to be(false) }
it("TrueClass returns self") { expect(true.deep_dup).to be(true) }
it("Integer returns self") { expect(1.deep_dup).to be(1) }
it("Float returns self") { expect(flt.deep_dup).to be(flt) }
it("Symbol returns self") { expect(:symbol.deep_dup).to be(:symbol) }
it("BigDecimal duplicates") { expect(big.deep_dup).to not_be(big) }
it("String duplicates") { expect("A string".deep_dup).to not_be("A string") }
it("Array duplicates deeply") do
element = [String.new("A string")]
array = [element]
expect(array.deep_dup[0][0]).to not_equal(element[0]).and eq(element[0])
expect(array.deep_dup[0][0]).to not_be(element[0]).and eq(element[0])
end
it("Hash duplicates deeply") do
element = { "first" => String.new("A string") }
hash = { first: element }
expect(hash.deep_dup[:first]["first"]).to not_equal(element["first"])
expect(hash.deep_dup[:first]["first"]).to not_be(element["first"])
.and eq(element["first"])
end
it("Set duplicates deeply") do
element = [BigDecimal.new("1.5")]
element = [big]
set = Set.new([element])
expect(set.deep_dup.to_a.first).to not_equal(element).and eq(element)
expect(set.deep_dup.to_a.first).to not_be(element).and eq(element)
end
end

context "#safe_copy" do # rubocop:disable BlockLength
it("NilClass returns self") { expect(nil.safe_copy).to equal(nil) }
it("FalseClass returns self") { expect(false.safe_copy).to equal(false) }
it("TrueClass returns self") { expect(true.safe_copy).to equal(true) }
it("Integer returns self") { expect(1.safe_copy).to equal(1) }
it("Float returns self") { expect(flt.safe_copy).to equal(flt) }
it("Symbol returns self") { expect(:symbol.safe_copy).to equal(:symbol) }
it("NilClass returns self") { expect(nil.safe_copy).to be(nil) }
it("FalseClass returns self") { expect(false.safe_copy).to be(false) }
it("TrueClass returns self") { expect(true.safe_copy).to be(true) }
it("Integer returns self") { expect(1.safe_copy).to be(1) }
it("Float returns self") { expect(flt.safe_copy).to be(flt) }
it("Symbol returns self") { expect(:symbol.safe_copy).to be(:symbol) }
it("BigDecimal duplicates and freeze") do
expect(BigDecimal.new("1.5").safe_copy).to not_equal(BigDecimal.new("1.5"))
.and eq(BigDecimal.new("1.5")).and be_frozen
expect(big.safe_copy).to not_be(big)
.and eq(big).and be_frozen
end
it("Frozen string returns self") do
frozen_string = String.new("A string").freeze
expect(frozen_string.safe_copy).to equal(frozen_string).and be_frozen
expect(frozen_string.safe_copy).to be(frozen_string).and be_frozen
end
it("String duplicates and freeze") do
unfrozen_string = String.new("A string")
expect(unfrozen_string.safe_copy).to not_equal(unfrozen_string)
expect(unfrozen_string.safe_copy).to not_be(unfrozen_string)
.and eq(unfrozen_string).and be_frozen
end
it("Array duplicates and freezes") do
expect([1, 2, 3].safe_copy).to not_equal([1, 2, 3]).and eq([1, 2, 3])
expect(ary.safe_copy).to not_be(ary).and eq(ary)
.and be_frozen
end
it("Hash duplicates and freezes") do
expect({ a: 1, b: 2 }.safe_copy).to not_equal(a: 1, b: 2).and eq(a: 1, b: 2)
expect(hsh.safe_copy).to not_be(hsh).and eq(hsh)
.and be_frozen
end
it("Set duplicates and freezes") do
expect(Set.new([1, 2]).safe_copy).to not_equal(Set.new([1, 2]))
.and eq(Set.new([1, 2])).and be_frozen
expect(set.safe_copy).to not_be(set)
.and eq(set).and be_frozen
end
it("Object duplicates and freezes") do
struct = Struct.new("Test")
object = struct.new
expect(object.safe_copy).to not_equal(object).and eq(object).and be_frozen
expect(object.safe_copy).to not_be(object).and eq(object).and be_frozen
end
end

context "#blank?" do
it("Object returns false") { expect(Object.new.blank?).to equal(false) }
it("NilClass returns true") { expect(nil.blank?).to equal(true) }
it("FalseClass returns true") { expect(false.blank?).to equal(true) }
it("TrueClass returns false") { expect(true.blank?).to equal(false) }
it("Integer returns false") { expect(1.blank?).to equal(false) }
it("Float returns false") { expect(flt.blank?).to equal(false) }
it("Symbol returns false") { expect(:symbol.blank?).to equal(false) }
it("BigDecimal returns false") { expect(BigDecimal.new("1.5").blank?).to equal(false) }
it("String returns true if empty") { expect("".blank?).to equal(true) }
it("String returns true if unicode whitespace") { expect("\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000".blank?).to equal(true) }
it("String returns true if regular whitespace") { expect(" ".blank?).to equal(true) }
it("String returns false if not empty") { expect("A string".blank?).to equal(false) }
it("Array returns true if empty") { expect([].blank?).to equal(true) }
it("Array returns false if not empty") { expect([1].blank?).to equal(false) }
it("Hash returns true if empty") { expect({}.blank?).to equal(true) }
it("Hash returns false if not empty") { expect({ a: 1 }.blank?).to equal(false) }
it("Set returns true if empty") { expect(Set.new.blank?).to equal(true) }
it("Set returns false if not empty") { expect(Set.new([1, 2]).blank?).to equal(false) }
it("Object returns false") { expect(Object.new.blank?).to be(false) }
it("NilClass returns true") { expect(nil.blank?).to be(true) }
it("FalseClass returns true") { expect(false.blank?).to be(true) }
it("TrueClass returns false") { expect(true.blank?).to be(false) }
it("Integer returns false") { expect(1.blank?).to be(false) }
it("Float returns false") { expect(flt.blank?).to be(false) }
it("Symbol returns false") { expect(:symbol.blank?).to be(false) }
it("BigDecimal returns false") { expect(big.blank?).to be(false) }
it("String returns true if empty") { expect("".blank?).to be(true) }
it("String returns true if unicode whitespace") { expect("\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000".blank?).to be(true) }
it("String returns true if regular whitespace") { expect(" ".blank?).to be(true) }
it("String returns false if not empty") { expect("A string".blank?).to be(false) }
it("Array returns true if empty") { expect([].blank?).to be(true) }
it("Array returns false if not empty") { expect(ary.blank?).to be(false) }
it("Hash returns true if empty") { expect({}.blank?).to be(true) }
it("Hash returns false if not empty") { expect(hsh.blank?).to be(false) }
it("Set returns true if empty") { expect(Set.new.blank?).to be(true) }
it("Set returns false if not empty") { expect(set.blank?).to be(false) }
end

context "#present?" do
it("Object returns true") { expect(Object.new.present?).to equal(true) }
it("NilClass returns false") { expect(nil.present?).to equal(false) }
it("FalseClass returns false") { expect(false.present?).to equal(false) }
it("TrueClass returns true") { expect(true.present?).to equal(true) }
it("Integer returns true") { expect(1.present?).to equal(true) }
it("Float returns true") { expect(flt.present?).to equal(true) }
it("Symbol returns true") { expect(:symbol.present?).to equal(true) }
it("BigDecimal returns true") { expect(BigDecimal.new("1.5").present?).to equal(true) }
it("String returns false if empty") { expect("".present?).to equal(false) }
it("String returns false if unicode whitespace") { expect("\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000".present?).to equal(false) }
it("String returns false if regular whitespace") { expect(" ".present?).to equal(false) }
it("String returns true if not empty") { expect("A string".present?).to equal(true) }
it("Array returns false if empty") { expect([].present?).to equal(false) }
it("Array returns true if not empty") { expect([1].present?).to equal(true) }
it("Hash returns false if empty") { expect({}.present?).to equal(false) }
it("Hash returns true if not empty") { expect({ a: 1 }.present?).to equal(true) }
it("Set returns false if empty") { expect(Set.new.present?).to equal(false) }
it("Set returns true if not empty") { expect(Set.new([1, 2]).present?).to equal(true) }
it("Object returns true") { expect(Object.new.present?).to be(true) }
it("NilClass returns false") { expect(nil.present?).to be(false) }
it("FalseClass returns false") { expect(false.present?).to be(false) }
it("TrueClass returns true") { expect(true.present?).to be(true) }
it("Integer returns true") { expect(1.present?).to be(true) }
it("Float returns true") { expect(flt.present?).to be(true) }
it("Symbol returns true") { expect(:symbol.present?).to be(true) }
it("BigDecimal returns true") { expect(big.present?).to be(true) }
it("String returns false if empty") { expect("".present?).to be(false) }
it("String returns false if unicode whitespace") { expect("\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000".present?).to be(false) }
it("String returns false if regular whitespace") { expect(" ".present?).to be(false) }
it("String returns true if not empty") { expect("A string".present?).to be(true) }
it("Array returns false if empty") { expect([].present?).to be(false) }
it("Array returns true if not empty") { expect(ary.present?).to be(true) }
it("Hash returns false if empty") { expect({}.present?).to be(false) }
it("Hash returns true if not empty") { expect(hsh.present?).to be(true) }
it("Set returns false if empty") { expect(Set.new.present?).to be(false) }
it("Set returns true if not empty") { expect(set.present?).to be(true) }
end
end
2 changes: 1 addition & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
require "coveralls"
Coveralls.wear!

RSpec::Matchers.define_negated_matcher :not_equal, :equal
RSpec::Matchers.define_negated_matcher :not_be, :be

$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require "ragabash"

0 comments on commit 8e52888

Please sign in to comment.