slowpoke 0.2.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b602f89b54077d5e970bb549ce1ad3e57db8d1ab6f15bf62ea3dbae868d6ca5
4
- data.tar.gz: 60cb3a0c463ebadeb4abec48794ffaa2b33b22af406c672ba2ac351f7134fbed
3
+ metadata.gz: b1f1038454b7897804fae9a78b8804de5ecd1d305929332fc544326df8ded0c4
4
+ data.tar.gz: 16c1ea1f4aea141681803f9f414620f4b12ffbf5dcb1ba5d8967ad3e6403ad6c
5
5
  SHA512:
6
- metadata.gz: 94352be6969f60055f4f6121f9b087fb5a4b2e144014b973fe6aa772cb7c4c9bd4cc5922a51dc3a41ce9a70bcd97f5f1bd0516ec764d002e410a0578cf1ffb25
7
- data.tar.gz: bf7c814b78e54a679798107f013083a596fab63fab960734fa5d32ba689e35128b33da7430aa560f789c56d3982d6e58f226f960828e985828793db11549e404
6
+ metadata.gz: 79c329a7bf360732c8ab05509b92502f7ebfea4025221568a6af4602897d7bc984a8a05f230d4bf1128221b172798ee3b436b5786e5e4b85119e737984a1fd64
7
+ data.tar.gz: 20dc8bc102e2fdcab2e3e896d0ff15a18f34009c0c18ea03ad56b1f26e37bb020c71aec4a7d8c6e66e338f0dbc63d222f65bda4e669d7c0efb4fb1ad428bfefb
data/CHANGELOG.md CHANGED
@@ -1,24 +1,43 @@
1
- ## 0.2.1
1
+ ## 0.4.0 (2022-10-01)
2
+
3
+ - Dropped support for Ruby < 2.6 and Rails < 5.2
4
+
5
+ ## 0.3.2 (2019-12-23)
6
+
7
+ - Added `on_timeout` method
8
+
9
+ ## 0.3.1 (2019-12-10)
10
+
11
+ - Added support for dynamic timeouts
12
+
13
+ ## 0.3.0 (2019-05-31)
14
+
15
+ - Use proper signal for Puma
16
+ - Dropped support for rack-timeout < 0.4
17
+ - Dropped support for migration timeouts
18
+ - Dropped support for Rails < 5
19
+
20
+ ## 0.2.1 (2018-05-21)
2
21
 
3
22
  - Don’t kill server in test environment
4
23
  - Require rack-timeout < 0.5
5
24
 
6
- ## 0.2.0
25
+ ## 0.2.0 (2017-11-05)
7
26
 
8
27
  - Fixed custom error pages for Rails 5.1
9
28
  - Fixed migration statement timeout
10
29
  - Don’t kill server in development
11
30
 
12
- ## 0.1.3
31
+ ## 0.1.3 (2016-08-03)
13
32
 
14
33
  - Fixed deprecation warning in Rails 5
15
34
  - No longer requires ActiveRecord
16
35
 
17
- ## 0.1.2
36
+ ## 0.1.2 (2016-02-10)
18
37
 
19
38
  - Updated to latest version of rack-timeout, removing the need to bubble timeouts
20
39
 
21
- ## 0.1.1
40
+ ## 0.1.1 (2015-08-02)
22
41
 
23
42
  - Fixed safer service timeouts
24
43
  - Added migration statement timeout
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Andrew Kane
1
+ Copyright (c) 2014-2021 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # Slowpoke
2
2
 
