Skip to content

Commit

Permalink
Merge 5156500 into b20031a
Browse files Browse the repository at this point in the history
  • Loading branch information
I left from github. probably committed Sep 30, 2014
2 parents b20031a + 5156500 commit 49ae219
Show file tree
Hide file tree
Showing 24 changed files with 1,002 additions and 70 deletions.
48 changes: 46 additions & 2 deletions README.md
Expand Up @@ -9,6 +9,12 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
[![Build Status](https://travis-ci.org/winebarrel/ridgepole.svg?branch=master)](https://travis-ci.org/winebarrel/ridgepole)
[![Coverage Status](https://coveralls.io/repos/winebarrel/ridgepole/badge.png?branch=master)](https://coveralls.io/r/winebarrel/ridgepole?branch=master)

**Notice**

* `>= 0.4.8`
* `activerecord-mysql-unsigned` is now optional. Please pass `--enable-mysql-unsigned` after you install [activerecord-mysql-unsigned](https://github.com/waka/activerecord-mysql-unsigned) if you want to use.
* Please pass `--enable-foreigner` after you install [foreigner](https://github.com/matthuhiggins/foreigner) if you want to use the foreign key.

## Installation

Add this line to your application's Gemfile:
Expand Down Expand Up @@ -46,7 +52,8 @@ Usage: ridgepole [options]
-o, --output FILE
-t, --tables TABLES
--ignore-tables TABLES
--disable-mysql-unsigned
--enable-mysql-unsigned
--enable-foreigner
--log-file LOG_FILE
--verbose
--debug
Expand Down Expand Up @@ -111,7 +118,7 @@ Apply `Schemafile`
```

## Rename
```sh
```ruby
create_table "articles", force: true do |t|
t.string "title"
t.text "desc", renamed_from: "text"
Expand All @@ -129,6 +136,43 @@ create_table "user_comments", force: true, renamed_from: "comments" do |t|
end
```

## Foreign Key
You can use the foreign key by passing `--enable-foreigner` ([foreigner](https://github.com/matthuhiggins/foreigner) is required)

```ruby
create_table "parent", force: true do |t|
end

create_table "child", id: false, force: true do |t|
t.integer "id"
t.integer "parent_id"
end

add_index "child", ["parent_id"], name: "par_ind", using: :btree

add_foreign_key "child", "parent", name: "child_ibfk_1", dependent: :delete
```


## Execute
```ruby
create_table "authors", force: true do |t|
t.string "name", null: false
end

create_table "books", force: true do |t|
t.string "title", null: false
t.integer "author_id", unsigned: true, null: false
end

add_index "books", ["author_id"], name: "idx_author_id", using: :btree

execute("ALTER TABLE books ADD CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors (id)") do |c|
# Execute SQL only if there is no foreign key
c.raw_connection.query("SELECT 1 FROM information_schema.key_column_usage WHERE TABLE_SCHEMA = 'bookshelf' AND CONSTRAINT_NAME = 'fk_author' LIMIT 1").each.length.zero?
end
```

## Diff
```sh
$ ridgepole --diff file1.schema file2.schema
Expand Down
43 changes: 30 additions & 13 deletions bin/ridgepole
Expand Up @@ -34,11 +34,23 @@ set_mode = proc do |m|
end

def noop_migrate(delta)
puts delta.script + "\n\n"
unless delta.script.empty?
puts delta.script + "\n\n"
end

migrated, out = delta.migrate(:noop => true)

delta.migrate(:noop => true).each_line do |line|
puts line.strip.gsub(/([^\d])([(),])([^\d])/) { "#{$1}#{$2}\n#{$3}" }.each_line.map {|i| "# #{i.gsub(/^\s+/, '')}"}.join + "\n\n"
if migrated
out.each_line do |line|
if line =~ /\A\s+/
puts "# #{line}"
else
puts line.strip.gsub(/([^\d])([(),])([^\d])/) { "#{$1}#{$2}\n#{$3}" }.each_line.map {|i| "# #{i.gsub(/^\s+/, '')}"}.join + "\n"
end
end
end

return migrated
end

ARGV.options do |opt|
Expand Down Expand Up @@ -79,7 +91,8 @@ ARGV.options do |opt|
opt.on('-o', '--output FILE') {|v| output_file = v }
opt.on('-t', '--tables TABLES', Array) {|v| options[:tables] = v }
opt.on('', '--ignore-tables TABLES', Array) {|v| options[:ignore_tables] = v.map {|i| Regexp.new(i) } }
opt.on('', '--disable-mysql-unsigned') { options[:disable_mysql_unsigned] = true }
opt.on('', '--enable-mysql-unsigned') { options[:enable_mysql_unsigned] = true }
opt.on('', '--enable-foreigner') { options[:enable_foreigner] = true }
opt.on('' , '--log-file LOG_FILE') {|v| options[:log_file] = v }
opt.on('' , '--verbose') { Ridgepole::Logger.verbose = true }
opt.on('' , '--debug') { options[:debug] = true }
Expand Down Expand Up @@ -161,17 +174,18 @@ begin

dsl = File.read(file)
delta = client.diff(dsl, :path => file)
differ = delta.differ?

if options[:dry_run]
if delta.differ?
noop_migrate(delta)
if differ
differ = noop_migrate(delta)
end
else
logger.verbose_info('# Update schema')
delta.migrate
differ, out = delta.migrate
end

unless delta.differ?
unless differ
logger.info('No change')
end
when :diff
Expand All @@ -193,15 +207,18 @@ begin

if diff_with_apply
logger.verbose_info('# Update schema')
differ = delta.differ?

if delta.differ?
delta.migrate
else
if differ
differ, out = delta.migrate
end

if differ
logger.info('No change')
end
elsif delta.differ?
noop_migrate(delta)
exit_code = 1
differ = noop_migrate(delta)
exit_code = 1 if differ
end
end
rescue => e
Expand Down
3 changes: 2 additions & 1 deletion lib/ridgepole.rb
Expand Up @@ -4,7 +4,7 @@

require 'active_record'
require 'active_support'
require 'active_support/core_ext/string/strip'
require 'active_support/core_ext'

module Ridgepole; end
require 'ridgepole/client'
Expand All @@ -13,6 +13,7 @@ module Ridgepole; end
require 'ridgepole/dsl_parser'
require 'ridgepole/dumper'
require 'ridgepole/execute_expander'
require 'ridgepole/ext/foreign_key'
require 'ridgepole/logger'
require 'ridgepole/migration_ext'
require 'ridgepole/schema_dumper_ext'
Expand Down
16 changes: 10 additions & 6 deletions lib/ridgepole/client.rb
Expand Up @@ -7,9 +7,13 @@ def initialize(conn_spec, options = {})
@parser = Ridgepole::DSLParser.new(@options)
@diff = Ridgepole::Diff.new(@options)

unless @options[:disable_mysql_unsigned]
if @options[:enable_mysql_unsigned]
require 'activerecord-mysql-unsigned'
end

if @options[:enable_foreigner]
Ridgepole::ForeignKey.init
end
end

def dump(&block)
Expand All @@ -22,21 +26,21 @@ def diff(dsl, opts = {})
logger = Ridgepole::Logger.instance

logger.verbose_info('# Parse DSL')
expected_definition = @parser.parse(dsl, opts)
expected_definition, expected_execute = @parser.parse(dsl, opts)
logger.verbose_info('# Load tables')
current_definition = @parser.parse(@dumper.dump)
current_definition, current_execute = @parser.parse(@dumper.dump)
logger.verbose_info('# Compare definitions')
@diff.diff(current_definition, expected_definition)
@diff.diff(current_definition, expected_definition, :execute => expected_execute)
end

class << self
def diff(dsl_or_config1, dsl_or_config2, options = {})
logger = Ridgepole::Logger.instance

logger.verbose_info('# Parse DSL1')
definition1 = load_definition(dsl_or_config1)
definition1, execute1 = load_definition(dsl_or_config1)
logger.verbose_info('# Parse DSL2')
definition2 = load_definition(dsl_or_config2)
definition2, execute2 = load_definition(dsl_or_config2)

logger.verbose_info('# Compare definitions')
diff = Ridgepole::Diff.new(options)
Expand Down
78 changes: 73 additions & 5 deletions lib/ridgepole/delta.rb
Expand Up @@ -4,6 +4,7 @@ class Ridgepole::Delta
def initialize(delta, options = {})
@delta = delta
@options = options
@logger = Ridgepole::Logger.instance
end

def migrate(options = {})
Expand Down Expand Up @@ -41,12 +42,15 @@ def script
end

def differ?
not script.empty?
not script.empty? or not delta_execute.empty?
end

private

def migrate0(options = {})
migrated = false
out = nil

if options[:noop]
disable_logging_orig = ActiveRecord::Migration.disable_logging

Expand All @@ -59,26 +63,74 @@ def migrate0(options = {})
end

Ridgepole::ExecuteExpander.without_operation(callback) do
eval_script(script, options.merge(:out => buf))
migrated = eval_script(script, options.merge(:out => buf))
end

buf.string.strip
out = buf.string.strip
ensure
ActiveRecord::Migration.disable_logging = disable_logging_orig
end
else
eval_script(script, options)
migrated = eval_script(script, options)
end

[migrated, out]
end

def eval_script(script, options = {})
execute_count = 0

begin
with_pre_post_query(options) do
ActiveRecord::Schema.new.instance_eval(script, SCRIPT_NAME, 1)
unless script.empty?
ActiveRecord::Schema.new.instance_eval(script, SCRIPT_NAME, 1)
end

execute_count = execute_sqls(options)
end
rescue => e
raise_exception(script, e)
end

not script.empty? or execute_count.nonzero?
end

def execute_sqls(options = {})
es = @delta[:execute] || []
out = options[:out] || $stdout
execute_count = 0

es.each do |exec|
sql, cond = exec.values_at(:sql, :condition)
executable = false

begin
executable = cond.nil? || cond.call(ActiveRecord::Base.connection)
rescue => e
errmsg = "[WARN] `#{sql}` is not executed: #{e.message}"

if @options[:debug]
errmsg = ([errmsg] + e.backtrace).join("\n\tfrom ")
end

Ridgepole::Logger.instance.warn(errmsg)

executable = false
end

next unless executable

if options[:noop]
out.puts(sql.strip_heredoc)
else
@logger.info(sql.strip_heredoc)
ActiveRecord::Base.connection.execute(sql)
end

execute_count += 1
end

return execute_count
end

def with_pre_post_query(options = {})
Expand Down Expand Up @@ -174,6 +226,14 @@ def append_create_table(table_name, attrs, buf)
end
end

if @options[:enable_foreigner] and not (foreign_keys = attrs[:foreign_keys] || {}).empty?
append_change_table(table_name, buf) do
foreign_keys.each do |foreign_key_name, foreign_key_attrs|
Ridgepole::ForeignKey.append_add_foreign_key(table_name, foreign_key_name, foreign_key_attrs, buf, @options)
end
end
end

buf.puts
end

Expand All @@ -197,6 +257,10 @@ def append_change(table_name, attrs, buf)
append_change_table(table_name, buf) do
append_change_definition(table_name, attrs[:definition] || {}, buf)
append_change_indices(table_name, attrs[:indices] || {}, buf)

if @options[:enable_foreigner]
Ridgepole::ForeignKey.append_change_foreign_keys(table_name, attrs[:foreign_keys] || {}, buf, @options)
end
end

buf.puts
Expand Down Expand Up @@ -324,4 +388,8 @@ def append_remove_index(table_name, index_name, attrs, buf)
EOS
end
end

def delta_execute
@delta[:execute] || []
end
end
14 changes: 10 additions & 4 deletions lib/ridgepole/diff.rb
Expand Up @@ -3,9 +3,9 @@ def initialize(options = {})
@options = options
end

def diff(from, to)
from = (from || {}).dup
to = (to || {}).dup
def diff(from, to, options = {})
from = (from || {}).deep_dup
to = (to || {}).deep_dup

if @options[:reverse]
from, to = to, from
Expand Down Expand Up @@ -36,6 +36,8 @@ def diff(from, to)
end
end

delta[:execute] = options[:execute]

Ridgepole::Delta.new(delta, @options)
end

Expand Down Expand Up @@ -77,6 +79,10 @@ def scan_change(table_name, from, to, delta)
scan_definition_change(from[:definition], to[:definition], from[:indices], table_delta)
scan_indices_change(from[:indices], to[:indices], to[:definition], table_delta, from[:options], to[:options])

if @options[:enable_foreigner]
Ridgepole::ForeignKey.scan_foreign_keys_change(from[:foreign_keys], to[:foreign_keys], table_delta, @options)
end

unless table_delta.empty?
delta[:change] ||= {}
delta[:change][table_name] = table_delta
Expand Down Expand Up @@ -249,7 +255,7 @@ def normalize_column_options!(attrs)
end

# XXX: MySQL only?
unless @options[:disable_mysql_unsigned]
if @options[:enable_mysql_unsigned]
opts[:unsigned] = false unless opts.has_key?(:unsigned)
end
end
Expand Down

0 comments on commit 49ae219

Please sign in to comment.