naught 0.0.3 → 1.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +74 -0
  4. data/.travis.yml +20 -2
  5. data/Changelog.md +8 -2
  6. data/Gemfile +24 -2
  7. data/README.markdown +30 -7
  8. data/Rakefile +11 -2
  9. data/lib/naught.rb +1 -1
  10. data/lib/naught/basic_object.rb +17 -0
  11. data/lib/naught/conversions.rb +55 -0
  12. data/lib/naught/null_class_builder.rb +33 -21
  13. data/lib/naught/null_class_builder/command.rb +3 -3
  14. data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +3 -7
  15. data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +7 -2
  16. data/lib/naught/null_class_builder/commands/impersonate.rb +2 -3
  17. data/lib/naught/null_class_builder/commands/mimic.rb +4 -7
  18. data/lib/naught/null_class_builder/commands/pebble.rb +3 -5
  19. data/lib/naught/null_class_builder/commands/predicates_return.rb +1 -2
  20. data/lib/naught/null_class_builder/commands/singleton.rb +1 -1
  21. data/lib/naught/null_class_builder/commands/traceable.rb +3 -2
  22. data/lib/naught/version.rb +1 -1
  23. data/naught.gemspec +11 -16
  24. data/spec/base_object_spec.rb +2 -2
  25. data/spec/basic_null_object_spec.rb +3 -3
  26. data/spec/blackhole_spec.rb +4 -4
  27. data/spec/explicit_conversions_spec.rb +23 -0
  28. data/spec/functions/actual_spec.rb +4 -4
  29. data/spec/functions/just_spec.rb +7 -7
  30. data/spec/functions/maybe_spec.rb +7 -7
  31. data/spec/functions/null_spec.rb +6 -6
  32. data/spec/implicit_conversions_spec.rb +5 -5
  33. data/spec/mimic_spec.rb +30 -35
  34. data/spec/naught/null_object_builder/command_spec.rb +1 -1
  35. data/spec/naught/null_object_builder_spec.rb +5 -5
  36. data/spec/naught_spec.rb +29 -23
  37. data/spec/pebble_spec.rb +13 -11
  38. data/spec/predicate_spec.rb +18 -14
  39. data/spec/singleton_null_object_spec.rb +4 -4
  40. data/spec/spec_helper.rb +4 -4
  41. data/spec/support/convertable_null.rb +2 -2
  42. data/spec/support/jruby.rb +3 -0
  43. data/spec/support/rubinius.rb +3 -0
  44. data/spec/support/ruby_18.rb +3 -0
  45. metadata +21 -82
  46. data/lib/naught/null_class_builder/conversions_module.rb +0 -57
  47. data/spec/conversions_spec.rb +0 -20
@@ -8,11 +8,11 @@ module Naught
8
8
  end
9
9
 
10
10
  def call
11
- raise NotImplementedError,
12
- "Method #call should be overriden in child classes"
11
+ fail NotImplementedError,
12
+ 'Method #call should be overriden in child classes'
13
13
  end
14
14
 
15
- def defer(options={}, &block)
15
+ def defer(options = {}, &block)
16
16
  @builder.defer(options, &block)
17
17
  end
18
18
  end
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'naught/null_class_builder/command'
2
3
 
3
4
  module Naught::NullClassBuilder::Commands
@@ -5,13 +6,8 @@ module Naught::NullClassBuilder::Commands
5
6
  def call
6
7
  defer do |subject|
7
8
  subject.module_eval do
8
- def to_s; ""; end
9
- def to_i; 0; end
10
- def to_f; 0.0; end
11
- def to_c; 0.to_c; end
12
- def to_r; 0.to_r; end
13
- def to_a; []; end
14
- def to_h; {}; end
9
+ extend Forwardable
10
+ def_delegators :nil, :to_a, :to_c, :to_f, :to_h, :to_i, :to_r, :to_s
15
11
  end
16
12
  end
17
13
  end
@@ -5,8 +5,13 @@ module Naught::NullClassBuilder::Commands
5
5
  def call
6
6
  defer do |subject|
7
7
  subject.module_eval do
8
- def to_ary; []; end
9
- def to_str; ''; end
8
+ def to_ary
9
+ []
10
+ end
11
+
12
+ def to_str
13
+ ''
14
+ end
10
15
  end
11
16
  end
12
17
  end
