white_noise 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 +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +11 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +127 -0
- data/LICENSE +202 -0
- data/README.md +185 -0
- data/Rakefile +7 -0
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/lib/noise/bugsnag_middleware.rb +31 -0
- data/lib/noise/error_serializer.rb +67 -0
- data/lib/noise/exception_renderer.rb +64 -0
- data/lib/noise/exception_responder.rb +56 -0
- data/lib/noise/exceptions_app.rb +27 -0
- data/lib/noise/notification.rb +92 -0
- data/lib/noise/public_error.rb +125 -0
- data/lib/noise/public_error_serializer.rb +16 -0
- data/lib/noise/railtie.rb +24 -0
- data/lib/noise/rate_limit_error.rb +26 -0
- data/lib/noise/rate_limit_error_responder.rb +15 -0
- data/lib/noise/version.rb +5 -0
- data/lib/noise.rb +20 -0
- data/lib/white_noise.rb +2 -0
- data/white_noise.gemspec +33 -0
- metadata +237 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Noise
|
|
3
|
+
# Determines how to render exception
|
|
4
|
+
#
|
|
5
|
+
# @example
|
|
6
|
+
# class ApplicationExceptionRenderer < ExceptionRenderer
|
|
7
|
+
# def render(responder)
|
|
8
|
+
# if env[Rack::PATH_INFO] =~ %r{\A/partners}
|
|
9
|
+
# {
|
|
10
|
+
# id: error_id,
|
|
11
|
+
# error: message,
|
|
12
|
+
# code: code
|
|
13
|
+
# }
|
|
14
|
+
# else
|
|
15
|
+
# super
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# def message
|
|
20
|
+
# if error.is_a?(PublicError)
|
|
21
|
+
# error.message
|
|
22
|
+
# else
|
|
23
|
+
# 'Internal Server Error'
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# def code
|
|
28
|
+
# if error.is_a?(PublicError)
|
|
29
|
+
# error.message_id
|
|
30
|
+
# else
|
|
31
|
+
# :internal_server_error
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
ExceptionRenderer = Struct.new(:env) do
|
|
37
|
+
# @param responder [ExceptionResponder]
|
|
38
|
+
# @return [String] error representation
|
|
39
|
+
def render(responder)
|
|
40
|
+
ActiveModel::SerializableResource.new(
|
|
41
|
+
Array(error),
|
|
42
|
+
each_serializer: error_serializer,
|
|
43
|
+
adapter: :json,
|
|
44
|
+
root: 'errors',
|
|
45
|
+
meta: { 'status' => responder.status_code },
|
|
46
|
+
scope: { http_status: responder.status_code, id: error_id },
|
|
47
|
+
).as_json.to_json
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def error_serializer
|
|
51
|
+
error.is_a?(PublicError) ? PublicErrorSerializer : ErrorSerializer
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [StandardError]
|
|
55
|
+
def error
|
|
56
|
+
env['action_dispatch.exception']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [String] error identifier, UUID
|
|
60
|
+
def error_id
|
|
61
|
+
env['action_dispatch.request_id']
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rack/utils'
|
|
3
|
+
require 'uber/inheritable_attr'
|
|
4
|
+
require 'action_dispatch'
|
|
5
|
+
require 'action_dispatch/middleware/exception_wrapper'
|
|
6
|
+
require 'active_support/core_ext/object/json'
|
|
7
|
+
require 'active_model_serializers'
|
|
8
|
+
|
|
9
|
+
module Noise
|
|
10
|
+
# Constructs error response (status, body)
|
|
11
|
+
class ExceptionResponder
|
|
12
|
+
class << self
|
|
13
|
+
# @param error [StandardError]
|
|
14
|
+
# @param status [Integer, Symbol] HTTP status to use for response
|
|
15
|
+
# @api private
|
|
16
|
+
#
|
|
17
|
+
def register(error, status:)
|
|
18
|
+
ActionDispatch::ExceptionWrapper.rescue_responses[error.to_s] = status
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param env [Hash] rack env
|
|
23
|
+
# @param exception_renderer [ExceptionRenderer]
|
|
24
|
+
def initialize(env, exception_renderer = Noise.config.exception_renderer.new(env))
|
|
25
|
+
@env = env
|
|
26
|
+
@exception_renderer = exception_renderer
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_reader :env, :exception_renderer
|
|
30
|
+
protected :env
|
|
31
|
+
|
|
32
|
+
# @return [Hash] JSON-serializable body
|
|
33
|
+
def body
|
|
34
|
+
@body ||= exception_renderer.render(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Hash] headers
|
|
38
|
+
def headers
|
|
39
|
+
{
|
|
40
|
+
'Content-Type' => "#{::Mime::JSON}; charset=#{ActionDispatch::Response.default_charset}",
|
|
41
|
+
'Content-Length' => body.bytesize.to_s,
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Integer] HTTP status code
|
|
46
|
+
def status_code
|
|
47
|
+
status_symbol = ActionDispatch::ExceptionWrapper.rescue_responses[error.class.name]
|
|
48
|
+
# calls `status_code` from Rack::Utils
|
|
49
|
+
Rack::Utils.status_code(status_symbol)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def error
|
|
53
|
+
env['action_dispatch.exception']
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative 'exception_responder'
|
|
3
|
+
|
|
4
|
+
module Noise
|
|
5
|
+
# Custom rails exception app to render all API level errors as JSON.
|
|
6
|
+
#
|
|
7
|
+
# Why it needed: in case we use default ActionController's `rescue_from`
|
|
8
|
+
# we will not be able to properly handle and render exceptions raised in middlewares (like Warden),
|
|
9
|
+
# so for processing of all possible exceptions we configure Rails' `config.exceptions_app`
|
|
10
|
+
# to use our own API-specific implementation.
|
|
11
|
+
#
|
|
12
|
+
class ExceptionsApp
|
|
13
|
+
# @param env [Hash] rack env
|
|
14
|
+
def call(env)
|
|
15
|
+
responder = build_responder(env)
|
|
16
|
+
[responder.status_code, responder.headers, [responder.body]]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def build_responder(env)
|
|
22
|
+
error = env['action_dispatch.exception']
|
|
23
|
+
responder_class = error.respond_to?(:responder_class) ? error.responder_class : ExceptionResponder
|
|
24
|
+
responder_class.new(env)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'active_support/core_ext/hash/transform_values'
|
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
|
4
|
+
require 'active_support/core_ext/hash/except'
|
|
5
|
+
require 'active_support/hash_with_indifferent_access'
|
|
6
|
+
require 'active_support/concern'
|
|
7
|
+
require 'action_dispatch/http/request'
|
|
8
|
+
|
|
9
|
+
module Noise
|
|
10
|
+
# Provides detailed information about exception
|
|
11
|
+
# and request context.
|
|
12
|
+
#
|
|
13
|
+
class Notification
|
|
14
|
+
WARNING = :warning
|
|
15
|
+
INFO = :info
|
|
16
|
+
ERROR = :error
|
|
17
|
+
SEVERITIES = [WARNING, INFO, ERROR].freeze
|
|
18
|
+
|
|
19
|
+
cattr_accessor :severities, instance_writer: false
|
|
20
|
+
self.severities = Hash.new(ERROR)
|
|
21
|
+
|
|
22
|
+
cattr_accessor :extractors, instance_writer: false
|
|
23
|
+
self.extractors = HashWithIndifferentAccess.new
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# @param error_class [Class<StandardError>, String]
|
|
27
|
+
# @param severity [Symbol] severity constant
|
|
28
|
+
def register(error_class, severity:)
|
|
29
|
+
if SEVERITIES.include?(severity)
|
|
30
|
+
severities[error_class.to_s] = severity
|
|
31
|
+
else
|
|
32
|
+
fail ArgumentError, "Wrong severity `#{severity}`, allowed: #{SEVERITIES}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Extract info from request and it to Bugsnag notification
|
|
37
|
+
# @param key [Symbol, String] name of the parameter
|
|
38
|
+
# @param extractor [#call]
|
|
39
|
+
# @return [void]
|
|
40
|
+
#
|
|
41
|
+
# class ApiClientExtractor
|
|
42
|
+
# def call(env)
|
|
43
|
+
# env.slice(:client_version, :client_id)
|
|
44
|
+
# end
|
|
45
|
+
# end
|
|
46
|
+
# Notification.extract(:api_client, ApiClientExtractor)
|
|
47
|
+
#
|
|
48
|
+
def extract(key, extractor)
|
|
49
|
+
extractors[key] = extractor
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param error [StandardError]
|
|
54
|
+
# @param env [Hash] rack env
|
|
55
|
+
# @see http://www.rubydoc.info/github/rack/rack/master/file/SPEC
|
|
56
|
+
#
|
|
57
|
+
def initialize(error, env)
|
|
58
|
+
@error = error
|
|
59
|
+
@env = env
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [{String, Any}]
|
|
63
|
+
def to_hash
|
|
64
|
+
extractors.except('user').transform_values do |extractor|
|
|
65
|
+
extractor.new.call(@env)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [String] Error severity
|
|
70
|
+
def severity
|
|
71
|
+
severities[@error.class.name].to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [Hash] User info
|
|
75
|
+
def user_info
|
|
76
|
+
user =
|
|
77
|
+
if extractors.key?('user')
|
|
78
|
+
extractors['user'].new.call(@env)
|
|
79
|
+
else
|
|
80
|
+
{}
|
|
81
|
+
end
|
|
82
|
+
fail '`name` key is reserved to identify error itself' if user.key?('name')
|
|
83
|
+
user.merge('name' => request.uuid)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def request
|
|
89
|
+
@request ||= ActionDispatch::Request.new(@env)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'noise/notification'
|
|
3
|
+
require 'noise/exception_responder'
|
|
4
|
+
require 'active_support/core_ext/object/blank'
|
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
|
6
|
+
require 'i18n'
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
module Noise
|
|
10
|
+
# Base class for all api level errors
|
|
11
|
+
#
|
|
12
|
+
class PublicError < StandardError
|
|
13
|
+
attr_reader :message_id
|
|
14
|
+
attr_reader :options
|
|
15
|
+
|
|
16
|
+
# @overload new(message_id, message)
|
|
17
|
+
# Instantiate error with given message_id and message
|
|
18
|
+
# @param message_id [Symbol]
|
|
19
|
+
# @param message_or_options [String]
|
|
20
|
+
# @overload new(message_id, options)
|
|
21
|
+
# Instantiate error with given message_id and options.
|
|
22
|
+
# Options would be passed to I18n key
|
|
23
|
+
# @param message_id [Symbol]
|
|
24
|
+
# @param message_or_options [Hash{Symbol => any}]
|
|
25
|
+
# @example
|
|
26
|
+
# Given the following I18n key exists:
|
|
27
|
+
# noise:
|
|
28
|
+
# public_error:
|
|
29
|
+
# unknown_fields: "Server does not know how to recognize these fields: %{fields}"
|
|
30
|
+
#
|
|
31
|
+
# To render error with this message:
|
|
32
|
+
# PublicError.new(:unknown_fields, fields: 'nickname, phone')
|
|
33
|
+
#
|
|
34
|
+
def initialize(message_id, message_or_options = nil)
|
|
35
|
+
@message_id = message_id.to_sym
|
|
36
|
+
case message_or_options
|
|
37
|
+
when Hash
|
|
38
|
+
@options = message_or_options
|
|
39
|
+
@message = nil
|
|
40
|
+
else
|
|
41
|
+
@options = {}
|
|
42
|
+
@message = message_or_options
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [String]
|
|
47
|
+
def message
|
|
48
|
+
@message.presence || I18n.t("noise.#{self.class.name.demodulize.underscore}.#{@message_id}", @options)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [String]
|
|
52
|
+
def inspect
|
|
53
|
+
"#<#{self.class}: #{message}>"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [ExceptionResponder]
|
|
57
|
+
# @api private
|
|
58
|
+
def responder_class
|
|
59
|
+
ExceptionResponder
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
# @param status [Symbol, Integer]
|
|
64
|
+
# @see http://apidock.com/rails/ActionController/Base/render#254-List-of-status-codes-and-their-symbols
|
|
65
|
+
# @param severity [Symbol, Integer]
|
|
66
|
+
# @see `Noise::Notification::SEVERITIES`
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# GoneError.register_as(:gone, :info)
|
|
70
|
+
#
|
|
71
|
+
def register_as(status, severity:)
|
|
72
|
+
Noise::ExceptionResponder.register(name, status: status)
|
|
73
|
+
Noise::Notification.register(name, severity: severity)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# 400
|
|
79
|
+
BadRequestError = Class.new(PublicError)
|
|
80
|
+
BadRequestError.register_as :bad_request, severity: :info
|
|
81
|
+
|
|
82
|
+
# 401
|
|
83
|
+
UnauthorizedError = Class.new(PublicError)
|
|
84
|
+
UnauthorizedError.register_as :unauthorized, severity: :warning
|
|
85
|
+
|
|
86
|
+
# 403
|
|
87
|
+
ForbiddenError = Class.new(PublicError)
|
|
88
|
+
ForbiddenError.register_as :forbidden, severity: :warning
|
|
89
|
+
|
|
90
|
+
# 404
|
|
91
|
+
class NotFoundError < PublicError
|
|
92
|
+
def initialize(message_id = 'not_found', message = nil)
|
|
93
|
+
super
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
NotFoundError.register_as :not_found, severity: :info
|
|
97
|
+
|
|
98
|
+
# 410
|
|
99
|
+
GoneError = Class.new(PublicError)
|
|
100
|
+
GoneError.register_as :gone, severity: :info
|
|
101
|
+
|
|
102
|
+
# 415
|
|
103
|
+
class UnsupportedMediaTypeError < PublicError
|
|
104
|
+
def initialize(message_id = 'unsupported_media_type', message = nil)
|
|
105
|
+
super
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
UnsupportedMediaTypeError.register_as :unsupported_media_type, severity: :info
|
|
109
|
+
|
|
110
|
+
# 422
|
|
111
|
+
class UnprocessableEntityError < PublicError
|
|
112
|
+
def initialize(message_id = :unprocessable_entity, message = nil)
|
|
113
|
+
super
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
UnprocessableEntityError.register_as :unprocessable_entity, severity: :info
|
|
117
|
+
|
|
118
|
+
# 503
|
|
119
|
+
ServiceUnavailableError = Class.new(PublicError)
|
|
120
|
+
ServiceUnavailableError.register_as :service_unavailable, severity: :warning
|
|
121
|
+
|
|
122
|
+
# 504
|
|
123
|
+
GatewayTimeoutError = Class.new(PublicError)
|
|
124
|
+
GatewayTimeoutError.register_as :gateway_timeout, severity: :warning
|
|
125
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Noise
|
|
4
|
+
# Errors api representation.
|
|
5
|
+
# Serializes api level errors to general errors format.
|
|
6
|
+
#
|
|
7
|
+
class PublicErrorSerializer < ErrorSerializer
|
|
8
|
+
def code
|
|
9
|
+
object.message_id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def title
|
|
13
|
+
object.message
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rails/railtie'
|
|
3
|
+
require 'noise'
|
|
4
|
+
|
|
5
|
+
module Noise
|
|
6
|
+
# Rails initializers
|
|
7
|
+
class Railtie < Rails::Railtie
|
|
8
|
+
initializer 'noise.exceptions_app' do
|
|
9
|
+
require 'noise/exceptions_app'
|
|
10
|
+
|
|
11
|
+
Rails.application.config.exceptions_app = Noise::ExceptionsApp.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
initializer 'noise.bugsnag' do
|
|
15
|
+
if Noise.config.bugsnag_enabled
|
|
16
|
+
require 'noise/bugsnag_middleware'
|
|
17
|
+
|
|
18
|
+
Bugsnag.configure do |config|
|
|
19
|
+
config.middleware.use Noise::BugsnagMiddleware
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'noise/public_error'
|
|
3
|
+
require 'noise/rate_limit_error_responder'
|
|
4
|
+
|
|
5
|
+
module Noise
|
|
6
|
+
# Rate limit error.
|
|
7
|
+
#
|
|
8
|
+
class RateLimitError < PublicError
|
|
9
|
+
attr_reader :retry_after
|
|
10
|
+
|
|
11
|
+
# @param message_id [Symbol]
|
|
12
|
+
# @param [String] retry_after
|
|
13
|
+
#
|
|
14
|
+
def initialize(message_id, retry_after:)
|
|
15
|
+
super(message_id)
|
|
16
|
+
|
|
17
|
+
@retry_after = retry_after
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def responder_class
|
|
21
|
+
RateLimitErrorResponder
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Noise::RateLimitError.register_as :too_many_requests, severity: :info
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'noise/exception_responder'
|
|
3
|
+
|
|
4
|
+
module Noise
|
|
5
|
+
# Special error responder with Retry-After header render support.
|
|
6
|
+
#
|
|
7
|
+
class RateLimitErrorResponder < ExceptionResponder
|
|
8
|
+
# @return [Hash]
|
|
9
|
+
def headers
|
|
10
|
+
super.merge(
|
|
11
|
+
'Retry-After' => error.retry_after.to_s,
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/noise.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'noise/version'
|
|
3
|
+
require 'noise/public_error'
|
|
4
|
+
require 'noise/rate_limit_error'
|
|
5
|
+
require 'active_support/configurable'
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
module Noise
|
|
9
|
+
include ActiveSupport::Configurable
|
|
10
|
+
extend ActiveSupport::Autoload
|
|
11
|
+
|
|
12
|
+
autoload :ErrorSerializer
|
|
13
|
+
autoload :ExceptionRenderer
|
|
14
|
+
autoload :PublicErrorSerializer
|
|
15
|
+
|
|
16
|
+
config.bugsnag_enabled = true
|
|
17
|
+
config.bugsnag_organization = nil
|
|
18
|
+
config.bugsnag_project = nil
|
|
19
|
+
config.exception_renderer = ExceptionRenderer
|
|
20
|
+
end
|
data/lib/white_noise.rb
ADDED
data/white_noise.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'noise/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'white_noise'
|
|
9
|
+
spec.version = Noise::VERSION
|
|
10
|
+
spec.authors = ['Tema Bolshakov']
|
|
11
|
+
spec.email = ['abolshakov@spbtv.com']
|
|
12
|
+
spec.license = 'Apache-2.0'
|
|
13
|
+
spec.summary = 'Defines middleware which renders exceptions and notifies Bugsnag.'
|
|
14
|
+
spec.homepage = 'https://github.com/SPBTV/white_noise'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
+
spec.bindir = 'exe'
|
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_runtime_dependency 'i18n', '~> 0.7.0'
|
|
22
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2'
|
|
23
|
+
spec.add_runtime_dependency 'actionpack', '~> 4.2'
|
|
24
|
+
spec.add_runtime_dependency 'active_model_serializers', '0.10.0.rc4'
|
|
25
|
+
spec.add_runtime_dependency 'uber', '~> 0.0.15'
|
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.4'
|
|
29
|
+
spec.add_development_dependency 'spbtv_code_style', '1.4.1'
|
|
30
|
+
spec.add_development_dependency 'bugsnag', '~> 6.6.3'
|
|
31
|
+
spec.add_development_dependency 'addressable', '~> 2.3'
|
|
32
|
+
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.2.3'
|
|
33
|
+
end
|