schema_plus 1.8.9 → 2.0.0.pre1

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/.travis.yml +1 -47
  4. data/CHANGELOG.md +0 -35
  5. data/README.md +73 -107
  6. data/Rakefile +7 -10
  7. data/TODO.md +51 -0
  8. data/gemfiles/Gemfile.base +2 -0
  9. data/lib/schema_column_plus.rb +7 -0
  10. data/lib/{schema_plus → schema_column_plus}/active_record/connection_adapters/column.rb +13 -11
  11. data/lib/schema_column_plus/middleware/model.rb +22 -0
  12. data/lib/schema_db_default.rb +13 -0
  13. data/lib/{schema_plus → schema_db_default}/active_record/attribute.rb +4 -4
  14. data/lib/schema_db_default/db_default.rb +17 -0
  15. data/lib/schema_db_default/middleware.rb +30 -0
  16. data/lib/schema_default_expr.rb +32 -0
  17. data/lib/schema_default_expr/active_record/connection_adapters/mysql_adapter.rb +17 -0
  18. data/lib/schema_default_expr/active_record/connection_adapters/postgresql_adapter.rb +18 -0
  19. data/lib/schema_default_expr/active_record/connection_adapters/sqlite3_adapter.rb +35 -0
  20. data/lib/schema_default_expr/middleware.rb +54 -0
  21. data/lib/schema_pg_enums.rb +6 -0
  22. data/lib/schema_pg_enums/active_record.rb +69 -0
  23. data/lib/schema_pg_enums/middleware.rb +23 -0
  24. data/lib/schema_plus.rb +17 -45
  25. data/lib/schema_plus/active_record/base.rb +6 -23
  26. data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +80 -181
  27. data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +78 -99
  28. data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +34 -114
  29. data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +16 -370
  30. data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +1 -67
  31. data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +18 -112
  32. data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +14 -116
  33. data/lib/schema_plus/active_record/migration/command_recorder.rb +8 -59
  34. data/lib/schema_plus/middleware/dumper.rb +94 -0
  35. data/lib/schema_plus/middleware/migration.rb +167 -0
  36. data/lib/schema_plus/middleware/model.rb +17 -0
  37. data/lib/schema_plus/version.rb +1 -1
  38. data/lib/schema_plus_tables.rb +15 -0
  39. data/lib/schema_plus_tables/active_record/connection_adapters/abstract_adapter.rb +20 -0
  40. data/lib/schema_plus_tables/active_record/connection_adapters/mysql_adapter.rb +25 -0
  41. data/lib/schema_plus_tables/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  42. data/lib/schema_plus_tables/active_record/connection_adapters/sqlite3_adapter.rb +12 -0
  43. data/lib/schema_views.rb +16 -0
  44. data/lib/schema_views/active_record/connection_adapters/abstract_adapter.rb +41 -0
  45. data/lib/schema_views/active_record/connection_adapters/mysql_adapter.rb +30 -0
  46. data/lib/schema_views/active_record/connection_adapters/postgresql_adapter.rb +31 -0
  47. data/lib/schema_views/active_record/connection_adapters/sqlite3_adapter.rb +18 -0
  48. data/lib/schema_views/middleware.rb +47 -0
  49. data/schema_dev.yml +1 -31
  50. data/schema_plus.gemspec +11 -9
  51. data/spec/foreign_key_definition_spec.rb +7 -7
  52. data/spec/foreign_key_spec.rb +63 -48
  53. data/spec/migration_spec.rb +58 -203
  54. data/spec/named_schemas_spec.rb +5 -88
  55. data/spec/{column_spec.rb → schema_column_plus/column_spec.rb} +26 -48
  56. data/spec/schema_db_default/column_spec.rb +58 -0
  57. data/spec/{column_default_spec.rb → schema_default_expr/column_default_spec.rb} +1 -2
  58. data/spec/schema_default_expr/schema_dumper_spec.rb +116 -0
  59. data/spec/schema_dumper_spec.rb +22 -327
  60. data/spec/{enum_spec.rb → schema_pg_enums/enum_spec.rb} +1 -1
  61. data/spec/schema_pg_enums/schema_dumper_spec.rb +37 -0
  62. data/spec/schema_views/named_schemas_spec.rb +97 -0
  63. data/spec/{views_spec.rb → schema_views/views_spec.rb} +1 -1
  64. data/spec/spec_helper.rb +2 -1
  65. data/spec/support/matchers/reference.rb +11 -12
  66. metadata +104 -57
  67. data/gemfiles/rails-3.2/Gemfile.base +0 -3
  68. data/gemfiles/rails-3.2/Gemfile.mysql +0 -10
  69. data/gemfiles/rails-3.2/Gemfile.mysql2 +0 -10
  70. data/gemfiles/rails-3.2/Gemfile.postgresql +0 -10
  71. data/gemfiles/rails-3.2/Gemfile.sqlite3 +0 -10
  72. data/gemfiles/rails-4.0/Gemfile.base +0 -3
  73. data/gemfiles/rails-4.0/Gemfile.mysql2 +0 -10
  74. data/gemfiles/rails-4.0/Gemfile.postgresql +0 -10
  75. data/gemfiles/rails-4.0/Gemfile.sqlite3 +0 -10
  76. data/gemfiles/rails-4.1/Gemfile.base +0 -3
  77. data/gemfiles/rails-4.1/Gemfile.mysql2 +0 -10
  78. data/gemfiles/rails-4.1/Gemfile.postgresql +0 -10
  79. data/gemfiles/rails-4.1/Gemfile.sqlite3 +0 -10
  80. data/lib/schema_plus/active_record/column_options_handler.rb +0 -117
  81. data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +0 -70
  82. data/lib/schema_plus/active_record/db_default.rb +0 -19
  83. data/lib/schema_plus/active_record/foreign_keys.rb +0 -137
  84. data/lib/schema_plus/active_record/schema_dumper.rb +0 -171
  85. data/lib/schema_plus/railtie.rb +0 -20
  86. data/spec/index_definition_spec.rb +0 -211
  87. data/spec/index_spec.rb +0 -249
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe "with multiple schemas" do
4
4
  def connection
@@ -35,52 +35,6 @@ describe "with multiple schemas" do
35
35
  end + ", login varchar(255))"
36
36
  end
37
37
 
38
- context "with indexes in each schema" do
39
- before(:each) do
40
- connection.execute 'CREATE INDEX ' + case connection.adapter_name
41
- when /^mysql/i then "index_users_on_login ON schema_plus_test2.users"
42
- when /^postgresql/i then "index_users_on_login ON schema_plus_test2.users"
43
- when /^sqlite/i then "schema_plus_test2.index_users_on_login ON users"
44
- end + " (login)"
45
- end
46
-
47
- it "should not find indexes in other schema" do
48
- User.reset_column_information
49
- expect(User.indexes).to be_empty
50
- end
51
-
52
- it "should find index in current schema" do
53
- connection.execute 'CREATE INDEX index_users_on_login ON users (login)'
54
- User.reset_column_information
55
- expect(User.indexes.map(&:name)).to eq(['index_users_on_login'])
56
- end
57
- end
58
-
59
- context "with views in each schema" do
60
- around(:each) do |example|
61
- begin
62
- example.run
63
- ensure
64
- connection.execute 'DROP VIEW schema_plus_test2.myview' rescue nil
65
- connection.execute 'DROP VIEW myview' rescue nil
66
- end
67
- end
68
-
69
- before(:each) do
70
- connection.views.each { |view| connection.drop_view view }
71
- connection.execute 'CREATE VIEW schema_plus_test2.myview AS SELECT * FROM users'
72
- end
73
-
74
- it "should not find views in other schema" do
75
- expect(connection.views).to be_empty
76
- end
77
-
78
- it "should find views in this schema" do
79
- connection.execute 'CREATE VIEW myview AS SELECT * FROM users'
80
- expect(connection.views).to eq(['myview'])
81
- end
82
- end
83
-
84
38
  context "with foreign key in each schema" do
85
39
  before(:each) do
86
40
  class Comment < ::ActiveRecord::Base ; end
@@ -116,9 +70,9 @@ describe "with multiple schemas" do
116
70
  t.integer :user_id, :foreign_key => true
117
71
  end
118
72
  Comment.reset_column_information
119
- expect(Comment.foreign_keys.map(&:column_names).flatten).to eq(["user_id"])
73
+ expect(Comment.foreign_keys.map(&:column).flatten).to eq(["user_id"])
120
74
  User.reset_column_information
121
- expect(User.reverse_foreign_keys.map(&:column_names).flatten).to eq(["user_id"])
75
+ expect(User.reverse_foreign_keys.map(&:column).flatten).to eq(["user_id"])
122
76
  end
123
77
 
124
78
  end
@@ -165,11 +119,11 @@ describe "with multiple schemas" do
165
119
  end
166
120
 
167
121
  it "should reference table in same schema" do
168
- expect(Member.foreign_keys.map(&:references_table_name)).to include "schema_plus_test2.groups"
122
+ expect(Member.foreign_keys.map(&:to_table)).to include "schema_plus_test2.groups"
169
123
  end
170
124
 
171
125
  it "should reference table in default schema", :mysql => :skip do
172
- expect(Member.foreign_keys.map(&:references_table_name)).to include "items"
126
+ expect(Member.foreign_keys.map(&:to_table)).to include "items"
173
127
  end
174
128
 
175
129
  it "should include the schema in the constraint name" do
@@ -179,41 +133,4 @@ describe "with multiple schemas" do
179
133
  end
180
134
  end
181
135
 
182
- context "when using PostGIS", :postgresql => :only do
183
- before(:all) do
184
- begin
185
- connection.execute "CREATE SCHEMA postgis"
186
- rescue ActiveRecord::StatementInvalid => e
187
- raise unless e.message =~ /already exists/
188
- end
189
- end
190
-
191
- around(:each) do |example|
192
- begin
193
- connection.execute "SET search_path to '$user','public','postgis'"
194
- example.run
195
- ensure
196
- connection.execute "SET search_path to '$user','public'"
197
- end
198
- end
199
-
200
- before(:each) do
201
- allow(connection).to receive(:adapter_name).and_return('PostGIS')
202
- end
203
-
204
- it "should hide views in postgis schema" do
205
- begin
206
- connection.create_view "postgis.hidden", "select 1", :force => true
207
- connection.create_view :myview, "select 2", :force => true
208
- expect(connection.views).to eq(["myview"])
209
- ensure
210
- connection.execute 'DROP VIEW postgis.hidden' rescue nil
211
- connection.execute 'DROP VIEW myview' rescue nil
212
- end
213
- end
214
- end
215
-
216
136
  end
217
-
218
-
219
-
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe "Column" do
4
4
 
@@ -15,8 +15,6 @@ describe "Column" do
15
15
  end
16
16
  it "works properly" do
17
17
  type = case
18
- when "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r <= "4.1".to_r
19
- { "type" => "string" }
20
18
  when SchemaDev::Rspec::Helpers.mysql?
21
19
  { "sql_type" => "varchar(255)" }
22
20
  when SchemaDev::Rspec::Helpers.postgresql?
@@ -84,6 +82,31 @@ describe "Column" do
84
82
  end
85
83
  end
86
84
 
85
+ context "with case insensitive" do
86
+ before(:each) do
87
+ create_table(User, :login => { :index => {}})
88
+ User.reset_column_information
89
+ @column = User.columns.find(&its.name == "login")
90
+ end
91
+
92
+ context "index", :mysql => :skip do
93
+
94
+ it "reports column as case insensitive" do
95
+ allow(User.indexes.first).to receive(:case_sensitive?).and_return(false);
96
+ expect(@column).not_to be_case_sensitive
97
+ end
98
+ end
99
+
100
+ context "database", :mysql => :only do
101
+
102
+ # make sure we haven't broken mysql's method
103
+ it "reports column as case insensitive" do
104
+ allow(migration).to receive(:collation).and_return("utf8_unicode_ci") # mysql determines case insensitivity its own way
105
+ expect(@column).not_to be_case_sensitive
106
+ end
107
+ end
108
+ end
109
+
87
110
  end
88
111
 
89
112
  context "regarding when it requires a value" do
@@ -105,51 +128,6 @@ describe "Column" do
105
128
 
106
129
  end
107
130
 
108
- context "using DB_DEFAULT" do
109
-
110
- before(:each) do
111
- create_table(User, :alpha => { :default => "gabba" }, :beta => {})
112
- end
113
-
114
- it "creating a record should respect default expression", :sqlite3 => :skip do
115
- User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello")
116
- expect(User.last.alpha).to eq("gabba")
117
- expect(User.last.beta).to eq("hello")
118
- end
119
-
120
- it "creating a record should raise an error", :sqlite3 => :only do
121
- expect { User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello") }.to raise_error ActiveRecord::StatementInvalid
122
- end
123
-
124
- it "updating a record should respect default expression", :sqlite3 => :skip do
125
- u = User.create!(:alpha => "hey", :beta => "hello")
126
- u.reload
127
- expect(u.alpha).to eq("hey")
128
- expect(u.beta).to eq("hello")
129
- u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye")
130
- u.reload
131
- expect(u.alpha).to eq("gabba")
132
- expect(u.beta).to eq("goodbye")
133
- end
134
-
135
- it "updating a record should raise an error", :sqlite3 => :only do
136
- u = User.create!(:alpha => "hey", :beta => "hello")
137
- expect { u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye") }.to raise_error ActiveRecord::StatementInvalid
138
- end
139
- end
140
-
141
- context "Postgresql array", :postgresql => :only do
142
-
143
- before(:each) do
144
- create_table(User, :alpha => { :default => [], :array => true })
145
- end
146
-
147
- it "respects array: true" do
148
- column = User.columns.find(&its.name == "alpha")
149
- expect(column.array).to be_truthy
150
- end
151
- end if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r >= "4.0".to_r
152
-
153
131
  protected
154
132
 
155
133
  def create_table(model, columns_with_options)
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe SchemaDbDefault do
4
+
5
+ let(:migration) { ::ActiveRecord::Migration }
6
+
7
+ before(:each) do
8
+ class User < ::ActiveRecord::Base ; end
9
+ create_table(User, :alpha => { :default => "gabba" }, :beta => {})
10
+ end
11
+
12
+ context "uses db default value", :sqlite3 => :skip do
13
+
14
+ it "when creating a record with DB_DEFAULT" do
15
+ User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello")
16
+ expect(User.last.alpha).to eq("gabba")
17
+ expect(User.last.beta).to eq("hello")
18
+ end
19
+
20
+ it "when updating a record with DB_DEFAULT" do
21
+ u = User.create!(:alpha => "hey", :beta => "hello")
22
+ u.reload
23
+ expect(u.alpha).to eq("hey")
24
+ expect(u.beta).to eq("hello")
25
+ u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye")
26
+ u.reload
27
+ expect(u.alpha).to eq("gabba")
28
+ expect(u.beta).to eq("goodbye")
29
+ end
30
+
31
+ end
32
+
33
+ context "raises an error", :sqlite3 => :only do
34
+
35
+ it "when creating a record with DB_DEFAULT" do
36
+ expect { User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello") }.to raise_error ActiveRecord::StatementInvalid
37
+ end
38
+
39
+ it "when updating a record with DB_DEFAULT" do
40
+ u = User.create!(:alpha => "hey", :beta => "hello")
41
+ expect { u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye") }.to raise_error ActiveRecord::StatementInvalid
42
+ end
43
+ end
44
+
45
+ protected
46
+
47
+ def create_table(model, columns_with_options)
48
+ migration.suppress_messages do
49
+ migration.create_table model.table_name, :force => true do |t|
50
+ columns_with_options.each_pair do |column, options|
51
+ t.send :string, column, options
52
+ end
53
+ end
54
+ model.reset_column_information
55
+ end
56
+ end
57
+ end
58
+
@@ -1,5 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
1
+ require 'spec_helper'
3
2
 
4
3
  describe "Column definition" do
5
4
  before(:each) do
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe "Schema dump" do
5
+
6
+ before(:all) do
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, :cascade => true end
13
+
14
+ create_table :posts, :force => true do |t|
15
+ t.text :body
16
+ t.integer :user_id
17
+ t.integer :first_comment_id
18
+ t.string :string_no_default
19
+ t.integer :short_id
20
+ t.string :str_short
21
+ t.integer :integer_col
22
+ t.float :float_col
23
+ t.decimal :decimal_col
24
+ t.datetime :datetime_col
25
+ t.timestamp :timestamp_col
26
+ t.time :time_col
27
+ t.date :date_col
28
+ t.binary :binary_col
29
+ t.boolean :boolean_col
30
+ end
31
+
32
+ end
33
+ end
34
+ class ::Post < ActiveRecord::Base ; end
35
+ end
36
+
37
+ context "with date default", :postgresql => :only do
38
+ it "should dump the default hash expr as now()" do
39
+ with_additional_column Post, :posted_at, :datetime, :default => :now do
40
+ expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr\s*=>)\s*"now\(\)"\s*\}})
41
+ end
42
+ end
43
+
44
+ it "should dump the default hash expr as CURRENT_TIMESTAMP" do
45
+ with_additional_column Post, :posted_at, :datetime, :default => {:expr => 'date \'2001-09-28\''} do
46
+ expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>).*2001-09-28.*})
47
+ end
48
+ end
49
+
50
+ it "can dump a complex default expression" do
51
+ with_additional_column Post, :name, :string, :default => {:expr => 'substring(random()::text from 3 for 6)'} do
52
+ expect(dump_posts).to match(%r{t\.string\s+"name",\s*(?:default:|:default\s*=>)\s*{\s*(?:expr:|:expr\s*=>)\s*"\\"substring\\"\(\(random\(\)\)::text, 3, 6\)"\s*}})
53
+ end
54
+ end
55
+ end
56
+
57
+ context "with date default", :sqlite3 => :only do
58
+ it "should dump the default hash expr as now" do
59
+ with_additional_column Post, :posted_at, :datetime, :default => :now do
60
+ expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr =>)\s*"\(DATETIME\('now'\)\)"\s*\}})
61
+ end
62
+ end
63
+
64
+ it "should dump the default hash expr string as now" do
65
+ with_additional_column Post, :posted_at, :datetime, :default => { :expr => "(DATETIME('now'))" } do
66
+ expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr =>)\s*"\(DATETIME\('now'\)\)"\s*\}})
67
+ end
68
+ end
69
+
70
+ it "should dump the default value normally" do
71
+ with_additional_column Post, :posted_at, :string, :default => "now" do
72
+ expect(dump_posts).to match(%r{t\.string\s*"posted_at",\s*(?:default:|:default =>)\s*"now"})
73
+ end
74
+ end
75
+ end
76
+
77
+ it "should leave out :default when default was changed to null" do
78
+ ActiveRecord::Migration.suppress_messages do
79
+ ActiveRecord::Migration.change_column_default :posts, :string_no_default, nil
80
+ end
81
+ # mysql2 includes 'limit: 255' in the output. that's OK, just want to
82
+ # make sure the full line doesn't have 'default' in it.
83
+ expect(dump_posts).to match(%r{t\.string\s+"string_no_default"\s*(,\s*limit:\s*\d+)?$})
84
+ end
85
+
86
+ protected
87
+ def to_regexp(string)
88
+ Regexp.new(Regexp.escape(string))
89
+ end
90
+
91
+ def with_additional_column(model, column_name, column_type, options)
92
+ table_columns = model.columns.reject{|column| column.name == 'id'}
93
+ ActiveRecord::Migration.suppress_messages do
94
+ ActiveRecord::Migration.create_table model.table_name, :force => true do |t|
95
+ table_columns.each do |column|
96
+ t.column column.name, column.type, :default => column.default
97
+ end
98
+ t.column column_name, column_type, options
99
+ end
100
+ end
101
+ yield
102
+ end
103
+
104
+ def dump_schema(opts={})
105
+ stream = StringIO.new
106
+ ActiveRecord::SchemaDumper.ignore_tables = Array.wrap(opts[:ignore]) || []
107
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
108
+ stream.string
109
+ end
110
+
111
+ def dump_posts
112
+ dump_schema(:ignore => %w[users comments])
113
+ end
114
+
115
+ end
116
+
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
  require 'stringio'
3
3
 
4
4
  describe "Schema dump" do
@@ -49,13 +49,13 @@ describe "Schema dump" do
49
49
 
50
50
  it "should include foreign_key definition" do
51
51
  with_foreign_key Post, :user_id, :users, :id do
52
- expect(dump_posts).to match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"]}))
52
+ expect(dump_posts).to match(%r{t.integer\s+"user_id".*foreign_key.*users})
53
53
  end
