Module: ActsAsService::ClassMethods

Defined in:
lib/acts_as_service.rb

Instance Method Summary collapse

Instance Method Details

#_display_nameObject

fetches the service’s name, using class name as default




199
200
201
# File 'lib/acts_as_service.rb', line 199

def _display_name
  respond_to?(:service_name) ? service_name : name.split("::").last
end

#_pidObject

the current process’ pid




277
278
279
# File 'lib/acts_as_service.rb', line 277

def _pid
  @@_pid ||= Process.pid
end

#_pid_file_contentObject

returns the entire contents of the pid file




257
258
259
260
261
262
263
264
# File 'lib/acts_as_service.rb', line 257

def _pid_file_content
  begin
    f = File.open(_pid_filename, 'r')
    return f.blank? ? f : f.read
  rescue Errno::ENOENT
    return nil
  end
end

#_pid_file_exists?Boolean

indicates if the pid file exists for this service


Returns:

  • (Boolean)


250
251
252
# File 'lib/acts_as_service.rb', line 250

def _pid_file_exists?
  File.exist?(_pid_filename)    
end

#_pid_file_pidObject

the pid found in the pidfile (if it exists, nil if it doesn’t)




269
270
271
272
# File 'lib/acts_as_service.rb', line 269

def _pid_file_pid
  /^(\d*)$/ =~ _pid_file_content
  return $1.blank? ? nil : $1.to_i
end

#_pid_file_process_running?Boolean

Checks to see if the process pointed to by the pid file is actually running Note that I couldn’t find a good way to check for the status of a different process by its pid, so this checks to see if the proces has a process group id, and if the process doesn’t exist, an exception is returned


Returns:

  • (Boolean)


288
289
290
291
292
293
294
295
# File 'lib/acts_as_service.rb', line 288

def _pid_file_process_running?
  begin
    Process.getpgid(_pid_file_pid)
    return true
  rescue
    return false
  end
end

#_pid_filenameObject

returns the pid filename




206
207
208
209
210
211
212
213
# File 'lib/acts_as_service.rb', line 206

def _pid_filename
  if respond_to?(:service_pid_filename)
    return service_pid_filename
  else
    return File.join(RAILS_ROOT, 'tmp', 'pids',
                     "#{_display_name.underscore.gsub(/\s+/, '_')}.pid")
  end
end

#_process_running?Boolean

returns true if the current process is the pid in the pid file. false otherwise


Returns:

  • (Boolean)


301
302
303
# File 'lib/acts_as_service.rb', line 301

def _process_running?
  return _pid == _pid_file_pid
end

#_shutting_down?Boolean

returns true if the service is in the process of shutting down. let subclasses access if they’d like


Returns:

  • (Boolean)


309
310
311
# File 'lib/acts_as_service.rb', line 309

def _shutting_down?
  ACTS_AS_SERVICE_SHUTTING_DOWN == _status
end

#_statusObject

the current status of the service. possible values:

ACTS_AS_SERVICE_STOPPED        : no service process is running
ACTS_AS_SERVICE_SHUTTING_DOWN  : process currently shutting down
ACTS_AS_SERVICE_RUNNING        : the current process is the service process
ACTS_AS_SERVICE_OTHER_RUNNING  : another pid is running the service
ACTS_AS_SERVICE_PID_NO_PROCESS : pidfile exists, but no process running



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/acts_as_service.rb', line 223

def _status
  _status = nil

  # logic:
  # if the pid file doesn't exist, it's stopped
  # otherwise, if 'shutting down', it's shutting down
  #            or if the pidfile's pid is running, another process is running
  #            or if the pidfile's pid matches this process, it's running
  #            otherwise, the pidfile's there but no one's running
  if !_pid_file_exists?  
    _status = ACTS_AS_SERVICE_STOPPED
  elsif Regexp.new(ACTS_AS_SERVICE_SHUTTING_DOWN) =~ _pid_file_content
    _status = ACTS_AS_SERVICE_SHUTTING_DOWN
  elsif _process_running?
    _status = ACTS_AS_SERVICE_RUNNING
  elsif _pid_file_process_running?
    _status = ACTS_AS_SERVICE_OTHER_RUNNING
  else
    _status = ACTS_AS_SERVICE_PID_NO_PROCESS
  end

  return _status
