just_maybe 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,17 +1,22 @@
1
1
  # JustMaybe
2
2
 
3
- A simple implementation of the "Maybe" special case pattern. It allows
4
- you to safely chain any number of method calls on an object before fetching
5
- the underlying result or a default value.
3
+ A simple implementation of the "Maybe" - "Special Case" pattern.
4
+
5
+ Maybe provides a generic null object that allows you to defer
6
+ the interpretation of what it means to be null until you need
7
+ to use the object.
8
+
9
+ Specifically they allow you to safely chain any number of method
10
+ calls on an object before returning it or a default value.
6
11
 
7
12
  ```ruby
8
- Maybe(nil).foo.bar.or_else { 'default result' }
13
+ Maybe(nil).foo.bar.or_else { 'default' }
9
14
  ```
10
15
  ## Installation
11
16
 
12
17
  Add this line to your application's Gemfile:
13
18
 
14
- gem 'just_maybe', :git => "git://github.com/mgreenly/justmaybe.git"
19
+ gem "just_maybe", "~> 0.1.0"
15
20
 
16
21
  And then execute:
17
22
 
@@ -20,30 +25,48 @@ And then execute:
20
25
  ## Background
21
26
 
22
27
  I created JustMaybe because I wanted a really simple solution that didn't
23
- introduce any changes to Object.
28
+ introduce any changes on Object and I found this paticular syntax very
29
+ readable.
30
+
31
+ My paticular use case was for presenter objects that needed to aggregate
32
+ lots of information by walking method chains of a supplied objects. In
33
+ this sitation the Presenter is aware of the appropriate default value to
34
+ use if the reference fails.
24
35
 
25
- My paticular use case was in Presenter objects that needed to collect
26
- information from the heirarchy of thier children for display. In this
27
- use case the Presenter always knows what it wants, where it's at, and
28
- what it should use as an alternative if it's not available.
36
+ Using Maybe allows me to get the benefit of null objects without having
37
+ to modify the underlying code.
29
38
 
30
39
  ## Examples
31
40
 
32
41
  ```ruby
33
- # Objects wrapped in maybe accept any message and return the result wrapped in maybe
34
- Maybe(42) * -1 # => Maybe(-42)
42
+ require 'just_maybe'
43
+ # => true
35
44
 
36
- # If the Maybe's underlying value doesn't respond to the message it returns Maybe(nil)
37
- Maybe(42).foo # => Maybe(nil)
45
+ # Calling Maybe(object) returns that object wrapped in a instance of Maybe
46
+ Maybe(42)
47
+ # => #<Maybe:0x000000010e7c88> @value=42
38
48
 
39
- # This allows you to use arbitrarily long method chains with no risk of raising
49
+ # An instance of maybe will pass all valid messages to the underlying value
50
+ # and wrap the result in a new instance Maybe
51
+ Maybe(42).next
52
+ # => <Maybe:0x0000000110b0e8> @value=43
53
+
54
+ # If the Maybe's underlying value doesn't respond to the message it returns
55
+ # nil wrapped in a new instance of Maybe
56
+ Maybe(42).foo
57
+ # => #<Maybe:0x00000001113540> @value=nil
58
+
59
+ # This allows you to make arbitrarily long method chains with no risk of raising
40
60
  # NoMethodError on a nil object
41
61
  Maybe(nil).foo.bar.baz # => Maybe(nil)
62
+ # => #<Maybe:0x00000001120830> @value=nil
42
63
 
43
64
  # Finally Maybe responds to #or_else { ... }. This causes the Maybe to return the
44
65
  # underlying value if it exists or the result of the block if it does not.
45
- Maybe([]).append('foo').append('bar').join(' ').or_else { 'baz' } # => "foo bar"
46
- Maybe(nil).append('foo').append('bar').join(' ').or_else { 'baz' } # => "baz"
66
+ Maybe('FOO').downcase.or_else { 'unknown' }
67
+ # => "foo"
68
+ Maybe(nil).downcase.or_else { 'unknown' }
69
+ # => "unknown"
47
70
 
48
71
  # There are a number of convenience methods that operate like or_else for common null values
49
72
  Maybe(nil).foo.bar.or_a # => []
@@ -58,11 +81,11 @@ Maybe(nil).foo.bar.or_s # => ''
58
81
  Maybe(42).__value__ # => 42
59
82
  Maybe(42).foo.bar.baz.__value__ # => nil
60
83
  ```
61
-
62
84
  ## Related
