schema_plus 0.1.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.
- 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,23 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
connection.tables.each do |table| drop_table table end
|
3
|
+
|
4
|
+
create_table :users, :force => true do |t|
|
5
|
+
t.string :login, :index => { :unique => true }
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :members, :force => true do |t|
|
9
|
+
t.string :login
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :comments, :force => true do |t|
|
13
|
+
t.string :content
|
14
|
+
t.integer :user
|
15
|
+
t.integer :user_id
|
16
|
+
t.foreign_key :user_id, :users, :id
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :posts, :force => true do |t|
|
20
|
+
t.string :content
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
connection.tables.each do |table| drop_table table end
|
3
|
+
|
4
|
+
create_table :users, :force => true do |t|
|
5
|
+
t.string :login
|
6
|
+
t.datetime :deleted_at
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :posts, :force => true do |t|
|
10
|
+
t.text :body
|
11
|
+
t.integer :user_id
|
12
|
+
t.integer :author_id
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :comments, :force => true do |t|
|
16
|
+
t.text :body
|
17
|
+
t.integer :post_id
|
18
|
+
t.foreign_key :post_id, :posts, :id
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
require 'models/post'
|
5
|
+
|
6
|
+
describe "Schema dump (core)" do
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
load_core_schema
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:dump) do
|
13
|
+
stream = StringIO.new
|
14
|
+
ActiveRecord::SchemaDumper.ignore_tables = %w[users comments]
|
15
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
16
|
+
stream.string
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should include foreign_key definition" do
|
20
|
+
with_foreign_key Post, :user_id, :users, :id do
|
21
|
+
dump.should match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"]}))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should include foreign_key options" do
|
26
|
+
with_foreign_key Post, :user_id, :users, :id, :on_update => :cascade, :on_delete => :set_null do
|
27
|
+
dump.should match(to_regexp(%q{t.foreign_key ["user_id"], "users", ["id"], :on_update => :cascade, :on_delete => :set_null}))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should include index definition" do
|
32
|
+
with_index Post, :user_id do
|
33
|
+
dump.should match(to_regexp(%q{t.index ["user_id"]}))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should include index name" do
|
38
|
+
with_index Post, :user_id, :name => "posts_user_id_index" do
|
39
|
+
dump.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index"}))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should define unique index" do
|
44
|
+
with_index Post, :user_id, :name => "posts_user_id_index", :unique => true do
|
45
|
+
dump.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :unique => true}))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if SchemaPlusHelpers.postgresql?
|
50
|
+
|
51
|
+
it "should define case insensitive index" do
|
52
|
+
with_index Post, :name => "posts_user_body_index", :expression => "USING btree (LOWER(body))" do
|
53
|
+
dump.should match(to_regexp(%q{t.index ["body"], :name => "posts_user_body_index", :case_sensitive => false}))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should define conditions" do
|
58
|
+
with_index Post, :user_id, :name => "posts_user_id_index", :conditions => "user_id IS NOT NULL" do
|
59
|
+
dump.should match(to_regexp(%q{t.index ["user_id"], :name => "posts_user_id_index", :conditions => "(user_id IS NOT NULL)"}))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should define expression" do
|
64
|
+
with_index Post, :name => "posts_freaky_index", :expression => "USING hash (least(id, user_id))" do
|
65
|
+
dump.should match(to_regexp(%q{t.index :name => "posts_freaky_index", :kind => "hash", :expression => "LEAST(id, user_id)"}))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should define kind" do
|
70
|
+
with_index Post, :name => "posts_body_index", :expression => "USING hash (body)" do
|
71
|
+
dump.should match(to_regexp(%q{t.index ["body"], :name => "posts_body_index", :kind => "hash"}))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
def to_regexp(string)
|
79
|
+
Regexp.new(Regexp.escape(string))
|
80
|
+
end
|
81
|
+
|
82
|
+
def with_foreign_key(model, columns, referenced_table_name, referenced_columns, options = {})
|
83
|
+
table_columns = model.columns.reject{|column| column.name == 'id'}
|
84
|
+
ActiveRecord::Migration.suppress_messages do
|
85
|
+
ActiveRecord::Migration.create_table model.table_name, :force => true do |t|
|
86
|
+
table_columns.each do |column|
|
87
|
+
t.column column.name, column.type
|
88
|
+
end
|
89
|
+
t.foreign_key columns, referenced_table_name, referenced_columns, options
|
90
|
+
end
|
91
|
+
end
|
92
|
+
model.reset_column_information
|
93
|
+
begin
|
94
|
+
yield
|
95
|
+
ensure
|
96
|
+
ActiveRecord::Migration.suppress_messages do
|
97
|
+
ActiveRecord::Migration.create_table model.table_name, :force => true do |t|
|
98
|
+
table_columns.each do |column|
|
99
|
+
t.column column.name, column.type
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def with_index(model, columns, options = {})
|
107
|
+
ActiveRecord::Migration.suppress_messages do
|
108
|
+
ActiveRecord::Migration.add_index(model.table_name, columns, options)
|
109
|
+
end
|
110
|
+
model.reset_column_information
|
111
|
+
begin
|
112
|
+
yield
|
113
|
+
ensure
|
114
|
+
ActiveRecord::Migration.suppress_messages do
|
115
|
+
ActiveRecord::Migration.remove_index(model.table_name, :name => determine_index_name(model, columns, options))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def determine_index_name(model, columns, options)
|
121
|
+
name = columns[:name] if columns.is_a?(Hash)
|
122
|
+
name ||= options[:name]
|
123
|
+
name ||= model.indexes.detect { |index| index.table == model.table_name.to_s && index.columns == Array(columns).collect(&:to_s) }.name
|
124
|
+
name
|
125
|
+
end
|
126
|
+
|
127
|
+
def determine_foreign_key_name(model, columns, options)
|
128
|
+
name = options[:name]
|
129
|
+
name ||= model.foreign_keys.detect { |fk| fk.table_name == model.table_name.to_s && fk.column_names == Array(columns).collect(&:to_s) }.name
|
130
|
+
end
|
131
|
+
|
132
|
+
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/schema_spec.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe ActiveRecord::Schema do
|
4
|
+
|
5
|
+
let(:schema) { ActiveRecord::Schema }
|
6
|
+
|
7
|
+
let(:connection) { ActiveRecord::Base.connection }
|
8
|
+
|
9
|
+
context "defining with auto_index and auto_create" do
|
10
|
+
|
11
|
+
around(:each) do |example|
|
12
|
+
with_auto_index do
|
13
|
+
with_auto_create do
|
14
|
+
example.run
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should pass" do
|
20
|
+
expect { define_schema }.should_not raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should create only explicity added indexes" do
|
24
|
+
define_schema
|
25
|
+
connection.tables.collect { |table| connection.indexes(table) }.flatten.should have(1).item
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should create only explicity added foriegn keys" do
|
29
|
+
define_schema
|
30
|
+
connection.tables.collect { |table| connection.foreign_keys(table) }.flatten.should have(1).item
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def define_schema
|
37
|
+
ActiveRecord::Migration.suppress_messages do
|
38
|
+
schema.define do
|
39
|
+
connection.tables.each do |table| drop_table table end
|
40
|
+
|
41
|
+
create_table :users, :force => true do
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table :posts, :force => true do |t|
|
45
|
+
t.integer :user_id, :references => :users, :index => true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def with_auto_index(value = true)
|
52
|
+
old_value = SchemaPlus.config.foreign_keys.auto_index
|
53
|
+
SchemaPlus.config.foreign_keys.auto_index = value
|
54
|
+
begin
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
SchemaPlus.config.foreign_keys.auto_index = old_value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_auto_create(value = true)
|
62
|
+
old_value = SchemaPlus.config.foreign_keys.auto_create
|
63
|
+
SchemaPlus.config.foreign_keys.auto_create = value
|
64
|
+
begin
|
65
|
+
yield
|
66
|
+
ensure
|
67
|
+
SchemaPlus.config.foreign_keys.auto_create = old_value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
if RUBY_VERSION > "1.9"
|
2
|
+
require 'simplecov'
|
3
|
+
require 'simplecov-gem-adapter'
|
4
|
+
SimpleCov.start "gem"
|
5
|
+
end
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
9
|
+
|
10
|
+
require 'rspec'
|
11
|
+
require 'active_record'
|
12
|
+
require 'schema_plus'
|
13
|
+
require 'connection'
|
14
|
+
|
15
|
+
SchemaPlus.insert
|
16
|
+
|
17
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include(SchemaPlusMatchers)
|
21
|
+
config.include(SchemaPlusHelpers)
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_schema(name)
|
25
|
+
ActiveRecord::Migration.suppress_messages do
|
26
|
+
eval(File.read(File.join(File.dirname(__FILE__), 'schema', name)))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_core_schema
|
31
|
+
SchemaPlus.setup do |config|
|
32
|
+
config.foreign_keys.auto_create = false;
|
33
|
+
end
|
34
|
+
load_schema('core_schema.rb')
|
35
|
+
load 'models/user.rb'
|
36
|
+
load 'models/post.rb'
|
37
|
+
load 'models/comment.rb'
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_auto_schema
|
41
|
+
SchemaPlus.setup do |config|
|
42
|
+
config.foreign_keys.auto_create = true;
|
43
|
+
end
|
44
|
+
load_schema('auto_schema.rb')
|
45
|
+
load 'models/user.rb'
|
46
|
+
load 'models/post.rb'
|
47
|
+
load 'models/comment.rb'
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_all_models
|
51
|
+
ObjectSpace.each_object(Class) do |c|
|
52
|
+
next unless c.ancestors.include? ActiveRecord::Base
|
53
|
+
next if c == ActiveRecord::Base
|
54
|
+
next if c.name.blank?
|
55
|
+
ActiveSupport::Dependencies.remove_constant c.name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
SimpleCov.command_name ActiveRecord::Base.connection.adapter_name if defined? SimpleCov
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# ported from rspec-rails
|
2
|
+
# There is no reason to install whole gem as we
|
3
|
+
# need only that tiny helper
|
4
|
+
module ::ActiveModel::Validations
|
5
|
+
|
6
|
+
def error_on(attribute)
|
7
|
+
self.valid?
|
8
|
+
[self.errors[attribute]].flatten.compact
|
9
|
+
end
|
10
|
+
|
11
|
+
alias :errors_on :error_on
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SchemaPlusHelpers
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def mysql?
|
5
|
+
ActiveRecord::Base.connection.adapter_name =~ /^mysql/i
|
6
|
+
end
|
7
|
+
|
8
|
+
def postgresql?
|
9
|
+
ActiveRecord::Base.connection.adapter_name =~ /^postgresql/i
|
10
|
+
end
|
11
|
+
|
12
|
+
def sqlite3?
|
13
|
+
ActiveRecord::Base.connection.adapter_name =~ /^sqlite/i
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module SchemaPlusMatchers
|
2
|
+
|
3
|
+
class HaveIndex
|
4
|
+
|
5
|
+
def initialize(expectation, options = {})
|
6
|
+
set_required_columns(expectation, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(model)
|
10
|
+
@model = model
|
11
|
+
@model.indexes.any? do |index|
|
12
|
+
index.columns.to_set == @required_columns &&
|
13
|
+
(@unique ? index.unique : true) &&
|
14
|
+
(@name ? index.name == @name.to_s : true)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure_message_for_should(should_not = false)
|
19
|
+
invert = should_not ? "not to" : ""
|
20
|
+
"Expected #{@model.table_name} to #{invert} contain index on #{@required_columns.entries.inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def failure_message_for_should_not
|
24
|
+
failure_message_for_should(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def on(expectation)
|
28
|
+
set_required_columns(expectation)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def set_required_columns(expectation, options = {})
|
34
|
+
@required_columns = Array(expectation).collect(&:to_s).to_set
|
35
|
+
@unique = options.delete(:unique)
|
36
|
+
@name = options.delete(:name)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def have_index(*expectation)
|
42
|
+
options = expectation.extract_options!
|
43
|
+
HaveIndex.new(expectation, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def have_unique_index(*expectation)
|
47
|
+
options = expectation.extract_options!
|
48
|
+
options[:unique] = true
|
49
|
+
HaveIndex.new(expectation, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module SchemaPlusMatchers
|
2
|
+
|
3
|
+
class Reference
|
4
|
+
def initialize(expected)
|
5
|
+
@column_names = nil
|
6
|
+
unless expected.empty?
|
7
|
+
@references_column_names = Array(expected).collect(&:to_s)
|
8
|
+
@references_table_name = @references_column_names.shift
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(model)
|
13
|
+
@model = model
|
14
|
+
if @references_table_name
|
15
|
+
@result = @model.foreign_keys.select do |fk|
|
16
|
+
fk.references_table_name == @references_table_name &&
|
17
|
+
@references_column_names.empty? ? true : fk.references_column_names == @references_column_names
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@result = @model.foreign_keys
|
21
|
+
end
|
22
|
+
if @column_names
|
23
|
+
@result.any? do |fk|
|
24
|
+
fk.column_names == @column_names &&
|
25
|
+
(@on_update ? fk.on_update == @on_update : true) &&
|
26
|
+
(@on_delete ? fk.on_delete == @on_delete : true)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
!@result.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message_for_should(should_not = false)
|
34
|
+
target_column_names = @column_names.present? ? "(#{@column_names.join(', ')})" : ""
|
35
|
+
destinantion_column_names = @references_table_name ? "#{@references_table_name}(#{@references_column_names.join(', ')})" : "anything"
|
36
|
+
invert = should_not ? 'not' : ''
|
37
|
+
"Expected #{@model.table_name}#{target_column_names} to #{invert} reference #{destinantion_column_names}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure_message_for_should_not
|
41
|
+
failure_message_for_should(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on(*column_names)
|
45
|
+
@column_names = column_names.collect(&:to_s)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_update(action)
|
50
|
+
@on_update = action
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_delete(action)
|
55
|
+
@on_delete = action
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def reference(*expect)
|
62
|
+
Reference.new(expect)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|