jeanine 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9120ffa8994858a7560330fa0afbe1bf18e2f94
4
- data.tar.gz: fd326e260cc8c90a5b70b8b8ad4a1cd5b0b9869a
3
+ metadata.gz: d3df31123e22ecf9f1dd465af168aa34b3199e75
4
+ data.tar.gz: 9fb4883db99cbf0a30b4895064bcb10f00896c6c
5
5
  SHA512:
6
- metadata.gz: 8b11b5b73abed4a7df44caf5a7b53ad6d2d52616fc23658e8c0d864239c0dc7b3fdfb6837c6d611f593944ad2518d92cc9af945ec3185a7bdfd783f913c3309c
7
- data.tar.gz: 2ca9b7d2196c08e57bda6f983cfb0a1da71c06772bb6b42b76deb0aa941d5cb454eef4041d9834f8b3eeea9644d5a86b22e48de5f6b951a32e39481d9ac631a4
6
+ metadata.gz: e871411c10b283ab9ad26c5b568870e651dc25110cb1e5ea842e607dfe1d3d28dfac640bb574cfe19ee0bb01575668e1906fdbd2c9d5304e0c85a75977d3bfd4
7
+ data.tar.gz: 214f87e732bbfa864f5b64574c27b4a284c260c7447dc867af01a94da52edac49308d427da84c19846b8c540a2309ab3d81117b99b6f4330df04b194b3b5c65a
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/jeanine/cli'
4
+
5
+ Jeanine::CLI.new(ARGV).execute
@@ -1,15 +1,14 @@
1
+ require 'rack'
2
+
1
3
  require 'forwardable'
2
4
  require 'logger'
3
5
 
4
6
  require "jeanine/version"
5
7
  require 'jeanine/core_ext'
6
- require 'jeanine/router'
7
- require 'jeanine/app'
8
- require 'tilt'
9
8
 
10
9
  module Jeanine
11
- def self.tilt_cache
12
- @title_cache ||= Tilt::Cache.new
10
+ def self._installed_plugins
11
+ @_installed_plugins ||= []
13
12
  end
14
13
 
15
14
  def self.logger
@@ -23,4 +22,7 @@ module Jeanine
23
22
  def self.router
24
23
  @router ||= Router.new
25
24
  end
25
+
26
+ autoload :Router, 'jeanine/router'
27
+ autoload :App, 'jeanine/app'
26
28
  end
@@ -1,22 +1,36 @@
1
- require 'jeanine/callbacks'
2
1
  require 'jeanine/mimes'
3
2
  require 'jeanine/request'
4
- require 'jeanine/rescuing'
5
3
  require 'jeanine/response'
6
- require 'jeanine/renderer'
7
4
  require 'jeanine/routing'
8
- require 'jeanine/session'
9
5
 
10
6
  module Jeanine
11
7
  class App
12
- include Session
13
8
  include Routing::Evaluation
9
+
14
10
  attr_reader :request, :response
15
11
 
12
+ def self.plugin(name)
13
+ unless Jeanine._installed_plugins.include?(name)
14
+ unless const_defined?("Jeanine::#{name}")
15
+ require "jeanine/#{name.to_s.underscore}"
16
+ end
17
+ include Kernel.const_get("Jeanine::#{name}")
18
+ Jeanine._installed_plugins << name
19
+ end
20
+ end
21
+
22
+ def self.installed_plugins
23
+ Jeanine._installed_plugins
24
+ end
25
+
26
+ # plugin :Callbacks
27
+ # plugin :Rendering
28
+ # plugin :Rescuing
29
+ # plugin :Session
30
+
16
31
  class << self
17
- include Callbacks
18
32
  include Routing::DSL
19
- include Rescuing
33
+
20
34
  alias :_new :new
21
35
  def new(*args, &block)
22
36
  initialize!
@@ -41,24 +55,11 @@ module Jeanine
41
55
  end
42
56
  end
43
57
 
44
- include Rescuing
45
-
46
58
  def call(env)
47
- begin
48
- @env = env
49
- @request = Jeanine::Request.new(env)
50
- @response = Jeanine::Response.new
51
- catch(:halt) { route_eval }
52
- rescue => error
53
- handler = self.class.rescue_handlers[error.class]
54
- raise error unless handler
55
- if handler.is_a?(Symbol)
56
- @response.write(send(handler, error))
57
- else
58
- @response.write(instance_exec(error, &handler))
59
- end
60
- @response.complete!
61
- end
59
+ @env = env
60
+ @request = Jeanine::Request.new(env)
61
+ @response = Jeanine::Response.new
62
+ catch(:halt) { route_eval }
62
63
  end
