Module: Anorexic

Defined in:
lib/anorexic.rb,
lib/anorexic/version.rb,
lib/anorexic/base/dsl.rb,
lib/anorexic/base/cache.rb,
lib/anorexic/base/events.rb,
lib/anorexic/base/timers.rb,
lib/anorexic/base/engine.rb,
lib/anorexic/base/logging.rb,
lib/anorexic/base/services.rb,
lib/anorexic/base/rack_app.rb,
lib/anorexic/handlers/stubs.rb,
lib/anorexic/handlers/route.rb,
lib/anorexic/base/io_reactor.rb,
lib/anorexic/base/connections.rb,
lib/anorexic/handlers/http_host.rb,
lib/anorexic/handlers/http_echo.rb,
lib/anorexic/server/helpers/http.rb,
lib/anorexic/handlers/http_router.rb,
lib/anorexic/handlers/magic_helpers.rb,
lib/anorexic/server/helpers/mime_types.rb,
lib/anorexic/handlers/controller_magic.rb,
lib/anorexic/server/protocols/websocket.rb,
lib/anorexic/server/services/no_service.rb,
lib/anorexic/server/services/ssl_service.rb,
lib/anorexic/server/protocols/ws_response.rb,
lib/anorexic/server/protocols/http_request.rb,
lib/anorexic/server/services/basic_service.rb,
lib/anorexic/server/protocols/http_protocol.rb,
lib/anorexic/server/protocols/http_response.rb

Overview

To make something new, we leap to the unknown.

Anorexic is a stand alone web services app, which supports RESTful HTTP and WebSockets.

Anorexic routes accept Regexp's (regular exceptions) for route paths. for example:

require 'anorexic'
listen
route(/[.]*/) {|request, response| response << "Your request, master: #{request.path}."}

The catch-all route (/[.]*/) has a shortcut '*', so it's possible to write:

require 'anorexic'
listen
route('*') {|request, response| response << "Your request, master: #{request.path}."}

Anorexic accepts an optional class object that can be passed using the `route` command. Passing a class object is especially useful for RESTful and WebSocket applications. read more at the Anorexic::StubWSCtrl and Anorexic::StubRESTCtrl documentation, which are stub classes used for testing routes.

require 'anorexic'
listen
route "*", Anorexic::StubRESTCtrl

class routes that have a specific path (including root, but not a catch-all or Regexp path) accept an implied `params` variable. the following path ('/'):

require 'anorexic'
listen
route "/", Anorexic::StubRESTCtrl
# client requests: /1
#  =>  Anorexic::StubRESTCtrl.new.show() # where params[:id] == 1

it is possible to use “magic” routes (i.e. `/resource/:type/(:id)/(:date)/[0-9]{8}/:foo`) and it is also possible to set the appropriate paramaters within the `before` method of the Conltroller.

Routes are handled in the order they are created. If overlapping routes exist, the first will execute first:

require 'anorexic'
listen
route('*') do |request, response|
   response << "Your request, master: #{request.path}." unless request.path.match /cats/
end
route('*') {|request, response| response.body << "Ahhh... I love cats!"}

all the examples above shuold be good to run from irb. updated examples can be found at the Readme file in the Github project: github.com/boazsegev/anorexic

thanks to Russ Olsen for his ideas for a DSL and his blog post at: www.jroller.com/rolsen/entry/building_a_dsl_in_ruby1

Defined Under Namespace

Modules: ControllerMagic, DSL, HTTP, HTTPEcho, MimeTypeHelper Classes: BasicService, CacheObject, Cookies, HTTPHost, HTTPProtocol, HTTPRequest, HTTPResponse, HTTPRouter, NoService, Route, SSLService, StubRESTCtrl, StubWSCtrl, TimedEvent, WSProtocol, WSResponse

Constant Summary collapse

VERSION =
"0.6.21"
CACHE_STORE =

contains the cached data, in the format: CACHE_STORE = CacheObject

