Class: DatabaseFork

Inherits:
Object
  • Object
show all
Defined in:
lib/database_fork.rb

Constant Summary collapse

DEFAULTS =
{
  'check_branch_name_regex' => '^feature_',
  'ignore' => [],
  'environments' => %w(development test)
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root_dir, logger = Logger.new(STDOUT)) ⇒ DatabaseFork

use DatabaseFork.new.run in your post-checkout hook



19
20
21
22
23
24
# File 'lib/database_fork.rb', line 19

def initialize(root_dir, logger = Logger.new(STDOUT))
  @root_dir = root_dir
  @config_file = File.join(@root_dir, '.db_forks.yml')
  @logger = logger
  @commands = []
end

Class Method Details

.setup_env(env, root_dir) ⇒ Object

call this at the end of your application.rb file



9
10
11
12
13
14
15
# File 'lib/database_fork.rb', line 9

def setup_env(env, root_dir)
  db_fork_var = "DATABASE_FORK_#{env}".upcase
  db_fork_file = File.join(root_dir, 'tmp', db_fork_var)
  if File.exists?(db_fork_file)
    ENV[db_fork_var] = open(db_fork_file).read.strip
  end
end

Instance Method Details

#app_connectionObject



156
157
158
# File 'lib/database_fork.rb', line 156

def app_connection
  @app_connection ||= YAML.load(ERB.new(open(File.join(@root_dir, '..', '..', 'config', 'database.yml')).read).result)
end

#ask_user(question) ⇒ Object



58
59
60
61
# File 'lib/database_fork.rb', line 58

def ask_user(question)
  log_info question
  IO.new(IO.sysopen('/dev/tty'), 'r').gets.chomp
end

#character_setObject

could be queried from source_db:



116
117
118
# File 'lib/database_fork.rb', line 116

def character_set
  config['character_set'] || 'utf8'
end

#collationObject

could be queried from source_db:



121
122
123
# File 'lib/database_fork.rb', line 121

def collation
  config['collation'] || 'utf8_unicode_ci'
end

#configObject



170
171
172
# File 'lib/database_fork.rb', line 170

def config
  @config ||= DEFAULTS.merge(YAML.load(open(@config_file).read)) rescue DEFAULTS
end

#connection_params(env = 'development') ⇒ Object

simplify make framework agnostic



140
141
142
143
144
145
146
# File 'lib/database_fork.rb', line 140

