schema_plus 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +25 -0
- data/README.rdoc +147 -0
- data/Rakefile +70 -0
- data/init.rb +1 -0
- data/lib/schema_plus/active_record/associations.rb +211 -0
- data/lib/schema_plus/active_record/base.rb +81 -0
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +96 -0
- data/lib/schema_plus/active_record/connection_adapters/column.rb +55 -0
- data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +115 -0
- data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +51 -0
- data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +111 -0
- data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +163 -0
- data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +39 -0
- data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +78 -0
- data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +130 -0
- data/lib/schema_plus/active_record/migration.rb +220 -0
- data/lib/schema_plus/active_record/schema.rb +27 -0
- data/lib/schema_plus/active_record/schema_dumper.rb +122 -0
- data/lib/schema_plus/active_record/validations.rb +139 -0
- data/lib/schema_plus/railtie.rb +12 -0
- data/lib/schema_plus/version.rb +3 -0
- data/lib/schema_plus.rb +248 -0
- data/schema_plus.gemspec +37 -0
- data/schema_plus.gemspec.rails3.0 +36 -0
- data/schema_plus.gemspec.rails3.1 +36 -0
- data/spec/association_spec.rb +529 -0
- data/spec/connections/mysql/connection.rb +18 -0
- data/spec/connections/mysql2/connection.rb +18 -0
- data/spec/connections/postgresql/connection.rb +15 -0
- data/spec/connections/sqlite3/connection.rb +14 -0
- data/spec/foreign_key_definition_spec.rb +23 -0
- data/spec/foreign_key_spec.rb +142 -0
- data/spec/index_definition_spec.rb +139 -0
- data/spec/index_spec.rb +71 -0
- data/spec/migration_spec.rb +405 -0
- data/spec/models/comment.rb +2 -0
- data/spec/models/post.rb +2 -0
- data/spec/models/user.rb +2 -0
- data/spec/references_spec.rb +78 -0
- data/spec/schema/auto_schema.rb +23 -0
- data/spec/schema/core_schema.rb +21 -0
- data/spec/schema_dumper_spec.rb +167 -0
- data/spec/schema_spec.rb +71 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/support/extensions/active_model.rb +13 -0
- data/spec/support/helpers.rb +16 -0
- data/spec/support/matchers/automatic_foreign_key_matchers.rb +2 -0
- data/spec/support/matchers/have_index.rb +52 -0
- data/spec/support/matchers/reference.rb +66 -0
- data/spec/support/reference.rb +66 -0
- data/spec/validations_spec.rb +294 -0
- data/spec/views_spec.rb +140 -0
- metadata +269 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module SchemaPlus
|
2
|
+
module ActiveRecord
|
3
|
+
module Schema #:nodoc: all
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def self.extended(base)
|
10
|
+
class << base
|
11
|
+
attr_accessor :defining
|
12
|
+
alias :defining? :defining
|
13
|
+
|
14
|
+
alias_method_chain :define, :schema_plus
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def define_with_schema_plus(info={}, &block)
|
19
|
+
self.defining = true
|
20
|
+
define_without_schema_plus(info, &block)
|
21
|
+
ensure
|
22
|
+
self.defining = false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
|
3
|
+
module SchemaPlus
|
4
|
+
module ActiveRecord
|
5
|
+
|
6
|
+
# SchemaPlus modifies ActiveRecord's schema dumper to include foreign
|
7
|
+
# key constraints and views.
|
8
|
+
#
|
9
|
+
# Additionally, index and foreign key constraint definitions are dumped
|
10
|
+
# inline in the create_table block. (This is done for elegance, but
|
11
|
+
# also because Sqlite3 does not allow foreign key constraints to be
|
12
|
+
# added to a table after it has been defined.)
|
13
|
+
#
|
14
|
+
# The tables and views are dumped in alphabetical order, subject to
|
15
|
+
# topological sort constraints that a table must be dumped before any
|
16
|
+
# view that references it or table that has a foreign key constaint to
|
17
|
+
# it.
|
18
|
+
#
|
19
|
+
module SchemaDumper
|
20
|
+
|
21
|
+
include TSort
|
22
|
+
|
23
|
+
def self.included(base) #:nodoc:
|
24
|
+
base.class_eval do
|
25
|
+
private
|
26
|
+
alias_method_chain :table, :schema_plus
|
27
|
+
alias_method_chain :tables, :schema_plus
|
28
|
+
alias_method_chain :indexes, :schema_plus
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def tables_with_schema_plus(stream) #:nodoc:
|
35
|
+
@table_dumps = {}
|
36
|
+
@re_view_referent = %r{(?:(?i)FROM|JOIN) \S*\b(#{(@connection.tables + @connection.views).join('|')})\b}
|
37
|
+
begin
|
38
|
+
tables_without_schema_plus(nil)
|
39
|
+
|
40
|
+
@connection.views.each do |view_name|
|
41
|
+
definition = @connection.view_definition(view_name)
|
42
|
+
@table_dumps[view_name] = " create_view #{view_name.inspect}, #{definition.inspect}\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
tsort().each do |table|
|
46
|
+
stream.print @table_dumps[table]
|
47
|
+
end
|
48
|
+
|
49
|
+
ensure
|
50
|
+
@table_dumps = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def tsort_each_node(&block) #:nodoc:
|
55
|
+
@table_dumps.keys.sort.each(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def tsort_each_child(table, &block) #:nodoc:
|
59
|
+
references = if @connection.views.include?(table)
|
60
|
+
@connection.view_definition(table).scan(@re_view_referent).flatten
|
61
|
+
else
|
62
|
+
@connection.foreign_keys(table).collect(&:references_table_name)
|
63
|
+
end
|
64
|
+
references.sort.uniq.each(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def table_with_schema_plus(table, ignore) #:nodoc:
|
68
|
+
|
69
|
+
stream = StringIO.new
|
70
|
+
table_without_schema_plus(table, stream)
|
71
|
+
stream.rewind
|
72
|
+
table_dump = stream.read
|
73
|
+
|
74
|
+
if i = (table_dump =~ /^\s*[e]nd\s*$/)
|
75
|
+
stream = StringIO.new
|
76
|
+
dump_indexes(table, stream)
|
77
|
+
dump_foreign_keys(table, stream)
|
78
|
+
stream.rewind
|
79
|
+
table_dump.insert i, stream.read
|
80
|
+
end
|
81
|
+
|
82
|
+
@table_dumps[table] = table_dump
|
83
|
+
end
|
84
|
+
|
85
|
+
def indexes_with_schema_plus(table, stream) #:nodoc:
|
86
|
+
# do nothing. we've already taken care of indexes as part of
|
87
|
+
# dumping the tables
|
88
|
+
end
|
89
|
+
|
90
|
+
def dump_indexes(table, stream) #:nodoc:
|
91
|
+
indexes = @connection.indexes(table)
|
92
|
+
indexes.each do |index|
|
93
|
+
stream.print " t.index"
|
94
|
+
unless index.columns.blank?
|
95
|
+
stream.print " #{index.columns.inspect}, :name => #{index.name.inspect}"
|
96
|
+
stream.print ", :unique => true" if index.unique
|
97
|
+
stream.print ", :kind => \"#{index.kind}\"" unless index.kind.blank?
|
98
|
+
stream.print ", :case_sensitive => false" unless index.case_sensitive?
|
99
|
+
stream.print ", :conditions => #{index.conditions.inspect}" unless index.conditions.blank?
|
100
|
+
index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
|
101
|
+
stream.print ", :length => #{Hash[*index.columns.zip(index.lengths).flatten].inspect}" if index_lengths.present?
|
102
|
+
else
|
103
|
+
stream.print " :name => #{index.name.inspect}"
|
104
|
+
stream.print ", :kind => \"#{index.kind}\"" unless index.kind.blank?
|
105
|
+
stream.print ", :expression => #{index.expression.inspect}"
|
106
|
+
end
|
107
|
+
|
108
|
+
stream.puts
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def dump_foreign_keys(table, stream) #:nodoc:
|
113
|
+
foreign_keys = @connection.foreign_keys(table)
|
114
|
+
foreign_keys.each do |foreign_key|
|
115
|
+
stream.print " "
|
116
|
+
stream.puts foreign_key.to_dump
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module SchemaPlus
|
2
|
+
module ActiveRecord
|
3
|
+
module Validations
|
4
|
+
|
5
|
+
def inherited(klass) #:nodoc:
|
6
|
+
if self == ::ActiveRecord::Base
|
7
|
+
klass.instance_eval do
|
8
|
+
|
9
|
+
# create a callback to load the validations before validation
|
10
|
+
# happens. the callback deletes itself after use (just to
|
11
|
+
# eliminate the callback overhead).
|
12
|
+
before_validation :load_schema_validations
|
13
|
+
private
|
14
|
+
define_method :load_schema_validations do
|
15
|
+
self.class.send :load_schema_validations
|
16
|
+
self.class.skip_callback :validation, :before, :load_schema_validations
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds schema-based validations to model.
|
24
|
+
# Attributes as well as associations are validated.
|
25
|
+
# For instance if there is column
|
26
|
+
#
|
27
|
+
# <code>email NOT NULL</code>
|
28
|
+
#
|
29
|
+
# defined at database-level it will be translated to
|
30
|
+
#
|
31
|
+
# <code>validates_presence_of :email</code>
|
32
|
+
#
|
33
|
+
# If there is an association named <tt>user</tt>
|
34
|
+
# based on <tt>user_id NOT NULL</tt> it will be translated to
|
35
|
+
#
|
36
|
+
# <code>validates_presence_of :user</code>
|
37
|
+
#
|
38
|
+
# Note it uses the name of association (user) not the column name (user_id).
|
39
|
+
# Only <tt>belongs_to</tt> associations are validated.
|
40
|
+
#
|
41
|
+
# This accepts following options:
|
42
|
+
# * :only - auto-validate only given attributes
|
43
|
+
# * :except - auto-validate all but given attributes
|
44
|
+
#
|
45
|
+
def schema_plus(*) #:nodoc:
|
46
|
+
super
|
47
|
+
load_schema_validations
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def load_schema_validations #:nodoc:
|
53
|
+
# Don't bother if: it's already been loaded; the class is abstract; not a base class; or the table doesn't exist
|
54
|
+
return unless create_schema_validations?
|
55
|
+
|
56
|
+
load_column_validations
|
57
|
+
load_association_validations
|
58
|
+
@schema_validations_loaded = true
|
59
|
+
end
|
60
|
+
|
61
|
+
def load_column_validations #:nodoc:
|
62
|
+
content_columns.each do |column|
|
63
|
+
name = column.name.to_sym
|
64
|
+
|
65
|
+
# Data-type validation
|
66
|
+
if column.type == :integer
|
67
|
+
validate_logged :validates_numericality_of, name, :allow_nil => true, :only_integer => true
|
68
|
+
elsif column.number?
|
69
|
+
validate_logged :validates_numericality_of, name, :allow_nil => true
|
70
|
+
elsif column.text? && column.limit
|
71
|
+
validate_logged :validates_length_of, name, :allow_nil => true, :maximum => column.limit
|
72
|
+
end
|
73
|
+
|
74
|
+
# NOT NULL constraints
|
75
|
+
if column.required_on
|
76
|
+
if column.type == :boolean
|
77
|
+
validate_logged :validates_inclusion_of, name, :in => [true, false], :message => :blank
|
78
|
+
else
|
79
|
+
validate_logged :validates_presence_of, name
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# UNIQUE constraints
|
84
|
+
add_uniqueness_validation(column) if column.unique?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def load_association_validations #:nodoc:
|
89
|
+
reflect_on_all_associations(:belongs_to).each do |association|
|
90
|
+
# :primary_key_name was deprecated (noisily) in rails 3.1
|
91
|
+
foreign_key_method = (association.respond_to? :foreign_key) ? :foreign_key : :primary_key_name
|
92
|
+
column = columns_hash[association.send(foreign_key_method).to_s]
|
93
|
+
next unless column
|
94
|
+
|
95
|
+
# NOT NULL constraints
|
96
|
+
validate_logged :validates_presence_of, association.name if column.required_on
|
97
|
+
|
98
|
+
# UNIQUE constraints
|
99
|
+
add_uniqueness_validation(column) if column.unique?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_uniqueness_validation(column) #:nodoc:
|
104
|
+
scope = column.unique_scope.map(&:to_sym)
|
105
|
+
condition = :"#{column.name}_changed?"
|
106
|
+
name = column.name.to_sym
|
107
|
+
validate_logged :validates_uniqueness_of, name, :scope => scope, :allow_nil => true, :if => condition
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_schema_validations? #:nodoc:
|
111
|
+
schema_plus_config.validations.auto_create? && !(@schema_validations_loaded || abstract_class? || name.blank? || !table_exists?)
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_logged(method, arg, opts={}) #:nodoc:
|
115
|
+
if _filter_validation(method, arg)
|
116
|
+
msg = "SchemaPlus validations: #{self.name}.#{method} #{arg.inspect}"
|
117
|
+
msg += ", #{opts.inspect[1...-1]}" if opts.any?
|
118
|
+
logger.info msg
|
119
|
+
send method, arg, opts
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def _filter_validation(macro, name) #:nodoc:
|
124
|
+
config = schema_plus_config.validations
|
125
|
+
types = [macro]
|
126
|
+
if match = macro.to_s.match(/^validates_(.*)_of$/)
|
127
|
+
types << match[1].to_sym
|
128
|
+
end
|
129
|
+
return false if config.only and not Array.wrap(config.only).include?(name)
|
130
|
+
return false if config.except and Array.wrap(config.except).include?(name)
|
131
|
+
return false if config.only_type and not (Array.wrap(config.only_type) & types).any?
|
132
|
+
return false if config.except_type and (Array.wrap(config.except_type) & types).any?
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
data/lib/schema_plus.rb
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'valuable'
|
2
|
+
|
3
|
+
require 'schema_plus/version'
|
4
|
+
require 'schema_plus/active_record/base'
|
5
|
+
require 'schema_plus/active_record/migration'
|
6
|
+
require 'schema_plus/active_record/connection_adapters/table_definition'
|
7
|
+
require 'schema_plus/active_record/connection_adapters/schema_statements'
|
8
|
+
require 'schema_plus/active_record/schema'
|
9
|
+
require 'schema_plus/active_record/schema_dumper'
|
10
|
+
require 'schema_plus/active_record/connection_adapters/abstract_adapter'
|
11
|
+
require 'schema_plus/active_record/connection_adapters/column'
|
12
|
+
require 'schema_plus/active_record/connection_adapters/foreign_key_definition'
|
13
|
+
require 'schema_plus/active_record/connection_adapters/index_definition'
|
14
|
+
require 'schema_plus/active_record/associations'
|
15
|
+
require 'schema_plus/railtie' if defined?(Rails)
|
16
|
+
|
17
|
+
module SchemaPlus
|
18
|
+
module ActiveRecord
|
19
|
+
|
20
|
+
autoload :Validations, 'schema_plus/active_record/validations'
|
21
|
+
|
22
|
+
module ConnectionAdapters
|
23
|
+
autoload :MysqlAdapter, 'schema_plus/active_record/connection_adapters/mysql_adapter'
|
24
|
+
autoload :PostgresqlAdapter, 'schema_plus/active_record/connection_adapters/postgresql_adapter'
|
25
|
+
autoload :Sqlite3Adapter, 'schema_plus/active_record/connection_adapters/sqlite3_adapter'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# This global configuation options for SchmeaPlus.
|
30
|
+
# Set them in +config/initializers/schema_plus.rb+ using:
|
31
|
+
#
|
32
|
+
# SchemaPlus.setup do |config|
|
33
|
+
# ...
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# The options are grouped into subsets based on area of functionality.
|
37
|
+
# See Config::ForeignKeys, Config::Associations, Config::Validations
|
38
|
+
#
|
39
|
+
class Config < Valuable
|
40
|
+
|
41
|
+
# This set of configuration options control SchemaPlus's foreign key
|
42
|
+
# constraint behavior. Set them in
|
43
|
+
# +config/initializers/schema_plus.rb+ using:
|
44
|
+
#
|
45
|
+
# SchemaPlus.setup do |config|
|
46
|
+
# config.foreign_keys.auto_create = ...
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
class ForeignKeys < Valuable
|
50
|
+
##
|
51
|
+
# :attr_accessor: auto_create
|
52
|
+
#
|
53
|
+
# Whether to automatically create foreign key constraints for columns
|
54
|
+
# suffixed with +_id+. Boolean, default is +true+.
|
55
|
+
has_value :auto_create, :klass => :boolean, :default => true
|
56
|
+
|
57
|
+
##
|
58
|
+
# :attr_accessor: auto_index
|
59
|
+
#
|
60
|
+
# Whether to automatically create indexes when creating foreign key constraints for columns.
|
61
|
+
# Boolean, default is +true+.
|
62
|
+
has_value :auto_index, :klass => :boolean, :default => true
|
63
|
+
|
64
|
+
##
|
65
|
+
# :attr_accessor: on_update
|
66
|
+
#
|
67
|
+
# The default value for +:on_update+ when creating foreign key
|
68
|
+
# constraints for columns. Valid values are as described in
|
69
|
+
# ForeignKeyDefinition, or +nil+ to let the database connection use
|
70
|
+
# its own default. Default is +nil+.
|
71
|
+
has_value :on_update
|
72
|
+
|
73
|
+
##
|
74
|
+
# :attr_accessor: on_delete
|
75
|
+
#
|
76
|
+
# The default value for +:on_delete+ when creating foreign key
|
77
|
+
# constraints for columns. Valid values are as described in
|
78
|
+
# ForeignKeyDefinition, or +nil+ to let the database connection use
|
79
|
+
# its own default. Default is +nil+.
|
80
|
+
has_value :on_delete
|
81
|
+
end
|
82
|
+
has_value :foreign_keys, :klass => ForeignKeys, :default => ForeignKeys.new
|
83
|
+
|
84
|
+
# This set of configuration options control SchemaPlus's automatic
|
85
|
+
# association behavior. Set them in
|
86
|
+
# +config/initializers/schema_plus.rb+ using:
|
87
|
+
#
|
88
|
+
# SchemaPlus.setup do |config|
|
89
|
+
# config.associations.auto_create = ...
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
class Associations < Valuable
|
93
|
+
|
94
|
+
##
|
95
|
+
# :attr_accessor: auto_create
|
96
|
+
#
|
97
|
+
# Whether to automatically create associations based on foreign keys.
|
98
|
+
# Boolean, default is +true+.
|
99
|
+
has_value :auto_create, :klass => :boolean, :default => true
|
100
|
+
|
101
|
+
##
|
102
|
+
# :attr_accessor: concise_names
|
103
|
+
#
|
104
|
+
# Whether to use concise naming (strip out common prefixes from class names).
|
105
|
+
# Boolean, default is +true+.
|
106
|
+
has_value :concise_names, :klass => :boolean, :default => true
|
107
|
+
|
108
|
+
##
|
109
|
+
# :attr_accessor: except
|
110
|
+
#
|
111
|
+
# List of association names to exclude from automatic creation.
|
112
|
+
# Value is a single name, an array of names, or +nil+. Default is +nil+.
|
113
|
+
has_value :except, :default => nil
|
114
|
+
|
115
|
+
##
|
116
|
+
# :attr_accessor: only
|
117
|
+
#
|
118
|
+
# List of association names to include in automatic creation.
|
119
|
+
# Value is a single name, and array of names, or +nil+. Default is +nil+.
|
120
|
+
has_value :only, :default => nil
|
121
|
+
|
122
|
+
##
|
123
|
+
# :attr_accessor: except_type
|
124
|
+
#
|
125
|
+
# List of association types to exclude from automatic creation.
|
126
|
+
# Value is one or an array of +:belongs_to+, +:has_many+, +:has_one+, and/or
|
127
|
+
# +:has_and_belongs_to_many+, or +nil+. Default is +nil+.
|
128
|
+
has_value :except_type, :default => nil
|
129
|
+
|
130
|
+
##
|
131
|
+
# :attr_accessor: only_type
|
132
|
+
#
|
133
|
+
# List of association types to include from automatic creation.
|
134
|
+
# Value is one or an array of +:belongs_to+, +:has_many+, +:has_one+, and/or
|
135
|
+
# +:has_and_belongs_to_many+, or +nil+. Default is +nil+.
|
136
|
+
has_value :only_type, :default => nil
|
137
|
+
|
138
|
+
end
|
139
|
+
has_value :associations, :klass => Associations, :default => Associations.new
|
140
|
+
|
141
|
+
# This set of configuration options control SchemaPlus's automatic
|
142
|
+
# validations behavior. Set them in
|
143
|
+
# +config/initializers/schema_plus.rb+ using:
|
144
|
+
#
|
145
|
+
# SchemaPlus.setup do |config|
|
146
|
+
# config.validations.auto_create = ...
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
class Validations < Valuable
|
150
|
+
##
|
151
|
+
# :attr_accessor: auto_create
|
152
|
+
#
|
153
|
+
# Whether to automatically create validations based on database constraints.
|
154
|
+
# Boolean, default is +true+.
|
155
|
+
has_value :auto_create, :klass => :boolean, :default => true
|
156
|
+
|
157
|
+
##
|
158
|
+
# :attr_accessor: only
|
159
|
+
#
|
160
|
+
# List of field names to include in automatic validation.
|
161
|
+
# Value is a single name, and array of names, or +nil+. Default is +nil+.
|
162
|
+
has_value :only, :default => nil
|
163
|
+
|
164
|
+
##
|
165
|
+
# :attr_accessor: except
|
166
|
+
#
|
167
|
+
# List of field names to exclude from automatic validation.
|
168
|
+
# Value is a single name, an array of names, or +nil+. Default is <tt>[:created_at, :updated_at, :created_on, :updated_on]</tt>.
|
169
|
+
has_value :except, :default => [:created_at, :updated_at, :created_on, :updated_on]
|
170
|
+
|
171
|
+
##
|
172
|
+
# :attr_accessor: only_type
|
173
|
+
#
|
174
|
+
# List of validation types to exclude from automatic validation.
|
175
|
+
# Value is a single type, and array of types, or +nil+. Default is +nil+.
|
176
|
+
# A type is specified as, e.g., +:validates_presence_of+ or simply +:presence+.
|
177
|
+
has_value :except_type, :default => nil
|
178
|
+
|
179
|
+
##
|
180
|
+
# :attr_accessor: only_type
|
181
|
+
#
|
182
|
+
# List of validation types to include in automatic validation.
|
183
|
+
# Value is a single type, and array of types, or +nil+. Default is +nil+.
|
184
|
+
# A type is specified as, e.g., +:validates_presence_of+ or simply +:presence+.
|
185
|
+
has_value :only_type, :default => nil
|
186
|
+
|
187
|
+
end
|
188
|
+
has_value :validations, :klass => Validations, :default => Validations.new
|
189
|
+
|
190
|
+
|
191
|
+
def dup #:nodoc:
|
192
|
+
self.class.new(Hash[attributes.collect{ |key, val| [key, Valuable === val ? val.class.new(val.attributes) : val] }])
|
193
|
+
end
|
194
|
+
|
195
|
+
def update_attributes(opts)#:nodoc:
|
196
|
+
opts = opts.dup
|
197
|
+
opts.keys.each { |key| self.send(key).update_attributes(opts.delete(key)) if self.class.attributes.include? key and Hash === opts[key] }
|
198
|
+
super(opts)
|
199
|
+
self
|
200
|
+
end
|
201
|
+
|
202
|
+
def merge(opts)#:nodoc:
|
203
|
+
dup.update_attributes(opts)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns the global configuration, i.e., the singleton instance of Config
|
209
|
+
def self.config
|
210
|
+
@config ||= Config.new
|
211
|
+
end
|
212
|
+
|
213
|
+
# Initialization block is passed a global Config instance that can be
|
214
|
+
# used to configure SchemaPlus behavior. E.g., if you want to disable
|
215
|
+
# automation creation of foreign key constraints for columns name *_id,
|
216
|
+
# put the following in config/initializers/schema_plus.rb :
|
217
|
+
#
|
218
|
+
# SchemaPlus.setup do |config|
|
219
|
+
# config.foreign_keys.auto_create = false
|
220
|
+
# end
|
221
|
+
#
|
222
|
+
def self.setup # :yields: config
|
223
|
+
yield config
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.insert_connection_adapters #:nodoc:
|
227
|
+
return if @inserted_connection_adapters
|
228
|
+
@inserted_connection_adapters = true
|
229
|
+
::ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
230
|
+
::ActiveRecord::ConnectionAdapters::Column.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::Column)
|
231
|
+
::ActiveRecord::ConnectionAdapters::IndexDefinition.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::IndexDefinition)
|
232
|
+
# (mysql2 v0.2.7 uses its own IndexDefinition, which is compatible with the monkey patches; so if that constant exists, include the patches
|
233
|
+
::ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::IndexDefinition) if defined? ::ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition
|
234
|
+
::ActiveRecord::ConnectionAdapters::SchemaStatements.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::SchemaStatements)
|
235
|
+
::ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, SchemaPlus::ActiveRecord::ConnectionAdapters::TableDefinition)
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.insert #:nodoc:
|
239
|
+
return if @inserted
|
240
|
+
@inserted = true
|
241
|
+
insert_connection_adapters
|
242
|
+
::ActiveRecord::Base.send(:include, SchemaPlus::ActiveRecord::Base)
|
243
|
+
::ActiveRecord::Migration.send(:include, SchemaPlus::ActiveRecord::Migration)
|
244
|
+
::ActiveRecord::Schema.send(:include, SchemaPlus::ActiveRecord::Schema)
|
245
|
+
::ActiveRecord::SchemaDumper.send(:include, SchemaPlus::ActiveRecord::SchemaDumper)
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|