shuber-interface 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -41,8 +41,10 @@ Then use the <tt>implements</tt> method in your classes (also aliased as <tt>imp
41
41
  method == :off ? @power = false : super
42
42
  end
43
43
 
44
+ # Use this whenever you have custom method_missing logic
45
+ # See http://www.ruby-doc.org/core/classes/Object.html#M001006
44
46
  def respond_to_missing?(method, include_private)
45
- method == :off
47
+ method == :off || super
46
48
  end
47
49
  end
48
50
 
@@ -80,7 +82,55 @@ You can also explicitly list <tt>interfaces</tt> to test
80
82
  end
81
83
  end
82
84
 
83
- == Note on Patches/Pull Requests
85
+
86
+ == Why would you ever want to use this?
87
+
88
+ It's useful for when you're working with libraries that have extensible APIs, like {writing custom ActiveModel compliant models}[http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/]
89
+
90
+ Imagine if we defined <tt>ActiveModel</tt> with something like
91
+
92
+ module ActiveModel
93
+ # checks if this object has been saved
94
+ def new_record?
95
+ end
96
+
97
+ # checks if this object is valid
98
+ def valid?
99
+ end
100
+
101
+ # and the rest of the methods...
102
+ end
103
+
104
+ We'd have a nice clear view of exactly which methods our custom implementations need to define AND one centralized place for documentation
105
+
106
+ Now we can define our custom implementation
107
+
108
+ class CompliantModel
109
+ implements ActiveModel
110
+
111
+ def valid?
112
+ true
113
+ end
114
+ end
115
+
116
+ Then we can easily test if we've completely implemented <tt>ActiveModel</tt> (or are missing any new methods because <tt>ActiveModel</tt> was updated)
117
+
118
+ require 'test/unit'
119
+
120
+ Test::Unit::TestCase.send(:include, Interface::TestHelper)
121
+
122
+ class CompliantModelTest < Test::Unit::TestCase
123
+ def test_should_implement_active_model
124
+ assert_implements_interface CompliantModel.new, ActiveModel # Failure: unimplemented interface methods for CompliantModel: {ActiveModel=>["new_record?"]}
125
+ end
126
+ end
127
+
128
+ <tt>ActiveModel</tt> actually already has a great solution for this problem (providing a module called <tt>ActiveModel::Lint::Tests</tt> that you can include into your test cases to test compliance with the API) but it's still a good example for demonstrating this gem's usefulness
129
+
130
+ You can see this gem used in {shuber/nestable}[https://github.com/shuber/nestable] which is actually why I created it
131
+
132
+
133
+ == Patches and pull requests
84
134
 
85
135
  * Fork the project.
86
136
  * Make your feature addition or bug fix.
@@ -4,9 +4,9 @@ module Interface
4
4
  def self.extended(base) # :nodoc:
5
5
  base.class_eval do
6
6
  instance_methods(false).each do |method|
7
- define_method(method) do |*args|
7
+ define_method(method) do |*args, &block|
8
8
  begin
9
- method_missing(method.to_sym, *args)
9
+ method_missing(method.to_sym, *args, &block)
10
10
  rescue NoMethodError
11
11
  raise NotImplementedError.new("#{self.class} needs to implement '#{method}' for interface #{base}")
12
12
  end
@@ -1,7 +1,7 @@
1
1
  module Interface
2
2
  # Contains interface testing methods to include in your test framework
3
3
  module TestHelper
4
- # Raises AssertionFailedError if <tt>object</tt> does not implement all methods from <tt>interfaces</tt>
4
+ # Raises <tt>AssertionFailedError</tt> if <tt>object</tt> does not implement all methods from <tt>interfaces</tt>
5
5
  #
6
6
  # <tt>interfaces</tt> defaults to <tt>object.interfaces</tt> if none are specified
7
7
  #
@@ -3,7 +3,7 @@ module Interface
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 0
6
- PATCH = 2
6
+ PATCH = 3
7
7
 
8
8
  # Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
9
9
  #
data/lib/interface.rb CHANGED
@@ -47,9 +47,21 @@ module Interface
47
47
  def unimplemented_methods_for(interface)
48
48
  interface.instance_methods(false).reject do |method|
49
49
  method = method.to_sym
50
- (respond_to?(method, true) && self.method(method).owner != interface) || (respond_to?(:respond_to_missing?, true) && respond_to_missing?(method, true))
50
+ (respond_to?(method, true) && self.method(method).owner != interface) || respond_to_missing?(method, true)
51
51
  end.sort
52
52
  end
53
+
54
+ # <tt>Object#respond_to_missing?</tt> wasn't implemented until ruby version 1.9
55
+ unless respond_to?(:respond_to_missing?)
56
+ def respond_to?(method, include_private = false) # :nodoc:
57
+ super || respond_to_missing?(method, include_private)
58
+ end
59
+
60
+ def respond_to_missing?(method, include_private) # :nodoc:
61
+ false
62
+ end
63
+ end
64
+
53
65
  end
54
66
 
55
67
  Object.send(:include, Interface)
@@ -27,7 +27,7 @@ class Device < BrokenDevice
27
27
  end
28
28
 
29
29
  def respond_to_missing?(method, include_private)
30
- method == :off
30
+ method == :off || super
31
31
  end
32
32
  end
33
33
 
@@ -83,4 +83,8 @@ class InterfaceTest < Test::Unit::TestCase
83
83
  assert_raises(Test::Unit::AssertionFailedError) { assert_implements_interface BrokenDevice.new, Remote }
84
84
  end
85
85
 
86
+ def test_should_respond_to_respond_to_missing
87
+ assert respond_to?(:respond_to_missing?)
88
+ end
89
+
86
90
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shuber-interface
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sean Huber
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-09 00:00:00 -08:00
18
+ date: 2011-02-10 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies: []
21
21