toe_tag 1.0.1
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 +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +13 -0
- data/README.md +76 -0
- data/Rakefile +7 -0
- data/lib/toe_tag/version.rb +3 -0
- data/lib/toe_tag.rb +157 -0
- data/spec/toe_tag_spec.rb +135 -0
- data/toe_tag.gemspec +24 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1ae4f2a77f06e19c9bf9aeebe69c27e70ea8aa65
|
4
|
+
data.tar.gz: 9f30f1da5507beed2d04a2b1e57091843f3ab443
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 68e0d402a1ab16aa5c5d7e167f55b822947e82d9a91894c15433a1c74228a841a667b84e2e05e455d93664f9705bb5b92f274f56d511e648526bde96fc398830
|
7
|
+
data.tar.gz: 0833f0010a6ccbd1398f2f19b65011746272dfd3e2de6ad10ce2c561254c2e0fd8ffe198578fbc04e01669bb17f0315faba26afd1569d80f25c5051815585747
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014 CrowdCompass
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
[](https://travis-ci.org/crowdcompass/toe_tag)
|
2
|
+
|
3
|
+
# `toe_tag`
|
4
|
+
|
5
|
+
Ruby's default exception handling is strictly based on module and class inheritance. `toe_tag` provides
|
6
|
+
additional utilities for creating your own exception groups, narrowing exceptions based on message, and
|
7
|
+
executing arbitrary blocks to determine whether an exception should be caught -- all within the familiar
|
8
|
+
rescue syntax.
|
9
|
+
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
require 'toe_tag'
|
13
|
+
|
14
|
+
# Exception grouping:
|
15
|
+
# Note that not all of the constants have to be defined. Missing ones will be ignored.
|
16
|
+
DatabaseError = ToeTag.category %w[ActiveRecord::JDBCError PG::Error ActiveRecord::StatementInvalid]
|
17
|
+
|
18
|
+
begin
|
19
|
+
leaky_database_call
|
20
|
+
rescue DatabaseError => err
|
21
|
+
# err could be any of the listed classes
|
22
|
+
end
|
23
|
+
|
24
|
+
# Filtering by message:
|
25
|
+
SpuriousError = DatabaseError.with_message(/spurious|pointless|meaningless/)
|
26
|
+
|
27
|
+
begin
|
28
|
+
boring_database_call # ! raises PG::Error, "something spurious happened"
|
29
|
+
rescue SpuriousError
|
30
|
+
log "something spurious happened, ignore it"
|
31
|
+
rescue DatabaseError
|
32
|
+
log "watch out, something bad happened"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Filtering by proc
|
36
|
+
class FancyError
|
37
|
+
attr_reader :error_code
|
38
|
+
|
39
|
+
def initialize(message, error_code)
|
40
|
+
super message
|
41
|
+
@error_code = error_code
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
FancierError = FancyError.with_proc{|e| e.error_code = 123 }
|
46
|
+
|
47
|
+
begin
|
48
|
+
fancy_api_call
|
49
|
+
rescue FancierError
|
50
|
+
log "it's one of those 123 errors again"
|
51
|
+
rescue FancyError
|
52
|
+
log "some unknown error, reraising"
|
53
|
+
raise
|
54
|
+
end
|
55
|
+
|
56
|
+
## Installation
|
57
|
+
|
58
|
+
Add this line to your application's Gemfile:
|
59
|
+
|
60
|
+
gem 'toe_tag'
|
61
|
+
|
62
|
+
And then execute:
|
63
|
+
|
64
|
+
$ bundle
|
65
|
+
|
66
|
+
Or install it yourself as:
|
67
|
+
|
68
|
+
$ gem install toe_tag
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
1. Fork it.
|
73
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
74
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
75
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
76
|
+
5. Submit a pull request.
|
data/Rakefile
ADDED
data/lib/toe_tag.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require "toe_tag/version"
|
2
|
+
|
3
|
+
module ToeTag
|
4
|
+
|
5
|
+
module Util
|
6
|
+
def self.try_constantize(name)
|
7
|
+
if RUBY_VERSION >= "2.0.0"
|
8
|
+
Object.const_get name
|
9
|
+
else
|
10
|
+
eval name
|
11
|
+
end
|
12
|
+
rescue NameError
|
13
|
+
# const doesn't exist, just return nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ExceptionBehavior
|
18
|
+
def with_message(message)
|
19
|
+
MessageSpec.new(message, self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ExceptionSpec < Module
|
24
|
+
include ExceptionBehavior
|
25
|
+
end
|
26
|
+
|
27
|
+
# Aggregates multiple exception classes into one single logical type. Intended
|
28
|
+
# to be used in rescue clauses, for cases where different underlying implementations
|
29
|
+
# may bubble up different exceptions meaning the same thing.
|
30
|
+
#
|
31
|
+
# The recommended usage is to assign these to constants and treat them as a sort of
|
32
|
+
# meta-exception. This may be useful in combination with ExceptionMessageCatcher.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# ErrorA = Class.new(StandardError)
|
36
|
+
# ErrorB = Class.new(StandardError)
|
37
|
+
# ErrorC = Class.new(StandardError)
|
38
|
+
#
|
39
|
+
# ErrorsABC = ToeTag.category ErrorA, ErrorB, ErrorC
|
40
|
+
#
|
41
|
+
# begin
|
42
|
+
# # some stuff
|
43
|
+
# rescue ErrorsABC => err
|
44
|
+
# # err is either ErrorA, ErrorB, or ErrorC
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
class CategorySpec < ExceptionSpec
|
48
|
+
attr_reader :exceptions
|
49
|
+
|
50
|
+
def initialize(*exceptions)
|
51
|
+
self.exceptions = exceptions.flatten.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
def ===(except)
|
55
|
+
exceptions.any?{|exc| exc === except }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Accepts a list of exception classes or names of exception classes and returns
|
59
|
+
# an ExceptionCategory covering those exception classes. If a name is provided,
|
60
|
+
# it is converted to a constant. If the constant doesn't exist, the name is ignored.
|
61
|
+
# This allows the creation of an ExceptionCategory covering multiple exception
|
62
|
+
# types that may not all be loaded in a given environment.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# DatabaseError = ExceptionCategory.category %w[ActiveRecord::JDBCError PG::Error]
|
66
|
+
def self.category(*names)
|
67
|
+
names = names.flatten.map{|except_name|
|
68
|
+
if except_name.kind_of?(String)
|
69
|
+
Util.try_constantize(except_name)
|
70
|
+
else
|
71
|
+
except_name
|
72
|
+
end
|
73
|
+
}.compact
|
74
|
+
new(*names)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
attr_writer :exceptions
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.category(*names)
|
83
|
+
CategorySpec.category(*names)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Wraps a proc to allow arbitrary logic when matching an exception.
|
87
|
+
class ProcSpec < ExceptionSpec
|
88
|
+
|
89
|
+
def initialize(blk, exception = StandardError)
|
90
|
+
self.exception = exception
|
91
|
+
self.block = blk
|
92
|
+
end
|
93
|
+
|
94
|
+
def ===(except)
|
95
|
+
exception === except && block[except]
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
attr_accessor :exception, :block
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.with_proc(&blk)
|
104
|
+
ProcSpec.new(&blk)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Wraps an exception class to allow matching against the message, too. Intended
|
108
|
+
# to be used in rescue clauses, for cases where one exception class
|
109
|
+
# (ActiveRecord::StatementInvalid, I'm looking at you) represents a host of
|
110
|
+
# underlying issues.
|
111
|
+
#
|
112
|
+
# The recommended usage is to assign these to constants and treat them as a sort
|
113
|
+
# of meta-exception. This may be useful in combination with ExceptionCategory.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# BogusError = StandardError.with_message(/bogus/)
|
117
|
+
#
|
118
|
+
# begin
|
119
|
+
# raise "bogus error man"
|
120
|
+
# rescue BogusError => err
|
121
|
+
# p err
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
class MessageSpec < ExceptionSpec
|
125
|
+
|
126
|
+
def initialize(message, exception = StandardError)
|
127
|
+
self.exception = exception
|
128
|
+
self.message = message
|
129
|
+
end
|
130
|
+
|
131
|
+
def ===(except)
|
132
|
+
exception === except && message === except.message
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
attr_accessor :exception, :message
|
138
|
+
|
139
|
+
def message=(val)
|
140
|
+
if val.kind_of?(String)
|
141
|
+
@message = /#{val}/
|
142
|
+
else
|
143
|
+
@message = val
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.with_message(message)
|
150
|
+
MessageSpec.new(message)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
class Exception
|
156
|
+
extend ToeTag::ExceptionBehavior
|
157
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'toe_tag'
|
2
|
+
|
3
|
+
describe ToeTag do
|
4
|
+
|
5
|
+
let(:error_a) { Class.new StandardError }
|
6
|
+
let(:error_b) { Class.new StandardError }
|
7
|
+
let(:error_c) { Class.new StandardError }
|
8
|
+
let(:error_oddball) { Class.new StandardError }
|
9
|
+
|
10
|
+
let(:error_group) { ToeTag::CategorySpec.new(error_a, error_b, error_c) }
|
11
|
+
|
12
|
+
describe ToeTag::CategorySpec do
|
13
|
+
|
14
|
+
it "should capture exceptions from any class it is composed from" do
|
15
|
+
expect {
|
16
|
+
begin
|
17
|
+
raise error_a
|
18
|
+
rescue error_group
|
19
|
+
end
|
20
|
+
begin
|
21
|
+
raise error_b
|
22
|
+
rescue error_group
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
raise error_c
|
26
|
+
rescue error_group
|
27
|
+
end
|
28
|
+
}.not_to raise_error
|
29
|
+
expect {
|
30
|
+
begin
|
31
|
+
raise error_oddball
|
32
|
+
rescue error_group
|
33
|
+
end
|
34
|
+
}.to raise_error(error_oddball)
|
35
|
+
end
|
36
|
+
|
37
|
+
context ".category" do
|
38
|
+
|
39
|
+
it "should look up exception types by name, skipping nonexistent ones" do
|
40
|
+
grouping = ToeTag.category %w[IndexError StandardError BogusError]
|
41
|
+
expect(grouping.exceptions.length).to eql 2
|
42
|
+
expect(grouping === NameError.new).to eql true
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ToeTag::ProcSpec do
|
50
|
+
|
51
|
+
it "should capture exceptions that return true from a given proc" do
|
52
|
+
catcher = ToeTag::ProcSpec.new(lambda{|e| e.message == "not spurious" })
|
53
|
+
expect {
|
54
|
+
begin
|
55
|
+
raise error_a, "not spurious"
|
56
|
+
rescue catcher
|
57
|
+
end
|
58
|
+
}.not_to raise_error
|
59
|
+
expect {
|
60
|
+
begin
|
61
|
+
raise error_a, "totally spurious"
|
62
|
+
rescue catcher
|
63
|
+
end
|
64
|
+
}.to raise_error(error_a)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ToeTag::MessageSpec do
|
70
|
+
|
71
|
+
it "should capture exceptions with a given message substring" do
|
72
|
+
catcher = ToeTag::MessageSpec.new("spurious")
|
73
|
+
expect {
|
74
|
+
begin
|
75
|
+
raise error_a, "a spurious error"
|
76
|
+
rescue catcher
|
77
|
+
end
|
78
|
+
}.not_to raise_error
|
79
|
+
expect {
|
80
|
+
begin
|
81
|
+
raise error_a, "a serious error"
|
82
|
+
rescue catcher
|
83
|
+
end
|
84
|
+
}.to raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
context "combined with ExceptionCategory" do
|
88
|
+
|
89
|
+
it "should capture exceptions within a set with a given message substring" do
|
90
|
+
catcher = ToeTag.category(error_group).with_message("spurious")
|
91
|
+
expect {
|
92
|
+
begin
|
93
|
+
raise error_a, "a spurious error"
|
94
|
+
rescue catcher
|
95
|
+
end
|
96
|
+
}.not_to raise_error
|
97
|
+
expect {
|
98
|
+
begin
|
99
|
+
raise error_oddball, "a spurious error, outside the group"
|
100
|
+
rescue catcher
|
101
|
+
end
|
102
|
+
}.to raise_error(error_oddball)
|
103
|
+
expect {
|
104
|
+
begin
|
105
|
+
raise error_b, "a serious error"
|
106
|
+
rescue catcher
|
107
|
+
end
|
108
|
+
}.to raise_error(error_b)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
context "using the Exception extensions" do
|
114
|
+
|
115
|
+
it "should catch errors by message" do
|
116
|
+
catcher = StandardError.with_message(/catch|retrieve|fetch/)
|
117
|
+
expect {
|
118
|
+
begin
|
119
|
+
raise "catch me if you can"
|
120
|
+
rescue catcher
|
121
|
+
end
|
122
|
+
}.not_to raise_error
|
123
|
+
expect {
|
124
|
+
begin
|
125
|
+
raise "fail"
|
126
|
+
rescue catcher
|
127
|
+
end
|
128
|
+
}.to raise_error(StandardError)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
data/toe_tag.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'toe_tag/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "toe_tag"
|
8
|
+
spec.version = ToeTag::VERSION
|
9
|
+
spec.authors = ["Matthew Boeh"]
|
10
|
+
spec.email = ["matt@crowdcompass.com"]
|
11
|
+
spec.description = %q{Utilities for catching and handling exceptions.}
|
12
|
+
spec.summary = %q{Utilities for catching and handling exceptions.}
|
13
|
+
spec.homepage = "https://github.com/crowdcompass/toetag"
|
14
|
+
spec.license = "Apache-2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: toe_tag
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew Boeh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-19 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.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.0.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.0.0
|
55
|
+
description: Utilities for catching and handling exceptions.
|
56
|
+
email:
|
57
|
+
- matt@crowdcompass.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .travis.yml
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/toe_tag.rb
|
69
|
+
- lib/toe_tag/version.rb
|
70
|
+
- spec/toe_tag_spec.rb
|
71
|
+
- toe_tag.gemspec
|
72
|
+
homepage: https://github.com/crowdcompass/toetag
|
73
|
+
licenses:
|
74
|
+
- Apache-2.0
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.0.0.rc.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Utilities for catching and handling exceptions.
|
96
|
+
test_files:
|
97
|
+
- spec/toe_tag_spec.rb
|
98
|
+
has_rdoc:
|