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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8235e0960e7f322d83876b91dc94d0d34883831
4
- data.tar.gz: f16b709e7881cfc0458a74f1c0968bd9d2cd8ab2
3
+ metadata.gz: e49398586e21e510dbabb4125a7e6be20a2ce675
4
+ data.tar.gz: 8caf835e9eb85951484f9d9b1e1ef36c9540d59e
5
5
  SHA512:
6
- metadata.gz: 6fba538515310312341f92cff1438f8d54dfb18d8df22ff71e05942ee377bf06e6f7c32c45ec4a91dda88f7bc4fd6f176a78865ac72395d62802531b558b0ff5
7
- data.tar.gz: 0b431d90fd29e1db6ddb9a094e7d9024efda0cee6558643474718727dfa8b85ecf1972aa57e6d14034eca413095100c87d60fe22911de3766e41820efd0251d6
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
@@ -46,10 +46,7 @@ module Slipcover
46
46
  end
47
47
 
48
48
  def database
49
- return @database if @database
50
- @database = Slipcover::Database.new(database_name)
51
- @database.create
52
- @database
49
+ @database ||= Slipcover::Database.new(database_name)
53
50
  end
54
51
 
55
52
  private
@@ -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).symbolize_keys
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
- raise error_class(e).new(e.message)
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
@@ -23,6 +23,8 @@ module Slipcover
23
23
  end
24
24
 
25
25
  def repackage(opts)
26
+ opts = opts.dup
27
+
26
28
  opts.each do |key, value|
27
29
  opts[key] = escape(value) if escape_key?(key)
28
30
  end
@@ -3,5 +3,9 @@ module Slipcover
3
3
  initializer "slipcover.configure_rails_initialization" do
4
4
  Slipcover::Config.rails!
5
5
  end
6
+
7
+ rake_tasks do
8
+ require File.dirname(__FILE__) + "/tasks.rb"
9
+ end
6
10
  end
7
11
  end
@@ -0,0 +1,4 @@
1
+ require 'slipcover'
2
+
3
+ load "#{File.dirname(__FILE__)}/tasks/pull.rake"
4
+
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Slipcover
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -64,7 +64,7 @@ describe Slipcover::Database, 'functional to the Slipcover database' do
64
64
 
65
65
  expect {
66
66
  database.info
67
- }.to raise_error( Slipcover::HttpAdapter::NotFound )
67
+ }.to raise_error( Slipcover::HttpAdapter::DBNotFound )
68
68
  end
69
69
  end
70
70
 
@@ -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({key: 'Deepti'})
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({startkey: 'Da', endkey: 'Fz'})
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.1.1
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-05-27 00:00:00.000000000 Z
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