3
- [Rack::Timeout](https://github.com/heroku/rack-timeout) is great. Slowpoke makes it better with:
3
+ [Rack::Timeout](https://github.com/heroku/rack-timeout) enhancements for Rails
4
4
 
5
+ - safer service timeouts
6
+ - dynamic timeouts
5
7
  - custom error pages
6
- - [safer service timeouts](https://github.com/heroku/rack-timeout/issues/39)
7
- - wait timeouts that don’t kill your web server
8
8
 
9
9
  ## Installation
10
10
 
11
11
  Add this line to your application’s Gemfile:
12
12
 
13
13
  ```ruby
14
- gem 'slowpoke'
14
+ gem "slowpoke"
15
15
  ```
16
16
 
17
17
  And run:
@@ -22,24 +22,38 @@ rails generate slowpoke:install
22
22
 
23
23
  This creates a `public/503.html` you can customize.
24
24
 
25
- ## How to Use
25
+ ## Development
26
26
 
27
- The default timeout is 15 seconds. Change this with:
27
+ To try out custom error pages in development, temporarily add to `config/environments/development.rb`:
28
28
 
29
29
  ```ruby
30
- Slowpoke.timeout = 10
30
+ config.slowpoke.timeout = 1
31
+ config.consider_all_requests_local = false
31
32
  ```
32
33
 
33
- or set:
34
+ And add a `sleep` call to one of your actions:
34
35
 
35
36
  ```ruby
36
- ENV["REQUEST_TIMEOUT"]
37
+ sleep(2)
37
38
  ```
38
39
 
39
- Test by adding a `sleep` call to one of your actions:
40
+ The custom error page should appear.
41
+
42
+ ## Production
43
+
44
+ The default timeout is 15 seconds. You can change this in `config/environments/production.rb` with:
40
45
 
41
46
  ```ruby
42
- sleep(20)
47
+ config.slowpoke.timeout = 5
48
+ ```
49
+
50
+ For dynamic timeouts, use:
51
+
52
+ ```ruby
53
+ config.slowpoke.timeout = lambda do |env|
54
+ request = Rack::Request.new(env)
55
+ request.path.start_with?("/admin") ? 15 : 5
56
+ end
43
57
  ```
44
58
 
45
59
  Subscribe to timeouts with:
@@ -50,41 +64,65 @@ ActiveSupport::Notifications.subscribe "timeout.slowpoke" do |name, start, finis
50
64
  end
51
65
  ```
52
66
 
53
- To learn more, see the [Rack::Timeout documentation](https://github.com/heroku/rack-timeout#the-rabbit-hole).
67
+ To learn more, see the [Rack::Timeout documentation](https://github.com/heroku/rack-timeout).
68
+
69
+ ## Safer Service Timeouts
70
+
71
+ Rack::Timeout can raise an exception at any point in the code, which can leave your app in an [unclean state](https://www.schneems.com/2017/02/21/the-oldest-bug-in-ruby-why-racktimeout-might-hose-your-server/). The safest way to recover from a request timeout is to spawn a new process. This is the default behavior for Slowpoke.
72
+
73
+ For threaded servers like Puma, this means killing all threads when any one of them times out. This can have a significant impact on performance.
74
+
75
+ You can customize this behavior with:
76
+
77
+ ```ruby
78
+ Slowpoke.on_timeout do |env|
79
+ next if Rails.env.development? || Rails.env.test?
80
+
81
+ exception = env["action_dispatch.exception"]
82
+ if exception && exception.backtrace.first.include?("/active_record/")
83
+ Slowpoke.kill
84
+ end
85
+ end
86
+ ```
87
+
88
+ Note: To access `env["action_dispatch.exception"]` in development, temporarily add to `config/environments/development.rb`:
89
+
90
+ ```ruby
91
+ config.consider_all_requests_local = false
92
+ ```
54
93
 
55
94
  ## Database Timeouts
56
95
 
57
- For PostgreSQL, set connect and statement timeouts in `config/database.yml`:
96
+ It’s a good idea to set a [statement timeout](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts/#statement-timeouts-1) and a [connect timeout](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts/#activerecord). For Postgres, your `config/database.yml` should include something like:
58
97
 
59
- ```yaml
98
+ ```yml
60
99
  production:
61
- connect_timeout: 1 # sec
100
+ connect_timeout: 3 # sec
62
101
  variables:
63
- statement_timeout: 250 # ms
102
+ statement_timeout: 5s
64
103
  ```
65
104
 
66
- To use a different statement timeout for migrations, set:
105
+ ## Upgrading
67
106
 
68
- ```ruby
69
- ENV["MIGRATION_STATEMENT_TIMEOUT"] = 60000 # ms
70
- ```
107
+ ### 0.3.0
71
108
 
72
- Test connect timeouts by setting your database host to an [unroutable IP](https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error).
109
+ If you set the timeout with:
73
110
 
74
- ```yaml
75
- development:
76
- host: 10.255.255.1
111
+ ```ruby
112
+ Slowpoke.timeout = 5
77
113
  ```
78
114
 
79
- Test statement timeouts with the [pg_sleep](https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-DELAY) function.
115
+ Remove it and add to `config/environments/production.rb`:
80
116
 
81
- ```sql
82
- SELECT pg_sleep(20);
117
+ ```ruby
118
+ config.slowpoke.timeout = 5
83
119
  ```
84
120
 
85
- ## Upgrading
121
+ If you use migration timeouts, check out [this guide](https://github.com/ankane/the-ultimate-guide-to-ruby-timeouts/#statement-timeouts-1) for how to configure them directly in `config/database.yml`.
122
+
123
+ ### 0.1.0
86
124
 
87
- `0.1.0` removes database timeouts, since Rails supports them by default. To restore the previous behavior, use:
125
+ `0.1.0` removes database timeouts, since Rails supports them by default. To restore the previous behavior, use:
88
126
 
89
127
  ```yaml
90
128
  production:
@@ -104,3 +142,11 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
104
142
  - Fix bugs and [submit pull requests](https://github.com/ankane/slowpoke/pulls)
105
143
  - Write, clarify, or fix documentation
106
144
  - Suggest or add new features
145
+
146
+ To get started with development:
147
+
148
+ ```sh
149
+ git clone https://github.com/ankane/slowpoke.git
150
+ cd slowpoke
151
+ bundle install
152
+ ```
@@ -4,7 +4,7 @@
4
4
  <title>This page took too long to load (503)</title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <style>
7
- body {
7
+ .rails-default-error-page {
8
8
  background-color: #EFEFEF;
9
9
  color: #2E2F30;
10
10
  text-align: center;
@@ -12,13 +12,13 @@
12
12
  margin: 0;
13
13
  }
14
14
 
15
- div.dialog {
15
+ .rails-default-error-page div.dialog {
16
16
  width: 95%;
17
17
  max-width: 33em;
18
18
  margin: 4em auto 0;
19
19
  }
20
20
 
21
- div.dialog > div {
21
+ .rails-default-error-page div.dialog > div {
22
22
  border: 1px solid #CCC;
23
23
  border-right-color: #999;
24
24
  border-left-color: #999;
@@ -31,13 +31,13 @@
31
31
  box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
32
  }
33
33
 
34
- h1 {
34
+ .rails-default-error-page h1 {
35
35
  font-size: 100%;
36
36
  color: #730E15;
37
37
  line-height: 1.5em;
38
38
  }
39
39
 
40
- div.dialog > p {
40
+ .rails-default-error-page div.dialog > p {
41
41
  margin: 0 0 1em;
42
42
  padding: 1em;
43
43
  background-color: #F7F7F7;
@@ -54,7 +54,7 @@
54
54
  </style>
55
55
  </head>
56
56
 
57
- <body>
57
+ <body class="rails-default-error-page">
58
58
  <!-- This file lives in public/503.html -->
59
59
  <div class="dialog">
60
60
  <div>
@@ -7,17 +7,11 @@ module Slowpoke
7
7
  def call(env)
8
8
  @app.call(env)
9
9
  ensure
10
- if env[Slowpoke::ENV_KEY]
11
- # extremely important
12
- # protect the process with a restart
13
- # https://github.com/heroku/rack-timeout/issues/39
14
- # can't do in timed_out state consistently
15
- if defined?(::PhusionPassenger)
16
- `passenger-config detach-process #{Process.pid}`
17
- else
18
- Process.kill("QUIT", Process.pid)
19
- end
20
- end
10
+ # extremely important
11
+ # protect the process with a restart
12
+ # https://github.com/heroku/rack-timeout/issues/39
13
+ # can't do in timed_out state consistently
14
+ Slowpoke.on_timeout.call(env) if env[Slowpoke::ENV_KEY]
21
15
  end
22
16
  end
23
17
  end
@@ -1,13 +1,26 @@
1
1
  module Slowpoke
2
2
  class Railtie < Rails::Railtie
3
+ config.slowpoke = ActiveSupport::OrderedOptions.new
4
+
5
+ # must happen outside initializer (so it runs earlier)
6
+ config.action_dispatch.rescue_responses.merge!(
7
+ "Rack::Timeout::RequestTimeoutError" => :service_unavailable,
8
+ "Rack::Timeout::RequestExpiryError" => :service_unavailable
9
+ )
10
+
3
11
  initializer "slowpoke" do |app|
4
- Rack::Timeout.service_timeout = Slowpoke.timeout
5
- if Rails::VERSION::MAJOR >= 5
6
- app.config.middleware.insert_after ActionDispatch::DebugExceptions, Rack::Timeout
12
+ service_timeout = app.config.slowpoke.timeout
13
+ service_timeout ||= ENV["RACK_TIMEOUT_SERVICE_TIMEOUT"] || ENV["REQUEST_TIMEOUT"] || ENV["TIMEOUT"] || 15
14
+
15
+ if service_timeout.respond_to?(:call)
16
+ app.config.middleware.insert_after ActionDispatch::DebugExceptions, Slowpoke::Timeout,
17
+ service_timeout: service_timeout
7
18
  else
8
- app.config.middleware.insert_before ActionDispatch::RemoteIp, Rack::Timeout
19
+ app.config.middleware.insert_after ActionDispatch::DebugExceptions, Rack::Timeout,
20
+ service_timeout: service_timeout.to_i
9
21
  end
10
- app.config.middleware.insert(0, Slowpoke::Middleware) unless Rails.env.development? || Rails.env.test?
22
+
23
+ app.config.middleware.insert(0, Slowpoke::Middleware)
11
24
  end
12
25
  end
13
26
  end
@@ -0,0 +1,18 @@
1
+ module Slowpoke
2
+ class Timeout
3
+ def initialize(app, service_timeout:)
4
+ @app = app
5
+ @service_timeout = service_timeout
6
+ @middleware = {}
7
+ end
8
+
9
+ def call(env)
10
+ service_timeout = @service_timeout.call(env)
11
+ if service_timeout
12
+ (@middleware[service_timeout] ||= Rack::Timeout.new(@app, service_timeout: service_timeout)).call(env)
13
+ else
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module Slowpoke
2
- VERSION = "0.2.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/slowpoke.rb CHANGED
@@ -1,32 +1,40 @@
1
- require "slowpoke/version"
1
+ # dependencies
2
2
  require "rack/timeout/base"
3
+
4
+ # modules
3
5
  require "slowpoke/middleware"
4
- require "slowpoke/migration"
5
6
  require "slowpoke/railtie"
6
- require "action_dispatch/middleware/exception_wrapper"
7
- require "action_controller/base"
7
+ require "slowpoke/timeout"
8
+ require "slowpoke/version"
8
9
 
9
10
  module Slowpoke
10
11
  ENV_KEY = "slowpoke.timed_out".freeze
11
12
 
12
- def self.timeout
13
- @timeout ||= (ENV["REQUEST_TIMEOUT"] || ENV["TIMEOUT"] || 15).to_i
13
+ def self.kill
14
+ if defined?(::PhusionPassenger)
15
+ `passenger-config detach-process #{Process.pid}`
16
+ elsif defined?(::Puma)
17
+ Process.kill("TERM", Process.pid)
18
+ else
19
+ Process.kill("QUIT", Process.pid)
20
+ end
14
21
  end
15
22
 
16
- def self.timeout=(timeout)
17
- timeout = timeout.to_i if timeout.respond_to?(:to_i)
18
- @timeout = Rack::Timeout.service_timeout = timeout
23
+ def self.on_timeout(&block)
24
+ if block_given?
25
+ @on_timeout = block
26
+ else
27
+ @on_timeout
28
+ end
19
29
  end
20
30
 
21
- def self.migration_statement_timeout
22
- ENV["MIGRATION_STATEMENT_TIMEOUT"]
31
+ on_timeout do |env|
32
+ next if Rails.env.development? || Rails.env.test?
33
+
34
+ Slowpoke.kill
23
35
  end
24
36
  end
25
37
 
26
- # custom error page
27
- ActionDispatch::ExceptionWrapper.rescue_responses["Rack::Timeout::RequestTimeoutError"] = :service_unavailable
28
- ActionDispatch::ExceptionWrapper.rescue_responses["Rack::Timeout::RequestExpiryError"] = :service_unavailable
29
-
30
38
  # remove noisy logger
31
39
  Rack::Timeout.unregister_state_change_observer(:logger)
32
40
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slowpoke
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-21 00:00:00.000000000 Z
11
+ date: 2022-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '5.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: actionpack
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,74 +44,35 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.0
48
- - - "<"
49
- - !ruby/object:Gem::Version
50
- version: 0.5.0
47
+ version: '0.4'
51
48
  type: :runtime
52
49
  prerelease: false
53
50
  version_requirements: !ruby/object:Gem::Requirement
54
51
  requirements:
55
52
  - - ">="
56
53
  - !ruby/object:Gem::Version
57
- version: 0.3.0
58
- - - "<"
59
- - !ruby/object:Gem::Version
60
- version: 0.5.0
61
- - !ruby/object:Gem::Dependency
62
- name: bundler
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: '0'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: '0'
75
- - !ruby/object:Gem::Dependency
76
- name: rake
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
- description:
90
- email:
91
- - andrew@chartkick.com
54
+ version: '0.4'
55
+ description:
56
+ email: andrew@ankane.org
92
57
  executables: []
93
58
  extensions: []
94
59
  extra_rdoc_files: []
95
60
  files:
96
- - ".gitignore"
97
61
  - CHANGELOG.md
98
- - Gemfile
99
62
  - LICENSE.txt
100
63
  - README.md
101
- - Rakefile
102
64
  - lib/generators/slowpoke/install_generator.rb
103
65
  - lib/generators/slowpoke/templates/503.html
104
66
  - lib/slowpoke.rb
105
67
  - lib/slowpoke/middleware.rb
106
- - lib/slowpoke/migration.rb
107
68
  - lib/slowpoke/railtie.rb
69
+ - lib/slowpoke/timeout.rb
108
70
  - lib/slowpoke/version.rb
109
- - slowpoke.gemspec
110
71
  homepage: https://github.com/ankane/slowpoke
111
72
  licenses:
112
73
  - MIT
113
74
  metadata: {}
114
- post_install_message:
75
+ post_install_message:
115
76
  rdoc_options: []
116
77
  require_paths:
117
78
  - lib
@@ -119,16 +80,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
80
  requirements:
120
81
  - - ">="
121
82
  - !ruby/object:Gem::Version
122
- version: '0'
83
+ version: '2.6'
123
84
  required_rubygems_version: !ruby/object:Gem::Requirement
124
85
  requirements:
125
86
  - - ">="
126
87
  - !ruby/object:Gem::Version
127
88
  version: '0'
128
89
  requirements: []
129
- rubyforge_project:
130
- rubygems_version: 2.7.6
131
- signing_key:
90
+ rubygems_version: 3.3.3
91
+ signing_key:
132
92
  specification_version: 4
133
- summary: Rack::Timeout is great. Slowpoke makes it better.
93
+ summary: Rack::Timeout enhancements for Rails
134
94
  test_files: []
data/.gitignore DELETED
@@ -1,14 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- *.bundle
11
- *.so
12
- *.o
13
- *.a
14
- mkmf.log
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in slowpoke.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- require "bundler/gem_tasks"
@@ -1,16 +0,0 @@
1
- module Slowpoke
2
- module Migration
3
- def connection
4
- connection = super
5
- if Slowpoke.migration_statement_timeout && !@migration_statement_timeout_set
6
- connection.execute("SET statement_timeout = #{Slowpoke.migration_statement_timeout.to_i}")
7
- @migration_statement_timeout_set = true
8
- end
9
- connection
10
- end
11
- end
12
- end
13
-
14
- ActiveSupport.on_load(:active_record) do
15
- ActiveRecord::Migration.prepend(Slowpoke::Migration)
16
- end
data/slowpoke.gemspec DELETED
@@ -1,26 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "slowpoke/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "slowpoke"
8
- spec.version = Slowpoke::VERSION
9
- spec.authors = ["Andrew Kane"]
10
- spec.email = ["andrew@chartkick.com"]
11
- spec.summary = "Rack::Timeout is great. Slowpoke makes it better."
12
- spec.homepage = "https://github.com/ankane/slowpoke"
13
- spec.license = "MIT"
14
-
15
- spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
19
-
20
- spec.add_dependency "railties"
21
- spec.add_dependency "actionpack"
22
- spec.add_dependency "rack-timeout", ">= 0.3.0", "< 0.5.0"
23
-
24
- spec.add_development_dependency "bundler"
25
- spec.add_development_dependency "rake"
26
- end