@@ -1,9 +1,8 @@
1
1
  module Naught::NullClassBuilder::Commands
2
2
  class Impersonate < Naught::NullClassBuilder::Commands::Mimic
3
- def initialize(builder, class_to_impersonate, options={})
3
+ def initialize(builder, class_to_impersonate, options = {})
4
4
  super
5
-
6
- builder.base_class = class_to_impersonate
5
+ builder.base_class = class_to_impersonate
7
6
  end
8
7
  end
9
8
  end
@@ -1,17 +1,18 @@
1
+ require 'naught/basic_object'
1
2
  require 'naught/null_class_builder/command'
2
3
 
3
4
  module Naught::NullClassBuilder::Commands
4
5
  class Mimic < Naught::NullClassBuilder::Command
5
6
  attr_reader :class_to_mimic, :include_super
6
7
 
7
- def initialize(builder, class_to_mimic, options={})
8
+ def initialize(builder, class_to_mimic, options = {})
8
9
  super(builder)
9
10
 
10
11
  @class_to_mimic = class_to_mimic
11
12
  @include_super = options.fetch(:include_super) { true }
12
13
 
13
14
  builder.base_class = root_class_of(class_to_mimic)
14
- builder.inspect_proc = -> { "<null:#{class_to_mimic}>" }
15
+ builder.inspect_proc = lambda { "<null:#{class_to_mimic}>" }
15
16
  builder.interface_defined = true
16
17
  end
17
18
 
@@ -26,11 +27,7 @@ module Naught::NullClassBuilder::Commands
26
27
  private
27
28
 
28
29
  def root_class_of(klass)
29
- if klass.ancestors.include?(Object)
30
- Object
31
- else
32
- BasicObject
33
- end
30
+ klass.ancestors.include?(Object) ? Object : Naught::BasicObject
34
31
  end
35
32
 
36
33
  def methods_to_stub
@@ -4,8 +4,7 @@ module Naught
4
4
  class NullClassBuilder
5
5
  module Commands
6
6
  class Pebble < ::Naught::NullClassBuilder::Command
7
-
8
- def initialize(builder, output=$stdout)
7
+ def initialize(builder, output = $stdout)
9
8
  @builder = builder
10
9
  @output = output
11
10
  end
@@ -15,18 +14,17 @@ module Naught
15
14
  subject.module_exec(@output) do |output|
16
15
 
17
16
  define_method(:method_missing) do |method_name, *args, &block|
18
- pretty_args = args.map(&:inspect).join(", ").gsub("\"", "'")
17
+ pretty_args = args.collect(&:inspect).join(', ').gsub("\"", "'")
19
18
  output.puts "#{method_name}(#{pretty_args}) from #{parse_caller}"
20
19
  self
21
20
  end
22
21
 
23
- private
24
-
25
22
  def parse_caller
26
23
  caller = Kernel.caller(2).first
