nsqrb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7690040858da3ff628ff511b8ae5e80aed8d9f6a
4
+ data.tar.gz: 5320e73575c124f494826c42437ddb6b74b955c1
5
+ SHA512:
6
+ metadata.gz: 40ba74403f991222d4b76aad50cfeecbe9995fe7ce78d3d5164104ff732e4330530471217b9f61537217ddd6e2c8af720a0b95f3255d86433e74e751c82f8d54
7
+ data.tar.gz: a9e062affdf6dc3fb540442b5bd568ab683ea4cf9fdfc70e5c8ec7b1177164b9a6f937b4ec9315b0961c8391b1711f889f4536b03fc3046a10c771f76a61ae12
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.com"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Mikhail Salosin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ nsqrb
2
+ =====
3
+
4
+ Basic NSQ ruby client library
@@ -0,0 +1,14 @@
1
+ module Nsqrb
2
+ class Command::Cls < Command
3
+
4
+ private
5
+
6
+ def success_codes
7
+ %w(CLOSE_WAIT)
8
+ end
9
+
10
+ def failure_codes
11
+ %w(E_INVALID)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Nsqrb
2
+ class Command::Fin < Command
3
+
4
+ private
5
+
6
+ def failure_codes
7
+ %w(E_INVALID E_FIN_FAILED)
8
+ end
9
+
10
+ def params
11
+ [:message_id]
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module Nsqrb
2
+ class Command::Identify < Command
3
+
4
+ def to_line
5
+ filtered = params.inject({}) do |hash, key|
6
+ hash[key] = @args[key] if @args[key].nil?
7
+ hash
8
+ end
9
+ payload = MultiJson.dump(filtered)
10
+ [name, "\n", payload.length, payload].pack('a*a*l>a*')
11
+ end
12
+
13
+ private
14
+
15
+ def success_codes
16
+ %w(OK)
17
+ end
18
+
19
+ def failure_codes
20
+ %w(E_INVALID E_BAD_BODY)
21
+ end
22
+
23
+ def params
24
+ [
25
+ :short_id, :long_id, :feature_negotiation, :heartbeat_interval, :output_buffer_size,
26
+ :output_buffer_timeout, :tls_v1, :snappy, :deflate, :deflate_level, :sample_rate
27
+ ]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ module Nsqrb
2
+ class Command::Nop < Command
3
+ end
4
+ end
@@ -0,0 +1,23 @@
1
+ module Nsqrb
2
+ class Command::Pub < Command
3
+
4
+ def to_line
5
+ msg = @args[:message].to_s
6
+ [name, ' ', @args[:topic_name], "\n", msg.length, msg].pack('a*a*a*a*l>a*')
7
+ end
8
+
9
+ private
10
+
11
+ def success_codes
12
+ %w(OK)
13
+ end
14
+
15
+ def failure_codes
16
+ %w(E_INVALID E_BAD_TOPIC E_BAD_MESSAGE E_PUB_FAILED)
17
+ end
18
+
19
+ def params
20
+ [:topic_name, :message]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Nsqrb
2
+ class Command::Rdy < Command
3
+
4
+ private
5
+
6
+ def failure_codes
7
+ %w(E_INVALID)
8
+ end
9
+
10
+ def params
11
+ [:count]
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Nsqrb
2
+ class Command::Req < Command
3
+
4
+ private
5
+
6
+ def failure_codes
7
+ %w(E_INVALID E_REQ_FAILED)
8
+ end
9
+
10
+ def params
11
+ [:message_id, :timeout]
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Nsqrb
2
+ class Command::Sub < Command
3
+
4
+ private
5
+
6
+ def success_codes
7
+ %w(OK)
8
+ end
9
+
10
+ def failure_codes
11
+ %(E_INVALID E_BAD_TOPIC E_BAD_CHANNEL)
12
+ end
13
+
14
+ def params
15
+ [:topic_name, :channel_name]
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ module Nsqrb
2
+ class Command::Touch < Command
3
+
4
+ private
5
+
6
+ def params
7
+ [:message_id]
8
+ end
9
+
10
+ def failure_codes
11
+ %w(E_INVALID E_TOUCH_FAILED)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,50 @@
1
+ module Nsqrb
2
+ class Command
3
+
4
+ def initialize(args = {})
5
+ @args = args
6
+ end
7
+
8
+ def to_line
9
+ "#{command}\n"
10
+ end
11
+
12
+ # Is response OK
13
+ def ok?(response)
14
+ response = response.content if response.is_a?(FrameType)
15
+ self.class.ok.include?(response)
16
+ end
17
+
18
+ # Is response ERROR
19
+ def error?(response)
20
+ response = response.content if response.is_a?(FrameType)
21
+ self.class.error.include?(response)
22
+ end
23
+
24
+ private
25
+
26
+ def command
27
+ values.unshift(name).join(' ')
28
+ end
29
+
30
+ def success_codes
31
+ []
32
+ end
33
+
34
+ def failure_codes
35
+ []
36
+ end
37
+
38
+ def params
39
+ []
40
+ end
41
+
42
+ def values
43
+ params.map { |param| @args[param] }
44
+ end
45
+
46
+ def name
47
+ self.class.name.split('::').last.upcase
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,81 @@
1
+ module Nsqrb
2
+ class Consumer
3
+ attr_reader :options, :messages, :errors, :responses
4
+
5
+ TCP_BUFFER = 64.kilobytes
6
+ PROTOCOL_VERSION = "v2"
7
+
8
+ def initialize(options = {})
9
+ @options = options
10
+ @messages = []
11
+ @errors = []
12
+ @responses = []
13
+ @parser = Parser.new
14
+ end
15
+
16
+ def receive
17
+ begin
18
+ buffer = @socket.recv(TCP_BUFFER)
19
+ @parser.add(buffer)
20
+ frames = @parser.parse
21
+ frames.each { |frame| handle(frame) }
22
+ rescue => e
23
+ close!
24
+ raise e
25
+ end
26
+ end
27
+
28
+ def confirm(message)
29
+ @socket.write Command::Fin.new(message_id: message.id).to_line
30
+ end
31
+
32
+ def requeue(message, timeout = 0)
33
+ @socket.write Command::Req.new(message_id: message.id, timeout: timeout).to_line
34
+ end
35
+
36
+ def touch(message)
37
+ @socket.write Command::Touch.new(message_id: message.id).to_line
38
+ end
39
+
40
+ def close!
41
+ return if @socket.nil? || @socket.closed?
42
+ @socket.write Command::Cls.new.to_line
43
+ @socket.close
44
+ @socket = nil
45
+ end
46
+
47
+ def connect!
48
+ close! if @socket && !@socket.closed?
49
+ @socket = TCPSocket.open(options[:host], options[:port])
50
+ @socket.write PROTOCOL_VERSION.rjust(4).upcase
51
+ @socket.write Command::Identify.new(identify_defaults.merge(options[:features] || {})).to_line
52
+ @socket.write Command::Sub.new(topic_name: options[:topic], channel_name: options[:channel]).to_line
53
+ @socket.write Command::Rdy.new(count: 1).to_line
54
+ puts 'Ready to receive!'
55
+ end
56
+
57
+ private
58
+
59
+ def identify_defaults
60
+ return @identify_defaults if @identify_defaults
61
+ @identify_defaults = {
62
+ short_id: Socket.gethostname,
63
+ long_id: Socket.gethostbyname(Socket.gethostname).flatten.compact.first,
64
+ user_agent: "nsqrb/#{Nsqrb::VERSION}",
65
+ feature_negotiation: true
66
+ }
67
+ end
68
+
69
+ def handle(frame)
70
+ if(frame.is_a?(Frame::Response) && frame.content == '_heartbeat_')
71
+ @socket.write Command::Nop.new.to_line
72
+ elsif frame.is_a?(Frame::Message)
73
+ @messages << frame
74
+ elsif frame.is_a?(Frame::Response)
75
+ @responses << frame
76
+ elsif frame.is_a?(Frame::Error)
77
+ @errors << frame
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,9 @@
1
+ module Nsqrb
2
+ class Frame::Error < Frame
3
+
4
+ def initialize(frame)
5
+ super(content: frame[:data])
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Nsqrb
2
+ class Frame::Message < Frame
3
+
4
+ def initialize(frame)
5
+ unpacked = frame[:data].unpack("Q>s>a16a#{frame[:size]}")
6
+ super(Hash[*([:timestamp, :attempts, :id, :content].zip(unpacked).flatten)])
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module Nsqrb
2
+ class Frame::Response < Frame
3
+
4
+ def initialize(frame)
5
+ super(content: frame[:data])
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Nsqrb
2
+ class Frame < OpenStruct
3
+ end
4
+ end
@@ -0,0 +1,47 @@
1
+ module Nsqrb
2
+ class Parser
3
+
4
+ # Frame types
5
+ FRAME_TYPES = {
6
+ 0 => Frame::Response,
7
+ 1 => Frame::Error,
8
+ 2 => Frame::Message
9
+ }
10
+
11
+ FRAME_TYPE_SIZE = 4
12
+ INFO_SIZE = 8
13
+
14
+ def initialize
15
+ @buffer = []
16
+ end
17
+
18
+ def add(bytes)
19
+ bytes = bytes.split('') if bytes.is_a?(String)
20
+ @buffer += bytes
21
+ end
22
+
23
+ # Parse all frames from current buffer
24
+ # @return Array [Frame]
25
+ def parse
26
+ frames = []
27
+ loop do
28
+ break if @buffer.length < INFO_SIZE
29
+ size, type = @buffer[0, INFO_SIZE].join.unpack('l>l>')
30
+ frame = { size: size - FRAME_TYPE_SIZE, type: type.to_i }
31
+
32
+ break if @buffer.length < frame[:size] + INFO_SIZE
33
+ frame[:data] = @buffer[INFO_SIZE, frame[:size]].join
34
+ frames << build_frame(frame)
35
+ @buffer.shift(INFO_SIZE + frame[:size])
36
+ end
37
+ frames
38
+ end
39
+
40
+ private
41
+
42
+ def build_frame(frame)
43
+ raise "Unknown frame type: #{frame[:type]}" unless FRAME_TYPES.has_key?(frame[:type])
44
+ FRAME_TYPES[frame[:type]].new frame
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,14 @@
1
+ module Nsqrb
2
+ class Producer
3
+ def initialize(host, port, topic)
4
+ @http = ::Net::HTTP.new(host, port)
5
+ @topic = topic
6
+ end
7
+
8
+ def post!(obj)
9
+ res = @http.post("/pub?topic=" + @topic, obj.to_json)
10
+ return if res.code == "200"
11
+ res.error!
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Nsqrb
2
+ VERSION = "0.0.1"
3
+ end
data/lib/nsqrb.rb ADDED
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ require 'nsqrb'
4
+ require 'nsqrb/command'
5
+ require 'nsqrb/frame'
6
+ require 'nsqrb/frame/error'
7
+ require 'nsqrb/frame/response'
8
+ require 'nsqrb/frame/message'
9
+ require 'nsqrb/parser'
10
+ require 'nsqrb/consumer'
11
+ require 'nsqrb/producer'
12
+
13
+ Dir[File.dirname(__FILE__) + '/nsqrb/command/*.rb'].each {|file| require file }
14
+
15
+ module Nsqrb
16
+ end
data/nsqrb.gemspec ADDED
@@ -0,0 +1,13 @@
1
+ require File.expand_path('../lib/nsqrb/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'nsqrb'
5
+ gem.version = Nsqrb::VERSION
6
+ gem.description = gem.summary = "Basic ruby NSQ (http://nsq.io) client library"
7
+ gem.authors = ["Mikhail Salosin"]
8
+ gem.email = 'mikhail@salosin.me'
9
+ gem.files = `git ls-files`.split("\n")
10
+ gem.test_files = `git ls-files -- test/*`.split("\n")
11
+ gem.homepage = 'http://rubygems.org/gems/nsqrb'
12
+ gem.license = 'MIT'
13
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nsqrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mikhail Salosin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Basic ruby NSQ (http://nsq.io) client library
14
+ email: mikhail@salosin.me
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - .gitignore
20
+ - Gemfile
21
+ - LICENSE
22
+ - README.md
23
+ - lib/nsqrb.rb
24
+ - lib/nsqrb/command.rb
25
+ - lib/nsqrb/command/cls.rb
26
+ - lib/nsqrb/command/fin.rb
27
+ - lib/nsqrb/command/identify.rb
28
+ - lib/nsqrb/command/nop.rb
29
+ - lib/nsqrb/command/pub.rb
30
+ - lib/nsqrb/command/rdy.rb
31
+ - lib/nsqrb/command/req.rb
32
+ - lib/nsqrb/command/sub.rb
33
+ - lib/nsqrb/command/touch.rb
34
+ - lib/nsqrb/consumer.rb
35
+ - lib/nsqrb/frame.rb
36
+ - lib/nsqrb/frame/error.rb
37
+ - lib/nsqrb/frame/message.rb
38
+ - lib/nsqrb/frame/response.rb
39
+ - lib/nsqrb/parser.rb
40
+ - lib/nsqrb/producer.rb
41
+ - lib/nsqrb/version.rb
42
+ - nsqrb.gemspec
43
+ homepage: http://rubygems.org/gems/nsqrb
44
+ licenses:
45
+ - MIT
46
+ metadata: {}
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 2.2.2
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Basic ruby NSQ (http://nsq.io) client library
67
+ test_files: []