fraggle-block 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.
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