corefines 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.adoc +198 -0
  4. data/Rakefile +23 -0
  5. data/lib/corefines/array.rb +30 -0
  6. data/lib/corefines/enumerable.rb +64 -0
  7. data/lib/corefines/hash.rb +164 -0
  8. data/lib/corefines/module.rb +95 -0
  9. data/lib/corefines/object.rb +389 -0
  10. data/lib/corefines/string.rb +211 -0
  11. data/lib/corefines/support/alias_submodules.rb +103 -0
  12. data/lib/corefines/support/classes_including_module.rb +27 -0
  13. data/lib/corefines/support/fake_refinements.rb +86 -0
  14. data/lib/corefines/symbol.rb +32 -0
  15. data/lib/corefines/version.rb +3 -0
  16. data/lib/corefines.rb +14 -0
  17. data/spec/array/second_spec.rb +10 -0
  18. data/spec/array/third_spec.rb +10 -0
  19. data/spec/enumerable/index_by_spec.rb +13 -0
  20. data/spec/enumerable/map_send_spec.rb +24 -0
  21. data/spec/hash/compact_spec.rb +48 -0
  22. data/spec/hash/op_plus_spec.rb +11 -0
  23. data/spec/hash/rekey_spec.rb +100 -0
  24. data/spec/hash/symbolize_keys_spec.rb +21 -0
  25. data/spec/module/alias_class_method_spec.rb +21 -0
  26. data/spec/module/alias_method_chain_spec.rb +76 -0
  27. data/spec/object/blank_spec.rb +128 -0
  28. data/spec/object/deep_dup_spec.rb +61 -0
  29. data/spec/object/else_spec.rb +24 -0
  30. data/spec/object/in_spec.rb +21 -0
  31. data/spec/object/instance_values_spec.rb +22 -0
  32. data/spec/object/then_if_spec.rb +64 -0
  33. data/spec/object/then_spec.rb +26 -0
  34. data/spec/object/try_spec.rb +47 -0
  35. data/spec/spec_helper.rb +30 -0
  36. data/spec/string/color_spec.rb +82 -0
  37. data/spec/string/concat_spec.rb +36 -0
  38. data/spec/string/decolor_spec.rb +27 -0
  39. data/spec/string/remove_spec.rb +57 -0
  40. data/spec/string/unindent_spec.rb +66 -0
  41. data/spec/support/alias_submodules_spec.rb +83 -0
  42. data/spec/support/classes_including_module_spec.rb +35 -0
  43. data/spec/support/fake_refinements_spec.rb +118 -0
  44. data/spec/symbol/call_spec.rb +16 -0
  45. metadata +175 -0
