deploy_mongo 0.0.1

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.
Files changed (37) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +34 -0
  4. data/README +43 -0
  5. data/Rakefile +1 -0
  6. data/bin/deploy_mongo +40 -0
  7. data/deploy_mongo.gemspec +24 -0
  8. data/deploy_mongo.rake +0 -0
  9. data/lib/deploy_mongo/config.rb +40 -0
  10. data/lib/deploy_mongo/db_schema.rb +38 -0
  11. data/lib/deploy_mongo/delta.rb +31 -0
  12. data/lib/deploy_mongo/delta_loader.rb +45 -0
  13. data/lib/deploy_mongo/delta_processor.rb +22 -0
  14. data/lib/deploy_mongo/deploy.rb +70 -0
  15. data/lib/deploy_mongo/mongo_shell.rb +17 -0
  16. data/lib/deploy_mongo/repository.rb +23 -0
  17. data/lib/deploy_mongo/version.rb +3 -0
  18. data/lib/deploy_mongo.rb +13 -0
  19. data/spec/integration/config_spec.rb +36 -0
  20. data/spec/integration/database_populator.rb +28 -0
  21. data/spec/integration/delta_loader_spec.rb +53 -0
  22. data/spec/integration/delta_processor_spec.rb +36 -0
  23. data/spec/integration/deltas/11_update_phone_with_std_codes.js +15 -0
  24. data/spec/integration/deltas/12_delete_customer_name_1.js +5 -0
  25. data/spec/integration/deltas/13_copy_and_create_new_customer.js +10 -0
  26. data/spec/integration/deltas/1_add_address_to_customer.js +5 -0
  27. data/spec/integration/deltas/2_add_phone_to_customer.js +5 -0
  28. data/spec/integration/deltas/5_not_a_yaml_file.rb +0 -0
  29. data/spec/integration/deploy_spec.rb +57 -0
  30. data/spec/integration/repository_spec.rb +27 -0
  31. data/spec/mongodb.yml +5 -0
  32. data/spec/spec_helper.rb +17 -0
  33. data/spec/unit/db_schema_spec.rb +46 -0
  34. data/spec/unit/delta_processor_spec.rb +38 -0
  35. data/spec/unit/deploy_spec.rb +90 -0
  36. data/spec/unit/repository_spec.rb +56 -0
  37. metadata +146 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in deploy_mongo.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec',">= 2.6.0"
