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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 1.9.3
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
+ [![Build Status](https://travis-ci.org/crowdcompass/toe_tag.svg?branch=master)](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
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ module ToeTag
2
+ VERSION = "1.0.1"
3
+ end
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: