iproto 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/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright (C) 2011 Mail.RU
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions
6
+ * are met:
7
+ * 1. Redistributions of source code must retain the above copyright
8
+ * notice, this list of conditions and the following disclaimer.
9
+ * 2. Redistributions in binary form must reproduce the above copyright
10
+ * notice, this list of conditions and the following disclaimer in the
11
+ * documentation and/or other materials provided with the distribution.
12
+ *
13
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
+ * SUCH DAMAGE.
24
+ */
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # About
2
+
3
+ Wrapper for Mail.Ru simple network protocol IProto. Support 2 modes: block via Ruby's TCPSocket and non block via EventMachine with fiblers.
4
+ Wrapper used in [Tarantool ruby client](http://github.com/mailru/tarantool-ruby) and private libs.
data/Rakefile ADDED
@@ -0,0 +1,131 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ def date
21
+ Date.today.to_s
22
+ end
23
+
24
+ def rubyforge_project
25
+ name
26
+ end
27
+
28
+ def gemspec_file
29
+ "#{name}.gemspec"
30
+ end
31
+
32
+ def gem_file
33
+ "#{name}-#{version}.gem"
34
+ end
35
+
36
+ def replace_header(head, header_name)
37
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
+ end
39
+
40
+ #############################################################################
41
+ #
42
+ # Standard tasks
43
+ #
44
+ #############################################################################
45
+
46
+
47
+ task :default => :spec
48
+ require 'rake/testtask'
49
+ Rake::TestTask.new(:spec) do |t|
50
+ t.libs << 'spec'
51
+ t.pattern = 'spec/**/*_spec.rb'
52
+ t.verbose = false
53
+ end
54
+
55
+ desc "Generate RCov test coverage and open in your browser"
56
+ task :coverage do
57
+ require 'rcov'
58
+ sh "rm -fr coverage"
59
+ sh "rcov test/test_*.rb"
60
+ sh "open coverage/index.html"
61
+ end
62
+
63
+ desc "Open an irb session preloaded with this library"
64
+ task :console do
65
+ sh "irb -rubygems -r ./lib/#{name}.rb"
66
+ end
67
+
68
+
69
+ #############################################################################
70
+ #
71
+ # Packaging tasks
72
+ #
73
+ #############################################################################
74
+
75
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
76
+ task :release => :build do
77
+ unless `git branch` =~ /^\* master$/
78
+ puts "You must be on the master branch to release!"
79
+ exit!
80
+ end
81
+ sh "git commit --allow-empty -m 'Release #{version}'"
82
+ sh "git tag v#{version}"
83
+ sh "git push origin master"
84
+ sh "git push origin v#{version}"
85
+ sh "gem push pkg/#{name}-#{version}.gem"
86
+ end
87
+
88
+ desc "Build #{gem_file} into the pkg directory"
89
+ task :build => :gemspec do
90
+ sh "mkdir -p pkg"
91
+ sh "gem build #{gemspec_file}"
92
+ sh "mv #{gem_file} pkg"
93
+ end
94
+
95
+ desc "Generate #{gemspec_file}"
96
+ task :gemspec => :validate do
97
+ # read spec file and split out manifest section
98
+ spec = File.read(gemspec_file)
99
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
100
+
101
+ # replace name version and date
102
+ replace_header(head, :name)
103
+ replace_header(head, :version)
104
+ replace_header(head, :date)
105
+ #comment this out if your rubyforge_project has a different name
106
+ replace_header(head, :rubyforge_project)
107
+
108
+ # determine file list from git ls-files
109
+ files = `git ls-files`.
110
+ split("\n").
111
+ sort.
112
+ reject { |file| file =~ /^\./ }.
113
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
114
+ map { |file| " #{file}" }.
115
+ join("\n")
116
+
117
+ # piece file back together and write
118
+ manifest = " s.files = %w[\n#{files}\n ]\n"
119
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
120
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
121
+ puts "Updated #{gemspec_file}"
122
+ end
123
+
124
+ desc "Validate #{gemspec_file}"
125
+ task :validate do
126
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
127
+ unless Dir['VERSION*'].empty?
128
+ puts "A `VERSION` file at root level violates Gem best practices."
129
+ exit!
130
+ end
131
+ end
data/iproto.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+ s.rubygems_version = '1.3.5'
5
+
6
+ s.name = 'iproto'
7
+ s.version = '0.1'
8
+ s.date = '2012-01-23'
9
+ s.rubyforge_project = 'iproto'
10
+
11
+ s.summary = "Mail.Ru simple network protocol"
12
+ s.description = "Mail.Ru simple network protocol"
13
+
14
+ s.authors = ["Andrew Rudenko"]
15
+ s.email = 'ceo@prepor.ru'
16
+ s.homepage = 'http://github.com/mailru/iproto-ruby'
17
+
18
+ s.require_paths = %w[lib]
19
+
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+ s.extra_rdoc_files = %w[README.md LICENSE]
22
+
23
+ # = MANIFEST =
24
+ s.files = %w[
25
+ README.md
26
+ Rakefile
27
+ iproto.gemspec
28
+ lib/iproto.rb
29
+ lib/iproto/connection_api.rb
30
+ lib/iproto/em.rb
31
+ lib/iproto/tcp_socket.rb
32
+ ]
33
+ # = MANIFEST =
34
+
35
+ ## Test files will be grabbed from the file list. Make sure the path glob
36
+ ## matches what you actually use.
37
+ s.test_files = s.files.select { |path| path =~ /^spec\/.*_spec\.rb/ }
38
+ end
data/lib/iproto.rb ADDED
@@ -0,0 +1,24 @@
1
+ module IProto
2
+ VERSION = '0.1'
3
+ class IProtoError < StandardError; end
4
+ class CouldNotConnect < IProtoError; end
5
+ class UnexpectedResponse < IProtoError; end
6
+
7
+ require 'iproto/connection_api'
8
+
9
+ # types:
10
+ # :em
11
+ # :block
12
+ def self.get_connection(host, port, type = :block)
13
+ case type
14
+ when :em
15
+ require 'iproto/em'
16
+ ::EM.connect host, port, IProto::EM::Connection
17
+ when :block
18
+ require 'iproto/tcp_socket'
19
+ IProto::TCPSocket.new(host, port)
20
+ else
21
+ raise "Undefined type #{type}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ module IProto
2
+ module ConnectionAPI
3
+ def next_request_id
4
+ @next_request_id ||= 0
5
+ @next_request_id += 1
6
+ if @next_request_id > 0xffffffff
7
+ @next_request_id = 0
8
+ end
9
+ @next_request_id
10
+ end
11
+ end
12
+ end
data/lib/iproto/em.rb ADDED
@@ -0,0 +1,117 @@
1
+ require 'eventmachine'
2
+ require 'fiber'
3
+ module IProto
4
+ module EM
5
+ module FixedHeaderProtocol
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def header_size(size = nil)
12
+ if size
13
+ @_header_size = size
14
+ else
15
+ @_header_size
16
+ end
17
+ end
18
+ end
19
+
20
+ attr_accessor :header, :body
21
+
22
+ def receive_data(data)
23
+ @buffer ||= ''
24
+ offset = 0
25
+ while (chunk = data[offset, _needed_size - @buffer.size]).size > 0 || _needed_size == 0
26
+ @buffer += chunk
27
+ offset += chunk.size
28
+ if @buffer.size == _needed_size
29
+ case _state
30
+ when :receive_header
31
+ @_state = :receive_body
32
+ receive_header @buffer
33
+ when :receive_body
34
+ @_state = :receive_header
35
+ receive_body @buffer
36
+ end
37
+ @buffer = ''
38
+ end
39
+ end
40
+ end
41
+
42
+ def receive_header(header)
43
+ # for override
44
+ end
45
+
46
+ def body_size
47
+ # for override
48
+ end
49
+
50
+ def receive_body(body)
51
+ # for override
52
+ end
53
+
54
+ def _needed_size
55
+ case _state
56
+ when :receive_header
57
+ self.class.header_size
58
+ when :receive_body
59
+ body_size
60
+ end
61
+ end
62
+
63
+ def _state
64
+ @_state ||= :receive_header
65
+ end
66
+ end
67
+
68
+ class Connection < ::EM::Connection
69
+ include IProto::ConnectionAPI
70
+ include FixedHeaderProtocol
71
+
72
+ header_size 12
73
+
74
+ def connection_completed
75
+ @connected = true
76
+ end
77
+
78
+ # begin FixedHeaderAndBody API
79
+ def body_size
80
+ @body_size
81
+ end
82
+
83
+ def receive_header(header)
84
+ @type, @body_size, @request_id = header.unpack('L3')
85
+ end
86
+
87
+ def receive_body(data)
88
+ fiber = waiting_requests.delete @request_id
89
+ raise IProto::UnexpectedResponse.new("For request id #{@request_id}") unless fiber
90
+ fiber.resume data
91
+ end
92
+ # end FixedHeaderAndBody API
93
+
94
+ # begin ConnectionAPI
95
+ def send_packet(request_id, data)
96
+ send_data data
97
+ f = Fiber.current
98
+ waiting_requests[request_id] = f
99
+ Fiber.yield
100
+ end
101
+ # end
102
+
103
+ def waiting_requests
104
+ @waiting_requests ||= {}
105
+ end
106
+
107
+ def close_connection(*args)
108
+ super(*args)
109
+ end
110
+
111
+ def unbind
112
+ raise IProto::CouldNotConnect.new unless @connected
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,27 @@
1
+ require 'socket'
2
+ module IProto
3
+
4
+ # TODO: timeouts
5
+ class TCPSocket < ::TCPSocket
6
+ include IProto::ConnectionAPI
7
+
8
+ # begin ConnectionAPI
9
+ def send_packet(request_id, data)
10
+ send data, 0
11
+ body_size = recv_header request_id
12
+ recv_body body_size
13
+ end
14
+ # end ConnectionAPI
15
+
16
+ def recv_header(request_id)
17
+ header = recv(12)
18
+ type, body_size, recv_request_id = header.unpack('L3')
19
+ raise UnexpectedResponse.new("Waiting response for request_id #{request_id}, but received for #{recv_request_id}") unless request_id == recv_request_id
20
+ body_size
21
+ end
22
+
23
+ def recv_body(body_size)
24
+ recv(body_size)
25
+ end
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iproto
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Rudenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-23 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Mail.Ru simple network protocol
15
+ email: ceo@prepor.ru
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files:
19
+ - README.md
20
+ - LICENSE
21
+ files:
22
+ - README.md
23
+ - Rakefile
24
+ - iproto.gemspec
25
+ - lib/iproto.rb
26
+ - lib/iproto/connection_api.rb
27
+ - lib/iproto/em.rb
28
+ - lib/iproto/tcp_socket.rb
29
+ - LICENSE
30
+ homepage: http://github.com/mailru/iproto-ruby
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options:
34
+ - --charset=UTF-8
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project: iproto
51
+ rubygems_version: 1.8.10
52
+ signing_key:
53
+ specification_version: 2
54
+ summary: Mail.Ru simple network protocol
55
+ test_files: []