omniscient 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dbme.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,34 @@
1
+ Omniscient is a gem that automatically dumps a MySQL database in a remote server
2
+ via SSH, copies the dumped file to localhost via SCP and imports it, overwriting the local
3
+ database (or table).
4
+
5
+ It'll be very useful for those that are developing using more than one machine and
6
+ don't want to dump and clone data manually all the time.
7
+
8
+ Getting Started
9
+ ===============
10
+
11
+ In your console, type the following, where aliasname is a name you choose
12
+ (i.e. home_computer, work etc):
13
+
14
+ $ omniscient clone aliasname
15
+
16
+ A setup will begin. The data will be save to ~/.omni_config.yml
17
+
18
+ Now that Omniscient knows where to connect, run the following command again
19
+
20
+ $ omniscient clone aliasname
21
+
22
+ You can clone a 'custom_dbname' database from that host.
23
+
24
+ $ omniscient clone aliasname -d custom_dbname
25
+
26
+ Troubleshoting
27
+ ==============
28
+
29
+ Tested on Mac OS X.
30
+
31
+ Credits
32
+ =======
33
+
34
+ Alexandre de Oliveira
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ # just 'rake test'
6
+ Rake::TestTask.new do |t|
7
+ # t.libs << "lib"
8
+ t.test_files = Dir["test/**/test*.rb"]
9
+ end
data/bin/omniscient ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../../lib/', __FILE__)
4
+
5
+ require "omniscient"
6
+ require "command"
7
+ require "shell/shell.rb"
8
+
9
+ command = Shell::Parser::get_command ARGV
10
+
11
+ unless command.empty? then
12
+ command_file = File.expand_path('../../lib/omniscient/command/'+command+'.rb', __FILE__)
13
+
14
+ # checks if class file exists
15
+ if File.exists? command_file then
16
+ require command_file
17
+ command = command.capitalize
18
+ command_obj = Omniscient::Command.const_get(command).new(ARGV)
19
+ end
20
+ end
data/lib/omniscient.rb ADDED
@@ -0,0 +1,11 @@
1
+ $:.unshift File.expand_path('../omniscient/', __FILE__)
2
+
3
+ require 'configuration'
4
+ require 'shell/shell'
5
+ require 'command'
6
+
7
+ module Omniscient
8
+ DUMP_FILENAME = '.omni_dump.sql'
9
+ DUMP_LOCAL_PATH = '~/'+DUMP_FILENAME
10
+ DUMP_REMOTE_PATH = DUMP_FILENAME
11
+ end
@@ -0,0 +1,48 @@
1
+ module Omniscient
2
+ module Adapter
3
+ class MySQL
4
+ attr_accessor :attributes
5
+ def initialize( config )
6
+ @attributes = config
7
+ end
8
+
9
+ def method_missing( name, *args, &block )
10
+ if args.empty? && block.nil?
11
+ if @attributes.has_key?(name.to_s)
12
+ @attributes[name.to_s]
13
+ elsif @attributes.has_key?(name.to_sym)
14
+ @attributes[name.to_sym]
15
+ else
16
+ nil
17
+ end
18
+ else
19
+ nil
20
+ end
21
+ end
22
+
23
+ def access_statements
24
+ str = ''
25
+ str << ' -u '+self.user unless self.user.nil?
26
+ str << ' '+self.database unless self.database.nil?
27
+ str << ' -p'+self.password unless self.password.empty?
28
+ str
29
+ end
30
+
31
+ def dump
32
+ str = 'mysqldump'
33
+ str << access_statements
34
+ str << ' '+self.table unless self.database.nil? || self.table.nil?
35
+ str << ' --single-transaction'
36
+ str << ' > .omni_dump.sql'
37
+ str
38
+ end
39
+
40
+ def import
41
+ str = 'mysql'
42
+ str << access_statements
43
+ str << ' < '+Omniscient::DUMP_LOCAL_PATH
44
+ str
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ module Omniscient
2
+
3
+ module Command
4
+
5
+ class Run
6
+ def initialize argv = ''
7
+ @argv = argv
8
+
9
+ @configurations = Omniscient::Configuration.new
10
+
11
+ if Shell::Parser.is_option "help", argv
12
+ help if self.respond_to?('help')
13
+ exit
14
+ end
15
+
16
+ if( self.respond_to?('run') )
17
+ run
18
+ elsif( self.respond_to?('help') )
19
+ help
20
+ end
21
+
22
+ end
23
+
24
+ def request_configuration
25
+ @configurations.configuration @configurations.questions(:alias_name => @alias_name)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,68 @@
1
+ require 'yaml'
2
+
3
+ module Omniscient
4
+
5
+ module Command
6
+
7
+ class Clone < Omniscient::Command::Run
8
+
9
+ def run
10
+
11
+ @alias_name = Shell::Parser.get_arguments(@argv).first || ""
12
+
13
+ has_conf = load_configuration_by_alias(@alias_name)
14
+ unless has_conf
15
+ request_configuration
16
+ end
17
+
18
+ database = Shell::Parser.get_option_value('d', @argv) || nil
19
+ @configurations['mysql']['database'] = database unless database.empty?
20
+
21
+ require 'ssh'
22
+ require 'scp'
23
+ require 'adapters/mysql'
24
+ @ssh = Omniscient::Ssh.new(@configurations['ssh'])
25
+ @scp = Omniscient::Scp.new(@configurations['ssh'])
26
+ @mysql = Omniscient::Adapter::MySQL.new(@configurations['mysql'])
27
+
28
+ # Dumps remotely
29
+ command_to_issue = "#{@ssh.connect} '#{@mysql.dump}'"
30
+ puts "Running => #{command_to_issue}"
31
+ exit unless system command_to_issue
32
+
33
+ # Copies dumped data
34
+ command_to_issue = "#{@scp.connect}:"+Omniscient::DUMP_REMOTE_PATH+" "+Omniscient::DUMP_LOCAL_PATH
35
+ puts "Running => #{command_to_issue}"
36
+ exit unless system command_to_issue
37
+
38
+ # Imports data
39
+ command_to_issue = "#{@mysql.import}"
40
+ puts "Running => #{command_to_issue}"
41
+ exit unless system command_to_issue
42
+
43
+ end
44
+
45
+ def load_configuration_by_alias alias_name
46
+ return false unless alias_name
47
+ return false unless File.exist?(File.expand_path('~/.omniscient_conf.yml'))
48
+
49
+ config_file = File.new(File.expand_path('~/.omniscient_conf.yml'), 'r')
50
+ existing_configurations = YAML::load(config_file.read)
51
+ if existing_configurations.kind_of?(Hash) && existing_configurations.has_key?(alias_name)
52
+ @configurations = existing_configurations[alias_name]
53
+ true
54
+ else
55
+ false
56
+ end
57
+
58
+ end
59
+
60
+ def help
61
+ puts 'Help is missing.'
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,83 @@
1
+ require 'yaml'
2
+
3
+ module Omniscient
4
+ class Configuration
5
+ def initialize( conf = {} )
6
+ @configuration = conf
7
+ end
8
+
9
+ def method_missing( name, *args, &block )
10
+ if args.empty? && block.nil? && @attributes.has_key?(name)
11
+ @attributes[name.to_sym]
12
+ else
13
+ nil
14
+ end
15
+ end
16
+
17
+ def questions informations
18
+
19
+ custom_alias = informations[:alias_name].nil? ? nil : informations[:alias_name]
20
+
21
+ unless custom_alias.nil?
22
+ puts "No configuration found for #{custom_alias}. You need to setup a remote machine.\n"
23
+ @configuration = { custom_alias => {} }
24
+ else
25
+ puts "No configuration found. You need to setup a remote machine.\n"
26
+ print "Type an name for this remote machine [a word you want]: "
27
+ custom_alias = Shell::Input.text.downcase
28
+ end
29
+
30
+ @configuration = { custom_alias => {} }
31
+ @configuration[custom_alias]['ssh'] = {}
32
+ @configuration[custom_alias]['mysql'] = {}
33
+
34
+ unless @configuration[custom_alias]['ssh'].has_key?('address')
35
+ print "SSH address [i.e. username@some_ip]: "
36
+ @configuration[custom_alias]['ssh']['address'] = Shell::Input.text
37
+ end
38
+
39
+ unless @configuration[custom_alias]['ssh'].has_key?('port')
40
+ print "SSH port [leave empty for default]: "
41
+ ssh_port = Shell::Input.text
42
+ @configuration[custom_alias]['ssh']['port'] = ssh_port.empty? ? 22 : ssh_port.empty?
43
+ end
44
+
45
+ unless @configuration[custom_alias]['mysql'].has_key?('user')
46
+ print "Database's username [default's 'root']: "
47
+ mysql_user = Shell::Input.text
48
+ @configuration[custom_alias]['mysql']['user'] = mysql_user.empty? ? 'root' : mysql_user
49
+ end
50
+
51
+ unless @configuration[custom_alias]['mysql'].has_key?('password')
52
+ system "stty -echo"
53
+ print "Database's password [leave it blank if none is needed]: "
54
+ mysql_password = Shell::Input.text
55
+ system "stty echo"
56
+ print "\n"
57
+ @configuration[custom_alias]['mysql']['password'] = mysql_password.empty? ? '' : mysql_password
58
+ end
59
+
60
+ unless @configuration[custom_alias]['mysql'].has_key?('database')
61
+ print "The default database name you will work with: "
62
+ mysql_db = Shell::Input.text
63
+ @configuration[custom_alias]['mysql']['database'] = mysql_db.empty? ? '' : mysql_db
64
+ end
65
+
66
+ config_file = File.new(File.expand_path('~/.omniscient_conf.yml'), 'a+')
67
+ existing_configurations = YAML::load(config_file.read)
68
+
69
+ if existing_configurations && existing_configurations.kind_of?(Hash)
70
+ @configuration = existing_configurations.merge(@configuration)
71
+ end
72
+ config_file.truncate(0)
73
+ config_file.puts YAML::dump(@configuration)
74
+ config_file.close
75
+
76
+ puts "\nDone."
77
+ puts "Now, just type: omniscient clone #{custom_alias}"
78
+ exit
79
+
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,10 @@
1
+ require 'configuration'
2
+ require 'ssh'
3
+
4
+ module Omniscient
5
+ class Connection
6
+ def initialize
7
+ @configuration = Omniscient::Configuration.new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module Omniscient
2
+ class Scp
3
+
4
+ def initialize( config )
5
+ @attributes = config
6
+ end
7
+
8
+ def method_missing( name, *args, &block )
9
+ if args.empty? && block.nil?
10
+ if @attributes.has_key?(name.to_s)
11
+ @attributes[name.to_s]
12
+ elsif @attributes.has_key?(name.to_sym)
13
+ @attributes[name.to_sym]
14
+ else
15
+ nil
16
+ end
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def get_address
23
+ return false unless self.address
24
+ self.address
25
+ end
26
+
27
+ def connect( custom_message = '' )
28
+ command = "scp "+self.get_address.to_s
29
+ command.to_s
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Omniscient
2
+ class Ssh
3
+
4
+ def initialize( config )
5
+ @attributes = config
6
+ end
7
+
8
+ def method_missing( name, *args, &block )
9
+ if args.empty? && block.nil?
10
+ if @attributes.has_key?(name.to_s)
11
+ @attributes[name.to_s]
12
+ elsif @attributes.has_key?(name.to_sym)
13
+ @attributes[name.to_sym]
14
+ else
15
+ nil
16
+ end
17
+ else
18
+ nil
19
+ end
20
+ end
21
+
22
+ def get_address
23
+ return false unless self.address
24
+ self.address
25
+ end
26
+
27
+ def connect( custom_message = '' )
28
+ command = "ssh "+self.get_address.to_s
29
+ command << " '"+custom_message+"'" unless custom_message.empty?
30
+ command.to_s
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Omniscient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,140 @@
1
+ module Shell
2
+
3
+ class Run
4
+
5
+ # all arguments passed
6
+ @argv
7
+
8
+ def initialize argv
9
+ @argv = argv
10
+
11
+ @options = Shell::Parser.get_options @argv
12
+ @command = Shell::Parser.get_command @argv
13
+ @arguments = Shell::Parser.get_arguments @argv
14
+ end
15
+
16
+ end
17
+
18
+ class Input
19
+
20
+ def self.text
21
+ begin
22
+ STDOUT.flush
23
+ STDIN.gets.strip
24
+ rescue
25
+ end
26
+ end
27
+
28
+ def self.yesno message
29
+ STDOUT.flush
30
+ has_result = false
31
+ while has_result == false
32
+ print message.strip + " "
33
+ input = STDIN.gets.strip
34
+
35
+ if input == "yes" or input == "y" then
36
+ final_input = "yes"
37
+ elsif input == "no" or input == "n" then
38
+ final_input = "no"
39
+ end
40
+
41
+ if ( final_input == "yes" or final_input == "no" )
42
+ has_result = true
43
+ end
44
+ end
45
+
46
+ if final_input == "yes"
47
+ result = true
48
+ else
49
+ result = false
50
+ end
51
+ result
52
+ end
53
+
54
+ end
55
+
56
+ class Parser
57
+
58
+ # defines the command of the application (e.g. 'push' in uplift push)
59
+ def self.get_command argv = []
60
+ command = String.new
61
+
62
+ argv.each {
63
+ |e|
64
+ e_length = e.length
65
+ if (e[0,2] != "--" and e[0,1] != "-") then
66
+ command = e
67
+ break
68
+ end
69
+ }
70
+
71
+ if command.empty? then
72
+ command = ""
73
+ end
74
+
75
+ command
76
+
77
+ end # get_command
78
+
79
+ # get only options in ARGV (arguments starting with _ or __)
80
+ def self.get_options argv
81
+ @options = Array.new
82
+
83
+ argv.each {
84
+ |e|
85
+ e_length = e.length
86
+ if e[0,2] == "--"
87
+ @options.push e[2,e_length]
88
+ elsif e[0,1] == "-"
89
+ @options.push e[1,e_length]
90
+ end
91
+ }
92
+ @options
93
+ end # get_options
94
+
95
+ def self.get_option_value option, argv = Array.new
96
+ next_item, option_value = false
97
+ argv.each { |e|
98
+ if next_item
99
+ option_value = e
100
+ break
101
+ end
102
+ e_length = e.length
103
+ if e[0,2] == "--"
104
+ next_item = true if e[2,e_length] == option
105
+ elsif e[0,1] == "-"
106
+ next_item = true if e[1,e_length] == option
107
+ end
108
+ }
109
+ option_value || ''
110
+ end
111
+
112
+ # get arguments. arguments are anything written besides the command and options.
113
+ # in 'push today --list', 'today' is the argument
114
+ def self.get_arguments argv
115
+ @arguments = Array.new
116
+ i = 0
117
+ argv.each {
118
+ |e|
119
+
120
+ i+= 1
121
+ next if i == 1
122
+
123
+ e_length = e.length
124
+ if e[0,2] != "--" and e[0,1] != "-"
125
+
126
+ @arguments.push e[0,e_length]
127
+ end
128
+
129
+ }
130
+ @arguments
131
+ end # get_options
132
+
133
+ def self.is_option option, argv = Array.new
134
+ argv_options = self.get_options argv
135
+ argv_options.include?(option)
136
+ end # is_option
137
+
138
+ end
139
+
140
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "omniscient/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "omniscient"
7
+ s.version = Omniscient::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Alexandre de Oliveira"]
10
+ s.email = ["chavedomundo@gmail.com"]
11
+ s.homepage = "http://github.com/kurko/omniscient"
12
+ s.summary = %q{Manage DBs from your local machine.}
13
+ s.description = %q{Clone DBs from one machine to another.}
14
+
15
+ s.rubyforge_project = "omniscient"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,37 @@
1
+ require "test/unit"
2
+ require "omniscient"
3
+ require "adapters/mysql"
4
+
5
+ class MySQLTest < Test::Unit::TestCase
6
+
7
+ def tear_down
8
+ @mysql = nil
9
+ end
10
+
11
+ def setup
12
+ @mysql = Omniscient::Adapter::MySQL.new(
13
+ :database => 'omniscient',
14
+ :user => 'root',
15
+ :password => 'pw' )
16
+ end
17
+
18
+ def test_initialization
19
+ assert_equal 'omniscient', @mysql.database
20
+ assert_equal 'root', @mysql.user
21
+ end
22
+
23
+ def test_dump
24
+ assert_equal %Q{mysqldump -u root -ppw omniscient --single-transaction > .omni_dump.sql}, @mysql.dump()
25
+ end
26
+
27
+ def test_dump_a_specific_table
28
+ @mysql.attributes[:table] = 'mytable'
29
+ assert_equal %Q{mysqldump -u root -ppw omniscient mytable --single-transaction > .omni_dump.sql}, @mysql.dump()
30
+ end
31
+
32
+ def test_import
33
+ assert_equal %Q{mysql -u root -ppw omniscient < }+Omniscient::DUMP_LOCAL_PATH, @mysql.import()
34
+ end
35
+
36
+
37
+ end
@@ -0,0 +1,13 @@
1
+ require "test/unit"
2
+ require "omniscient"
3
+ require "command/clone"
4
+
5
+ class CloneTest < Test::Unit::TestCase
6
+ def setup
7
+
8
+ end
9
+
10
+ def test_start
11
+
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ require "test/unit"
2
+ require "omniscient"
3
+
4
+ class SshTest < Test::Unit::TestCase
5
+
6
+ def tear_down
7
+ @command = nil
8
+ end
9
+
10
+ def setup
11
+ @command = Omniscient::Command::Run.new
12
+ end
13
+
14
+ def test_initialization
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,9 @@
1
+ require "test/unit"
2
+ require "omniscient"
3
+ require "connection"
4
+
5
+ class ConnectionTest < Test::Unit::TestCase
6
+ def setup
7
+ @conn = Omniscient::Connection.new
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ require "test/unit"
2
+ require "omniscient"
3
+ require "ssh"
4
+
5
+ class SshTest < Test::Unit::TestCase
6
+
7
+ def tear_down
8
+ @ssh = nil
9
+ end
10
+
11
+ def setup
12
+ @ssh = Omniscient::Ssh.new :address => 'user@localhost'
13
+ end
14
+
15
+ def test_initialization
16
+ assert_equal 'user@localhost', @ssh.address
17
+ end
18
+
19
+ def test_address
20
+ assert_equal 'user@localhost', @ssh.get_address()
21
+ end
22
+
23
+ def test_address_without_user
24
+ @ssh = Omniscient::Ssh.new :address => 'localhost'
25
+ assert_equal 'localhost', @ssh.get_address()
26
+ end
27
+
28
+ def test_address_with_port
29
+ @ssh = Omniscient::Ssh.new :address => 'user@localhost', :port => '22'
30
+ # TODO
31
+ end
32
+
33
+ def test_address_with_port
34
+ assert_equal %q{ssh user@localhost 'custom message'}, @ssh.connect('custom message')
35
+ end
36
+
37
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniscient
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Alexandre de Oliveira
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-06 00:00:00 -03:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Clone DBs from one machine to another.
22
+ email:
23
+ - chavedomundo@gmail.com
24
+ executables:
25
+ - omniscient
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.markdown
34
+ - Rakefile
35
+ - bin/omniscient
36
+ - lib/omniscient.rb
37
+ - lib/omniscient/adapters/mysql.rb
38
+ - lib/omniscient/command.rb
39
+ - lib/omniscient/command/clone.rb
40
+ - lib/omniscient/configuration.rb
41
+ - lib/omniscient/connection.rb
42
+ - lib/omniscient/scp.rb
43
+ - lib/omniscient/ssh.rb
44
+ - lib/omniscient/version.rb
45
+ - lib/shell/shell.rb
46
+ - omniscient.gemspec
47
+ - test/lib/omniscient/adapters/test_mysql.rb
48
+ - test/lib/omniscient/command/test_clone.rb
49
+ - test/lib/omniscient/test_command.rb
50
+ - test/lib/omniscient/test_connection.rb
51
+ - test/lib/omniscient/test_ssh.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/kurko/omniscient
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project: omniscient
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Manage DBs from your local machine.
84
+ test_files:
85
+ - test/lib/omniscient/adapters/test_mysql.rb
86
+ - test/lib/omniscient/command/test_clone.rb
87
+ - test/lib/omniscient/test_command.rb
88
+ - test/lib/omniscient/test_connection.rb
89
+ - test/lib/omniscient/test_ssh.rb