Skip to content

Commit

Permalink
added Sequel support
Browse files Browse the repository at this point in the history
  • Loading branch information
sferik committed Oct 26, 2009
1 parent ccb8ff0 commit 061fa28
Show file tree
Hide file tree
Showing 15 changed files with 540 additions and 2 deletions.
14 changes: 14 additions & 0 deletions lib/abstract_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ def self.all
@models << new(model) if model
end
@models.sort!{|a, b| a.model.to_s <=> b.model.to_s}
when :sequel
Dir.glob(Merb.dir_for(:model) / Merb.glob_for(:model)).each do |filename|
# FIXME: This heuristic for finding Sequel models could be too strict
File.read(filename).scan(/^class ([\w\d_\-:]+) < Sequel::Model$/).flatten.each do |m|
model = lookup(m.to_s.to_sym)
@models << new(model) if model
end
end
@models.sort!{|a, b| a.model.to_s <=> b.model.to_s}
else
raise "MerbAdmin does not support the #{Merb.orm} ORM"
end
Expand All @@ -42,6 +51,8 @@ def self.lookup(model_name)
return model if model.superclass == ActiveRecord::Base
when :datamapper
return model if model.include?(DataMapper::Resource)
when :sequel
return model if model.superclass == Sequel::Model
end
nil
end
Expand All @@ -59,6 +70,9 @@ def initialize(model)
when :datamapper
require 'datamapper_support'
self.extend(DatamapperSupport)
when :sequel
require 'sequel_support'
self.extend(SequelSupport)
else
raise "MerbAdmin does not support the #{Merb.orm} ORM"
end
Expand Down
25 changes: 23 additions & 2 deletions lib/merb-admin/slicetasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,27 @@
end
end

desc "Copies sample models, copies and runs sample migrations, and loads sample data"
task :sequel => ["sequel:copy_sample_models", "sequel:copy_sample_migrations", "sequel:migrate", "load_sample_data"]
namespace :sequel do
desc "Copies sample models into your app"
task :copy_sample_models do
copy_models(:sequel)
end

desc "Copies sample migrations into your app"
task :copy_sample_migrations do
copy_migrations(:sequel)
end

desc "Perform migration using migrations in schema/migrations"
task :migrate do
require 'sequel/extensions/migration'
Rake::Task["sequel:db:migrate"].reenable
Rake::Task["sequel:db:migrate"].invoke
end
end

desc "Loads sample data into your app"
task :load_sample_data do
load_data
Expand All @@ -63,13 +84,13 @@

def load_data
begin
require "mlb"
require "mlb"
rescue LoadError => e
puts "LoadError: #{e}"
puts "gem install mlb -s http://gemcutter.org"
return
end

puts "Loading current MLB leagues, divisions, teams, and players"
MLB.teams.each do |mlb_team|
unless league = MerbAdmin::AbstractModel.new("League").first(:conditions => ["name = ?", mlb_team.league])
Expand Down
309 changes: 309 additions & 0 deletions lib/sequel_support.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
require 'sequel'
require 'sequel/extensions/pagination'

class Sequel::Model
=begin
# Intialize each column to the default value for new model objects
def after_initialize
super
model.columns.each do |x|
if !@values.include?(x) && db_schema[x][:allow_null]
send("#{x}=", db_schema[x][:ruby_default])
end
end
end
=end

# Return an empty array for *_to_many association methods for new model objects
def _load_associated_objects(opts)
opts.returns_array? && new? ? [] : super
end
end

module MerbAdmin
class AbstractModel
module SequelSupport
def get(id)
model.first(:id => id).extend(InstanceMethods)
end

def count(options = {})
if options[:conditions] && !options[:conditions].empty?
# If options[:conditions] isn't cloned, Sequel eats the first condition!
model.where(options[:conditions].clone).count
else
model.count
end
end

def first(options = {})
sort = options.delete(:sort) || :id
sort_order = options.delete(:sort_reverse) ? :desc : :asc

if options[:conditions] && !options[:conditions].empty?
# If options[:conditions] isn't cloned, Sequel eats the first condition!
model.order(sort.to_sym.send(sort_order)).first(options[:conditions].clone).extend(InstanceMethods)
else
model.order(sort.to_sym.send(sort_order)).first.extend(InstanceMethods)
end
end

def all(options = {})
offset = options.delete(:offset)
limit = options.delete(:limit)

sort = options.delete(:sort) || :id
sort_order = options.delete(:sort_reverse) ? :desc : :asc

if options[:conditions] && !options[:conditions].empty?
# If options[:conditions] isn't cloned, Sequel eats the first condition!
model.where(options[:conditions].clone).order(sort.to_sym.send(sort_order))
else
model.order(sort.to_sym.send(sort_order))
end
end

def paginated(options = {})
page = options.delete(:page) || 1
per_page = options.delete(:per_page) || MerbAdmin[:per_page]
page_count = (count(options).to_f / per_page).ceil

