fyrehose 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.
data/.gitkeep ADDED
File without changes
data/fyrehose.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fyrehose"
6
+ s.version = "0.0.1"
7
+ s.date = Date.today.to_s
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Paul Asmuth"]
10
+ s.email = ["paul@paulasmuth.com"]
11
+ s.homepage = "http://github.com/paulasmuth/fyrehose"
12
+ s.summary = %q{ruby client for the fyrehose pub/sub protocol}
13
+ s.description = %q{ruby client for the fyrehose pub/sub protocol}
14
+
15
+ s.licenses = ["MIT"]
16
+
17
+ s.files = `git ls-files`.split("\n") - [".gitignore", ".rspec", ".travis.yml"]
18
+ s.test_files = `git ls-files -- spec/*`.split("\n")
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,107 @@
1
+ module Fyrehose
2
+ class InputStream
3
+
4
+ attr_accessor :state, :buf, :pos
5
+
6
+ def initialize
7
+ @state = -7
8
+ @pos = 0
9
+ @buf = ""
10
+ end
11
+
12
+ def <<(chunk)
13
+ buf << chunk
14
+ end
15
+
16
+ def each
17
+ trim = 0
18
+
19
+ while pos < buf.size do
20
+ case state
21
+
22
+ when -7
23
+ self.state += 1
24
+ type = nil
25
+ txid = ""
26
+ channel = ""
27
+ len_or_flags = ""
28
+ body = ""
29
+ next
30
+
31
+ when -6
32
+ raise ProtocolError if buf[pos] != "#"
33
+ self.state += 1
34
+
35
+ when -3, -5
36
+ if buf[pos] == " "
37
+ self.state += 1
38
+ else
39
+ txid << buf[pos] if state == -5
40
+ channel << buf[pos] if state == -3
41
+ end
42
+
43
+ when -4
44
+ if buf[pos] == "$"
45
+ type = :ack
46
+ self.state = -1
47
+ elsif buf[pos] == "@"
48
+ self.state += 1
49
+ else
50
+ raise ProtocolError
51
+ end
52
+
53
+ when -2
54
+ self.state += 1
55
+ if buf[pos] == "*"
56
+ type = :data
57
+ elsif buf[pos] == "+"
58
+ type = :flags
59
+ else
60
+ raise ProtocolError
61
+ end
62
+
63
+ when -1
64
+ if buf[pos] == " " || buf[pos] == "\n"
65
+ len_or_flags = len_or_flags.to_i
66
+ if type == :data
67
+ self.state = len_or_flags
68
+ else
69
+ self.state += 1
70
+ next
71
+ end
72
+ else
73
+ len_or_flags << buf[pos]
74
+ end
75
+
76
+ when 0
77
+ yield({
78
+ :type => type,
79
+ :txid => txid,
80
+ :channel => channel,
81
+ :flags => len_or_flags,
82
+ :body => body
83
+ })
84
+ trim = pos + 1
85
+ self.state = -7
86
+
87
+ else
88
+ self.state -= 1
89
+ body << buf[pos]
90
+
91
+ end
92
+
93
+ self.pos += 1
94
+ end
95
+
96
+ if trim > 0
97
+ self.pos -= trim
98
+ self.buf = self.buf[trim..-1]
99
+ end
100
+
101
+ rescue Fyrehose::ProtocolError => e
102
+ raise Fyrehose::ProtocolError.new(self.buf, self.pos)
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,19 @@
1
+ module Fyrehose
2
+ class ProtocolError < StandardError
3
+
4
+ INFO_SIZE = 30
5
+
6
+ def initialize(buf=nil, pos=nil)
7
+ return unless buf
8
+ offset = [0, pos - (INFO_SIZE / 2)].max
9
+ @info = "\n => "
10
+ @info << buf[offset..offset + INFO_SIZE].gsub("\n", " ")
11
+ @info << "\n#{" " * (pos - offset + 7)}^\n"
12
+ end
13
+
14
+ def to_s
15
+ "invalid token#{@info}"
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ require "eventmachine"
2
+
3
+ class Fyrehose::Reactor < EventMachine::Connection
4
+
5
+ def self.run(host, port, opts = {}, &block)
6
+ unless block
7
+ raise "missing proc{ |channel,data| } for #run"
8
+ end
9
+
10
+ EventMachine.run do
11
+ reactor = EventMachine.connect(host, port, Fyrehose::Reactor)
12
+ reactor.instance_eval(&block)
13
+ end
14
+ end
15
+
16
+ def post_init
17
+ @input_stream = Fyrehose::InputStream.new
18
+ @callbacks = []
19
+ end
20
+
21
+ def deliver(channel, data)
22
+ send_data("##{next_txid} @#{channel} *#{data.size} #{data}\n")
23
+ end
24
+
25
+ def set_flags(channel, flags)
26
+ send_data("##{next_txid} @#{channel} +#{flags}\n")
27
+ end
28
+
29
+ def subscribe(channel)
30
+ set_flags(channel, 1)
31
+ end
32
+
33
+ def unsubscribe(channel)
34
+ set_flags(channel, 0)
35
+ end
36
+
37
+ def on_message(&block)
38
+ @callbacks << block
39
+ end
40
+
41
+ def receive_data(chunk)
42
+ @input_stream << chunk
43
+
44
+ @input_stream.each do |msg|
45
+ next unless msg[:type] == :data
46
+
47
+ @callbacks.each do |block|
48
+ block.call(msg[:channel], msg[:body])
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def next_txid
56
+ rand(8**32).to_s(36)
57
+ end
58
+
59
+ end
data/lib/fyrehose.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "fyrehose/input_stream"
2
+ require "fyrehose/protocol_error"
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fyrehose
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Paul Asmuth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-03 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ruby client for the fyrehose pub/sub protocol
15
+ email:
16
+ - paul@paulasmuth.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitkeep
22
+ - fyrehose.gemspec
23
+ - lib/fyrehose.rb
24
+ - lib/fyrehose/input_stream.rb
25
+ - lib/fyrehose/protocol_error.rb
26
+ - lib/fyrehose/reactor.rb
27
+ homepage: http://github.com/paulasmuth/fyrehose
28
+ licenses:
29
+ - MIT
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 1.8.24
49
+ signing_key:
50
+ specification_version: 3
51
+ summary: ruby client for the fyrehose pub/sub protocol
52
+ test_files: []