27
24
  method_name = caller.match(/\`([\w\s]+(\(\d+\s\w+\))?[\w\s]*)/)
28
25
  method_name ? method_name[1] : caller
29
26
  end
27
+ private :parse_caller
30
28
  end
31
29
  end
32
30
  end
@@ -20,8 +20,7 @@ module Naught::NullClassBuilder::Commands
20
20
  subject.module_exec(@predicate_return_value) do |return_value|
21
21
  if subject.method_defined?(:method_missing)
22
22
  original_method_missing = instance_method(:method_missing)
23
- define_method(:method_missing) do
24
- |method_name, *args, &block|
23
+ define_method(:method_missing) do |method_name, *args, &block|
25
24
  if method_name.to_s.end_with?('?')
26
25
  return_value
27
26
  else
@@ -3,7 +3,7 @@ require 'naught/null_class_builder/command'
3
3
  module Naught::NullClassBuilder::Commands
4
4
  class Singleton < Naught::NullClassBuilder::Command
5
5
  def call
6
- defer(class: true) do |subject|
6
+ defer(:class => true) do |subject|
7
7
  require 'singleton'
8
8
  subject.module_eval do
9
9
  include ::Singleton
@@ -7,8 +7,9 @@ module Naught::NullClassBuilder::Commands
7
7
  subject.module_eval do
8
8
  attr_reader :__file__, :__line__
9
9
 
10
- def initialize(options={})
11
- backtrace = options.fetch(:caller) { Kernel.caller(4) }
10
+ def initialize(options = {})
11
+ range = (RUBY_VERSION.to_f == 1.9 && RUBY_PLATFORM != 'java') ? 4 : 3
12
+ backtrace = options.fetch(:caller) { Kernel.caller(range) }
12
13
  @__file__, line, _ = backtrace[0].split(':')
13
14
  @__line__ = line.to_i
14
15
  end
@@ -1,3 +1,3 @@
1
1
  module Naught
2
- VERSION = "0.0.3"
2
+ VERSION = '1.0.0'
3
3
  end
@@ -4,24 +4,19 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'naught/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "naught"
7
+ spec.name = 'naught'
8
8
  spec.version = Naught::VERSION
9
- spec.authors = ["Avdi Grimm"]
10
- spec.email = ["avdi@avdi.org"]
9
+ spec.authors = ['Avdi Grimm']
10
+ spec.email = ['avdi@avdi.org']
11
11
  spec.description = %q{Naught is a toolkit for building Null Objects}
12
- spec.summary = %q{Naught is a toolkit for building Null Objects}
13
- spec.homepage = "https://github.com/avdi/naught"
14
- spec.license = "MIT"
12
+ spec.summary = spec.description
13
+ spec.homepage = 'https://github.com/avdi/naught'
14
+ spec.license = 'MIT'
15
15
 
16
- spec.files = `git ls-files`.split($/)
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec", "~> 2.14"
24
- spec.add_development_dependency "guard"
25
- spec.add_development_dependency "guard-rspec"
26
- spec.add_development_dependency "guard-bundler"
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
27
22
  end
@@ -11,7 +11,7 @@ describe 'null object with a custom base class' do
11
11
  end
12
12
 
13
13
  it 'respond to base class methods' do
14
- expect(null.methods).to be_a_kind_of(Array)
14
+ expect(null.methods).to be_an Array
15
15
  end
16
16
 
17
17
  it 'respond to unknown methods' do
@@ -23,7 +23,7 @@ describe 'null object with a custom base class' do
23
23
  Naught.build do |b|
24
24
  default_base_class = b.base_class
25
25
  end
26
- expect(default_base_class).to eq(BasicObject)
26
+ expect(default_base_class).to eq(Naught::BasicObject)
27
27
  end
28
28
 
29
29
  describe 'singleton null object' do
@@ -11,7 +11,7 @@ describe 'basic null object' do
11
11
  end
12
12
 
13
13
  it 'accepts any arguments for any messages' do
14
- null.foobaz(1,2,3)
14
+ null.foobaz(1, 2, 3)
15
15
  end
16
16
 
17
17
  it 'reports that it responds to any message' do
@@ -21,7 +21,7 @@ describe 'basic null object' do
21
21
  end
22
22
 
23
23
  it 'can be inspected' do
24
- expect(null.inspect).to eq("<null>")
24
+ expect(null.inspect).to eq('<null>')
25
25
  end
26
26
 
27
27
  it 'knows its own class' do
@@ -32,4 +32,4 @@ describe 'basic null object' do
32
32
  expect(null_class.get.class).to be(null_class)
33
33
  end
34
34
 
35
- end
35
+ end
@@ -2,15 +2,15 @@ require 'spec_helper'
2
2
 
3
3
  describe 'black hole null object' do
4
4
  subject(:null) { null_class.new }
5
- let(:null_class) {
5
+ let(:null_class) do
6
6
  Naught.build do |b|
7
7
  b.black_hole
8
8
  end
9
- }
9
+ end
10
10
 
11
11
  it 'returns self from arbitray method calls' do
12
12
  expect(null.info).to be(null)
13
13
  expect(null.foobaz).to be(null)
14
- expect(null << "bar").to be(null)
14
+ expect(null << 'bar').to be(null)
15
15
  end
16
- end
16
+ end
@@ -0,0 +1,23 @@
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
@@ -10,13 +10,13 @@ describe 'Actual()' do
10
10
 
11
11
  specify 'given anything else, returns the input unchanged' do
12
12
  expect(Actual(false)).to be(false)
13
- str = "hello"
13
+ str = 'hello'
14
14
  expect(Actual(str)).to be(str)
15
15
  expect(Actual(nil)).to be_nil
16
16
  end
17
17
 
18
18
  it 'also works with blocks' do
19
- expect(Actual{ConvertableNull.new}).to be_nil
20
- expect(Actual{"foo"}).to eq("foo")
19
+ expect(Actual { ConvertableNull.new }).to be_nil
20
+ expect(Actual { 'foo' }).to eq('foo')
21
21
  end
22
- end
22
+ end
@@ -5,18 +5,18 @@ describe 'Just()' do
5
5
 
6
6
  specify 'passes non-nullish values through' do
7
7
  expect(Just(false)).to be(false)
8
- str = "hello"
8
+ str = 'hello'
9
9
  expect(Just(str)).to be(str)
10
10
  end
11
11
 
12
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)
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
16
  end
17
17
 
18
18
  it 'also works with blocks' do
19
- expect{Just{nil}.class}.to raise_error(ArgumentError)
20
- expect(Just{"foo"}).to eq("foo")
19
+ expect { Just { nil }.class }.to raise_error(ArgumentError)
20
+ expect(Just { 'foo' }).to eq('foo')
21
21
  end
22
- end
22
+ end
@@ -13,23 +13,23 @@ describe 'Maybe()' do
13
13
  end
14
14
 
15
15
  specify 'given anything in null_equivalents, return a null object' do
16
- expect(Maybe("").class).to be(ConvertableNull)
16
+ expect(Maybe('').class).to be(ConvertableNull)
17
17
  end
18
18
 
19
19
  specify 'given anything else, returns the input unchanged' do
20
20
  expect(Maybe(false)).to be(false)
21
- str = "hello"
21
+ str = 'hello'
22
22
  expect(Maybe(str)).to be(str)
23
23
  end
24
24
 
25
25
  it 'generates null objects with useful trace info' do
26
- null = Maybe(); line = __LINE__
27
- expect(null.__line__).to eq(line)
26
+ null, line = Maybe(), __LINE__
28
27
  expect(null.__file__).to eq(__FILE__)
28
+ expect(null.__line__).to eq(line)
29
29
  end
30
30
 
31
31
  it 'also works with blocks' do
32
- expect(Maybe{nil}.class).to eq(ConvertableNull)
33
- expect(Maybe{"foo"}).to eq("foo")
32
+ expect(Maybe { nil }.class).to eq(ConvertableNull)
33
+ expect(Maybe { 'foo' }).to eq('foo')
34
34
  end
35
- end
35
+ end
@@ -17,18 +17,18 @@ describe 'Null()' do
17
17
  end
18
18
 
19
19
  specify 'given anything in null_equivalents, return a null object' do
20
- expect(Null("").class).to be(ConvertableNull)
20
+ expect(Null('').class).to be(ConvertableNull)
21
21
  end
22
22
 
23
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)
24
+ expect { Null(false) }.to raise_error(ArgumentError)
25
+ expect { Null('hello') }.to raise_error(ArgumentError)
26
26
  end
27
27
 
28
28
  it 'generates null objects with useful trace info' do
29
- null = Null(); line = __LINE__
30
- expect(null.__line__).to eq(line)
29
+ null, line = Null(), __LINE__
31
30
  expect(null.__file__).to eq(__FILE__)
31
+ expect(null.__line__).to eq(line)
32
32
  end
33
33
 
34
- end
34
+ end
@@ -2,24 +2,24 @@ require 'spec_helper'
2
2
 
3
3
  describe 'implicitly convertable null object' do
4
4
  subject(:null) { null_class.new }
5
- let(:null_class) {
5
+ let(:null_class) do
6
6
  Naught.build do |b|
7
7
  b.define_implicit_conversions
8
8
  end
9
- }
9
+ end
10
10
  it 'implicitly splats the same way an empty array does' do
11
11
  a, b = null
12
12
  expect(a).to be_nil
13
13
  expect(b).to be_nil
14
14
  end
15
15
  it 'is implicitly convertable to String' do
16
- expect(eval(null)).to be_nil
16
+ expect(instance_eval(null)).to be_nil
17
17
  end
18
18
  it 'implicitly converts to an empty array' do
19
19
  expect(null.to_ary).to eq([])
20
20
  end
21
21
  it 'implicitly converts to an empty string' do
22
- expect(null.to_str).to eq("")
22
+ expect(null.to_str).to eq('')
23
23
  end
24
24
 
25
- end
25
+ end
@@ -3,27 +3,39 @@ require 'logger'
3
3
 
4
4
  describe 'null object mimicking a class' do
5
5
  class User
6
- def login; "bob"; end
6
+ def login
7
+ 'bob'
8
+ end
7
9
  end
8
10
 
9
11
  module Authorizable
10
- def authorized_for?(object); true; end
12
+ def authorized_for?(object)
13
+ true
14
+ end
11
15
  end
12
16
 
13
17
  class LibraryPatron < User
14
18
  include Authorizable
15
19
 
16
- def member?; true; end
17
- def name; "Bob"; end
18
- def notify_of_overdue_books(titles); puts 'Notifying...'; end
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
19
31
  end
20
32
 
21
33
  subject(:null) { mimic_class.new }
22
- let(:mimic_class) {
34
+ let(:mimic_class) do
23
35
  Naught.build do |b|
24
36
  b.mimic LibraryPatron
25
37
  end
26
- }
38
+ end
27
39
  it 'responds to all methods defined on the target class' do
28
40
  expect(null.member?).to be_nil
29
41
  expect(null.name).to be_nil
@@ -31,7 +43,7 @@ describe 'null object mimicking a class' do
31
43
  end
32
44
 
33
45
  it 'does not respond to methods not defined on the target class' do
34
- expect{null.foobar}.to raise_error(NoMethodError)
46
+ expect { null.foobar }.to raise_error(NoMethodError)
35
47
  end
36
48
 
37
49
  it 'reports which messages it does and does not respond to' do
@@ -41,7 +53,7 @@ describe 'null object mimicking a class' do
41
53
  expect(null).not_to respond_to(:foobar)
42
54
  end
43
55
  it 'has an informative inspect string' do
44
- expect(null.inspect).to eq("<null:LibraryPatron>")
56
+ expect(null.inspect).to eq('<null:LibraryPatron>')
45
57
  end
46
58
 
47
59
  it 'excludes Object methods from being mimicked' do
@@ -55,11 +67,11 @@ describe 'null object mimicking a class' do
55
67
  end
56
68
 
57
69
  describe 'with include_super: false' do
58
- let(:mimic_class) {
70
+ let(:mimic_class) do
59
71
  Naught.build do |b|
60
- b.mimic LibraryPatron, include_super: false
72
+ b.mimic LibraryPatron, :include_super => false
61
73
  end
62
- }
74
+ end
63
75
 
64
76
  it 'excludes inherited methods' do
65
77
  expect(null).to_not respond_to(:authorized_for?)
@@ -70,53 +82,36 @@ end
70
82
 
71
83
  describe 'using mimic with black_hole' do
72
84
  subject(:null) { mimic_class.new }
73
- let(:mimic_class) {
85
+ let(:mimic_class) do
74
86
  Naught.build do |b|
75
87
  b.mimic Logger
76
88
  b.black_hole
77
89
  end
78
- }
90
+ end
79
91
 
80
92
  def self.it_behaves_like_a_black_hole_mimic
81
93
  it 'returns self from mimicked methods' do
82
94
  expect(null.info).to equal(null)
83
95
  expect(null.error).to equal(null)
84
- expect(null << "test").to equal(null)
96
+ expect(null << 'test').to equal(null)
85
97
  end
86
98
 
87
99
  it 'does not respond to methods not defined on the target class' do
88
- expect{null.foobar}.to raise_error(NoMethodError)
100
+ expect { null.foobar }.to raise_error(NoMethodError)
89
101
  end
90
102
  end
91
103
 
92
104
  it_behaves_like_a_black_hole_mimic
93
105
 
94
106
  describe '(reverse order)' do
95
- let(:mimic_class) {
107
+ let(:mimic_class) do
96
108
  Naught.build do |b|
97
109
  b.black_hole
98
110
  b.mimic Logger
99
111
  end
100
- }
112
+ end
101
113
 
102
114
  it_behaves_like_a_black_hole_mimic
103
115
  end
104
116
 
105
117
  end
106
-
107
- describe 'mimicking a non-Object-derived class' do
108
- class MinimalClass < BasicObject
109
- end
110
-
111
- subject(:null) { mimic_class.new }
112
- let(:mimic_class) {
113
- Naught.build do |b|
114
- b.mimic MinimalClass
115
- end
116
- }
117
-
118
- it 'generates a BasicObject-derived null class' do
119
- expect(BasicObject).to be === null
120
- expect(Object).not_to be === null
121
- end
122
- end