{}
LOCK =
Mutex.new
CACHABLE =
%w{cache object slim haml css map js html scss sass coffee txt xml json yaml rb}
SHUTDOWN_CALLBACKS =

DANGER ZONE - Anorexic Engine. an Array containing all the shutdown callbacks that need to be called.

[]
EVENTS =

DANGER ZONE - Anorexic Engine. an Array containing all the current events.

[]
LOCKER =

DANGER ZONE - Anorexic Engine. the Mutex locker for the event machine.

Mutex.new
TIMERS =

DANGER ZONE - Anorexic Engine. an Array containing all the current events.

[]
T_LOCKER =

DANGER ZONE - Anorexic Engine. the Mutex locker for the event machine.

Mutex.new
SERVICES =

DANGER ZONE - Anorexic Engine. the services store

{}
S_LOCKER =

DANGER ZONE - Anorexic Engine. the services mutex

Mutex.new
IO_LOCKER =

DANGER ZONE - Anorexic Engine. the io reactor mutex

Mutex.new
IO_CONNECTION_DIC =

DANGER ZONE - Anorexic Engine. the connections store

{}
C_LOCKER =

DANGER ZONE - Anorexic Engine. the connections mutex

Mutex.new

Class Method Summary collapse

Class Method Details

.add_connection(io, params) ⇒ Object

Anorexic Engine, DO NOT CALL. adds a new connection to the connection stack


18
19
20
21
22
# File 'lib/anorexic/base/connections.rb', line 18

def add_connection io, params
  connection = params[:service_type].new(io, params)
  C_LOCKER.synchronize {IO_CONNECTION_DIC[connection.socket] = connection} if connection
  callback(connection, :on_message)
end

.add_service(port, paramaters = {}) ⇒ Object

public API to add a service to the framework. accepts:

port

port number

parameters

a hash of paramaters that are passed on to the service for handling (and from there, service dependent, to the protocol and/or handler).

parameters are any of the following:

host

the host name. defaults to any host not explicitly defined (a catch-all).

alias

a String or an Array of Strings which represent alternative host names (i.e. `alias: [“admin.google.com”, “admin.gmail.com”]`).

root

the public root folder. if this is defined, static files will be served from the location.

assets

the assets root folder. defaults to nil (no assets support). if the path is defined, assets will be served from `/assets/…` (or the public_asset path defined) before any static files. assets will not be served if the file in the /public/assets folder if up to date (a rendering attempt will be made for systems that allow file writing).

assets_public

the assets public uri location (uri format, NOT a file path). defaults to `/assets`. assets will be saved (or rendered) to the assets public folder and served as static files.

assets_callback

a method that accepts one parameters: `request` and renders any custom assets. the method should return `false` unless it has created a response object (`response = Anorexic::HTTPResponse.new(request)`) and sent a response to the client using `response.finish`.

save_assets

saves the rendered assets to the filesystem, under the public folder. defaults to false.

templates

the templates root folder. defaults to nil (no template support). templates can be rendered by a Controller class, using the `render` method.

ssl

if true, an SSL service will be attempted. if no certificate is defined, an attempt will be made to create a self signed certificate.

ssl_key

the public key for the SSL service.

ssl_cert

the certificate for the SSL service.

assets:

assets support will render `.sass`, `.scss` and `.coffee` and save them as local files (`.css`, `.css`, and `.js` respectively) before sending them as static files. if it is impossible to write the files, they will be rendered dynamically for every request (it would be better to render them before-hand).

templates:

templates can be either an ERB file on a Haml file.


41
42
43
44
45
46
47
48
49
# File 'lib/anorexic/base/services.rb', line 41

def add_service port, paramaters = {}
  paramaters[:port] ||= port
  paramaters[:service_type] ||= ( paramaters[:ssl] ? SSLService : BasicService)
  service = nil
  service = paramaters[:service_type].create_service(port, paramaters) unless ( defined?(BUILDING_ANOREXIC_TEMPLATE) || defined?(ANOREXIC_ON_RACK) )
  S_LOCKER.synchronize {SERVICES[service] = paramaters}
  info "Started listening on port #{port}."
  true
