shuber-interface 0.0.2 → 0.0.3
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.
- data/README.rdoc +52 -2
- data/lib/interface/abstract.rb +2 -2
- data/lib/interface/test_helper.rb +1 -1
- data/lib/interface/version.rb +1 -1
- data/lib/interface.rb +13 -1
- data/test/interface_test.rb +5 -1
- metadata +4 -4
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
|
-
|
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.
|
data/lib/interface/abstract.rb
CHANGED
@@ -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
|
#
|
data/lib/interface/version.rb
CHANGED
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) ||
|
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)
|
data/test/interface_test.rb
CHANGED
@@ -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:
|
4
|
+
hash: 25
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
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-
|
18
|
+
date: 2011-02-10 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|