naught 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/lib/naught/basic_object.rb +4 -14
  4. data/lib/naught/call_location.rb +131 -0
  5. data/lib/naught/caller_info.rb +128 -0
  6. data/lib/naught/chain_proxy.rb +51 -0
  7. data/lib/naught/conversions.rb +108 -34
  8. data/lib/naught/null_class_builder/command.rb +42 -5
  9. data/lib/naught/null_class_builder/commands/callstack.rb +89 -0
  10. data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +25 -9
  11. data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +22 -12
  12. data/lib/naught/null_class_builder/commands/impersonate.rb +21 -5
  13. data/lib/naught/null_class_builder/commands/mimic.rb +87 -25
  14. data/lib/naught/null_class_builder/commands/null_safe_proxy.rb +92 -0
  15. data/lib/naught/null_class_builder/commands/pebble.rb +21 -18
  16. data/lib/naught/null_class_builder/commands/predicates_return.rb +51 -31
  17. data/lib/naught/null_class_builder/commands/singleton.rb +18 -17
  18. data/lib/naught/null_class_builder/commands/traceable.rb +21 -12
  19. data/lib/naught/null_class_builder/commands.rb +10 -8
  20. data/lib/naught/null_class_builder.rb +217 -120
  21. data/lib/naught/stub_strategy.rb +30 -0
  22. data/lib/naught/version.rb +3 -1
  23. data/lib/naught.rb +31 -7
  24. metadata +34 -66
  25. data/.gitignore +0 -23
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -74
  28. data/.travis.yml +0 -20
  29. data/Changelog.md +0 -12
  30. data/Gemfile +0 -31
  31. data/Guardfile +0 -15
  32. data/README.markdown +0 -436
  33. data/Rakefile +0 -15
  34. data/naught.gemspec +0 -22
  35. data/spec/base_object_spec.rb +0 -47
  36. data/spec/basic_null_object_spec.rb +0 -35
  37. data/spec/blackhole_spec.rb +0 -16
  38. data/spec/explicit_conversions_spec.rb +0 -23
  39. data/spec/functions/actual_spec.rb +0 -22
  40. data/spec/functions/just_spec.rb +0 -22
  41. data/spec/functions/maybe_spec.rb +0 -35
  42. data/spec/functions/null_spec.rb +0 -34
  43. data/spec/implicit_conversions_spec.rb +0 -25
  44. data/spec/mimic_spec.rb +0 -117
  45. data/spec/naught/null_object_builder/command_spec.rb +0 -10
  46. data/spec/naught/null_object_builder_spec.rb +0 -31
  47. data/spec/naught_spec.rb +0 -101
  48. data/spec/pebble_spec.rb +0 -77
  49. data/spec/predicate_spec.rb +0 -84
  50. data/spec/singleton_null_object_spec.rb +0 -35
  51. data/spec/spec_helper.rb +0 -13
  52. data/spec/support/convertable_null.rb +0 -4
  53. data/spec/support/jruby.rb +0 -3
  54. data/spec/support/rubinius.rb +0 -3
  55. data/spec/support/ruby_18.rb +0 -3
