fraggle-block 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ *.swp
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fraggle-block.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # Fraggle::Block
2
+
3
+ A synchronous Ruby client for [Doozer](https://github.com/ha/doozer).
4
+
5
+ ## Install
6
+
7
+ $ gem install fraggle-block
8
+
9
+ ## Use
10
+
11
+ >> require 'rubygems'
12
+ >> require 'fraggle/block'
13
+ >> client = Fraggle::Block.connect
14
+ => #<Fraggle::Block::Client:0x10217b260 @connection=#<Fraggle::Block::Connection:0x10217bbc0 @cn=#<TCPSocket:0x10217b3c8>, host"127.0.0.1", port8046
15
+ >> client.set('/foo', 'test', 0)
16
+ => Fraggle::Block::Response tag: 0, flags: 3, rev: 482
17
+ >> foo = client.get('/foo')
18
+ => Fraggle::Block::Response value: "test", tag: 0, flags: 3, rev: 482
19
+ >> foo = client.getdir('/')
20
+ => [Fraggle::Block::Response path: "foo", tag: 0, flags: 1, Fraggle::Block::Response path: "ctl", tag: 0, flags: 1, Fraggle::Block::Response tag: 0, flags: 2]
21
+ >> foo = client.del('/foo', 482)
22
+ => Fraggle::Block::Response tag: 0, flags: 3
23
+ >> client.disconnect
24
+ => nil
25
+
26
+ See [examples](https://github.com/dylanegan/fraggle-block/tree/master/examples) for more.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ HOME = ENV["HOME"]
5
+ PBDIR = HOME+"/Code/heroku/doozer/src/pkg/proto"
6
+
7
+ namespace :proto do
8
+ task :update do
9
+ ENV["BEEFCAKE_NAMESPACE"] = "Fraggle::Block"
10
+ sh(
11
+ "protoc",
12
+ "--beefcake_out", "lib/fraggle/block",
13
+ "-I", PBDIR,
14
+ PBDIR+"/msg.proto"
15
+ )
16
+ end
17
+ end
18
+
19
+ require 'rake/testtask'
20
+
21
+ namespace :test do
22
+ Rake::TestTask.new(:all) do |t|
23
+ t.libs << "test"
24
+ t.pattern = 'test/**/*_test.rb'
25
+ t.verbose = true
26
+ end
27
+ end
data/bin/fraggle-block ADDED
@@ -0,0 +1,5 @@
1
+ require 'fraggle/block'
2
+ require 'irb'
3
+
4
+ @client = Fraggle::Block.connect ARGV.shift
5
+ IRB.start
@@ -0,0 +1,27 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'rubygems'
3
+ require 'fraggle/block'
4
+
5
+ client = Fraggle::Block.connect
6
+
7
+ rev = client.set('/foo', 'test', 0).rev
8
+ puts "Setting /foo to test with rev #{rev}"
9
+
10
+ foo = client.get('/foo')
11
+ puts "Got /foo with #{foo.value}"
12
+
13
+ root = client.getdir('/')
14
+ puts "Directly under / is #{root.collect { |file| file.path }.join(', ')}"
15
+
16
+ client.del('/foo', rev)
17
+ puts "Deleted /foo"
18
+
19
+ foo = client.get('/foo')
20
+ puts foo.inspect
21
+
22
+ walk = client.walk('/**')
23
+ walk.each do |file|
24
+ puts "#{file.path} #{file.rev} #{file.value}"
25
+ end
26
+
27
+ client.disconnect
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "fraggle/block/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fraggle-block"
7
+ s.version = Fraggle::Block::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dylan Egan", "Blake Mizerany"]
10
+ s.email = ["dylanegan@gmail.com", "blake.mizerany@gmail.com"]
11
+ s.homepage = "https://github.com/dylanegan/fraggle-block"
12
+ s.summary = %q{A synchronous Ruby client for Doozer.}
13
+ s.description = %q{A synchronous Ruby client for Doozer.}
14
+
15
+ s.rubyforge_project = "fraggle-block"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "beefcake", "~>0.3"
23
+ s.add_dependency "system_timer", "1.0"
24
+
25
+ s.add_development_dependency "turn"
26
+ end
@@ -0,0 +1,86 @@
1
+ require 'fraggle/block/connection'
2
+
3
+ module Fraggle
4
+ module Block
5
+ class Client
6
+ include Request::Verb
7
+
8
+ class OutOfNodes < StandardError; end
9
+
10
+ attr_accessor :addrs
11
+ attr_reader :connection
12
+
13
+ def initialize(addrs = [])
14
+ @addrs = addrs
15
+ connect
16
+ end
17
+
18
+ def rev
19
+ request = Request.new(:verb => REV)
20
+ send(request).first
21
+ end
22
+
23
+ def get(path, rev = nil)
24
+ request = Request.new(:path => path, :rev => rev, :verb => GET)
25
+ send(request).first
26
+ end
27
+
28
+ def getdir(path, rev = nil, offset = nil, limit = nil)
29
+ request = Request.new(:path => path, :rev => rev, :offset => offset, :limit => limit, :verb => GETDIR)
30
+ send(request)
31
+ end
32
+
33
+ def set(path, value, rev)
34
+ request = Request.new(:path => path, :value => value, :rev => rev, :verb => SET)
35
+ send(request).first
36
+ end
37
+
38
+ def del(path, rev)
39
+ request = Request.new(:path => path, :rev => rev, :verb => DEL)
40
+ send(request).first
41
+ end
42
+
43
+ def walk(path, rev = nil)
44
+ request = Request.new(:path => path, :rev => rev, :verb => WALK)
45
+ send(request)
46
+ end
47
+
48
+ def disconnect
49
+ @connection.disconnect
50
+ end
51
+
52
+ def reconnect
53
+ disconnect
54
+ connect
55
+ end
56
+
57
+ def connect
58
+ begin
59
+ host, port = @addrs.shift.split(':')
60
+ @connection = connection_to(host, port)
61
+ find_all_of_the_nodes
62
+ rescue => e
63
+ retry if @addrs.any?
64
+ raise OutOfNodes, "where did they go?"
65
+ end
66
+ end
67
+
68
+ def connection_to(host, port)
69
+ Connection.new(host, port)
70
+ end
71
+
72
+ def find_all_of_the_nodes
73
+ walk('/ctl/node/*/addr').each do |node|
74
+ @addrs << node.value unless @addrs.include? node.value
75
+ end
76
+ end
77
+
78
+ protected
79
+
80
+ def send(request)
81
+ @connection.send(request)
82
+ @connection.read
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,53 @@
1
+ require 'fraggle/block/msg.pb'
2
+ require 'socket'
3
+ require "system_timer"
4
+
5
+ module Fraggle
6
+ module Block
7
+ class Connection
8
+ attr_accessor :host, :port, :sock
9
+
10
+ def initialize(host, port)
11
+ @host = host
12
+ @port = port
13
+ @sock = connect
14
+ end
15
+
16
+ def address
17
+ "#{@host}:#{@port}"
18
+ end
19
+
20
+ def connect
21
+ SystemTimer.timeout_after(10) do
22
+ s = TCPSocket.new(@host, @port)
23
+ s.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
24
+ s
25
+ end
26
+ end
27
+
28
+ def disconnect
29
+ @sock.close
30
+ end
31
+
32
+ def send(req)
33
+ req.tag = 0
34
+ data = req.encode
35
+ head = [data.length].pack("N")
36
+ @sock.write(head+data)
37
+ end
38
+
39
+ def read
40
+ responses = []
41
+ loop do
42
+ head = @sock.read(4)
43
+ length = head.unpack("N")[0]
44
+ data = @sock.read(length)
45
+ response = Response.decode(data)
46
+ responses << response if response.valid?
47
+ break if response.done?
48
+ end
49
+ responses
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,63 @@
1
+ ## Generated from msg.proto for proto
2
+ require "beefcake"
3
+
4
+ module Fraggle
5
+ module Block
6
+
7
+ class Request
8
+ include Beefcake::Message
9
+
10
+ module Verb
11
+ GET = 1
12
+ SET = 2
13
+ DEL = 3
14
+ ESET = 4
15
+ REV = 5
16
+ NOP = 7
17
+ WATCH = 8
18
+ WALK = 9
19
+ CANCEL = 10
20
+ GETDIR = 14
21
+ STAT = 16
22
+ end
23
+
24
+ required :tag, :int32, 1
25
+ required :verb, Request::Verb, 2
26
+ optional :path, :string, 4
27
+ optional :value, :bytes, 5
28
+ optional :other_tag, :int32, 6
29
+ optional :offset, :int32, 7
30
+ optional :limit, :int32, 8
31
+ optional :rev, :int64, 9
32
+
33
+ end
34
+
35
+ class Response
36
+ include Beefcake::Message
37
+
38
+ module Err
39
+ OTHER = 127
40
+ TAG_IN_USE = 1
41
+ UNKNOWN_VERB = 2
42
+ REDIRECT = 3
43
+ TOO_LATE = 4
44
+ REV_MISMATCH = 5
45
+ BAD_PATH = 6
46
+ MISSING_ARG = 7
47
+ NOTDIR = 20
48
+ ISDIR = 21
49
+ NOENT = 22
50
+ end
51
+
52
+ required :tag, :int32, 1
53
+ required :flags, :int32, 2
54
+ optional :rev, :int64, 3
55
+ optional :path, :string, 5
56
+ optional :value, :bytes, 6
57
+ optional :len, :int32, 8
58
+ optional :err_code, Response::Err, 100
59
+ optional :err_detail, :string, 101
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ module Fraggle
2
+ module Block
3
+ class Response
4
+ VALID = 1
5
+ DONE = 2
6
+
7
+ def valid?
8
+ (flags & VALID) > 0
9
+ end
10
+
11
+ def done?
12
+ (flags & DONE) > 0
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Fraggle
2
+ module Block
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'fraggle/block/client'
2
+ require 'fraggle/block/response'
3
+
4
+ module Fraggle
5
+ module Block
6
+ DEFAULT_URI = "doozerd:?" + [
7
+ "ca=127.0.0.1:8046",
8
+ "ca=127.0.0.1:8041",
9
+ "ca=127.0.0.1:8042",
10
+ "ca=127.0.0.1:8043"
11
+ ].join("&")
12
+
13
+ def self.connect(uri=nil)
14
+ uri = uri || ENV["DOOZER_URI"] || DEFAULT_URI
15
+
16
+ addrs = URI.parse(uri)
17
+
18
+ if addrs.length == 0
19
+ raise ArgumentError, "there were no addrs supplied in the uri (#{uri.inspect})"
20
+ end
21
+
22
+ Client.new(addrs)
23
+ end
24
+
25
+ module URI
26
+ def self.parse(u)
27
+ if u =~ /^doozerd:\?(.*)$/
28
+ parts = $1.split("&")
29
+ parts.inject([]) do |m, pt|
30
+ k, v = pt.split("=")
31
+ if k == "ca"
32
+ m << v
33
+ end
34
+ m
35
+ end
36
+ else
37
+ raise ArgumentError, "invalid doozerd uri"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ module Fraggle
4
+ module Block
5
+ class ClientTest < Test::Unit::TestCase
6
+ include Request::Verb
7
+ def setup
8
+ @client = Fraggle::Block::MockClient.new(['1.1.1.1:1'])
9
+ @connection = @client.connection
10
+ end
11
+
12
+ def test_simple_reconnect
13
+ assert_equal '1.1.1.1:1', @client.connection.address
14
+ @client.addrs = ['127.0.0.1:8047', '127.0.0.1:8048']
15
+ @client.reconnect
16
+ assert_equal '127.0.0.1:8047', @client.connection.address
17
+ @client.reconnect
18
+ assert_equal '127.0.0.1:8048', @client.connection.address
19
+ assert_raises(Fraggle::Block::Client::OutOfNodes) { @client.reconnect }
20
+ end
21
+
22
+ def test_simple_rev
23
+ exp = write_response(Response.new(:rev => 0))
24
+ assert_equal exp.first, @client.rev
25
+ assert_equal [Request.new(:verb => REV, :tag => 0)], @connection.sent
26
+ end
27
+
28
+ def test_simple_get
29
+ exp = write_response(Response.new(:value => 'bar'))
30
+ assert_equal exp.first, @client.get('/foo')
31
+ assert_equal [Request.new(:verb => GET, :tag => 0, :path => '/foo')], @connection.sent
32
+ end
33
+
34
+ def test_simple_getdir
35
+ exp = write_response([Response.new(:verb => GETDIR, :path => '/foo/bar', :flags => 1), Response.new(:verb => GETDIR, :path => '/foo/baz')])
36
+ response = @client.getdir('/foo')
37
+ assert_equal exp, response
38
+ assert_equal '/foo/baz', response.last.path
39
+ assert_equal [Request.new(:verb => GETDIR, :tag => 0, :path => '/foo')], @connection.sent
40
+ end
41
+
42
+ def test_simple_set
43
+ exp = write_response(Response.new(:rev => 10))
44
+ assert_equal exp.first, @client.set('/foo', 'bar', 0)
45
+ assert_equal [Request.new(:verb => SET, :tag => 0, :path => '/foo', :value => 'bar', :rev => 0)], @connection.sent
46
+ end
47
+
48
+ def test_simple_del
49
+ exp = write_response(Response.new(:flags => 1|2))
50
+ assert_equal exp.first, @client.del('/foo', 0)
51
+ assert_equal [Request.new(:verb => DEL, :tag => 0, :path => '/foo', :rev => 0)], @connection.sent
52
+ end
53
+
54
+ def test_simple_walk
55
+ exp = write_response([Response.new(:verb => WALK, :path => '/foo/bar', :flags => 1), Response.new(:verb => WALK, :path => '/foo/baz')])
56
+ assert_equal exp, @client.walk('/foo/*')
57
+ assert_equal [Request.new(:verb => WALK, :tag => 0, :path => '/foo/*')], @connection.sent
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ module Fraggle
4
+ module Block
5
+ class ConnectionTest < Test::Unit::TestCase
6
+ include Request::Verb
7
+ def setup
8
+ @connection = Fraggle::Block::MockConnection.new('1.1.1.1', 1)
9
+ end
10
+
11
+ def test_simple_request
12
+ request = Fraggle::Block::Request.new(:verb => GET, :path => '/foo')
13
+ @connection.send(request)
14
+ @connection.sock.rewind
15
+
16
+ request.tag = 0
17
+ encoded = request.encode
18
+ head = [encoded.length].pack("N")
19
+ assert_equal head+encoded, @connection.sock.read
20
+ end
21
+
22
+ def test_simple_response
23
+ exp = write_response(Response.new(:value => 'test', :rev => 0))
24
+ @connection.sock.rewind
25
+ assert_equal exp, @connection.read
26
+ end
27
+
28
+ def test_simple_disconnect
29
+ assert !@connection.sock.closed?
30
+ @connection.disconnect
31
+ assert @connection.sock.closed?
32
+ end
33
+ end
34
+ end
35
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'test/unit'
2
+ require 'stringio'
3
+
4
+ require 'rubygems'
5
+ $:.unshift File.dirname(__FILE__) + '/../lib'
6
+ require 'fraggle/block'
7
+
8
+ module Fraggle
9
+ module Block
10
+ class MockConnection < Connection
11
+ attr_reader :sent
12
+
13
+ def connect
14
+ StringIO.new
15
+ end
16
+
17
+ def send(request)
18
+ @sent = [request]
19
+ super
20
+ end
21
+ end
22
+
23
+ class MockClient < Client
24
+ def send(request)
25
+ @connection.send(request)
26
+ @connection.sock.rewind
27
+ @connection.read
28
+ end
29
+
30
+ def connection_to(host, port)
31
+ MockConnection.new(host, port)
32
+ end
33
+
34
+ def find_all_of_the_nodes
35
+ @addrs
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ class Test::Unit::TestCase
42
+ def write_response(responses)
43
+ @connection.sock.reopen
44
+ responses = Array(responses)
45
+ responses.each do |response|
46
+ response.tag = 0
47
+ response.flags ||= 1|2
48
+ encoded = response.encode.to_s
49
+ head = [response.encode.length].pack("N")
50
+ @connection.sock.write head+encoded
51
+ end
52
+ responses
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fraggle-block
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Dylan Egan
14
+ - Blake Mizerany
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-04-22 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: beefcake
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 0
32
+ - 3
33
+ version: "0.3"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: system_timer
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 1
47
+ - 0
48
+ version: "1.0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: turn
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ description: A synchronous Ruby client for Doozer.
66
+ email:
67
+ - dylanegan@gmail.com
68
+ - blake.mizerany@gmail.com
69
+ executables:
70
+ - fraggle-block
71
+ extensions: []
72
+
73
+ extra_rdoc_files: []
74
+
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - bin/fraggle-block
81
+ - examples/client.rb
82
+ - fraggle-block.gemspec
83
+ - lib/fraggle/block.rb
84
+ - lib/fraggle/block/client.rb
85
+ - lib/fraggle/block/connection.rb
86
+ - lib/fraggle/block/msg.pb.rb
87
+ - lib/fraggle/block/response.rb
88
+ - lib/fraggle/block/version.rb
89
+ - test/client_test.rb
90
+ - test/connection_test.rb
91
+ - test/helper.rb
92
+ homepage: https://github.com/dylanegan/fraggle-block
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options: []
97
+
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ hash: 3
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ requirements: []
119
+
120
+ rubyforge_project: fraggle-block
121
+ rubygems_version: 1.7.1
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: A synchronous Ruby client for Doozer.
125
+ test_files:
126
+ - test/client_test.rb
127
+ - test/connection_test.rb
128
+ - test/helper.rb