63
64
 
64
65
  private
@@ -71,11 +72,6 @@ module Jeanine
71
72
  end
72
73
  end
73
74
 
74
- def render(*args)
75
- @response.action_variables = instance_variables_cache
76
- Renderer.new(@response).render(*args)
77
- end
78
-
79
75
  def instance_variables_cache
80
76
  instance_variables.each_with_object({}) do |var, obj|
81
77
  obj[var] = instance_variable_get(var)
@@ -1,28 +1,106 @@
1
1
  module Jeanine
2
2
  module Callbacks
3
- def _callbacks
4
- @_callbacks ||= {
5
- before: [],
6
- after: [],
7
- before_all: [],
8
- after_all: []
9
- }
3
+ def self.included(klass)
4
+ klass.extend InstanceMethods
5
+ klass.prepend EvaluationMethods
10
6
  end
11
7
 
12
- def _register_callback(type, paths = [], &block)
13
- if paths == []
14
- _callbacks["#{type}_all".to_sym] << { block: block }
15
- else
16
- _callbacks[type] << { paths: paths, block: block }
8
+ module InstanceMethods
9
+ def _callbacks
10
+ @_callbacks ||= {
11
+ before: [],
12
+ after: [],
13
+ before_all: [],
14
+ after_all: []
15
+ }
16
+ end
17
+
18
+ def before(*paths, &block)
19
+ _register_callback(:before, paths, &block)
17
20
  end
18
- end
19
21
 
20
- def before(*paths, &block)
21
- _register_callback(:before, paths, &block)
22
+ def after(*paths, &block)
23
+ _register_callback(:after, paths, &block)
24
+ end
25
+
26
+ private
27
+
28
+ def _register_callback(type, paths = [], &block)
29
+ if paths == []
30
+ _callbacks["#{type}_all".to_sym] << { block: block }
31
+ else
32
+ _callbacks[type] << { paths: paths, block: block }
33
+ end
34
+ end
22
35
  end
23
36
 
24
- def after(*paths, &block)
25
- _register_callback(:after, paths, &block)
37
+ module EvaluationMethods
38
+ def route_eval
39
+ before_find_route!
40
+ route = find_route
41
+
42
+ if route
43
+ before_evaluate_route!
44
+ result = instance_eval(&route[:block])
45
+ @response.write(result)
46
+ after_evaluate_route!
47
+ else
48
+ @response.status = 404
49
+ end
50
+ after_response!
51
+ @response.complete!
52
+ end
53
+
54
+ private
55
+
56
+ def before_find_route!
57
+ run_before_callbacks!(:before_all)
58
+ end
59
+
60
+ def before_evaluate_route!
61
+ run_before_callbacks!(:before)
62
+ end
63
+
64
+ def after_evaluate_route!
65
+ run_after_callbacks!(:after)
66
+ end
67
+
68
+ def after_response!
69
+ run_after_callbacks!(:after_all)
70
+ end
71
+
72
+ def run_before_callbacks!(type)
73
+ if type == :before_all
74
+ self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
75
+ else
76
+ matching_callbacks(type) do |callback|
77
+ eval_callback(&callback[:block])
78
+ end
79
+ end
80
+ end
81
+
82
+ def run_after_callbacks!(type)
83
+ if type == :after_all
84
+ self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
85
+ else
86
+ matching_callbacks(type) do |callback|
87
+ eval_callback(&callback[:block])
88
+ end
89
+ end
90
+ end
91
+
92
+ def matching_callbacks(type)
93
+ self.class._callbacks[type].select do |callback|
94
+ paths = callback[:paths]
95
+ if paths.detect { |path| path.match?(@request.path_info) }
96
+ yield callback
97
+ end
98
+ end
99
+ end
100
+
101
+ def eval_callback(*args, &callback)
102
+ instance_exec(*args, &callback)
103
+ end
26
104
  end
27
105
  end
28
106
  end
