ardb 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,4 +19,8 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_development_dependency("assert", ["~> 2.0"])
21
21
 
22
+ gem.add_dependency('activerecord', ["~> 3.2"])
23
+ gem.add_dependency('activesupport', ["~> 3.2"])
24
+ gem.add_dependency('ns-options', ["~> 1.1"])
25
+
22
26
  end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (c) 2011-Present Kelly Redding and Collin Redding
4
+ #
5
+
6
+ require 'ardb/cli'
7
+ Ardb::CLI.run *ARGV
@@ -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; end
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
@@ -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
@@ -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