Class: SMS::Router
- Inherits:
-
Object
- Object
- SMS::Router
- Defined in:
- lib/rubysms/router.rb
Instance Attribute Summary collapse
-
#apps ⇒ Object
readonly
Returns the value of attribute apps.
-
#backends ⇒ Object
readonly
Returns the value of attribute backends.
Instance Method Summary collapse
-
#add(something) ⇒ Object
Accepts an SMS::Backend::Base or SMS::App instance, which is stored until serve_forever is called.
-
#add_app(app) ⇒ Object
Adds an SMS application (which is usually an instance of a subclass of SMS::App, but anything’s fine, so long as it quacks the right way) to this router, which will be started once serve_forever is called.
-
#add_backend(backend, *args) ⇒ Object
Adds an SMS backend (which MUST be is_a?(SMS::Backend::Base), for now), or a symbol representing a loadable SMS backend, which is passed on to SMS::Backend.create (along with *args) to be required and initialized.
-
#incoming(msg) ⇒ Object
Relays a given incoming message from a specific backend to all applications.
-
#initialize ⇒ Router
constructor
A new instance of Router.
-
#log(*args) ⇒ Object
proxy methods to pass events to the logger with the pretty.
- #log_exception(error, prefix_message = nil) ⇒ Object
- #log_with_time(*args) ⇒ Object
-
#outgoing(msg) ⇒ Object
Notifies each application of an outgoing message, and logs it.
-
#serve_forever ⇒ Object
Starts listening for incoming messages on all backends, and never returns.
Constructor Details
Instance Attribute Details
#apps ⇒ Object (readonly)
Returns the value of attribute apps.
6 7 8 |
# File 'lib/rubysms/router.rb', line 6 def apps @apps end |
#backends ⇒ Object (readonly)
Returns the value of attribute backends.
6 7 8 |
# File 'lib/rubysms/router.rb', line 6 def backends @backends end |
Instance Method Details
#add(something) ⇒ Object
Accepts an SMS::Backend::Base or SMS::App instance, which is stored until serve_forever is called. DEPRECATED because it’s confusing and magical.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/rubysms/router.rb', line 100 def add(something) log "Router#add is deprecated; use " +\ "#add_backend and #add_app", :warn if something.is_a? SMS::Backend::Base @backends.push(something) elsif something.is_a? SMS::App @apps.push(something) else raise RuntimeError, "Router#add doesn't know what " +\ "to do with a #{something.klass}" end # store a reference back to this router in # the app or backend, so it can talk back something.router = self end |
#add_app(app) ⇒ Object
Adds an SMS application (which is usually an instance of a subclass of SMS::App, but anything’s fine, so long as it quacks the right way) to this router, which will be started once serve_forever is called.
124 125 126 127 |
# File 'lib/rubysms/router.rb', line 124 def add_app(app) @apps.push(app) app.router = self end |
#add_backend(backend, *args) ⇒ Object
Adds an SMS backend (which MUST be is_a?(SMS::Backend::Base), for now), or a symbol representing a loadable SMS backend, which is passed on to SMS::Backend.create (along with *args) to be required and initialized. This only really works with built-in backends, for now, but is useful for initializing those:
# start serving with a single
# http backend on port 9000
router = SMS::Router.new
router.add_backend(:HTTP, 9000)
router.serve_forever
# start serving on two gsm
# modems with pin numbers
router = SMS::Router.new
router.add_backend(:GSM, "/dev/ttyS0", 1234)
router.add_backend(:GSM, "/dev/ttyS1", 5678)
router.serve_forever
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/rubysms/router.rb', line 148 def add_backend(backend, *args) # if a backend object was given, add it to this router # TODO: this modifies the argument just slightly. would # it be better to duplicate the object first? if backend.is_a?(SMS::Backend::Base) @backends.push(backend) backend.router = self # if it's a named backend, spawn it (along # with the optional arguments) and recurse elsif backend.is_a?(Symbol) or backend.is_a?(String) add_backend SMS::Backend.create(backend.to_sym, nil, *args) # no idea what this # backend is = boom else raise RuntimeError, "Router#add_backend doesn't know what " +\ "to do with #{backend} (#{backend.klass})" end end |
#incoming(msg) ⇒ Object
Relays a given incoming message from a specific backend to all applications.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/rubysms/router.rb', line 173 def incoming(msg) log_with_time "[#{msg.backend.label}] #{msg.sender.key}: #{msg.text} (#{msg.text.length})", :in # iterate apps starting with # the highest numeric priority sorted = @apps.sort_by { |a| a.priority }.reverse # notify each application of the message. # they may or may not respond to it, and # may throw the :halt symbol to stop the # notifying further apps. this is useful # in conjunction with App.priority catch(:halt) do sorted.each do |app| begin catch(:continue) do app.incoming msg # if the app responded to the message, cancel # further processing - unless :continue was # thrown, which jumps over this check unless msg.responses.empty? throw :halt end end # something went boom in the app # log it, and continue with the next rescue StandardError => err log_exception(err) # if msg.responses.empty? # msg.respond("Sorry, there was an error while processing your message.") # end end end end end |
#log(*args) ⇒ Object
proxy methods to pass events to the logger with the pretty
19 20 21 |
# File 'lib/rubysms/router.rb', line 19 def log(*args) @log.event(*args) end |
#log_exception(error, prefix_message = nil) ⇒ Object
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 |
# File 'lib/rubysms/router.rb', line 27 def log_exception(error, =nil) msgs = [error.class, error.] # add each line until the current frame is within # rubysms (the remainder will just be from gems) catch(:done) do error.backtrace.each do |line| if line =~ /^#{SMS::Root}/ throw :done end # still within the application, # so add the frame to the log msgs.push(" " + line) end end # if a prefix was provided (to give a litle # more info on what went wrong), prepend it # to the output and indent the rest unless .nil? msgs = [] + msgs.collect do |msg| " " + msg.to_s end end @log.event msgs, :warn end |
#log_with_time(*args) ⇒ Object
23 24 25 |
# File 'lib/rubysms/router.rb', line 23 def log_with_time(*args) @log.event_with_time(*args) end |
#outgoing(msg) ⇒ Object
Notifies each application of an outgoing message, and logs it. Should be called by all backends prior to sending.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/rubysms/router.rb', line 214 def outgoing(msg) log_with_time "[#{msg.backend.label}] #{msg.recipient.key}: #{msg.text} (#{msg.text.length})", :out log("Outgoing message exceeds 140 characters", :warn) if msg.text.length > 140 # iterate apps starting with the loest numeric priority (the # opposite to #incoming,so a :highest priority app gets the # first look at incoming, and the last word on what goes out) sorted = @apps.sort_by { |a| a.priority } # notify each app of the outgoing sms # note that the sending can still fail sorted.each do |app| app.outgoing msg end end |
#serve_forever ⇒ Object
Starts listening for incoming messages on all backends, and never returns.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rubysms/router.rb', line 59 def serve_forever # (attempt to) start up each # backend in a separate thread @backends.each do |b| Thread.new do b.start end end # applications don't need their own # thread (they're notified in serial), # but do have a #start method @apps.each { |a| a.start } # catch interrupts and display a nice message (rather than # a backtrace). to avoid seeing control characters (^C) in # the output, disable the "echoctl" option in your terminal # (i added "stty -echoctl" to my .bashrc) trap("INT") do log "Shutting down", :stop # fire the "stop" method of # each application and backend # before terminating the process (@backends + @apps).each do |inst| inst.stop end exit end # block until ctrl+c while true do sleep 5 end end |