@@ -0,0 +1,32 @@
1
+ require 'fileutils'
2
+
3
+ module Jeanine
4
+ class CLI
5
+ def initialize(args)
6
+ @args = args
7
+ end
8
+
9
+ def execute
10
+ if @args[0] == 'new'
11
+ command_new!
12
+ else
13
+ $stdout.puts "I don't know how to `#{@args[0]}`. Maybe you meant `new`?"
14
+ end
15
+ end
16
+
17
+ def command_new!
18
+ FileUtils.mkdir("#{@args[1]}") unless Dir.exists?("#{@args[1]}")
19
+ FileUtils.mkdir("#{@args[1]}/config") unless Dir.exists?("#{@args[1]}/config")
20
+ relative_dir = "lib/jeanine/generator/new"
21
+ Dir.glob("lib/jeanine/generator/new/**/*.*").each do |file|
22
+ new_dir = file.gsub(relative_dir, "#{@args[1]}")[0...-3]
23
+ FileUtils.copy_file(file, new_dir)
24
+ end
25
+ relative_dir = "lib/jeanine/generator/new/config"
26
+ Dir.glob("lib/jeanine/generator/new/config/**/*.*").each do |file|
27
+ new_dir = file.gsub(relative_dir, "#{@args[1]}/config")[0...-3]
28
+ FileUtils.copy_file(file, new_dir)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -5,4 +5,19 @@ class Hash
5
5
  def reverse_merge!(other_hash)
6
6
  replace(reverse_merge(other_hash))
7
7
  end
8
+ def deep_transform_keys(&block)
9
+ result = {}
10
+ each do |key, value|
11
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
12
+ end
13
+ result
14
+ end
15
+
16
+ def deep_transform_keys!(&block)
17
+ keys.each do |key|
18
+ value = delete(key)
19
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
20
+ end
21
+ self
22
+ end
8
23
  end
@@ -3,4 +3,11 @@ class String
3
3
  return nil if self.empty?
4
4
  self
5
5
  end
6
+ def underscore
7
+ self.gsub(/::/, '/').
8
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
9
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
10
+ tr("-", "_").
11
+ downcase
12
+ end
6
13
  end
@@ -1,18 +1,16 @@
1
1
  module Broding
2
- module Environment
3
- def env
4
- @_env ||= (ENV["RACK_ENV"].presence || "development")
5
- end
2
+ def env
3
+ @_env ||= (ENV["RACK_ENV"].presence || "development")
4
+ end
6
5
 
7
- def groups(*groups)
8
- hash = groups.extract_options!
9
- env = Jeanine.env
10
- groups.unshift(:default, env)
11
- groups.concat ENV["BRODY_GROUPS"].to_s.split(",")
12
- groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
13
- groups.compact!
14
- groups.uniq!
15
- groups
16
- end
6
+ def groups(*groups)
7
+ hash = groups.extract_options!
8
+ env = Jeanine.env
9
+ groups.unshift(:default, env)
10
+ groups.concat ENV["BRODY_GROUPS"].to_s.split(",")
11
+ groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
12
+ groups.compact!
13
+ groups.uniq!
14
+ groups
17
15
  end
18
16
  end
@@ -1,9 +1,15 @@
1
1
  module Jeanine
2
2
  class Mimes
3
3
  MimeTypeNotFound = Class.new(StandardError)
4
- MIME_TYPES_INVERTED = Rack::Mime::MIME_TYPES.invert
4
+ MIME_TYPES_INVERTED = ::Rack::Mime::MIME_TYPES.invert
5
+
6
+ def self.loaded?
7
+ @loaded
8
+ end
5
9
 
6
10
  def self.load!
11
+ return if loaded?
12
+ @loaded = true
7
13
  register(:json, Rack::Mime::MIME_TYPES[".json"])
8
14
  register(:html, Rack::Mime::MIME_TYPES[".html"])
9
15
  register(:text, Rack::Mime::MIME_TYPES[".text"])
@@ -1,6 +1,15 @@
1
1
  require 'jeanine/view'
2
+ require 'jeanine/view_paths'
2
3
 
3
4
  module Jeanine
5
+ def self.view_paths
6
+ @_view_paths ||= Set.new(["views"])
7
+ end
8
+
9
+ def self.tilt_cache
10
+ @title_cache ||= Tilt::Cache.new
11
+ end
12
+
4
13
  class Renderer
5
14
  def self._renderers
6
15
  @_renderers ||= Set.new
@@ -81,6 +90,16 @@ module Jeanine
81
90
  Thread.current[:tilt_cache] ||= Tilt::Cache.new
82
91
  end
83
92
 
93
+ def find_template!(template)
94
+ Jeanine.view_paths.to_a.each do |path|
95
+ template_with_path = "#{path}/#{template}"
96
+ if File.exist?(template_with_path)
97
+ return template_with_path
98
+ end
99
+ end
100
+ raise "Template not found in view paths. Looking for #{template} in #{Jeanine.view_paths.to_a.join(', ')}"
101
+ end
102
+
84
103
  def html(engine, template_name, options = {}, locals = {}, &block)
85
104
  locals = options.delete(:locals) || locals || {}
86
105
  layout = options[:layout]
@@ -94,6 +113,7 @@ module Jeanine
94
113
  unless layout.include?("layouts/")
95
114
  layout = "layouts/#{layout}"
96
115
  end
116
+ layout = find_template!(layout)
97
117
  options = options.merge(layout: false, scope: scope)
98
118
  catch(:layout_missing) { return html(engine, layout, options, locals) { output } }
99
119
  end
@@ -104,7 +124,8 @@ module Jeanine
104
124
  Tilt::Cache.new.fetch engine, template_name, options do
105
125
  template = Tilt[engine]
106
126
  raise "Template engine not found: #{engine}" if template.nil?
107
- template.new("views/#{template_name}", options)
127
+ template_name = find_template!(template_name)
128
+ template.new(template_name, options)
108
129
  end
109
130
  end
110
131
 
@@ -127,4 +148,21 @@ module Jeanine
127
148
  text
128
149
  end
129
150
  end
151
+
152
+ module Rendering
153
+ def self.included(klass)
154
+ klass.extend ClassMethods
155
+ end
156
+
157
+ def render(*args)
158
+ @response.action_variables = instance_variables_cache
159
+ Renderer.new(@response).render(*args)
160
+ end
161
+
162
+ module ClassMethods
163
+ def append_view_path(path)
164
+ Jeanine.view_paths << path
165
+ end
166
+ end
167
+ end
130
168
  end
@@ -1,5 +1,4 @@
1
1
  require 'rack/request'
2
- require 'jeanine/headers'
3
2
 
4
3
  module Jeanine
5
4
  class Request < Rack::Request
@@ -17,8 +16,8 @@ module Jeanine
17
16
  super
18
17
  end
19
18
 
20
- def headers
21
- @headers ||= Jeanine::Headers.new(self)
19
+ def params
20
+ super.deep_transform_keys!(&:to_sym)
22
21
  end
23
22
 
24
23
  def post?
@@ -0,0 +1,41 @@
1
+ module Jeanine
2
+ module Rescuable
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.prepend InstanceMethods
6
+ end
7
+
8
+ module InstanceMethods
9
+ def call(env)
10
+ begin
11
+ super
12
+ rescue => error
13
+ handler = self.class.rescue_handlers[error.class.to_s]
14
+ raise error unless handler
15
+ if handler.is_a?(Symbol)
16
+ @response.write(send(handler, error))
17
+ else
18
+ @response.write(instance_exec(error, &handler))
19
+ end
20
+ @response.complete!
21
+ end
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def rescue_from(*exceptions, with: nil, &block)
27
+ exceptions.each do |exception|
28
+ if with
29
+ rescue_handlers[exception.to_s] = with
30
+ else
31
+ rescue_handlers[exception.to_s] = block
32
+ end
33
+ end
34
+ end
35
+
36
+ def rescue_handlers
37
+ @rescue_handlers ||= {}
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,20 +1,14 @@
1
1
  module Jeanine
2
2
  module Routing
3
3
  module Evaluation
4
- IGNORED_INSTANCE_VARIABLES = [:@env]
5
4
  def route_eval
6
- before_find_route!
7
5
  route = find_route
8
-
9
6
  if route
10
- before_evaluate_route!
11
7
  result = instance_eval(&route[:block])
12
8
  @response.write(result)
13
- after_evaluate_route!
14
9
  else
15
10
  @response.status = 404
16
11
  end
17
- after_response!
18
12
  @response.complete!
19
13
  end
20
14
 
@@ -34,7 +28,7 @@ module Jeanine
34
28
  while index < matches.captures.size
35
29
  param = route[:params][index]
36
30
  @request.params[param] = matches.captures[index]
37
- if index == match.captures.size
31
+ if index == matches.captures.size
38
32
  @request.params[param].gsub!(@request.format)