end

.cache_data(filename, data, mtime = Time.now) ⇒ Object

places data into the cache, under an identifier ( file name ).


53
54
55
56
# File 'lib/anorexic/base/cache.rb', line 53

def cache_data filename, data, mtime = Time.now
  LOCK.synchronize { CACHE_STORE[filename] = CacheObject.new( data, mtime )  }
  data
end

.cache_needs_update?(filename) ⇒ Boolean

returns true if the file has been update since data was last cached.

Returns:

  • (Boolean)

73
74
75
76
# File 'lib/anorexic/base/cache.rb', line 73

def cache_needs_update? filename
  return true if CACHE_STORE[filename].nil? || CACHE_STORE[filename].mtime < File.mtime(filename)
  false
end

.cached?(filename) ⇒ Boolean

returns true if the filename is cached.

Returns:

  • (Boolean)

63
64
65
# File 'lib/anorexic/base/cache.rb', line 63

def cached? filename
  !CACHE_STORE[filename].nil?
end

.call(env) ⇒ Object

Anorexic dresses up for Rack - this is a watered down version missing some features (such as flash and WebSockets). a full featured Anorexic app, with WebSockets, requires the use of the Anorexic server (the built-in server)


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/anorexic/base/rack_app.rb', line 13

def call env
  raise "No Anorexic Services" unless Anorexic::SERVICES[0]
  Object.const_set('ANOREXIC_ON_RACK', true) unless defined? ANOREXIC_ON_RACK

  # re-encode to utf-8, as it's all BINARY encoding at first
  env["rack.input"].rewind
  env['rack.input'] = StringIO.new env["rack.input"].read.encode("utf-8", "binary", invalid: :replace, undef: :replace, replace: '')      
  env.each do |k, v|
    if k.to_s.match /^[A-Z]/
      if v.is_a?(String) && !v.frozen?
        v.force_encoding("binary").encode!("utf-8", "binary", invalid: :replace, undef: :replace, replace: '') unless v.force_encoding("utf-8").valid_encoding?
      end
    end
  end
  # re-key params
  # new_params = {}
  # env[:params].each {|k,v| HTTP.add_param_to_hash k, v, new_params}
  # env[:params] = new_params

  # make hashes magical
  make_hash_accept_symbols(env)

  # use Anorexic Cookies
  env["rack.request.cookie_string"] = env["HTTP_COOKIE"]
  env["rack.request.cookie_hash"] = Anorexic::Cookies.new.update(env["rack.request.cookie_hash"] || {})

  # chomp path
  env["PATH_INFO"].chomp! '/'

  # get response
  response = Anorexic::SERVICES[0][1][:handler].call env

  return response if response.is_a?(Array)

  response.finish
  response.fix_headers
  headers = response.headers
  # set cookie headers
  headers.delete 'transfer-encoding'
  headers.delete 'connection'
  unless response.cookies.empty?
    headers["Set-Cookie"] = []
    response.cookies.each {|k,v| headers["Set-Cookie"] << ("#{k.to_s}=#{v.to_s}")}
  end
  [response.status, headers, response.body]
end

.callback(object, method, *args, &block) ⇒ Object

Public API. creates an asynchronous call to a method, with an optional callback: demo use: `callback( Kernel, :sleep, 1 ) { puts “this is a demo” }` callback sets an asynchronous method call with a callback.

paramaters:

object

the object holding the method to be called (use `Kernel` for global methods).

method

the method's name (Symbol). this is the method that will be called.

*arguments

any additional arguments that should be sent to the method (the main method, not the callback).

this method also accepts an optional block (the callback) that will be called once the main method has completed. the block will recieve the method's returned value.

i.e.

