messed 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Gemfile +8 -0
  2. data/README.rdoc +22 -0
  3. data/Rakefile +76 -0
  4. data/VERSION +1 -0
  5. data/application_spec/Rakefile +12 -0
  6. data/application_spec/app/runner.rb +3 -0
  7. data/application_spec/bin/runner +18 -0
  8. data/application_spec/bin/status +36 -0
  9. data/application_spec/bin/web +29 -0
  10. data/application_spec/config/environment.rb +8 -0
  11. data/application_spec/config/environments/development.rb +0 -0
  12. data/application_spec/log/development.log +6 -0
  13. data/application_spec/spec/application_spec.rb +12 -0
  14. data/application_spec/spec/spec.opts +7 -0
  15. data/application_spec/spec/spec_helper.rb +42 -0
  16. data/bin/messed +9 -0
  17. data/lib/messed/booter.rb +114 -0
  18. data/lib/messed/configuration.rb +94 -0
  19. data/lib/messed/controller/helper.rb +8 -0
  20. data/lib/messed/controller/processing.rb +22 -0
  21. data/lib/messed/controller/respond.rb +48 -0
  22. data/lib/messed/controller.rb +17 -0
  23. data/lib/messed/em_runner.rb +50 -0
  24. data/lib/messed/interface/adapter/sms.rb +4 -0
  25. data/lib/messed/interface/adapter/twitter_consumer.rb +35 -0
  26. data/lib/messed/interface/adapter/twitter_search.rb +111 -0
  27. data/lib/messed/interface/adapter/twitter_sender.rb +44 -0
  28. data/lib/messed/interface/adapter/twitter_streaming.rb +50 -0
  29. data/lib/messed/interface/adapter.rb +68 -0
  30. data/lib/messed/interface/runner.rb +20 -0
  31. data/lib/messed/interface.rb +74 -0
  32. data/lib/messed/logger.rb +50 -0
  33. data/lib/messed/matcher.rb +50 -0
  34. data/lib/messed/message/twitter.rb +23 -0
  35. data/lib/messed/message.rb +35 -0
  36. data/lib/messed/queue/beanstalk.rb +56 -0
  37. data/lib/messed/queue.rb +17 -0
  38. data/lib/messed/session/memcache.rb +40 -0
  39. data/lib/messed/session.rb +7 -0
  40. data/lib/messed/tasks/console.rb +70 -0
  41. data/lib/messed/tasks/generation.rb +12 -0
  42. data/lib/messed/tasks/runner.rb +50 -0
  43. data/lib/messed/tasks/status.rb +37 -0
  44. data/lib/messed/tasks.rb +9 -0
  45. data/lib/messed/util/remote_status.rb +45 -0
  46. data/lib/messed.rb +235 -0
  47. data/patterns/messed/Pattern +24 -0
  48. data/patterns/messed/Rakefile +12 -0
  49. data/patterns/messed/app/runner.rb +5 -0
  50. data/patterns/messed/bin/console +3 -0
  51. data/patterns/messed/bin/runner +3 -0
  52. data/patterns/messed/bin/status +3 -0
  53. data/patterns/messed/bin/web +13 -0
  54. data/patterns/messed/config/environment.rb +15 -0
  55. data/patterns/messed/config/environments/development.rb +2 -0
  56. data/patterns/messed/config/environments/production.rb +0 -0
  57. data/patterns/messed/config/environments/test.rb +0 -0
  58. data/patterns/messed/spec/application_spec.rb +12 -0
  59. data/patterns/messed/spec/spec.opts +7 -0
  60. data/patterns/messed/spec/spec_helper.rb +44 -0
  61. data/spec/adapter/applications/twitter_search/app/application.rb +3 -0
  62. data/spec/adapter/applications/twitter_search/app/runner.rb +7 -0
  63. data/spec/adapter/applications/twitter_search/bin/incoming +89 -0
  64. data/spec/adapter/applications/twitter_search/bin/runner +21 -0
  65. data/spec/adapter/applications/twitter_search/bin/status +36 -0
  66. data/spec/adapter/applications/twitter_search/config/environment.rb +11 -0
  67. data/spec/adapter/applications/twitter_search/config/environments/development.rb +0 -0
  68. data/spec/adapter/applications/twitter_search/log/development.log +1006 -0
  69. data/spec/adapter/applications/twitter_sender/app/application.rb +0 -0
  70. data/spec/adapter/applications/twitter_sender/app/runner.rb +0 -0
  71. data/spec/adapter/applications/twitter_sender/bin/incoming +89 -0
  72. data/spec/adapter/applications/twitter_sender/bin/runner +21 -0
  73. data/spec/adapter/applications/twitter_sender/bin/status +36 -0
  74. data/spec/adapter/applications/twitter_sender/config/environment.rb +11 -0
  75. data/spec/adapter/applications/twitter_sender/config/environments/development.rb +0 -0
  76. data/spec/adapter/applications/twitter_sender/log/development.log +146 -0
  77. data/spec/adapter/http/direct_message +0 -0
  78. data/spec/adapter/http/twitter_search +20 -0
  79. data/spec/adapter/http/update +0 -0
  80. data/spec/adapter/twitter_search_spec.rb +36 -0
  81. data/spec/adapter/twitter_sender_spec.rb +64 -0
  82. data/spec/booter_spec.rb +7 -0
  83. data/spec/fixtures/booter/app/application.rb +3 -0
  84. data/spec/fixtures/booter/app/runner.rb +7 -0
  85. data/spec/fixtures/booter/bin/incoming +89 -0
  86. data/spec/fixtures/booter/bin/runner +21 -0
  87. data/spec/fixtures/booter/bin/status +36 -0
  88. data/spec/fixtures/booter/config/environment.rb +15 -0
  89. data/spec/fixtures/booter/config/environments/development.rb +0 -0
  90. data/spec/fixtures/booter/log/development.log +11 -0
  91. data/spec/message_spec.rb +52 -0
  92. data/spec/session_spec.rb +34 -0
  93. data/spec/spec.opts +7 -0
  94. data/spec/spec_helper.rb +8 -0
  95. data/test/app/runner.rb +5 -0
  96. data/test/config/environment.rb +15 -0
  97. data/test/config/environments/development.rb +2 -0
  98. data/test/config/environments/production.rb +0 -0
  99. data/test/config/environments/test.rb +0 -0
  100. data/test/spec/application_spec.rb +12 -0
  101. data/test/spec/spec_helper.rb +44 -0
  102. metadata +291 -0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://gems.github.com'
