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
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
.*.sw?
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
rdoc
|
19
|
+
pkg
|
20
|
+
|
21
|
+
## PROJECT::SPECIFIC
|
22
|
+
.rvmrc
|
23
|
+
*.log
|
24
|
+
*.sqlite3
|
25
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2006 RedHill Consulting, Pty. Ltd.
|
2
|
+
Copyright (c) 2009 Michal Lomnicki & Ronen Barzel
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
Except as contained in this notice, the name(s) of the above copyright
|
16
|
+
holders shall not be used in advertising or otherwise to promote the sale,
|
17
|
+
use or other dealings in this Software without prior written authorization.
|
18
|
+
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
22
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
23
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
24
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
25
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
= SchemaPlus
|
2
|
+
|
3
|
+
== Overview
|
4
|
+
|
5
|
+
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.
|
6
|
+
|
7
|
+
For added rails DRYness see also the gems {+schema_associations+}[http://rubygems.org/gems/schema_associations] and {+schema_validations+}[http://rubygems.org/gems/schema_associations] <b>IMPORTANT PRERELEASE NOTE: <i>Those are not yet separate gems, they are currently bundled in here. They're not documented yet though.</i></b>
|
8
|
+
|
9
|
+
== Compatibility
|
10
|
+
|
11
|
+
SchemaPlus supports all combinations of:
|
12
|
+
* rails 3.0 or 3.1
|
13
|
+
* MRI ruby 1.8.7 or 1.9.2
|
14
|
+
* Postgres, MySQL (using mysql or mysql2 gem), or Sqlite3
|
15
|
+
|
16
|
+
== Installation
|
17
|
+
|
18
|
+
Install from http://rubygems.org via
|
19
|
+
|
20
|
+
$ gem install "schema_plus"
|
21
|
+
|
22
|
+
or in a Gemfile
|
23
|
+
|
24
|
+
gem "schema_plus"
|
25
|
+
|
26
|
+
== Features
|
27
|
+
|
28
|
+
Here some examples that show off the high points. For full details see the RDoc documentation.
|
29
|
+
|
30
|
+
=== Indexes
|
31
|
+
|
32
|
+
With standard rails migrations, you specify indexes separately from the table definition:
|
33
|
+
|
34
|
+
# Standard Rails approach...
|
35
|
+
create_table :parts do |t|
|
36
|
+
t.string :name
|
37
|
+
t.string :product_code
|
38
|
+
end
|
39
|
+
|
40
|
+
add_index :parts, :name # index repeats table and column names and is defined separately
|
41
|
+
add_index :parts, :product_code, :unique => true
|
42
|
+
|
43
|
+
But with SchemaPlus rather than specify your outside your table definition you can specify your indexes when you define each column:
|
44
|
+
|
45
|
+
# More DRY way...
|
46
|
+
create_table :parts do |t|
|
47
|
+
t.string :name, :index => true
|
48
|
+
t.string :product_code, :index => :unique
|
49
|
+
end
|
50
|
+
|
51
|
+
Options can be provided index using a hash, for example:
|
52
|
+
|
53
|
+
t.string :product_code, :index => { :unique => true, :name => "my_index_name" }
|
54
|
+
|
55
|
+
You can also create multi-column indexes, for example:
|
56
|
+
|
57
|
+
t.string :first_name
|
58
|
+
t.string :last_name, :index => { :with => :first_name }
|
59
|
+
|
60
|
+
t.string :country_code
|
61
|
+
t.string :area_code
|
62
|
+
t.string :local_number :index => { :width => [:country_code, :area_code], :unique => true }
|
63
|
+
|
64
|
+
If you're using Postgresql, SchemaPlus provides support for conditions, expressions, index methods, and case-insensitive indexes; see doc at SchemaPlus::ActiveRecord::ConnectionAdapters::PostgresqlAdapter and SchemaPlus::ActiveRecord::ConnectionAdapters::IndexDefinition
|
65
|
+
|
66
|
+
And when you query column information using ActiveRecord::Base#columns, SchemaPlus analogously provides index information relevant to each column: which indexes reference the column, whether the column must be unique, etc. See doc at SchemaPlus::ActiveRecord::ConnectionAdapters::Column
|
67
|
+
|
68
|
+
=== Foreign Key Constraints
|
69
|
+
|
70
|
+
SchemaPlus adds support for foreign key constraints. In fact, for the
|
71
|
+
common convention that you name a column with suffix +_id+ to indicate that
|
72
|
+
it's a foreign key, SchemaPlus automatically defines the appropriate
|
73
|
+
constraint.
|
74
|
+
|
75
|
+
You can explicitly specify foreign key constraints, or override the
|
76
|
+
automatic ones, using the +:references+ option to specify the table
|
77
|
+
name (and optionally that table's key column name, if it's not +id+).
|
78
|
+
|
79
|
+
Here are some examples:
|
80
|
+
|
81
|
+
t.integer :author_id # automatically references table 'authors', key id
|
82
|
+
t.integer :parent_id # special name parent_id automatically references its own table (for tree nodes)
|
83
|
+
t.integer :author, :references => :authors # non-conventional column name needs :references for a constraint
|
84
|
+
t.integer :author_id, :refences => :authors # same as automatic behavior
|
85
|
+
t.integer :author_id, :refences => [:authors, :id] # same as automatic behavior
|
86
|
+
t.integer :author_id, :references => :people # override table name
|
87
|
+
t.integer :author_id, :references => [:people, :ssn] # override table name and key
|
88
|
+
t.integer :author_id, :referencs => nil # don't create a constraint
|
89
|
+
|
90
|
+
|
91
|
+
You can also modify the behavior using +:on_delete+, +:on_update+, and +:deferrable+
|
92
|
+
|
93
|
+
t.integer :author_id, :on_delete => :cascade
|
94
|
+
|
95
|
+
The foreign key behavior can be configured globally (see Config) or per-table (see create_table).
|
96
|
+
|
97
|
+
To examine your foreign key constraints, connection.foreign_keys returns a
|
98
|
+
list of foreign key constraints defined for a given table, and
|
99
|
+
connection.reverse_foreign_keys returns a list of foreign key constraints
|
100
|
+
that reference a given table. See SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.
|
101
|
+
|
102
|
+
=== Views
|
103
|
+
|
104
|
+
SchemaPlus provides support for creating and dropping views. For example:
|
105
|
+
|
106
|
+
create_view :uncommented_posts, "SELECT * FROM posts LEFT OUTER JOIN comments ON comments.post_id = posts.id WHERE comments.id IS NULL"
|
107
|
+
drop_view :uncommented_posts
|
108
|
+
|
109
|
+
ActiveRecord works with views the same as with ordinary tables. That is, for the above view you can define
|
110
|
+
|
111
|
+
class UncommentedPosts < ActiveRecord::Base
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
== History
|
116
|
+
|
117
|
+
* SchemaPlus is derived from several "Red Hill On Rails" plugins
|
118
|
+
originally created by harukizaemon (https://github.com/harukizaemon)
|
119
|
+
with later contributions from
|
120
|
+
* Michał Łomnicki (https://github.com/mlomnicki)
|
121
|
+
* François Beausoleil (https://github.com/francois)
|
122
|
+
* Greg Barnett (https://github.com/greg-barnett)
|
123
|
+
* Ronen Barzel (https://github.com/ronen)
|
124
|
+
|
125
|
+
* SchemaPlus was created in 2011 by Michal Lomnicki and Ronen Barzel
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
== Testing
|
130
|
+
|
131
|
+
SchemaPlus is tested using rspec. To run the tests, after you've forked & cloned: Make sure you have Postgresql and MySQL running. Create database user "schema_plus" with permissions for database "schema_plus_unittest". Then:
|
132
|
+
|
133
|
+
$ cd schema_plus
|
134
|
+
$ bundle install
|
135
|
+
$ rake postgresql:build_databases
|
136
|
+
$ rake mysql:build_databases
|
137
|
+
$ rake postgresql:spec # to run postgresql tests only
|
138
|
+
$ rake mysql:spec # to run mysql tests only
|
139
|
+
$ rake mysql2:spec # to run mysql2 tests only
|
140
|
+
$ rake sqlite3:spec # to run sqlite3 tests only
|
141
|
+
$ rake spec # run all tests
|
142
|
+
|
143
|
+
If you're running ruby 1.9.2, code coverage results will be in coverage/index.html -- it should be at 100% coverage.
|
144
|
+
|
145
|
+
== License
|
146
|
+
|
147
|
+
This plugin is released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
%w[postgresql mysql mysql2 sqlite3].each do |adapter|
|
6
|
+
namespace adapter do
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
+
spec.rspec_opts = "-Ispec/connections/#{adapter}"
|
9
|
+
spec.fail_on_error = false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Run postgresql, mysql2 and sqlite3 tests'
|
15
|
+
task :spec do
|
16
|
+
%w[postgresql mysql mysql2 sqlite3].each do |adapter|
|
17
|
+
Rake::Task["#{adapter}:spec"].invoke
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :spec
|
22
|
+
|
23
|
+
require 'rake/rdoctask'
|
24
|
+
Rake::RDocTask.new do |rdoc|
|
25
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
26
|
+
|
27
|
+
rdoc.rdoc_dir = 'rdoc'
|
28
|
+
rdoc.title = "schema_plus #{version}"
|
29
|
+
rdoc.rdoc_files.include('README*')
|
30
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
31
|
+
end
|
32
|
+
|
33
|
+
namespace :postgresql do
|
34
|
+
desc 'Build the PostgreSQL test databases'
|
35
|
+
task :build_databases do
|
36
|
+
%x( createdb -E UTF8 schema_plus_unittest )
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Drop the PostgreSQL test databases'
|
40
|
+
task :drop_databases do
|
41
|
+
%x( dropdb schema_plus_unittest )
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Rebuild the PostgreSQL test databases'
|
45
|
+
task :rebuild_databases => [:drop_databases, :build_databases]
|
46
|
+
end
|
47
|
+
|
48
|
+
task :build_postgresql_databases => 'postgresql:build_databases'
|
49
|
+
task :drop_postgresql_databases => 'postgresql:drop_databases'
|
50
|
+
task :rebuild_postgresql_databases => 'postgresql:rebuild_databases'
|
51
|
+
|
52
|
+
MYSQL_DB_USER = 'schema_plus'
|
53
|
+
namespace :mysql do
|
54
|
+
desc 'Build the MySQL test databases'
|
55
|
+
task :build_databases do
|
56
|
+
%x( echo "create DATABASE schema_plus_unittest DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{MYSQL_DB_USER})
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'Drop the MySQL test databases'
|
60
|
+
task :drop_databases do
|
61
|
+
%x( mysqladmin --user=#{MYSQL_DB_USER} -f drop schema_plus_unittest )
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Rebuild the MySQL test databases'
|
65
|
+
task :rebuild_databases => [:drop_databases, :build_databases]
|
66
|
+
end
|
67
|
+
|
68
|
+
task :build_mysql_databases => 'mysql:build_databases'
|
69
|
+
task :drop_mysql_databases => 'mysql:drop_databases'
|
70
|
+
task :rebuild_mysql_databases => 'mysql:rebuild_databases'
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'schema_plus' unless defined?(SchemaPlus)
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module SchemaPlus
|
4
|
+
module ActiveRecord
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
module Relation #:nodoc:
|
8
|
+
def self.included(base)
|
9
|
+
base.alias_method_chain :initialize, :schema_plus
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize_with_schema_plus(klass, *args)
|
13
|
+
klass.send :_load_schema_plus_associations
|
14
|
+
initialize_without_schema_plus(klass, *args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.extended(base) #:nodoc:
|
19
|
+
class << base
|
20
|
+
alias_method_chain :reflect_on_association, :schema_plus
|
21
|
+
alias_method_chain :reflect_on_all_associations, :schema_plus
|
22
|
+
end
|
23
|
+
::ActiveRecord::Relation.send :include, Relation
|
24
|
+
end
|
25
|
+
|
26
|
+
def reflect_on_association_with_schema_plus(*args) #:nodoc:
|
27
|
+
_load_schema_plus_associations
|
28
|
+
reflect_on_association_without_schema_plus(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reflect_on_all_associations_with_schema_plus(*args) #:nodoc:
|
32
|
+
_load_schema_plus_associations
|
33
|
+
reflect_on_all_associations_without_schema_plus(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def define_attribute_methods(*args) #:nodoc:
|
37
|
+
super
|
38
|
+
_load_schema_plus_associations
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def _load_schema_plus_associations #:nodoc:
|
44
|
+
return if @schema_plus_associations_loaded
|
45
|
+
@schema_plus_associations_loaded = true
|
46
|
+
return unless schema_plus_config.associations.auto_create?
|
47
|
+
|
48
|
+
reverse_foreign_keys.each do | foreign_key |
|
49
|
+
if foreign_key.table_name =~ /^#{table_name}_(.*)$/ || foreign_key.table_name =~ /^(.*)_#{table_name}$/
|
50
|
+
other_table = $1
|
51
|
+
if other_table == other_table.pluralize and connection.columns(foreign_key.table_name).any?{|col| col.name == "#{other_table.singularize}_id"}
|
52
|
+
_define_association(:has_and_belongs_to_many, foreign_key, other_table)
|
53
|
+
else
|
54
|
+
_define_association(:has_one_or_many, foreign_key)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
_define_association(:has_one_or_many, foreign_key)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
foreign_keys.each do | foreign_key |
|
62
|
+
_define_association(:belongs_to, foreign_key)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def _define_association(macro, fk, referencing_table_name = nil) #:nodoc:
|
67
|
+
return unless fk.column_names.size == 1
|
68
|
+
|
69
|
+
referencing_table_name ||= fk.table_name
|
70
|
+
|
71
|
+
column_name = fk.column_names.first
|
72
|
+
reference_name = column_name.sub(/_id$/, '')
|
73
|
+
references_name = fk.references_table_name.singularize
|
74
|
+
referencing_name = referencing_table_name.singularize
|
75
|
+
|
76
|
+
references_class_name = references_name.classify
|
77
|
+
referencing_class_name = referencing_name.classify
|
78
|
+
|
79
|
+
references_concise = _concise_name(references_name, referencing_name)
|
80
|
+
referencing_concise = _concise_name(referencing_name, references_name)
|
81
|
+
|
82
|
+
case reference_name
|
83
|
+
when 'parent'
|
84
|
+
belongs_to = 'parent'
|
85
|
+
belongs_to_concise = 'parent'
|
86
|
+
|
87
|
+
has_one = 'child'
|
88
|
+
has_one_concise = 'child'
|
89
|
+
|
90
|
+
has_many = 'children'
|
91
|
+
has_many_concise = 'children'
|
92
|
+
|
93
|
+
when references_name
|
94
|
+
belongs_to = references_name
|
95
|
+
belongs_to_concise = references_concise
|
96
|
+
|
97
|
+
has_one = referencing_name
|
98
|
+
has_one_concise = referencing_concise
|
99
|
+
|
100
|
+
has_many = referencing_name.pluralize
|
101
|
+
has_many_concise = referencing_concise.pluralize
|
102
|
+
|
103
|
+
when /(.*)_#{references_name}$/, /(.*)_#{references_concise}$/
|
104
|
+
label = $1
|
105
|
+
belongs_to = "#{label}_#{references_name}"
|
106
|
+
belongs_to_concise = "#{label}_#{references_concise}"
|
107
|
+
|
108
|
+
has_one = "#{referencing_name}_as_#{label}"
|
109
|
+
has_one_concise = "#{referencing_concise}_as_#{label}"
|
110
|
+
|
111
|
+
has_many = "#{referencing_name.pluralize}_as_#{label}"
|
112
|
+
has_many_concise = "#{referencing_concise.pluralize}_as_#{label}"
|
113
|
+
|
114
|
+
when /^#{references_name}_(.*)$/, /^#{references_concise}_(.*)$/
|
115
|
+
label = $1
|
116
|
+
belongs_to = "#{references_name}_#{label}"
|
117
|
+
belongs_to_concise = "#{references_concise}_#{label}"
|
118
|
+
|
119
|
+
has_one = "#{referencing_name}_as_#{label}"
|
120
|
+
has_one_concise = "#{referencing_concise}_as_#{label}"
|
121
|
+
|
122
|
+
has_many = "#{referencing_name.pluralize}_as_#{label}"
|
123
|
+
has_many_concise = "#{referencing_concise.pluralize}_as_#{label}"
|
124
|
+
|
125
|
+
else
|
126
|
+
belongs_to = reference_name
|
127
|
+
belongs_to_concise = reference_name
|
128
|
+
|
129
|
+
has_one = "#{referencing_name}_as_#{reference_name}"
|
130
|
+
has_one_concise = "#{referencing_concise}_as_#{reference_name}"
|
131
|
+
|
132
|
+
has_many = "#{referencing_name.pluralize}_as_#{reference_name}"
|
133
|
+
has_many_concise = "#{referencing_concise.pluralize}_as_#{reference_name}"
|
134
|
+
end
|
135
|
+
|
136
|
+
case macro
|
137
|
+
when :has_and_belongs_to_many
|
138
|
+
name = has_many
|
139
|
+
name_concise = has_many_concise
|
140
|
+
opts = {:class_name => referencing_class_name, :join_table => fk.table_name, :foreign_key => column_name}
|
141
|
+
when :belongs_to
|
142
|
+
name = belongs_to
|
143
|
+
name_concise = belongs_to_concise
|
144
|
+
opts = {:class_name => references_class_name, :foreign_key => column_name}
|
145
|
+
when :has_one_or_many
|
146
|
+
opts = {:class_name => referencing_class_name, :foreign_key => column_name}
|
147
|
+
# use connection.indexes and connection.colums rather than class
|
148
|
+
# methods of the referencing class because using the class
|
149
|
+
# methods would require getting the class -- which might trigger
|
150
|
+
# an autoload which could start some recursion making things much
|
151
|
+
# harder to debug.
|
152
|
+
if connection.indexes(referencing_table_name, "#{referencing_table_name} Indexes").any?{|index| index.unique && index.columns == [column_name]}
|
153
|
+
macro = :has_one
|
154
|
+
name = has_one
|
155
|
+
name_concise = has_one_concise
|
156
|
+
else
|
157
|
+
macro = :has_many
|
158
|
+
name = has_many
|
159
|
+
name_concise = has_many_concise
|
160
|
+
if connection.columns(referencing_table_name, "#{referencing_table_name} Columns").any?{ |col| col.name == 'position' }
|
161
|
+
opts[:order] = :position
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
name = name_concise if _use_concise_name?
|
166
|
+
name = name.to_sym
|
167
|
+
if (_filter_association(macro, name) && !_method_exists?(name))
|
168
|
+
logger.info "SchemaPlus associations: #{self.name || self.table_name.classify}.#{macro} #{name.inspect}, #{opts.inspect[1...-1]}"
|
169
|
+
send macro, name, opts.dup
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def _concise_name(string, other) #:nodoc:
|
174
|
+
case
|
175
|
+
when string =~ /^#{other}_(.*)$/ then $1
|
176
|
+
when string =~ /(.*)_#{other}$/ then $1
|
177
|
+
when leader = _common_leader(string,other) then string[leader.length, string.length-leader.length]
|
178
|
+
else string
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def _common_leader(string, other) #:nodoc:
|
183
|
+
leader = nil
|
184
|
+
other.split('_').each do |part|
|
185
|
+
test = "#{leader}#{part}_"
|
186
|
+
break unless string.start_with? test
|
187
|
+
leader = test
|
188
|
+
end
|
189
|
+
return leader
|
190
|
+
end
|
191
|
+
|
192
|
+
def _use_concise_name? #:nodoc:
|
193
|
+
schema_plus_config.associations.concise_names?
|
194
|
+
end
|
195
|
+
|
196
|
+
def _filter_association(macro, name) #:nodoc:
|
197
|
+
config = schema_plus_config.associations
|
198
|
+
return false if config.only and not Array.wrap(config.only).include?(name)
|
199
|
+
return false if config.except and Array.wrap(config.except).include?(name)
|
200
|
+
return false if config.only_type and not Array.wrap(config.only_type).include?(macro)
|
201
|
+
return false if config.except_type and Array.wrap(config.except_type).include?(macro)
|
202
|
+
return true
|
203
|
+
end
|
204
|
+
|
205
|
+
def _method_exists?(name) #:nodoc:
|
206
|
+
method_defined?(name) || private_method_defined?(name) and not (name == :type && [Object, Kernel].include?(instance_method(:type).owner))
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|