@@ -0,0 +1,61 @@
1
+ describe Object do
2
+ using Corefines::Object::deep_dup
3
+
4
+ describe '#deep_dup' do
5
+
6
+ it "duplicates array and its values" do
7
+ ary = ['foo', 'bar']
8
+ dup = ary.deep_dup
9
+ dup[1].prepend('x')
10
+
11
+ expect(dup).to_not be ary
12
+ expect(ary[1]).to eq 'bar'
13
+ expect(dup[1]).to eq 'xbar'
14
+ end
15
+
16
+ it "duplicates values inside a nested array" do
17
+ ary = ['foo', ['bar', 'baz']]
18
+ dup = ary.deep_dup
19
+ dup[1][0].prepend('x')
20
+
21
+ expect(ary[1][0]).to eq 'bar'
22
+ expect(dup[1][0]).to eq 'xbar'
23
+ end
24
+
25
+ it "duplicates a nested hash" do
26
+ hash = {a: {b: 'b'}}
27
+ dup = hash.deep_dup
28
+ dup[:a][:c] = 'c'
29
+
30
+ expect(hash[:a][:c]).to be_nil
31
+ expect(dup[:a][:c]).to eq 'c'
32
+ end
33
+
34
+ it "duplicates an object" do
35
+ obj = ::Object.new
36
+ dup = obj.deep_dup
37
+ dup.instance_variable_set(:@a, 1)
38
+
39
+ expect(obj.instance_variable_defined? :@a).to be_falsy
40
+ expect(dup.instance_variable_defined? :@a).to be_truthy
41
+ end
42
+
43
+ it "calls #deep_dup on nested object when it responds to it" do
44
+ obj = Object.new.instance_eval do
45
+ def deep_dup; 'foo' end
46
+ self
47
+ end
48
+ ary = [1, obj]
49
+ dup = ary.deep_dup
50
+
51
+ expect(ary[1]).to eql obj
52
+ expect(dup[1]).to eq 'foo'
53
+ end
54
+
55
+ it "doesn't raise error for non-duplicable object" do
56
+ [nil, false, true, :foo, 1.0, method(:to_s)].each do |obj|
57
+ expect { obj.deep_dup }.to_not raise_error
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,24 @@
1
+ describe Object do
2
+ using Corefines::Object::else
3
+
4
+ describe '#else' do
5
+
6
+ it "returns self without calling the block" do
7
+ obj = 'yay'
8
+ expect(obj.else { fail }).to eql obj
9
+ end
10
+
11
+ [ nil, false ].each do |val|
12
+ context "called on #{val.nil? ? 'nil' : val}" do
13
+
14
+ it "calls the block and returns result" do
15
+ expect(val.else { 42 }).to eq 42
16
+ end
17
+
18
+ it "passes self to the block" do
19
+ val.else { |x| expect(x).to eql val }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ describe Object do
2
+ using Corefines::Object::in?
3
+
4
+ describe '#in?' do
5
+ let(:other) { [] }
6
+
7
+ it "returns true when argument includes self" do
8
+ expect(other).to receive(:include?).with(42).and_return(true)
9
+ expect(42.in? other).to be true
10
+ end
11
+
12
+ it "returns false when argument doesn't include self" do
13
+ expect(other).to receive(:include?).with(66).and_return(false)
14
+ expect(66.in? other).to be false
15
+ end
16
+
17
+ it "raises ArgumentError when argument doesn't respond #include?" do
18
+ expect { 66.in? Object.new }.to raise_error ArgumentError
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ describe Object do
2
+ using Corefines::Object::instance_values
3
+
4
+ describe '#instance_values' do
5
+ let :obj do
6
+ Object.new.tap do |o|
7
+ o.instance_variable_set(:@foo, 'first')
8
+ o.instance_variable_set(:@bar, 'second')
9
+ end
10
+ end
11
+
12
+ it "returns hash that maps instance variables to their values" do
13
+ expect(obj.instance_values).to eq({foo: 'first', bar: 'second'})
14
+ end
15
+
16
+ context "no instance variables" do
17
+ it "returns an empty array" do
18
+ expect(''.instance_values).to be_empty
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,64 @@
1
+ describe Object do
2
+ using Corefines::Object::then_if
3
+
4
+ describe '#then_if' do
5
+
6
+ shared_examples 'conditions evaluates to true' do |*conditions|
7
+ it "yields self to the block and returns that block's value" do
8
+ expect('yay'.then_if(*conditions) { |x| x + '!' }).to eq 'yay!'
9
+ end
10
+ end
11
+
12
+ shared_examples 'conditions evaluates to false' do |*conditions|
13
+ it "returns self without calling the block" do
14
+ obj = 'yay'
15
+ expect(obj.then_if(*conditions) { fail 'block should not be called' }).to eql obj
16
+ end
17
+ end
18
+
19
+
20
+ context "no conditions given" do
21
+
22
+ context "and self evaluates to true" do
23
+ it_behaves_like 'conditions evaluates to true'
24
+ end
25
+
26
+ context "and self evaluates to false" do
27
+ [ false, nil ].each do |val|
28
+ it "returns self without calling the block" do
29
+ expect(val.then_if { fail 'block should not be called' }).to eql val
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ [ :ascii_only?, [:start_with?, 'y'], ->(x) { x.start_with? 'y' },
36
+ :ascii_only?.to_proc, true, 'foo'
37
+ ].each do |condition|
38
+ context "with #{condition.class} condition" do
39
+ include_examples 'conditions evaluates to true', condition
40
+ end
41
+ end
42
+
43
+ [ :empty?, [:start_with?, 'x'], ->(x) { x.start_with? 'x' },
44
+ :empty?.to_proc, false, nil
45
+ ].each do |condition|
46
+ context "with #{condition.class} condition that evaluates to true" do
47
+ include_examples 'conditions evaluates to false', condition
48
+ end
49
+ end
50
+
51
+ context "with multiple conditions" do
52
+
53
+ context "all evaluates to true" do
54
+ conditions = [ true, [:start_with?, 'y'], :ascii_only? ]
55
+ include_examples 'conditions evaluates to true', *conditions
56
+ end
57
+
58
+ context "one evaluates to false" do
59
+ conditions = [ true, [:start_with?, 'y'], false, :ascii_only? ]
60
+ include_examples 'conditions evaluates to false', *conditions
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,26 @@
1
+ describe Object do
2
+ using Corefines::Object::then
3
+
4
+ describe '#then' do
5
+
6
+ it "calls the block and returns result" do
7
+ expect('yay'.then { 42 }).to eq 42
8
+ end
9
+
10
+ it "passes self to the block" do
11
+ 'yay'.then { |x| expect(x).to eq 'yay' }
12
+ end
13
+
14
+ context "called on nil" do
15
+ it "returns nil without calling the block" do
16
+ expect(nil.then { fail }).to be_nil
17
+ end
18
+ end
19
+
20
+ context "called on false" do
21
+ it "returns false without calling the block" do
22
+ expect(false.then { fail }).to be false
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,47 @@
1
+ describe Object do
2
+ using Corefines::Object::try
3
+
4
+ let(:str) { "allons-y!" }
5
+
6
+ describe '#try' do
7
+ context "non-existing method" do
8
+ it "returns nil" do
9
+ expect(str.try(:non_existing_method)).to be_nil
10
+ end
11
+ end
12
+
13
+ context "valid method" do
14
+ it "calls and returns the result" do
15
+ expect(str.try(:upcase)).to eq "ALLONS-Y!"
16
+ end
17
+
18
+ it "forwards arguments" do
19
+ expect(str.try(:sub, '!', '?')).to eq "allons-y?"
20
+ end
21
+
22
+ it "forwards block" do
23
+ expect(str.try(:sub, '!') { |m| '?' }).to eq "allons-y?"
24
+ end
25
+ end
26
+
27
+ context "on nil" do
28
+ it "returns nil" do
29
+ expect(nil.try(:upcase)).to be_nil
30
+ end
31
+ end
32
+
33
+ context "on false" do
34
+ it "calls and returns the result" do
35
+ expect(false.try(:to_s)).to eq 'false'
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#try!' do
41
+ context "non-existing method" do
42
+ it "raises NoMethodError" do
43
+ expect { str.try!(:non_existing_method) }.to raise_error NoMethodError
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'simplecov'
2
+ require 'corefines'
3
+
4
+ RSpec.configure do |config|
5
+
6
+ # rspec-expectations config
7
+ config.expect_with :rspec do |expects|
8
+
9
+ # This option disables deprecated 'should' syntax.
10
+ expects.syntax = :expect
11
+
12
+ # This option makes the +description+ and +failure_message+ of custom
13
+ # matchers include text for helper methods defined using +chain+, e.g.:
14
+ # be_bigger_than(2).and_smaller_than(4).description
15
+ # # => "be bigger than 2 and smaller than 4"
16
+ # ...rather than:
17
+ # # => "be bigger than 2"
18
+ expects.include_chain_clauses_in_custom_matcher_descriptions = true
19
+ end
20
+
21
+ # rspec-mocks config
22
+ config.mock_with :rspec do |mocks|
23
+
24
+ # Prevents you from mocking or stubbing a method that does not exist on
25
+ # a real object.
26
+ mocks.verify_partial_doubles = true
27
+ end
28
+
29
+ config.color = true
30
+ end
@@ -0,0 +1,82 @@
1
+ describe String do
2
+ using Corefines::String::color
3
+
4
+ describe '#color' do
5
+
6
+ shared_examples :defaults do |opts|
7
+ it "returns text with the default mode, foreground and background colors" do
8
+ expect(text.color(opts)).to eq "\e[0;39;49m#{text}\e[0m"
9
+ end
10
+ end
11
+
12
+ subject(:text) { "This is\nsome text" }
13
+
14
+ context Symbol do
15
+ it "returns text with the specified foreground color" do
16
+ expect(text.color(:blue)).to eq "\e[0;34;49m#{text}\e[0m"
17
+ end
18
+
19
+ context "unknown color name" do
20
+ include_examples :defaults, :foo
21
+ end
22
+ end
23
+
24
+ context Fixnum do
25
+ it "returns text with the specified foreground color" do
26
+ expect(text.color(7)).to eq "\e[0;37;49m#{text}\e[0m"
27
+ end
28
+ end
29
+
30
+ context Hash do
31
+
32
+ context "empty" do
33
+ include_examples :defaults, {}
34
+ end
35
+
36
+ context "with unknown mode and color names" do
37
+ include_examples :defaults, {mode: :foo, text: :foo, background: :foo}
38
+ end
39
+
40
+ context "with valid options" do
41
+ it "returns text with the specified mode" do
42
+ [:bold, 1].each do |mode|
43
+ expect(text.color(mode: mode)).to eq "\e[1;39;49m#{text}\e[0m"
44
+ end
45
+ end
46
+
47
+ it "returns text with the specified foreground color" do
48
+ [:red, 1].each do |color|
49
+ expect(text.color(text: color)).to eq "\e[0;31;49m#{text}\e[0m"
50
+ end
51
+ end
52
+
53
+ it "returns text with the specified background color" do
54
+ [:green, 2].each do |color|
55
+ expect(text.color(background: color)).to eq "\e[0;39;42m#{text}\e[0m"
56
+ end
57
+ end
58
+
59
+ it "returns text with the specified mode, foreground and background colors" do
60
+ expect(text.color(mode: :bold, text: :blue, background: :yellow)).to eq "\e[1;34;43m#{text}\e[0m"
61
+ expect(text.color(mode: 1, text: 4, background: 3)).to eq "\e[1;34;43m#{text}\e[0m"
62
+ end
63
+ end
64
+ end
65
+
66
+ context "already colored text" do
67
+ it "changes specified and preserves unspecified attributes" do
68
+ expect("\e[1;34;42m#{text}\e[0m".color(text: :red)).to eq "\e[1;31;42m#{text}\e[0m"
69
+ end
70
+
71
+ it "doesn't change invalidly specified attributes" do
72
+ expect("\e[1;34;42m#{text}\e[0m".color(text: :foo)).to eq "\e[1;34;42m#{text}\e[0m"
73
+ end
74
+
75
+ it "changes multiple escape sequences" do
76
+ text = 'none' + 'red'.color(:red) + 'none' + 'blue'.color(:blue) + 'none'
77
+ expected = "\e[0;39;42mnone\e[0m\e[0;31;42mred\e[0m\e[0;39;42mnone\e[0m\e[0;34;42mblue\e[0m\e[0;39;42mnone\e[0m"
78
+ expect(text.color(background: :green)).to eq expected
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ describe String do
2
+ using Corefines::String::concat!
3
+
4
+ describe '#concat!' do
5
+
6
+ context "without separator" do
7
+ subject { 'foo' }
8
+
9
+ it "appends the given string to self" do
10
+ subject.concat! 'bar'
11
+ is_expected.to eq 'foobar'
12
+ end
13
+ end
14
+
15
+ context "with separator" do
16
+
17
+ context "when self is empty" do
18
+ subject { '' }
19
+
20
+ it "appends the given string to self" do
21
+ subject.concat! 'bar', "\n"
22
+ is_expected.to eq 'bar'
23
+ end
24
+ end
25
+
26
+ context "when self is not empty" do
27
+ subject { 'foo' }
28
+
29
+ it "appends the given separator and string to self" do
30
+ subject.concat! 'bar', "\n"
31
+ is_expected.to eq "foo\nbar"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ describe String do
2
+ using Corefines::String::decolor
3
+
4
+ describe '#decolor' do
5
+
6
+ subject(:text) { "This is\nsome text" }
7
+
8
+ context "colored text" do
9
+ it "strips ANSI escape sequence" do
10
+ expect("\e[1;34;42m#{text}\e[0m".decolor).to eq text
11
+ end
12
+ end
13
+
14
+ context "concatenated colored texts" do
15
+ it "strips all ANSI escape sequences" do
16
+ text = "\e[0;39;42mSome\ncolored\e[0m\e[0;31;42m text\e[0m"
17
+ expect(text.decolor).to eq "Some\ncolored text"
18
+ end
19
+ end
20
+
21
+ context "plain string" do
22
+ it "passes without change" do
23
+ expect(text.decolor).to eq text
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ describe String do
2
+ using Corefines::String::remove
3
+
4
+ subject(:str) { "This is a good day to die" }
5
+ let!(:original) { str.dup }
6
+
7
+ describe '#remove' do
8
+
9
+ context "string" do
10
+ it "returns a new string with occurrences of the pattern removed" do
11
+ expect(str.remove(" to die")).to eq "This is a good day"
12
+ expect(str.remove("is")).to eq "Th a good day to die"
13
+ expect(str).to eq original
14
+ end
15
+ end
16
+
17
+ context "regexp" do
18
+ it "returns a new string with occurrences of the pattern removed" do
19
+ expect(str.remove(/\s*to.*$/)).to eq "This is a good day"
20
+ expect(str.remove(/\s/)).to eq "Thisisagooddaytodie"
21
+ expect(str).to eq original
22
+ end
23
+ end
24
+
25
+ context "multiple patterns" do
26
+ it "returns a new string with occurrences of the patterns removed" do
27
+ expect(str.remove("to die", /\s*$/)).to eq "This is a good day"
28
+ expect(str.remove("to", "die", /\s*/)).to eq "Thisisagoodday"
29
+ expect(str).to eq original
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#remove!' do
35
+
36
+ context "string" do
37
+ it "removes occurrences of the pattern and returns self" do
38
+ expect(str.remove!(" to die")).to eq "This is a good day"
39
+ expect(str.remove!("is")).to eq "Th a good day"
40
+ end
41
+ end
42
+
43
+ context "regexp" do
44
+ it "removes occurrences of the pattern and returns self" do
45
+ expect(str.remove!(/\s*to.*$/)).to eq "This is a good day"
46
+ expect(str.remove!(/\s/)).to eq "Thisisagoodday"
47
+ end
48
+ end
49
+
50
+ context "multiple patterns" do
51
+ it "removes occurrences of the patterns and returns self" do
52
+ expect(str.remove!("to die", /\s*$/)).to eq "This is a good day"
53
+ expect(str.remove!("to", "die", /\s*/)).to eq "Thisisagoodday"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,66 @@
1
+ describe String do
2
+ using Corefines::String::unindent
3
+
4
+ describe '#unindent' do
5
+
6
+ context "single-line" do
7
+
8
+ it "removes space indentation" do
9
+ expect(" abc".unindent).to eq 'abc'
10
+ end
11
+
12
+ it "removes tab indentation" do
13
+ expect("\tabc".unindent).to eq 'abc'
14
+ end
15
+
16
+ it "removes mixed space and tab indentation" do
17
+ expect("\t\s\t\sabc".unindent).to eq 'abc'
18
+ end
19
+ end
20
+
21
+ context "multi-line" do
22
+
23
+ it "removes indentation" do
24
+ expect(" abc\n abc".unindent).to eq "abc\nabc"
25
+ end
26
+
27
+ it "keeps relative indentation" do
28
+ expect(" abc\n abc".unindent).to eq " abc\nabc"
29
+ end
30
+
31
+ it "ignores blank lines for indent calculation" do
32
+ expect("\n\tabc\n\n\t\tabc\n".unindent).to eq "\nabc\n\n\tabc\n"
33
+ end
34
+
35
+ it "unindents more complex input" do
36
+ input = <<-EOF
37
+ module WishScanner
38
+
39
+ def scan_for_a_wish
40
+ wish = self.read.detect do |thought|
41
+ thought.index('wish: ') == 0
42
+ end
43
+
44
+ wish.gsub('wish: ', '')
45
+ end
46
+ end
47
+ EOF
48
+
49
+ expected = <<-EOF
50
+ module WishScanner
51
+
52
+ def scan_for_a_wish
53
+ wish = self.read.detect do |thought|
54
+ thought.index('wish: ') == 0
55
+ end
56
+
57
+ wish.gsub('wish: ', '')
58
+ end
59
+ end
60
+ EOF
61
+
62
+ expect(input.unindent).to eq expected
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,83 @@
1
+ describe Corefines::Support::AliasSubmodules do
2
+
3
+ before :context do
4
+ # Define dynamically to not pollute global context of other tests.
5
+ ModuleX = Module.new.tap do |m|
6
+ m.class_eval <<-CODE
7
+ Something = 66
8
+ module Indent end
9
+ module StripHeredoc end
10
+ module Blank end
11
+ include Corefines::Support::AliasSubmodules
12
+ CODE
13
+ end
14
+ end
15
+
16
+ after :context do
17
+ Object.send(:remove_const, :ModuleX)
18
+ end
19
+
20
+
21
+ describe '.included' do
22
+
23
+ subject(:target) { ModuleX }
24
+
25
+ it 'defines method-named "alias" for each submodule' do
26
+ expect(ModuleX.indent).to eql ModuleX::Indent
27
+ expect(ModuleX::strip_heredoc).to eql ModuleX::StripHeredoc
28
+ end
29
+
30
+ it "ignores constants that are not modules" do
31
+ is_expected.to_not respond_to :something
32
+ end
33
+
34
+ it "includes all submodules into the target" do
35
+ is_expected.to include ModuleX::Indent, ModuleX::StripHeredoc
36
+ end
37
+ end
38
+
39
+
40
+ describe '.[]' do
41
+ subject { ModuleX[:indent, :strip_heredoc] }
42
+
43
+ it "returns module that includes the named modules" do
44
+ is_expected.to be_instance_of ::Module
45
+ is_expected.to include ModuleX::Indent, ModuleX::StripHeredoc
46
+ is_expected.to_not include ModuleX::Blank
47
+ end
48
+
49
+ it "raises ArgumentError if any submodule alias doesn't exist" do
50
+ expect { ModuleX[:indent, :invalid] }.to raise_error ArgumentError
51
+ end
52
+ end
53
+
54
+
55
+ describe '.method_name' do
56
+
57
+ def method_name(str)
58
+ described_class.send(:method_name, str)
59
+ end
60
+
61
+ it "string without any uppercase returns untouched" do
62
+ str = 'allonsy!'
63
+ expect(method_name str).to eql str
64
+ end
65
+
66
+ {
67
+ 'Unindent' => 'unindent',
68
+ 'StripHeredoc' => 'strip_heredoc',
69
+ 'ToJSON' => 'to_json',
70
+ }.merge(described_class.const_get(:OPERATORS_MAP)).each do |input, expected|
71
+
72
+ it "converts '#{input}' to '#{expected}'" do
73
+ input_clone = input.to_s.dup
74
+ expect(method_name input).to eq expected
75
+ expect(input.to_s).to eql input_clone
76
+ end
77
+
78
+ it "converts :#{input} to '#{expected}'" do
79
+ expect(method_name input.to_sym).to eq expected
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,35 @@
1
+ require 'corefines/support/classes_including_module'
2
+ require 'forwardable'
3
+
4
+ describe Corefines::Support do
5
+
6
+ describe '.classes_including_module' do
7
+ extend Forwardable
8
+
9
+ def_delegator :described_class, :classes_including_module
10
+
11
+ before :context do
12
+ ModuleX, ModuleY = Module.new, Module.new
13
+ ClassA, ClassB = Class.new, Class.new(Array)
14
+ [ClassA, ClassB].each { |m| m.send(:include, ModuleX) }
15
+ end
16
+
17
+ after :context do
18
+ [:ModuleX, :ModuleY, :ClassA, :ClassB].each do |name|
19
+ Object.send(:remove_const, name)
20
+ end
21
+ end
22
+
23
+ it "yields classes that includes the module" do
24
+ actual = []
25
+ classes_including_module(ModuleX) { |c| actual << c }
26
+ expect(actual).to contain_exactly ClassA, ClassB
27
+ end
28
+
29
+ it "yields nothing for module that isn't included anywhere" do
30
+ actual = []
31
+ classes_including_module(ModuleY) { |c| actual << c }
32
+ expect(actual).to be_empty
33
+ end
34
+ end
35
+ end