subclass_must_implement 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.simplecov +1 -0
- data/README.md +35 -2
- data/lib/subclass_must_implement.rb +20 -6
- data/lib/subclass_must_implement/rspec_matchers/require_subclass_to_implement_matcher.rb +42 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/subclass_must_implement_spec.rb +12 -2
- data/subclass_must_implement.gemspec +1 -0
- data/tags +40 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9eaa616a9b8edf88f77acd435aae5663f5f7263
|
4
|
+
data.tar.gz: 459bd4737551b160317f6cec391be93c23a73666
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d211c4b1ded0fce194fcf8c745cc79e4029d4d50cb37b4cb9a1258b00e7d4900cbe9337805eb4dddbf62c27982a918d59592dbfc6ffb032021ec7c8008dfadbc
|
7
|
+
data.tar.gz: c16e69619f667b52bccd90c8a160cd71bb6e40d4332546a63153d805b147a0bed880d41a8e2a8ba908f7f193eeffad8e16b768153218421b90d9c39c61748efb
|
data/.gitignore
CHANGED
data/.simplecov
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# SubclassMustImplement
|
2
2
|
|
3
|
-
There are circumstances when it is desirable to specify certain methods as abstract,
|
4
|
-
|
3
|
+
There are circumstances when it is desirable to specify certain methods as abstract,
|
4
|
+
i.e. it is the responsibility of any subclass to implement them.
|
5
|
+
Getting a `MethodMIssing` is not helpful; an error that explicitly explains that the
|
6
|
+
missing method is required by contract to fully implement the interface will save much
|
7
|
+
time and frustration.
|
5
8
|
|
6
9
|
## Installation
|
7
10
|
|
@@ -65,6 +68,36 @@ b.bar # return :bar
|
|
65
68
|
b.foo # raises a NotImplementedError with the specified error message "Version expected!!!"
|
66
69
|
```
|
67
70
|
|
71
|
+
## Using With RSpec
|
72
|
+
|
73
|
+
There is a custom [RSpec](http://rspec.info/) matcher included to simplify testing.
|
74
|
+
|
75
|
+
Example:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# NOTE: depending on gem load order, you may need to manually load the matcher.
|
79
|
+
# If so, add the following line to your `spec_helper.rb`
|
80
|
+
require "subclass_must_implement/rspec_matcher/require_subclass_to_implement_matcher"
|
81
|
+
|
82
|
+
# Given the following class:
|
83
|
+
class BaseBar
|
84
|
+
extend SubclassMustImplement
|
85
|
+
|
86
|
+
subclass_must_implement :foo, :bar, err_message: "Version expected!!!"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Test to ensure required functionality is specified:
|
90
|
+
|
91
|
+
# Custom error messages can be specified
|
92
|
+
it { expect(BaseBar).to require_subclass_to_implement(:version).with_error_message("Version expected!!!")}
|
93
|
+
|
94
|
+
# Either the class or the instance can be passed to the matcher
|
95
|
+
it { expect(BaseBar.new).to require_subclass_to_implement(:sub_version) }
|
96
|
+
|
97
|
+
# Sometimes it makes sense to specify certain methods as not required
|
98
|
+
it { expect(BaseBar).to_not require_subclass_to_implement(:foo) }
|
99
|
+
```
|
100
|
+
|
68
101
|
## Contributing
|
69
102
|
|
70
103
|
1. Fork it ( https://github.com/[my-github-username]/subclass_must_implement/fork )
|
@@ -42,29 +42,39 @@
|
|
42
42
|
#
|
43
43
|
module SubclassMustImplement
|
44
44
|
|
45
|
-
#
|
45
|
+
# Inject the `subclass_must_implement` macro.
|
46
46
|
def self.included(base)
|
47
47
|
base.extend ClassMethods
|
48
48
|
end
|
49
49
|
|
50
|
-
#
|
50
|
+
# Inject the `subclass_must_implement` macro.
|
51
51
|
def self.extended(base)
|
52
52
|
base.extend ClassMethods
|
53
53
|
end
|
54
54
|
|
55
|
-
#
|
55
|
+
# Return the Gem version as a string.
|
56
|
+
# @return [String]
|
56
57
|
def self.version
|
57
|
-
"0.0.
|
58
|
+
"0.0.2"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create the default error message for the given method name.
|
62
|
+
# @param method_name [String|Symbol]
|
63
|
+
# @return [String]
|
64
|
+
def self.default_error_message(method_name)
|
65
|
+
"`#{method_name}` must be implemented in a subclass."
|
58
66
|
end
|
59
67
|
|
60
68
|
# The class level macros are defined here.
|
61
69
|
module ClassMethods
|
62
70
|
|
63
|
-
#
|
71
|
+
# Define a method for each method name that raises a NotImplementedError when called.
|
64
72
|
# Pass in a custom error message if desired using the err_message named argument.
|
73
|
+
# @param method_names [Enumerable<Symbol>]
|
74
|
+
# @param err_message [String]
|
65
75
|
def subclass_must_implement(*method_names, err_message: nil)
|
66
76
|
method_names.each do |method_name|
|
67
|
-
err = err_message.nil? ?
|
77
|
+
err = err_message.nil? ? ::SubclassMustImplement.default_error_message(method_name) : "#{err_message}"
|
68
78
|
define_method method_name do |*_|
|
69
79
|
raise NotImplementedError, err
|
70
80
|
end
|
@@ -72,3 +82,7 @@ module SubclassMustImplement
|
|
72
82
|
end
|
73
83
|
end
|
74
84
|
end
|
85
|
+
|
86
|
+
if defined? RSpec
|
87
|
+
require "subclass_must_implement/rspec_matchers/require_subclass_to_implement_matcher"
|
88
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "rspec/expectations"
|
2
|
+
|
3
|
+
RSpec::Matchers.define :require_subclass_to_implement do |method_name|
|
4
|
+
|
5
|
+
chain :with_error_message do |err_message|
|
6
|
+
@err_message = err_message
|
7
|
+
end
|
8
|
+
|
9
|
+
match do |klass|
|
10
|
+
item = if klass.is_a?(Class)
|
11
|
+
klass.new
|
12
|
+
else
|
13
|
+
klass
|
14
|
+
end
|
15
|
+
|
16
|
+
if item.respond_to?(method_name)
|
17
|
+
begin
|
18
|
+
item.__send__ method_name
|
19
|
+
false
|
20
|
+
rescue NotImplementedError => err
|
21
|
+
@err_message ||= ::SubclassMustImplement.default_error_message(method_name)
|
22
|
+
err.message == @err_message
|
23
|
+
end
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
end # match do
|
29
|
+
|
30
|
+
failure_message do
|
31
|
+
"should require subclasses to implement method `#{method_name}`, " \
|
32
|
+
"otherwise raise a NotImplementedError with message '#{@err_message}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
failure_message_when_negated do
|
36
|
+
"should not require method `#{method_name}` to be implemented by subclasses"
|
37
|
+
end
|
38
|
+
|
39
|
+
description do
|
40
|
+
"method `#{method_name}` must be implemented by subclasses"
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -30,7 +30,7 @@ RSpec.describe SubclassMustImplement do
|
|
30
30
|
let(:bar) { Bar.new }
|
31
31
|
|
32
32
|
context "module included" do
|
33
|
-
it "will raise a
|
33
|
+
it "will raise a NotImplementedError if any required method is not implemented in the subclass" do
|
34
34
|
expect { foo.bar }.to raise_error(NotImplementedError, "`bar` must be implemented in a subclass.")
|
35
35
|
expect { foo.baz }.to raise_error(NotImplementedError, "`baz` must be implemented in a subclass.")
|
36
36
|
end
|
@@ -41,7 +41,7 @@ RSpec.describe SubclassMustImplement do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
context "module extended" do
|
44
|
-
it "will raise a
|
44
|
+
it "will raise a NotImplementedError if any required method is not implemented in the subclass" do
|
45
45
|
expect { bar.sub_version }.to raise_error(NotImplementedError, "Version expected!!!")
|
46
46
|
end
|
47
47
|
|
@@ -49,4 +49,14 @@ RSpec.describe SubclassMustImplement do
|
|
49
49
|
expect(bar.version).to eq(described_class.version)
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
context "matcher" do
|
54
|
+
it { expect(BaseFoo ).to require_subclass_to_implement(:foo) }
|
55
|
+
it { expect(BaseFoo.new).to require_subclass_to_implement(:bar) }
|
56
|
+
it { expect(BaseFoo).not_to require_subclass_to_implement(:qux) }
|
57
|
+
|
58
|
+
it do
|
59
|
+
expect(BaseBar).to require_subclass_to_implement(:version).with_error_message("Version expected!!!")
|
60
|
+
end
|
61
|
+
end
|
52
62
|
end
|
data/tags
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
2
|
+
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
3
|
+
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
|
4
|
+
!_TAG_PROGRAM_NAME Exuberant Ctags //
|
5
|
+
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
|
6
|
+
!_TAG_PROGRAM_VERSION 5.8 //
|
7
|
+
AllFiles coverage/index.html 32;" a line:32
|
8
|
+
Bar spec/subclass_must_implement_spec.rb 23;" c line:23
|
9
|
+
BaseBar spec/subclass_must_implement_spec.rb 17;" c line:17
|
10
|
+
BaseFoo spec/subclass_must_implement_spec.rb 5;" c line:5
|
11
|
+
ClassMethods lib/subclass_must_implement.rb 61;" m line:61 class:SubclassMustImplement
|
12
|
+
Foo spec/subclass_must_implement_spec.rb 11;" c line:11
|
13
|
+
K.D coverage/assets/0.10.2/application.js 17;" f line:17
|
14
|
+
K.E coverage/assets/0.10.2/application.js 17;" f line:17
|
15
|
+
K.l coverage/assets/0.10.2/application.js 17;" f line:17
|
16
|
+
K.m coverage/assets/0.10.2/application.js 17;" f line:17
|
17
|
+
SubclassMustImplement lib/subclass_must_implement.rb 43;" m line:43
|
18
|
+
V.N coverage/assets/0.10.2/application.js 17;" f line:17
|
19
|
+
b$.bA coverage/assets/0.10.2/application.js 17;" f line:17
|
20
|
+
b$.bW coverage/assets/0.10.2/application.js 17;" f line:17
|
21
|
+
b$.bX coverage/assets/0.10.2/application.js 17;" f line:17
|
22
|
+
b$.bY coverage/assets/0.10.2/application.js 17;" f line:17
|
23
|
+
b$.bZ coverage/assets/0.10.2/application.js 17;" f line:17
|
24
|
+
b$.bi coverage/assets/0.10.2/application.js 17;" f line:17
|
25
|
+
b$.bj coverage/assets/0.10.2/application.js 17;" f line:17
|
26
|
+
b$.bk coverage/assets/0.10.2/application.js 17;" f line:17
|
27
|
+
b$.bl coverage/assets/0.10.2/application.js 17;" f line:17
|
28
|
+
b$.bm coverage/assets/0.10.2/application.js 17;" f line:17
|
29
|
+
bh.bg coverage/assets/0.10.2/application.js 17;" f line:17
|
30
|
+
cp.ce coverage/assets/0.10.2/application.js 17;" f line:17
|
31
|
+
cp.cf coverage/assets/0.10.2/application.js 17;" f line:17
|
32
|
+
extended lib/subclass_must_implement.rb 51;" F line:51 class:SubclassMustImplement
|
33
|
+
foo spec/subclass_must_implement_spec.rb 12;" f line:12 class:Foo
|
34
|
+
function.cr coverage/assets/0.10.2/application.js 17;" f line:17
|
35
|
+
included lib/subclass_must_implement.rb 46;" F line:46 class:SubclassMustImplement
|
36
|
+
k.function.J coverage/assets/0.10.2/application.js 17;" f line:17
|
37
|
+
k.var.e coverage/assets/0.10.2/application.js 17;" f line:17
|
38
|
+
subclass_must_implement lib/subclass_must_implement.rb 65;" f line:65 class:SubclassMustImplement.ClassMethods
|
39
|
+
version lib/subclass_must_implement.rb 56;" F line:56 class:SubclassMustImplement
|
40
|
+
version spec/subclass_must_implement_spec.rb 24;" f line:24 class:Bar
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subclass_must_implement
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arthur Shagall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Auto-generate abstract method stubs to mark them as required to be implemented
|
98
112
|
in subclasses.
|
99
113
|
email:
|
@@ -111,9 +125,11 @@ files:
|
|
111
125
|
- README.md
|
112
126
|
- Rakefile
|
113
127
|
- lib/subclass_must_implement.rb
|
128
|
+
- lib/subclass_must_implement/rspec_matchers/require_subclass_to_implement_matcher.rb
|
114
129
|
- spec/spec_helper.rb
|
115
130
|
- spec/subclass_must_implement_spec.rb
|
116
131
|
- subclass_must_implement.gemspec
|
132
|
+
- tags
|
117
133
|
homepage: ''
|
118
134
|
licenses:
|
119
135
|
- MIT
|