schema_plus 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +28 -3
- data/gemfiles/Gemfile.rails-2.3.lock +1 -1
- data/gemfiles/Gemfile.rails-3.0.lock +1 -1
- data/gemfiles/Gemfile.rails-3.1.lock +2 -2
- data/lib/rails/tasks/database.rake +5 -0
- data/lib/schema_plus.rb +1 -0
- data/lib/schema_plus/active_record/column_options_handler.rb +55 -0
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +4 -1
- data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +3 -7
- data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +22 -17
- data/lib/schema_plus/active_record/foreign_keys.rb +4 -54
- data/lib/schema_plus/active_record/schema.rb +8 -7
- data/lib/schema_plus/active_record/schema_dumper.rb +65 -53
- data/lib/schema_plus/railtie.rb +8 -1
- data/lib/schema_plus/version.rb +1 -1
- data/runspecs +2 -2
- data/schema_plus.gemspec +1 -1
- data/spec/connection_spec.rb +10 -0
- data/spec/migration_spec.rb +5 -0
- data/spec/schema_dumper_spec.rb +84 -49
- data/spec/views_spec.rb +2 -0
- metadata +135 -109
- data/spec/references_spec.rb +0 -78
data/README.rdoc
CHANGED
@@ -45,7 +45,7 @@ With standard rails migrations, you specify indexes separately from the table de
|
|
45
45
|
add_index :parts, :name # index repeats table and column names and is defined separately
|
46
46
|
add_index :parts, :product_code, :unique => true
|
47
47
|
|
48
|
-
But with SchemaPlus
|
48
|
+
But with SchemaPlus you can specify your indexes when you define each column:
|
49
49
|
|
50
50
|
# More DRY way...
|
51
51
|
create_table :parts do |t|
|
@@ -103,9 +103,9 @@ the same arguments.
|
|
103
103
|
|
104
104
|
The foreign key behavior can be configured globally (see Config) or per-table (see create_table).
|
105
105
|
|
106
|
-
To examine your foreign key constraints, connection.foreign_keys returns a
|
106
|
+
To examine your foreign key constraints, <tt>connection.foreign_keys</tt> returns a
|
107
107
|
list of foreign key constraints defined for a given table, and
|
108
|
-
connection.reverse_foreign_keys returns a list of foreign key constraints
|
108
|
+
<tt>connection.reverse_foreign_keys</tt> returns a list of foreign key constraints
|
109
109
|
that reference a given table. See SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.
|
110
110
|
|
111
111
|
=== Views
|
@@ -120,6 +120,31 @@ ActiveRecord works with views the same as with ordinary tables. That is, for th
|
|
120
120
|
class UncommentedPosts < ActiveRecord::Base
|
121
121
|
end
|
122
122
|
|
123
|
+
=== Schema Dump and Load (schema.rb)
|
124
|
+
|
125
|
+
When dumping <tt>schema.rb</tt>, SchemaPlus orders the views and tables in
|
126
|
+
the schema dump alphabetically, but subject to the requirement that each
|
127
|
+
table or view be defined before those that depend on it. This allows all
|
128
|
+
foreign key constraints to be defined within the scope of the table
|
129
|
+
definition. (Unless there are cyclical dependencies, in which case some
|
130
|
+
foreign keys constraints must be defined later.)
|
131
|
+
|
132
|
+
Also, when dumping <tt>schema.rb</tt>, SchemaPlus dumps explicit foreign key
|
133
|
+
definition statements rather than relying on the auto-creation behavior,
|
134
|
+
for maximum clarity and for independence from global config. And
|
135
|
+
correspondingly, when loading a schema, i.e. with the context of
|
136
|
+
<tt>ActiveRecord::Schema.define</tt>, SchemaPlus ensures that auto creation of
|
137
|
+
foreign keys is turned off regardless of the global setting. But if for
|
138
|
+
some reason you are creating your schema.rb file by hand, and would like to
|
139
|
+
take advantage of auto-creation of foreign keys, you can re-enable it:
|
140
|
+
|
141
|
+
ActiveRecord::Schema.define do
|
142
|
+
SchemaPlus.config.foreign_keys.auto_create = true
|
143
|
+
SchemaPlus.config.foreign_keys.auto_index = true
|
144
|
+
|
145
|
+
create_table ...etc...
|
146
|
+
end
|
147
|
+
|
123
148
|
|
124
149
|
== History
|
125
150
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: /home/snatcher/projects/schema_plus
|
3
3
|
specs:
|
4
|
-
schema_plus (0.
|
4
|
+
schema_plus (0.2.0)
|
5
5
|
rails
|
6
6
|
valuable
|
7
7
|
|
@@ -97,7 +97,7 @@ GEM
|
|
97
97
|
sprockets (2.0.0)
|
98
98
|
hike (~> 1.2)
|
99
99
|
rack (~> 1.0)
|
100
|
-
tilt (
|
100
|
+
tilt (~> 1.1, != 1.3.0)
|
101
101
|
sqlite3 (1.3.4)
|
102
102
|
thor (0.14.6)
|
103
103
|
tilt (1.3.3)
|
data/lib/schema_plus.rb
CHANGED
@@ -2,6 +2,7 @@ require 'valuable'
|
|
2
2
|
|
3
3
|
require 'schema_plus/version'
|
4
4
|
require 'schema_plus/active_record/base'
|
5
|
+
require 'schema_plus/active_record/column_options_handler'
|
5
6
|
require 'schema_plus/active_record/foreign_keys'
|
6
7
|
require 'schema_plus/active_record/connection_adapters/table_definition'
|
7
8
|
require 'schema_plus/active_record/connection_adapters/schema_statements'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SchemaPlus::ActiveRecord
|
2
|
+
module ColumnOptionsHandler
|
3
|
+
def schema_plus_handle_column_options(table_name, column_name, column_options, opts = {}) #:nodoc:
|
4
|
+
config = opts[:config] || SchemaPlus.config
|
5
|
+
if references = get_references(table_name, column_name, column_options, config)
|
6
|
+
if index = column_options.fetch(:index, config.foreign_keys.auto_index?)
|
7
|
+
column_index(table_name, column_name, index)
|
8
|
+
end
|
9
|
+
add_foreign_key(table_name, column_name, references.first, references.last,
|
10
|
+
column_options.reverse_merge(:on_update => config.foreign_keys.on_update,
|
11
|
+
:on_delete => config.foreign_keys.on_delete))
|
12
|
+
elsif column_options[:index]
|
13
|
+
column_index(table_name, column_name, column_options[:index])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
# If auto_create is true:
|
20
|
+
# get_references('comments', 'post_id') # => ['posts', 'id']
|
21
|
+
#
|
22
|
+
# And if <tt>column_name</tt> is parent_id it references to the same table
|
23
|
+
# get_references('pages', 'parent_id') # => ['pages', 'id']
|
24
|
+
#
|
25
|
+
# If :references option is given, it is used (whether or not auto_create is true)
|
26
|
+
# get_references('widgets', 'main_page_id', :references => 'pages')) => ['pages', 'id']
|
27
|
+
#
|
28
|
+
# Also the referenced id column may be specified:
|
29
|
+
# get_references('addresses', 'member_id', :references => ['users', 'uuid']) => ['users', 'uuid']
|
30
|
+
#
|
31
|
+
def get_references(table_name, column_name, column_options = {}, config = {}) #:nodoc:
|
32
|
+
if column_options.has_key?(:references)
|
33
|
+
references = column_options[:references]
|
34
|
+
references = [references, :id] unless references.nil? || references.is_a?(Array)
|
35
|
+
references
|
36
|
+
elsif config.foreign_keys.auto_create?
|
37
|
+
case column_name.to_s
|
38
|
+
when 'parent_id'
|
39
|
+
[table_name, :id]
|
40
|
+
when /^(.*)_id$/
|
41
|
+
determined_table_name = ActiveRecord::Base.pluralize_table_names ? $1.to_s.pluralize : $1
|
42
|
+
[determined_table_name, :id]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def column_index(table_name, column_name, options) #:nodoc:
|
48
|
+
options = {} if options == true
|
49
|
+
options = { :unique => true } if options == :unique
|
50
|
+
column_name = [column_name] + Array.wrap(options.delete(:with)).compact
|
51
|
+
add_index(table_name, column_name, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -39,7 +39,10 @@ module SchemaPlus
|
|
39
39
|
# compatible with the monkey patches; but the definition only
|
40
40
|
# appears once the adapter is loaded. so wait til now to check
|
41
41
|
# if that constant exists, then include the patches
|
42
|
-
::ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition
|
42
|
+
if mysql2index = ::ActiveRecord::ConnectionAdapters::Mysql2IndexDefinition rescue nil # rescues NameError
|
43
|
+
monkeypatch = SchemaPlus::ActiveRecord::ConnectionAdapters::IndexDefinition
|
44
|
+
mysql2index.send(:include, monkeypatch) unless mysql2index.include? monkeypatch
|
45
|
+
end
|
43
46
|
end
|
44
47
|
extend(SchemaPlus::ActiveRecord::ForeignKeys)
|
45
48
|
end
|
@@ -61,19 +61,15 @@ module SchemaPlus
|
|
61
61
|
end
|
62
62
|
|
63
63
|
# Dumps a definition of foreign key.
|
64
|
-
|
65
|
-
|
66
|
-
# It was introduced to satisfy sqlite which requires foreign key definitions
|
67
|
-
# to be declared when creating a table. That approach is fine for MySQL and
|
68
|
-
# PostgreSQL too.
|
69
|
-
def to_dump
|
70
|
-
dump = " t.foreign_key"
|
64
|
+
def to_dump(opts={})
|
65
|
+
dump = (opts[:inline] ? " t.foreign_key" : "add_foreign_key #{table_name.inspect},")
|
71
66
|
dump << " [#{Array(column_names).collect{ |name| name.inspect }.join(', ')}]"
|
72
67
|
dump << ", #{references_table_name.inspect}, [#{Array(references_column_names).collect{ |name| name.inspect }.join(', ')}]"
|
73
68
|
dump << ", :on_update => :#{on_update}" if on_update
|
74
69
|
dump << ", :on_delete => :#{on_delete}" if on_delete
|
75
70
|
dump << ", :deferrable => #{deferrable}" if deferrable
|
76
71
|
dump << ", :name => #{name.inspect}" if name
|
72
|
+
dump << "\n"
|
77
73
|
dump
|
78
74
|
end
|
79
75
|
|
@@ -52,6 +52,10 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
52
52
|
# <tt>_id</tt>. So the above examples are redundant, unless automatic
|
53
53
|
# creation was disabled at initialization in the global Config.
|
54
54
|
#
|
55
|
+
# SchemaPlus likewise by default automatically creates foreign key constraints for
|
56
|
+
# columns defined via <tt>t.references</tt>, unless the
|
57
|
+
# <tt>:polymorphic</tt> option is true
|
58
|
+
#
|
55
59
|
# Finally, the configuration for foreign keys can be overriden on a per-table
|
56
60
|
# basis by passing Config options to Migration::ClassMethods#create_table, such as
|
57
61
|
#
|
@@ -60,6 +64,7 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
60
64
|
# end
|
61
65
|
#
|
62
66
|
module TableDefinition
|
67
|
+
include SchemaPlus::ActiveRecord::ColumnOptionsHandler
|
63
68
|
|
64
69
|
attr_accessor :schema_plus_config #:nodoc:
|
65
70
|
|
@@ -69,6 +74,7 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
69
74
|
attr_accessor :indexes
|
70
75
|
alias_method_chain :initialize, :schema_plus
|
71
76
|
alias_method_chain :column, :schema_plus
|
77
|
+
alias_method_chain :references, :schema_plus
|
72
78
|
alias_method_chain :primary_key, :schema_plus
|
73
79
|
alias_method_chain :to_sql, :schema_plus
|
74
80
|
end
|
@@ -84,18 +90,16 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
84
90
|
column(name, :primary_key, options)
|
85
91
|
end
|
86
92
|
|
93
|
+
def references_with_schema_plus(*args)
|
94
|
+
options = args.extract_options!
|
95
|
+
options[:references] = nil if options[:polymorphic]
|
96
|
+
args << options
|
97
|
+
references_without_schema_plus(*args)
|
98
|
+
end
|
99
|
+
|
87
100
|
def column_with_schema_plus(name, type, options = {}) #:nodoc:
|
88
101
|
column_without_schema_plus(name, type, options)
|
89
|
-
|
90
|
-
if index = options.fetch(:index, fk_use_auto_index?)
|
91
|
-
self.column_index(name, index)
|
92
|
-
end
|
93
|
-
foreign_key(name, references.first, references.last,
|
94
|
-
options.reverse_merge(:on_update => schema_plus_config.foreign_keys.on_update,
|
95
|
-
:on_delete => schema_plus_config.foreign_keys.on_delete))
|
96
|
-
elsif options[:index]
|
97
|
-
self.column_index(name, options[:index])
|
98
|
-
end
|
102
|
+
schema_plus_handle_column_options(self.name, name, options, :config => schema_plus_config)
|
99
103
|
self
|
100
104
|
end
|
101
105
|
|
@@ -116,15 +120,16 @@ module SchemaPlus::ActiveRecord::ConnectionAdapters
|
|
116
120
|
end
|
117
121
|
|
118
122
|
protected
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
self.index(name, options)
|
123
|
+
# The only purpose of that method is to provide a consistent intefrace
|
124
|
+
# for ColumnOptionsHandler. First argument (table name) is ignored.
|
125
|
+
def add_index(_, *args) #:nodoc:
|
126
|
+
index(*args)
|
124
127
|
end
|
125
128
|
|
126
|
-
|
127
|
-
|
129
|
+
# The only purpose of that method is to provide a consistent intefrace
|
130
|
+
# for ColumnOptionsHandler. First argument (table name) is ignored.
|
131
|
+
def add_foreign_key(_, *args) #:nodoc:
|
132
|
+
foreign_key(*args)
|
128
133
|
end
|
129
134
|
|
130
135
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module SchemaPlus::ActiveRecord
|
2
2
|
module ForeignKeys
|
3
|
+
include SchemaPlus::ActiveRecord::ColumnOptionsHandler
|
4
|
+
|
3
5
|
# Enhances ActiveRecord::ConnectionAdapters::AbstractAdapter#add_column to support indexes and foreign keys, with automatic creation
|
4
6
|
#
|
5
7
|
# == Indexes
|
@@ -104,69 +106,17 @@ module SchemaPlus::ActiveRecord
|
|
104
106
|
#
|
105
107
|
def add_column(table_name, column_name, type, options = {})
|
106
108
|
super
|
107
|
-
|
109
|
+
schema_plus_handle_column_options(table_name, column_name, options)
|
108
110
|
end
|
109
111
|
|
110
112
|
# Enhances ActiveRecord::Migration#change_column to support indexes and foreign keys same as add_column.
|
111
113
|
def change_column(table_name, column_name, type, options = {})
|
112
114
|
super
|
113
115
|
remove_foreign_key_if_exists(table_name, column_name)
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
# Determines referenced table and column.
|
118
|
-
# Used in migrations.
|
119
|
-
#
|
120
|
-
# If auto_create is true:
|
121
|
-
# get_references('comments', 'post_id') # => ['posts', 'id']
|
122
|
-
#
|
123
|
-
# And if <tt>column_name</tt> is parent_id it references to the same table
|
124
|
-
# get_references('pages', 'parent_id') # => ['pages', 'id']
|
125
|
-
#
|
126
|
-
# If :references option is given, it is used (whether or not auto_create is true)
|
127
|
-
# get_references('widgets', 'main_page_id', :references => 'pages'))
|
128
|
-
# # => ['pages', 'id']
|
129
|
-
#
|
130
|
-
# Also the referenced id column may be specified:
|
131
|
-
# get_references('addresses', 'member_id', :references => ['users', 'uuid'])
|
132
|
-
# # => ['users', 'uuid']
|
133
|
-
def get_references(table_name, column_name, options = {}, config=nil) #:nodoc:
|
134
|
-
column_name = column_name.to_s
|
135
|
-
if options.has_key?(:references)
|
136
|
-
references = options[:references]
|
137
|
-
references = [references, :id] unless references.nil? || references.is_a?(Array)
|
138
|
-
references
|
139
|
-
elsif (config || SchemaPlus.config).foreign_keys.auto_create? && !ActiveRecord::Schema.defining?
|
140
|
-
if column_name == 'parent_id'
|
141
|
-
[table_name, :id]
|
142
|
-
elsif column_name =~ /^(.*)_id$/
|
143
|
-
determined_table_name = ActiveRecord::Base.pluralize_table_names ? $1.to_s.pluralize : $1
|
144
|
-
[determined_table_name, :id]
|
145
|
-
end
|
146
|
-
end
|
116
|
+
schema_plus_handle_column_options(table_name, column_name, options)
|
147
117
|
end
|
148
118
|
|
149
119
|
protected
|
150
|
-
def handle_column_options(table_name, column_name, options) #:nodoc:
|
151
|
-
if references = get_references(table_name, column_name, options)
|
152
|
-
if index = options.fetch(:index, SchemaPlus.config.foreign_keys.auto_index? && !ActiveRecord::Schema.defining?)
|
153
|
-
column_index(table_name, column_name, index)
|
154
|
-
end
|
155
|
-
add_foreign_key(table_name, column_name, references.first, references.last,
|
156
|
-
options.reverse_merge(:on_update => SchemaPlus.config.foreign_keys.on_update,
|
157
|
-
:on_delete => SchemaPlus.config.foreign_keys.on_delete))
|
158
|
-
elsif options[:index]
|
159
|
-
column_index(table_name, column_name, options[:index])
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def column_index(table_name, column_name, options) #:nodoc:
|
164
|
-
options = {} if options == true
|
165
|
-
options = { :unique => true } if options == :unique
|
166
|
-
column_name = [column_name] + Array.wrap(options.delete(:with)).compact
|
167
|
-
add_index(table_name, column_name, options)
|
168
|
-
end
|
169
|
-
|
170
120
|
def remove_foreign_key_if_exists(table_name, column_name) #:nodoc:
|
171
121
|
foreign_keys = ActiveRecord::Base.connection.foreign_keys(table_name.to_s)
|
172
122
|
fk = foreign_keys.detect { |fk| fk.table_name == table_name.to_s && fk.column_names == Array(column_name).collect(&:to_s) }
|
@@ -8,18 +8,19 @@ module SchemaPlus
|
|
8
8
|
module ClassMethods
|
9
9
|
def self.extended(base)
|
10
10
|
class << base
|
11
|
-
attr_accessor :defining
|
12
|
-
alias :defining? :defining
|
13
|
-
|
14
11
|
alias_method_chain :define, :schema_plus
|
15
12
|
end
|
16
13
|
end
|
17
14
|
|
18
15
|
def define_with_schema_plus(info={}, &block)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
fk_override = { :auto_create => false, :auto_index => false }
|
17
|
+
save = Hash[fk_override.keys.collect{|key| [key, SchemaPlus.config.foreign_keys.send(key)]}]
|
18
|
+
begin
|
19
|
+
SchemaPlus.config.foreign_keys.update_attributes(fk_override)
|
20
|
+
define_without_schema_plus(info, &block)
|
21
|
+
ensure
|
22
|
+
SchemaPlus.config.foreign_keys.update_attributes(save)
|
23
|
+
end
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -31,24 +31,60 @@ module SchemaPlus
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
def break_fk_cycles #:nodoc:
|
35
|
+
strongly_connected_components.select{|component| component.size > 1}.each do |tables|
|
36
|
+
table = tables.sort.first
|
37
|
+
backref_fks = @inline_fks[table].select{|fk| tables.include?(fk.references_table_name)}
|
38
|
+
@inline_fks[table] -= backref_fks
|
39
|
+
@dump_dependencies[table] -= backref_fks.collect(&:references_table_name)
|
40
|
+
backref_fks.each do |fk|
|
41
|
+
@backref_fks[fk.references_table_name] << fk
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
34
46
|
def tables_with_schema_plus(stream) #:nodoc:
|
35
47
|
@table_dumps = {}
|
36
|
-
@
|
37
|
-
|
38
|
-
|
48
|
+
@inline_fks = Hash.new{ |h, k| h[k] = [] }
|
49
|
+
@backref_fks = Hash.new{ |h, k| h[k] = [] }
|
50
|
+
@dump_dependencies = {}
|
39
51
|
|
40
|
-
|
41
|
-
definition = @connection.view_definition(view_name)
|
42
|
-
@table_dumps[view_name] = " create_view #{view_name.inspect}, #{definition.inspect}\n"
|
43
|
-
end
|
52
|
+
tables_without_schema_plus(nil)
|
44
53
|
|
45
|
-
|
46
|
-
|
54
|
+
@connection.views.each do |view_name|
|
55
|
+
definition = @connection.view_definition(view_name)
|
56
|
+
@table_dumps[view_name] = " create_view #{view_name.inspect}, #{definition.inspect}\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
re_view_referent = %r{(?:(?i)FROM|JOIN) \S*\b(#{(@table_dumps.keys).join('|')})\b}
|
60
|
+
@table_dumps.keys.each do |table|
|
61
|
+
if @connection.views.include?(table)
|
62
|
+
dependencies = @connection.view_definition(table).scan(re_view_referent).flatten
|
63
|
+
else
|
64
|
+
@inline_fks[table] = @connection.foreign_keys(table)
|
65
|
+
dependencies = @inline_fks[table].collect(&:references_table_name)
|
47
66
|
end
|
67
|
+
@dump_dependencies[table] = dependencies.sort.uniq - ::ActiveRecord::SchemaDumper.ignore_tables
|
68
|
+
end
|
48
69
|
|
49
|
-
|
50
|
-
|
70
|
+
# Normally we dump foreign key constraints inline in the table
|
71
|
+
# definitions, both for visual cleanliness and because sqlite3
|
72
|
+
# doesn't allow foreign key constraints to be added afterwards.
|
73
|
+
# But in case there's a cycle in the constraint references, some
|
74
|
+
# constraints will need to be broken out then added later. (Adding
|
75
|
+
# constraints later won't work with sqlite3, but that means sqlite3
|
76
|
+
# won't let you create cycles in the first place.)
|
77
|
+
break_fk_cycles while strongly_connected_components.any?{|component| component.size > 1}
|
78
|
+
|
79
|
+
tsort().each do |table|
|
80
|
+
table_dump = @table_dumps[table]
|
81
|
+
if i = (table_dump =~ /^\s*[e]nd\s*$/)
|
82
|
+
table_dump.insert i, dump_indexes(table) + dump_foreign_keys(@inline_fks[table], :inline => true)
|
83
|
+
end
|
84
|
+
stream.print table_dump
|
85
|
+
stream.puts dump_foreign_keys(@backref_fks[table], :inline => false)+"\n" if @backref_fks[table].any?
|
51
86
|
end
|
87
|
+
|
52
88
|
end
|
53
89
|
|
54
90
|
def tsort_each_node(&block) #:nodoc:
|
@@ -56,30 +92,13 @@ module SchemaPlus
|
|
56
92
|
end
|
57
93
|
|
58
94
|
def tsort_each_child(table, &block) #:nodoc:
|
59
|
-
|
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)
|
95
|
+
@dump_dependencies[table].each(&block)
|
65
96
|
end
|
66
97
|
|
67
98
|
def table_with_schema_plus(table, ignore) #:nodoc:
|
68
|
-
|
69
99
|
stream = StringIO.new
|
70
100
|
table_without_schema_plus(table, stream)
|
71
|
-
stream.
|
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
|
101
|
+
@table_dumps[table] = stream.string
|
83
102
|
end
|
84
103
|
|
85
104
|
def indexes_with_schema_plus(table, stream) #:nodoc:
|
@@ -87,36 +106,29 @@ module SchemaPlus
|
|
87
106
|
# dumping the tables
|
88
107
|
end
|
89
108
|
|
90
|
-
def dump_indexes(table
|
91
|
-
|
92
|
-
|
93
|
-
stream.print " t.index"
|
109
|
+
def dump_indexes(table) #:nodoc:
|
110
|
+
@connection.indexes(table).collect{ |index|
|
111
|
+
dump = " t.index"
|
94
112
|
unless index.columns.blank?
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
113
|
+
dump << " #{index.columns.inspect}, :name => #{index.name.inspect}"
|
114
|
+
dump << ", :unique => true" if index.unique
|
115
|
+
dump << ", :kind => \"#{index.kind}\"" unless index.kind.blank?
|
116
|
+
dump << ", :case_sensitive => false" unless index.case_sensitive?
|
117
|
+
dump << ", :conditions => #{index.conditions.inspect}" unless index.conditions.blank?
|
100
118
|
index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
|
101
|
-
|
119
|
+
dump << ", :length => #{Hash[*index.columns.zip(index.lengths).flatten].inspect}" if index_lengths.present?
|
102
120
|
else
|
103
|
-
|
104
|
-
|
105
|
-
|
121
|
+
dump << " :name => #{index.name.inspect}"
|
122
|
+
dump << ", :kind => \"#{index.kind}\"" unless index.kind.blank?
|
123
|
+
dump << ", :expression => #{index.expression.inspect}"
|
106
124
|
end
|
107
|
-
|
108
|
-
|
109
|
-
end
|
125
|
+
dump << "\n"
|
126
|
+
}.join
|
110
127
|
end
|
111
128
|
|
112
|
-
def dump_foreign_keys(
|
113
|
-
foreign_keys
|
114
|
-
foreign_keys.each do |foreign_key|
|
115
|
-
stream.print " "
|
116
|
-
stream.puts foreign_key.to_dump
|
117
|
-
end
|
129
|
+
def dump_foreign_keys(foreign_keys, opts={}) #:nodoc:
|
130
|
+
foreign_keys.collect{ |foreign_key| " " + foreign_key.to_dump(:inline => opts[:inline]) }.join
|
118
131
|
end
|
119
|
-
|
120
132
|
end
|
121
133
|
end
|
122
134
|
end
|
data/lib/schema_plus/railtie.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module SchemaPlus
|
1
|
+
module SchemaPlus
|
2
2
|
class Railtie < Rails::Railtie #:nodoc:
|
3
3
|
|
4
4
|
initializer 'schema_plus.insert', :after => "active_record.initialize_database" do
|
@@ -7,5 +7,12 @@ module SchemaPlus
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
rake_tasks do
|
11
|
+
load 'rails/tasks/database.rake'
|
12
|
+
if task = Rake.application.tasks.find { |task| task.name == 'db:schema:dump' }
|
13
|
+
task.enhance(["schema_plus:load"])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
10
17
|
end
|
11
18
|
end
|
data/lib/schema_plus/version.rb
CHANGED
data/runspecs
CHANGED
@@ -8,7 +8,7 @@ RAILS_VERSIONS = %W[2.3 3.0 3.1]
|
|
8
8
|
DB_ADAPTERS = %W[postgresql mysql mysql2 sqlite3]
|
9
9
|
|
10
10
|
o = OpenStruct.new
|
11
|
-
o.ruby_versions =
|
11
|
+
o.ruby_versions = RUBY_VERSIONS
|
12
12
|
o.rails_versions = RAILS_VERSIONS
|
13
13
|
o.db_adapters = DB_ADAPTERS - ["mysql"]
|
14
14
|
|
@@ -71,7 +71,7 @@ o.ruby_versions.each do |ruby|
|
|
71
71
|
puts "\n\n*** ruby version #{ruby} - rails version #{rails} - db adapters: #{o.db_adapters.join(' ')} [#{n} of #{total}]\n\n"
|
72
72
|
gemfile = File.join(GEMFILES_DIR, "Gemfile.rails-#{rails}")
|
73
73
|
n += 1
|
74
|
-
command = %Q{BUNDLE_GEMFILE="#{gemfile}" rvm #{ruby}
|
74
|
+
command = %Q{BUNDLE_GEMFILE="#{gemfile}" rvm #{ruby} do #{cmd}}
|
75
75
|
puts command
|
76
76
|
next if o.dry_run
|
77
77
|
system(command) or errs << "ruby #{ruby}, rails #{rails}"
|
data/schema_plus.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "schema_plus"
|
7
7
|
s.version = SchemaPlus::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["Ronen Barzel", "
|
9
|
+
s.authors = ["Ronen Barzel", "Michal Lomnicki"]
|
10
10
|
s.email = ["ronen@barzel.org", "michal.lomnicki@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/lomba/schema_plus"
|
12
12
|
s.summary = "Enhances ActiveRecord schema mechanism, including more DRY index creation and support for foreign key constraints and views."
|
data/spec/migration_spec.rb
CHANGED
@@ -38,6 +38,11 @@ describe ActiveRecord::Migration do
|
|
38
38
|
@model.should_not reference(:users, :id).on(:user_id)
|
39
39
|
end
|
40
40
|
|
41
|
+
it "should not create foreign key using t.references with :polymorphic => true" do
|
42
|
+
create_table(@model, :user => {:METHOD => :references, :polymorphic => true})
|
43
|
+
@model.should_not reference(:users, :id).on(:user_id)
|
44
|
+
end
|
45
|
+
|
41
46
|
it "should create foreign key to the same table on parent_id" do
|
42
47
|
create_table(@model, :parent_id => {})
|
43
48
|
@model.should reference(@model.table_name, :id).on(:parent_id)
|
data/spec/schema_dumper_spec.rb
CHANGED
@@ -1,48 +1,94 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
describe "Schema dump (core)" do
|
4
|
+
describe "Schema dump" do
|
7
5
|
|
8
6
|
before(:all) do
|
9
|
-
|
7
|
+
SchemaPlus.setup do |config|
|
8
|
+
config.foreign_keys.auto_create = false
|
9
|
+
end
|
10
|
+
ActiveRecord::Migration.suppress_messages do
|
11
|
+
ActiveRecord::Schema.define do
|
12
|
+
connection.tables.each do |table| drop_table table end
|
13
|
+
|
14
|
+
create_table :users, :force => true do |t|
|
15
|
+
t.string :login
|
16
|
+
t.datetime :deleted_at
|
17
|
+
t.integer :first_post_id
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :posts, :force => true do |t|
|
21
|
+
t.text :body
|
22
|
+
t.integer :user_id
|
23
|
+
t.integer :first_comment_id
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table :comments, :force => true do |t|
|
27
|
+
t.text :body
|
28
|
+
t.integer :post_id
|
29
|
+
t.integer :commenter_id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
class ::User < ActiveRecord::Base ; end
|
34
|
+
class ::Post < ActiveRecord::Base ; end
|
35
|
+
class ::Comment < ActiveRecord::Base ; end
|
10
36
|
end
|
11
37
|
|
12
|
-
let(:
|
38
|
+
let(:dump_posts) do
|
13
39
|
stream = StringIO.new
|
14
40
|
ActiveRecord::SchemaDumper.ignore_tables = %w[users comments]
|
15
41
|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
16
42
|
stream.string
|
17
43
|
end
|
18
44
|
|
45
|
+
let(:dump_all) do
|
46
|
+
stream = StringIO.new
|
47
|
+
ActiveRecord::SchemaDumper.ignore_tables = []
|
48
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
49
|
+
stream.string
|
50
|
+
end
|
51
|
+
|
19
52
|
it "should include foreign_key definition" do
|
20
53
|
with_foreign_key Post, :user_id, :users, :id do
|
21
|
-
|
54
|
+
dump_posts.should match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"]}))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with constraint dependencies" do
|
59
|
+
it "should sort in Posts => Comments direction" do
|
60
|
+
with_foreign_key Comment, :post_id, :posts, :id do
|
61
|
+
dump_all.should match(%r{create_table "posts".*create_table "comments"}m)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
it "should sort in Comments => Posts direction" do
|
65
|
+
with_foreign_key Post, :first_comment_id, :comments, :id do
|
66
|
+
dump_all.should match(%r{create_table "comments".*create_table "posts"}m)
|
67
|
+
end
|
22
68
|
end
|
23
69
|
end
|
24
70
|
|
25
71
|
it "should include foreign_key options" do
|
26
72
|
with_foreign_key Post, :user_id, :users, :id, :on_update => :cascade, :on_delete => :set_null do
|
27
|
-
|
73
|
+
dump_posts.should match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"], :on_update => :cascade, :on_delete => :set_null}))
|
28
74
|
end
|
29
75
|
end
|
30
76
|
|
31
77
|
it "should include index definition" do
|
32
78
|
with_index Post, :user_id do
|
33
|
-
|
79
|
+
dump_posts.should match(to_regexp(%q{t.index ["user_id"]}))
|
34
80
|
end
|
35
81
|
end
|
36
82
|
|
37
83
|
it "should include index name" do
|
38
84
|
with_index Post, :user_id, :name => "posts_user_id_index" do
|
39
|
-
|
85
|
+
dump_posts.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index"}))
|
40
86
|
end
|
41
87
|
end
|
42
88
|
|
43
89
|
it "should define unique index" do
|
44
90
|
with_index Post, :user_id, :name => "posts_user_id_index", :unique => true do
|
45
|
-
|
91
|
+
dump_posts.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :unique => true}))
|
46
92
|
end
|
47
93
|
end
|
48
94
|
|
@@ -50,30 +96,54 @@ describe "Schema dump (core)" do
|
|
50
96
|
|
51
97
|
it "should define case insensitive index" do
|
52
98
|
with_index Post, :name => "posts_user_body_index", :expression => "USING btree (LOWER(body))" do
|
53
|
-
|
99
|
+
dump_posts.should match(to_regexp(%q{t.index ["body"], :name => "posts_user_body_index", :case_sensitive => false}))
|
54
100
|
end
|
55
101
|
end
|
56
102
|
|
57
103
|
it "should define conditions" do
|
58
104
|
with_index Post, :user_id, :name => "posts_user_id_index", :conditions => "user_id IS NOT NULL" do
|
59
|
-
|
105
|
+
dump_posts.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :conditions => "(user_id IS NOT NULL)"}))
|
60
106
|
end
|
61
107
|
end
|
62
108
|
|
63
109
|
it "should define expression" do
|
64
110
|
with_index Post, :name => "posts_freaky_index", :expression => "USING hash (least(id, user_id))" do
|
65
|
-
|
111
|
+
dump_posts.should match(to_regexp(%q{t.index :name => "posts_freaky_index", :kind => "hash", :expression => "LEAST(id, user_id)"}))
|
66
112
|
end
|
67
113
|
end
|
68
114
|
|
69
115
|
it "should define kind" do
|
70
116
|
with_index Post, :name => "posts_body_index", :expression => "USING hash (body)" do
|
71
|
-
|
117
|
+
dump_posts.should match(to_regexp(%q{t.index ["body"], :name => "posts_body_index", :kind => "hash"}))
|
72
118
|
end
|
73
119
|
end
|
74
120
|
|
75
121
|
end
|
76
122
|
|
123
|
+
unless SchemaPlusHelpers.sqlite3?
|
124
|
+
context "with cyclic foreign key constraints" do
|
125
|
+
before (:all) do
|
126
|
+
ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, :commenter_id, User.table_name, :id)
|
127
|
+
ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, :post_id, Post.table_name, :id)
|
128
|
+
ActiveRecord::Base.connection.add_foreign_key(Post.table_name, :first_comment_id, Comment.table_name, :id)
|
129
|
+
ActiveRecord::Base.connection.add_foreign_key(Post.table_name, :user_id, User.table_name, :id)
|
130
|
+
ActiveRecord::Base.connection.add_foreign_key(User.table_name, :first_post_id, Post.table_name, :id)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not raise an error" do
|
134
|
+
expect { dump_all }.should_not raise_error
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should dump constraints after the tables they reference" do
|
138
|
+
dump_all.should match(%r{create_table "comments".*foreign_key.*\["first_comment_id"\], "comments", \["id"\]}m)
|
139
|
+
dump_all.should match(%r{create_table "posts".*foreign_key.*\["first_post_id"\], "posts", \["id"\]}m)
|
140
|
+
dump_all.should match(%r{create_table "posts".*foreign_key.*\["post_id"\], "posts", \["id"\]}m)
|
141
|
+
dump_all.should match(%r{create_table "users".*foreign_key.*\["commenter_id"\], "users", \["id"\]}m)
|
142
|
+
dump_all.should match(%r{create_table "users".*foreign_key.*\["user_id"\], "users", \["id"\]}m)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
77
147
|
protected
|
78
148
|
def to_regexp(string)
|
79
149
|
Regexp.new(Regexp.escape(string))
|
@@ -130,38 +200,3 @@ describe "Schema dump (core)" do
|
|
130
200
|
end
|
131
201
|
|
132
202
|
end
|
133
|
-
|
134
|
-
describe "Schema dump (auto)" do
|
135
|
-
|
136
|
-
before(:all) do
|
137
|
-
load_auto_schema
|
138
|
-
end
|
139
|
-
|
140
|
-
let(:dump) do
|
141
|
-
stream = StringIO.new
|
142
|
-
ActiveRecord::SchemaDumper.ignore_tables = []
|
143
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
144
|
-
stream.string
|
145
|
-
end
|
146
|
-
|
147
|
-
unless SchemaPlusHelpers.sqlite3?
|
148
|
-
it "shouldn't include :index option for index" do
|
149
|
-
add_column(:author_id, :integer, :references => :users, :index => true) do
|
150
|
-
dump.should_not match(/index => true/)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
protected
|
156
|
-
def add_column(column_name, *args)
|
157
|
-
table = Post.table_name
|
158
|
-
ActiveRecord::Migration.suppress_messages do
|
159
|
-
ActiveRecord::Migration.add_column(table, column_name, *args)
|
160
|
-
Post.reset_column_information
|
161
|
-
yield if block_given?
|
162
|
-
ActiveRecord::Migration.remove_column(table, column_name)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
data/spec/views_spec.rb
CHANGED
@@ -98,6 +98,8 @@ describe ActiveRecord do
|
|
98
98
|
|
99
99
|
def define_schema_and_data
|
100
100
|
migration.suppress_messages do
|
101
|
+
connection.views.each do |view| connection.drop_view view end
|
102
|
+
connection.tables.each do |table| connection.drop_table table end
|
101
103
|
|
102
104
|
schema.define do
|
103
105
|
|
metadata
CHANGED
@@ -1,126 +1,144 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: schema_plus
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
6
10
|
platform: ruby
|
7
|
-
authors:
|
11
|
+
authors:
|
8
12
|
- Ronen Barzel
|
9
|
-
-
|
13
|
+
- Michal Lomnicki
|
10
14
|
autorequire:
|
11
15
|
bindir: bin
|
12
16
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
|
18
|
+
date: 2011-10-22 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
16
22
|
name: rails
|
17
|
-
requirement: &69439930 !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
|
-
requirements:
|
20
|
-
- - ! '>='
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '0'
|
23
|
-
type: :runtime
|
24
23
|
prerelease: false
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
34
31
|
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: valuable
|
35
35
|
prerelease: false
|
36
|
-
|
37
|
-
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :runtime
|
44
|
+
version_requirements: *id002
|
45
|
+
- !ruby/object:Gem::Dependency
|
38
46
|
name: rake
|
39
|
-
|
40
|
-
|
41
|
-
requirements:
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
42
50
|
- - ~>
|
43
|
-
- !ruby/object:Gem::Version
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
- 8
|
55
|
+
- 7
|
44
56
|
version: 0.8.7
|
45
57
|
type: :development
|
46
|
-
|
47
|
-
|
48
|
-
- !ruby/object:Gem::Dependency
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
49
60
|
name: rspec
|
50
|
-
requirement: &69439120 !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
|
-
requirements:
|
53
|
-
- - ! '>='
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
type: :development
|
57
61
|
prerelease: false
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: '0'
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
version: "0"
|
67
69
|
type: :development
|
70
|
+
version_requirements: *id004
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: pg
|
68
73
|
prerelease: false
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
74
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
78
81
|
type: :development
|
82
|
+
version_requirements: *id005
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mysql
|
79
85
|
prerelease: false
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '0'
|
86
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
89
93
|
type: :development
|
94
|
+
version_requirements: *id006
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: sqlite3
|
90
97
|
prerelease: false
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version: '0'
|
98
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
100
105
|
type: :development
|
106
|
+
version_requirements: *id007
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: simplecov
|
101
109
|
prerelease: false
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
111
117
|
type: :development
|
118
|
+
version_requirements: *id008
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: simplecov-gem-adapter
|
112
121
|
prerelease: false
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
122
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
type: :development
|
130
|
+
version_requirements: *id009
|
131
|
+
description: "SchemaPlus is an ActiveRecord extension that provides enhanced capabilities for schema definition and querying, including: enhanced and more DRY index capabilities, support and automation for foreign key constraints, and support for views."
|
132
|
+
email:
|
118
133
|
- ronen@barzel.org
|
119
134
|
- michal.lomnicki@gmail.com
|
120
135
|
executables: []
|
136
|
+
|
121
137
|
extensions: []
|
138
|
+
|
122
139
|
extra_rdoc_files: []
|
123
|
-
|
140
|
+
|
141
|
+
files:
|
124
142
|
- .gitignore
|
125
143
|
- .travis.yml
|
126
144
|
- Gemfile
|
@@ -134,8 +152,10 @@ files:
|
|
134
152
|
- gemfiles/Gemfile.rails-3.1
|
135
153
|
- gemfiles/Gemfile.rails-3.1.lock
|
136
154
|
- init.rb
|
155
|
+
- lib/rails/tasks/database.rake
|
137
156
|
- lib/schema_plus.rb
|
138
157
|
- lib/schema_plus/active_record/base.rb
|
158
|
+
- lib/schema_plus/active_record/column_options_handler.rb
|
139
159
|
- lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb
|
140
160
|
- lib/schema_plus/active_record/connection_adapters/column.rb
|
141
161
|
- lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb
|
@@ -153,6 +173,7 @@ files:
|
|
153
173
|
- runspecs
|
154
174
|
- schema_plus.gemspec
|
155
175
|
- spec/column_spec.rb
|
176
|
+
- spec/connection_spec.rb
|
156
177
|
- spec/connections/mysql/connection.rb
|
157
178
|
- spec/connections/mysql2/connection.rb
|
158
179
|
- spec/connections/postgresql/connection.rb
|
@@ -166,7 +187,6 @@ files:
|
|
166
187
|
- spec/models/post.rb
|
167
188
|
- spec/models/user.rb
|
168
189
|
- spec/rails3_migration_spec.rb
|
169
|
-
- spec/references_spec.rb
|
170
190
|
- spec/schema/auto_schema.rb
|
171
191
|
- spec/schema/core_schema.rb
|
172
192
|
- spec/schema_dumper_spec.rb
|
@@ -178,29 +198,35 @@ files:
|
|
178
198
|
- spec/support/matchers/reference.rb
|
179
199
|
- spec/support/reference.rb
|
180
200
|
- spec/views_spec.rb
|
201
|
+
has_rdoc: true
|
181
202
|
homepage: https://github.com/lomba/schema_plus
|
182
203
|
licenses: []
|
204
|
+
|
183
205
|
post_install_message:
|
184
206
|
rdoc_options: []
|
185
|
-
|
207
|
+
|
208
|
+
require_paths:
|
186
209
|
- lib
|
187
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
requirements:
|
196
|
-
- -
|
197
|
-
- !ruby/object:Gem::Version
|
198
|
-
|
210
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
segments:
|
215
|
+
- 0
|
216
|
+
version: "0"
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
segments:
|
222
|
+
- 0
|
223
|
+
version: "0"
|
199
224
|
requirements: []
|
225
|
+
|
200
226
|
rubyforge_project: schema_plus
|
201
|
-
rubygems_version: 1.
|
227
|
+
rubygems_version: 1.3.6
|
202
228
|
signing_key:
|
203
229
|
specification_version: 3
|
204
|
-
summary: Enhances ActiveRecord schema mechanism, including more DRY index creation
|
205
|
-
and support for foreign key constraints and views.
|
230
|
+
summary: Enhances ActiveRecord schema mechanism, including more DRY index creation and support for foreign key constraints and views.
|
206
231
|
test_files: []
|
232
|
+
|
data/spec/references_spec.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
|
-
|
4
|
-
describe 'get_references method' do
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
@target = ActiveRecord::Migration.connection
|
8
|
-
@table_name = 'comments'
|
9
|
-
@column_name = 'post_id'
|
10
|
-
@destination_table = 'posts'
|
11
|
-
@destinantion_column = :id
|
12
|
-
end
|
13
|
-
|
14
|
-
around(:each) do |example|
|
15
|
-
with_auto_create do
|
16
|
-
example.run
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
it "should accept table name and column name to references" do
|
21
|
-
lambda { @target.get_references(@table_name, @column_name) }.should_not raise_error
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should return an array" do
|
25
|
-
@target.get_references(@table_name, @column_name).should be_an(Array)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "should return nil if auto_create is disabled" do
|
29
|
-
SchemaPlus.config.foreign_keys.auto_create = false
|
30
|
-
@target.get_references(@table_name, @column_name).should be_nil
|
31
|
-
end
|
32
|
-
|
33
|
-
it "should split column name to table name and primary key" do
|
34
|
-
result = @target.get_references(@table_name, @column_name)
|
35
|
-
result[0].should eql @destination_table
|
36
|
-
result[1].should eql @destinantion_column
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should not auto create referencs when configured not to" do
|
40
|
-
with_auto_create(false) do
|
41
|
-
result = @target.get_references(@table_name, @column_name)
|
42
|
-
result.should be_nil
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should handle parent_id as belonging to the same table" do
|
47
|
-
column_name = 'parent_id'
|
48
|
-
result = @target.get_references(@table_name, column_name)
|
49
|
-
result[0].should eql @table_name
|
50
|
-
result[1].should eql :id
|
51
|
-
end
|
52
|
-
|
53
|
-
it "should accept :references option which overrides default table name" do
|
54
|
-
result = @target.get_references(@table_name, @column_name, :references => 'users')
|
55
|
-
result[0].should eql 'users'
|
56
|
-
result[1].should eql :id
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should accept :references option which overrides default table name and default column name" do
|
60
|
-
result = @target.get_references(@table_name, @column_name, :references => ['users', 'uuid'])
|
61
|
-
result[0].should eql 'users'
|
62
|
-
result[1].should eql 'uuid'
|
63
|
-
end
|
64
|
-
|
65
|
-
protected
|
66
|
-
|
67
|
-
def with_auto_create(value = true)
|
68
|
-
old_value = SchemaPlus.config.foreign_keys.auto_create
|
69
|
-
SchemaPlus.config.foreign_keys.auto_create = value
|
70
|
-
begin
|
71
|
-
yield
|
72
|
-
ensure
|
73
|
-
SchemaPlus.config.foreign_keys.auto_create = old_value
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|