54
54
  end
55
55
 
56
56
  it "should include foreign_key name" do
57
57
  with_foreign_key Post, :user_id, :users, :id, :name => "yippee" do
58
- expect(dump_posts).to match(/foreign_key.*user_id.*users.*id.*:name => "yippee"/)
58
+ expect(dump_posts).to match(/user_id.*foreign_key.*users.*name: "yippee"/)
59
59
  end
60
60
  end
61
61
 
@@ -66,7 +66,7 @@ describe "Schema dump" do
66
66
  end
67
67
 
68
68
 
69
- it "should sort foreign_key definitions" do
69
+ xit "should sort foreign_key definitions" do
70
70
  with_foreign_keys Comment, [ [ :post_id, :posts, :id ], [ :commenter_id, :users, :id ]] do
71
71
  expect(dump_schema).to match(/foreign_key.+commenter_id.+foreign_key.+post_id/m)
72
72
  end
@@ -94,183 +94,19 @@ describe "Schema dump" do
94
94
 
95
95
  end
96
96
 
97
- context "with date default", :postgresql => :only do
98
- it "should dump the default hash expr as now()" do
99
- with_additional_column Post, :posted_at, :datetime, :default => :now do
100
- expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr\s*=>)\s*"now\(\)"\s*\}})
101
- end
102
- end
103
-
104
- it "should dump the default hash expr as CURRENT_TIMESTAMP" do
105
- with_additional_column Post, :posted_at, :datetime, :default => {:expr => 'date \'2001-09-28\''} do
106
- expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>).*2001-09-28.*})
107
- end
108
- end
109
-
110
- it "can dump a complex default expression" do
111
- with_additional_column Post, :name, :string, :default => {:expr => 'substring(random()::text from 3 for 6)'} do
112
- expect(dump_posts).to match(%r{t\.string\s+"name",\s*(?:default:|:default\s*=>)\s*{\s*(?:expr:|:expr\s*=>)\s*"\\"substring\\"\(\(random\(\)\)::text, 3, 6\)"\s*}})
113
- end
114
- end
115
- end
116
-
117
- context "with date default", :sqlite3 => :only do
118
- it "should dump the default hash expr as now" do
119
- with_additional_column Post, :posted_at, :datetime, :default => :now do
120
- expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr =>)\s*"\(DATETIME\('now'\)\)"\s*\}})
121
- end
122
- end
123
-
124
- it "should dump the default hash expr string as now" do
125
- with_additional_column Post, :posted_at, :datetime, :default => { :expr => "(DATETIME('now'))" } do
126
- expect(dump_posts).to match(%r{t\.datetime\s+"posted_at",\s*(?:default:|:default =>)\s*\{\s*(?:expr:|:expr =>)\s*"\(DATETIME\('now'\)\)"\s*\}})
127
- end
128
- end
129
-
130
- it "should dump the default value normally" do
131
- with_additional_column Post, :posted_at, :string, :default => "now" do
132
- expect(dump_posts).to match(%r{t\.string\s*"posted_at",\s*(?:default:|:default =>)\s*"now"})
133
- end
134
- end
135
- end
136
-
137
- it "should leave out :default when default was changed to null" do
138
- ActiveRecord::Migration.suppress_messages do
139
- ActiveRecord::Migration.change_column_default :posts, :string_no_default, nil
140
- end
141
- # mysql2 includes 'limit: 255' in the output. that's OK, just want to
142
- # make sure the full line doesn't have 'default' in it.
143
- expect(dump_posts).to match(%r{t\.string\s+"string_no_default"\s*(,\s*limit:\s*\d+)?$})
144
- end
145
-
146
97
  it "should include foreign_key options" do