def connection_params(env = 'development')
  @connection_params ||= if ENV['USER'] == 'vagrant'
    %Q{--user=#{app_connection[env]['username']} --password=#{app_connection[env]['password']} --socket=#{app_connection[env]['socket']}}
  else
    %Q{--user=#{app_connection[env]['username']} --password=#{app_connection[env]['password']} --host=#{app_connection[env]['host']} --port=#{app_connection[env]['port']}}
  end
end

#create_database(env = 'development') ⇒ Object

TODO: refactor to adapter



80
81
82
# File 'lib/database_fork.rb', line 80

def create_database(env = 'development')
  run_command %Q{mysql #{connection_params(env)} -e "CREATE DATABASE IF NOT EXISTS #{fork_db_name(env)} CHARACTER SET '#{character_set}' COLLATE '#{collation}';"}, "create database #{fork_db_name(env)}"
end

#create_database_fork!Object



63
64
65
66
67
68
69
70
71
72
# File 'lib/database_fork.rb', line 63

def create_database_fork!
  config['environments'].each do |env|
    log_info "creating database fork '#{fork_db_name(env)}' from #{source_db(env)}"

    create_dump(env)
    create_database(env)
    import_dump(env)
    delete_dump_file(env)
  end
end

#create_dump(env = 'development') ⇒ Object

TODO: refactor to adapter



75
76
77
# File 'lib/database_fork.rb', line 75

def create_dump(env = 'development')
  run_command %Q{mysqldump #{connection_params(env)} --routines --triggers -C #{source_db(env)} > #{dump_file_path(env)}}, "dumping #{source_db(env)}"
end

#current_branchObject



160
161
162
# File 'lib/database_fork.rb', line 160

def current_branch
  @current_branch ||= `git rev-parse --abbrev-ref HEAD`.strip.gsub('/', '_')
end

#delete_dump_file(env = 'development') ⇒ Object



89
90
91
# File 'lib/database_fork.rb', line 89

def delete_dump_file(env = 'development')
  run_command "rm #{dump_file_path(env)}", 'cleanup'
end

#dump_file_path(env = 'development') ⇒ Object



111
112
113
# File 'lib/database_fork.rb', line 111

def dump_file_path(env = 'development')
  File.join(@root_dir, 'tmp', "dump_#{env}.sql")
end

#export_envObject



99
100
101
102
# File 'lib/database_fork.rb', line 99

def export_env
  run_command "echo #{fork_db_name('development')} > ./tmp/DATABASE_FORK_DEVELOPMENT", 'setting DATABASE_FORK_DEVELOPMENT'
  run_command "echo #{fork_db_name('test')} > ./tmp/DATABASE_FORK_TEST", 'setting DATABASE_FORK_TEST'
end

#fork_db_name(env = 'development') ⇒ Object



148
149
150
# File 'lib/database_fork.rb', line 148

def fork_db_name(env = 'development')
  "#{source_db(env)}_#{current_branch}".strip
end

#fork_exists?(env = 'development') ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
136
# File 'lib/database_fork.rb', line 133

def fork_exists?(env = 'development')
  command = %Q{mysql #{connection_params[env]} -s -N -e "SHOW DATABASES LIKE '#{fork_db_name(env)}';" }
  !`#{command}`.empty?
end

#import_dump(env = 'development') ⇒ Object

TODO: refactor to adapter



85
86
87
# File 'lib/database_fork.rb', line 85

def import_dump(env = 'development')
  run_command %Q{mysql #{connection_params(env)} -C -A -D#{fork_db_name(env)} < #{dump_file_path(env)}}, 'importing dump'
end

#log_debug(message) ⇒ Object



129
130
131
# File 'lib/database_fork.rb', line 129

def log_debug(message)
  @logger.debug message
end

#log_info(message) ⇒ Object



125
126
127
# File 'lib/database_fork.rb', line 125

def log_info(message)
  @logger.info message
end

#reset_envObject



93
94
95
96
97
# File 'lib/database_fork.rb', line 93

def reset_env
  log_info 'Resetting fork information'
  run_command "rm ./tmp/DATABASE_FORK_DEVELOPMENT", 'rm DATABASE_FORK_DEVELOPMENT'
  run_command "rm ./tmp/DATABASE_FORK_TEST", 'rm DATABASE_FORK_TEST'
end

#runObject

TODO: simplify this somehow



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/database_fork.rb', line 27

def run
  if config['ignore'].include?(current_branch)
    log_info 'This branch name is ignored in .db_fork.yml config. Skipping along.'
    reset_env
  elsif Regexp.new(config['check_branch_name_regex']).match(current_branch)
    log_info 'branch qualified for database forking'

    if fork_exists?
      log_info 'database fork already exists'
      export_env
    else
      case ask_user("Create database: '#{fork_db_name}'? (y(es), n(no), enter=ignore)")
        when 'y'
          create_database_fork!
          export_env
        when 'n'
          # do nothing
          reset_env
        else
          config['ignore'] << current_branch
          reset_env
      end
    end
  else
    reset_env
  end

  save_config
end

#run_command(command, message, dry_run = false) ⇒ Object



104
105
106
107
108
109
# File 'lib/database_fork.rb', line 104

def run_command(command, message, dry_run = false)
  log_info message
  log_debug command
  @commands << [command, message]
  `#{command}` unless dry_run
end

#save_configObject



174
175
176
177
178
# File 'lib/database_fork.rb', line 174

def save_config
  File.open(@config_file, 'w') do |f|
    f.puts @config.to_yaml
  end
end

#source_db(env = 'development') ⇒ Object



152
153
154
# File 'lib/database_fork.rb', line 152

def source_db(env= 'development')
  app_connection[env]['database']
end