dpla-analysand 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG +67 -0
- data/Gemfile +8 -0
- data/LICENSE +22 -0
- data/README +48 -0
- data/Rakefile +22 -0
- data/analysand.gemspec +33 -0
- data/bin/analysand +27 -0
- data/lib/analysand.rb +3 -0
- data/lib/analysand/bulk_response.rb +14 -0
- data/lib/analysand/change_watcher.rb +280 -0
- data/lib/analysand/config_response.rb +25 -0
- data/lib/analysand/connection_testing.rb +52 -0
- data/lib/analysand/database.rb +322 -0
- data/lib/analysand/errors.rb +60 -0
- data/lib/analysand/http.rb +90 -0
- data/lib/analysand/instance.rb +255 -0
- data/lib/analysand/reading.rb +26 -0
- data/lib/analysand/response.rb +35 -0
- data/lib/analysand/response_headers.rb +18 -0
- data/lib/analysand/session_response.rb +16 -0
- data/lib/analysand/status_code_predicates.rb +25 -0
- data/lib/analysand/streaming_view_response.rb +90 -0
- data/lib/analysand/version.rb +3 -0
- data/lib/analysand/view_response.rb +24 -0
- data/lib/analysand/view_streaming/builder.rb +142 -0
- data/lib/analysand/viewing.rb +95 -0
- data/lib/analysand/writing.rb +71 -0
- data/script/setup_database.rb +45 -0
- data/spec/analysand/a_response.rb +70 -0
- data/spec/analysand/change_watcher_spec.rb +102 -0
- data/spec/analysand/database_spec.rb +243 -0
- data/spec/analysand/database_writing_spec.rb +488 -0
- data/spec/analysand/instance_spec.rb +205 -0
- data/spec/analysand/response_spec.rb +26 -0
- data/spec/analysand/view_response_spec.rb +44 -0
- data/spec/analysand/view_streaming/builder_spec.rb +73 -0
- data/spec/analysand/view_streaming_spec.rb +122 -0
- data/spec/fixtures/vcr_cassettes/get_config.yml +40 -0
- data/spec/fixtures/vcr_cassettes/get_many_config.yml +40 -0
- data/spec/fixtures/vcr_cassettes/head_request_with_etag.yml +40 -0
- data/spec/fixtures/vcr_cassettes/reload_config.yml +114 -0
- data/spec/fixtures/vcr_cassettes/unauthorized_put_config.yml +43 -0
- data/spec/fixtures/vcr_cassettes/view.yml +40 -0
- data/spec/smoke/database_thread_spec.rb +59 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/database_access.rb +40 -0
- data/spec/support/example_isolation.rb +86 -0
- data/spec/support/test_parameters.rb +39 -0
- metadata +283 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 43f7060645db528706131e0bb8c6feaa5311cb45
|
4
|
+
data.tar.gz: a9d1a3d8e8434f3039d7b0f441a4053000b1bca4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 510cc920abf5f1fa85b82ed86e7d0f8fb4276761a424626122bfa79025c6795b4692481706a57208dde8b5916b0ff3afd4e151a7b496935dfbf84bd384688f29
|
7
|
+
data.tar.gz: 1b247083c79d19ec91aa958712f6e7e7d3d80d33546914b3a7776b62ede70745d71ddea5c5f2e1142f5cb3ffe8ade9e7f8e191df6f4e851b3e0d9c2cd02f3a7d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
Issue numbers refer to issues on Analysand's Github tracker:
|
2
|
+
https://github.com/yipdw/analysand/issues
|
3
|
+
|
4
|
+
3.0.2 (2014-12-01)
|
5
|
+
-----------------
|
6
|
+
|
7
|
+
* Change Celluloid dependency to ~> 0.16.0.
|
8
|
+
|
9
|
+
3.0.1 (2014-01-14)
|
10
|
+
------------------
|
11
|
+
|
12
|
+
* Change Celluloid dependency to ~> 0.15.0.
|
13
|
+
|
14
|
+
3.0.0 (2013-07-08)
|
15
|
+
------------------
|
16
|
+
|
17
|
+
* Change Celluloid dependency to 0.14.
|
18
|
+
|
19
|
+
3.0.0.pre2 (2013-04-15)
|
20
|
+
-----------------------
|
21
|
+
|
22
|
+
* Change Celluloid dependency to 0.13.
|
23
|
+
Please note: Analysand does not require celluloid/autostart. It's up to you
|
24
|
+
to decide whether or not you need that for your application.
|
25
|
+
|
26
|
+
3.0.0.pre (2013-02-26)
|
27
|
+
----------------------
|
28
|
+
|
29
|
+
* Instance#set_config renamed to Instance#put_config
|
30
|
+
* Instance#put_admin, Instance#delete_admin for db admin setup
|
31
|
+
* JSON encoding/decoding removed from Instance#*_config methods: all values are
|
32
|
+
sent to/received from CouchDB verbatim. This means that you'll have to quote all values,
|
33
|
+
e.g.
|
34
|
+
|
35
|
+
instance.set_config("stats/rate", 1200)
|
36
|
+
|
37
|
+
becomes
|
38
|
+
|
39
|
+
instance.put_config("stats/rate", '"1200"')
|
40
|
+
|
41
|
+
|
42
|
+
x.y.z (2012-12-31)
|
43
|
+
------------------
|
44
|
+
|
45
|
+
* Analysand::Writing#bulk_docs! now raises BulkOperationFailed on 401 responses
|
46
|
+
|
47
|
+
2.0.0 (2012-11-29)
|
48
|
+
------------------
|
49
|
+
|
50
|
+
* Instance#establish_session and Instance#renew_session now return a (session,
|
51
|
+
Analysand::Response pair)
|
52
|
+
* Share HTTP code between Database and Instance
|
53
|
+
* Session handling on Instance rewritten: #post_session, #get_session
|
54
|
+
* New response methods: #cookies, #session_cookie
|
55
|
+
|
56
|
+
1.1.0 (2012-11-03)
|
57
|
+
------------------
|
58
|
+
|
59
|
+
* View streaming (#3)
|
60
|
+
* ChangeWatchers now pass credentials when checking CouchDB status (#4)
|
61
|
+
* Some code organization cleanups
|
62
|
+
* require "analysand" now loads the Database and Instance classes
|
63
|
+
|
64
|
+
1.0.1 (2012-10-01)
|
65
|
+
------------------
|
66
|
+
|
67
|
+
* Initial release
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 David Yip
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
1. Analysand
|
2
|
+
|
3
|
+
Analysand is a CouchDB client library of dubious worth. It was extracted from
|
4
|
+
the a-m-v.org catalog application:
|
5
|
+
https://code.ninjawedding.org/git/amvorg-underground/catalog.git.
|
6
|
+
|
7
|
+
Analysand was written for Ruby 1.9. It is known to work on Ruby 1.9.3-p194 and
|
8
|
+
Rubinius 2.0.0.
|
9
|
+
|
10
|
+
2. Features
|
11
|
+
|
12
|
+
* GET, PUT, DELETE on databases
|
13
|
+
* GET, PUT, DELETE, HEAD, COPY on documents
|
14
|
+
* GET, PUT on document attachments
|
15
|
+
* GET, POST on views
|
16
|
+
* GET, PUT on server configuration
|
17
|
+
* GET, PUT, POST on arbitrary service handlers
|
18
|
+
* POST /_session
|
19
|
+
* POST /_bulk_docs
|
20
|
+
* View streaming
|
21
|
+
* Celluloid::IO-based change feed watchers
|
22
|
+
* Cookie and HTTP Basic authentication for all of the above
|
23
|
+
* Database objects can be safely shared across threads
|
24
|
+
|
25
|
+
3. Development
|
26
|
+
|
27
|
+
You'll need a CouchDB >= 1.1.0 instance. I recommend not using a CouchDB
|
28
|
+
instance that you're using for anything else; Analysand requires the presence
|
29
|
+
of specific admin and non-admin users for its test suite.
|
30
|
+
|
31
|
+
See spec/support/test_parameters.rb for usernames, passwords, and connection
|
32
|
+
information.
|
33
|
+
|
34
|
+
Naturally, we hang with all the cool kids:
|
35
|
+
|
36
|
+
* Travis CI: https://travis-ci.org/#!/yipdw/analysand
|
37
|
+
* Code Climate: https://codeclimate.com/github/yipdw/analysand
|
38
|
+
* Gemnasium: https://gemnasium.com/yipdw/analysand
|
39
|
+
|
40
|
+
4. License
|
41
|
+
|
42
|
+
Copyright 2012 David Yip; made available under the MIT license.
|
43
|
+
|
44
|
+
5. Special thanks
|
45
|
+
|
46
|
+
Fear of Tigers, 3LAU, Ellie Goulding, TeddyLoid, Susumu Hirasawa.
|
47
|
+
|
48
|
+
# vim:ts=2:sw=2:et:tw=78
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
namespace :git do
|
9
|
+
desc 'Strip trailing whitespace from tracked source files'
|
10
|
+
task :strip_spaces do
|
11
|
+
`git ls-files`.split("\n").each do |file|
|
12
|
+
puts file
|
13
|
+
|
14
|
+
if `file '#{file}'` =~ /text/
|
15
|
+
sh "git stripspace < '#{file}' > '#{file}.out'"
|
16
|
+
mv "#{file}.out", file
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :spec
|
data/analysand.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/analysand/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["David Yip"]
|
6
|
+
gem.email = ["yipdw@member.fsf.org"]
|
7
|
+
gem.description = %q{A terrible burden for a couch}
|
8
|
+
gem.summary = %q{A CouchDB client of dubious worth}
|
9
|
+
gem.homepage = "https://github.com/yipdw/analysand"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "dpla-analysand"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Analysand::VERSION
|
17
|
+
|
18
|
+
gem.required_ruby_version = '>= 1.9'
|
19
|
+
|
20
|
+
gem.add_dependency 'celluloid', '~> 0.16.0'
|
21
|
+
gem.add_dependency 'celluloid-io'
|
22
|
+
gem.add_dependency 'http_parser.rb'
|
23
|
+
gem.add_dependency 'json'
|
24
|
+
gem.add_dependency 'json-stream'
|
25
|
+
gem.add_dependency 'net-http-persistent'
|
26
|
+
gem.add_dependency 'rack'
|
27
|
+
gem.add_dependency 'yajl-ruby'
|
28
|
+
|
29
|
+
gem.add_development_dependency 'rake'
|
30
|
+
gem.add_development_dependency 'rspec'
|
31
|
+
gem.add_development_dependency 'vcr'
|
32
|
+
gem.add_development_dependency 'webmock'
|
33
|
+
end
|
data/bin/analysand
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
|
+
|
5
|
+
require 'analysand'
|
6
|
+
require 'irb'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
$URI = URI('http://localhost:5984/analysand_test')
|
10
|
+
|
11
|
+
def make_db(uri = $URI)
|
12
|
+
Analysand::Database.new(uri)
|
13
|
+
end
|
14
|
+
|
15
|
+
puts <<-END
|
16
|
+
------------------------------------------------------------------------------
|
17
|
+
Type make_db to make an Analysand::Database object. The default URI is
|
18
|
+
|
19
|
+
#{$URI}
|
20
|
+
|
21
|
+
To point at different databases, supply a URI object to make_db, e.g.
|
22
|
+
|
23
|
+
make_db(URI('https://couchdb.example.org:6984/supersekrit'))
|
24
|
+
------------------------------------------------------------------------------
|
25
|
+
END
|
26
|
+
|
27
|
+
IRB.start
|
data/lib/analysand.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'analysand/response'
|
2
|
+
|
3
|
+
module Analysand
|
4
|
+
##
|
5
|
+
# A subclass of Response that adjusts success? to check for individual error
|
6
|
+
# records.
|
7
|
+
class BulkResponse < Response
|
8
|
+
def success?
|
9
|
+
super && body.none? { |r| r.has_key?('error') }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# vim:ts=2:sw=2:et:tw=78
|
@@ -0,0 +1,280 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'celluloid/io'
|
3
|
+
require 'analysand/connection_testing'
|
4
|
+
require 'http/parser'
|
5
|
+
require 'net/http'
|
6
|
+
require 'rack/utils'
|
7
|
+
require 'uri'
|
8
|
+
require 'yajl'
|
9
|
+
|
10
|
+
module Analysand
|
11
|
+
##
|
12
|
+
# A Celluloid::IO actor that watches the changes feed of a CouchDB database.
|
13
|
+
# When a change is received, it passes the change to a #process method.
|
14
|
+
#
|
15
|
+
# ChangeWatchers monitor changes using continuous mode and set up a heartbeat
|
16
|
+
# to fire approximately every 10 seconds.
|
17
|
+
#
|
18
|
+
# ChangeWatchers begin watching for changes as soon as they are initialized.
|
19
|
+
# To send a shutdown message:
|
20
|
+
#
|
21
|
+
# a.stop
|
22
|
+
#
|
23
|
+
# The watcher will terminate on the next heartbeat.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# Failure modes
|
27
|
+
# =============
|
28
|
+
#
|
29
|
+
# ChangeWatcher deals with the following failures in the following ways:
|
30
|
+
#
|
31
|
+
# * If Errno::ECONNREFUSED is raised whilst connecting to CouchDB, it will
|
32
|
+
# retry the connection in 30 seconds.
|
33
|
+
# * If the connection to CouchDB's changes feed is abruptly terminated, it
|
34
|
+
# dies.
|
35
|
+
# * If an exception is raised during HTTP or JSON parsing, it dies.
|
36
|
+
#
|
37
|
+
# Situations where the actor dies should be handled by a supervisor.
|
38
|
+
#
|
39
|
+
#
|
40
|
+
# Example usage
|
41
|
+
# =============
|
42
|
+
#
|
43
|
+
# class Accumulator < Analysand::ChangeWatcher
|
44
|
+
# attr_accessor :results
|
45
|
+
#
|
46
|
+
# def initialize(database)
|
47
|
+
# super(database)
|
48
|
+
#
|
49
|
+
# self.results = []
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# def process(change)
|
53
|
+
# results << change
|
54
|
+
#
|
55
|
+
# # Once a ChangeWatcher has successfully processed a change, it
|
56
|
+
# # SHOULD invoke #change_processed.
|
57
|
+
# change_processed(change)
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# a = Accumulator.new('http://localhost:5984/mydb')
|
62
|
+
#
|
63
|
+
# # or with supervision:
|
64
|
+
# a = Accumulator.supervise('http://localhost:5984/mydb')
|
65
|
+
class ChangeWatcher
|
66
|
+
include Celluloid::IO
|
67
|
+
include Celluloid::Logger
|
68
|
+
include ConnectionTesting
|
69
|
+
include Rack::Utils
|
70
|
+
|
71
|
+
# Read at most this many bytes off the socket at a time.
|
72
|
+
QUANTUM = 4096
|
73
|
+
|
74
|
+
##
|
75
|
+
# Checks services. If all services pass muster, enters a read loop.
|
76
|
+
#
|
77
|
+
# The database parameter may be either a URL-as-string or a
|
78
|
+
# Analysand::Database.
|
79
|
+
#
|
80
|
+
# If overriding the initializer, you MUST call super.
|
81
|
+
def initialize(database)
|
82
|
+
@db = database
|
83
|
+
@waiting = {}
|
84
|
+
@http_parser = ::Http::Parser.new(self)
|
85
|
+
@json_parser = Yajl::Parser.new
|
86
|
+
@json_parser.on_parse_complete = lambda { |doc| process(doc) }
|
87
|
+
|
88
|
+
async.start
|
89
|
+
end
|
90
|
+
|
91
|
+
# The URI of the changes feed. This URI incorporates any changes
|
92
|
+
# made by customize_query.
|
93
|
+
def changes_feed_uri
|
94
|
+
query = {
|
95
|
+
'feed' => 'continuous',
|
96
|
+
'heartbeat' => '10000'
|
97
|
+
}
|
98
|
+
|
99
|
+
customize_query(query)
|
100
|
+
|
101
|
+
uri = (@db.respond_to?(:uri) ? @db.uri : URI(@db)).dup
|
102
|
+
uri.path += '/_changes'
|
103
|
+
uri.query = build_query(query)
|
104
|
+
uri
|
105
|
+
end
|
106
|
+
|
107
|
+
# The connection_ok method is called before connecting to the changes feed.
|
108
|
+
# By default, it checks that there's an HTTP service listening on the
|
109
|
+
# changes feed.
|
110
|
+
#
|
111
|
+
# If the method returns true, then we connect to the changes feed and begin
|
112
|
+
# processing. If it returns false, a warning message is logged and the
|
113
|
+
# connection check will be retried in 30 seconds.
|
114
|
+
#
|
115
|
+
# This method can be overridden if you need to check additional services.
|
116
|
+
# When you override the method, make sure that you don't discard the return
|
117
|
+
# value of the original definition:
|
118
|
+
#
|
119
|
+
# # Wrong
|
120
|
+
# def connection_ok
|
121
|
+
# super
|
122
|
+
# ...
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# # Right
|
126
|
+
# def connection_ok
|
127
|
+
# ok = super
|
128
|
+
#
|
129
|
+
# ok && my_other_test
|
130
|
+
# end
|
131
|
+
def connection_ok
|
132
|
+
test_http_connection(changes_feed_uri) do |req|
|
133
|
+
customize_request(req)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def start
|
138
|
+
return if @started
|
139
|
+
|
140
|
+
@started = true
|
141
|
+
|
142
|
+
while !connection_ok
|
143
|
+
error "Some services used by #{self.class.name} did not check out ok; will retry in 30 seconds"
|
144
|
+
sleep 30
|
145
|
+
end
|
146
|
+
|
147
|
+
connect
|
148
|
+
|
149
|
+
info "#{self.class} entering read loop"
|
150
|
+
|
151
|
+
@running = true
|
152
|
+
|
153
|
+
while @running
|
154
|
+
@http_parser << @socket.readpartial(QUANTUM)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Once we're done, close things up.
|
158
|
+
@started = false
|
159
|
+
@socket.close
|
160
|
+
end
|
161
|
+
|
162
|
+
def stop
|
163
|
+
@running = false
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Can be used to set query parameters. query is a Hash. The query hash
|
168
|
+
# has two default parameters:
|
169
|
+
#
|
170
|
+
# | Key | Value |
|
171
|
+
# | feed | continuous |
|
172
|
+
# | heartbeat | 10000 |
|
173
|
+
#
|
174
|
+
# It is NOT RECOMMENDED that they be changed.
|
175
|
+
#
|
176
|
+
# By default, this does nothing. Provide behavior in a subclass.
|
177
|
+
def customize_query(query)
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Can be used to add headers. req is a Net::HTTP::Get instance.
|
182
|
+
#
|
183
|
+
# By default, this does nothing. Provide behavior in a subclass.
|
184
|
+
def customize_request(req)
|
185
|
+
end
|
186
|
+
|
187
|
+
##
|
188
|
+
# This method should implement your change-processing logic.
|
189
|
+
#
|
190
|
+
# change is a Hash containing keys id, seq, and changes. See [0] for
|
191
|
+
# more information.
|
192
|
+
#
|
193
|
+
# By default, this does nothing. Provide behavior in a subclass.
|
194
|
+
#
|
195
|
+
# [0]: http://guide.couchdb.org/draft/notifications.html#continuous
|
196
|
+
def process(change)
|
197
|
+
end
|
198
|
+
|
199
|
+
class Waiter < Celluloid::Future
|
200
|
+
alias_method :wait, :value
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Returns an object that can be used to block a thread until a document
|
205
|
+
# with the given ID has been processed.
|
206
|
+
#
|
207
|
+
# Intended for testing.
|
208
|
+
def waiter_for(id)
|
209
|
+
@waiting[id] = true
|
210
|
+
|
211
|
+
Waiter.new do
|
212
|
+
loop do
|
213
|
+
break true if !@waiting[id]
|
214
|
+
sleep 0.1
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Notify waiters.
|
221
|
+
def change_processed(change)
|
222
|
+
@waiting.delete(change['id'])
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# Http::Parser callback.
|
227
|
+
#
|
228
|
+
# @private
|
229
|
+
def on_headers_complete(parser)
|
230
|
+
status = @http_parser.status_code.to_i
|
231
|
+
|
232
|
+
raise "Request failed: expected status 200, got #{status}" unless status == 200
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# Http::Parser callback.
|
237
|
+
#
|
238
|
+
# @private
|
239
|
+
def on_body(chunk)
|
240
|
+
@json_parser << chunk
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# @private
|
245
|
+
def connect
|
246
|
+
req = prepare_request
|
247
|
+
uri = changes_feed_uri
|
248
|
+
|
249
|
+
info "#{self.class} connecting to #{req.path}"
|
250
|
+
|
251
|
+
@socket = TCPSocket.new(uri.host, uri.port)
|
252
|
+
|
253
|
+
# Make the request.
|
254
|
+
data = [
|
255
|
+
"GET #{req.path} HTTP/1.1"
|
256
|
+
]
|
257
|
+
|
258
|
+
req.each_header { |k, v| data << "#{k}: #{v}" }
|
259
|
+
|
260
|
+
@socket.write(data.join("\r\n"))
|
261
|
+
@socket.write("\r\n\r\n")
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# @private
|
266
|
+
def disconnect
|
267
|
+
@socket.close if @socket && !@socket.closed?
|
268
|
+
end
|
269
|
+
|
270
|
+
finalizer :disconnect
|
271
|
+
|
272
|
+
##
|
273
|
+
# @private
|
274
|
+
def prepare_request
|
275
|
+
Net::HTTP::Get.new(changes_feed_uri.to_s).tap do |req|
|
276
|
+
customize_request(req)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|