Skip to content

Instantly share code, notes, and snippets.

@tmm1
Created June 23, 2010 23:36
Show Gist options
  • Save tmm1/450736 to your computer and use it in GitHub Desktop.
Save tmm1/450736 to your computer and use it in GitHub Desktop.
patch to add sequel support to a new rails3 app
diff --git a/app/models/model.rb b/app/models/model.rb
new file mode 100644
index 0000000..14c4620
--- /dev/null
+++ b/app/models/model.rb
@@ -0,0 +1,2 @@
+class Model < Sequel::Model
+end
\ No newline at end of file
diff --git a/config/application.rb b/config/application.rb
index 43d772f..6bc5bf0 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -1,6 +1,11 @@
require File.expand_path('../boot', __FILE__)
-require 'rails/all'
+# Since we don't need ActiveRecord or ActiveResource we'll just pick
+# which frameworks to include in our app
+# require 'rails/all'
+require 'active_support/all'
+require 'action_controller/railtie'
+require 'action_mailer/railtie'
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
diff --git a/config/initializers/sequel.rb b/config/initializers/sequel.rb
new file mode 100644
index 0000000..231a6f9
--- /dev/null
+++ b/config/initializers/sequel.rb
@@ -0,0 +1,45 @@
+# TODO modify sequel serialization plugin to use yajl as a backend
+require 'yajl/json_gem'
+JSON.default_options.merge! :symbolize_keys=>true
+
+module Kernel
+ if defined? id
+ undef :id
+ undef :type
+ end
+end
+
+module Sequel
+ class Dataset
+ def size
+ count
+ end
+ end
+end
+
+# Setup plugins and extensions we're using
+Sequel.extension :pagination
+Sequel.extension :pretty_table
+
+Sequel::Model.unrestrict_primary_key
+Sequel::Model.plugin :hook_class_methods
+Sequel::Model.plugin :active_model
+Sequel::Model.plugin :validation_helpers
+Sequel::Model.plugin :timestamps
+Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS[:presence].update(
+ :message => lambda{"is missing"}
+)
+
+# this lives in /lib
+require File.expand_path('../../../lib/sequel_extensions', __FILE__)
+
+Sequel.default_timezone = :utc
+
+# clear out any existing connections
+Sequel::DATABASES.clear
+Sequel::Model.db = nil
+
+# connect to the db configured for the current Rails.env
+DB = Sequel.connect(Rails.configuration.database_configuration[Rails.env])
+
+DB.loggers << Rails.logger
\ No newline at end of file
diff --git a/db/migrate/20100623000000_model.rb b/db/migrate/20100623000000_model.rb
new file mode 100644
index 0000000..1dca971
--- /dev/null
+++ b/db/migrate/20100623000000_model.rb
@@ -0,0 +1,22 @@
+Class.new(Sequel::Migration) do
+ def up
+ create_table :models do
+ primary_key :id
+ varchar :name, :null => false
+ int :size
+ blob :data
+ datetime :created_at
+
+ index :created_at
+ end
+ alter_table :models do
+ drop_column :name
+ add_column :description, :blob
+ set_column_type :description, :text
+ end
+ end
+
+ def down
+ drop_table :models
+ end
+end
\ No newline at end of file
diff --git a/lib/sequel_extensions.rb b/lib/sequel_extensions.rb
new file mode 100644
index 0000000..4a47b63
--- /dev/null
+++ b/lib/sequel_extensions.rb
@@ -0,0 +1,49 @@
+require 'securerandom'
+
+class Sequel::Model
+ def type
+ self[:type]
+ end
+
+ def_dataset_method :on_duplicate_key_update, :insert_ignore
+
+ def set_columns(params, *fields)
+ fields = fields.flatten
+ params = params.reject{ |k,v| !fields.include?(k.to_sym) }
+ set_only(params, fields)
+ end
+
+ def save!
+ save(:validate=>false)
+ end
+
+ def full_messages(options = {})
+ full_messages = []
+
+ @errors.each_key do |attr|
+ @errors[attr].each do |message|
+ next unless message
+
+ if attr == "base"
+ full_messages << message
+ else
+ attr_name = @base.class.human_attribute_name(attr)
+ full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message
+ end
+ end
+ end
+ full_messages
+ end
+
+ alias :attributes :values
+end
+
+class Sequel::Dataset
+ def keyed_hash(key)
+ inject({}){|h,v| h.update v[key] => v }
+ end
+
+ def keyed_arr_hash(key)
+ inject(Hash.new{|ho,k| ho[k] = []}){|h,v| h[v[key]] << v; h}
+ end
+end
\ No newline at end of file
diff --git a/lib/sequel_migration.rb b/lib/sequel_migration.rb
new file mode 100644
index 0000000..233b9eb
--- /dev/null
+++ b/lib/sequel_migration.rb
@@ -0,0 +1,159 @@
+require File.expand_path('../config/application', __FILE__)
+require 'active_record/errors'
+require 'active_record/migration'
+
+DB = Sequel.connect(Rails.configuration.database_configuration[Rails.env])
+DB.loggers << Logger.new(STDOUT)
+
+if defined? ActiveRecord::Base
+ raise "cannot use the migration hack if ActiveRecord::Base exists"
+else
+ module ActiveRecord::Base
+ class << self
+ def connection
+ unless DB.respond_to?(:initialize_schema_migrations_table)
+ def DB.initialize_schema_migrations_table
+ unless table_exists?(:schema_migrations)
+ create_table(:schema_migrations) do
+ varchar :version, :null => false, :unique => true
+ end
+
+ if table_exists?(:schema_info)
+ version = DB[:schema_info].get(:version)
+ migrated = DB[:schema_migrations].select(:version).map(:version).map(&:to_i)
+
+ migrations_path = ActiveRecord::Migrator.migrations_path
+ versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename|
+ filename.split('/').last.split('_').first.to_i
+ end
+
+ DB[:schema_migrations].insert_ignore.insert(:version => version)
+
+ inserted = Set.new
+ (versions - migrated).each do |v|
+ if inserted.include?(v)
+ raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
+ elsif v < version
+ DB[:schema_migrations].insert(:version => v)
+ inserted << v
+ end
+ end
+
+ DB.drop_table(:schema_info)
+ end
+ end
+ end
+ def DB.supports_migrations?
+ true
+ end
+ def DB.supports_ddl_transactions?
+ false
+ end
+ end
+
+ DB
+ end
+ def logger
+ DB.loggers.first
+ end
+ def transaction(&blk)
+ DB.transaction(&blk)
+ end
+ end
+ end
+end
+
+module Sequel
+ extension :migration
+ remove_const :Migrator
+ class Migration
+ class << self
+ def migrate(direction)
+ case direction
+ when :up then announce "migrating"
+ when :down then announce "reverting"
+ end
+
+ result = nil
+ time = Benchmark.measure { result = new(DB).send(direction) }
+
+ case direction
+ when :up then announce "migrated (%.4fs)" % time.real; write
+ when :down then announce "reverted (%.4fs)" % time.real; write
+ end
+
+ result
+ end
+
+ def write(text="")
+ puts(text)
+ end
+
+ def announce(message)
+ version = defined?(@version) ? @version : nil
+
+ text = "#{version} #{name}: #{message}"
+ length = [0, 75 - text.length].max
+ write "== %s %s" % [text, "=" * length]
+ end
+ end
+ end
+ class MigrationProxy < ActiveRecord::MigrationProxy
+ def load_migration
+ Migration.descendants.clear
+ load(File.expand_path(filename))
+ klass = Migration.descendants.last
+ klass
+ end
+ end
+ class Migrator < ActiveRecord::Migrator
+ class << self
+ def schema_migrations_table_name
+ :schema_migrations
+ end
+ def migrations_table
+ DB[schema_migrations_table_name]
+ end
+ def get_all_versions
+ migrations_table.select(:version).map(:version).map(&:to_i).sort
+ end
+ def current_version
+ if DB.table_exists?(schema_migrations_table_name)
+ migrations_table.order(:version.desc).limit(1).get(:version) || 0
+ else
+ 0
+ end
+ end
+ def proper_table_name(name)
+ name
+ end
+ end
+
+ def migrations
+ unless @migrations
+ super
+ @migrations.map!{ |m|
+ o = MigrationProxy.new
+ o.name, o.version, o.filename = m.name, m.version, m.filename
+ o
+ }
+ end
+ @migrations
+ end
+
+ def record_version_state_after_migrating(version)
+ @migrated_versions ||= []
+ if down?
+ @migrated_versions.delete(version)
+ self.class.migrations_table.where(:version => version).delete
+ else
+ @migrated_versions.push(version).sort!
+ self.class.migrations_table.insert(:version => version)
+ end
+ end
+
+ def ddl_transaction
+ yield # assume no transactions for ddl (mysql)
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/tasks/sequel.rake b/lib/tasks/sequel.rake
new file mode 100644
index 0000000..2f20b78
--- /dev/null
+++ b/lib/tasks/sequel.rake
@@ -0,0 +1,147 @@
+# TODO: DRY these up
+namespace :db do
+ namespace :schema do
+ desc "Create a db/schema.rb file that can be portably used against any DB supported by Sequel"
+ task :dump do
+ Sequel.extension :schema_dumper
+ db = Sequel.connect(Rails.configuration.database_configuration[Rails.env])
+ File.open(ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb", "w") do |file|
+ file.write(db.dump_schema_migration)
+ end
+ Rake::Task["db:schema:dump"].reenable
+ end
+
+ desc "Load a schema.rb file into the database"
+ task :load do
+ file = ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb"
+ if File.exists?(file)
+ load(file)
+ else
+ abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/boot.rb to limit the frameworks that will be loaded}
+ end
+ end
+ end
+
+ namespace :create do
+ desc 'Create all the local databases defined in config/database.yml'
+ task :all do
+ Rails.configuration.database_configuration.each_value do |config|
+ next unless config['database']
+ database = config.delete('database')
+ DB = Sequel.connect(config)
+ default_options = "DEFAULT CHARSET utf8 COLLATE utf8_unicode_ci"
+ puts "Creating database \"#{config['database']}\" if it doesn't already exist"
+ DB.run "CREATE DATABASE IF NOT EXISTS `#{config['database']}` /*!40100 #{default_options} */"
+ end
+ end
+ end
+
+ desc "Create the database defined in config/database.yml for the current Rails.env - also creates the test database if Rails.env.development?"
+ task :create do
+ connect_options = Rails.configuration.database_configuration[Rails.env]
+ connect_options.delete('database')
+ DB = Sequel.connect(connect_options)
+ default_options = "DEFAULT CHARSET utf8 COLLATE utf8_unicode_ci"
+ puts "Creating database \"#{Rails.configuration.database_configuration[Rails.env]['database']}\" if it doesn't already exist"
+ DB.run "CREATE DATABASE IF NOT EXISTS `#{Rails.configuration.database_configuration[Rails.env]['database']}` /*!40100 #{default_options} */"
+ if Rails.env.development? && Rails.configuration.database_configuration['test']
+ puts "Creating database \"#{Rails.configuration.database_configuration['test']['database']}\" if it doesn't already exist"
+ DB.run "CREATE DATABASE IF NOT EXISTS `#{Rails.configuration.database_configuration['test']['database']}` /*!40100 #{default_options} */"
+ end
+ end
+
+ namespace :drop do
+ desc 'Drops all the local databases defined in config/database.yml'
+ task :all do
+ Rails.configuration.database_configuration.each_value do |config|
+ next unless config['database']
+ database = config.delete('database')
+ DB = Sequel.connect(config)
+ puts "Dropping database #{database} if it exists"
+ DB.run "DROP DATABASE IF EXISTS `#{database}`"
+ end
+ end
+ end
+
+ desc "Create the database defined in config/database.yml for the current Rails.env - also creates the test database if Rails.env.development?"
+ task :drop do
+ connect_options = Rails.configuration.database_configuration[Rails.env]
+ connect_options.delete('database')
+ DB = Sequel.connect(connect_options)
+
+ puts "Dropping database #{Rails.configuration.database_configuration[Rails.env]['database']} if it exists"
+ DB.run "DROP DATABASE IF EXISTS `#{Rails.configuration.database_configuration[Rails.env]['database']}`"
+ end
+
+ namespace :migrate do
+ task :load do
+ require File.expand_path('../../sequel_migration', __FILE__)
+ end
+
+ desc 'Rollbacks the database one migration and re migrate up. If you want to rollback more than one step, define STEP=x. Target specific version with VERSION=x.'
+ task :redo => :load do
+ if ENV["VERSION"]
+ Rake::Task["db:migrate:down"].invoke
+ Rake::Task["db:migrate:up"].invoke
+ else
+ Rake::Task["db:rollback"].invoke
+ Rake::Task["db:migrate"].invoke
+ end
+ end
+
+ desc 'Resets your database using your migrations for the current environment'
+ task :reset => ["db:drop", "db:create", "db:migrate"]
+
+ desc 'Runs the "up" for a given migration VERSION.'
+ task :up => :load do
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+ raise "VERSION is required" unless version
+ Sequel::Migrator.run(:up, "db/migrate/", version)
+ Rake::Task["db:schema:dump"].invoke
+ end
+
+ desc 'Runs the "down" for a given migration VERSION.'
+ task :down => :load do
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
+ raise "VERSION is required" unless version
+ Sequel::Migrator.run(:down, "db/migrate/", version)
+ Rake::Task["db:schema:dump"].invoke
+ end
+ end
+
+ desc 'Migrate the database to the latest version'
+ task :migrate => :'migrate:load' do
+ Sequel::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
+ Rake::Task["db:schema:dump"].invoke
+ end
+
+ desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n'
+ task :rollback => :'migrate:load' do
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
+ Sequel::Migrator.rollback('db/migrate/', step)
+ Rake::Task["db:schema:dump"].invoke
+ end
+
+ desc 'Pushes the schema to the next version. Specify the number of steps with STEP=n'
+ task :forward => :'migrate:load' do
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
+ Sequel::Migrator.forward('db/migrate/', step)
+ Rake::Task["db:schema:dump"].invoke
+ end
+
+ desc 'Load the seed data from db/seeds.rb'
+ task :seed => :environment do
+ seed_file = File.join(Rails.root, 'db', 'seeds.rb')
+ load(seed_file) if File.exist?(seed_file)
+ end
+
+ desc 'Create the database, load the schema, and initialize with the seed data'
+ task :setup => [ 'db:create', 'db:migrate', 'db:seed' ]
+
+ desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
+ task :reset => [ 'db:drop', 'db:setup' ]
+
+ namespace :test do
+ task :prepare => ['db:reset']
+ end
+end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment