slipcover 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/Rakefile +11 -0
- data/lib/slipcover/dev_tools.rb +68 -0
- data/lib/slipcover/document.rb +1 -4
- data/lib/slipcover/http_adapter.rb +27 -3
- data/lib/slipcover/query.rb +2 -0
- data/lib/slipcover/railtie.rb +4 -0
- data/lib/slipcover/tasks.rb +4 -0
- data/lib/slipcover/tasks/pull.rake +12 -0
- data/lib/slipcover/version.rb +1 -1
- data/spec/database_spec.rb +1 -1
- data/spec/http_adapter_spec.rb +37 -0
- data/spec/query_spec.rb +3 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e49398586e21e510dbabb4125a7e6be20a2ce675
|
4
|
+
data.tar.gz: 8caf835e9eb85951484f9d9b1e1ef36c9540d59e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a960e3e93453cd982db2c6195517c1f15ed71b7dc3185d43b748a2f31fbd53c4c2fadeab9431146c3171d56f0705252193a216a0aa4096259c305271d05a9fb4
|
7
|
+
data.tar.gz: 3ddda15b941a725632490bb1046d0b93b7af34aa99c01c7aea059f544bf54147721e6ea8cccc5db451fdfb9b155b679e68b22dde06ac6e697ed5d026d9642523
|
data/Rakefile
CHANGED
@@ -1 +1,12 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
task :console do
|
4
|
+
require 'irb'
|
5
|
+
require 'irb/completion'
|
6
|
+
require 'slipcover'
|
7
|
+
Slipcover::Config.yaml_path = File.expand_path("../spec/support/slipcover.yml", __FILE__)
|
8
|
+
Slipcover::Config.view_dir = File.expand_path("../spec/support/slipcover_views", __FILE__)
|
9
|
+
ARGV.clear
|
10
|
+
IRB.start
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'logger'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Slipcover
|
7
|
+
module DevTools
|
8
|
+
|
9
|
+
class Replicator
|
10
|
+
attr_reader :source_environment, :source_server
|
11
|
+
attr_reader :target_environment, :target_server
|
12
|
+
attr_reader :logger, :http
|
13
|
+
|
14
|
+
def initialize(source_environment = "staging", target_environment = "development", logger = Logger.new("/dev/null"))
|
15
|
+
@source_environment = source_environment
|
16
|
+
@source_server = Slipcover::Server.new(Slipcover::Config.yaml_path, source_environment)
|
17
|
+
|
18
|
+
@target_environment = target_environment
|
19
|
+
@target_server = Slipcover::Server.new(Slipcover::Config.yaml_path, target_environment)
|
20
|
+
|
21
|
+
@logger = logger
|
22
|
+
@http = HttpAdapter.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform
|
26
|
+
logger.info "Fetching list of #{source_environment} databases..."
|
27
|
+
logger.info "Replicating these dbs: #{dbs_to_replicate.join(",")}"
|
28
|
+
|
29
|
+
dbs_to_replicate.each_with_index.map do |db, i|
|
30
|
+
Thread.new(db, i, &method(:replicate))
|
31
|
+
end.map(&:join)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dbs_to_replicate
|
35
|
+
@dbs_to_replicate ||= begin
|
36
|
+
require 'slipcover/http_adapter'
|
37
|
+
dbs = http.get(source_server.url + "/_all_dbs")
|
38
|
+
dbs.select { |db| db.end_with?(source_environment) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def replicate(source_db_name, th_id)
|
43
|
+
target_db_name = infer_target_db_name(source_db_name)
|
44
|
+
source_url = db_url(source_server, source_db_name)
|
45
|
+
target_url = db_url(target_server, target_db_name)
|
46
|
+
|
47
|
+
logger.info "#{th_id}: Replicating #{source_url} into #{target_url}"
|
48
|
+
|
49
|
+
response = http.post(target_server.url + "/_replicate", { source: source_url, target: target_url, create_target: true })
|
50
|
+
|
51
|
+
logger.debug("#{th_id}: " + response.to_json)
|
52
|
+
|
53
|
+
raise "#{th_id}: Replication of #{source_url} into #{target_url} failed" unless response[:ok]
|
54
|
+
|
55
|
+
logger.info "#{th_id}: Done replicating #{source_url} into #{target_url}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def infer_target_db_name(source_db_name)
|
59
|
+
source_db_name.sub(/#{source_environment}$/, target_environment)
|
60
|
+
end
|
61
|
+
|
62
|
+
def db_url(server, db_name)
|
63
|
+
"http://" + server.url + "/" + db_name
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/slipcover/document.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module Slipcover
|
2
2
|
class HttpAdapter
|
3
|
+
def head(url, data={})
|
4
|
+
try {
|
5
|
+
parse( RestClient.head(url + query_string(data), headers) )
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
3
9
|
def get(url, data={})
|
4
10
|
try {
|
5
11
|
parse( RestClient.get(url + query_string(data), headers) )
|
@@ -26,7 +32,7 @@ module Slipcover
|
|
26
32
|
|
27
33
|
def query_string(data)
|
28
34
|
return "" if data.empty?
|
29
|
-
|
35
|
+
|
30
36
|
query = data.map do |key, value|
|
31
37
|
"#{key}=#{value}"
|
32
38
|
end.join("&")
|
@@ -35,7 +41,8 @@ module Slipcover
|
|
35
41
|
end
|
36
42
|
|
37
43
|
def parse(response)
|
38
|
-
JSON.parse(response)
|
44
|
+
parsed = JSON.parse(response)
|
45
|
+
parsed.is_a?(Hash) ? parsed.symbolize_keys : parsed
|
39
46
|
end
|
40
47
|
|
41
48
|
def try
|
@@ -45,7 +52,15 @@ module Slipcover
|
|
45
52
|
end
|
46
53
|
|
47
54
|
def reraise(e)
|
48
|
-
|
55
|
+
# As we keep doing more stuff with couchdb we might want to update this list
|
56
|
+
response = JSON.parse(e.response) rescue Hash.new
|
57
|
+
case response["reason"]
|
58
|
+
when "no_db_file" then raise DBNotFound, e.response
|
59
|
+
when "missing_named_view" then raise NamedViewNotFound, e.response
|
60
|
+
when "missing" then raise DocumentNotFound, e.response
|
61
|
+
else
|
62
|
+
raise error_class(e).new(response.empty? ? e.message : e.response)
|
63
|
+
end
|
49
64
|
end
|
50
65
|
|
51
66
|
def error_class(e)
|
@@ -67,6 +82,15 @@ module Slipcover
|
|
67
82
|
class NotFound < RuntimeError
|
68
83
|
end
|
69
84
|
|
85
|
+
class DBNotFound < RuntimeError
|
86
|
+
end
|
87
|
+
|
88
|
+
class NamedViewNotFound < RuntimeError
|
89
|
+
end
|
90
|
+
|
91
|
+
class DocumentNotFound < RuntimeError
|
92
|
+
end
|
93
|
+
|
70
94
|
def headers
|
71
95
|
{:content_type => :json, :accept => :json}
|
72
96
|
end
|
data/lib/slipcover/query.rb
CHANGED
data/lib/slipcover/railtie.rb
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
namespace :slipcover do
|
2
|
+
namespace :db do
|
3
|
+
desc "Pull all CouchDB databases from the target environment into the local CouchDB"
|
4
|
+
task :pull, [:stage_name] => (:environment if defined?(Rails)) do |t, args|
|
5
|
+
require 'slipcover/dev_tools'
|
6
|
+
logger = Logger.new(STDOUT)
|
7
|
+
logger.level = ENV["DEBUG"] ? Logger::DEBUG : Logger::INFO
|
8
|
+
stage = args[:stage_name] || "staging"
|
9
|
+
Slipcover::DevTools::Replicator.new(stage, "development", logger).perform
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/slipcover/version.rb
CHANGED
data/spec/database_spec.rb
CHANGED
data/spec/http_adapter_spec.rb
CHANGED
@@ -29,4 +29,41 @@ describe Slipcover::HttpAdapter do
|
|
29
29
|
adapter.delete('http://url.com', {foo: 'bar'})
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
describe "response processing" do
|
34
|
+
it "can digest json object result" do
|
35
|
+
RestClient.stub(:get).and_return('{ "foo": "bar" }')
|
36
|
+
adapter.get('http://url.com/db/something_returing_an_array').should == { foo: "bar" }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can digest json array result" do
|
40
|
+
RestClient.stub(:get).and_return("[123, 456]")
|
41
|
+
adapter.get('http://url.com/db/something_returing_an_array').should == [123, 456]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "edge cases" do
|
46
|
+
it "raises DBNotFound when the error reason is 'no_db_file'" do
|
47
|
+
RestClient.stub(:post).and_raise(RestClient::ResourceNotFound.new('{"error": "not_found", "reason": "no_db_file"}'))
|
48
|
+
expect do
|
49
|
+
adapter.post('http://url.com/non_existing_db/document_to_update', {})
|
50
|
+
end.to raise_error(Slipcover::HttpAdapter::DBNotFound)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "raises NamedViewNotFound when the error reason is 'missing_named_view'" do
|
54
|
+
RestClient.stub(:post).and_raise(RestClient::ResourceNotFound.new('{"error": "not_found", "reason": "missing_named_view"}'))
|
55
|
+
expect do
|
56
|
+
adapter.post('http://url.com/existing_db/non_existing_document', {})
|
57
|
+
end.to raise_error(Slipcover::HttpAdapter::NamedViewNotFound)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "raises DocumentNotFound when the error reason is 'missing'" do
|
61
|
+
RestClient.stub(:post).and_raise(RestClient::ResourceNotFound.new('{"error": "not_found", "reason": "missing"}'))
|
62
|
+
expect do
|
63
|
+
adapter.post('http://url.com/existing_db/non_existing_document', {})
|
64
|
+
end.to raise_error(Slipcover::HttpAdapter::DocumentNotFound)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Need to test other exceptions!
|
68
|
+
end
|
32
69
|
end
|
data/spec/query_spec.rb
CHANGED
@@ -37,7 +37,7 @@ describe Slipcover::Query do
|
|
37
37
|
context 'with options' do
|
38
38
|
context 'with key' do
|
39
39
|
it "returns only the related records" do
|
40
|
-
results = query.all(
|
40
|
+
results = query.all(key: 'Deepti')
|
41
41
|
results.size.should == 1
|
42
42
|
results.first[:name].should == 'Deepti'
|
43
43
|
end
|
@@ -45,9 +45,9 @@ describe Slipcover::Query do
|
|
45
45
|
|
46
46
|
context 'with a startkey and endkey' do
|
47
47
|
it "return the range of docs" do
|
48
|
-
results = query.all(
|
48
|
+
results = query.all(startkey: 'Da', endkey: 'Fz')
|
49
49
|
results.size.should == 2
|
50
|
-
results.map{ |doc| doc[:name] }.should =~ ['Deepti', 'Fito']
|
50
|
+
results.map { |doc| doc[:name] }.should =~ ['Deepti', 'Fito']
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slipcover
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kane Baccigalupi
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-06-04 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rest-client
|
@@ -102,11 +102,14 @@ files:
|
|
102
102
|
- lib/slipcover/config.rb
|
103
103
|
- lib/slipcover/database.rb
|
104
104
|
- lib/slipcover/design_document.rb
|
105
|
+
- lib/slipcover/dev_tools.rb
|
105
106
|
- lib/slipcover/document.rb
|
106
107
|
- lib/slipcover/http_adapter.rb
|
107
108
|
- lib/slipcover/query.rb
|
108
109
|
- lib/slipcover/railtie.rb
|
109
110
|
- lib/slipcover/server.rb
|
111
|
+
- lib/slipcover/tasks.rb
|
112
|
+
- lib/slipcover/tasks/pull.rake
|
110
113
|
- lib/slipcover/version.rb
|
111
114
|
- slipcover.gemspec
|
112
115
|
- spec/database_spec.rb
|