rails_daemons 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 341d44fec654ca5a7bd975916ab575d877103b1c
4
+ data.tar.gz: 171b1b87e26e5468321c4683e82e93bf598fb643
5
+ SHA512:
6
+ metadata.gz: 1623e1db2fea6fc3f69f4b2ca4680453c4ab59a8eda59dfd117394b62ba5aeac725f2dcef972122bebe397622a829790f01a42007318343f601076c404261a90
7
+ data.tar.gz: 3cdabbaeb5782b96c560296afb3b2e488fda08d62fb2d6cf4f090bf0d6b3509328b289ad3970a29de81928fb378f2e755604775d22e4f841e54eb142f9fb9432
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/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ rails_daemons
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.5
data/Capfile ADDED
@@ -0,0 +1,25 @@
1
+ # Load DSL and Setup Up Stages
2
+ require 'capistrano/setup'
3
+
4
+ # Includes default deployment tasks
5
+ require 'capistrano/deploy'
6
+
7
+ # Includes tasks from other gems included in your Gemfile
8
+ #
9
+ # For documentation on these, see for example:
10
+ #
11
+ # https://github.com/capistrano/rvm
12
+ # https://github.com/capistrano/rbenv
13
+ # https://github.com/capistrano/chruby
14
+ # https://github.com/capistrano/bundler
15
+ # https://github.com/capistrano/rails/tree/master/assets
16
+ # https://github.com/capistrano/rails/tree/master/migrations
17
+ #
18
+ # require 'capistrano/rvm'
19
+ # require 'capistrano/rbenv'
20
+ # require 'capistrano/chruby'
21
+ # require 'capistrano/bundler'
22
+ # require 'capistrano/rails/assets'
23
+ # require 'capistrano/rails/migrations'
24
+
25
+ require 'rails_daemons/capistrano'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rails_daemons.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sergey Malykh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # RailsDaemons
2
+
3
+ Background workers for Rails. The gem is designed to safely handling exceptions occured in workers. Daemons restarts gracefully to achieve zerro downtime. RailsDaemons includes [Capistrano](https://github.com/capistrano/capistrano) and [Monit](https://mmonit.com/monit/) support. The gem also supports logrotate by safely handling signal USR1.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rails_daemons'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rails_daemons
18
+
19
+ ## Capistrano integration
20
+
21
+ Add ```require 'rails_daemons/capistrano'``` to Capfile
22
+
23
+ This will get available commands:
24
+
25
+ ```
26
+ cap production daemon:start[<worker_name>]
27
+ cap production daemon:restart[<worker_name>]
28
+ cap production daemon:stop[<worker_name>]
29
+ ```
30
+
31
+ ## Background worker controls
32
+
33
+ ```
34
+ bundle exec daemon start <worker_name>
35
+ bundle exec daemon restart <worker_name>
36
+ bundle exec daemon stop <worker_name>
37
+ ```
38
+
39
+ ## Worker construction
40
+
41
+ Create background worker.
42
+
43
+ ```ruby
44
+ class ParserWorker
45
+ include RailsDaemons::Worker
46
+
47
+ def work
48
+ # your work here
49
+ end
50
+ end
51
+ ```
52
+
53
+ Also you can specify:
54
+
55
+ * ```tick``` - delay between daemon cycles (in seconds, default 1.0)
56
+ * ```start``` - work that should be done once on the daemon`s start (and restart)
57
+ * ```shutdown``` - work that should be done before the daemon`s stop (either on exception or regular stop)
58
+
59
+ Example usage (with Mongoid):
60
+
61
+ ```ruby
62
+ # app/models/parsing.rb
63
+ class Parsing
64
+ include Mongoid::Document
65
+
66
+ field :url
67
+ field :state, default: 'pending'
68
+
69
+ scope :pending, ->() { where( state: 'pending' ) }
70
+ scope :running, ->() { where( state: 'running' ) }
71
+
72
+ class << self
73
+ def start
74
+ [ 'http://www.example.com' ].each do |url|
75
+ create!( url: url )
76
+ end
77
+ end
78
+ end
79
+
80
+ def process( logger )
81
+ set( state: 'running' )
82
+
83
+ # do the job
84
+
85
+ rescue => e
86
+ logger.info e.inspect
87
+
88
+ set( state: 'halted' )
89
+ end
90
+ end
91
+
92
+ # app/daemons/parser_worker.rb
93
+ class ParserWorker
94
+ include RailsDaemons::Worker
95
+
96
+ def tick
97
+ 3 # in seconds
98
+ end
99
+
100
+ # daemon start
101
+ def start
102
+ require 'mechanize'
103
+ require 'rufus-scheduler'
104
+
105
+ scheduler = Rufus::Scheduler.new
106
+
107
+ scheduler.every '12h' do
108
+ Parsing.start # create parsing jobs every 12 hours
109
+ end
110
+ end
111
+
112
+ # main work
113
+ def work
114
+ Parsing.pending.each do |parsing|
115
+ t = Thread.new do
116
+ parsing.process( $logger )
117
+ end
118
+
119
+ # exceptions handled in method ```process``` (just for an example)
120
+ t.abort_on_exception = false
121
+ end
122
+ end
123
+
124
+ # stop the daemon
125
+ def shutdown
126
+ Parsing.running.each do |parsing|
127
+ parsing.set( state: 'stopped' )
128
+ end
129
+ end
130
+ end
131
+
132
+ ```
133
+
134
+ ## Monit integration:
135
+
136
+ Put the following code to /etc/init.d/<worker_name>, replace <worker_name> with you name.
137
+
138
+ ```
139
+ #!/bin/bash
140
+ ### BEGIN INIT INFO
141
+ # Provides: <worker_name>
142
+ # Required-Start: $all
143
+ # Required-Stop: $network $local_fs $syslog
144
+ # Default-Start: 2 3 4 5
145
+ # Default-Stop: 0 1 6
146
+ # Short-Description: Start daemon at boot
147
+ # Description: Enable daemon at boot time.
148
+ ### END INIT INFO
149
+
150
+ set -u
151
+ set -e
152
+
153
+ # Change these to match your app:
154
+ APP_NAME=app
155
+ ENV=production
156
+ USER=user
157
+ APP_ROOT="/home/$USER/$APP_NAME/current"
158
+
159
+ SET_PATH="cd $APP_ROOT; rvm use `cat $APP_ROOT/.ruby-version`@`cat $APP_ROOT/.ruby-gemset`"
160
+ OUT=">> $APP_ROOT/log/worker_name.$ENV.monit.log 2>&1"
161
+
162
+ cd $APP_ROOT || exit 1
163
+
164
+ case ${1-help} in
165
+ start)
166
+ su - $USER -c "$SET_PATH; RAILS_ENV=$ENV bundle exec daemon start <worker_name> $OUT"
167
+ ;;
168
+ stop)
169
+ su - $USER -c "$SET_PATH; RAILS_ENV=$ENV bundle exec daemon stop <worker_name> $OUT"
170
+ ;;
171
+ restart|reload)
172
+ su - $USER -c "$SET_PATH; RAILS_ENV=$ENV bundle exec daemon restart <worker_name> $OUT"
173
+ ;;
174
+ *)
175
+ echo >&2 "Usage: $0 <start|stop|restart>"
176
+ exit 1
177
+ ;;
178
+ esac
179
+ ```
180
+
181
+ Monit task:
182
+
183
+ ```
184
+ check process <worker_name> with pidfile /<path_to_project>/current/tmp/pids/<worker_name>.production.pid
185
+ start program = "/etc/init.d/<worker_name> start"
186
+ stop program = "/etc/init.d/<worker_name> stop"
187
+ if changed pid for 3 times within 5 cycles then restart
188
+ if 5 restarts within 5 cycles then timeout
189
+ ```
190
+
191
+ ## Logrotate integration
192
+
193
+ ```
194
+ /<path_to_project>/shared/log/*.log
195
+ {
196
+ su <user> <group>
197
+ daily
198
+ missingok
199
+ rotate 360
200
+ compress
201
+ delaycompress
202
+ notifempty
203
+ dateext
204
+ create 0660 <user> <group>
205
+ postrotate
206
+ [ ! -f /<path_to_project>/shared/tmp/pids/unicorn.pid ] || kill -USR1 `cat /<path_to_project>/shared/tmp/pids/unicorn.pid`
207
+ [ ! -f /<path_to_project>/shared/tmp/pids/<worker_name>.production.pid ] || kill -USR1 `cat /<path_to_project>/shared/tmp/pids/<worker_name>.production.pid`
208
+ endscript
209
+ }
210
+ ```
211
+
212
+ ## Contributing
213
+
214
+ 1. Fork it
215
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
216
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
217
+ 4. Push to the branch (`git push origin my-new-feature`)
218
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/Thorfile ADDED
@@ -0,0 +1,14 @@
1
+ # https://shvets.github.io/blog/2013/12/14/using_thor_as_rake_replacement.html
2
+ unless defined? Thor::Runner
3
+ require 'bundler'
4
+
5
+ gems = Bundler::Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil).requested_specs
6
+
7
+ gem = gems.find { |gem| gem.name == 'thor' }
8
+
9
+ load "#{ENV['GEM_HOME']}/gems/#{gem.name}-#{gem.version}/bin/thor"
10
+ end
11
+
12
+ Dir.glob("lib/rails_daemons/tasks/*.thor") do |name|
13
+ Thor::Util.load_thorfile(name)
14
+ end
data/bin/daemon ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+ require 'rails_daemons/thor'
4
+ RailsDaemons::Daemon.start
@@ -0,0 +1,6 @@
1
+ require "rails_daemons/version"
2
+ require "rails_daemons/railtie" if defined?(Rails)
3
+ require "rails_daemons/worker"
4
+
5
+ module RailsDaemons
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'capistrano/version'
2
+
3
+ if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release < Gem::Version.new("3.0")
4
+ raise "RailsDaemons requires Capistrano 3.x"
5
+ end
6
+
7
+ load File.expand_path("../tasks/rails_daemons.cap", __FILE__)
@@ -0,0 +1,4 @@
1
+ module RailsDaemons
2
+ class Railtie < Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,40 @@
1
+ namespace :daemon do
2
+ desc 'Start background worker'
3
+ task :start, :worker_name do |task, args|
4
+ raise 'Worked is not specified' if args[:worker_name].nil?
5
+
6
+ on roles(:app) do
7
+ within release_path do
8
+ with rails_env: fetch(:rails_env) do
9
+ execute :bundle, :exec, :daemon, "start #{args[:worker_name]}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ desc 'Restart background worker'
16
+ task :restart, :worker_name do |task, args|
17
+ raise 'Worked is not specified' if args[:worker_name].nil?
18
+
19
+ on roles(:app) do
20
+ within release_path do
21
+ with rails_env: fetch(:rails_env) do
22
+ execute :bundle, :exec, :daemon, "restart #{args[:worker_name]}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ desc 'Stop background worker'
29
+ task :stop, :worker_name do |task, args|
30
+ raise 'Worked is not specified' if args[:worker_name].nil?
31
+
32
+ on roles(:app) do
33
+ within release_path do
34
+ with rails_env: fetch(:rails_env) do
35
+ execute :bundle, :exec, :daemon, "stop #{args[:worker_name]}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ require 'thor/rails'
2
+
3
+ module ::RailsDaemons
4
+ class Daemon < Thor
5
+ include Thor::Rails
6
+ namespace :daemon
7
+
8
+ no_commands do
9
+ def get_daemon( name )
10
+ RailsDaemons.qualified_const_get( name.camelize )
11
+ rescue NameError => e
12
+ puts "Unknown daemon '#{name}'"
13
+ end
14
+ end
15
+
16
+ desc "start <worker_name>", "Start background worker"
17
+ def start( name )
18
+ daemon = get_daemon( name )
19
+ return if daemon.nil?
20
+ daemon.new.daemonize
21
+ end
22
+
23
+ desc "restart <worker_name>", "Restart background worker (alias for start command)"
24
+ def restart( name )
25
+ invoke :start, [ name ]
26
+ end
27
+
28
+ desc "stop <worker_name>", "Stop background worker"
29
+ def stop( name )
30
+ get_daemon( name ).stop
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ require 'thor'
2
+ Thor::Util.load_thorfile File.expand_path("../tasks/rails_daemons.thor", __FILE__)
@@ -0,0 +1,25 @@
1
+ module RailsDaemons
2
+ module Utils
3
+ extend self
4
+
5
+ def join( *paths )
6
+ # TODO: remove this dirty code
7
+ return Rails.root.join( *paths ) if Rails.root.to_s !~ /.*\/releases\/\d{14}/
8
+
9
+ paths = [ '..', '..', 'current' ] + paths
10
+
11
+ path = Rails.root.join( *paths )
12
+ FileUtils.mkdir_p( File.dirname( path ) )
13
+ path
14
+ end
15
+
16
+ def logger( file_name )
17
+ logger = Logger.new( join( 'log', file_name ) )
18
+ logger.level = Logger::INFO
19
+ logger.datetime_format = "%Y-%m-%d %H:%M:%S"
20
+ logger.formatter = Logger::Formatter.new
21
+
22
+ logger
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module RailsDaemons
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,214 @@
1
+ require 'rails_daemons/utils'
2
+ require 'unicorn/util'
3
+ require 'active_support/concern'
4
+
5
+ module RailsDaemons
6
+ module Worker
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def daemonize
11
+ pid = fork do
12
+ $logger = Utils.logger( "#{self.class.worker_name}.#{Rails.env}.log" )
13
+ $logger.level = Logger::INFO
14
+
15
+ working
16
+ end
17
+
18
+ Process.detach( pid )
19
+
20
+ puts "#{self.class.worker_name} (#{pid}) started."
21
+ end
22
+
23
+ def working
24
+ $stop_working = false
25
+ $reopening = false
26
+
27
+ Signal.trap "INT" do
28
+ Thread.new do
29
+ shutdowning
30
+ end
31
+ end
32
+
33
+ Signal.trap "USR1" do
34
+ $reopening = true
35
+ end
36
+
37
+ stop_old_worker
38
+
39
+ starting
40
+
41
+ loop do
42
+ reopen_logs if $reopening
43
+ break if $stop_working
44
+
45
+ work
46
+
47
+ sleep tick
48
+ end
49
+ end
50
+
51
+ def reopen_logs
52
+ pid = self.class.get_pid
53
+
54
+ logger.info "Reopen logs #{self.class.worker_name} (#{pid})"
55
+
56
+ Unicorn::Util.reopen_logs
57
+
58
+ logger.info "Logs reopened #{self.class.worker_name} (#{pid})"
59
+
60
+ $reopening = false
61
+ end
62
+
63
+ def logger
64
+ $logger
65
+ end
66
+
67
+ def starting
68
+ $0 = "RAILS_ENV=#{Rails.env} " + Utils.join( '' ).to_s + " bundle exec thor daemon:start #{self.class}"
69
+
70
+ redirect_io
71
+ start
72
+ store_pid
73
+ end
74
+
75
+ def redirect_io
76
+ # https://github.com/ghazel/daemons/blob/d09e132ea67001ba4d6bf6481fb53c4bd4fd9195/lib/daemons/daemonize.rb#L241
77
+ begin; STDIN.reopen "/dev/null"; rescue ::Exception; end
78
+
79
+ begin
80
+ STDOUT.reopen( Utils.join( 'log', "#{self.class.worker_name}.#{Rails.env}.out.log" ), "a" )
81
+ STDOUT.sync = true
82
+ rescue ::Exception
83
+ begin; STDOUT.reopen "/dev/null"; rescue ::Exception; end
84
+ end
85
+
86
+ begin; STDERR.reopen STDOUT; rescue ::Exception; end
87
+ STDERR.sync = true
88
+ end
89
+
90
+ def start
91
+ puts "Start #{self.class.name}"
92
+ end
93
+
94
+ def work
95
+ raise 'Not implemented! Write your own work'
96
+ end
97
+
98
+ def tick
99
+ 1.0
100
+ end
101
+
102
+ def shutdowning
103
+ pid = self.class.get_pid
104
+
105
+ logger.info "Graceful shutdown #{self.class.worker_name} (#{pid})"
106
+ $stop_working = true
107
+
108
+ shutdown
109
+
110
+ puts "#{self.class.worker_name} (#{pid}) stopped."
111
+
112
+ exit 0
113
+
114
+ rescue => e
115
+ logger.error "exiting #{self.class.worker_name}, unable to stop gracefully"
116
+ logger.error e.message
117
+ logger.error e.backtrace.join( "\n" )
118
+
119
+ File.remove( self.class.pid_file ) if File.exists?( self.class.pid_file )
120
+
121
+ exit 1
122
+ end
123
+
124
+ def shutdown
125
+ end
126
+
127
+ def stop_old_worker
128
+ return unless File.file?( self.class.pid_file )
129
+
130
+ pid = self.class.get_pid
131
+
132
+ unless self.class.running?( pid )
133
+ logger.info "Stale pid file (#{pid}), deleting"
134
+ File.delete( self.class.pid_file )
135
+
136
+ return
137
+ end
138
+
139
+ logger.info "Killing old worker (#{pid})"
140
+ Process.kill( "INT", pid )
141
+
142
+ # wait for die
143
+ 32.times do
144
+ sleep 3
145
+
146
+ unless self.class.running?( pid )
147
+ logger.info "Old worker (#{pid}) died by himself"
148
+
149
+ return
150
+ end
151
+ end
152
+
153
+ return unless self.class.running?( pid )
154
+
155
+ logger.error "Old worker (#{pid}) isn't going to die, doing kill -9"
156
+
157
+ Process.kill( "KILL", pid )
158
+ File.delete( self.class.pid_file )
159
+ end
160
+
161
+ def store_pid
162
+ FileUtils.mkdir_p( File.dirname( self.class.pid_file ) )
163
+ IO.write( self.class.pid_file, Process.pid.to_s )
164
+ end
165
+ end
166
+
167
+ module ClassMethods
168
+ def daemonize
169
+ self.new.daemonize
170
+ end
171
+
172
+ def stop
173
+ pid = get_pid
174
+
175
+ if running?( pid )
176
+ Process.kill( 'INT', pid )
177
+ else
178
+ puts "Worker #{name} (#{pid}) not running"
179
+ end
180
+ end
181
+
182
+ def get_pid
183
+ return unless File.exists?( pid_file )
184
+ File.read( pid_file ).to_i
185
+ end
186
+
187
+ def pid_file
188
+ Utils.join( 'tmp', 'pids', "#{worker_name}.#{Rails.env}.pid" )
189
+ end
190
+
191
+ def worker_name
192
+ name.underscore
193
+ end
194
+
195
+ def running?( pid )
196
+ return false if pid.blank?
197
+
198
+ # https://github.com/ghazel/daemons/blob/d09e132ea67001ba4d6bf6481fb53c4bd4fd9195/lib/daemons/pid.rb#L17
199
+ # Check if process is in existence
200
+ # The simplest way to do this is to send signal '0'
201
+ # (which is a single system call) that doesn't actually
202
+ # send a signal
203
+ begin
204
+ Process.kill(0, pid)
205
+ return true
206
+ rescue Errno::ESRCH
207
+ return false
208
+ rescue ::Exception # for example on EPERM (process exists but does not belong to us)
209
+ return true
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'rails_daemons/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rails_daemons"
7
+ spec.version = RailsDaemons::VERSION
8
+ spec.authors = ["Sergey Malykh"]
9
+ spec.email = ["xronos.i.am@gmail.com"]
10
+ spec.description = %q{Daemons for Rails. Can be restarted on the host by Thor or remotely by Capistrano, monitored by Monit}
11
+ spec.summary = %q{Daemons for Rails}
12
+ spec.homepage = "https://github.com/xronos-i-am/rails_daemons"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
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_development_dependency "capistrano", '~> 3.3'
21
+ spec.add_development_dependency "bundler"
22
+
23
+ spec.add_dependency "unicorn"
24
+ spec.add_dependency "thor-rails"
25
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_daemons
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Malykh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
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: unicorn
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Daemons for Rails. Can be restarted on the host by Thor or remotely by
70
+ Capistrano, monitored by Monit
71
+ email:
72
+ - xronos.i.am@gmail.com
73
+ executables:
74
+ - daemon
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".ruby-gemset"
80
+ - ".ruby-version"
81
+ - Capfile
82
+ - Gemfile
83
+ - LICENSE.txt
84
+ - README.md
85
+ - Rakefile
86
+ - Thorfile
87
+ - bin/daemon
88
+ - lib/rails_daemons.rb
89
+ - lib/rails_daemons/capistrano.rb
90
+ - lib/rails_daemons/railtie.rb
91
+ - lib/rails_daemons/tasks/rails_daemons.cap
92
+ - lib/rails_daemons/tasks/rails_daemons.thor
93
+ - lib/rails_daemons/thor.rb
94
+ - lib/rails_daemons/utils.rb
95
+ - lib/rails_daemons/version.rb
96
+ - lib/rails_daemons/worker.rb
97
+ - rails_daemons.gemspec
98
+ homepage: https://github.com/xronos-i-am/rails_daemons
99
+ licenses:
100
+ - MIT
101
+ metadata: {}
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubyforge_project:
118
+ rubygems_version: 2.4.4
119
+ signing_key:
120
+ specification_version: 4
121
+ summary: Daemons for Rails
122
+ test_files: []