interrobang 1.0.0
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 +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +12 -0
- data/.yardopts +6 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +266 -0
- data/Rakefile +8 -0
- data/interrobang.gemspec +28 -0
- data/lib/interrobang.rb +103 -0
- data/lib/interrobang/version.rb +3 -0
- data/test/lib/predicate_bang_test.rb +189 -0
- data/test/test_helper.rb +7 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6095e5897bbea3cff7301fdc71e9957fb8780eab
|
4
|
+
data.tar.gz: d1832778b017ca518c234006241e5bbd8406e17b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60c8312652570cad7ae79b0a0388f84b040bcd4d4cdb0bb9083d3c0283a2a9bd4991eea28f2c4d68cb5eedf3b4b157acdf6f25c93fc9f2fc94bf95a90bf1f3fe
|
7
|
+
data.tar.gz: 622daee4c4311256827dea66f1b6251e2f9ddc27129be4e0143e33c409b46ecb0e75eb7d2e480bdce36bceb48a7a5fb2963610e52358026486e799672a6ccd4e
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Faraz Yashar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
# Interrobang :interrobang:
|
2
|
+
|
3
|
+
[](https://travis-ci.org/fny/interrobang)
|
4
|
+
[](https://codeclimate.com/github/fny/interrobang)
|
5
|
+
|
6
|
+
Convert your `predicate_methods?` into `bang_methods!` without abusing `method_missing`.
|
7
|
+
|
8
|
+
`Interrobang` currently only works with Ruby versions that support keyword arguments.
|
9
|
+
|
10
|
+
## Overview
|
11
|
+
|
12
|
+
Say we have the following class:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
class Answer
|
16
|
+
# Say these return a boolean.
|
17
|
+
def correct?; end
|
18
|
+
def is_correct; end
|
19
|
+
def is_factual; end
|
20
|
+
def is_right; end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
`Interrobang` automagically adds corresponding bang methods for any predicate methods that end in a `?`. The bang methods explode when the predicate method returns a falsey value.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
Interrobang.bangify(Answer)
|
28
|
+
answer = Answer.new # => [:correct!]
|
29
|
+
answer.respond_to?(:correct!) # => true (no method missing shenanigans!)
|
30
|
+
Answer.new.correct! # => Raises Interrobang::FalsePredicate if `#correct?` is false
|
31
|
+
```
|
32
|
+
|
33
|
+
You can add prefixes and suffixes to the generated bang method.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
Interrobang.bangify(Answer, prefix: 'ensure_', suffix: '_or_else')
|
37
|
+
# => [:ensure_correct_or_else!]
|
38
|
+
Answer.new.ensure_correct_or_else!
|
39
|
+
# => Raises Interrobang::FalsePredicate if `#correct?` is false
|
40
|
+
```
|
41
|
+
|
42
|
+
Provide your own blocks to execute on failure. You can optionally access the symbol of the predicate method as an argument.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Interrobang.bangify(Answer, prefix: 'ensure_') do |predicate_method|
|
46
|
+
raise StandardError, predicate_method
|
47
|
+
end # => [:ensure_correct!]
|
48
|
+
Answer.new.ensure_correct! # => Raises StandardError if `#correct?` is false
|
49
|
+
```
|
50
|
+
|
51
|
+
Need to convert a single method? No problem.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Interrobang.bangify_method(Answer, :correct?, prefix: 'ensure_', suffix: '_on_saturday') do
|
55
|
+
if Time.now.saturday?
|
56
|
+
raise WeekendLaziness
|
57
|
+
else
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end # => :ensure_correct_on_saturday!
|
61
|
+
```
|
62
|
+
|
63
|
+
### Filters
|
64
|
+
|
65
|
+
Perhaps you'd like to convert methods that match a different pattern?
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Interrobang.bangify(Answer, matching: %r{\Ais_.*\z})
|
69
|
+
# => [:is_correct!, :is_factual!, :is_right!]
|
70
|
+
```
|
71
|
+
|
72
|
+
You can exclude methods that match the pattern with `except`.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Interrobang.bangify(Answer, matching: %r{\Ais_.*\z},
|
76
|
+
except: [:is_factual, :is_right])
|
77
|
+
# => [:is_correct!]
|
78
|
+
```
|
79
|
+
|
80
|
+
Maybe you'd like to state the methods to convert explicitly?
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
Interrobang.bangify(Answer, only: :is_correct) # => [:is_correct!]
|
84
|
+
```
|
85
|
+
|
86
|
+
You can opt to include methods from parent classes, but proceed with caution...
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Interrobang.bangify(Answer, include_super: true, prefix: 'ensure_')
|
90
|
+
# => [:ensure_correct!, :ensure_nil!, :ensure_eql!, :ensure_tainted!, :ensure_untrusted!, :ensure_frozen!, :ensure_instance_variable_defined!, :ensure_instance_of!, :ensure_kind_of!, :ensure_is_a!, :ensure_respond_to!, :ensure_equal!]
|
91
|
+
Answer.new.ensure_nil! # => Raises Interrobang::FalsePredicate
|
92
|
+
```
|
93
|
+
|
94
|
+
Too lazy to type `Interrobang`? Just `extend` it. It's methods are `module_function`s!
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class Answer
|
98
|
+
extend Interrobang
|
99
|
+
bangify self
|
100
|
+
bangify_method self, :is_special
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
See `lib/interrobang.rb` for complete documentation and the tests for details.
|
105
|
+
|
106
|
+
## Installation
|
107
|
+
|
108
|
+
Add this line to your application's Gemfile:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
gem 'interrobang'
|
112
|
+
```
|
113
|
+
|
114
|
+
And then execute:
|
115
|
+
|
116
|
+
$ bundle
|
117
|
+
|
118
|
+
Or install it yourself as:
|
119
|
+
|
120
|
+
$ gem install interrobang
|
121
|
+
|
122
|
+
|
123
|
+
## Example Use Case with Rails
|
124
|
+
|
125
|
+
`Interrobang` works wonderfully with permission-related objects. Say we have a bangified `Protector` class that defines user permissions in our application:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class Protector
|
129
|
+
NotSignedIn = Class.new(Exception)
|
130
|
+
Unauthorized = Class.new(Exception)
|
131
|
+
|
132
|
+
def initialize(user)
|
133
|
+
@user = user
|
134
|
+
end
|
135
|
+
|
136
|
+
def signed_in?
|
137
|
+
@user.is_a?(User)
|
138
|
+
end
|
139
|
+
|
140
|
+
def admin?
|
141
|
+
@user && @user.is_admin
|
142
|
+
end
|
143
|
+
|
144
|
+
def can_edit_user?(other_user)
|
145
|
+
@user && (@user.is_admin || @user.id == other_user.id)
|
146
|
+
end
|
147
|
+
|
148
|
+
Interrobang.bangify(self, prefix: 'ensure_') do |predicate_method|
|
149
|
+
raise Unauthorized, "#{predicate_method} failed"
|
150
|
+
end
|
151
|
+
|
152
|
+
Interrobang.bangify_method(self, :signed_in?, prefix: 'ensure_') do |predicate_method|
|
153
|
+
raise NotSignedIn, "#{predicate_method} failed"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
In our controller, we can then define rescue handlers for the those exceptions, and add a method to access a `Protector` instance.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
class ApplicationController < ActionController::Base
|
162
|
+
def protector
|
163
|
+
@protector ||= Protector.new(current_user)
|
164
|
+
end
|
165
|
+
|
166
|
+
rescue_from Protector::NotSignedIn do
|
167
|
+
redirect_to sign_in_path, alert: "Please sign in to continue."
|
168
|
+
end
|
169
|
+
|
170
|
+
rescue_from Protector::Unauthorized do
|
171
|
+
# Handle as you will
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
Now we can call `protector.ensure_signed_in!`, `protector.ensure_admin!`, `protector.ensure_can_edit!(other_user)!` from any controller and trigger the errors defined with `Interrobang`.
|
177
|
+
|
178
|
+
### Aside: Testing Tricks with Rescue Handlers
|
179
|
+
|
180
|
+
For tests, we can stub the rescue handlers with methods that expose the original errors so we can check for them directly.
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
# spec/support/helpers.rb
|
184
|
+
def raise_handled_rescues(controller = ApplicationController)
|
185
|
+
stubbed_handlers = controller.rescue_handlers.map { |rescue_handler|
|
186
|
+
name, proc = rescue_handler
|
187
|
+
[ name, -> { raise Kernel.const_get(name) } ]
|
188
|
+
}
|
189
|
+
allow(controller).to receive(:rescue_handlers).and_return(stubbed_handlers)
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
This allows us to test that proper errors are being raised independently from testing each error's particular handling.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
# spec/controllers/users_controller_spec.rb
|
197
|
+
RSpec.describe UsersController, type: :controller do
|
198
|
+
before { raise_handled_rescues }
|
199
|
+
after { reset_handled_rescues }
|
200
|
+
describe "GET index" do
|
201
|
+
context "unauthenticated user" do
|
202
|
+
it "raises Protector::NotSignedIn" do
|
203
|
+
expect { get :index }.to raise_error(Protector::NotSignedIn)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# spec/controllers/application_controller_spec.rb
|
210
|
+
RSpec.describe ApplicationController, type: :controller do
|
211
|
+
describe "Protector::NotSignedIn rescue handler" do
|
212
|
+
controller { def index; raise Protector::NotSignedIn; end }
|
213
|
+
it "redirects to the sign in page" do
|
214
|
+
get :index
|
215
|
+
expect(response).to redirect_to sign_in_path
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
## What are these predicate methods and bang methods?
|
222
|
+
|
223
|
+
**Predicate methods** return a Boolean. By Ruby convention, these methods typically end in a `?`. Other languages like [Scheme][scheme-conventions], [C#][csharp-predicates], [Java][java-predicates], support this interface too.
|
224
|
+
|
225
|
+
**Bang methods** are "dangerous" or modify the receiver. By convention, these methods typically end with a `!`. In the case of `Interrobang`, these methods are considered "dangerous" because they may raise an exception.
|
226
|
+
|
227
|
+
### Fun Fact
|
228
|
+
|
229
|
+
The Ruby conventions for `?` and `!` are borrowed from [Scheme][scheme-conventions]:
|
230
|
+
|
231
|
+
> 1.3.5 Naming conventions
|
232
|
+
>
|
233
|
+
>
|
234
|
+
> By convention, the names of procedures that always return a boolean value usually end in ``?''. Such procedures are called predicates.
|
235
|
+
>
|
236
|
+
> By convention, the names of procedures that store values into previously
|
237
|
+
> allocated locations (see section 3.4) usually end in ``!''. Such procedures
|
238
|
+
> are called mutation procedures. By convention, the value returned by a
|
239
|
+
> mutation procedure is unspecified.
|
240
|
+
|
241
|
+
## Development
|
242
|
+
|
243
|
+
Be sure to test all the things. Just `rake test`. You can use `bundle console` to play with things in an IRB session.
|
244
|
+
|
245
|
+
## Contributing
|
246
|
+
|
247
|
+
1. Fork it ( https://github.com/fny/interrobang/fork )
|
248
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
249
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
250
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
251
|
+
5. Create a new Pull Request
|
252
|
+
|
253
|
+
## Special Thanks To...
|
254
|
+
|
255
|
+
- [Discourse][discourse] for inspiring me to find a `method_missing`-free alternative to its [EnsureMagic][discourse-ensure]
|
256
|
+
- [Michael Josephson][josephson] for [pointing me in the right direction][so-question]
|
257
|
+
- To all [contributers][github-contributers]! :beers:
|
258
|
+
|
259
|
+
[csharp-predicates]: https://msdn.microsoft.com/en-us/library/bfcke1bz%28v=vs.110%29.aspx "Predicate<T> Delegate"
|
260
|
+
[java-predicates]: https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html "Interface Predicate<T>"
|
261
|
+
[scheme-conventions]: http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-4.html#%_sec_1.3.5 "Scheme Naming Conventions"
|
262
|
+
[discourse]: http://discourse.org "Discourse"
|
263
|
+
[discourse-ensure]: https://github.com/discourse/discourse/blob/ba0084edee8ace004855b987e1661a7eaff60122/lib/guardian/ensure_magic.rb "module EnsureMagic"
|
264
|
+
[josephson]: http://www.josephson.org/ "Michael Josephson"
|
265
|
+
[so-question]: http://stackoverflow.com/questions/28818193/define-method-based-on-existing-method-in-ruby "Define Method Based on Existing Method in Ruby - Stack Overflow"
|
266
|
+
[github-contributers]: https://github.com/fny/interrobang/graphs/contributors "Predicate Bang Contributers - GitHub"
|
data/Rakefile
ADDED
data/interrobang.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'interrobang/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'interrobang'
|
8
|
+
spec.version = Interrobang::VERSION
|
9
|
+
spec.authors = ["Faraz Yashar"]
|
10
|
+
spec.email = ["faraz.yashar@gmail.com"]
|
11
|
+
spec.summary = "Convert your predicate_methods? into bang_methods! without
|
12
|
+
abusing method_missing"
|
13
|
+
spec.description = "Convert your predicate_methods? into bang_methods! without
|
14
|
+
abusing method_missing."
|
15
|
+
spec.homepage = 'https://github.com/fny/interrobang'
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5.5'
|
26
|
+
|
27
|
+
spec.required_ruby_version = '>= 2.0.0'
|
28
|
+
end
|
data/lib/interrobang.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'interrobang/version'
|
2
|
+
|
3
|
+
# Convert your `#predicate_methods?` to `#bang_methods!`
|
4
|
+
module Interrobang
|
5
|
+
# Exception to raise when no block is provided for bangified falsey methods
|
6
|
+
FalsePredicate = Class.new(Exception)
|
7
|
+
|
8
|
+
# Regexp that matches methods that end in question marks.
|
9
|
+
DEFAULT_PATTERN = %r{\A[^?]+\?\z}
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Converts the specified predicate methods in a class to bang methods.
|
14
|
+
#
|
15
|
+
# klass - The Class to target for bangification
|
16
|
+
# block - An optional block to run if a predicate method returns something falsey
|
17
|
+
#
|
18
|
+
# Options
|
19
|
+
#
|
20
|
+
# matching - The Regexp used to match methods that should be bangified; defaults to DEFAULT_PATTERN
|
21
|
+
# only - The Symbol or Symbol Array of methods to bangify exclusively
|
22
|
+
# except - The Symbol or Symbol Array of methods to ignore when pattern matching
|
23
|
+
# prefix - The String prefix to add to front of the bangified method
|
24
|
+
# suffix - The String suffix to add to end of the bangified method
|
25
|
+
# inlcude_super - The Boolean specifying whether to bangify parent methods
|
26
|
+
#
|
27
|
+
# Returns the Symbol Array of bangified method names.
|
28
|
+
def bangify(klass, matching: DEFAULT_PATTERN, only: [], except: [], prefix: '', suffix: '', include_super: false)
|
29
|
+
method_keys = klass.instance_methods(include_super)
|
30
|
+
only = [only] unless only.is_a?(Array)
|
31
|
+
except = [except] unless except.is_a?(Array)
|
32
|
+
if only.empty?
|
33
|
+
method_keys.map do |method_key|
|
34
|
+
if method_key.to_s =~ matching && !except.include?(method_key)
|
35
|
+
if block_given?
|
36
|
+
bangify_method(klass, method_key, prefix: prefix, suffix: suffix, &Proc.new)
|
37
|
+
else
|
38
|
+
bangify_method(klass, method_key, prefix: prefix, suffix: suffix)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end.compact
|
42
|
+
else
|
43
|
+
method_keys.map do |method_key|
|
44
|
+
if only.include?(method_key)
|
45
|
+
if block_given?
|
46
|
+
bangify_method(klass, method_key, prefix: prefix, suffix: suffix, &Proc.new)
|
47
|
+
else
|
48
|
+
bangify_method(klass, method_key, prefix: prefix, suffix: suffix)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end.compact
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converts the specified predicate method to a bang method.
|
56
|
+
#
|
57
|
+
# klass - The Class to target for bangification
|
58
|
+
# predicate_method - The Symbol of the predicate method
|
59
|
+
# block - An optional block to run if a predicate method returns something falsey
|
60
|
+
#
|
61
|
+
# Options
|
62
|
+
#
|
63
|
+
# matching - The Regexp used to match methods that should be bangified
|
64
|
+
# prefix - The String prefix to add to front of the bangified method
|
65
|
+
# suffix - The String suffix to add to end of the bangified method
|
66
|
+
#
|
67
|
+
# Returns the Symbol name of the bang method created.
|
68
|
+
def bangify_method(klass, predicate_method, prefix: '', suffix: '')
|
69
|
+
predicate_method_string = predicate_method.to_s
|
70
|
+
method_name_base =
|
71
|
+
case predicate_method_string[-1]
|
72
|
+
when '=', '!'
|
73
|
+
return
|
74
|
+
when '?'
|
75
|
+
predicate_method.to_s[0..-2]
|
76
|
+
else
|
77
|
+
predicate_method.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
bang_method = :"#{prefix}#{method_name_base}#{suffix}!"
|
81
|
+
|
82
|
+
klass.class_eval do
|
83
|
+
if block_given?
|
84
|
+
define_method(bang_method) do |*args, &block|
|
85
|
+
if send(predicate_method, *args, &block)
|
86
|
+
true
|
87
|
+
else
|
88
|
+
yield(predicate_method)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
else
|
92
|
+
define_method(bang_method) do |*args, &block|
|
93
|
+
if send(predicate_method, *args, &block)
|
94
|
+
true
|
95
|
+
else
|
96
|
+
raise(Interrobang::FalsePredicate, "#{predicate_method} is false")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
bang_method
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
SomeError = Class.new(Exception)
|
4
|
+
|
5
|
+
def test_class
|
6
|
+
Class.new {
|
7
|
+
def true?; true; end
|
8
|
+
def veritable?; true; end
|
9
|
+
def so_true; true; end
|
10
|
+
def so_very_true; true; end
|
11
|
+
def false?; false; end
|
12
|
+
def so_false; false; end
|
13
|
+
def assignment=; end
|
14
|
+
def bang!; '!'; end
|
15
|
+
def with_argument?(bool); bool; end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Interrobang do
|
20
|
+
describe '.bangify_method' do
|
21
|
+
describe "with a method that ends in a ?" do
|
22
|
+
it "adds a ! method dropping the ?" do
|
23
|
+
klass = test_class
|
24
|
+
Interrobang.bangify_method(klass, :true?)
|
25
|
+
assert klass.new.true!
|
26
|
+
end
|
27
|
+
it "has no method missing shenanigans" do
|
28
|
+
klass = test_class
|
29
|
+
Interrobang.bangify_method(klass, :true?)
|
30
|
+
assert klass.new.respond_to?(:true!)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "with a method that does not end in a ?" do
|
35
|
+
it "adds a ! method" do
|
36
|
+
klass = test_class
|
37
|
+
Interrobang.bangify_method(klass, :so_true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns the symbol of the bangified method" do
|
42
|
+
klass = test_class
|
43
|
+
assert_equal Interrobang.bangify_method(klass, :true?), :true!
|
44
|
+
end
|
45
|
+
|
46
|
+
it "works on methods with arguments" do
|
47
|
+
klass = test_class
|
48
|
+
Interrobang.bangify_method(klass, :with_argument?)
|
49
|
+
assert klass.new.with_argument!(true)
|
50
|
+
-> { klass.new.with_argument!(false) }.must_raise Interrobang::FalsePredicate
|
51
|
+
end
|
52
|
+
|
53
|
+
it "does not convert assignment methods" do
|
54
|
+
klass = test_class
|
55
|
+
Interrobang.bangify_method klass, :assignment_=
|
56
|
+
-> { klass.new.assignment_! }.must_raise NoMethodError
|
57
|
+
end
|
58
|
+
|
59
|
+
it "does not convert bang methods" do
|
60
|
+
klass = test_class
|
61
|
+
Interrobang.bangify_method klass, :bang!
|
62
|
+
assert_equal klass.new.bang!, '!'
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "options" do
|
66
|
+
it "adds any provided prefix or suffix to the bang method" do
|
67
|
+
klass = test_class
|
68
|
+
Interrobang.bangify_method(klass, :true?, prefix: 'prefix_', suffix: '_suffix')
|
69
|
+
assert klass.new.prefix_true_suffix!
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "falsey predicates" do
|
74
|
+
describe "without a custom block" do
|
75
|
+
it "raises a FalsePredicate error" do
|
76
|
+
klass = test_class
|
77
|
+
Interrobang.bangify_method klass, :false?
|
78
|
+
err = -> { klass.new.false! }.must_raise Interrobang::FalsePredicate
|
79
|
+
assert_equal err.message, 'false? is false'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "with a provided block" do
|
84
|
+
it "performs the provided block for the bang method" do
|
85
|
+
klass = test_class
|
86
|
+
Interrobang.bangify_method klass, :false? do
|
87
|
+
raise SomeError
|
88
|
+
end
|
89
|
+
-> { klass.new.false! }.must_raise SomeError
|
90
|
+
end
|
91
|
+
|
92
|
+
it "allows the provided block to take the predicate method as an argument" do
|
93
|
+
klass = test_class
|
94
|
+
Interrobang.bangify_method klass, :false? do |predicate_method|
|
95
|
+
raise SomeError, "#{predicate_method} isn't true"
|
96
|
+
end
|
97
|
+
err = -> { klass.new.false! }.must_raise SomeError
|
98
|
+
assert_equal err.message, "false? isn't true"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '.bangify' do
|
105
|
+
it "converts all predicate? methods by default" do
|
106
|
+
klass = test_class
|
107
|
+
Interrobang.bangify klass
|
108
|
+
assert klass.new.true!
|
109
|
+
assert klass.new.veritable!
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns an array of symbols of the bangified methods" do
|
113
|
+
klass = test_class
|
114
|
+
assert_equal Interrobang.bangify(klass), [:true!, :veritable!, :false!, :with_argument!]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "converts all methods according to the provided prefix and suffix" do
|
118
|
+
klass = test_class
|
119
|
+
Interrobang.bangify klass, prefix: 'prefix_', suffix: '_suffix'
|
120
|
+
assert klass.new.prefix_true_suffix!
|
121
|
+
assert klass.new.prefix_veritable_suffix!
|
122
|
+
end
|
123
|
+
|
124
|
+
it "converts all methods that match the provided pattern" do
|
125
|
+
klass = test_class
|
126
|
+
Interrobang.bangify klass, matching: %r{\Aso_.*\z}
|
127
|
+
assert klass.new.so_true!
|
128
|
+
assert klass.new.so_very_true!
|
129
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
130
|
+
end
|
131
|
+
|
132
|
+
it "converts all methods that match the provided pattern respected except" do
|
133
|
+
klass = test_class
|
134
|
+
Interrobang.bangify klass, matching: %r{\Aso_.*\z}, except: [:so_very_true]
|
135
|
+
assert klass.new.so_true!
|
136
|
+
-> { klass.new.so_very_true! }.must_raise NoMethodError
|
137
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
138
|
+
end
|
139
|
+
|
140
|
+
it "except option accepts a singular symbol" do
|
141
|
+
klass = test_class
|
142
|
+
Interrobang.bangify klass, matching: %r{\Aso_.*\z}, except: :so_very_true
|
143
|
+
assert klass.new.so_true!
|
144
|
+
-> { klass.new.so_very_true! }.must_raise NoMethodError
|
145
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
146
|
+
end
|
147
|
+
|
148
|
+
it "converts only the methods specified in the only option" do
|
149
|
+
klass = test_class
|
150
|
+
Interrobang.bangify klass, only: [:so_true]
|
151
|
+
assert klass.new.so_true!
|
152
|
+
-> { klass.new.so_very_true! }.must_raise NoMethodError
|
153
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
154
|
+
end
|
155
|
+
|
156
|
+
it "except option accepts a singular symbol" do
|
157
|
+
klass = test_class
|
158
|
+
Interrobang.bangify klass, only: :so_true
|
159
|
+
assert klass.new.so_true!
|
160
|
+
-> { klass.new.so_very_true! }.must_raise NoMethodError
|
161
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
162
|
+
end
|
163
|
+
|
164
|
+
it "converts only the methods specified in the only option with a block" do
|
165
|
+
klass = test_class
|
166
|
+
Interrobang.bangify klass, only: [:so_false] do
|
167
|
+
raise SomeError
|
168
|
+
end
|
169
|
+
-> { klass.new.so_false! }.must_raise SomeError
|
170
|
+
-> { klass.new.so_true! }.must_raise NoMethodError
|
171
|
+
-> { klass.new.true! }.must_raise NoMethodError
|
172
|
+
end
|
173
|
+
|
174
|
+
it "performs the provided block for the bang method" do
|
175
|
+
klass = test_class
|
176
|
+
Interrobang.bangify klass do
|
177
|
+
raise SomeError
|
178
|
+
end
|
179
|
+
assert klass.new.true!
|
180
|
+
-> { klass.new.false! }.must_raise SomeError
|
181
|
+
end
|
182
|
+
|
183
|
+
it "converts super methods when specified" do
|
184
|
+
klass = test_class
|
185
|
+
Interrobang.bangify klass, include_super: true, prefix: 'ensure_'
|
186
|
+
-> { klass.new.ensure_nil! }.must_raise Interrobang::FalsePredicate
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: interrobang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Faraz Yashar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.5'
|
55
|
+
description: |-
|
56
|
+
Convert your predicate_methods? into bang_methods! without
|
57
|
+
abusing method_missing.
|
58
|
+
email:
|
59
|
+
- faraz.yashar@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- ".travis.yml"
|
66
|
+
- ".yardopts"
|
67
|
+
- Gemfile
|
68
|
+
- LICENSE.txt
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- interrobang.gemspec
|
72
|
+
- lib/interrobang.rb
|
73
|
+
- lib/interrobang/version.rb
|
74
|
+
- test/lib/predicate_bang_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
homepage: https://github.com/fny/interrobang
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.0.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.6
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Convert your predicate_methods? into bang_methods! without abusing method_missing
|
100
|
+
test_files:
|
101
|
+
- test/lib/predicate_bang_test.rb
|
102
|
+
- test/test_helper.rb
|
103
|
+
has_rdoc:
|