Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Commit

Permalink
Merge pull request #119 from lomba/rails410
Browse files Browse the repository at this point in the history
Added support for Rails Edge 4.1.0beta
  • Loading branch information
ronen committed Aug 20, 2013
2 parents 8ab6a78 + dad4fa3 commit df7a925
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 83 deletions.
9 changes: 8 additions & 1 deletion lib/schema_plus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,16 @@ def self.insert_connection_adapters #:nodoc:
::ActiveRecord::ConnectionAdapters::SchemaStatements.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::SchemaStatements)
::ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::TableDefinition)

if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r >= "4.1".to_r
::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::AbstractAdapter::AddColumnOptions)
else
::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::AbstractAdapter::AddColumnOptions)
end

if ::ActiveRecord::VERSION::MAJOR.to_i >= 4
::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation)
::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::AbstractAdapter::VisitTableDefinition)
end

end

def self.insert #:nodoc:
Expand Down
125 changes: 72 additions & 53 deletions lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ def initialize_with_schema_plus(*args) #:nodoc:
# for this.
adapter_module = SchemaPlus::ActiveRecord::ConnectionAdapters.const_get(adapter)
self.class.send(:include, adapter_module) unless self.class.include?(adapter_module)

if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r >= "4.1".to_r
self.class.const_get(:SchemaCreation).send(:include, adapter_module.const_get(:AddColumnOptions))
else
self.class.send(:include, adapter_module.const_get(:AddColumnOptions))
end

extend(SchemaPlus::ActiveRecord::ForeignKeys)
end

Expand Down Expand Up @@ -113,26 +120,73 @@ def supports_partial_indexes?
false
end

def add_column_options!(sql, options)
if options_include_default?(options)
default = options[:default]
if default.is_a? Hash
value = default[:value]
expr = sql_for_function(default[:expr]) || default[:expr] if default[:expr]
else
value = default
expr = sql_for_function(default)
end
if expr
raise ArgumentError, "Invalid default expression" unless default_expr_valid?(expr)
sql << " DEFAULT #{expr}"
module AddColumnOptions
def self.included(base) #:nodoc:
base.alias_method_chain :add_column_options!, :schema_plus
end

def add_column_options_with_schema_plus!(sql, options)
if options_include_default?(options)
default = options[:default]

if default.is_a? Hash
value = default[:value]
expr = sql_for_function(default[:expr]) || default[:expr] if default[:expr]
else
value = default
expr = sql_for_function(default)
end

if expr
raise ArgumentError, "Invalid default expression" unless default_expr_valid?(expr)
sql << " DEFAULT #{expr}"
# must explicitly check for :null to allow change_column to work on migrations
if options[:null] == false
sql << " NOT NULL"
end
else
add_column_options_without_schema_plus!(sql, options.merge(default: value))
end
else
sql << " DEFAULT #{quote(value, options[:column])}" unless value.nil?
add_column_options_without_schema_plus!(sql, options)
end
end
# must explicitly check for :null to allow change_column to work on migrations
if options[:null] == false
sql << " NOT NULL"

#####################################################################
#
# The functions below here are abstract; each subclass should
# define them all. Defining them here only for reference.

# (abstract) Return true if the passed expression can be used as a column
# default value. (For most databases the specific expression
# doesn't matter, and the adapter's function would return a
# constant true if default expressions are supported or false if
# they're not.)
def default_expr_valid?(expr) raise "Internal Error: Connection adapter didn't override abstract function"; end

# (abstract) Return SQL definition for a given canonical function_name symbol.
# Currently, the only function to support is :now, which should
# return a DATETIME object for the current time.
def sql_for_function(function_name) raise "Internal Error: Connection adapter didn't override abstract function"; end
end

module VisitTableDefinition
def self.included(base) #:nodoc:
base.alias_method_chain :visit_TableDefinition, :schema_plus
end

def visit_TableDefinition_with_schema_plus(o) #:nodoc:
create_sql = visit_TableDefinition_without_schema_plus(o)
last_chunk = ") #{o.options}"

unless create_sql.end_with?(last_chunk)
raise "Internal Error: Can't find '#{last_chunk}' at end of '#{create_sql}' - Rails internals have changed!"
end