puts 'while we wait for my work to complete, can you tell me your name?'
Anorexic.callback(STDIO, :gets) do |name|
     puts "thank you, #{name}. I'm working on your request as we speak."
end
# do more work without waiting for the chat to start nor complete.

55
56
57
# File 'lib/anorexic/base/events.rb', line 55

def callback object, method, *args, &block
  push_event object.method(method), *args, &block
end

.clear_connectionsObject

clears closed connections from the stack


29
30
31
# File 'lib/anorexic/base/connections.rb', line 29

def clear_connections
  C_LOCKER.synchronize { IO_CONNECTION_DIC.values.each {|c| callback c, :on_disconnect if c.disconnected? || c.timedout? } }
end

.create_logger(log_file = STDOUT, copy_to_stdout = false) ⇒ Object Also known as: set_logger

create and set the logger object. accepts:

log_file

a log file name to be used for logging

copy_to_stdout

if false, log will only log to file. defaults to true.


23
24
25
26
27
28
# File 'lib/anorexic/base/logging.rb', line 23

def create_logger log_file = STDOUT, copy_to_stdout = false
  @copy_to_stdout = false
  @copy_to_stdout = ::Logger.new(STDOUT) if copy_to_stdout
  @logger = ::Logger.new(log_file)
  @logger
end

.error(line) ⇒ Object

logs errors


53
54
55
56
# File 'lib/anorexic/base/logging.rb', line 53

def error line
  @logger.error line
  @copy_to_stdout.error line if @copy_to_stdout
end

.events?Boolean

returns true if there are any unhandled events

Returns:

  • (Boolean)

17
18
19
# File 'lib/anorexic/base/events.rb', line 17

def events?
  LOCKER.synchronize {!EVENTS.empty?}
end

.fatal(line) ⇒ Object

logs a fatal error


58
59
60
61
# File 'lib/anorexic/base/logging.rb', line 58

def fatal line
  @logger.fatal line
  @copy_to_stdout.fatal line if @copy_to_stdout
end

.file_exists?(filename) ⇒ Boolean

returns true if the file exists on disk or in the cache.

Returns:

  • (Boolean)

68
69
70
# File 'lib/anorexic/base/cache.rb', line 68

def file_exists? filename
  (CACHE_STORE[filename] || File.exists?(filename)) ? true : false
end

.file_mtime(filename) ⇒ Object

review a file's modification time


29
30
31
32
# File 'lib/anorexic/base/cache.rb', line 29

def file_mtime filename
  return CACHE_STORE[filename].mtime if cached?(filename)
  File.mtime(filename)
end

.fire_eventObject

Anorexic Engine, DO NOT CALL. pulls an event from the event's stack and processes it.


71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/anorexic/base/events.rb', line 71

def fire_event
  event = LOCKER.synchronize {EVENTS.shift}
  return false unless event
  begin
    event[0].call(*event[1])
  rescue OpenSSL::SSL::SSLError => e
    warn "SSL Bump - SSL Certificate refused?"
  rescue Exception => e
    raise if e.is_a?(SignalException) || e.is_a?(SystemExit)
    error e
  end
  true
end

.fire_timersObject

DANGER ZONE - Used by the Anorexic engine to review timed events and push them to the event stack


67
68
69
70
# File 'lib/anorexic/base/timers.rb', line 67

def fire_timers
  return false if T_LOCKER.locked?
  T_LOCKER.synchronize { TIMERS.delete_if {|t| t.done? } }
end

.get_cached(filename) ⇒ Object

Get data from the cache. will throw an exception if there is no data in the cache.


58
59
60
# File 'lib/anorexic/base/cache.rb', line 58

def get_cached filename
  CACHE_STORE[filename].data # if CACHE_STORE[filename]
end

.idle_sleepObject

Anorexic event cycle settings: how long to wait for IO activity before forcing another cycle.

No timing methods will be called during this interval.

get the current idle setting


20
21
22
# File 'lib/anorexic/base/engine.rb', line 20

