slipcover 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|