end

#restartObject

stops the current service process and runs a new one in the current process




156
157
158
159
# File 'lib/acts_as_service.rb', line 156

def restart
  stop
  start
end

#service_pidObject

method for outside consumption of the pid value. hopefully not tempting method name for class-writers to conflict with…




187
188
189
# File 'lib/acts_as_service.rb', line 187

def service_pid
  _pid_file_pid
end

#service_running?Boolean

method for outside consumption of status info. hopefully not tempting method name for class-writers to conflict with…


Returns:

  • (Boolean)


179
180
181
# File 'lib/acts_as_service.rb', line 179

def service_running?
  _status == ACTS_AS_SERVICE_RUNNING || _status == ACTS_AS_SERVICE_OTHER_RUNNING
end

#shutdownObject

initiate shutdown. call this from within perform_work_chunk if the service’s work is done and it should shut down (makes sense for cronjobs, say)




165
166
167
168
169
170
171
172
173
# File 'lib/acts_as_service.rb', line 165

def shutdown
  if self.respond_to?(:before_stop)
    before_stop
  end
  # change the pid file so the original process sees this 'stop' signal 
  File.open(_pid_filename, 'a') do |f|
    f.write("\n#{ACTS_AS_SERVICE_SHUTTING_DOWN}")
  end
end

#startObject

starts the process if it’s not already running




81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/acts_as_service.rb', line 81

def start
  begin
    if _status != ACTS_AS_SERVICE_STOPPED &&
         _status != ACTS_AS_SERVICE_PID_NO_PROCESS

      puts "#{_display_name} (#{_pid_file_pid}) is already running. Ignoring."
    else
      # clean out a stale pid
      if _status == ACTS_AS_SERVICE_PID_NO_PROCESS
        puts 'Pid file exists but process is not running. Removing old pid file.'
        File.delete(_pid_filename)
      end
      puts "Starting #{_display_name} (#{_pid})...."
      puts "Run #{name}.stop to stop\n\n"
      File.open(_pid_filename, 'w') {|f| f.write(_pid.to_s) }
      if self.respond_to?(:after_start)
        after_start
      end
      _sleep_till = Time.zone.now - 1
      while (_status == ACTS_AS_SERVICE_RUNNING)
        if Time.zone.now >= _sleep_till
          perform_work_chunk

          # only reset sleep till if asked to; otherwise, just perform next
          # work chunk right away (never change _sleep_till)
          if self.respond_to?(:sleep_time)
            _sleep_till = Time.zone.now + self.sleep_time
          end
        else
          _check_time_interval = if self.respond_to? :sleep_check_timeout
                                   self.sleep_check_timeout
                                 else
                                   SLEEP_CHECK_TIMEOUT
                                 end

          sleep [_check_time_interval, _sleep_till - Time.zone.now].min
        end
      end
      puts "Shutting down #{_display_name} (#{_pid})"
      File.delete(_pid_filename)
    end
  # if something happens, dump an error and clean up the pidfile if
  # it's owned by this process
  rescue Object => e
    puts "ERROR: #{e}\n#{e.respond_to?(:backtrace) ? e.backtrace.join("\n  ") : ''}"
    puts "Exiting (#{_pid})\n"
    if _process_running?
      File.delete(_pid_filename)
    end
  end
end

#stopObject

stops the process if it’s running




136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/acts_as_service.rb', line 136

def stop
  if _status == ACTS_AS_SERVICE_STOPPED
    puts "#{_display_name} is not running"
  elsif _status == ACTS_AS_SERVICE_PID_NO_PROCESS
    puts 'Pid file exists but process is not running. Removing old pid file.'
    File.delete(_pid_filename)
  else
    pid_to_stop = _pid_file_pid
    puts "Stopping #{_display_name} (#{pid_to_stop})...."
    shutdown
    while (_status != ACTS_AS_SERVICE_STOPPED)
      sleep(1)
    end
    puts "#{_display_name} (#{pid_to_stop}) stopped\n"
  end
end