def idle_sleep
  @idle_sleep ||= 0.1
end

.idle_sleep=(value) ⇒ Object

Anorexic event cycle settings: how long to wait for IO activity before forcing another cycle.

No timing methods will be called during this interval.

set the current idle setting


28
29
30
# File 'lib/anorexic/base/engine.rb', line 28

def idle_sleep= value
  @idle_sleep = value
end

.info(line) ⇒ Object

logs info


43
44
45
46
# File 'lib/anorexic/base/logging.rb', line 43

def info line
  @logger.info line
  @copy_to_stdout.info line if @copy_to_stdout
end

.io_reactorObject

Anorexic Engine, DO NOT CALL. waits on IO and pushes events. all threads hang while reactor is active (unless events are already 'in the pipe'.)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/anorexic/base/io_reactor.rb', line 11

def io_reactor
  IO_LOCKER.synchronize do
    return false unless EVENTS.empty?
    united = SERVICES.keys + IO_CONNECTION_DIC.keys
    return false if united.empty?
    io_r = (IO.select(united, nil, united, idle_sleep) ) #rescue false)
    if io_r
      io_r[0].each do |io|
        if SERVICES[io]
          begin
            connection = io.accept_nonblock
            callback Anorexic, :add_connection, connection, SERVICES[io]
          rescue Errno::EWOULDBLOCK => e

          rescue Exception => e
            error e
            # SERVICES.delete s if s.closed?
          end
        elsif IO_CONNECTION_DIC[io]
          callback(IO_CONNECTION_DIC[io], :on_message)
        else
          IO_CONNECTION_DIC.delete(io)
          SERVICES.delete(io)
        end
      end
      io_r[2].each { |io| (IO_CONNECTION_DIC.delete(io) || SERVICES.delete(io)).close rescue true }
    end
  end
  true
end

.load_file(filename) ⇒ Object