63
85
  * [Virtuous Code](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
64
86
  * [mondadic](https://github.com/pzol/monadic)
65
87
  * [rumonade](https://github.com/ms-ati/rumonade)
88
+ * [thoughtbot](http://robots.thoughtbot.com/post/8181879506/if-you-gaze-into-nil-nil-gazes-also-into-you)
66
89
 
67
90
  ## Contributing
68
91
 
@@ -1,72 +1,68 @@
1
- def Maybe(object)
2
- Maybe.new(object)
3
- end
4
-
5
- class Maybe
1
+ class Maybe < BasicObject
6
2
  def initialize(object)
7
- if object.kind_of?(Maybe)
8
- @value = object.__value__
3
+ if object.kind_of?(::Maybe)
4
+ @object = object.__value__
9
5
  else
10
- @value = object
6
+ @object = object
11
7
  end
12
8
  end
13
9
 
10
+ def instance_of?(klass)
11
+ ::Maybe == klass
12
+ end
13
+
14
14
  def __value__
15
- @value
15
+ @object
16
16
  end
17
-
17
+
18
18
  def inspect
19
- '#<%s:0x%0.14x> @value=%s' % [self.class, object_id * 2, @value.inspect]
19
+ ::Kernel.sprintf('#<Maybe:0x%0.14x> @object=%s', __id__ * 2, @object.inspect)
20
20
  end
21
21
 
22
- def method_missing(method, *args)
23
- if @value.respond_to?(method)
24
- Maybe(@value.__send__(method, *args))
25
- else
26
- Maybe(nil)
22
+ def method_missing(method, *args, &block)
23
+ begin
24
+ ::Maybe.new(@object.__send__(method, *args, &block))
25
+ rescue ::NoMethodError
26
+ ::Maybe.new(nil)
27
27
  end
28
28
  end
29
29
 
30
30
  def or_a
31
- return @value unless @value.nil?
31
+ return @object unless @object.nil?
32
32
  []
33
33
  end
34
34
 
35
35
  def or_else(&block)
36
- return @value unless @value.nil?
36
+ return @object unless @object.nil?
37
37
  yield
38
38
  end
39
39
 
40
40
  def or_f
41
- return @value unless @value.nil?
41
+ return @object unless @object.nil?
42
42
  0.0
43
43
  end
44
44
 
45
45
  def or_h
46
- return @value unless @value.nil?
46
+ return @object unless @object.nil?
47
47
  {}
48
48
  end
49
49
 
50
50
  def or_i
51
- return @value unless @value.nil?
51
+ return @object unless @object.nil?
52
52
  0
53
53
  end
54
54
 
55
55
  def or_nil
56
- return @value unless @value.nil?
56
+ return @object unless @object.nil?
57
57
  nil
58
58
  end
59
59
 
60
60
  def or_s
61
- return @value unless @value.nil?
61
+ return @object unless @object.nil?
62
62
  ''
63
63
  end
64
+ end
64
65
 
65
- def ==(other)
66
- if other.kind_of?(Maybe)
67
- @value == other.__value__
68
- else
69
- @value == other
70
- end
71
- end
66
+ def Maybe(object)
67
+ Maybe.new(object)
72
68
  end
@@ -1,3 +1,3 @@
1
1
  module JustMaybe
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/spec/maybe_spec.rb CHANGED
@@ -3,24 +3,76 @@ require 'just_maybe/maybe'
3
3
  describe Maybe do
4
4
  let(:object) { double }
5
5
 
6
- it "value returns wrapped object" do
7
- Maybe(object).__value__.should == object
6
+ it "delegates messages to wrapped object" do
7
+ object.should_receive(:foo).with('bar')
8
+ Maybe(object).foo('bar')
8
9
  end
9
10
 
10
- it "return values for methods handled by underlying value are wrapped in new Maybe" do
11
- object.stub(:some_method).and_return('this object')
12
- Maybe(object).some_method.__value__.should == 'this object'
11
+ it "calling methods that don't exist return Maybe(nil)" do
12
+ Maybe(nil).foo.__value__.should be_nil
13
13
  end
14
14
 
15
- it "calling a method that does not exist returns Maybe(nil)" do
16
- Maybe(object).method_that_does_not_exist.should == Maybe(nil)
15
+ it "it doesn't nest" do
16
+ Maybe(Maybe(nil)).__value__.should_not be_instance_of(Maybe)
17
17
  end
18
18
 
19
- it "#or_else returns object when object is not nil" do
19
+ it "__value__ unwraps the underlying object" do
20
+ Maybe(object).__value__.should be(object)
21
+ end
22
+
23
+ it "#or_else returns then underlying object when it's not nil" do
20
24
  Maybe(object).or_else{'unexpected result'}.should == object
21
25
  end
22
26
 
23
- it "#or_else returns default when object is nil" do
27
+ it "#or_else yields to supplied block when the underlying object is nil" do
24
28
  Maybe(nil).or_else{'expected result'}.should == 'expected result'
25
29
  end
30
+
31
+ it "#or_a returns then underlying object when it's not nil" do
32
+ Maybe(object).or_a.should be(object)
33
+ end
34
+
35
+ it "#or_a defaults to []" do
36
+ Maybe(nil).or_a.should == []
37
+ end
38
+
39
+ it "#or_f returns then underlying object when it's not nil" do
40
+ Maybe(object).or_f.should be(object)
41
+ end
42
+
43
+ it "#or_f defaults to 0.0" do
44
+ Maybe(nil).or_f.should == 0.0
45
+ end
46
+
47
+ it "#or_h returns then underlying object when it's not nil" do
48
+ Maybe(object).or_h.should be(object)
49
+ end
50
+
51
+ it "#or_h defaults to {}" do
52
+ Maybe(nil).or_h.should == {}
53
+ end
54
+
55
+ it "#or_i returns then underlying object when it's not nil" do
56
+ Maybe(object).or_i.should be(object)
57
+ end
58
+
59
+ it "#or_i defaults to 0" do
60
+ Maybe(nil).or_i.should == 0
61
+ end
62
+
63
+ it "#or_nil returns then underlying object when it's not nil" do
64
+ Maybe(object).or_nil.should be(object)
65
+ end
66
+
67
+ it "#or_nil defaults to nil" do
68
+ Maybe(nil).or_nil.should == nil
69
+ end
70
+
71
+ it "#or_s returns then underlying object when it's not nil" do
72
+ Maybe(object).or_s.should be(object)
73
+ end
74
+
75
+ it "#or_s defaults to ''" do
76
+ Maybe(nil).or_s.should == ''
77
+ end
26
78
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: just_maybe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: