ardb 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ardb.gemspec +4 -0
- data/bin/ardb +7 -0
- data/lib/ardb.rb +93 -1
- data/lib/ardb/adapter/base.rb +24 -0
- data/lib/ardb/adapter/mysql.rb +22 -0
- data/lib/ardb/adapter/postgresql.rb +49 -0
- data/lib/ardb/adapter/sqlite.rb +36 -0
- data/lib/ardb/cli.rb +113 -0
- data/lib/ardb/migration_helpers.rb +74 -0
- data/lib/ardb/runner.rb +65 -0
- data/lib/ardb/runner/create_command.rb +21 -0
- data/lib/ardb/runner/drop_command.rb +21 -0
- data/lib/ardb/runner/generate_command.rb +64 -0
- data/lib/ardb/runner/migrate_command.rb +48 -0
- data/lib/ardb/test_helpers.rb +24 -0
- data/lib/ardb/version.rb +1 -1
- data/test/helper.rb +17 -1
- data/test/unit/adapter/base_tests.rb +41 -0
- data/test/unit/adapter/mysql_tests.rb +40 -0
- data/test/unit/adapter/postgresql_tests.rb +39 -0
- data/test/unit/adapter/sqlite_tests.rb +39 -0
- data/test/unit/ardb_tests.rb +55 -0
- data/test/unit/config_tests.rb +42 -0
- data/test/unit/migration_helpers_tests.rb +59 -0
- data/test/unit/runner/create_command_tests.rb +18 -0
- data/test/unit/runner/drop_command_tests.rb +17 -0
- data/test/unit/runner/generate_command_tests.rb +46 -0
- data/test/unit/runner/migrate_command_tests.rb +62 -0
- data/test/unit/runner_tests.rb +72 -0
- data/test/unit/test_helpers_tests.rb +14 -0
- metadata +99 -6
data/ardb.gemspec
CHANGED
data/bin/ardb
ADDED
data/lib/ardb.rb
CHANGED
@@ -1,3 +1,95 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'ns-options'
|
3
|
+
require 'pathname'
|
4
|
+
require 'singleton'
|
5
|
+
|
1
6
|
require "ardb/version"
|
2
7
|
|
3
|
-
module Ardb
|
8
|
+
module Ardb
|
9
|
+
NotConfiguredError = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
def self.config; Config; end
|
12
|
+
def self.configure(&block); Config.define(&block); end
|
13
|
+
|
14
|
+
def self.adapter; Adapter.current; end
|
15
|
+
|
16
|
+
def self.validate!
|
17
|
+
if !self.config.required_set?
|
18
|
+
raise NotConfiguredError, "missing required configs"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.init(connection=true)
|
23
|
+
validate!
|
24
|
+
Adapter.init
|
25
|
+
|
26
|
+
# setup AR
|
27
|
+
ActiveRecord::Base.logger = self.config.logger
|
28
|
+
ActiveRecord::Base.establish_connection(self.config.db.to_hash) if connection
|
29
|
+
end
|
30
|
+
|
31
|
+
class Config
|
32
|
+
include NsOptions::Proxy
|
33
|
+
|
34
|
+
namespace :db do
|
35
|
+
option :adapter, String, :required => true
|
36
|
+
option :database, String, :required => true
|
37
|
+
option :encoding, String, :required => false
|
38
|
+
option :url, String, :required => false
|
39
|
+
option :username, String, :required => false
|
40
|
+
option :password, String, :required => false
|
41
|
+
end
|
42
|
+
|
43
|
+
option :root_path, Pathname, :required => true
|
44
|
+
option :logger, :required => true
|
45
|
+
option :migrations_path, String, :default => proc{ default_migrations_path }
|
46
|
+
option :schema_path, String, :default => proc{ default_schema_path }
|
47
|
+
|
48
|
+
def self.default_migrations_path; root_path.join("db/migrations"); end
|
49
|
+
def self.default_schema_path; root_path.join("db/schema.rb"); end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Adapter
|
54
|
+
include Singleton
|
55
|
+
|
56
|
+
attr_reader :current
|
57
|
+
|
58
|
+
def init
|
59
|
+
@current = Adapter.send(Ardb.config.db.adapter)
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset
|
63
|
+
@current = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def sqlite
|
67
|
+
require 'ardb/adapter/sqlite'
|
68
|
+
Adapter::Sqlite.new
|
69
|
+
end
|
70
|
+
alias_method :sqlite3, :sqlite
|
71
|
+
|
72
|
+
def postgresql
|
73
|
+
require 'ardb/adapter/postgresql'
|
74
|
+
Adapter::Postgresql.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def mysql
|
78
|
+
require 'ardb/adapter/mysql'
|
79
|
+
Adapter::Mysql.new
|
80
|
+
end
|
81
|
+
alias_method :mysql2, :mysql
|
82
|
+
|
83
|
+
# nice singleton api
|
84
|
+
|
85
|
+
def self.method_missing(method, *args, &block)
|
86
|
+
self.instance.send(method, *args, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.respond_to?(method)
|
90
|
+
super || self.instance.respond_to?(method)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ardb; end
|
2
|
+
class Ardb::Adapter; end
|
3
|
+
class Ardb::Adapter::Base
|
4
|
+
|
5
|
+
attr_reader :config_settings, :database
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@config_settings = Ardb.config.db.to_hash
|
9
|
+
@database = Ardb.config.db.database
|
10
|
+
end
|
11
|
+
|
12
|
+
def foreign_key_add_sql(*args); raise NotImplementedError; end
|
13
|
+
def foreign_key_drop_sql(*args); raise NotImplementedError; end
|
14
|
+
|
15
|
+
def create_db(*args); raise NotImplementedError; end
|
16
|
+
def drop_db(*args); raise NotImplementedError; end
|
17
|
+
|
18
|
+
def drop_tables(*args); raise NotImplementedError; end
|
19
|
+
|
20
|
+
def ==(other_adapter)
|
21
|
+
self.class == other_adapter.class
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ardb'
|
2
|
+
require 'ardb/adapter/base'
|
3
|
+
|
4
|
+
class Ardb::Adapter
|
5
|
+
|
6
|
+
class Mysql < Base
|
7
|
+
|
8
|
+
def foreign_key_add_sql
|
9
|
+
"ALTER TABLE :from_table"\
|
10
|
+
" ADD CONSTRAINT :name"\
|
11
|
+
" FOREIGN KEY (:from_column)"\
|
12
|
+
" REFERENCES :to_table (:to_column)"
|
13
|
+
end
|
14
|
+
|
15
|
+
def foreign_key_drop_sql
|
16
|
+
"ALTER TABLE :from_table"\
|
17
|
+
" DROP FOREIGN KEY :name"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'ardb'
|
2
|
+
require 'ardb/adapter/base'
|
3
|
+
|
4
|
+
class Ardb::Adapter
|
5
|
+
|
6
|
+
class Postgresql < Base
|
7
|
+
|
8
|
+
def public_schema_settings
|
9
|
+
self.config_settings.merge({
|
10
|
+
:database => 'postgres',
|
11
|
+
:schema_search_path => 'public'
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_db
|
16
|
+
ActiveRecord::Base.establish_connection(self.public_schema_settings)
|
17
|
+
ActiveRecord::Base.connection.create_database(self.database, self.config_settings)
|
18
|
+
ActiveRecord::Base.establish_connection(self.config_settings)
|
19
|
+
end
|
20
|
+
|
21
|
+
def drop_db
|
22
|
+
ActiveRecord::Base.establish_connection(self.public_schema_settings)
|
23
|
+
ActiveRecord::Base.connection.drop_database(self.database)
|
24
|
+
end
|
25
|
+
|
26
|
+
def drop_tables
|
27
|
+
ActiveRecord::Base.connection.tap do |conn|
|
28
|
+
tables = conn.execute "SELECT table_name"\
|
29
|
+
" FROM information_schema.tables"\
|
30
|
+
" WHERE table_schema = 'public';"
|
31
|
+
tables.each{ |row| conn.execute "DROP TABLE #{row['table_name']} CASCADE" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def foreign_key_add_sql
|
36
|
+
"ALTER TABLE :from_table"\
|
37
|
+
" ADD CONSTRAINT :name"\
|
38
|
+
" FOREIGN KEY (:from_column)"\
|
39
|
+
" REFERENCES :to_table (:to_column)"
|
40
|
+
end
|
41
|
+
|
42
|
+
def foreign_key_drop_sql
|
43
|
+
"ALTER TABLE :from_table"\
|
44
|
+
" DROP CONSTRAINT :name"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'ardb'
|
4
|
+
require 'ardb/adapter/base'
|
5
|
+
|
6
|
+
class Ardb::Adapter
|
7
|
+
|
8
|
+
class Sqlite < Base
|
9
|
+
|
10
|
+
def db_file_path
|
11
|
+
if (path = Pathname.new(self.database)).absolute?
|
12
|
+
path.to_s
|
13
|
+
else
|
14
|
+
Ardb.config.root_path.join(path).to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate!
|
19
|
+
if File.exist?(self.db_file_path)
|
20
|
+
raise Ardb::Runner::CmdError, "#{self.database} already exists"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_db
|
25
|
+
validate!
|
26
|
+
FileUtils.mkdir_p File.dirname(self.db_file_path)
|
27
|
+
ActiveRecord::Base.establish_connection(self.config_settings)
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop_db
|
31
|
+
FileUtils.rm(self.db_file_path) if File.exist?(self.db_file_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/ardb/cli.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'ardb/version'
|
2
|
+
require 'ardb/runner'
|
3
|
+
|
4
|
+
module Ardb
|
5
|
+
|
6
|
+
class CLI
|
7
|
+
|
8
|
+
def self.run(*args)
|
9
|
+
self.new.run(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@cli = CLIRB.new do
|
14
|
+
option 'root_path', 'root path Ardb should use (`Dir.pwd`)', {
|
15
|
+
:abbrev => 'p', :value => String
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(*args)
|
21
|
+
begin
|
22
|
+
@cli.parse!(args)
|
23
|
+
Ardb::Runner.new(@cli.args, @cli.opts).run
|
24
|
+
rescue CLIRB::HelpExit
|
25
|
+
puts help
|
26
|
+
rescue CLIRB::VersionExit
|
27
|
+
puts Ardb::VERSION
|
28
|
+
rescue Ardb::Runner::UnknownCmdError => err
|
29
|
+
$stderr.puts "#{err.message}\n\n"
|
30
|
+
$stderr.puts help
|
31
|
+
exit(1)
|
32
|
+
rescue Ardb::NotConfiguredError, Ardb::Runner::CmdError => err
|
33
|
+
$stderr.puts "#{err.message}"
|
34
|
+
exit(1)
|
35
|
+
rescue Ardb::Runner::CmdFail => err
|
36
|
+
exit(1)
|
37
|
+
rescue CLIRB::Error => exception
|
38
|
+
$stderr.puts "#{exception.message}\n\n"
|
39
|
+
$stderr.puts help
|
40
|
+
exit(1)
|
41
|
+
rescue Exception => exception
|
42
|
+
$stderr.puts "#{exception.class}: #{exception.message}"
|
43
|
+
$stderr.puts exception.backtrace.join("\n")
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
exit(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def help
|
50
|
+
"Usage: ardb [options] COMMAND\n"\
|
51
|
+
"\n"\
|
52
|
+
"Options:"\
|
53
|
+
"#{@cli}"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class CLIRB # Version 1.0.0, https://github.com/redding/cli.rb
|
59
|
+
Error = Class.new(RuntimeError);
|
60
|
+
HelpExit = Class.new(RuntimeError); VersionExit = Class.new(RuntimeError)
|
61
|
+
attr_reader :argv, :args, :opts, :data
|
62
|
+
|
63
|
+
def initialize(&block)
|
64
|
+
@options = []; instance_eval(&block) if block
|
65
|
+
require 'optparse'
|
66
|
+
@data, @args, @opts = [], [], {}; @parser = OptionParser.new do |p|
|
67
|
+
p.banner = ''; @options.each do |o|
|
68
|
+
@opts[o.name] = o.value; p.on(*o.parser_args){ |v| @opts[o.name] = v }
|
69
|
+
end
|
70
|
+
p.on_tail('--version', ''){ |v| raise VersionExit, v.to_s }
|
71
|
+
p.on_tail('--help', ''){ |v| raise HelpExit, v.to_s }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def option(*args); @options << Option.new(*args); end
|
76
|
+
def parse!(argv)
|
77
|
+
@args = (argv || []).dup.tap do |args_list|
|
78
|
+
begin; @parser.parse!(args_list)
|
79
|
+
rescue OptionParser::ParseError => err; raise Error, err.message; end
|
80
|
+
end; @data = @args + [@opts]
|
81
|
+
end
|
82
|
+
def to_s; @parser.to_s; end
|
83
|
+
def inspect
|
84
|
+
"#<#{self.class}:#{'0x0%x' % (object_id << 1)} @data=#{@data.inspect}>"
|
85
|
+
end
|
86
|
+
|
87
|
+
class Option
|
88
|
+
attr_reader :name, :opt_name, :desc, :abbrev, :value, :klass, :parser_args
|
89
|
+
|
90
|
+
def initialize(name, *args)
|
91
|
+
settings, @desc = args.last.kind_of?(::Hash) ? args.pop : {}, args.pop || ''
|
92
|
+
@name, @opt_name, @abbrev = parse_name_values(name, settings[:abbrev])
|
93
|
+
@value, @klass = gvalinfo(settings[:value])
|
94
|
+
@parser_args = if [TrueClass, FalseClass, NilClass].include?(@klass)
|
95
|
+
["-#{@abbrev}", "--[no-]#{@opt_name}", @desc]
|
96
|
+
else
|
97
|
+
["-#{@abbrev}", "--#{@opt_name} #{@opt_name.upcase}", @klass, @desc]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def parse_name_values(name, custom_abbrev)
|
104
|
+
[ (processed_name = name.to_s.strip.downcase), processed_name.gsub('_', '-'),
|
105
|
+
custom_abbrev || processed_name.gsub(/[^a-z]/, '').chars.first || 'a'
|
106
|
+
]
|
107
|
+
end
|
108
|
+
def gvalinfo(v); v.kind_of?(Class) ? [nil,gklass(v)] : [v,gklass(v.class)]; end
|
109
|
+
def gklass(k); k == Fixnum ? Integer : k; end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'ardb'
|
2
|
+
|
3
|
+
module Ardb; end
|
4
|
+
module Ardb::MigrationHelpers
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def foreign_key(from_table, from_column, to_table, options={})
|
8
|
+
fk = ForeignKey.new(from_table, from_column, to_table, options)
|
9
|
+
execute(fk.add_sql)
|
10
|
+
end
|
11
|
+
|
12
|
+
def drop_foreign_key(*args)
|
13
|
+
from_table, from_column = args[0..1]
|
14
|
+
options = args.last.kind_of?(Hash) ? args.last : {}
|
15
|
+
fk = ForeignKey.new(from_table, from_column, nil, options)
|
16
|
+
execute(fk.drop_sql)
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_column_with_fk(table, column)
|
20
|
+
drop_foreign_key(table, column)
|
21
|
+
remove_column(table, column)
|
22
|
+
end
|
23
|
+
|
24
|
+
class ForeignKey
|
25
|
+
attr_reader :from_table, :from_column, :to_table, :to_column, :name, :adapter
|
26
|
+
|
27
|
+
def initialize(from_table, from_column, to_table, options=nil)
|
28
|
+
options ||= {}
|
29
|
+
@from_table = from_table.to_s
|
30
|
+
@from_column = from_column.to_s
|
31
|
+
@to_table = to_table.to_s
|
32
|
+
@to_column = (options[:to_column] || 'id').to_s
|
33
|
+
@name = (options[:name] || "fk_#{@from_table}_#{@from_column}").to_s
|
34
|
+
@adapter = Ardb::Adapter.send(Ardb.config.db.adapter)
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_sql
|
38
|
+
apply_data(@adapter.foreign_key_add_sql)
|
39
|
+
end
|
40
|
+
|
41
|
+
def drop_sql
|
42
|
+
apply_data(@adapter.foreign_key_drop_sql)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def apply_data(template_sql)
|
48
|
+
template_sql.
|
49
|
+
gsub(':from_table', @from_table).
|
50
|
+
gsub(':from_column', @from_column).
|
51
|
+
gsub(':to_table', @to_table).
|
52
|
+
gsub(':to_column', @to_column).
|
53
|
+
gsub(':name', @name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This file will setup the AR migration command recorder for being able to change our
|
58
|
+
# stuff, require it in an initializer
|
59
|
+
|
60
|
+
module RecorderMixin
|
61
|
+
|
62
|
+
def foreign_key(*args)
|
63
|
+
record(:foreign_key, args)
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def invert_foreign_key(args)
|
69
|
+
[ :drop_foreign_key, args ]
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/ardb/runner.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ardb'
|
2
|
+
|
3
|
+
module Ardb; end
|
4
|
+
class Ardb::Runner
|
5
|
+
UnknownCmdError = Class.new(ArgumentError)
|
6
|
+
CmdError = Class.new(RuntimeError)
|
7
|
+
CmdFail = Class.new(RuntimeError)
|
8
|
+
|
9
|
+
attr_reader :cmd_name, :cmd_args, :opts, :root_path
|
10
|
+
|
11
|
+
def initialize(args, opts)
|
12
|
+
@opts = opts
|
13
|
+
@cmd_name = args.shift || ""
|
14
|
+
@cmd_args = args
|
15
|
+
@root_path = @opts.delete('root_path') || Dir.pwd
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
setup_run
|
20
|
+
case @cmd_name
|
21
|
+
when 'migrate'
|
22
|
+
require 'ardb/runner/migrate_command'
|
23
|
+
MigrateCommand.new.run
|
24
|
+
when 'generate'
|
25
|
+
require 'ardb/runner/generate_command'
|
26
|
+
GenerateCommand.new(@cmd_args).run
|
27
|
+
when 'create'
|
28
|
+
require 'ardb/runner/create_command'
|
29
|
+
CreateCommand.new.run
|
30
|
+
when 'drop'
|
31
|
+
require 'ardb/runner/drop_command'
|
32
|
+
DropCommand.new.run
|
33
|
+
when 'null'
|
34
|
+
NullCommand.new.run
|
35
|
+
else
|
36
|
+
raise UnknownCmdError, "unknown command `#{@cmd_name}`"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def setup_run
|
43
|
+
Ardb.config.root_path = @root_path
|
44
|
+
DbConfigFile.new.require_if_exists
|
45
|
+
Ardb.init(false) # don't establish a connection
|
46
|
+
end
|
47
|
+
|
48
|
+
class DbConfigFile
|
49
|
+
PATH = 'config/db.rb'
|
50
|
+
def initialize
|
51
|
+
@path = Ardb.config.root_path.join(PATH)
|
52
|
+
end
|
53
|
+
|
54
|
+
def require_if_exists
|
55
|
+
require @path.to_s if File.exists?(@path.to_s)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class NullCommand
|
60
|
+
def run
|
61
|
+
# if this was a real command it would do something here
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|