load the file from the cache (if exists) or the file system (if it doesn't)


25
26
27
# File 'lib/anorexic/base/cache.rb', line 25

def load_file filename
  cached?(filename) ? get_cached(filename) : reload_file(filename)
end

.log(line) ⇒ Object

logs info


38
39
40
41
# File 'lib/anorexic/base/logging.rb', line 38

def log line
  @logger.info line
  @copy_to_stdout.info line if @copy_to_stdout
end

.log_raw(line) ⇒ Object

writes a raw line to the log\


32
33
34
35
# File 'lib/anorexic/base/logging.rb', line 32

def log_raw line
  @logger << line
  @copy_to_stdout << line if @copy_to_stdout
end

.loggerObject

gets the active logger


12
13
14
# File 'lib/anorexic/base/logging.rb', line 12

def logger
  @logger
end

.logger_copyObject

gets the active STDOUT copy, if exists


16
17
18
# File 'lib/anorexic/base/logging.rb', line 16

def logger_copy
  @copy_to_stdout
end

.make_hash_accept_symbols(hash) ⇒ Object

tweeks a hash object to read both :symbols and strings (similar to Rails but without).


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/anorexic/handlers/magic_helpers.rb', line 26

def self.make_hash_accept_symbols hash
  @magic_hash_proc ||= Proc.new do |hs,k|
    if k.is_a?(Symbol) && hs.has_key?( k.to_s)
      hs[k.to_s]
    elsif k.is_a?(String) && hs.has_key?( k.to_sym)
      hs[k.to_sym]
    end
  end
  hash.default_proc = @magic_hash_proc
  hash.values.each do |v|
    if v.is_a?(Hash)
      make_hash_accept_symbols v
    end
  end
end

.max_threadsObject

Anorexic event cycle settings: gets how many worker threads Anorexic will run.


7
8
9
# File 'lib/anorexic/base/engine.rb', line 7

def max_threads
  @max_threads ||= 16
end

.max_threads=(value) ⇒ Object

Anorexic event cycle settings: sets how many worker threads Anorexic will run.


11
12
13
# File 'lib/anorexic/base/engine.rb', line 11

def max_threads= value
  @max_threads = value
end

.on_shutdown(object = nil, method = nil, *args, &block) ⇒ Object

Public API. adds a callback to be called once the services were shut down. see: callback for more info.


60
61
62
63
64
65
66
67
68
# File 'lib/anorexic/base/events.rb', line 60

def on_shutdown object=nil, method=nil, *args, &block
  if block && !object && !method
    LOCKER.synchronize {SHUTDOWN_CALLBACKS << [block, args]}
  elsif block
    LOCKER.synchronize {SHUTDOWN_CALLBACKS << [(Proc.new {|*a| block.call(object.method(method).call(*a))} ), args]}
  elsif object && method
    LOCKER.synchronize {SHUTDOWN_CALLBACKS << [object.method(method), args]}
  end
end

.push_event(handler, *args, &block) ⇒ Object

Public API. pushes an event to the event's stack

accepts:

handler

an object that answers to `call`, usually a Proc or a method.

*arg

any arguments that will be passed to the handler's `call` method.

if a block is passed along, it will be used as a callback: the block will be called with the values returned by the handler's `call` method.


28
29
30
31
32
33
34
# File 'lib/anorexic/base/events.rb', line 28

def push_event handler, *args, &block
  if block
    LOCKER.synchronize {EVENTS << [(Proc.new {|a| Anorexic.push_event block, handler.call(*a)} ), args]}
  else
    LOCKER.synchronize {EVENTS << [handler, args]}
  end
end

.reload_file(filename) ⇒ Object

force a file onto the cache (only if it is cachable - otherwise will load the file but will not cache it).


35
36
37
38
39
40
41
# File 'lib/anorexic/base/cache.rb', line 35

def reload_file filename
  if CACHABLE.include? filename.match(/\.([^\.]+)$/)[1]
    return cache_data filename, IO.read(filename), File.mtime(filename)
  else
    return IO.read(filename)
  end
end

.remove_connection(connection) ⇒ Object

Anorexic Engine, DO NOT CALL. removes a connection from the connection stack


24
25
26
# File 'lib/anorexic/base/connections.rb', line 24

def remove_connection connection
  C_LOCKER.synchronize { IO_CONNECTION_DIC.delete connection.socket }
end

.run_after(seconds, handler, *args, &block) ⇒ Object

pushes a timed event to the timers's stack

accepts:

seconds

the minimal amount of seconds to wait before calling the handler's `call` method.

handler

an object that answers to `call`, usually a Proc or a method.

*arg

any arguments that will be passed to the handler's `call` method.

if a block is passed along, it will be used as a callback: the block will be called with the values returned by the handler's `call` method.


50
51
52
# File 'lib/anorexic/base/timers.rb', line 50

def run_after seconds, handler, *args, &block
  T_LOCKER.synchronize {TIMERS << TimedEvent.new(seconds, false, handler, args, block); TIMERS.last}
end

.run_every(seconds, handler, *args, &block) ⇒ Object

pushes a repeated timed event to the timers's stack

accepts:

seconds

the minimal amount of seconds to wait before calling the handler's `call` method.

handler

an object that answers to `call`, usually a Proc or a method.

*arg

any arguments that will be passed to the handler's `call` method.

if a block is passed along, it will be used as a callback: the block will be called with the values returned by the handler's `call` method.


62
63
64
# File 'lib/anorexic/base/timers.rb', line 62

def run_every seconds, handler, *args, &block
  T_LOCKER.synchronize {TIMERS << TimedEvent.new(seconds, true, handler, args, block); TIMERS.last}
end

.save_file(filename, data, save_to_disk = false) ⇒ Object

places data into the cache, and attempts to save the data to a file name.


43
44
45
46
47
48
49
50
51
# File 'lib/anorexic/base/cache.rb', line 43

def save_file filename, data, save_to_disk = false
  cache_data filename, data if CACHABLE.include? filename.match(/\.([^\.]+)$/)[1]
  begin
    IO.write filename, data if save_to_disk
  rescue Exception => e
    Anorexic.warn("File couldn't be written (#{filename}) - file system error?")
  end
  data
end

.start_servicesObject

Anorexic Engine, DO NOT CALL. creates the thread pool and starts cycling through the events.


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/anorexic/base/engine.rb', line 33

def start_services
  # prepare threads
  exit_flag = false
  threads = []
  run_every(5 , Anorexic.method(:clear_connections)) #{info "Cleared inactive Connections"}
  run_every 3600 , GC.method(:start)
  # run_every( 1 , Proc.new() { Anorexic.info "#{IO_CONNECTION_DIC.length} active connections ( #{ IO_CONNECTION_DIC.select{|k,v| v.protocol.is_a?(WSProtocol)} .length } websockets)." })
  (max_threads).times {  Thread.new { thread_cycle until exit_flag }  }    

  # Thread.new { check_connections until SERVICES.empty? }
  #...
  # set signal tarps
  trap('INT'){ exit_flag = true; raise "close Anorexic" }
  trap('TERM'){ exit_flag = true; raise "close Anorexic" }
  puts 'Services running. Press ^C to stop'
  # sleep until trap raises exception (cycling might cause the main thread to ignor signals and lose attention)
  (sleep unless SERVICES.empty?) rescue true
  # start shutdown.
  exit_flag = true
  # set new tarps
  trap('INT'){ puts 'Forced exit.'; Kernel.exit }#rescue true}
  trap('TERM'){ puts 'Forced exit.'; Kernel.exit }#rescue true }
  puts 'Started shutdown process. Press ^C to force quit.'
  # shut down listening sockets
  stop_services
  # disconnect active connections
  stop_connections
  # cycle down threads
  info "Waiting for workers to cycle down"
  threads.each {|t| t.join if t.alive?}

  # rundown any active events
  thread_cycle

  # call shutdown callbacks
  SHUTDOWN_CALLBACKS.each {|s| s[0].call(*s[1]) }
  SHUTDOWN_CALLBACKS.clear

  # return exit code?
  0
end

.stop_connectionsObject

Anorexic Engine, DO NOT CALL. disconnectes all active connections


12
13
14
15
# File 'lib/anorexic/base/connections.rb', line 12

def stop_connections
  log 'Stopping connections'
  C_LOCKER.synchronize {IO_CONNECTION_DIC.values.each {|c| c.timeout = -1; callback c, :on_disconnect unless c.disconnected?} ; IO_CONNECTION_DIC.clear}
end

.stop_servicesObject

Anorexic Engine, DO NOT CALL. stops all services - active connection will remain open until completion.


52
53
54
55
# File 'lib/anorexic/base/services.rb', line 52

def stop_services
  info 'Stopping services'
  S_LOCKER.synchronize {SERVICES.each {|s, p| s.close rescue true; info "Stoped listening on port #{p[:port]}"}; SERVICES.clear }
end

.thread_cycle(flag = 0) ⇒ Object

Anorexic Engine, DO NOT CALL. runs one thread cycle


76
77
78
79
80
81
82
83
84
# File 'lib/anorexic/base/engine.rb', line 76

def self.thread_cycle flag = 0
  io_reactor rescue false # stop_connections
  true while fire_event
  fire_timers

  rescue Exception => e

  error e
end

.timers?Boolean

returns true if there are any unhandled events

Returns:

  • (Boolean)

38
39
40
# File 'lib/anorexic/base/timers.rb', line 38

def timers?
  T_LOCKER.synchronize {!TIMERS.empty?}
end

.warn(line) ⇒ Object

logs warning


48
49
50
51
# File 'lib/anorexic/base/logging.rb', line 48

def warn line
  @logger.warn line
  @copy_to_stdout.warn line if @copy_to_stdout
end