147
- with_foreign_key Post, :user_id, :users, :id, :on_update => :cascade, :on_delete => :set_null do
148
- expect(dump_posts).to match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"], :on_update => :cascade, :on_delete => :set_null}))
149
- end
150
- end
151
-
152
- it "should include index definition" do
153
- with_index Post, :user_id do
154
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id"]}))
155
- end
156
- end
157
-
158
- it "should include index name" do
159
- with_index Post, :user_id, :name => "custom_name" do
160
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id"], :name => "custom_name"}))
161
- end
162
- end
163
-
164
- it "should define unique index" do
165
- with_index Post, :user_id, :name => "posts_user_id_index", :unique => true do
166
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :unique => true}))
167
- end
168
- end
169
-
170
- it "should sort indexes" do
171
- with_index Post, :user_id do
172
- with_index Post, :short_id do
173
- expect(dump_posts).to match(/on_short_id.+on_user_id/m)
174
- end
175
- end
176
- end
177
-
178
- it "should include index order", :mysql => :skip do
179
- with_index Post, [:user_id, :first_comment_id, :short_id], :order => { :user_id => :asc, :first_comment_id => :desc } do
180
- expect(dump_posts).to match(%r{t.index \["user_id", "first_comment_id", "short_id"\],.*:order => {"user_id" => :asc, "first_comment_id" => :desc, "short_id" => :asc}})
181
- end
182
- end
183
-
184
- context "index extras", :postgresql => :only do
185
-
186
- it "should define case insensitive index" do
187
- with_index Post, [:body, :string_no_default], :case_sensitive => false do
188
- expect(dump_posts).to match(to_regexp(%q{t.index ["body", "string_no_default"], :name => "index_posts_on_body_and_string_no_default", :case_sensitive => false}))
189
- end
190
- end
191
-
192
- it "should define index with type cast" do
193
- with_index Post, [:integer_col], :name => "index_with_type_cast", :expression => "LOWER(integer_col::text)" do
194
- expect(dump_posts).to match(to_regexp(%q{t.index :name => "index_with_type_cast", :expression => "lower((integer_col)::text)"}))
195
- end
196
- end
197
-
198
-
199
- it "should define case insensitive index with mixed ids and strings" do
200
- with_index Post, [:user_id, :str_short, :short_id, :body], :case_sensitive => false do
201
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id", "str_short", "short_id", "body"], :name => "index_posts_on_user_id_and_str_short_and_short_id_and_body", :case_sensitive => false}))
202
- end
203
- end
204
-
205
- [:integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |col_type|
206
- col_name = "#{col_type}_col"
207
- it "should define case insensitive index that includes an #{col_type}" do
208
- with_index Post, [:user_id, :str_short, col_name, :body], :case_sensitive => false do
209
- expect(dump_posts).to match(to_regexp(%Q!t.index ["user_id", "str_short", "#{col_name}", "body"], :name => "index_posts_on_user_id_and_str_short_and_#{col_name}_and_body", :case_sensitive => false!))
210
- end
211
- end
212
- end
213
-
214
- it "should define conditions" do
215
- with_index Post, :user_id, :name => "posts_user_id_index", :conditions => "user_id IS NOT NULL" do
216
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :conditions => "(user_id IS NOT NULL)"}))
217
- end
218
- end
219
-
220
- it "should define expression" do
221
- with_index Post, :name => "posts_freaky_index", :expression => "USING hash (least(id, user_id))" do
222
- expect(dump_posts).to match(to_regexp(%q{t.index :name => "posts_freaky_index", :kind => "hash", :expression => "LEAST(id, user_id)"}))
223
- end
224
- end
225
-
226
- it "should define operator_class" do
227
- with_index Post, :body, :operator_class => 'text_pattern_ops' do
228
- expect(dump_posts).to match(to_regexp(%q{t.index ["body"], :name => "index_posts_on_body", :operator_class => {"body" => "text_pattern_ops"}}))
229
- end
230
- end
231
-
232
- it 'should dump proper operator_class with case_sensitive => false' do
233
- with_index Post, :body, :operator_class => 'text_pattern_ops', :case_sensitive => false do
234
- expect(dump_posts).to match(to_regexp(%q{t.index ["body"], :name => "index_posts_on_body", :case_sensitive => false, :operator_class => {"body" => "text_pattern_ops"}}))
235
- end
236
- end
237
-
238
- it "should dump unique: true with expression (Issue #142)" do
239
- with_index Post, :name => "posts_user_body_index", :unique => true, :expression => "BTRIM(LOWER(body))" do
240
- expect(dump_posts).to match(%r{#{to_regexp(%q{t.index :name => "posts_user_body_index", :unique => true, :expression => "btrim(lower(body))"})}$})
241
- end
242
- end
243
-
244
-
245
- it "should not define :case_sensitive => false with non-trivial expression" do
246
- with_index Post, :name => "posts_user_body_index", :expression => "BTRIM(LOWER(body))" do
247
- expect(dump_posts).to match(%r{#{to_regexp(%q{t.index :name => "posts_user_body_index", :expression => "btrim(lower(body))"})}$})
248
- end
98
+ with_foreign_key Post, :user_id, :users, :id, :on_update => :cascade, :on_delete => :nullify do
99
+ expect(dump_posts).to match(%q[t.integer\s*"user_id",.*foreign_key: {references: "users", name: "fk_posts_user_id", on_update: :cascade, on_delete: :nullify}])
249
100
  end
250
-
251
-
252
- it "should define kind" do
253
- with_index Post, :name => "posts_body_index", :expression => "USING hash (body)" do
254
- expect(dump_posts).to match(to_regexp(%q{t.index ["body"], :name => "posts_body_index", :kind => "hash"}))
255
- end
256
- end
257
-
258
- it "should not include index order for non-ordered index types" do
259
- with_index Post, :user_id, :kind => :hash do
260
- expect(dump_posts).to match(to_regexp(%q{t.index ["user_id"], :name => "index_posts_on_user_id", :kind => "hash"}))
261
- expect(dump_posts).not_to match(%r{:order})
262
- end
263
- end
264
-
265
101
  end
266
102
 
267
103
  context "with cyclic foreign key constraints", :sqlite3 => :skip do
268
104
  before(:all) do
269
- ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, :commenter_id, User.table_name, :id)
270
- ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, :post_id, Post.table_name, :id)
271
- ActiveRecord::Base.connection.add_foreign_key(Post.table_name, :first_comment_id, Comment.table_name, :id)
272
- ActiveRecord::Base.connection.add_foreign_key(Post.table_name, :user_id, User.table_name, :id)
273
- ActiveRecord::Base.connection.add_foreign_key(User.table_name, :first_post_id, Post.table_name, :id)
105
+ ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, User.table_name, column: :commenter_id)
106
+ ActiveRecord::Base.connection.add_foreign_key(Comment.table_name, Post.table_name, column: :post_id)
107
+ ActiveRecord::Base.connection.add_foreign_key(Post.table_name, Comment.table_name, column: :first_comment_id)
108
+ ActiveRecord::Base.connection.add_foreign_key(Post.table_name, User.table_name, column: :user_id)
109
+ ActiveRecord::Base.connection.add_foreign_key(User.table_name, Post.table_name, column: :first_post_id)
274
110
  end
275
111
 
276
112
  it "should not raise an error" do
@@ -278,124 +114,17 @@ describe "Schema dump" do
278
114
  end
279
115
 
280
116
  it "should dump constraints after the tables they reference" do
281
- expect(dump_schema).to match(%r{create_table "comments".*foreign_key.*\["first_comment_id"\], "comments", \["id"\]}m)
282
- expect(dump_schema).to match(%r{create_table "posts".*foreign_key.*\["first_post_id"\], "posts", \["id"\]}m)
283
- expect(dump_schema).to match(%r{create_table "posts".*foreign_key.*\["post_id"\], "posts", \["id"\]}m)
284
- expect(dump_schema).to match(%r{create_table "users".*foreign_key.*\["commenter_id"\], "users", \["id"\]}m)
285
- expect(dump_schema).to match(%r{create_table "users".*foreign_key.*\["user_id"\], "users", \["id"\]}m)
286
- end
287
-
288
- context 'with complicated schemas' do
289
- before(:all) do
290
-
291
- SchemaPlus.setup do |config|
292
- config.foreign_keys.auto_create = false
293
- end
294
- ActiveRecord::Migration.suppress_messages do
295
- ActiveRecord::Schema.define do
296
- connection.tables.each do |table| drop_table table, :cascade => true end
297
-
298
- create_table :period_types, force: true do |t|
299
- t.string :name
300
- end
301
-
302
- create_table :grade_systems, force: true do |t|
303
- t.string :name
304
- t.integer :school_id
305
- t.integer :parent_id
306
- t.integer :profile_id
307
- end
308
-
309
- create_table :schools, force: true do |t|
310
- t.string :name
311
- t.integer :default_grade_system_id
312
- end
313
-
314
- create_table :academic_years, force: true do |t|
315
- t.string :name
316
- t.integer :school_id
317
- t.integer :period_type_id
318
- end
319
-
320
- create_table :buildings, force: true do |t|
321
- t.string :name
322
- t.integer :school_id
323
- end
324
-
325
- create_table :publishing_houses, force: true do |t|
326
- t.string :name
327
- end
328
-
329
- create_table :profiles, force: true do |t|
330
- t.integer :school_id
331
- t.integer :publishing_house_id
332
- t.integer :building_id
333
- end
334
-
335
- create_table :class_units, force: true do |t|
336
- t.string :name
337
- t.integer :school_id
338
- t.integer :mentor_id
339
- t.integer :building_id
340
- end
341
- end
342
- end
343
-
344
- class ::AcademicYear < ActiveRecord::Base ; end
345
- class ::Building < ActiveRecord::Base ; end
346
- class ::ClassUnit < ActiveRecord::Base ; end
347
- class ::GradeSystem < ActiveRecord::Base ; end
348
- class ::Profile < ActiveRecord::Base ; end
349
- class ::PublishingHouse < ActiveRecord::Base ; end
350
- class ::PeriodType < ActiveRecord::Base ; end
351
- class ::School < ActiveRecord::Base ; end
352
-
353
- ActiveRecord::Base.connection.add_foreign_key(School.table_name, :default_grade_system_id, GradeSystem.table_name, :id)
354
- ActiveRecord::Base.connection.add_foreign_key(GradeSystem.table_name, :school_id, School.table_name, :id)
355
- ActiveRecord::Base.connection.add_foreign_key(GradeSystem.table_name, :parent_id, GradeSystem.table_name, :id)
356
- ActiveRecord::Base.connection.add_foreign_key(GradeSystem.table_name, :profile_id, Profile.table_name, :id)
357
- ActiveRecord::Base.connection.add_foreign_key(Profile.table_name, :building_id, Building.table_name, :id)
358
- ActiveRecord::Base.connection.add_foreign_key(Profile.table_name, :school_id, School.table_name, :id)
359
- ActiveRecord::Base.connection.add_foreign_key(ClassUnit.table_name, :school_id, School.table_name, :id)
360
- ActiveRecord::Base.connection.add_foreign_key(ClassUnit.table_name, :building_id, Building.table_name, :id)
361
- ActiveRecord::Base.connection.add_foreign_key(ClassUnit.table_name, :mentor_id, Profile.table_name, :id)
362
- ActiveRecord::Base.connection.add_foreign_key(Building.table_name, :school_id, School.table_name, :id)
363
- ActiveRecord::Base.connection.add_foreign_key(AcademicYear.table_name, :school_id, School.table_name, :id)
364
- ActiveRecord::Base.connection.add_foreign_key(AcademicYear.table_name, :period_type_id, PeriodType.table_name, :id)
365
- ActiveRecord::Base.connection.add_foreign_key(Profile.table_name, :publishing_house_id, PublishingHouse.table_name, :id)
366
- end
367
-
368
- it "should not raise an error" do
369
- expect { dump_schema }.to_not raise_error
370
- end
371
-
372
- it "should dump each constraint after both related tables were defined" do
373
- expect(dump_schema.scan(%r{add_foreign_key}m).count).to eq 1
374
- expect(dump_schema).to match(%r{create_table "schools".*add_foreign_key\s+"schools".*\["default_grade_system_id"\], "grade_systems", \["id"\]}m)
375
- expect(dump_schema).to match(%r{create_table "grade_systems".*add_foreign_key\s+"schools".*\["default_grade_system_id"\], "grade_systems", \["id"\]}m)
376
- end
117
+ expect(dump_schema).to match(%r{create_table "comments"(.|\n)*first_comment_id.*foreign_key.*comments})
118
+ expect(dump_schema).to match(%r{create_table "posts"(.|\n)*first_post_id.*foreign_key.*posts})
119
+ expect(dump_schema).to match(%r{create_table "posts"(.|\n)*add_foreign_key.*posts.*post_id})
120
+ expect(dump_schema).to match(%r{create_table "users"(.|\n)*add_foreign_key.*users.*commenter_id})
121
+ expect(dump_schema).to match(%r{create_table "users"(.|\n)*foreign_key.*users.*user_id})
377
122
  end
378
- end
379
-
380
- context 'with enum', :postgresql => :only do
381
- let(:connection) { ActiveRecord::Base.connection }
382
123
 
383
- it 'should include enum' do
384
- begin
385
- connection.execute "CREATE TYPE color AS ENUM ('red', 'green', 'blue')"
386
- expect(dump_schema).to match(%r{create_enum "color", "red", "green", "blue"})
387
- ensure
388
- connection.execute "DROP TYPE color"
389
- end
390
- end
391
-
392
- it 'should include enum with schema' do
393
- begin
394
- connection.execute "CREATE SCHEMA cmyk; CREATE TYPE cmyk.color AS ENUM ('cyan', 'magenta', 'yellow', 'black')"
395
- expect(dump_schema).to match(%r{create_enum "color", "cyan", "magenta", "yellow", "black", :schema => "cmyk"})
396
- ensure
397
- connection.execute "DROP SCHEMA cmyk CASCADE"
398
- end
124
+ it "should dump comments for delayed constraint definitions" do
125
+ expect(dump_schema).to match(%r{"post_id".*# foreign key references "posts"})
126
+ expect(dump_schema).to match(%r{"commenter_id".*# foreign key references "users"})
127
+ expect(dump_schema).to match(%r{"user_id".*# foreign key references "users"})
399
128
  end
400
129
  end
401
130
 
@@ -404,19 +133,6 @@ describe "Schema dump" do
404
133
  Regexp.new(Regexp.escape(string))
405
134
  end
406
135
 
407
- def with_additional_column(model, column_name, column_type, options)
408
- table_columns = model.columns.reject{|column| column.name == 'id'}
409
- ActiveRecord::Migration.suppress_messages do
410
- ActiveRecord::Migration.create_table model.table_name, :force => true do |t|
411
- table_columns.each do |column|
412
- t.column column.name, column.type, :default => column.default
413
- end
414
- t.column column_name, column_type, options
415
- end
416
- end
417
- yield
418
- end
419
-
420
136
  def with_foreign_key(model, columns, referenced_table_name, referenced_columns, options = {}, &block)
421
137
  with_foreign_keys(model, [[columns, referenced_table_name, referenced_columns, options]], &block)
422
138
  end
@@ -429,7 +145,7 @@ describe "Schema dump" do
429
145
  t.column column.name, column.type
430
146
  end
431
147
  columnsets.each do |columns, referenced_table_name, referenced_columns, options|
432
- t.foreign_key columns, referenced_table_name, referenced_columns, options || {}
148
+ t.foreign_key columns, referenced_table_name, (options||{}).merge(column: referenced_columns)
433
149
  end
434
150
  end
435
151
  end
@@ -447,30 +163,9 @@ describe "Schema dump" do
447
163
  end
448
164
  end
449
165
 
450
- def with_index(model, columns, options = {})
451
- ActiveRecord::Migration.suppress_messages do
452
- ActiveRecord::Migration.add_index(model.table_name, columns, options)
453
- end
454
- model.reset_column_information
455
- begin
456
- yield
457
- ensure
458
- ActiveRecord::Migration.suppress_messages do
459
- ActiveRecord::Migration.remove_index(model.table_name, :name => determine_index_name(model, columns, options))
460
- end
461
- end
462
- end
463
-
464
- def determine_index_name(model, columns, options)
465
- name = columns[:name] if columns.is_a?(Hash)
466
- name ||= options[:name]
467
- name ||= model.indexes.detect { |index| index.table == model.table_name.to_s && index.columns.sort == Array(columns).collect(&:to_s).sort }.name
468
- name
469
- end
470
-
471
166
  def determine_foreign_key_name(model, columns, options)
472
167
  name = options[:name]
473
- name ||= model.foreign_keys.detect { |fk| fk.table_name == model.table_name.to_s && fk.column_names == Array(columns).collect(&:to_s) }.name
168
+ name ||= model.foreign_keys.detect { |fk| fk.from_table == model.table_name.to_s && Array.wrap(fk.column) == Array.wrap(columns).collect(&:to_s) }.name
474
169
  end
475
170
 
476
171
  def dump_schema(opts={})