39
33
  end
40
34
  index += 1
@@ -43,54 +37,6 @@ module Jeanine
43
37
  route
44
38
  end
45
39
 
46
- def before_find_route!
47
- run_before_callbacks!(:before_all)
48
- end
49
-
50
- def before_evaluate_route!
51
- run_before_callbacks!(:before)
52
- end
53
-
54
- def after_evaluate_route!
55
- run_after_callbacks!(:after)
56
- end
57
-
58
- def after_response!
59
- run_after_callbacks!(:after_all)
60
- end
61
-
62
- def run_before_callbacks!(type)
63
- if type == :before_all
64
- self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
65
- else
66
- matching_callbacks(type) do |callback|
67
- eval_callback(&callback[:block])
68
- end
69
- end
70
- end
71
-
72
- def run_after_callbacks!(type)
73
- if type == :after_all
74
- self.class._callbacks[type].each { |callback| eval_callback(&callback[:block]) }
75
- else
76
- matching_callbacks(type) do |callback|
77
- eval_callback(&callback[:block])
78
- end
79
- end
80
- end
81
-
82
- def matching_callbacks(type)
83
- self.class._callbacks[type].select do |callback|
84
- paths = callback[:paths]
85
- if paths.detect { |path| path.match?(@request.path_info) }
86
- yield callback
87
- end
88
- end
89
- end
90
-
91
- def eval_callback(*args, &callback)
92
- instance_exec(*args, &callback)
93
- end
94
40
  end
95
41
  end
96
42
  end
@@ -1,3 +1,3 @@
1
1
  module Jeanine
2
- VERSION = "0.2.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -0,0 +1,8 @@
1
+ require 'tilt'
2
+
3
+ module Jeanine
4
+
5
+ module ViewPaths
6
+
7
+ end
8
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeanine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Brody
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-07 00:00:00.000000000 Z
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -66,16 +66,60 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- description: A framework.
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
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'
111
+ description: A lightning-fast, batteries-included Ruby web micro-framework.
70
112
  email:
71
113
  - git@josh.mn
72
114
  executables: []
73
115
  extensions: []
74
116
  extra_rdoc_files: []
75
117
  files:
118
+ - exe/jeanine
76
119
  - lib/jeanine.rb
77
120
  - lib/jeanine/app.rb
78
121
  - lib/jeanine/callbacks.rb
122
+ - lib/jeanine/cli.rb
79
123
  - lib/jeanine/core_ext.rb
80
124
  - lib/jeanine/core_ext/array.rb
81
125
  - lib/jeanine/core_ext/hash.rb
@@ -85,9 +129,9 @@ files:
85
129
  - lib/jeanine/headers.rb
86
130
  - lib/jeanine/mimes.rb
87
131
  - lib/jeanine/path_proxy.rb
88
- - lib/jeanine/renderer.rb
132
+ - lib/jeanine/rendering.rb
89
133
  - lib/jeanine/request.rb
90
- - lib/jeanine/rescuing.rb
134
+ - lib/jeanine/rescuable.rb
91
135
  - lib/jeanine/response.rb
92
136
  - lib/jeanine/router.rb
93
137
  - lib/jeanine/routing.rb
@@ -96,6 +140,7 @@ files:
96
140
  - lib/jeanine/session.rb
97
141
  - lib/jeanine/version.rb
98
142
  - lib/jeanine/view.rb
143
+ - lib/jeanine/view_paths.rb
99
144
  homepage: https://github.com/joshmn/jeanine
100
145
  licenses:
101
146
  - MIT
@@ -122,5 +167,5 @@ rubyforge_project:
122
167
  rubygems_version: 2.6.14.3
123
168
  signing_key:
124
169
  specification_version: 4
125
- summary: A framework.
170
+ summary: A lightning-fast, batteries-included Ruby web micro-framework.
126
171
  test_files: []
@@ -1,17 +0,0 @@
1
- module Jeanine
2
- module Rescuing
3
- def rescue_from(*exceptions, with: nil, &block)
4
- exceptions.each do |exception|
5
- if with
6
- rescue_handlers[exception] = with
7
- else
8
- rescue_handlers[exception] = block
9
- end
10
- end
11
- end
12
-
13
- def rescue_handlers
14
- @rescue_handlers ||= {}
15
- end
16
- end
17
- end