2
+ source 'http://gemcutter.org'
3
+
4
+ gem 'methodmissing-hwia'
5
+ gem 'hashify'
6
+ gem 'em-http-request'
7
+ gem 'em-jack'
8
+ gem 'dressmaker'
data/README.rdoc ADDED
@@ -0,0 +1,22 @@
1
+ = Messed
2
+
3
+ == Short messaging framework
4
+
5
+ We deal with short messages all the time. Email, SMS, twitter and XMPP are examples of such. This framework attempts to make it simple to deal with these various types of services.
6
+
7
+ == Getting started
8
+
9
+ Generate your app, fill it in.
10
+
11
+ == Topology
12
+
13
+ /----------\ Worker /----------\
14
+ | | /-------\ Worker /-------\ | |
15
+ | Incoming |==> | Queue | ==> Worker ==> | Queue | ==> | Outgoing |
16
+ | | \-------/ Worker \-------/ | |
17
+ \----------/ Worker \----------/
18
+
19
+ == Sample app
20
+
21
+ Let's listen to every tweet your friend sends you, and, if its in delicious, add it, otherwise, reply with a message saying how much you enjoyed that link.
22
+
data/Rakefile ADDED
@@ -0,0 +1,76 @@
1
+ libdir = File.expand_path("lib")
2
+ $:.unshift(libdir) unless $:.include?(libdir)
3
+
4
+ require 'messed'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "messed"
10
+ s.description = s.summary = "A framework for short message paradigms"
11
+ s.email = "joshbuddy@gmail.com"
12
+ s.homepage = "http://github.com/joshbuddy/messed"
13
+ s.authors = ["Joshua Hull"]
14
+ s.files = FileList["[A-Z]*", "{lib,spec,bin,application_spec,patterns}/**/*"]
15
+ s.add_dependency 'eventmachine'
16
+ s.add_dependency 'em-http-request', '>=0.2.6'
17
+ s.add_dependency 'em-beanstalk', '>=0.0.6'
18
+ s.add_dependency 'hashify', '>=0.0.2'
19
+ s.add_dependency 'hwia', '>=1.0.2'
20
+ s.add_dependency 'twitter-stream', '>=0.1.3'
21
+ s.add_dependency 'activesupport'
22
+ s.add_dependency 'dressmaker', '>=0.0.3'
23
+ s.add_dependency 'beanstalk-client'
24
+ s.add_dependency 'rspec'
25
+ s.add_dependency 'json'
26
+ s.add_dependency 'thor'
27
+ end
28
+ Jeweler::GemcutterTasks.new
29
+ rescue LoadError
30
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
31
+ end
32
+
33
+ require 'spec'
34
+ require 'spec/rake/spectask'
35
+ task :spec => ['spec:all', 'spec:application']
36
+ namespace(:spec) do
37
+ Spec::Rake::SpecTask.new(:all) do |t|
38
+ t.spec_opts ||= []
39
+ t.spec_opts << "-rubygems"
40
+ t.spec_opts << "--options" << "spec/spec.opts"
41
+ t.spec_files = FileList['spec/**/*_spec.rb']
42
+ end
43
+
44
+ Spec::Rake::SpecTask.new(:application) do |t|
45
+ ENV['MESSED_HOME'] = '..'
46
+ t.ruby_opts ||= []
47
+ t.ruby_opts << '-Capplication_spec'
48
+ t.spec_opts ||= []
49
+ t.spec_opts << "-rubygems"
50
+ t.spec_opts << "--options" << "spec/spec.opts"
51
+ t.spec_files = FileList['application_spec/spec/**/*_spec.rb'].map{|f| f[/application_spec\/(.*)/, 1]}
52
+ end
53
+
54
+ end
55
+
56
+ desc "Run all examples with RCov"
57
+ Spec::Rake::SpecTask.new('spec_with_rcov') do |t|
58
+ t.spec_files = FileList['spec/**/*.rb']
59
+ t.rcov = true
60
+ t.rcov_opts = ['--exclude', 'spec']
61
+ end
62
+
63
+ require 'rake/rdoctask'
64
+ desc "Generate documentation"
65
+ Rake::RDocTask.new do |rd|
66
+ rd.main = "README.rdoc"
67
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
68
+ rd.rdoc_dir = 'rdoc'
69
+ end
70
+
71
+ begin
72
+ require 'code_stats'
73
+ CodeStats::Tasks.new
74
+ rescue LoadError
75
+ puts "Code stats not available, install it with gem install code_stats"
76
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,12 @@
1
+ require 'spec'
2
+ require 'spec/rake/spectask'
3
+ task :spec => 'spec:all'
4
+ namespace(:spec) do
5
+ Spec::Rake::SpecTask.new(:all) do |t|
6
+ t.spec_opts ||= []
7
+ t.spec_opts << "-rubygems"
8
+ t.spec_opts << "--options" << "spec/spec.opts"
9
+ t.spec_files = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ end
@@ -0,0 +1,3 @@
1
+ with 'this is my message' do
2
+ say 'well, that was fun'
3
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'messed'
6
+ rescue LoadError
7
+ if ENV['MESSED_HOME']
8
+ require File.join(ENV['MESSED_HOME'], 'lib', 'messed')
9
+ else
10
+ raise("no messed!")
11
+ end
12
+ end
13
+
14
+ require 'thor'
15
+
16
+ $root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
17
+
18
+ Messed::Tasks::Runner.start
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'messed'
6
+ rescue LoadError
7
+ if ENV['MESSED_HOME']
8
+ require File.join(ENV['MESSED_HOME'], 'lib', 'messed')
9
+ else
10
+ raise("no messed!")
11
+ end
12
+ end
13
+
14
+ require 'thor'
15
+
16
+ class IncomingRunnerTask < Thor
17
+
18
+ method_options :detach => false
19
+ method_options :environment => "development"
20
+ desc "start [NAME]", "start an incoming instance"
21
+ def start(name = nil)
22
+ application = Messed::Booter.new(File.expand_path(File.join(File.dirname(__FILE__), '..')), options.environment)
23
+ incoming = if name.nil? and application.incoming_map.size == 1
24
+ application.incoming_map.values.first
25
+ elsif application.incoming_map.key?(name)
26
+ application.incoming_map[name]
27
+ else
28
+ raise(name ? "unable to find incoming with name #{name}" : "You have no incoming setup.")
29
+ end
30
+
31
+ IncomingRunner.new(incoming)
32
+ end
33
+
34
+ end
35
+
36
+ IncomingRunnerTask.start
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'messed'
6
+ rescue LoadError
7
+ if ENV['MESSED_HOME']
8
+ require File.join(ENV['MESSED_HOME'], 'lib', 'messed')
9
+ else
10
+ raise("no messed!")
11
+ end
12
+ end
13
+
14
+ require 'thor'
15
+
16
+ class WebTask < Thor
17
+
18
+ method_options :detach => false
19
+ method_options :environment => "development"
20
+ desc "start [NAME]", "start an incoming instance"
21
+ def start(name = nil)
22
+
23
+
24
+
25
+ end
26
+
27
+ end
28
+
29
+ IncomingRunnerTask.start
@@ -0,0 +1,8 @@
1
+ queues.incoming.host = '127.0.0.1'
2
+ queues.incoming.port = 11300
3
+ queues.incoming.tube = 'incoming-tube'
4
+
5
+ queues.outgoing.host = '127.0.0.1'
6
+ queues.outgoing.port = 11300
7
+ queues.outgoing.tube = 'outgoing-tube'
8
+
@@ -0,0 +1,6 @@
1
+ Processing `this is my message'
2
+ Processing `this is my message'
3
+ Processing `this is my message'
4
+ Processing `this is my message'
5
+ Processing `this is my message'
6
+ Processing `this is my message'
@@ -0,0 +1,12 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "test" do
4
+
5
+ include MessedSpecHelper
6
+
7
+ it "should have a test" do
8
+ process('this is my message')
9
+ outgoing_messages.size.should == 1
10
+ outgoing_messages.first.body.should == "well, that was fun"
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --loadby
5
+ mtime
6
+ --reverse
7
+ --backtrace
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ begin
3
+ require 'messed'
4
+ rescue LoadError
5
+ if ENV['MESSED_HOME']
6
+ require File.join(ENV['MESSED_HOME'], 'lib', 'messed')
7
+ else
8
+ raise("no messed!")
9
+ end
10
+ end
11
+
12
+ class MessedSpecHolder
13
+
14
+ attr_accessor :booter, :outgoing_messages
15
+
16
+ def initialize
17
+ @booter = Messed::Booter.new(File.join(File.dirname(__FILE__), '..'))
18
+ end
19
+
20
+ def application
21
+ @booter.application
22
+ end
23
+
24
+ def process(message)
25
+ @outgoing_messages = application.process(application.message_class.new(message))
26
+ end
27
+
28
+ end
29
+
30
+ module MessedSpecHelper
31
+
32
+ Holder = MessedSpecHolder.new
33
+
34
+ def process(message)
35
+ Holder.process(message)
36
+ end
37
+
38
+ def outgoing_messages
39
+ Holder.outgoing_messages
40
+ end
41
+
42
+ end
data/bin/messed ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+ # Messed generation script
3
+ require File.dirname(__FILE__) + '/../lib/messed'
4
+
5
+ if ARGV[0]
6
+ Messed::Tasks::Generation.new(ARGV[0])
7
+ else
8
+ $stderr.puts "Please specify a name for your application"
9
+ end
@@ -0,0 +1,114 @@
1
+ require 'fileutils'
2
+
3
+ class Messed
4
+ class Booter
5
+
6
+ include Logger::LoggingModule
7
+
8
+ attr_reader :root_directory, :environment, :application, :interface_map, :configuration, :pid
9
+
10
+ def initialize(root_directory, options = {}, &block)
11
+ if block
12
+ EMRunner.new(:detach => options[:detach], :supress_banner => options[:supress_banner]) {
13
+ setup_booter(root_directory, options)
14
+ record_pid(Process.pid)
15
+ yield self
16
+ }
17
+ else
18
+ setup_booter(root_directory, options)
19
+ end
20
+ end
21
+
22
+ def setup_booter(root_directory, options)
23
+ $LOAD_PATH << File.expand_path(root_directory)
24
+
25
+ @root_directory = root_directory
26
+ @environment = options[:environment] || 'development'
27
+
28
+ load_configuration
29
+ prepare_root
30
+ setup_logger
31
+ setup_queues
32
+ setup_interfaces
33
+ setup_application
34
+ end
35
+
36
+ def record_pid(pid)
37
+ @pid = pid
38
+ end
39
+
40
+ def expand_pid_file(pid_file)
41
+ File.expand_path(pid_file, root_directory)
42
+ end
43
+
44
+ def write_pid_file(pid_file)
45
+ File.open(expand_pid_file(pid_file), 'w') {|f| f << pid} if pid_file
46
+ end
47
+
48
+ def read_pid_file(pid_file)
49
+ Integer(File.read(expand_pid_file(pid_file)))
50
+ rescue ArgumentError
51
+ nil
52
+ end
53
+
54
+ def prepare_root
55
+ FileUtils.mkdir_p(File.join(root_directory, 'log'))
56
+ FileUtils.mkdir_p(File.join(root_directory, 'tmp', 'pid'))
57
+ end
58
+
59
+ def load_configuration
60
+ @configuration = Configuration.load_from_directory(@environment, File.expand_path(File.join(root_directory, 'config')))
61
+ end
62
+
63
+ def setup_queues
64
+ @incoming_queue = create_incoming_queue
65
+ @outgoing_queue = create_outgoing_queue
66
+ end
67
+
68
+ def setup_application
69
+ @application = Messed.new
70
+ @application.booter = self
71
+ @application.incoming = create_incoming_queue
72
+ @application.outgoing = create_outgoing_queue
73
+ @application.instance_eval(File.read(runner_file), runner_file)
74
+ end
75
+
76
+ def setup_logger
77
+ Messed::Logger.instance.setup_logger(configuration.logger || ::Logger.new(File.open(File.join(root_directory, 'log', "#{environment}.log"), File::WRONLY | File::APPEND | File::CREAT)), configuration.log_level || :debug)
78
+ end
79
+
80
+ def self.possible_interfaces(path)
81
+ booter = new(path)
82
+ booter.configuration.interfaces.names
83
+ end
84
+
85
+ def runner_file
86
+ File.join(root_directory, 'app/runner.rb')
87
+ end
88
+
89
+ def environmental_configuration_file
90
+ File.join(root_directory, "config/environments/#{environment}.rb")
91
+ end
92
+
93
+ def setup_interfaces
94
+ @interface_map = {}
95
+ configuration.interfaces.each do |name, conf|
96
+ logger.info "Setting up interface: #{name.inspect}"
97
+ @interface_map[name] = Interface.interface_from_configuration(self, name, conf)
98
+ end
99
+ end
100
+
101
+ def interface_for(name)
102
+ interface_map[name]
103
+ end
104
+
105
+ def create_incoming_queue
106
+ Messed::Queue::Beanstalk.new(configuration.queues.incoming.tube, configuration.queues.incoming.host, configuration.queues.incoming.port)
107
+ end
108
+
109
+ def create_outgoing_queue
110
+ Messed::Queue::Beanstalk.new(configuration.queues.outgoing.tube, configuration.queues.outgoing.host, configuration.queues.outgoing.port)
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,94 @@
1
+ class Messed
2
+ class Configuration
3
+
4
+ module ConfigHelper
5
+
6
+ def with(&block)
7
+ instance_eval(&block)
8
+ end
9
+
10
+ end
11
+
12
+ def self.load_from_directory(environment, dir)
13
+ environments = []
14
+ Dir[File.join(dir, '*')].each do |file|
15
+ environments << file if File.directory?(file)
16
+ end
17
+
18
+ instance = new(environments, environment)
19
+
20
+ instance.instance_eval(File.read(File.join(dir, 'environment.rb')), File.join(dir, 'environment.rb'), 1)
21
+ instance.instance_eval(File.read(File.join(dir, 'environments', "#{environment}.rb")), File.join(dir, 'environments', "#{environment}.rb"), 1)
22
+
23
+ instance
24
+ end
25
+
26
+ class Interfaces
27
+ include ConfigHelper
28
+
29
+ def initialize
30
+ @interfaces = {}
31
+ end
32
+
33
+ def each(&block)
34
+ @interfaces.each(&block)
35
+ end
36
+
37
+ def names
38
+ @interfaces.keys
39
+ end
40
+
41
+ def method_missing(method, *args, &block)
42
+ @interfaces[method] ||= IndividualInterface.new
43
+ end
44
+
45
+ class IndividualInterface
46
+
47
+ include ConfigHelper
48
+
49
+ attr_accessor :adapter, :options, :status_port, :status_address, :pid_file
50
+
51
+ def initialize
52
+ @options = {}
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ class Queues
60
+ include ConfigHelper
61
+ attr_reader :incoming, :outgoing
62
+
63
+ def initialize
64
+ @incoming = IndivialQueue.new
65
+ @outgoing = IndivialQueue.new
66
+ end
67
+
68
+ class IndivialQueue
69
+ include ConfigHelper
70
+ attr_accessor :host, :port, :tube
71
+ end
72
+
73
+ end
74
+
75
+ class Application
76
+ include ConfigHelper
77
+ attr_accessor :status_port, :status_address, :pid_file
78
+ end
79
+
80
+ include ConfigHelper
81
+
82
+ attr_reader :queues, :interfaces, :application, :environment, :environments
83
+ attr_accessor :logger, :log_level
84
+
85
+ def initialize(environments, environment)
86
+ @environments, @environment = environments, environment
87
+ @queues = Queues.new
88
+ @interfaces = Interfaces.new
89
+ @application = Application.new
90
+ end
91
+
92
+
93
+ end
94
+ end
@@ -0,0 +1,8 @@
1
+ class Messed
2
+ class Controller
3
+ module Helper
4
+ attr_accessor :message, :params, :session
5
+ end
6
+ end
7
+ end
8
+
@@ -0,0 +1,22 @@
1
+ class Messed
2
+ class Controller
3
+
4
+ module Processing
5
+ def self.included(target)
6
+
7
+ target.send(:class_variable_set, :@@after, []) unless target.send(:class_variable_defined?, :@@after)
8
+ target.class_eval "
9
+
10
+ def self.after_processing(t)
11
+ @@after << t
12
+ end
13
+
14
+ def reset_processing!
15
+ @@after.each {|a| send(a)}
16
+ end
17
+ "
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ class Messed
2
+ class Controller
3
+ module Respond
4
+
5
+ def self.included(c)
6
+ c.instance_eval "
7
+ after_processing :clear_responses
8
+ "
9
+ end
10
+
11
+ def responses
12
+ @response ||= []
13
+ end
14
+
15
+ def clear_responses
16
+ responses.clear
17
+ end
18
+
19
+ def say(response)
20
+ set_response(response)
21
+ end
22
+
23
+ def reply(response, options = {})
24
+ options[:to] ||= message.from
25
+ options[:from] ||= message.to
26
+ options[:in_reply_to] ||= message
27
+ set_response(response, options)
28
+ end
29
+
30
+ def whisper(response, options = {})
31
+ options[:private] = true
32
+ reply(response, options)
33
+ end
34
+
35
+ def set_response(response, options = nil)
36
+ message = message_class.new
37
+ message.body = response.is_a?(Message) ? response.body : response
38
+ if options
39
+ options.each do |key, value|
40
+ message.send(:"#{key}=", value)
41
+ end
42
+ end
43
+ self.responses << message
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ class Messed
2
+ class Controller
3
+
4
+ autoload :Processing, File.join('messed', 'controller', 'processing')
5
+ autoload :Respond, File.join('messed', 'controller', 'respond')
6
+ autoload :Helper, File.join('messed', 'controller', 'helper')
7
+
8
+ def self.inherited(subclass)
9
+ subclass.instance_eval "
10
+ include Messed::Controller::Helper
11
+ include Messed::Controller::Processing
12
+ include Messed::Controller::Respond
13
+ "
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,50 @@
1
+ class Messed
2
+ class EMRunner
3
+
4
+ include Logger::LoggingModule
5
+
6
+ class StatusHandler < EventMachine::Connection
7
+
8
+ attr_accessor :interface
9
+
10
+ Terminator = "\r\n"
11
+ TerminatorRegex = /\r\n/
12
+ Error = "ERROR\r\n"
13
+
14
+ def receive_data(data)
15
+ ss = StringScanner.new(data)
16
+ while part = ss.scan_until(TerminatorRegex)
17
+ if part == 'status'
18
+ send_data(interface.status)
19
+ send_data(Terminator)
20
+ else
21
+ send_data(Error)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def initialize(options, &block)
28
+ logger.info "Starting..." unless options[:supress_banner]
29
+ if options[:detach]
30
+ pid = EM.fork_reactor do
31
+ trap("INT") { EM.stop_reactor_loop }
32
+ EM.run do
33
+ EM.next_tick(&block)
34
+ end
35
+ end
36
+ Process.detach(pid)
37
+ exit(0)
38
+ else
39
+ EM.run do
40
+ EM.next_tick(&block)
41
+ end
42
+ end
43
+ end
44
+
45
+ def quit
46
+ EM.stop; logger.info "\nStoping #{interface.name.to_s}"; exit(0)
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,4 @@
1
+ class Messed
2
+ class Interface
3
+ class Adapter
4
+ class SMS < Adapter