Class: Newman::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/newman/server.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings, mailer) ⇒ Server

To initialize a ‘Newman::Server` object, a settings object and mailer object must be provided.

Instantiating a server object directly can be useful for building live integration tests, or for building cron jobs which process email periodically rather than in a busy-wait loop. See one of Newman’s [live tests](github.com/mendicant-university/newman/blob/master/examples/live_test.rb) for an example of how this approach works.



118
119
120
121
122
# File 'lib/newman/server.rb', line 118

def initialize(settings, mailer)
  self.settings = settings
  self.mailer   = mailer
  self.apps     = []
end

Instance Attribute Details

#appsObject

These accessors are mostly meant for use with server objects under test mode, or server objects that have been explicitly instantiated. If you are using ‘Newman::Server.simple` to run your apps, it’s safe to treat these as an implementation detail; all important data will get passed down into your apps on each ‘tick`.



132
133
134
# File 'lib/newman/server.rb', line 132

def apps
  @apps
end

#loggerObject

Returns the logger object that was set via ‘Newman::Server#logger=`, or delegates to `default_logger` if no custom logger was provided.



140
141
142
# File 'lib/newman/server.rb', line 140

def logger
  @logger || default_logger
end

#mailerObject

These accessors are mostly meant for use with server objects under test mode, or server objects that have been explicitly instantiated. If you are using ‘Newman::Server.simple` to run your apps, it’s safe to treat these as an implementation detail; all important data will get passed down into your apps on each ‘tick`.



132
133
134
# File 'lib/newman/server.rb', line 132

def mailer
  @mailer
end

#settingsObject

These accessors are mostly meant for use with server objects under test mode, or server objects that have been explicitly instantiated. If you are using ‘Newman::Server.simple` to run your apps, it’s safe to treat these as an implementation detail; all important data will get passed down into your apps on each ‘tick`.



132
133
134
# File 'lib/newman/server.rb', line 132

def settings
  @settings
end

Class Method Details

.simple(app, settings_file) ⇒ Object

‘Newman::Server#simple` is the same as `Newman::Server#simple!`, but automatically starts an infinite polling loop.



66
67
68
69
# File 'lib/newman/server.rb', line 66

def self.simple(app, settings_file)
  server = simple!(app, settings_file)
  server.run
end

.simple!(app, settings_file) ⇒ Object

‘Newman::Server.simple!` automatically generates a `Newman::Mailer` object and `Newman::Settings` object from the provided `settings_file`. These objects are then passed on to `Newman::Server.new` and a server instance is created.

The following example demonstrates how to use this method:

ping_pong = Newman::Application.new do
  subject(:match, "ping") do
    respond(:subject => "pong")
  end

  default do
    respond(:subject => "You missed the ball!")
  end
end 

s = Newman::Server.simple!(ping_pong, "config/environment.rb")
# call s.tick or s.run at some later point.

Given a proper configuration file, this will make it possible to easily get your applications up and running with simple request and response logging enabled.



52
53
54
55
56
57
58
59
# File 'lib/newman/server.rb', line 52

def self.simple!(app, settings_file)
  settings     = Settings.from_file(settings_file)
  mailer       = Mailer.new(settings)
  server       = new(settings, mailer)
  server.apps  = [RequestLogger, app, ResponseLogger]

  server
end

.test_mode(settings_file) ⇒ Object

‘Newman::Server.test_mode` automatically generates a `Newman::TestMailer` object and `Newman::Settings` object from the provided `settings_file`. These objects are then passed on to `Newman::Server.new` and a server instance which is preconfigured for use in integration testing is returned.

Using the application from the ‘Newman::Server.simple!` documentation above, it’d be possible to write a simple integration test using this method in the following way:

server = Newman::Server.test_mode("config/environment.rb")
server.apps << ping_pong

mailer = server.mailer
mailer.deliver_message(:to      => "[email protected]",
                       :subject => "ping)

server.tick

mailer.messages.first.subject.must_equal("pong")

It’s worth mentioning that although ‘Newman::Server.test_mode` is part of Newman’s external interface, the ‘Newman::TestMailer` object is considered part of its internals. This is due to some ugly issues with global state and the overall brittleness of the current implementation. Expect a bit of weirdness if you plan to use this feature, at least until we improve upon it.



100
101
102
103
104
105
# File 'lib/newman/server.rb', line 100

def self.test_mode(settings_file)
  settings = Settings.from_file(settings_file)
  mailer   = TestMailer.new(settings)

  new(settings, mailer)
end

Instance Method Details

#runObject

‘Newman::Server.run` kicks off a busy wait loop, alternating between calling `Newman::Server.tick` and sleeping for the amount of time specified by `settings.service.polling_interval`. We originally planned to use an EventMachine periodic timer here to potentially make running several servers within a single process easier, but had trouble coming up with a use case that made the extra dependency worth it.



153
154
155
156
157
158
# File 'lib/newman/server.rb', line 153

def run
  loop do
    tick
    sleep settings.service.polling_interval
  end
end

#tickObject

‘Newman::Server.tick` runs the following sequence for each incoming request.

1) A response is generated with the TO field set to the FROM field of the request, and the FROM field set to ‘settings.service.default_sender`. Applications can change these values later, but these are sensible defaults that work for most common needs.

2) The list of ‘apps` is iterated over sequentially, and each application’s ‘call` method is invoked with a parameters hash which include the `request` email, the `response` email, the `settings` object being used by the server, and the `logger` object being used by the server.

2a) If any application raises an exception, that exception is caught and the processing of the current request is halted. Details about the failure are logged and if ‘settings.service.raise_exceptions` is enabled, the exception is re-raised, typically taking the server down with it. This setting is off by default.

2b) If there are any server errors (such as an error retrieving messages via IMAP), those errors are logged and re-raised, taking the server down. Currently, you should use a process watcher to restart Newman to protect against such failures, but be careful about restarting without knowing what went wrong!

3) Assuming an exception is not encountered, the response is delivered.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/newman/server.rb', line 190

def tick         
  mailer.messages.each do |request|        
    response = mailer.new_message(:to   => request.from, 
                                  :from => settings.service.default_sender)
    
    begin
      apps.each do |app|
        app.call(:request  => request, 
                 :response => response, 
                 :settings => settings,
                 :logger   => logger)
      end
    rescue StandardError => e
      logger.info("FAIL") { e.to_s }
      logger.debug("FAIL") { "#{e.inspect}\n"+e.backtrace.join("\n  ") }

      if settings.service.raise_exceptions
        raise
      else
        next
      end
    end

    response.deliver
  end
rescue Exception => e
  logger.fatal("Caught exception: #{e}\n\n#{e.backtrace.join("\n")}")
  raise
end