Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dux/c7c90677d2112558c334 to your computer and use it in GitHub Desktop.
Save dux/c7c90677d2112558c334 to your computer and use it in GitHub Desktop.
Ruby ActiveRecord auto migrations without Rails, standalone
#!/usr/bin/env ruby
require './scripts/lib/auto_migrate.rb'
db_config = YAML.load_file('./config/database.yml')[ENV['RACK_ENV'] || 'development']
AutoMigrate.connect(db_config)
require './config/schema.rb'
#!/usr/bin/env ruby
require 'ap'
require 'yaml'
require 'active_support'
require 'active_record'
require 'colorize'
class AutoMigrate
attr_accessor :fields
def initialize(table_name)
@table_name = table_name
@fields = {}
end
def self.connect(opts)
puts "#{opts['adapter']}://#{opts['username']}@#{opts['database']}".yellow
puts '--'
ActiveRecord::Base.establish_connection(opts)
end
def self.table(table_name)
klass = table_name.to_s.classify
Object.class_eval "class #{klass} < ActiveRecord::Base; end" unless Object.const_defined?(klass)
unless ActiveRecord::Base.connection.table_exists?(table_name.to_s)
# ActiveRecord::Migration.drop_table table_name
ActiveRecord::Base.connection.create_table(table_name)
end
t = new(table_name)
yield(t)
t.fix_fields
t.update
end
def self.migrate(&block)
instance_eval(&block)
end
def fix_fields
for field, vals in @fields
type = vals[0]
opts = vals[1]
if type == :string
opts[:limit] ||= 255
end
if type == :boolean
opts[:default] ||= false
end
opts[:null] = true unless opts[:null].class.name == 'FalseClass'
opts[:array] ||= false
opts[:default] = [] if opts[:array]
end
end
def update
begin
obj = @table_name.to_s.classify.constantize
o = obj.new
rescue
puts "Object #{@table_name.to_s.classify.red} does not exist, yet table #{@table_name.to_s.red} exists!"
return
end
# remove extra fields
existing_fields = o.attributes.keys - ['id']
for field in (existing_fields - @fields.keys.map(&:to_s))
ActiveRecord::Migration.remove_column @table_name, field
end
puts "Table #{@table_name.to_s.yellow}, #{@fields.keys.length} fields"
for field, opts_in in @fields
type = opts_in[0]
opts = opts_in[1]
# create missing columns
unless o.respond_to?(field)
ActiveRecord::Migration.add_column @table_name, field, type, opts
next
end
if current = obj.columns_hash[field.to_s]
current_type = current.cast_type.type
is_change = false
is_change = true if current_type != type
is_change = true if current_type == :string && ((current.limit || 255).to_i != opts[:limit] || current.null != opts[:null])
is_change = true if current.array != opts[:array]
if is_change
puts "#{field} limit:#{current.limit || 255} != #{opts[:limit]}, null:#{current.null} != #{opts[:null]}, array:#{current.array} != #{opts[:array]}"
ActiveRecord::Migration.change_column @table_name, field, type, opts
end
end
add_index(field, opts[:index]) if opts[:index]
end
end
def add_index(field, index=nil)
field = [field, index.to_sym] if index && !index.kind_of?(TrueClass)
ActiveRecord::Migration.add_index(@table_name, field) unless ActiveRecord::Migration.index_exists?(@table_name, field)
end
def rename(field_old, field_new)
existing_fields = @table_name.to_s.classify.constantize.new.attributes.keys.map(&:to_sym)
if existing_fields.index(field_old) && ! existing_fields.index(field_new)
ActiveRecord::Migration.rename_column(@table_name, field_old, field_new)
end
end
def method_missing(type, *args)
name = args[0]
opts = args[1] || {}
# puts "#{@table_name} - #{type} - #{args[0]}"
if [:string, :integer, :text, :boolean, :datetime].index(type)
@fields[name] = [type, opts]
elsif type == :timestamps
opts[:null] ||= false
@fields[:created_at] = [:datetime, opts]
@fields[:created_by] = [:integer, opts]
@fields[:updated_at] = [:datetime, opts]
@fields[:updated_by] = [:integer, opts]
elsif type == :polymorphic
@fields["#{name}_id".to_sym] = [:integer, opts]
@fields["#{name}_type".to_sym] = [:string, opts.merge(:limit=>100, :index=>"#{name}_id")]
else
puts "Unknown #{type.to_s.red}"
end
end
end
# t.rename :old_field_name, :new_field_name
# t.timestamps
# t.polymorphic :grp
# t.add_index :created_by
# rest same as rails schema
AutoMigrate.migrate do
table :logs do |t|
t.string "kind"
t.string "name"
t.integer "created_by"
t.datetime "created_at"
t.polymorphic :grp
end
table :buckets do |t|
t.string "name", null: false
t.text "description"
t.boolean "active", default: true
t.string "tags", array: true
t.integer "child_buckets", array: true
t.string "template"
t.string "image"
t.timestamps
t.add_index :created_by
end
table :links do |t|
t.string "name", null: false
t.string "url", null: false
t.string "domain", null: false
t.string "tags", array: true
t.string "kind"
t.string "description"
t.boolean "active", default: true
t.boolean "is_article", default: false
t.integer "bucket_id", null: false
t.timestamps
t.add_index :created_by
end
table :notes do |t|
t.string "name", null: false
t.text "data"
t.integer "bucket_id", null: false
t.boolean "active", default: true
t.string "tags", array: true
t.timestamps
end
table :domains do |t|
t.string "name", null: false
t.text "description"
t.boolean "is_article"
t.datetime "created_at", null:true
t.datetime "updated_at", null:true
t.integer "created_by", null:true, index:true
end
table :users do |t|
t.string "name", limit: 140
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email"
t.string "avatar"
t.boolean "active"
t.string "pass"
t.string "connect_via"
t.string "description"
t.string "token"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment