method_man 1.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +30 -0
- data/.ruby-version +2 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile +1 -0
- data/README.md +29 -6
- data/Rakefile +2 -1
- data/lib/method_object/version.rb +2 -1
- data/lib/method_object.rb +152 -24
- data/method_man.gemspec +8 -5
- data/spec/method_man_spec.rb +168 -21
- data/spec/spec_helper.rb +1 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0ddc3147244cc0dc922a4ebc1aa9512efedfecf
|
4
|
+
data.tar.gz: 5139372bb0da36319b9cd7a3338da3dd507e1724
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71436d2cf1e9b82acf277477ae6009d1e6d05288f93a848cb8055dfa8b6169c27fdc615ed5ca3ab710b4241f45f658aa0ec5989e2f42380f7b1df92893453a21
|
7
|
+
data.tar.gz: 7cf527f78e56ed58fa9e64f7e0149f006fc398fbb81309f4315fe790288075ed07cd600c623f987daafdffc12dbb8fb127bc5c320a9b0bd83075d218b2eb45d7
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- spec/*
|
4
|
+
TargetRubyVersion: 2.4
|
5
|
+
|
6
|
+
# We don't care about method length, since we check method cyclomatic
|
7
|
+
# complexity.
|
8
|
+
Metrics/MethodLength:
|
9
|
+
Enabled: false
|
10
|
+
Metrics/ClassLength:
|
11
|
+
Enabled: false
|
12
|
+
Metrics/ModuleLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
# Trailing commas make for clearer diffs because the last line won't appear
|
16
|
+
# to have been changed, as it would if it lacked a comma and had one added.
|
17
|
+
Style/TrailingCommaInArguments:
|
18
|
+
EnforcedStyleForMultiline: comma
|
19
|
+
Style/TrailingCommaInLiteral:
|
20
|
+
EnforcedStyleForMultiline: comma
|
21
|
+
|
22
|
+
# Cop supports --auto-correct.
|
23
|
+
# Configuration parameters: PreferredDelimiters.
|
24
|
+
Style/PercentLiteralDelimiters:
|
25
|
+
PreferredDelimiters:
|
26
|
+
# Using `[]` for string arrays instead of `()`, since normal arrays are
|
27
|
+
# indicated with `[]` not `()`.
|
28
|
+
'%w': '[]'
|
29
|
+
'%W': '[]'
|
30
|
+
'%i': '[]'
|
data/.ruby-version
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
2.
|
1
|
+
2.4.0
|
2
|
+
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## 2.0.0
|
4
|
+
Convert MethodObject to use inheritance and a class method for dynamic setup of class internals
|
5
|
+
- Enables code editors to find declaration of MethodObject
|
6
|
+
- Allows constants to be nested whereas previous implementation was based on a Struct which could not contain constants.
|
7
|
+
|
8
|
+
## 2.1.0
|
9
|
+
Allow automatic delegation inspired by Golang's embedding.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
Defines a MethodObject class which facilitates basic setup for Kent Beck's "[method object](http://c2.com/cgi/wiki?MethodObject)".
|
4
4
|
|
5
|
-
The MethodObject class is largely based on a Ruby Struct, but with a few additional features:
|
6
|
-
|
7
5
|
* Facilitates basic method object pattern setup. You only need to supply an instance `call` method.
|
8
6
|
* Accepts a list of arguments which are mapped to required keyword arguments.
|
9
7
|
* Disallows calling `new` on the resulting MethodObject class instance.
|
@@ -22,13 +20,13 @@ gem 'method_man', require: 'method_object'
|
|
22
20
|
```ruby
|
23
21
|
require 'method_object'
|
24
22
|
|
25
|
-
MakeArbitraryArray
|
23
|
+
class MakeArbitraryArray < MethodObject
|
24
|
+
attrs(:first_name, :last_name, :message)
|
25
|
+
|
26
26
|
def call
|
27
27
|
[fullname, message, 42]
|
28
28
|
end
|
29
29
|
|
30
|
-
private
|
31
|
-
|
32
30
|
def fullname
|
33
31
|
"#{first_name} #{last_name}"
|
34
32
|
end
|
@@ -39,5 +37,30 @@ gem 'method_man', require: 'method_object'
|
|
39
37
|
last_name: 'Smith',
|
40
38
|
message: 'Hi',
|
41
39
|
)
|
42
|
-
=> [
|
40
|
+
=> ['John Smith', 'Hi', 42]
|
41
|
+
```
|
42
|
+
|
43
|
+
Also allows automatic delegation inspired by Go's
|
44
|
+
[delegation](https://nathany.com/good/).
|
45
|
+
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
require 'method_object'
|
49
|
+
|
50
|
+
class MakeArbitraryArray < MethodObject
|
51
|
+
attrs(:company)
|
52
|
+
|
53
|
+
def call
|
54
|
+
[
|
55
|
+
company.name,
|
56
|
+
name, # Automatic delegation since company has a `name` method
|
57
|
+
company_name, # Automatic delegation with prefix
|
58
|
+
]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
company = Company.new(name: 'Tyrell Corporation')
|
63
|
+
|
64
|
+
MakeArbitraryArray.call(company: company)
|
65
|
+
=> ['Tyrell Corporation', 'Tyrell Corporation', 'Tyrell Corporation']
|
43
66
|
```
|
data/Rakefile
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'bundler/gem_tasks'
|
data/lib/method_object.rb
CHANGED
@@ -1,42 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'method_object/version'
|
2
3
|
|
4
|
+
# See gemspec for description
|
3
5
|
class MethodObject
|
4
|
-
|
5
|
-
|
6
|
-
|
6
|
+
class AmbigousMethodError < NameError; end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def attrs(*attributes)
|
10
|
+
@attributes = attributes
|
11
|
+
Setup.call(attributes: attributes, subclass: self)
|
12
|
+
end
|
7
13
|
|
8
|
-
|
9
|
-
|
10
|
-
|
14
|
+
def call(**args)
|
15
|
+
new(args).call
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader(:attributes)
|
19
|
+
|
20
|
+
private(:new)
|
11
21
|
end
|
12
22
|
|
23
|
+
def initialize(_); end
|
24
|
+
|
13
25
|
def call
|
14
|
-
|
15
|
-
|
26
|
+
raise NotImplementedError, 'Define the call method'
|
27
|
+
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
def method_missing(name, *args, &block)
|
30
|
+
candidates = candidates_for_method_missing(name)
|
31
|
+
case candidates.length
|
32
|
+
when 0
|
33
|
+
super
|
34
|
+
when 1
|
35
|
+
define_and_call_new_method(candidates.first)
|
36
|
+
else
|
37
|
+
handle_ambiguous_missing_method(candidates, name)
|
24
38
|
end
|
25
39
|
end
|
26
40
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
41
|
+
def respond_to_missing?(name)
|
42
|
+
candidates_for_method_missing(name).length == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def candidates_for_method_missing(method_name)
|
46
|
+
potential_candidates =
|
47
|
+
self.class.attributes.map do |attribute|
|
48
|
+
PotentialDelegator.new(attribute, send(attribute), method_name)
|
49
|
+
end +
|
50
|
+
self.class.attributes.map do |attribute|
|
51
|
+
PotentialDelegatorWithPrefix.new(
|
52
|
+
attribute,
|
53
|
+
send(attribute),
|
54
|
+
method_name,
|
55
|
+
)
|
31
56
|
end
|
32
|
-
|
57
|
+
potential_candidates.select(&:candidate?)
|
33
58
|
end
|
34
59
|
|
35
|
-
def
|
36
|
-
|
60
|
+
def define_and_call_new_method(candidate)
|
61
|
+
self.class.class_eval(
|
62
|
+
<<-RUBY
|
63
|
+
def #{candidate.delegated_method}
|
64
|
+
#{candidate.attribute}.#{candidate.method_to_call_on_delegate}
|
65
|
+
end
|
66
|
+
RUBY
|
67
|
+
)
|
68
|
+
send(candidate.delegated_method)
|
37
69
|
end
|
38
70
|
|
39
|
-
def
|
40
|
-
|
71
|
+
def handle_ambiguous_missing_method(candidates, method_name)
|
72
|
+
raise(
|
73
|
+
AmbigousMethodError,
|
74
|
+
"#{method_name} is ambiguous: " +
|
75
|
+
candidates
|
76
|
+
.map do |candidate|
|
77
|
+
"#{candidate.attribute}.#{candidate.method_to_call_on_delegate}"
|
78
|
+
end
|
79
|
+
.join(', '),
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Represents a possible match of the form:
|
84
|
+
# some_method => my_attribute.some_method
|
85
|
+
PotentialDelegator = Struct.new(:attribute, :object, :delegated_method) do
|
86
|
+
def candidate?
|
87
|
+
object.respond_to?(delegated_method)
|
88
|
+
end
|
89
|
+
|
90
|
+
def call
|
91
|
+
object.public_send(delegated_method)
|
92
|
+
end
|
93
|
+
|
94
|
+
alias_method :method_to_call_on_delegate, :delegated_method
|
95
|
+
end
|
96
|
+
|
97
|
+
# Represents a possible match of the form:
|
98
|
+
# my_attribute_some_method => my_attribute.some_method
|
99
|
+
PotentialDelegatorWithPrefix =
|
100
|
+
Struct.new(:attribute, :object, :delegated_method) do
|
101
|
+
def candidate?
|
102
|
+
name_matches? && object.respond_to?(method_to_call_on_delegate)
|
103
|
+
end
|
104
|
+
|
105
|
+
def call
|
106
|
+
object.public_send(method_to_call_on_delegate)
|
107
|
+
end
|
108
|
+
|
109
|
+
def method_to_call_on_delegate
|
110
|
+
delegated_method.to_s.sub(prefix, '')
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def name_matches?
|
116
|
+
delegated_method.to_s.start_with?(prefix)
|
117
|
+
end
|
118
|
+
|
119
|
+
def prefix
|
120
|
+
"#{attribute}_"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Dynamically defines custom attr_readers and initializer
|
125
|
+
class Setup < SimpleDelegator
|
126
|
+
def self.call(attributes:, subclass:)
|
127
|
+
new(attributes, subclass).call
|
128
|
+
end
|
129
|
+
|
130
|
+
attr_accessor(:attributes)
|
131
|
+
|
132
|
+
def initialize(attributes, subclass)
|
133
|
+
self.attributes = attributes
|
134
|
+
super(subclass)
|
135
|
+
end
|
136
|
+
|
137
|
+
def call
|
138
|
+
define_attr_readers
|
139
|
+
define_initializer
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def define_attr_readers
|
145
|
+
__getobj__.send(:attr_reader, *attributes)
|
146
|
+
end
|
147
|
+
|
148
|
+
def attr_accessor(attribute)
|
149
|
+
super
|
150
|
+
end
|
151
|
+
|
152
|
+
def define_initializer
|
153
|
+
class_eval(
|
154
|
+
<<-RUBY
|
155
|
+
def initialize(#{required_keyword_args_string})
|
156
|
+
#{assignments}
|
157
|
+
end
|
158
|
+
RUBY
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
def required_keyword_args_string
|
163
|
+
attributes.map { |arg| "#{arg}:" }.join(',')
|
164
|
+
end
|
165
|
+
|
166
|
+
def assignments
|
167
|
+
attributes.map { |attribute| "@#{attribute} = #{attribute}\n" }.join
|
168
|
+
end
|
41
169
|
end
|
42
170
|
end
|
data/method_man.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'method_object/version'
|
@@ -7,16 +8,18 @@ Gem::Specification.new do |spec|
|
|
7
8
|
spec.name = 'method_man'
|
8
9
|
spec.version = MethodObject::VERSION
|
9
10
|
spec.authors = ['Clay Shentrup']
|
10
|
-
spec.email = %w
|
11
|
-
spec.summary =
|
12
|
-
|
11
|
+
spec.email = %w[cshentrup@gmail.com]
|
12
|
+
spec.summary = 'Provides a MethodObject class which implements Kent' +
|
13
|
+
%q(Beck's "method object" pattern.)
|
14
|
+
spec.description = 'Provides a MethodObject class which implements Kent' +
|
15
|
+
%q(Beck's "method object" pattern.)
|
13
16
|
spec.homepage = 'https://github.com/brokenladder/method_man'
|
14
17
|
spec.license = 'MIT'
|
15
18
|
|
16
|
-
spec.files = `git ls-files`.split(
|
19
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = %w
|
22
|
+
spec.require_paths = %w[lib]
|
20
23
|
|
21
24
|
spec.required_ruby_version = '>= 2.1'
|
22
25
|
|
data/spec/method_man_spec.rb
CHANGED
@@ -1,38 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'method_object'
|
2
3
|
|
3
4
|
describe MethodObject do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
it 'makes new a private class method' do
|
6
|
+
expect { subject.new }.to raise_error(NoMethodError)
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'without attrs' do
|
10
|
+
subject do
|
11
|
+
Class.new(described_class) do
|
12
|
+
def call
|
13
|
+
true
|
14
|
+
end
|
8
15
|
end
|
9
16
|
end
|
10
|
-
end
|
11
17
|
|
12
|
-
|
13
|
-
let(:value_two) { 2 }
|
14
|
-
let(:actual_result) do
|
15
|
-
subject.call(value_one: value_one, value_two: value_two)
|
18
|
+
specify { expect(subject.call).to be(true) }
|
16
19
|
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
context 'with attrs' do
|
22
|
+
subject do
|
23
|
+
Class.new(described_class) do
|
24
|
+
attrs(:company, :user)
|
21
25
|
|
22
|
-
|
23
|
-
expect { subject.call }.to raise_error ArgumentError
|
24
|
-
end
|
26
|
+
@sent_messages = []
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
def self.sent_messages
|
29
|
+
@sent_messages
|
30
|
+
end
|
31
|
+
|
32
|
+
def call
|
33
|
+
{
|
34
|
+
address: address,
|
35
|
+
respond_to_address: respond_to_missing?(:address),
|
36
|
+
company_address: company_address,
|
37
|
+
respond_to_company_address: respond_to_missing?(:company_address),
|
38
|
+
company: company,
|
39
|
+
respond_to_name: respond_to_missing?(:name),
|
40
|
+
company_name: company_name,
|
41
|
+
respond_to_company_name: respond_to_missing?(:company_name),
|
42
|
+
user: user,
|
43
|
+
user_name: user_name,
|
44
|
+
respond_to_user_name: respond_to_missing?(:user_name),
|
45
|
+
respond_to_missing: respond_to_missing?(:undefined_method),
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:company) do
|
52
|
+
double('company', address: company_address, name: company_name)
|
53
|
+
end
|
54
|
+
let(:company_address) { '101 Minitru Lane' }
|
55
|
+
let(:company_name) { 'Periscope Data' }
|
56
|
+
let(:user) { double('user', name: user_name) }
|
57
|
+
let(:user_name) { 'Woody' }
|
58
|
+
|
59
|
+
let(:result) { subject.call(company: company, user: user) }
|
60
|
+
|
61
|
+
specify do
|
62
|
+
expect(result).to eq(
|
63
|
+
address: company_address,
|
64
|
+
respond_to_address: true,
|
65
|
+
company_address: company_address,
|
66
|
+
respond_to_company_address: true,
|
67
|
+
company: company,
|
68
|
+
respond_to_name: false,
|
69
|
+
company_name: company_name,
|
70
|
+
respond_to_company_name: true,
|
71
|
+
user: user,
|
72
|
+
user_name: user_name,
|
73
|
+
respond_to_user_name: true,
|
74
|
+
respond_to_missing: false,
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'uses required keyword arguments' do
|
79
|
+
expect { subject.call }.to raise_error(ArgumentError)
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with ambiguous method call' do
|
83
|
+
subject do
|
84
|
+
Class.new(described_class) do
|
85
|
+
attrs(:company, :user)
|
86
|
+
|
87
|
+
def call
|
88
|
+
name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
specify do
|
94
|
+
expect { result }.to raise_error(
|
95
|
+
MethodObject::AmbigousMethodError,
|
96
|
+
a_string_including('company.name, user.name'),
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'ambigous method call due to delegation' do
|
102
|
+
subject do
|
103
|
+
Class.new(described_class) do
|
104
|
+
attrs(:company, :user)
|
105
|
+
|
106
|
+
def call
|
107
|
+
company_address
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
let(:user) { double('user', company_address: nil) }
|
113
|
+
|
114
|
+
specify do
|
115
|
+
expect { result }.to raise_error(
|
116
|
+
MethodObject::AmbigousMethodError,
|
117
|
+
a_string_including('user.company_address, company.address'),
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'respecting method privacy' do
|
123
|
+
let(:subject) do
|
124
|
+
Class.new(described_class) do
|
125
|
+
attrs(:diary)
|
126
|
+
|
127
|
+
def call
|
128
|
+
diary_contents
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
let(:diary) do
|
133
|
+
Module.new do
|
134
|
+
def self.contents; end
|
135
|
+
|
136
|
+
private_class_method(:contents)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
specify do
|
141
|
+
expect { subject.call(diary: diary) }.to raise_error(StandardError)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe '"memoizes" method calls' do
|
146
|
+
subject do
|
147
|
+
Class.new(described_class) do
|
148
|
+
attrs(:company)
|
149
|
+
@sent_messages = []
|
150
|
+
|
151
|
+
def self.sent_messages
|
152
|
+
@sent_messages
|
153
|
+
end
|
154
|
+
|
155
|
+
def call
|
156
|
+
[name, name]
|
157
|
+
end
|
158
|
+
|
159
|
+
# Ensure it defines resolved methods
|
160
|
+
def method_missing(method, *_args)
|
161
|
+
if self.class.sent_messages.include?(method)
|
162
|
+
raise 'method not memoized'
|
163
|
+
end
|
164
|
+
self.class.sent_messages << method
|
165
|
+
super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
specify do
|
171
|
+
expect(subject.call(company: company)).to eq([company_name, company_name])
|
172
|
+
end
|
173
|
+
end
|
28
174
|
end
|
29
175
|
|
30
176
|
context 'without a provided instance call method' do
|
31
|
-
subject
|
177
|
+
subject do
|
178
|
+
Class.new(described_class) { attrs(:user_1_age) }
|
179
|
+
end
|
32
180
|
|
33
|
-
|
34
|
-
expect { subject.call(
|
35
|
-
.to raise_error NotImplementedError
|
181
|
+
specify do
|
182
|
+
expect { subject.call(user_1_age: user_1_age) }.to raise_error(NameError)
|
36
183
|
end
|
37
184
|
end
|
38
185
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_man
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Clay Shentrup
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.2'
|
55
|
-
description: Provides a MethodObject class which implements
|
55
|
+
description: Provides a MethodObject class which implements KentBeck's "method object"
|
56
56
|
pattern.
|
57
57
|
email:
|
58
58
|
- cshentrup@gmail.com
|
@@ -62,7 +62,9 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
64
|
- ".rspec"
|
65
|
+
- ".rubocop.yml"
|
65
66
|
- ".ruby-version"
|
67
|
+
- CHANGELOG.md
|
66
68
|
- Gemfile
|
67
69
|
- LICENSE
|
68
70
|
- LICENSE.txt
|
@@ -93,10 +95,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
95
|
version: '0'
|
94
96
|
requirements: []
|
95
97
|
rubyforge_project:
|
96
|
-
rubygems_version: 2.
|
98
|
+
rubygems_version: 2.6.8
|
97
99
|
signing_key:
|
98
100
|
specification_version: 4
|
99
|
-
summary: Provides a MethodObject class which implements
|
101
|
+
summary: Provides a MethodObject class which implements KentBeck's "method object"
|
100
102
|
pattern.
|
101
103
|
test_files:
|
102
104
|
- spec/method_man_spec.rb
|