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 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