unless o.foreign_keys.empty?
create_sql[create_sql.size - last_chunk.size, 0] = ', ' + o.foreign_keys.map(&:to_sql) * ', '
end
create_sql
end
end

Expand All @@ -141,7 +195,7 @@ def add_column_options!(sql, options)
# The functions below here are abstract; each subclass should
# define them all. Defining them here only for reference.
#

# (abstract) Returns the names of all views, as an array of strings
def views(name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end

Expand All @@ -157,41 +211,6 @@ def foreign_keys(table_name, name = nil) raise "Internal Error: Connection adapt
# (abstract) Return the ForeignKeyDefinition objects for foreign key
# constraints defined on other tables that reference this table
def reverse_foreign_keys(table_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; [] end

# (abstract) Return true if the passed expression can be used as a column
# default value. (For most databases the specific expression
# doesn't matter, and the adapter's function would return a
# constant true if default expressions are supported or false if
# they're not.)
def default_expr_valid?(expr) raise "Internal Error: Connection adapter didn't override abstract function"; end

# (abstract) Return SQL definition for a given canonical function_name symbol.
# Currently, the only function to support is :now, which should
# return a DATETIME object for the current time.
def sql_for_function(function_name) raise "Internal Error: Connection adapter didn't override abstract function"; end


if ::ActiveRecord::VERSION::MAJOR.to_i >= 4
module SchemaCreation
def self.included(base) #:nodoc:
base.alias_method_chain :visit_TableDefinition, :schema_plus
end

def visit_TableDefinition_with_schema_plus(o) #:nodoc:
create_sql = visit_TableDefinition_without_schema_plus(o)
last_chunk = ") #{o.options}"

unless create_sql.end_with?(last_chunk)
raise "Internal Error: Can't find '#{last_chunk}' at end of '#{create_sql}' - Rails internals have changed!"
end

unless o.foreign_keys.empty?
create_sql[create_sql.size - last_chunk.size, 0] = ', ' + o.foreign_keys.map(&:to_sql) * ', '
end
create_sql
end
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,15 @@ def view_definition(view_name, name = nil)
sql
end

def default_expr_valid?(expr)
false # only the TIMESTAMP column accepts SQL column defaults and rails uses DATETIME
end
module AddColumnOptions
def default_expr_valid?(expr)
false # only the TIMESTAMP column accepts SQL column defaults and rails uses DATETIME
end

def sql_for_function(function)
case function
when :now then 'CURRENT_TIMESTAMP'
def sql_for_function(function)
case function
when :now then 'CURRENT_TIMESTAMP'
end
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,14 +289,16 @@ def load_foreign_keys(sql, name = nil) #:nodoc:
foreign_keys
end

def default_expr_valid?(expr)
true # arbitrary sql is okay in PostgreSQL
end
module AddColumnOptions
def default_expr_valid?(expr)
true # arbitrary sql is okay in PostgreSQL
end

def sql_for_function(function)
case function
when :now
"NOW()"
def sql_for_function(function)
case function
when :now
"NOW()"
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def self.included(base)
alias_method_chain :indexes, :schema_plus
alias_method_chain :rename_table, :schema_plus
end

if ::ActiveRecord::VERSION::MAJOR.to_i < 4
::ActiveRecord::ConnectionAdapters::SQLiteColumn.send(:include, SQLiteColumn) unless ::ActiveRecord::ConnectionAdapters::SQLiteColumn.include?(SQLiteColumn)
else
Expand Down Expand Up @@ -116,14 +117,16 @@ def get_foreign_keys(table_name = nil, name = nil)
foreign_keys
end

def default_expr_valid?(expr)
true # arbitrary sql is okay
end
module AddColumnOptions
def default_expr_valid?(expr)
true # arbitrary sql is okay
end

def sql_for_function(function)
case function
when :now
"(DATETIME('now'))"
def sql_for_function(function)
case function
when :now
"(DATETIME('now'))"
end
end
end
end
Expand Down
78 changes: 69 additions & 9 deletions spec/column_definition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@
end
end

let(:connection) { ActiveRecord::Base.connection }
def call_add_column_options!(*params, &block)
if ::ActiveRecord::VERSION::MAJOR >= 4
ActiveRecord::Base.connection.schema_creation.send(:add_column_options!, *params, &block)
else
ActiveRecord::Base.connection.add_column_options!(*params, &block)
end
end

context "text columns" do
before(:each) do
Expand All @@ -32,7 +38,7 @@

context "just default passed" do
before(:each) do
connection.add_column_options!(@sql, { :default => "2011-12-11 00:00:00" })
call_add_column_options!(@sql, { :default => "2011-12-11 00:00:00" })
end

subject { @sql}
Expand All @@ -44,7 +50,7 @@

context "just default passed in hash" do
before(:each) do
connection.add_column_options!(@sql, { :default => { :value => "2011-12-11 00:00:00" } })
call_add_column_options!(@sql, { :default => { :value => "2011-12-11 00:00:00" } })
end

subject { @sql}
Expand All @@ -54,10 +60,34 @@
end
end

context "default passed with no nulls" do
before(:each) do
call_add_column_options!(@sql, { :default => "2011-12-11 00:00:00", null: false })
end

subject { @sql}

it "should use the normal default" do
should == "time_taken text DEFAULT '2011-12-11 00:00:00' NOT NULL"
end
end

context "default passed in hash with no nulls" do
before(:each) do
call_add_column_options!(@sql, { :default => { :value => "2011-12-11 00:00:00" }, null: false })
end

subject { @sql}

it "should use the normal default" do
should == "time_taken text DEFAULT '2011-12-11 00:00:00' NOT NULL"
end
end

context "default function passed as now" do
before(:each) do
begin
connection.add_column_options!(@sql, { :default => :now })
call_add_column_options!(@sql, { :default => :now })
rescue ArgumentError => e
@raised_argument_error = e
end
Expand All @@ -84,8 +114,38 @@
end
end

context "default function passed as now with no nulls" do
before(:each) do
begin
call_add_column_options!(@sql, { :default => :now, null: false })
rescue ArgumentError => e
@raised_argument_error = e
end
end

subject { @sql }

if SchemaPlusHelpers.postgresql?
it "should use NOW() as the default" do
should == "time_taken text DEFAULT NOW() NOT NULL"
end
end

if SchemaPlusHelpers.sqlite3?
it "should use NOW() as the default" do
should == "time_taken text DEFAULT (DATETIME('now')) NOT NULL"
end
end

if SchemaPlusHelpers.mysql?
it "should raise an error" do
@raised_argument_error.should be_a ArgumentError
end
end
end

context "valid expr passed as default" do
subject { connection.add_column_options!(@sql, { :default => { :expr => 'NOW()' } }); @sql }
subject { call_add_column_options!(@sql, { :default => { :expr => 'NOW()' } }); @sql }

if SchemaPlusHelpers.postgresql?
it "should use NOW() as the default" do
Expand All @@ -109,11 +169,11 @@
context "invalid expr passed as default" do
if SchemaPlusHelpers.mysql?
it "should raise an error" do
lambda {connection.add_column_options!(@sql, { :default => { :expr => "ARBITRARY_EXPR" } })}.should raise_error ArgumentError
lambda {call_add_column_options!(@sql, { :default => { :expr => "ARBITRARY_EXPR" } })}.should raise_error ArgumentError
end
else
it "should just accept the SQL" do
connection.add_column_options!(@sql, { :default => { :expr => "ARBITRARY_EXPR" } })
call_add_column_options!(@sql, { :default => { :expr => "ARBITRARY_EXPR" } })
@sql.should == "time_taken text DEFAULT ARBITRARY_EXPR"
end
end
Expand All @@ -127,7 +187,7 @@

context "passed as boolean false" do
before(:each) do
connection.add_column_options!(@sql, { :default => false })
call_add_column_options!(@sql, { :default => false })
end

subject { @sql}
Expand All @@ -139,7 +199,7 @@

context "passed as boolean true" do
before(:each) do
connection.add_column_options!(@sql, { :default => true })
call_add_column_options!(@sql, { :default => true })
end

subject { @sql}
Expand Down

0 comments on commit df7a925

Please sign in to comment.