messed 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -0,0 +1,35 @@
1
+ class Messed
2
+ class Interface
3
+ class Adapter
4
+ class TwitterConsumer < Adapter
5
+
6
+ attr_accessor :started_at, :packets_processed, :errors, :last_error, :last_ok, :last_status
7
+
8
+ def init
9
+ @started_at = Time.new
10
+ @packets_processed = 0
11
+ @errors = 0
12
+ @last_error = nil
13
+ @last_ok = nil
14
+ @last_status = nil
15
+ end
16
+
17
+ def type
18
+ :twitter
19
+ end
20
+
21
+ def status
22
+ {
23
+ :started_at => started_at,
24
+ :packets_processed => packets_processed,
25
+ :errors => errors,
26
+ :last_error => last_error,
27
+ :last_ok => last_ok,
28
+ :last_status => last_status
29
+ }
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,111 @@
1
+ require 'set'
2
+ require 'rack'
3
+
4
+ class Messed
5
+ class Interface
6
+ class Adapter
7
+ class TwitterSearch < TwitterConsumer
8
+
9
+ attr_reader :packets_processed
10
+
11
+ def build_query
12
+ Rack::Utils.build_query(interface.configuration.options[:fetch][:query])
13
+ end
14
+
15
+ def start
16
+ @ids ||= default_ids
17
+ # do work.
18
+ begin
19
+ query = build_query
20
+ logger.debug "Twitter Search -> #{query.inspect}"
21
+ http = EventMachine::HttpRequest.new("http://#{interface.configuration.options[:fetch][:host]}/#{interface.configuration.options[:fetch][:path]}").
22
+ get(:query => query, :timeout => 30)
23
+ http.callback {
24
+ self.last_status = http.response_header.status
25
+ case http.response_header.status
26
+ when 200
27
+ self.last_ok = Time.new
28
+ data = JSON.parse(http.response)
29
+ interface.configuration.options[:fetch][:query][:since_id] = data['max_id'] if interface.configuration.options[:fetch][:query]
30
+ @test_set = Set.new(@ids)
31
+ data['results'].each do |result|
32
+ result_to_message(result)
33
+ end
34
+ trim_ids
35
+ end
36
+ EM.add_timer(interface.configuration.options[:interval]) do
37
+ start
38
+ end
39
+ }
40
+ http.errback {
41
+ self.errors += 1
42
+ self.last_error = Time.new
43
+ EM.add_timer(interface.configuration.options[:interval]) do
44
+ start
45
+ end
46
+ }
47
+ rescue RuntimeError
48
+ EM.add_timer(interface.configuration.options[:interval]) do
49
+ start
50
+ end
51
+ end
52
+ end
53
+
54
+ def id_retention_count
55
+ interface.configuration.options[:id_retention_size] || 500
56
+ end
57
+
58
+ def trim_ids
59
+ while @ids.size > id_retention_count
60
+ @ids.shift
61
+ end
62
+ end
63
+
64
+ def store_ids?
65
+ id_retention_file
66
+ end
67
+
68
+ def write_ids
69
+ File.open(id_retention_file, 'w') { |f| f << @ids.to_json }
70
+ end
71
+
72
+ def read_ids
73
+ @ids = JSON.parse(File.read(id_retention_file))
74
+ end
75
+
76
+ def default_ids
77
+ store_ids? ? read_ids : []
78
+ rescue
79
+ logger.error "Twitter Search: Unable to load ids from #{id_retention_file.inspect}"
80
+ []
81
+ end
82
+
83
+ def id_retention_file
84
+ interface.configuration.options[:id_retention_tmp_file]
85
+ end
86
+
87
+ def result_to_message(result)
88
+ unless @test_set.include?(result['id'])
89
+ message = Message::Twitter.new do |m|
90
+ m.body = result['text']
91
+ m.from = result['from_user']
92
+ m.to = result['to_user_id']
93
+ m.created_at = Time.rfc2822(result['created_at'])
94
+ m.profile_image_url = result['profile_image_url']
95
+ m.id = result['id']
96
+ m.geo = result['geo']
97
+ m.from_user_id = result['from_user_id']
98
+ m.iso_language_code = result['iso_language_code']
99
+ m.source = result['source']
100
+ end
101
+ @ids << message.id
102
+ @packets_processed += 1
103
+ interface.application.incoming << message
104
+ logger.debug "Twitter Search: Adding message #{message.id}: #{message.body} to incoming queue"
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,44 @@
1
+ require 'twitter'
2
+
3
+ class Messed
4
+ class Interface
5
+ class Adapter
6
+ class TwitterSender < Adapter
7
+
8
+ def message_class
9
+ Messed::Message::Twitter
10
+ end
11
+
12
+ def start
13
+ jack = EM::Beanstalk.new
14
+ jack.watch(interface.application.outgoing.tube) do
15
+ jack.use(interface.application.outgoing.tube) do
16
+ jack.each_job do |job|
17
+ process_outgoing(job, message_class.from_json(job.body))
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def process_outgoing(job, message)
24
+ if message.private?
25
+ req = EventMachine::HttpRequest.new("http://twitter.com/direct_messages/new.json")
26
+ data = {:body => {:user => message.to_user_id, :text => message.body}, :timeout => 30, :head => {'authorization' => [interface.configuration.options[:username], interface.configuration.options[:password]]}}
27
+ data[:body][:in_reply_to] = message.in_reply_to if message.in_reply_to
28
+ http = req.post(data)
29
+ http.callback {
30
+ job.delete
31
+ }
32
+ else
33
+ http = EventMachine::HttpRequest.new("http://twitter.com/statuses/update.json").
34
+ post(:body => {:status => message.body}, :timeout => 30)
35
+ http.callback {
36
+ job.delete
37
+ }
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,50 @@
1
+ require 'twitter/json_stream'
2
+
3
+ class Messed
4
+ class Interface
5
+ class Adapter
6
+ class TwitterStreaming < TwitterConsumer
7
+
8
+ include Logger::LoggingModule
9
+
10
+ def start
11
+ stream = Twitter::JSONStream.connect(
12
+ :path => "/1/statuses/#{interface.configuration.options['streaming']['type']}.json?track=%23classicmoviequotes",
13
+ :auth => "#{interface.configuration.options['username']}:#{interface.configuration.options['password']}"
14
+ )
15
+
16
+ stream.each_item do |item|
17
+ self.last_ok = Time.new
18
+ result = JSON.parse(item)
19
+ message = Message::Twitter.new do |m|
20
+ m.body = result['text']
21
+ m.from = result['from_user']
22
+ m.to = result['to_user_id']
23
+ m.created_at = Time.rfc2822(result['created_at'])
24
+ m.profile_image_url = result['profile_image_url']
25
+ m.id = result['id']
26
+ m.geo = result['geo']
27
+ m.from_user_id = result['from_user_id']
28
+ m.iso_language_code = result['iso_language_code']
29
+ m.source = result['source']
30
+ end
31
+ self.packets_processed += 1
32
+ interface.application.incoming << message
33
+ end
34
+
35
+ stream.on_error do |message|
36
+ logger.error "message #{message.inspect}"
37
+ self.errors += 1
38
+ self.last_error = message
39
+ end
40
+
41
+ stream.on_max_reconnects do |timeout, retries|
42
+ logger.error "message -> #{timeout} #{retries}"
43
+ # Something is wrong on your side. Send yourself an email.
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,68 @@
1
+ require 'json'
2
+
3
+ class Messed
4
+ class Interface
5
+ class Adapter
6
+
7
+ include Logger::LoggingModule
8
+
9
+ autoload :TwitterSearch, File.join('messed', 'interface', 'adapter', 'twitter_search')
10
+ autoload :TwitterSender, File.join('messed', 'interface', 'adapter', 'twitter_sender')
11
+ autoload :TwitterStreaming, File.join('messed', 'interface', 'adapter', 'twitter_streaming')
12
+
13
+ # abstract class for twitter consumption
14
+ autoload :TwitterConsumer, File.join(File.dirname(__FILE__), 'adapter', 'twitter_consumer')
15
+
16
+ Registry = {}
17
+ def self.register_for_name(name, class_name)
18
+ Registry[name] = class_name
19
+ end
20
+
21
+ register_for_name :twitter_search, 'Messed::Interface::Adapter::TwitterSearch'
22
+ register_for_name :twitter_sender, 'Messed::Interface::Adapter::TwitterSender'
23
+ register_for_name :twitter_streaming, 'Messed::Interface::Adapter::TwitterStreaming'
24
+
25
+ def self.for_name(name)
26
+ class_name = Registry[name]
27
+ class_name ?
28
+ class_name.constantize :
29
+ raise("No adapter for #{name}")
30
+ end
31
+
32
+ attr_reader :interface
33
+
34
+ def initialize(interface)
35
+ @interface = interface
36
+ init
37
+ end
38
+
39
+ def save_state(state)
40
+ File.open(state_file, 'w') {|f| f << state.to_json }
41
+ end
42
+
43
+ def load_state
44
+ JSON.parse(File.read(state_file))
45
+ end
46
+
47
+ def state_file
48
+ "/tmp/#{name}.state"
49
+ end
50
+
51
+ # any post initialization goes here
52
+ def init
53
+ end
54
+
55
+ def type
56
+ raise "method type must be defined"
57
+ end
58
+
59
+ def start(detach)
60
+ raise "method start must be defined"
61
+ end
62
+
63
+ def send(message)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ require 'thin'
2
+
3
+ class Messed
4
+ class Interface
5
+ class Runner
6
+
7
+ attr_reader :runnable, :options
8
+
9
+ def initialize(runnable, options)
10
+ @runnable = runnable
11
+ @options = options
12
+ end
13
+
14
+ def start
15
+ runnable.start(options[:detach])
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,74 @@
1
+ class Messed
2
+ class Interface
3
+
4
+ include Logger::LoggingModule
5
+
6
+ autoload :Runner, File.join('messed', 'interface', 'runner')
7
+ autoload :Adapter, File.join('messed', 'interface', 'adapter')
8
+
9
+ def self.interface_from_configuration(booter, name, configuration)
10
+ interface = Interface.new
11
+ interface.send(:name=, name)
12
+ interface.send(:configuration=, configuration)
13
+ interface.send(:adapter=, Adapter.for_name(configuration.adapter).new(interface))
14
+ interface.send(:booter=, booter)
15
+ interface
16
+ end
17
+
18
+ def to_s
19
+ "#{Array(configuration['mode']) * ', '} ->> #{name}"
20
+ end
21
+
22
+ def status
23
+ {
24
+ 'adapter' => adapter.status,
25
+ 'configuration' => configuration,
26
+ 'name' => name
27
+ }
28
+ end
29
+
30
+ attr_reader :booter, :configuration, :adapter, :name, :started_at
31
+
32
+ def initialize
33
+ @started_at = Time.new
34
+ end
35
+
36
+ def application
37
+ booter.application
38
+ end
39
+
40
+ def status_host
41
+ configuration.status_address || '0.0.0.0'
42
+ end
43
+
44
+ def status_port
45
+ configuration.status_port || 11190
46
+ end
47
+
48
+ def stop
49
+ Process.kill("INT", booter.read_pid_file(configuration.pid_file))
50
+ exit(0)
51
+ end
52
+
53
+ def start
54
+ begin
55
+ EM.start_server(status_host, status_port, EMRunner::StatusHandler) do |c|
56
+ c.interface = self
57
+ end
58
+ logger.info "Status handler for #{self.class} started on #{status_host}:#{status_port}"
59
+ rescue RuntimeError => e
60
+ if e.message =~ /no acceptor/
61
+ logger.error "Unable to start status handler"
62
+ else
63
+ raise e
64
+ end
65
+
66
+ end
67
+ booter.write_pid_file(configuration.pid_file)
68
+ adapter.start
69
+ end
70
+
71
+ protected
72
+ attr_writer :booter, :configuration, :adapter, :name
73
+ end
74
+ end
@@ -0,0 +1,50 @@
1
+ require 'logger'
2
+
3
+ class Messed
4
+ class Logger
5
+ module LoggingModule
6
+ def self.included(cls)
7
+ cls.class_eval(<<-HERE_DOC, __FILE__, __LINE__)
8
+
9
+ def self.logger
10
+ Messed::Logger.instance.logger
11
+ end
12
+
13
+ def self.logger=(logger)
14
+ Messed::Logger.instance.logger = logger
15
+ end
16
+
17
+ HERE_DOC
18
+ end
19
+
20
+ def logger
21
+ Messed::Logger.instance.logger
22
+ end
23
+
24
+ def logger=(logger)
25
+ Messed::Logger.instance.logger = logger
26
+ end
27
+
28
+ end
29
+
30
+ attr_accessor :logger
31
+
32
+ def initialize
33
+ setup_logger
34
+ end
35
+ private :initialize
36
+
37
+ def self.instance
38
+ @@instance
39
+ end
40
+
41
+ def setup_logger(logger = ::Logger.new(STDOUT), log_level = :debug)
42
+ @logger = logger
43
+ @logger.level = ::Logger.const_get(log_level.to_s.upcase.to_sym) if log_level
44
+ end
45
+
46
+ @@instance = self.new
47
+
48
+ end
49
+ end
50
+
@@ -0,0 +1,50 @@
1
+ class Messed
2
+ class Matcher
3
+
4
+ attr_accessor :destination
5
+
6
+ def stop_processing?
7
+ true
8
+ end
9
+
10
+ class Conditional < Matcher
11
+
12
+ attr_reader :matches
13
+
14
+ def initialize(body_matcher, other_matchers = nil)
15
+ @body_matcher = body_matcher
16
+ @other_matchers = other_matchers
17
+ end
18
+
19
+ def match?(message)
20
+ matches = true
21
+ self.matches = nil
22
+ if @body_matcher
23
+ matches &&= @body_matcher === message.body
24
+ self.matches = Regexp.last_match if matches && @body_matcher.is_a?(Regexp)
25
+ end
26
+
27
+ if @other_matchers
28
+ keys = @other_matchers.keys
29
+ unless matches
30
+ key = keys.pop
31
+ matches &&= @other_matchers[key] === message.send(key)
32
+ end
33
+ end
34
+ matches
35
+ end
36
+
37
+ private
38
+ attr_writer :matches
39
+ end
40
+
41
+ class Always < Matcher
42
+
43
+ def match?(message)
44
+ true
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ class Messed
2
+ class Message
3
+ class Twitter < Message
4
+
5
+ attr_accessor :created_at, :profile_image_url, :id, :geo, :from_user_id, :iso_language_code, :source, :private, :to_user_id
6
+ hash_accessor :profile_image_url, :id, :geo, :from_user_id, :iso_language_code, :source, :to_user_id, :private
7
+ hash_convert :created_at => Hashify::Convert::Time
8
+
9
+ def initialize(body = nil)
10
+ super(body)
11
+ self.private = false
12
+ end
13
+
14
+ def unique_id
15
+ from_user_id
16
+ end
17
+
18
+ alias_method :private?, :private
19
+
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,35 @@
1
+ class Messed
2
+
3
+ class Message
4
+
5
+ autoload :Twitter, File.join('messed', 'message', 'twitter')
6
+
7
+ include Hashify
8
+ include Hashify::Json
9
+
10
+ attr_accessor :body
11
+ attr_accessor :from
12
+ attr_accessor :to
13
+ attr_accessor :enqueued_at
14
+ attr_accessor :in_reply_to
15
+
16
+ hash_accessor :body, :from, :to
17
+ hash_convert :enqueued_at => Hashify::Convert::Time
18
+ hash_convert :in_reply_to => [proc{|x| x && x.to_hash}, proc{|x, parent| x && parent.class.from_hash(x)}]
19
+
20
+ def self.class_for_type(type)
21
+ case type
22
+ when :twitter
23
+ Twitter
24
+ else
25
+ raise type
26
+ end
27
+ end
28
+
29
+ def initialize(body = nil)
30
+ self.body = body
31
+ yield self if block_given?
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,56 @@
1
+ class Messed
2
+ class Queue
3
+ class Beanstalk < Queue
4
+
5
+ include Logger::LoggingModule
6
+
7
+ attr_accessor :application
8
+ attr_reader :tube
9
+
10
+ def initialize(tube, host = '127.0.0.1', port = 11300)
11
+ @tube, @host, @port = tube, host, port
12
+ @beanstalk = ::Beanstalk::Pool.new(Array("#{host}:#{port}"))
13
+ @beanstalk.use(tube)
14
+ @beanstalk.watch(tube)
15
+ end
16
+
17
+ def status
18
+ @beanstalk.stats_tube(tube)
19
+ end
20
+
21
+ def take(block = true)
22
+ job = beanstalk.reserve
23
+ begin
24
+ message = application.message_class.from_json(job.body)
25
+ rescue JSON::ParserError
26
+ logger.error "malformed message #{job.body}"
27
+ job.delete
28
+ else
29
+ yield message
30
+ job.delete
31
+ end
32
+ end
33
+
34
+ def <<(message)
35
+ beanstalk.put message.to_json
36
+ end
37
+
38
+ def jobs_available?
39
+ not jobs_available.zero?
40
+ end
41
+
42
+ def drain!
43
+ while jobs_available?
44
+ beanstalk.reserve.delete
45
+ end
46
+ end
47
+
48
+ def jobs_available
49
+ beanstalk.stats_tube(tube)['current-jobs-ready']
50
+ end
51
+ protected
52
+ attr_reader :beanstalk
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ require 'beanstalk-client'
2
+
3
+ class Messed
4
+ class Queue
5
+
6
+ autoload :Beanstalk, File.join('messed', 'queue', 'beanstalk')
7
+
8
+ def <<(message)
9
+ raise
10
+ end
11
+
12
+ def take
13
+ raise
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'memcached'
2
+ require 'json'
3
+ require 'digest/md5'
4
+
5
+ class Messed
6
+ class Session
7
+ class Memcache < Session
8
+
9
+ def initialize(config = "127.0.0.1:11211")
10
+ @connection = Memcached.new(config)
11
+ end
12
+
13
+ def reset!(id)
14
+ connection.delete(id)
15
+ end
16
+
17
+ def with(id)
18
+ data = data(id)
19
+ begin
20
+ yield data
21
+ ensure
22
+ connection.set(key(id), data.to_json)
23
+ end
24
+ end
25
+
26
+ protected
27
+ attr_reader :connection
28
+
29
+ def data(id)
30
+ JSON.parse(connection.get(key(id))).strhash
31
+ rescue Memcached::NotFound
32
+ StrHash.new
33
+ end
34
+
35
+ def key(id)
36
+ Digest::MD5.hexdigest(id.to_s)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ class Messed
2
+ class Session
3
+
4
+ autoload :Memcache, File.join('messed', 'session', 'memcache')
5
+
6
+ end
7
+ end