7
+ gem 'mongo'
8
+ gem 'bson'
9
+ gem 'bson_ext'
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ deploy_mongo (0.0.1)
5
+ bson (>= 1.3.1)
6
+ bson_ext (>= 1.3.1)
7
+ mongo (>= 1.3.1)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ bson (1.3.1)
13
+ bson_ext (1.3.1)
14
+ diff-lcs (1.1.2)
15
+ mongo (1.3.1)
16
+ bson (>= 1.3.1)
17
+ rspec (2.6.0)
18
+ rspec-core (~> 2.6.0)
19
+ rspec-expectations (~> 2.6.0)
20
+ rspec-mocks (~> 2.6.0)
21
+ rspec-core (2.6.4)
22
+ rspec-expectations (2.6.0)
23
+ diff-lcs (~> 1.1.2)
24
+ rspec-mocks (2.6.0)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bson
31
+ bson_ext
32
+ deploy_mongo!
33
+ mongo
34
+ rspec (>= 2.6.0)
data/README ADDED
@@ -0,0 +1,43 @@
1
+ deploy_mongo
2
+ =============
3
+ deploy_mongo is db deploy tool for mongodb.
4
+
5
+
6
+ How to install
7
+ =================
8
+ gem install deploy_mongo
9
+
10
+
11
+ Usage
12
+ ======
13
+ deploy_mongo 'path/to/mongodb.yml' [rollback:(1|2|3|..|all)]
14
+
15
+ [rollback:all] rollbacks all the deltas
16
+
17
+
18
+ Example
19
+ =======
20
+ deploy_mongo/spec/mongodb.yml for mongodb.yml config file
21
+ deploy_mongo/spec/integration/deltas for delta files
22
+
23
+
24
+ Config Parameters
25
+ ==================
26
+ hostname: <couchdb database server hostname>
27
+ port: <couchdb server port number , by default 5984>
28
+ delta_path: < path/to/deltas folder relative to configdb.yml>
29
+ database: <couchdb database name>
30
+ mongo_shell_path: < full path to mongo shell (can be found in mongodb install folder/bin )>
31
+
32
+ Delta File name format
33
+ =======================
34
+ <Delta Number>_<Description>.js
35
+ example 1_add_address_to_customer.js
36
+
37
+
38
+ Delta File content format
39
+ =========================
40
+ <command used to modify database>
41
+ //@undo
42
+ <rollback command used when rolling back applied delta.>
43
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/deploy_mongo ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ def print_usage
5
+ puts "usage: deploy_mongo 'path/to/mongodb.yml' [rollback:(1|2|..|all)]"
6
+ puts "example: deploy_mongo 'path/to/mongodb.yml'"
7
+ puts "example: deploy_mongo 'path/to/mongodb.yml' rollback:all"
8
+ puts "example: deploy_mongo 'path/to/mongodb.yml' rollback:1"
9
+ exit(-1)
10
+ end
11
+
12
+ if (ARGV.length < 1 || ARGV.length > 2 )
13
+ print_usage
14
+ end
15
+
16
+ if (ARGV.length == 2 and !ARGV[1].start_with?('rollback:'))
17
+ print_usage
18
+ end
19
+
20
+ no_of_deltas_to_rollback = 0
21
+
22
+ if(ARGV.length == 2)
23
+ if (ARGV[1].split(':')[1] != 'all')
24
+ no_of_deltas_to_rollback = ARGV[1].split(':')[1].to_i
25
+ print_usage if no_of_deltas_to_rollback == 0
26
+ end
27
+ end
28
+
29
+ if (!File.exists?(ARGV[0]))
30
+ puts "file '#{ARGV[0]}' does not exist "
31
+ exit(-1)
32
+ end
33
+
34
+
35
+ require 'deploy_mongo'
36
+
37
+ config = DeployMongo::Config.create_from_file(ARGV[0])
38
+ deploy = DeployMongo::Deploy.new(config)
39
+ deploy.run if ARGV.length == 1
40
+ deploy.rollback(no_of_deltas_to_rollback) if ARGV.length == 2
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "deploy_mongo/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "deploy_mongo"
7
+ s.version = DeployMongo::VERSION
8
+ s.authors = ["venky"]
9
+ s.email = ["venkatesh.swdev@gmail.com"]
10
+ s.homepage = "https://github.com/venkateshcm/deploy_mongo"
11
+ s.summary = "dbDeploy for couchdb"
12
+ s.description = "dbDeploy for couchdb"
13
+
14
+ s.rubyforge_project = "deploy_mongo"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_dependency('bson', '>= 1.3.1')
21
+ s.add_dependency('bson_ext', '>= 1.3.1')
22
+ s.add_dependency('mongo', '>= 1.3.1')
23
+ s.add_development_dependency('rspec', '>= 2.6.0')
24
+ end
data/deploy_mongo.rake ADDED
File without changes
@@ -0,0 +1,40 @@
1
+ module DeployMongo
2
+
3
+ class Config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def self.create_from_file(config_file)
10
+ config = YAML.load_file(config_file)
11
+ config = config.merge("config_folder_path" => File.dirname(config_file))
12
+ Config.new(config)
13
+ end
14
+
15
+ def hostname
16
+ @config["hostname"]
17
+ end
18
+
19
+ def port
20
+ @config["port"]
21
+ end
22
+
23
+ def delta_path
24
+ @config["config_folder_path"] + "/" + @config["delta_path"]
25
+ end
26
+
27
+ def database
28
+ @config["database"]
29
+ end
30
+
31
+ def mongo_shell_path
32
+ @config["mongo_shell_path"]
33
+ end
34
+
35
+ def merge_config(config)
36
+ @config = @config.merge(config)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ module DeployMongo
2
+
3
+ class DbSchema
4
+
5
+ def initialize(schema,repository)
6
+ @schema = schema
7
+ @repository = repository
8
+ end
9
+
10
+ def self.load_or_create(config,repository)
11
+ default_schema = {"_id"=>"schema__schema_document_key__", 'applied_deltas'=>[]}
12
+ schema = repository.get_schema
13
+ if (schema.nil?)
14
+ repository.save_schema(default_schema)
15
+ schema = repository.get_schema
16
+ end
17
+ DbSchema.new(schema,repository)
18
+ end
19
+
20
+ def applied_deltas
21
+ @schema["applied_deltas"]
22
+ end
23
+
24
+ def completed(delta)
25
+ @schema['applied_deltas'].push(delta.id)
26
+ @repository.save_schema(@schema)
27
+ @schema = @repository.get_schema
28
+ end
29
+
30
+ def rollback(delta)
31
+ @schema['applied_deltas'].delete(delta.id)
32
+ @repository.save_schema(@schema)
33
+ @schema = @repository.get_schema
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,31 @@
1
+ module DeployMongo
2
+
3
+ class Delta
4
+ def initialize(id,file_name,command,rollback_command)
5
+ @id=id
6
+ @file_name = file_name
7
+ @command = command
8
+ @rollback_command = rollback_command
9
+ end
10
+
11
+ def id
12
+ @id
13
+ end
14
+
15
+
16
+ def file_name
17
+ @file_name
18
+ end
19
+
20
+ def command
21
+ @command
22
+ end
23
+
24
+ def rollback_command
25
+ @rollback_command
26
+ end
27
+
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,45 @@
1
+ module DeployMongo
2
+
3
+ class DeltaLoader
4
+ def initialize(deltas_folder)
5
+ @deltas_folder = deltas_folder
6
+ end
7
+
8
+ def get_deltas
9
+ hash = {}
10
+ files = Dir["#{@deltas_folder}/*.js"].select {|f| File.file?(f)}
11
+ files.each do |file|
12
+ file_name = File.basename(file)
13
+ key = file_name.split('_')[0].to_i
14
+ if (key == 0)
15
+ e = RuntimeError.new("invalid file name #{file_name}")
16
+ raise e
17
+ end
18
+ if (hash.has_key?(key))
19
+ e = RuntimeError.new("duplicate key found in file #{file_name}")
20
+ raise e
21
+ end
22
+
23
+ hash[key] = convert_to_delta(key,file)
24
+ end
25
+ hash
26
+ end
27
+
28
+ def convert_to_delta(id, file)
29
+ file = File.open(file, "rb")
30
+ contents = file.read
31
+ file.close
32
+ command = ''
33
+ rollback_command = ''
34
+ commands = contents.split('//@undo')
35
+ raise "#{file_name} content is not valid " if commands.count == 0
36
+ command = commands[0]
37
+ rollback_command = commands[1] if commands[1]
38
+
39
+ file_name = File.basename(file)
40
+ Delta.new(id,file_name,command,rollback_command)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,22 @@
1
+ module DeployMongo
2
+
3
+ class DeltaProcessor
4
+ def initialize(config,delta)
5
+ @delta = delta
6
+ @config = config
7
+ end
8
+
9
+ def apply
10
+ user_command = @delta.command.gsub("'","''")
11
+ shell = MongoShell.new(@config.database)
12
+ shell.execute(user_command)
13
+ end
14
+
15
+ def rollback
16
+ user_rollback_command = @delta.rollback_command.gsub("'","''")
17
+ shell = MongoShell.new(@config.database)
18
+ shell.execute(user_rollback_command)
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,70 @@
1
+ module DeployMongo
2
+
3
+ class Deploy
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def run
9
+ repository = Repository.new(@config)
10
+ delta_loader = DeltaLoader.new(@config.delta_path)
11
+ deltas_map = delta_loader.get_deltas
12
+ couch_schema = DbSchema.load_or_create(@config,repository)
13
+ all_delta_keys = deltas_map.keys.sort
14
+ applied_deltas = couch_schema.applied_deltas.sort
15
+ last_applied_delta_id = 0
16
+ last_applied_delta_id = applied_deltas[-1] if applied_deltas.count > 0
17
+ delta_keys_to_apply = all_delta_keys.select {|key| key > last_applied_delta_id }
18
+
19
+ deltas = []
20
+ puts "Deltas #{all_delta_keys} found"
21
+ puts "Already applied deltas #{applied_deltas}"
22
+ puts "Applying deltas #{delta_keys_to_apply}"
23
+ puts ""
24
+ puts "Start applying deltas"
25
+
26
+ delta_keys_to_apply.each do |key|
27
+ delta = deltas_map[key]
28
+ DeltaProcessor.new(@config,delta).apply
29
+ couch_schema.completed(delta)
30
+ deltas.push(delta)
31
+ puts "applied delta #{delta.file_name}"
32
+ end
33
+
34
+ puts "Finished applying deltas"
35
+ puts ""
36
+ puts "Deltas #{all_delta_keys - applied_deltas - delta_keys_to_apply} where skiped"
37
+
38
+ deltas
39
+ end
40
+
41
+ def rollback (no_of_deltas_to_rollback = 0)
42
+
43
+ repository = Repository.new(@config)
44
+ delta_loader = DeltaLoader.new(@config.delta_path)
45
+ deltas_map = delta_loader.get_deltas
46
+ couch_schema = DbSchema.load_or_create(@config,repository)
47
+ applied_deltas = couch_schema.applied_deltas.sort.reverse
48
+ no_of_deltas_to_rollback = applied_deltas.length if no_of_deltas_to_rollback == 0
49
+
50
+ deltas = []
51
+ puts "Already applied deltas #{applied_deltas}"
52
+ puts "Rolling back deltas #{applied_deltas[0..(no_of_deltas_to_rollback-1)]}"
53
+ puts ""
54
+ puts "Start rolling back deltas"
55
+
56
+ applied_deltas[0..(no_of_deltas_to_rollback-1)].each do |key|
57
+ delta = deltas_map[key]
58
+ DeltaProcessor.new(@config,delta).rollback
59
+ couch_schema.rollback(delta)
60
+ deltas.push(delta)
61
+ puts "rollback delta #{delta.file_name}"
62
+ end
63
+
64
+ puts "Finished rolling back deltas"
65
+ deltas
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,17 @@
1
+ module DeployMongo
2
+ class MongoShell
3
+
4
+ def initialize(database)
5
+ @database = database
6
+ end
7
+
8
+ def execute(command)
9
+ command = command.gsub("'","''")
10
+ mongo_command = "/Users/admin/work/mongodb-osx-x86_64-1.8.2/bin/mongo #{@database} --eval '#{command}'"
11
+ #puts mongo_command
12
+ `#{mongo_command}`
13
+ raise "error" if ($?.to_i != 0)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module DeployMongo
2
+
3
+ class Repository
4
+ PAGE_SIZE = 10
5
+
6
+ def initialize(couch_config)
7
+ @config = couch_config
8
+ end
9
+
10
+ def get_schema
11
+ db = Mongo::Connection.new(@config.hostname,@config.port).db(@config.database)
12
+ schema = db.collection("schema").find_one("_id" => "schema__schema_document_key__")
13
+ schema
14
+ end
15
+
16
+ def save_schema(json)
17
+ db = Mongo::Connection.new(@config.hostname,@config.port).db(@config.database)
18
+ json = json.merge("_id" => "schema__schema_document_key__")
19
+ db.collection("schema").save(json)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,3 @@
1
+ module DeployMongo
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+ require 'yaml'
3
+ require 'mongo'
4
+
5
+ require 'deploy_mongo/version'
6
+ require 'deploy_mongo/config'
7
+ require 'deploy_mongo/repository'
8
+ require 'deploy_mongo/delta_processor'
9
+ require 'deploy_mongo/delta_loader'
10
+ require 'deploy_mongo/delta'
11
+ require 'deploy_mongo/deploy'
12
+ require 'deploy_mongo/db_schema'
13
+ require 'deploy_mongo/mongo_shell'
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+ describe Config, "read couch db config" do
5
+ it "should load hostname from mongodb.yml" do
6
+ Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml').hostname.should == "localhost"
7
+ end
8
+
9
+ it "should load portnumber from mongodb.yml" do
10
+ Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml').port.should == 27017
11
+ end
12
+
13
+ it "should load delta path" do
14
+ Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml').delta_path.should == File.dirname(__FILE__) + "/../integration/deltas"
15
+ end
16
+
17
+ it "should load database name" do
18
+ Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml').database.should == "test"
19
+ end
20
+
21
+ it "should load mongo shell path" do
22
+ Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml').mongo_shell_path.should == "/Users/admin/work/mongodb-osx-x86_64-1.8.2/bin/mongo"
23
+ end
24
+
25
+ it "should merge config with passed in values" do
26
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
27
+ config.merge_config("database"=>"db")
28
+ config.database.should == "db"
29
+ end
30
+
31
+ it "should fail with an exception when config file is not found" do
32
+ lambda { Config.create_from_file(File.dirname(__FILE__) + '/../couchdb1.yml') }.should raise_error()
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ module DeployMongo
2
+ class DatabasePopulator
3
+ def initialize(database)
4
+ @database = database
5
+ end
6
+
7
+ def with_type(doc_type)
8
+ @doc_type = doc_type
9
+ self
10
+ end
11
+
12
+ def with_records(no_of_records)
13
+ @no_of_records = no_of_records
14
+ self
15
+ end
16
+
17
+ def build
18
+ mongo_shell = MongoShell.new(@database)
19
+ mongo_shell.execute("db.dropDatabase();")
20
+ (1..@no_of_records).each do |i|
21
+ h = {"name"=> "name_#{i}","type"=>@doc_type}
22
+ json = JSON.generate(h)
23
+ mongo_shell.execute("db.#{@doc_type}.save(#{json})")
24
+ end
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+
5
+ describe DeltaLoader, "load all delta" do
6
+
7
+ it "should load all YAML files from the deltas folder" do
8
+
9
+ command = "db.customer.update({},{$set: {\"address\": \"some address\" } } , false , true );\n\n"
10
+ rollback_command = "\n\ndb.customer.update({},{$unset: {\"address\": 1}},false,true);\n"
11
+
12
+ delta_loader = DeltaLoader.new(File.dirname(__FILE__)+'/deltas')
13
+ deltas = delta_loader.get_deltas
14
+ deltas.count.should == 5
15
+ deltas[1].file_name.should == "1_add_address_to_customer.js"
16
+ deltas[1].command.should == command
17
+ deltas[1].rollback_command.should == rollback_command
18
+ end
19
+
20
+ it "should raise an error when file is not in deltanumber_description format" do
21
+ File.new(File.dirname(__FILE__)+'/deltas/invalid_delta_file.js','w')
22
+ begin
23
+ delta_loader = DeltaLoader.new(File.dirname(__FILE__)+'/deltas')
24
+ lambda { delta_loader.get_deltas }.should raise_error()
25
+ ensure
26
+ File.delete(File.dirname(__FILE__)+'/deltas/invalid_delta_file.js')
27
+ end
28
+ end
29
+
30
+ it "should raise an error when two deltas have the same key" do
31
+ File.new(File.dirname(__FILE__)+'/deltas/11_duplicate_key.js','w')
32
+ begin
33
+ delta_loader = DeltaLoader.new(File.dirname(__FILE__)+'/deltas')
34
+ lambda { delta_loader.get_deltas }.should raise_error()
35
+ ensure
36
+ File.delete(File.dirname(__FILE__)+'/deltas/11_duplicate_key.js')
37
+ end
38
+
39
+ end
40
+
41
+ it "should raise an error when file content does not have both type and command" do
42
+ File.new(File.dirname(__FILE__)+'/deltas/100_invalid_delta_file_content.js','w')
43
+ begin
44
+ delta_loader = DeltaLoader.new(File.dirname(__FILE__)+'/deltas')
45
+ lambda { delta_loader.get_deltas }.should raise_error()
46
+ ensure
47
+ File.delete(File.dirname(__FILE__)+'/deltas/100_invalid_delta_file_content.js')
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/database_populator'
3
+
4
+ module DeployMongo
5
+
6
+ describe DeltaProcessor, "integration" do
7
+
8
+ before :all do
9
+ DatabasePopulator.new("test").with_type("customer").with_records(30).build
10
+ end
11
+
12
+
13
+ it "integration load relavent documents and apply delta" do
14
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
15
+ command = 'db.customer.update({},{$set: {"address":"some address"}},false,true)'
16
+ rollback_command = 'db.customer.update({},{$unset: {"address":1}},false,true)'
17
+ delta = Delta.new(1,'file_name',command,rollback_command)
18
+ delta_processor = DeltaProcessor.new(config,delta)
19
+ delta_processor.apply
20
+ end
21
+
22
+ it "integration load relavent documents apply delta and rollback" do
23
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
24
+ command = 'db.customer.update({},{$set: {"address":"some address"}},false,true)'
25
+ rollback_command = 'db.customer.update({},{$unset: {"address":1}},false,true)'
26
+ delta = Delta.new(1,'file_name',command,rollback_command)
27
+ delta_processor = DeltaProcessor.new(config,delta)
28
+ delta_processor.apply
29
+
30
+ delta_processor.rollback
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,15 @@
1
+ db.customer.find({}).forEach(update_phone);
2
+ function update_phone(doc){
3
+ doc.phone = "+91" + doc.phone;
4
+ db.customer.save(doc);
5
+ }
6
+
7
+ //@undo
8
+
9
+ db.customer.find({}).forEach(update_phone);
10
+
11
+ function update_phone(doc){
12
+ doc.phone = doc.phone.substring(3);
13
+ db.customer.save(doc);
14
+ }
15
+
@@ -0,0 +1,5 @@
1
+ db.customer.remove({ "name" : "name_1" });
2
+
3
+ //@undo
4
+
5
+ db.customer.save({"name" : "name_1", "address" : "some address" , "phone" : "+9168687868" });
@@ -0,0 +1,10 @@
1
+ db.customer.find({ "name" : "name_2" }).forEach(create_customer);
2
+ function create_customer(doc){
3
+ delete doc._id;
4
+ doc.name = "new name";
5
+ db.customer.save(doc);
6
+ }
7
+
8
+ //@undo
9
+
10
+ db.customer.remove({"name" : "new name"})
@@ -0,0 +1,5 @@
1
+ db.customer.update({},{$set: {"address": "some address" } } , false , true );
2
+
3
+ //@undo
4
+
5
+ db.customer.update({},{$unset: {"address": 1}},false,true);
@@ -0,0 +1,5 @@
1
+ db.customer.update({},{$set: {"phone": "897907979" } } , false , true )
2
+
3
+ //@undo
4
+
5
+ db.customer.update({},{$unset: {"phone": 1}},false,true)
File without changes
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/database_populator'
3
+
4
+ module DeployMongo
5
+
6
+ describe Deploy, "load and execute deltas first database" do
7
+
8
+ before :all do
9
+ DatabasePopulator.new("test").with_type("customer").with_records(30).build
10
+ end
11
+
12
+ it "load and execute deltas" do
13
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
14
+ deploy = Deploy.new(config)
15
+ deltas = deploy.run
16
+ deltas.count.should == 5
17
+ deltas[0].file_name.should == "1_add_address_to_customer.js"
18
+ end
19
+ end
20
+
21
+
22
+ describe Deploy, "execute deltas and rollback all deltas" do
23
+
24
+ before :all do
25
+ DatabasePopulator.new("test").with_type("customer").with_records(30).build
26
+ end
27
+
28
+ it "load and execute deltas" do
29
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
30
+ deploy = Deploy.new(config)
31
+ deploy.run
32
+ deltas = deploy.rollback
33
+ deltas.count.should == 5
34
+ deltas[0].file_name.should == "13_copy_and_create_new_customer.js"
35
+ end
36
+ end
37
+
38
+ describe Deploy, "execute deltas and rollback only 2 deltas" do
39
+
40
+ before :all do
41
+ DatabasePopulator.new("test").with_type("customer").with_records(30).build
42
+ end
43
+
44
+ it "load and execute deltas" do
45
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
46
+ deploy = Deploy.new(config)
47
+ deploy.run
48
+ deltas = deploy.rollback(2)
49
+ deltas.count.should == 2
50
+ deltas[0].file_name.should == "13_copy_and_create_new_customer.js"
51
+ deltas[1].file_name.should == "12_delete_customer_name_1.js"
52
+ end
53
+ end
54
+
55
+
56
+
57
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/database_populator'
3
+
4
+ module DeployMongo
5
+ describe Repository, "execute a delta" do
6
+
7
+ before :all do
8
+ DatabasePopulator.new("test").with_type("customer").with_records(1).build
9
+ end
10
+
11
+ it "create schema document if it does not exist and get schema to verify" do
12
+ config = Config.create_from_file(File.dirname(__FILE__) + '/../mongodb.yml')
13
+ config.merge_config({"database"=>"test"})
14
+ schema = {"_id"=>"schema__schema_document_key__","type"=>"__schema__", 'applied_deltas'=>[], "type_versions"=>{}}
15
+ repository = Repository.new(config)
16
+ rows = []
17
+ repository.save_schema(schema)
18
+ schema_doc = repository.get_schema
19
+ schema_doc['_id'].should == schema['_id']
20
+ schema_doc['type'].should == schema['type']
21
+ schema_doc['applied_deltas'].should == schema['applied_deltas']
22
+ schema_doc['type_versions'].should == schema['type_versions']
23
+ end
24
+
25
+ end
26
+
27
+ end
data/spec/mongodb.yml ADDED
@@ -0,0 +1,5 @@
1
+ hostname: localhost
2
+ port: 27017
3
+ delta_path: "integration/deltas"
4
+ database: "test"
5
+ mongo_shell_path: "/Users/admin/work/mongodb-osx-x86_64-1.8.2/bin/mongo"
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require File.dirname(__FILE__) + '/../lib/deploy_mongo'
4
+
5
+
6
+ def get_couchdb_config
7
+ DeployMongo::Config.new({"hostname"=>"localhost","port"=>27017,"database"=>"test",'delta_path'=>"path/to/deltas","config_folder_path" => "/somefolder","doc_type_field"=>"type","type_version_field" => 'type_version' })
8
+ end
9
+
10
+ def create_json_response(total_rows,num_of_records,offset=0)
11
+ h = {"total_rows"=> total_rows,"offset" => offset, "rows" => []}
12
+ (1..num_of_records).each do |x|
13
+ h["rows"].push({"id"=>x})
14
+ end
15
+ JSON.generate(h)
16
+ end
17
+
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+
5
+ describe DbSchema, "Manage Schema" do
6
+
7
+ it "load schema document" do
8
+ repository = mock(Repository)
9
+ repository.should_receive(:get_schema).and_return({"_id"=>"special_key",'applied_deltas'=>[1,2]})
10
+ schema = DbSchema.load_or_create(get_couchdb_config,repository)
11
+ schema.applied_deltas.should == [1,2]
12
+ end
13
+
14
+ it "create if schema document does not exist" do
15
+ repository = mock(Repository)
16
+ repository.should_receive(:get_schema).ordered
17
+ repository.should_receive(:save_schema).ordered
18
+ repository.should_receive(:get_schema).ordered.and_return({"_id"=>"special_key",'applied_deltas'=>[]})
19
+ schema = DbSchema.load_or_create(get_couchdb_config,repository)
20
+ schema.applied_deltas.should == []
21
+ end
22
+
23
+ it "on completion update schema document" do
24
+ schema_doc = {"_id"=>"special_key","type"=>"__schema__", 'applied_deltas'=>[]}
25
+ updated_schema_doc = {"_id"=>"special_key","type"=>"__schema__", 'applied_deltas'=>[1]}
26
+ repository = mock(Repository)
27
+ repository.should_receive(:save_schema).ordered.with(updated_schema_doc)
28
+ repository.should_receive(:get_schema).ordered.and_return(updated_schema_doc)
29
+ schema = DbSchema.new(schema_doc,repository)
30
+ schema.completed(Delta.new(1,'file_name','command','rollback_command'))
31
+ schema.applied_deltas.should == [1]
32
+ end
33
+
34
+ it "on rollback update schema document" do
35
+ schema_doc = {"_id"=>"special_key","type"=>"__schema__", 'applied_deltas'=>[1]}
36
+ updated_schema_doc = {"_id"=>"special_key","type"=>"__schema__", 'applied_deltas'=>[]}
37
+ repository = mock(Repository)
38
+ repository.should_receive(:save_schema).ordered.with(updated_schema_doc)
39
+ repository.should_receive(:get_schema).ordered.and_return(updated_schema_doc)
40
+ schema = DbSchema.new(schema_doc,repository)
41
+ schema.rollback(Delta.new(1,'file_name','command','rollback_command'))
42
+ schema.applied_deltas.should == []
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+
5
+ describe DeltaProcessor, "execute a delta" do
6
+ it "should apply delta" do
7
+ mock_shell = mock(MongoShell)
8
+ MongoShell.should_receive(:new).with('test').and_return(mock_shell)
9
+
10
+ command= 'db.customer.update({},{$set: {"address": "some address" } } , false , true )'
11
+ rollback_command= 'db.customer.update({},{$unset: {"address": 1}},false,true)'
12
+
13
+ mock_shell.should_receive(:execute).with(command)
14
+ delta = Delta.new(1,'file_name',command,rollback_command)
15
+ next_type_version = 1
16
+ delta_processor = DeltaProcessor.new(get_couchdb_config,delta)
17
+ delta_processor.apply
18
+ end
19
+
20
+ it "should rollback delta" do
21
+ mock_shell = mock(MongoShell)
22
+ MongoShell.should_receive(:new).with('test').and_return(mock_shell)
23
+
24
+ command= 'db.customer.update({},{$set: {"address": "some address" } } , false , true )'
25
+ rollback_command= 'db.customer.update({},{$unset: {"address": 1}},false,true)'
26
+
27
+ mock_shell.should_receive(:execute).with(rollback_command)
28
+ delta = Delta.new(1,'file_name',command,rollback_command)
29
+ next_type_version = 1
30
+ delta_processor = DeltaProcessor.new(get_couchdb_config,delta)
31
+ delta_processor.rollback
32
+ end
33
+
34
+
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,90 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+ describe Deploy, "load and execute deltas" do
5
+ it "load and execute deltas in correct order" do
6
+ config = get_couchdb_config
7
+ delta = Delta.new(1,'file_name','map function','rollback_command')
8
+ delta2 = Delta.new(10,'file_name2','map function','rollback_command')
9
+ deltas_map = {1=>delta,10=>delta2}
10
+
11
+ mock_delta_loader = mock(DeltaLoader)
12
+ DeltaLoader.should_receive(:new).with("/somefolder/path/to/deltas").and_return(mock_delta_loader)
13
+ mock_delta_loader.should_receive(:get_deltas).and_return(deltas_map)
14
+
15
+ mock_repository = mock(Repository)
16
+ Repository.should_receive(:new).with(config).and_return(mock_repository)
17
+
18
+ mock_delta_processor = mock(DeltaProcessor)
19
+ DeltaProcessor.should_receive(:new).with(config,delta).ordered.and_return(mock_delta_processor)
20
+ mock_delta_processor.should_receive(:apply)
21
+
22
+ mock_delta_processor2 = mock(DeltaProcessor)
23
+ DeltaProcessor.should_receive(:new).with(config,delta2).ordered.and_return(mock_delta_processor2)
24
+ mock_delta_processor2.should_receive(:apply)
25
+
26
+ mock_couch_db_schema = mock(DbSchema)
27
+ mock_couch_db_schema.should_receive(:applied_deltas).and_return([])
28
+ DbSchema.should_receive(:load_or_create).with(config,mock_repository).ordered.and_return(mock_couch_db_schema)
29
+ mock_couch_db_schema.should_receive(:completed).ordered.with(delta)
30
+ mock_couch_db_schema.should_receive(:completed).ordered.with(delta2)
31
+
32
+ deploy = Deploy.new(config)
33
+ deploy.run
34
+ end
35
+
36
+ it "executes unapplied deltas only in correct order" do
37
+ config = get_couchdb_config
38
+ delta = Delta.new(1,'file_name','map function','rollback_command')
39
+ delta2 = Delta.new(10,'file_name2','map function','rollback_command')
40
+ deltas_map = {1=>delta,10=>delta2}
41
+
42
+ mock_delta_loader = mock(DeltaLoader)
43
+ DeltaLoader.should_receive(:new).with("/somefolder/path/to/deltas").and_return(mock_delta_loader)
44
+ mock_delta_loader.should_receive(:get_deltas).and_return(deltas_map)
45
+
46
+ mock_repository = mock(Repository)
47
+ Repository.should_receive(:new).with(config).and_return(mock_repository)
48
+
49
+ mock_delta_processor = mock(DeltaProcessor)
50
+ DeltaProcessor.should_receive(:new).with(config,delta2).and_return(mock_delta_processor)
51
+ mock_delta_processor.should_receive(:apply)
52
+
53
+ mock_couch_db_schema = mock(DbSchema)
54
+ mock_couch_db_schema.should_receive(:applied_deltas).and_return([1])
55
+ DbSchema.should_receive(:load_or_create).with(config,mock_repository).ordered.and_return(mock_couch_db_schema)
56
+ mock_couch_db_schema.should_receive(:completed).with(delta2)
57
+
58
+ deploy = Deploy.new(config)
59
+ deploy.run
60
+ end
61
+
62
+ it "executes rollback deltas" do
63
+ config = get_couchdb_config
64
+ delta = Delta.new(1,'file_name','map function','rollback_command')
65
+ delta2 = Delta.new(10,'file_name2','map function','rollback_command')
66
+ deltas_map = {1=>delta,10=>delta2}
67
+
68
+ mock_delta_loader = mock(DeltaLoader)
69
+ DeltaLoader.should_receive(:new).with("/somefolder/path/to/deltas").and_return(mock_delta_loader)
70
+ mock_delta_loader.should_receive(:get_deltas).and_return(deltas_map)
71
+
72
+ mock_repository = mock(Repository)
73
+ Repository.should_receive(:new).with(config).and_return(mock_repository)
74
+
75
+ mock_delta_processor = mock(DeltaProcessor)
76
+ DeltaProcessor.should_receive(:new).with(config,delta).and_return(mock_delta_processor)
77
+ mock_delta_processor.should_receive(:rollback)
78
+
79
+ mock_couch_db_schema = mock(DbSchema)
80
+ mock_couch_db_schema.should_receive(:applied_deltas).and_return([1])
81
+ DbSchema.should_receive(:load_or_create).with(config,mock_repository).ordered.and_return(mock_couch_db_schema)
82
+ mock_couch_db_schema.should_receive(:rollback).with(delta)
83
+
84
+ deploy = Deploy.new(config)
85
+ deploy.rollback
86
+ end
87
+
88
+
89
+ end
90
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module DeployMongo
4
+
5
+ describe Repository, "execute a delta" do
6
+
7
+ it "get schema document" do
8
+ schema = {"_id"=>"special_key","type"=>"__schema__", 'applied_deltas'=>[1,2], "type_versions"=>{"customer"=>10}}
9
+ mock_server = mock(Mongo::Connection)
10
+ Mongo::Connection.should_receive(:new).with("localhost",27017).and_return(mock_server)
11
+ mock_db = mock(Mongo::DB)
12
+ mock_server.should_receive(:db).with("test").and_return(mock_db)
13
+ mock_collection = mock(Mongo::Collection)
14
+ mock_db.should_receive(:collection).with("schema").and_return(mock_collection)
15
+ mock_collection.should_receive(:find_one).with("_id" => "schema__schema_document_key__").and_return(schema)
16
+ repository = Repository.new(get_couchdb_config)
17
+ rows = []
18
+ repository.get_schema.should == schema
19
+
20
+ end
21
+
22
+ it "save schema document without id" do
23
+ schema = {"type"=>"__schema__", 'applied_deltas'=>[1,2], "type_versions"=>{"customer"=>10}}
24
+ mock_server = mock(Mongo::Connection)
25
+ Mongo::Connection.should_receive(:new).with("localhost",27017).and_return(mock_server)
26
+ mock_db = mock(Mongo::DB)
27
+ mock_server.should_receive(:db).with("test").and_return(mock_db)
28
+ mock_collection = mock(Mongo::Collection)
29
+ mock_db.should_receive(:collection).with("schema").and_return(mock_collection)
30
+ mock_collection.should_receive(:save).with(schema.merge("_id" => "schema__schema_document_key__")).and_return(schema)
31
+ repository = Repository.new(get_couchdb_config)
32
+ rows = []
33
+ repository.save_schema(schema)
34
+
35
+ end
36
+
37
+ it "save schema document with id" do
38
+ schema = {"_id" => "schema__schema_document_key__", "type"=>"__schema__", 'applied_deltas'=>[1,2], "type_versions"=>{"customer"=>10}}
39
+ mock_server = mock(Mongo::Connection)
40
+ Mongo::Connection.should_receive(:new).with("localhost",27017).and_return(mock_server)
41
+ mock_db = mock(Mongo::DB)
42
+ mock_server.should_receive(:db).with("test").and_return(mock_db)
43
+ mock_collection = mock(Mongo::Collection)
44
+ mock_db.should_receive(:collection).with("schema").and_return(mock_collection)
45
+ mock_collection.should_receive(:save).with(schema).and_return(schema)
46
+ repository = Repository.new(get_couchdb_config)
47
+ rows = []
48
+ repository.save_schema(schema)
49
+
50
+ end
51
+
52
+
53
+
54
+ end
55
+
56
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deploy_mongo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - venky
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-07-26 00:00:00.000000000 +05:30
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bson
17
+ requirement: &2161041180 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 1.3.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2161041180
26
+ - !ruby/object:Gem::Dependency
27
+ name: bson_ext
28
+ requirement: &2161040540 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *2161040540
37
+ - !ruby/object:Gem::Dependency
38
+ name: mongo
39
+ requirement: &2161039960 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: 1.3.1
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2161039960
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: &2161039300 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 2.6.0
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *2161039300
59
+ description: dbDeploy for couchdb
60
+ email:
61
+ - venkatesh.swdev@gmail.com
62
+ executables:
63
+ - deploy_mongo
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - .gitignore
68
+ - Gemfile
69
+ - Gemfile.lock
70
+ - README
71
+ - Rakefile
72
+ - bin/deploy_mongo
73
+ - deploy_mongo.gemspec
74
+ - deploy_mongo.rake
75
+ - lib/deploy_mongo.rb
76
+ - lib/deploy_mongo/config.rb
77
+ - lib/deploy_mongo/db_schema.rb
78
+ - lib/deploy_mongo/delta.rb
79
+ - lib/deploy_mongo/delta_loader.rb
80
+ - lib/deploy_mongo/delta_processor.rb
81
+ - lib/deploy_mongo/deploy.rb
82
+ - lib/deploy_mongo/mongo_shell.rb
83
+ - lib/deploy_mongo/repository.rb
84
+ - lib/deploy_mongo/version.rb
85
+ - spec/integration/config_spec.rb
86
+ - spec/integration/database_populator.rb
87
+ - spec/integration/delta_loader_spec.rb
88
+ - spec/integration/delta_processor_spec.rb
89
+ - spec/integration/deltas/11_update_phone_with_std_codes.js
90
+ - spec/integration/deltas/12_delete_customer_name_1.js
91
+ - spec/integration/deltas/13_copy_and_create_new_customer.js
92
+ - spec/integration/deltas/1_add_address_to_customer.js
93
+ - spec/integration/deltas/2_add_phone_to_customer.js
94
+ - spec/integration/deltas/5_not_a_yaml_file.rb
95
+ - spec/integration/deploy_spec.rb
96
+ - spec/integration/repository_spec.rb
97
+ - spec/mongodb.yml
98
+ - spec/spec_helper.rb
99
+ - spec/unit/db_schema_spec.rb
100
+ - spec/unit/delta_processor_spec.rb
101
+ - spec/unit/deploy_spec.rb
102
+ - spec/unit/repository_spec.rb
103
+ has_rdoc: true
104
+ homepage: https://github.com/venkateshcm/deploy_mongo
105
+ licenses: []
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project: deploy_mongo
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: dbDeploy for couchdb
128
+ test_files:
129
+ - spec/integration/config_spec.rb
130
+ - spec/integration/database_populator.rb
131
+ - spec/integration/delta_loader_spec.rb
132
+ - spec/integration/delta_processor_spec.rb
133
+ - spec/integration/deltas/11_update_phone_with_std_codes.js
134
+ - spec/integration/deltas/12_delete_customer_name_1.js
135
+ - spec/integration/deltas/13_copy_and_create_new_customer.js
136
+ - spec/integration/deltas/1_add_address_to_customer.js
137
+ - spec/integration/deltas/2_add_phone_to_customer.js
138
+ - spec/integration/deltas/5_not_a_yaml_file.rb
139
+ - spec/integration/deploy_spec.rb
140
+ - spec/integration/repository_spec.rb
141
+ - spec/mongodb.yml
142
+ - spec/spec_helper.rb
143
+ - spec/unit/db_schema_spec.rb
144
+ - spec/unit/delta_processor_spec.rb
145
+ - spec/unit/deploy_spec.rb
146
+ - spec/unit/repository_spec.rb