wine 0.1.0

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.
@@ -0,0 +1,38 @@
1
+ Wine for Ruby
2
+ =============
3
+
4
+ Wine is a protocol for configuring and monitoring agents. Wine for Ruby is an
5
+ implementation of the protocol for Ruby agents, and there is [Wine for Java][]
6
+ for the JVM as well.
7
+
8
+ [Wine for Java]: http://github.com/valotrading/wine-java
9
+
10
+
11
+ Installation
12
+ ------------
13
+
14
+ Install Wine for Ruby:
15
+
16
+ gem install wine
17
+
18
+
19
+ Development
20
+ -----------
21
+
22
+ Install the dependencies:
23
+
24
+ bundle install
25
+
26
+ Run the tests:
27
+
28
+ bundle exec rake
29
+
30
+ Run the test server:
31
+
32
+ bundle exec bin/wine-test-server
33
+
34
+
35
+ License
36
+ -------
37
+
38
+ Wine for Ruby is released under the Apache License, Version 2.0.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'wine'
4
+
5
+ begin
6
+ Wine::TestServer.start(ARGV)
7
+ rescue Interrupt
8
+ exit
9
+ end
@@ -0,0 +1,6 @@
1
+ require 'wine/connection'
2
+ require 'wine/error'
3
+ require 'wine/message'
4
+ require 'wine/session'
5
+ require 'wine/test_server'
6
+ require 'wine/version'
@@ -0,0 +1,45 @@
1
+ require 'wine/error'
2
+ require 'wine/message'
3
+
4
+ module Wine
5
+ class Connection
6
+
7
+ DEFAULT_TIMEOUT = 5000
8
+
9
+ attr_reader :socket
10
+
11
+ def initialize(socket)
12
+ @socket = socket
13
+ end
14
+
15
+ def send(message)
16
+ message.write(@socket)
17
+ end
18
+
19
+ def recv(timeout = DEFAULT_TIMEOUT)
20
+ readable = IO.select([ @socket ], nil, nil, timeout / 1000.0)
21
+ return nil unless readable
22
+
23
+ msg_type = @socket.recv(1, Socket::MSG_PEEK)
24
+ raise ConnectionClosed unless msg_type.length == 1
25
+
26
+ Message.read(@socket)
27
+ end
28
+
29
+ def recv_nonblock
30
+ begin
31
+ msg_type = @socket.recv_nonblock(1, Socket::MSG_PEEK)
32
+ raise ConnectionClosed unless msg_type.length == 1
33
+
34
+ Message.read(@socket)
35
+ rescue IO::WaitReadable
36
+ nil
37
+ end
38
+ end
39
+
40
+ def close
41
+ @socket.close
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ module Wine
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ class ConnectionRefused < Error
7
+ end
8
+
9
+ class ConnectionClosed < Error
10
+ end
11
+
12
+ class ResponseTimeout < Error
13
+ end
14
+
15
+ class ProtocolError < Error
16
+ end
17
+
18
+ end
@@ -0,0 +1,71 @@
1
+ require 'bindata'
2
+ require 'wine/error'
3
+
4
+ module Wine
5
+ module Message
6
+
7
+ LOGIN = 'L'
8
+ LOGIN_ACCEPTED = 'A'
9
+ LOGIN_REJECTED = 'J'
10
+ GET = 'G'
11
+ VALUE = 'V'
12
+ SET = 'S'
13
+
14
+ class Login < BinData::Record
15
+ string :msg_type, :value => LOGIN
16
+ string :username, :length => 8, :pad_byte => ' '
17
+ string :password, :length => 20, :pad_byte => ' '
18
+ end
19
+
20
+ class LoginAccepted < BinData::Record
21
+ string :msg_type, :value => LOGIN_ACCEPTED
22
+ end
23
+
24
+ class LoginRejected < BinData::Record
25
+ string :msg_type, :value => LOGIN_REJECTED
26
+ end
27
+
28
+ class Get < BinData::Record
29
+ string :msg_type, :value => GET
30
+ int32be :key_length, :value => lambda { key_data.length }
31
+ string :key_data, :read_length => :key_length
32
+ end
33
+
34
+ class Value < BinData::Record
35
+ string :msg_type, :value => VALUE
36
+ int32be :key_length, :value => lambda { key_data.length }
37
+ int32be :value_length, :value => lambda { value_data.length }
38
+ string :key_data, :read_length => :key_length
39
+ string :value_data, :read_length => :value_length
40
+ end
41
+
42
+ class Set < BinData::Record
43
+ string :msg_type, :value => SET
44
+ int32be :key_length, :value => lambda { key_data.length }
45
+ int32be :value_length, :value => lambda { value_data.length }
46
+ string :key_data, :read_length => :key_length
47
+ string :value_data, :read_length => :value_length
48
+ end
49
+
50
+ def self.read(io)
51
+ begin
52
+ type = Types[io.read(1)]
53
+ type.read(io)
54
+ rescue StandardError
55
+ raise ProtocolError
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ Types = {
62
+ LOGIN => Login,
63
+ LOGIN_ACCEPTED => LoginAccepted,
64
+ LOGIN_REJECTED => LoginRejected,
65
+ GET => Get,
66
+ VALUE => Value,
67
+ SET => Set
68
+ }
69
+
70
+ end
71
+ end
@@ -0,0 +1,66 @@
1
+ require 'socket'
2
+ require 'wine/connection'
3
+ require 'wine/error'
4
+ require 'wine/message'
5
+
6
+ module Wine
7
+ class Session
8
+
9
+ def self.connect(host, port)
10
+ begin
11
+ socket = TCPSocket.new(host, port)
12
+ connection = Connection.new(socket)
13
+
14
+ new(connection)
15
+ rescue Errno::ECONNREFUSED
16
+ raise ConnectionRefused
17
+ end
18
+ end
19
+
20
+ def initialize(connection)
21
+ @connection = connection
22
+ end
23
+
24
+ def login(username, password, timeout = Connection::DEFAULT_TIMEOUT)
25
+ request = Message::Login.new(:username => username, :password => password)
26
+ @connection.send(request)
27
+
28
+ response = @connection.recv(timeout)
29
+ raise ResponseTimeout unless response
30
+
31
+ case response.msg_type
32
+ when Message::LOGIN_ACCEPTED
33
+ true
34
+ when Message::LOGIN_REJECTED
35
+ false
36
+ else
37
+ raise ProtocolError
38
+ end
39
+ end
40
+
41
+ def get(key, timeout = Connection::DEFAULT_TIMEOUT)
42
+ request = Message::Get.new(:key_data => key)
43
+ @connection.send(request)
44
+
45
+ response = @connection.recv(timeout)
46
+ raise ResponseTimeout unless response
47
+
48
+ case response.msg_type
49
+ when Message::VALUE
50
+ response.value_data
51
+ else
52
+ raise ProtocolError
53
+ end
54
+ end
55
+
56
+ def set(key, value)
57
+ request = Message::Set.new(:key_data => key, :value_data => value)
58
+ @connection.send(request)
59
+ end
60
+
61
+ def close
62
+ @connection.close
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,137 @@
1
+ require 'logger'
2
+ require 'socket'
3
+ require 'wine/connection'
4
+ require 'wine/error'
5
+ require 'wine/message'
6
+
7
+ module Wine
8
+ class TestServer
9
+
10
+ Timeout = 100.0 / 1000.0
11
+
12
+ def self.start(args)
13
+ usage unless args.length == 1
14
+
15
+ port = args[0].to_i
16
+ usage if port == 0
17
+
18
+ log = Logger.new(STDERR)
19
+
20
+ server = new(port, log)
21
+ server.run
22
+ end
23
+
24
+ def self.usage
25
+ abort "Usage: wine-test-server <port>"
26
+ end
27
+
28
+ def initialize(port, logger)
29
+ @port = port
30
+ @log = logger
31
+
32
+ @stopped = false
33
+
34
+ @config = {}
35
+
36
+ @server_socket = nil
37
+ @connections = {}
38
+
39
+ @log.progname = "wine-test-server"
40
+ @log.formatter = Proc.new do |severity, time, progname, msg|
41
+ "#{progname}: #{severity.downcase}: #{msg}\n"
42
+ end
43
+ end
44
+
45
+ def run
46
+ listen
47
+
48
+ @log.info("Listening on port #{@port}")
49
+
50
+ until @stopped do
51
+ readable, writable, erroneous = IO.select(sockets, [], [], Timeout)
52
+ next unless readable
53
+
54
+ readable.each do |socket|
55
+ if socket == @server_socket
56
+ accept
57
+ else
58
+ recv(@connections[socket])
59
+ end
60
+ end
61
+ end
62
+
63
+ sockets.each do |socket|
64
+ socket.close
65
+ end
66
+ end
67
+
68
+ def stop
69
+ @stopped = true
70
+ end
71
+
72
+ private
73
+
74
+ def listen
75
+ @server_socket = Socket.new(:INET, :SOCK_STREAM)
76
+ @server_socket.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, true)
77
+ @server_socket.bind(Socket.sockaddr_in(@port, "127.0.0.1"))
78
+ @server_socket.listen(5)
79
+ end
80
+
81
+ def sockets
82
+ [ @server_socket ] + client_sockets
83
+ end
84
+
85
+ def client_sockets
86
+ @connections.keys
87
+ end
88
+
89
+ def accept
90
+ begin
91
+ client_socket, client_addrinfo = @server_socket.accept_nonblock
92
+
93
+ @connections[client_socket] = Connection.new(client_socket)
94
+ rescue IO::WaitReadable
95
+ # Do nothing.
96
+ end
97
+ end
98
+
99
+ def recv(connection)
100
+ begin
101
+ message = connection.recv_nonblock
102
+ if message
103
+ @log.info("Received #{message}")
104
+
105
+ handle(connection, message)
106
+ end
107
+ rescue ConnectionClosed
108
+ @log.info("Connection closed")
109
+
110
+ close(connection)
111
+ rescue Error
112
+ @log.warn("Closing connection")
113
+
114
+ close(connection)
115
+ end
116
+ end
117
+
118
+ def handle(connection, message)
119
+ case message.msg_type
120
+ when Message::LOGIN
121
+ connection.send(Message::LoginAccepted.new)
122
+ when Message::GET
123
+ value = @config.fetch(message.key_data, '')
124
+ connection.send(Message::Value.new(:key_data => message.key_data, :value_data => value))
125
+ when Message::SET
126
+ @config[message.key_data] = message.value_data
127
+ end
128
+ end
129
+
130
+ def close(connection)
131
+ @connections.delete(connection.socket)
132
+
133
+ connection.close
134
+ end
135
+
136
+ end
137
+ end
@@ -0,0 +1,3 @@
1
+ module Wine
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wine
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pekka Enberg
9
+ - Jussi Virtanen
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bindata
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '1.4'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '1.4'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rake
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: '10.0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '10.0'
47
+ description: ! "Wine for Ruby\n=============\n\nWine is a protocol for configuring
48
+ and monitoring agents. Wine for Ruby is an\nimplementation of the protocol for Ruby
49
+ agents, and there is [Wine for Java][]\nfor the JVM as well.\n\n [Wine for Java]:
50
+ http://github.com/valotrading/wine-java\n\n\nInstallation\n------------\n\nInstall
51
+ Wine for Ruby:\n\n gem install wine\n\n\nDevelopment\n-----------\n\nInstall
52
+ the dependencies:\n\n bundle install\n\nRun the tests:\n\n bundle exec rake\n\nRun
53
+ the test server:\n\n bundle exec bin/wine-test-server\n\n\nLicense\n-------\n\nWine
54
+ for Ruby is released under the Apache License, Version 2.0.\n"
55
+ email: engineering@valotrading.com
56
+ executables:
57
+ - wine-test-server
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - README.md
62
+ - bin/wine-test-server
63
+ - lib/wine.rb
64
+ - lib/wine/version.rb
65
+ - lib/wine/error.rb
66
+ - lib/wine/connection.rb
67
+ - lib/wine/session.rb
68
+ - lib/wine/message.rb
69
+ - lib/wine/test_server.rb
70
+ homepage: http://github.com/valotrading/wine-ruby
71
+ licenses:
72
+ - Apache License, Version 2.0
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.23
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: A protocol for configuring and monitoring agents
95
+ test_files: []