@@ -1,16 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'black hole null object' do
4
- subject(:null) { null_class.new }
5
- let(:null_class) do
6
- Naught.build do |b|
7
- b.black_hole
8
- end
9
- end
10
-
11
- it 'returns self from arbitray method calls' do
12
- expect(null.info).to be(null)
13
- expect(null.foobaz).to be(null)
14
- expect(null << 'bar').to be(null)
15
- end
16
- end
@@ -1,23 +0,0 @@
1
- require 'spec_helper.rb'
2
-
3
- describe 'explicitly convertable null object' do
4
- let(:null_class) do
5
- Naught.build do |b|
6
- b.define_explicit_conversions
7
- end
8
- end
9
- subject(:null) { null_class.new }
10
-
11
- it 'defines common explicit conversions to return zero values' do
12
- expect(null.to_s).to eq('')
13
- expect(null.to_a).to eq([])
14
- expect(null.to_i).to eq(0)
15
- expect(null.to_f).to eq(0.0)
16
- if RUBY_VERSION >= '2.0'
17
- expect(null.to_h).to eq({})
18
- elsif RUBY_VERSION >= '1.9'
19
- expect(null.to_c).to eq(Complex(0))
20
- expect(null.to_r).to eq(Rational(0))
21
- end
22
- end
23
- end
@@ -1,22 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Actual()' do
4
- include ConvertableNull::Conversions
5
-
6
- specify 'given a null object, returns nil' do
7
- null = ConvertableNull.get
8
- expect(Actual(null)).to be_nil
9
- end
10
-
11
- specify 'given anything else, returns the input unchanged' do
12
- expect(Actual(false)).to be(false)
13
- str = 'hello'
14
- expect(Actual(str)).to be(str)
15
- expect(Actual(nil)).to be_nil
16
- end
17
-
18
- it 'also works with blocks' do
19
- expect(Actual { ConvertableNull.new }).to be_nil
20
- expect(Actual { 'foo' }).to eq('foo')
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Just()' do
4
- include ConvertableNull::Conversions
5
-
6
- specify 'passes non-nullish values through' do
7
- expect(Just(false)).to be(false)
8
- str = 'hello'
9
- expect(Just(str)).to be(str)
10
- end
11
-
12
- specify 'rejects nullish values' do
13
- expect { Just(nil) }.to raise_error(ArgumentError)
14
- expect { Just('') }.to raise_error(ArgumentError)
15
- expect { Just(ConvertableNull.get) }.to raise_error(ArgumentError)
16
- end
17
-
18
- it 'also works with blocks' do
19
- expect { Just { nil }.class }.to raise_error(ArgumentError)
20
- expect(Just { 'foo' }).to eq('foo')
21
- end
22
- end
@@ -1,35 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Maybe()' do
4
- include ConvertableNull::Conversions
5
-
6
- specify 'given nil, returns a null object' do
7
- expect(Maybe(nil).class).to be(ConvertableNull)
8
- end
9
-
10
- specify 'given a null object, returns the same null object' do
11
- null = ConvertableNull.get
12
- expect(Maybe(null)).to be(null)
13
- end
14
-
15
- specify 'given anything in null_equivalents, return a null object' do
16
- expect(Maybe('').class).to be(ConvertableNull)
17
- end
18
-
19
- specify 'given anything else, returns the input unchanged' do
20
- expect(Maybe(false)).to be(false)
21
- str = 'hello'
22
- expect(Maybe(str)).to be(str)
23
- end
24
-
25
- it 'generates null objects with useful trace info' do
26
- null, line = Maybe(), __LINE__
27
- expect(null.__file__).to eq(__FILE__)
28
- expect(null.__line__).to eq(line)
29
- end
30
-
31
- it 'also works with blocks' do
32
- expect(Maybe { nil }.class).to eq(ConvertableNull)
33
- expect(Maybe { 'foo' }).to eq('foo')
34
- end
35
- end
@@ -1,34 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Null()' do
4
- include ConvertableNull::Conversions
5
-
6
- specify 'given no input, returns a null object' do
7
- expect(Null().class).to be(ConvertableNull)
8
- end
9
-
10
- specify 'given nil, returns a null object' do
11
- expect(Null(nil).class).to be(ConvertableNull)
12
- end
13
-
14
- specify 'given a null object, returns the same null object' do
15
- null = ConvertableNull.get
16
- expect(Null(null)).to be(null)
17
- end
18
-
19
- specify 'given anything in null_equivalents, return a null object' do
20
- expect(Null('').class).to be(ConvertableNull)
21
- end
22
-
23
- specify 'given anything else, raises an ArgumentError' do
24
- expect { Null(false) }.to raise_error(ArgumentError)
25
- expect { Null('hello') }.to raise_error(ArgumentError)
26
- end
27
-
28
- it 'generates null objects with useful trace info' do
29
- null, line = Null(), __LINE__
30
- expect(null.__file__).to eq(__FILE__)
31
- expect(null.__line__).to eq(line)
32
- end
33
-
34
- end
@@ -1,25 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'implicitly convertable null object' do
4
- subject(:null) { null_class.new }
5
- let(:null_class) do
6
- Naught.build do |b|
7
- b.define_implicit_conversions
8
- end
9
- end
10
- it 'implicitly splats the same way an empty array does' do
11
- a, b = null
12
- expect(a).to be_nil
13
- expect(b).to be_nil
14
- end
15
- it 'is implicitly convertable to String' do
16
- expect(instance_eval(null)).to be_nil
17
- end
18
- it 'implicitly converts to an empty array' do
19
- expect(null.to_ary).to eq([])
20
- end
21
- it 'implicitly converts to an empty string' do
22
- expect(null.to_str).to eq('')
23
- end
24
-
25
- end
data/spec/mimic_spec.rb DELETED
@@ -1,117 +0,0 @@
1
- require 'spec_helper'
2
- require 'logger'
3
-
4
- describe 'null object mimicking a class' do
5
- class User
6
- def login
7
- 'bob'
8
- end
9
- end
10
-
11
- module Authorizable
12
- def authorized_for?(object)
13
- true
14
- end
15
- end
16
-
17
- class LibraryPatron < User
18
- include Authorizable
19
-
20
- def member?
21
- true
22
- end
23
-
24
- def name
25
- 'Bob'
26
- end
27
-
28
- def notify_of_overdue_books(titles)
29
- puts 'Notifying...'
30
- end
31
- end
32
-
33
- subject(:null) { mimic_class.new }
34
- let(:mimic_class) do
35
- Naught.build do |b|
36
- b.mimic LibraryPatron
37
- end
38
- end
39
- it 'responds to all methods defined on the target class' do
40
- expect(null.member?).to be_nil
41
- expect(null.name).to be_nil
42
- expect(null.notify_of_overdue_books(['The Grapes of Wrath'])).to be_nil
43
- end
44
-
45
- it 'does not respond to methods not defined on the target class' do
46
- expect { null.foobar }.to raise_error(NoMethodError)
47
- end
48
-
49
- it 'reports which messages it does and does not respond to' do
50
- expect(null).to respond_to(:member?)
51
- expect(null).to respond_to(:name)
52
- expect(null).to respond_to(:notify_of_overdue_books)
53
- expect(null).not_to respond_to(:foobar)
54
- end
55
- it 'has an informative inspect string' do
56
- expect(null.inspect).to eq('<null:LibraryPatron>')
57
- end
58
-
59
- it 'excludes Object methods from being mimicked' do
60
- expect(null.object_id).not_to be_nil
61
- expect(null.hash).not_to be_nil
62
- end
63
-
64
- it 'includes inherited methods' do
65
- expect(null.authorized_for?('something')).to be_nil
66
- expect(null.login).to be_nil
67
- end
68
-
69
- describe 'with include_super: false' do
70
- let(:mimic_class) do
71
- Naught.build do |b|
72
- b.mimic LibraryPatron, :include_super => false
73
- end
74
- end
75
-
76
- it 'excludes inherited methods' do
77
- expect(null).to_not respond_to(:authorized_for?)
78
- expect(null).to_not respond_to(:login)
79
- end
80
- end
81
- end
82
-
83
- describe 'using mimic with black_hole' do
84
- subject(:null) { mimic_class.new }
85
- let(:mimic_class) do
86
- Naught.build do |b|
87
- b.mimic Logger
88
- b.black_hole
89
- end
90
- end
91
-
92
- def self.it_behaves_like_a_black_hole_mimic
93
- it 'returns self from mimicked methods' do
94
- expect(null.info).to equal(null)
95
- expect(null.error).to equal(null)
96
- expect(null << 'test').to equal(null)
97
- end
98
-
99
- it 'does not respond to methods not defined on the target class' do
100
- expect { null.foobar }.to raise_error(NoMethodError)
101
- end
102
- end
103
-
104
- it_behaves_like_a_black_hole_mimic
105
-
106
- describe '(reverse order)' do
107
- let(:mimic_class) do
108
- Naught.build do |b|
109
- b.black_hole
110
- b.mimic Logger
111
- end
112
- end
113
-
114
- it_behaves_like_a_black_hole_mimic
115
- end
116
-
117
- end
@@ -1,10 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Naught
4
- describe NullClassBuilder::Command do
5
- it 'is abstract' do
6
- command = NullClassBuilder::Command.new(nil)
7
- expect { command.call }.to raise_error(NotImplementedError)
8
- end
9
- end
10
- end
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Naught
4
- class NullClassBuilder
5
- module Commands
6
- class TestCommand
7
- end
8
- end
9
- end
10
-
11
- describe NullClassBuilder do
12
- subject(:builder) { NullClassBuilder.new }
13
- it 'responds to commands defined in NullObjectBuilder::Commands' do
14
- expect(builder).to respond_to(:test_command)
15
- end
16
-
17
- it 'translates method calls into command invocations including arguments' do
18
- test_command = double
19
- expect(NullClassBuilder::Commands::TestCommand).to receive(:new).
20
- with(builder, 'foo', 42).
21
- and_return(test_command)
22
- expect(test_command).to receive(:call).and_return('COMMAND RESULT')
23
- expect(builder.test_command('foo', 42)).to eq('COMMAND RESULT')
24
- end
25
-
26
- it 'handles missing non-command missing methods normally' do
27
- expect(builder).not_to respond_to(:nonexistant_method)
28
- expect { builder.nonexistent_method }.to raise_error(NoMethodError)
29
- end
30
- end
31
- end
data/spec/naught_spec.rb DELETED
@@ -1,101 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'null object impersonating another type' do
4
- class Point
5
- def x
6
- 23
7
- end
8
-
9
- def y
10
- 42
11
- end
12
- end
13
-
14
- subject(:null) { impersonation_class.new }
15
- let(:impersonation_class) do
16
- Naught.build do |b|
17
- b.impersonate Point
18
- end
19
- end
20
-
21
- it 'matches the impersonated type' do
22
- expect(null).to be_a Point
23
- end
24
-
25
- it 'responds to methods from the impersonated type' do
26
- expect(null.x).to be_nil
27
- expect(null.y).to be_nil
28
- end
29
-
30
- it 'does not respond to unknown methods' do
31
- expect { null.foo }.to raise_error(NoMethodError)
32
- end
33
- end
34
-
35
- describe 'traceable null object' do
36
- subject(:trace_null) do
37
- null_object_and_line.first
38
- end
39
- let(:null_object_and_line) do
40
- obj, line = trace_null_class.new, __LINE__
41
- [obj, line]
42
- end
43
- let(:instantiation_line) { null_object_and_line.last }
44
- let(:trace_null_class) do
45
- Naught.build do |b|
46
- b.traceable
47
- end
48
- end
49
-
50
- it 'remembers the file it was instantiated from' do
51
- expect(trace_null.__file__).to eq(__FILE__)
52
- end
53
-
54
- it 'remembers the line it was instantiated from' do
55
- expect(trace_null.__line__).to eq(instantiation_line)
56
- end
57
-
58
- def make_null
59
- trace_null_class.get(:caller => caller(1))
60
- end
61
-
62
- it 'can accept custom backtrace info' do
63
- obj, line = make_null, __LINE__
64
- expect(obj.__line__).to eq(line)
65
- end
66
- end
67
-
68
- describe 'customized null object' do
69
- subject(:custom_null) { custom_null_class.new }
70
- let(:custom_null_class) do
71
- Naught.build do |b|
72
- b.define_explicit_conversions
73
- def to_path
74
- '/dev/null'
75
- end
76
-
77
- def to_s
78
- 'NOTHING TO SEE HERE'
79
- end
80
- end
81
- end
82
-
83
- it 'responds to custom-defined methods' do
84
- expect(custom_null.to_path).to eq('/dev/null')
85
- end
86
-
87
- it 'allows generated methods to be overridden' do
88
- expect(custom_null.to_s).to eq('NOTHING TO SEE HERE')
89
- end
90
- end
91
- TestNull = Naught.build
92
-
93
- describe 'a named null object class' do
94
- it 'has named ancestor modules', :pending => rubinius? do
95
- expect(TestNull.ancestors[0..2].collect(&:name)).to eq([
96
- 'TestNull',
97
- 'TestNull::Customizations',
98
- 'TestNull::GeneratedMethods'
99
- ])
100
- end
101
- end
data/spec/pebble_spec.rb DELETED
@@ -1,77 +0,0 @@
1
- require 'spec_helper'
2
- require 'stringio'
3
-
4
- describe 'pebble null object' do
5
- class Caller
6
- def call_method(thing)
7
- thing.info
8
- end
9
-
10
- def call_method_inside_block(thing)
11
- 2.times.each { thing.info }
12
- end
13
-
14
- def call_method_inside_nested_block(thing)
15
- 2.times.each { 2.times.each { thing.info } }
16
- end
17
- end
18
-
19
- subject(:null) { null_class.new }
20
- let(:null_class) do
21
- output = test_output # getting local binding
22
- Naught.build do |b|
23
- b.pebble output
24
- end
25
- end
26
-
27
- let(:test_output) { StringIO.new }
28
-
29
- it 'prints the name of the method called' do
30
- expect(test_output).to receive(:puts).with(/^info\(\)/)
31
- null.info
32
- end
33
-
34
- it 'prints the arguments received' do
35
- expect(test_output).to receive(:puts).with(/^info\(\'foo\', 5, \:sym\)/)
36
- null.info('foo', 5, :sym)
37
- end
38
-
39
- it 'prints the name of the caller' do
40
- expect(test_output).to receive(:puts).with(/from call_method$/)
41
- Caller.new.call_method(null)
42
- end
43
-
44
- it 'returns self' do
45
- expect(null.info).to be(null)
46
- end
47
-
48
- context 'when is called from a block' do
49
- it 'prints the indication of a block',
50
- :pending => jruby? || rubinius? || ruby_18? do
51
- expect(test_output).to receive(:puts).twice.
52
- with(/from block/)
53
- Caller.new.call_method_inside_block(null)
54
- end
55
-
56
- it 'prints the name of the method that has the block' do
57
- expect(test_output).to receive(:puts).twice.
58
- with(/call_method_inside_block$/)
59
- Caller.new.call_method_inside_block(null)
60
- end
61
- end
62
-
63
- context 'when is called from many levels blocks' do
64
- it 'prints the indication of blocks and its levels',
65
- :pending => jruby? || rubinius? || ruby_18? do
66
- expect(test_output).to receive(:puts).exactly(4).times.
67
- with(/from block \(2 levels\)/)
68
- Caller.new.call_method_inside_nested_block(null)
69
- end
70
-
71
- it 'prints the name of the method that has the block' do
72
- expect(test_output).to receive(:puts).exactly(4).times.
73
- with(/call_method_inside_nested_block$/)
74
- Caller.new.call_method_inside_nested_block(null)
75
- end
76
- end
77
- end
@@ -1,84 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'a null object with predicates_return(false)' do
4
- subject(:null) { null_class.new }
5
- let(:null_class) do
6
- Naught.build do |config|
7
- config.predicates_return false
8
- end
9
- end
10
-
11
- it 'responds to predicate-style methods with false' do
12
- expect(null.too_much_coffee?).to eq(false)
13
- end
14
-
15
- it 'responds to other methods with nil' do
16
- expect(null.foobar).to eq(nil)
17
- end
18
-
19
- describe '(black hole)' do
20
- let(:null_class) do
21
- Naught.build do |config|
22
- config.black_hole
23
- config.predicates_return false
24
- end
25
- end
26
-
27
- it 'responds to predicate-style methods with false' do
28
- expect(null.too_much_coffee?).to eq(false)
29
- end
30
-
31
- it 'responds to other methods with self' do
32
- expect(null.foobar).to be(null)
33
- end
34
- end
35
-
36
- describe '(black hole, reverse order config)' do
37
- let(:null_class) do
38
- Naught.build do |config|
39
- config.predicates_return false
40
- config.black_hole
41
- end
42
- end
43
-
44
- it 'responds to predicate-style methods with false' do
45
- expect(null.too_much_coffee?).to eq(false)
46
- end
47
-
48
- it 'responds to other methods with self' do
49
- expect(null.foobar).to be(null)
50
- end
51
- end
52
-
53
- class Coffee
54
- def black?
55
- true
56
- end
57
-
58
- def origin
59
- 'Ethiopia'
60
- end
61
- end
62
-
63
- describe '(mimic)' do
64
- let(:null_class) do
65
- Naught.build do |config|
66
- config.mimic Coffee
67
- config.predicates_return false
68
- end
69
- end
70
-
71
- it 'responds to predicate-style methods with false' do
72
- expect(null.black?).to eq(false)
73
- end
74
-
75
- it 'responds to other methods with nil' do
76
- expect(null.origin).to be(nil)
77
- end
78
-
79
- it 'does not respond to undefined methods' do
80
- expect(null).not_to respond_to(:leaf_variety)
81
- expect { null.leaf_variety }.to raise_error(NoMethodError)
82
- end
83
- end
84
- end
@@ -1,35 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'singleton null object' do
4
- subject(:null_class) do
5
- Naught.build do |b|
6
- b.singleton
7
- end
8
- end
9
-
10
- it 'does not respond to .new' do
11
- expect { null_class.new }.to raise_error
12
- end
13
-
14
- it 'has only one instance' do
15
- null1 = null_class.instance
16
- null2 = null_class.instance
17
- expect(null1).to be(null2)
18
- end
19
-
20
- it 'can be cloned' do
21
- null = null_class.instance
22
- expect(null.clone).to be(null)
23
- end
24
-
25
- it 'can be duplicated' do
26
- null = null_class.instance
27
- expect(null.dup).to be(null)
28
- end
29
- it 'aliases .instance to .get' do
30
- expect(null_class.get).to be null_class.instance
31
- end
32
- it 'permits arbitrary arguments to be passed to .get' do
33
- null_class.get(42, :foo => 'bar')
34
- end
35
- end
data/spec/spec_helper.rb DELETED
@@ -1,13 +0,0 @@
1
- GEM_ROOT = File.expand_path('../../', __FILE__)
2
- $LOAD_PATH.unshift File.join(GEM_ROOT, 'lib')
3
-
4
- if ENV['TRAVIS']
5
- require 'coveralls'
6
- Coveralls.wear!
7
- else
8
- require 'simplecov'
9
- SimpleCov.start
10
- end
11
-
12
- require 'naught'
13
- Dir[File.join(GEM_ROOT, 'spec', 'support', '**/*.rb')].each { |f| require f }
@@ -1,4 +0,0 @@
1
- ConvertableNull = Naught.build do |b|
2
- b.null_equivalents << ''
3
- b.traceable
4
- end
@@ -1,3 +0,0 @@
1
- def jruby?
2
- RUBY_PLATFORM == 'java'
3
- end
@@ -1,3 +0,0 @@
1
- def rubinius?
2
- defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
3
- end
@@ -1,3 +0,0 @@
1
- def ruby_18?
2
- RUBY_VERSION.to_f == 1.8
3
- end