sort = options.delete(:sort) || :id
sort_order = options.delete(:sort_reverse) ? :desc : :asc

if options[:conditions] && !options[:conditions].empty?
# If options[:conditions] isn't cloned, Sequel eats the first condition!
[page_count, model.paginate(page.to_i, per_page).where(options[:conditions].clone).order(sort.to_sym.send(sort_order))]
else
[page_count, model.paginate(page.to_i, per_page).order(sort.to_sym.send(sort_order))]
end
end

def create(params = {})
model.create(params)
end

def new(params = {})
model.new(params).extend(InstanceMethods)
end

def destroy_all!
model.all.each do |object|
object.destroy
end
end

def has_many_associations
associations.select do |association|
association[:type] == :has_many
end
end

def has_one_associations
associations.select do |association|
association[:type] == :has_one
end
end

def belongs_to_associations
associations.select do |association|
association[:type] == :belongs_to
end
end

def associations
model.all_association_reflections.map do |association|
{
:name => association_name_lookup(association),
:pretty_name => association_pretty_name_lookup(association),
:type => association_type_lookup(association),
:parent_model => association_parent_model_lookup(association),
:parent_key => association_parent_key_lookup(association),
:child_model => association_child_model_lookup(association),
:child_key => association_child_key_lookup(association),
}
end
end

def properties
model.columns.map do |property|
{
:name => property,
:pretty_name => property.to_s.gsub(/_id$/, "").gsub("_", " ").capitalize,
:type => property_type_lookup(property),
:length => property_length_lookup(property),
:nullable? => model.db_schema[property][:allow_null],
:serial? => model.db_schema[property][:primary_key],
}
end
end

private

def property_type_lookup(property)
case model.db_schema[property][:db_type]
when /\A(?:medium|small)?int(?:eger)?(?:\((?:\d+)\))?\z/io
:integer
when /\Atinyint(?:\((\d+)\))?\z/io
:boolean
when /\Abigint(?:\((?:\d+)\))?\z/io
:integer
when /\A(?:real|float|double(?: precision)?)\z/io
:float
when 'boolean'
:boolean
when /\A(?:(?:tiny|medium|long|n)?text|clob)\z/io
:text
when 'date'
:date
when /\A(?:small)?datetime\z/io
:datetime
when /\Atimestamp(?: with(?:out)? time zone)?\z/io
:datetime
when /\Atime(?: with(?:out)? time zone)?\z/io
:time
when /\An?char(?:acter)?(?:\((\d+)\))?\z/io
:string
when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/io
:string
when /\A(?:small)?money\z/io
:big_decimal
when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/io
:big_decimal
when 'year'
:integer
else
:string
end
end

def property_length_lookup(property)
case model.db_schema[property][:db_type]
when /\An?char(?:acter)?(?:\((\d+)\))?\z/io
$1 ? $1.to_i : 255
when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/io
$1 ? $1.to_i : 255
else
nil
end
end

def association_name_lookup(association)
case association[:type]
when :one_to_many
if association[:one_to_one]
association[:name].to_s.singularize.to_sym
else
association[:name]
end
when :many_to_one
association[:name]
else
raise "Unknown association type"
end
end

def association_pretty_name_lookup(association)
case association[:type]
when :one_to_many
if association[:one_to_one]
association[:name].to_s.singularize.gsub('_', ' ').capitalize
else
association[:name].to_s.gsub('_', ' ').capitalize
end
when :many_to_one
association[:name].to_s.gsub('_', ' ').capitalize
else
raise "Unknown association type"
end
end

def association_type_lookup(association)
case association[:type]
when :one_to_many
if association[:one_to_one]
:has_one
else
:has_many
end
when :many_to_one
:belongs_to
else
raise "Unknown association type"
end
end

def association_parent_model_lookup(association)
case association[:type]
when :one_to_many
association[:model]
when :many_to_one
Object.const_get(association[:class_name])
else
raise "Unknown association type"
end
end

def association_parent_key_lookup(association)
[:id]
end

def association_child_model_lookup(association)
case association[:type]
when :one_to_many
Object.const_get(association[:class_name])
when :many_to_one
association[:model]
else
raise "Unknown association type"
end
end

def association_child_key_lookup(association)
case association[:type]
when :one_to_many
association[:keys]
when :many_to_one
["#{association[:class_name].snake_case}_id".to_sym]
else
raise "Unknown association type"
end
end

module InstanceMethods
def id
super
end

def save
super
end

def destroy
super
end

def update_attributes(attributes)
# NOTE: Not sure why calling update(attributes) raises
# Argument Error: wrong number of arguments (1 for 0)
# but this seems to work:
set(attributes)
save
end

def errors
super
end

def clear_association(association)
association.clear # FIXME!
end

def reset
super
end
end

end
end
end
Loading

0 comments on commit 061fa28

Please sign in to comment.