subclass_must_implement 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be7eacd344df936c6557036eed810d353b476445
4
- data.tar.gz: f7c20318294fc06bb3ff92a2776d81f7cc8fb5d6
3
+ metadata.gz: b9eaa616a9b8edf88f77acd435aae5663f5f7263
4
+ data.tar.gz: 459bd4737551b160317f6cec391be93c23a73666
5
5
  SHA512:
6
- metadata.gz: 5de082a47ac63d76765acfa7bbb2777c7a067ba91861903862dd4b2430b380fd0addbe3d79bdf809780861b124cbbc8f9d3043dce4b8cc3814cd23925a91ebeb
7
- data.tar.gz: 3aec63e7c84a7983315504ff8e0fefd81ade0bc02bf4555f2f81891e5b4ee4e97dda9a2a2157b71a36f571c2580b778aa56b80cca191c2e2a1a9ec13567e2af2
6
+ metadata.gz: d211c4b1ded0fce194fcf8c745cc79e4029d4d50cb37b4cb9a1258b00e7d4900cbe9337805eb4dddbf62c27982a918d59592dbfc6ffb032021ec7c8008dfadbc
7
+ data.tar.gz: c16e69619f667b52bccd90c8a160cd71bb6e40d4332546a63153d805b147a0bed880d41a8e2a8ba908f7f193eeffad8e16b768153218421b90d9c39c61748efb
data/.gitignore CHANGED
@@ -21,4 +21,3 @@ tmp
21
21
  *.a
22
22
  mkmf.log
23
23
  .ruby-version
24
-
data/.simplecov CHANGED
@@ -8,6 +8,7 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
8
8
 
9
9
  SimpleCov.start do
10
10
  add_filter "/spec/"
11
+ add_filter "/lib/subclass_must_implement/rspec_matchers"
11
12
 
12
13
  # Fail the build when coverage is < 100%:
13
14
  at_exit do
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, i.e. it is the responsibility of any subclass to implement them.
4
- Getting a `MethodMIssing` is not helpful; an error that explicitly explains that the missing method is required by contract will save much time.
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
- # Injects the `subclass_must_implement` macro
45
+ # Inject the `subclass_must_implement` macro.
46
46
  def self.included(base)
47
47
  base.extend ClassMethods
48
48
  end
49
49
 
50
- # Injects the `subclass_must_implement` macro
50
+ # Inject the `subclass_must_implement` macro.
51
51
  def self.extended(base)
52
52
  base.extend ClassMethods
53
53
  end
54
54
 
55
- # Returns the Gem version as a string
55
+ # Return the Gem version as a string.
56
+ # @return [String]
56
57
  def self.version
57
- "0.0.1"
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
- # Defines a method for each method name that raises a NotImplementedError when called.
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? ? "`#{method_name}` must be implemented in a subclass." : "#{err_message}"
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
@@ -1,6 +1,8 @@
1
1
  require 'simplecov'
2
2
  SimpleCov.start
3
3
 
4
+ load "subclass_must_implement.rb"
5
+
4
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
5
7
  RSpec.configure do |config|
6
8
  # rspec-expectations config goes here. You can use an alternate
@@ -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 not implemented error if any required method is not implemented in the subclass" do
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 not implemented error if any required method is not implemented in the subclass" do
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
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "simplecov"
25
25
  spec.add_development_dependency "simplecov-rcov-text"
26
26
  spec.add_development_dependency "colorize"
27
+ spec.add_development_dependency "yard"
27
28
  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.1
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-08 00:00:00.000000000 Z
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