corefines 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.adoc +198 -0
- data/Rakefile +23 -0
- data/lib/corefines/array.rb +30 -0
- data/lib/corefines/enumerable.rb +64 -0
- data/lib/corefines/hash.rb +164 -0
- data/lib/corefines/module.rb +95 -0
- data/lib/corefines/object.rb +389 -0
- data/lib/corefines/string.rb +211 -0
- data/lib/corefines/support/alias_submodules.rb +103 -0
- data/lib/corefines/support/classes_including_module.rb +27 -0
- data/lib/corefines/support/fake_refinements.rb +86 -0
- data/lib/corefines/symbol.rb +32 -0
- data/lib/corefines/version.rb +3 -0
- data/lib/corefines.rb +14 -0
- data/spec/array/second_spec.rb +10 -0
- data/spec/array/third_spec.rb +10 -0
- data/spec/enumerable/index_by_spec.rb +13 -0
- data/spec/enumerable/map_send_spec.rb +24 -0
- data/spec/hash/compact_spec.rb +48 -0
- data/spec/hash/op_plus_spec.rb +11 -0
- data/spec/hash/rekey_spec.rb +100 -0
- data/spec/hash/symbolize_keys_spec.rb +21 -0
- data/spec/module/alias_class_method_spec.rb +21 -0
- data/spec/module/alias_method_chain_spec.rb +76 -0
- data/spec/object/blank_spec.rb +128 -0
- data/spec/object/deep_dup_spec.rb +61 -0
- data/spec/object/else_spec.rb +24 -0
- data/spec/object/in_spec.rb +21 -0
- data/spec/object/instance_values_spec.rb +22 -0
- data/spec/object/then_if_spec.rb +64 -0
- data/spec/object/then_spec.rb +26 -0
- data/spec/object/try_spec.rb +47 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/string/color_spec.rb +82 -0
- data/spec/string/concat_spec.rb +36 -0
- data/spec/string/decolor_spec.rb +27 -0
- data/spec/string/remove_spec.rb +57 -0
- data/spec/string/unindent_spec.rb +66 -0
- data/spec/support/alias_submodules_spec.rb +83 -0
- data/spec/support/classes_including_module_spec.rb +35 -0
- data/spec/support/fake_refinements_spec.rb +118 -0
- data/spec/symbol/call_spec.rb +16 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|