theaboutbox-foreigner 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.swp
2
+ pkg/*
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem "rspec", '<= 2.0.0.beta.18'
4
+ gem "mysql"
5
+ gem "pg"
6
+ gem "sqlite3-ruby"
7
+ gem "activesupport", '>= 3.0.0.beta4'
8
+ gem "activerecord", '>= 3.0.0.beta4'
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 David Wilkie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.textile ADDED
@@ -0,0 +1,109 @@
1
+ h1. Sparkfly Foreigner
2
+
3
+ sparkfly-foreigner is a fork of "Matt Higgins Foreigner":http://github.com/matthuhiggins/foreigner/ and the "dwilkie-foreigner":http://github.com/dwilkie/foreigner
4
+
5
+ Sparkfly requires an enterprise-ready Rails environment, which means we need solid PostgreSQL support and referential integrity.
6
+ Further, all of our plugins must be thoroughly tested. To make sure that PostgreSQL support is where we want it to be, the work
7
+ done by Matt Higgins and D. Wilkie were forked and all the tests were converted to use rspec. Dependency on plugin_test_helper
8
+ was taken out.
9
+
10
+ h2. Test Coverage
11
+
12
+ These tests were created and tested against our target platform, Rails 3. If you are using Rails 2.3.5,
13
+ use sparkfly-foreigner 0.6.0. this currently works with Rails 3.0 beta 3.
14
+
15
+ Rspec coverage was against Rspec 2.0 beta6
16
+
17
+ PostgreSQL (tested against 8.3.9, 8.4.2)
18
+ - "Schema" tests: do the table migrations actually add referential integrity?
19
+ - "Schema Extractor" tests: does the foreign_key() method extract out the foreign key from information_schema?
20
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
21
+
22
+ MySQL (tested against 5.0.90 / Gentoo mysql-5.0.90-r2 hardened x86_64)
23
+ - "Schema" tests: do the table migrations actually add referential integrity?
24
+ - "Schema Extractor" tests: does the foreign_key() method extract out the foreign key from information_schema?
25
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
26
+
27
+ SQLite3 (tested against 3.6.22 in-memory only)
28
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
29
+ - Warning: sqlite3 probably does not actually work when you use it.
30
+
31
+ Schema Dumper
32
+ - Are we emitting the correct schema.rb syntax for adding foreign key constraints?
33
+
34
+ TODO:
35
+ - CHECK Constraints (PostgreSQL-only)
36
+ - CHECK Constraints implemented as triggers for MySQL. This is unlikely to be supported since we don't plan to deploy to MySQL
37
+
38
+ h2. Usage
39
+
40
+ h3. Installation
41
+
42
+ Add this to your Gemfile:
43
+ <pre>
44
+ gem 'sparkfly-foreigner', :require => 'foreigner'
45
+ </pre>
46
+
47
+ h3. Migrations
48
+
49
+ To specify a different column name you can do the following:
50
+ <pre>
51
+ create_table :comments do |t|
52
+ t.integer :article_id, :null => false
53
+ t.foreign_key :post, :column => :article_id
54
+ end
55
+ </pre>
56
+
57
+ To specify dependencies (nullify or delete) you can do the following:
58
+ <pre>
59
+ create_table :comments do |t|
60
+ t.references :post, :foreign_key => {:dependent => :delete}, :null => false
61
+ end
62
+ </pre>
63
+
64
+ Here's another example using a different column name and the dependent option
65
+ <pre>
66
+ create_table :comments do |t|
67
+ t.integer :article_id, :null => false
68
+ t.foreign_key :post, :column => :article_id, :dependent => :nullify
69
+ end
70
+ </pre>
71
+
72
+ To specify a reference with a foreign key constraint using Rails convention:
73
+ <pre>
74
+ create_table :comments do |t|
75
+ t.references :post, :foreign_key => true, :null => false
76
+ end
77
+ </pre>
78
+
79
+ Read the specs for more example usage.
80
+
81
+ h3. schema.rb
82
+
83
+ All of the constrants are updated in schema.rb when you either:
84
+ <pre>
85
+ rake db:migrate
86
+ rake db:schema:dump
87
+ </pre>
88
+ This allows you to see the state of your migratons and
89
+ take advantage of using <pre>rake db:schema:load</pre>
90
+
91
+ h2. Testing
92
+
93
+ You will need rspec 2.0. At the time of this writing, you install it with:
94
+ <pre>
95
+ gem install rspec --pre
96
+ </pre>
97
+
98
+ You will also want to make sure you have Rubygems 1.3.6
99
+
100
+ Run "rake spec" to run all the spec. Make sure that all of the PostgreSQL and MySQL databases defined in
101
+ spec/spec_helper.rb are created before you run those tests.
102
+
103
+ h2. Copyrights
104
+
105
+ Copyright (c) 2010 Ho-Sheng Hsiao, released under the MIT license
106
+
107
+ Modified from code written by David Wilkie and Matt Higgins. See http://github.com/sparkfly/foreigner for details on
108
+ what got changed from what.
109
+
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rspec'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc 'Default: run specs.'
8
+ task :default => :spec
9
+
10
+ desc 'Run the specs'
11
+ Rspec::Core::RakeTask.new(:spec) do |t|
12
+ #t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
13
+ #t.spec_files = FileList['spec/**/*_spec.rb']
14
+ end
15
+
16
+ desc 'Generate documentation for the foreigner plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'Foreigner'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "theaboutbox-foreigner"
29
+ gemspec.summary = "Foreign keys for Rails migrations for PostgreSQL, MySQL and Sqlite3. Based on dwilkie-foreigner"
30
+ gemspec.description = "Allows you to add foreign keys to your migrations and enforce them. Forked to fix exception on Rails 3 beta 4"
31
+ gemspec.email = "hosh@sparkfly.com"
32
+ gemspec.homepage = "http://github.com/theaboutbox/foreigner"
33
+ gemspec.authors = ["Ho-Sheng Hsiao"]
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: gem install jeweler"
38
+ end
39
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.2
data/lib/foreigner.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'foreigner/connection_adapters/abstract/schema_statements'
2
+ require 'foreigner/connection_adapters/abstract/schema_definitions'
3
+ require 'foreigner/semantics/sql_2003'
4
+ require 'foreigner/schema_dumper'
5
+
6
+ module Foreigner
7
+ mattr_accessor :adapters
8
+ self.adapters = {}
9
+
10
+ class << self
11
+ def register(adapter_name, file_name)
12
+ adapters[adapter_name] = file_name
13
+ end
14
+
15
+ def load_adapter!
16
+ if adapters.key?(configured_adapter)
17
+ require adapters[configured_adapter]
18
+ end
19
+ end
20
+
21
+ def configured_adapter
22
+ ActiveRecord::Base.connection.adapter_name.downcase
23
+ end
24
+
25
+ def on_load(&block)
26
+ if defined?(Rails) && Rails.version >= '3.0'
27
+ ActiveSupport.on_load :active_record do
28
+ ActiveSupport.on_load :before_initialize do
29
+ unless ActiveRecord::Base.connected?
30
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
31
+ ActiveRecord::Base.establish_connection
32
+ end
33
+ block.call
34
+ end
35
+ end
36
+ else
37
+ yield
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ Foreigner.register 'mysql', 'foreigner/connection_adapters/mysql_adapter'
44
+ Foreigner.register 'sqlite3', 'foreigner/connection_adapters/sqlite3_adapter'
45
+ Foreigner.register 'postgresql', 'foreigner/connection_adapters/postgresql_adapter'
46
+
47
+ Foreigner.on_load do
48
+ module ActiveRecord
49
+ module ConnectionAdapters
50
+ include Foreigner::ConnectionAdapters::SchemaStatements
51
+ include Foreigner::ConnectionAdapters::SchemaDefinitions
52
+ end
53
+
54
+ SchemaDumper.class_eval do
55
+ include Foreigner::SchemaDumper
56
+ end
57
+ end
58
+
59
+ Foreigner.load_adapter! if defined?(Rails) # Audo-load if within Rails
60
+ end
@@ -0,0 +1,154 @@
1
+ module Foreigner
2
+ module ConnectionAdapters
3
+ class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
4
+ end
5
+
6
+ module SchemaDefinitions
7
+ def self.included(base)
8
+ base::TableDefinition.class_eval do
9
+ include Foreigner::ConnectionAdapters::TableDefinition
10
+ end
11
+
12
+ base::Table.class_eval do
13
+ include Foreigner::ConnectionAdapters::Table
14
+ end
15
+ end
16
+ end
17
+
18
+ module TableDefinition
19
+ class ForeignKey < Struct.new(:base, :to_table, :options)
20
+
21
+ def to_sql
22
+ base.foreign_key_definition(to_table, options)
23
+ end
24
+ alias to_s :to_sql
25
+ end
26
+
27
+ def self.included(base)
28
+ base.class_eval do
29
+ include InstanceMethods
30
+ alias_method_chain :references, :foreign_keys
31
+ alias_method_chain :to_sql, :foreign_keys
32
+ end
33
+ end
34
+
35
+ module InstanceMethods
36
+ # Adds a :foreign_key option to TableDefinition.references.
37
+ # If :foreign_key is true, a foreign key constraint is added to the table.
38
+ # You can also specify a hash, which is passed as foreign key options.
39
+ #
40
+ # ===== Examples
41
+ # ====== Add goat_id column and a foreign key to the goats table.
42
+ # t.references(:goat, :foreign_key => true)
43
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
44
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
45
+ #
46
+ # Note: No foreign key is created if :polymorphic => true is used.
47
+ def references_with_foreign_keys(*args)
48
+ options = args.extract_options!
49
+ fk_options = options.delete(:foreign_key)
50
+
51
+ if fk_options && !options[:polymorphic]
52
+ fk_options = {} if fk_options == true
53
+ args.each { |to_table| foreign_key(to_table, fk_options) }
54
+ end
55
+
56
+ references_without_foreign_keys(*(args << options))
57
+ end
58
+
59
+ # Defines a foreign key for the table. +to_table+ can be a single Symbol, or
60
+ # an Array of Symbols.
61
+ #
62
+ # ===== Examples
63
+ # ====== Creating a simple foreign key
64
+ # t.foreign_key(:person)
65
+ # ====== Defining the column
66
+ # t.foreign_key(:person, :column => :sender_id)
67
+ # ====== Specify cascading foreign key
68
+ # t.foreign_key(:person, :dependent => :delete)
69
+ def foreign_key(to_table, options = {})
70
+ if @base.supports_foreign_keys?
71
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
72
+ foreign_keys << ForeignKey.new(@base, to_table, options)
73
+ end
74
+ end
75
+
76
+ def to_sql_with_foreign_keys
77
+ sql = to_sql_without_foreign_keys
78
+ sql << ', ' << (foreign_keys * ', ') if foreign_keys.present?
79
+ sql
80
+ end
81
+
82
+ private
83
+ def foreign_keys
84
+ @foreign_keys ||= []
85
+ end
86
+ end
87
+ end
88
+
89
+ module Table
90
+ def self.included(base)
91
+ base.class_eval do
92
+ include InstanceMethods
93
+ alias_method_chain :references, :foreign_keys
94
+ end
95
+ end
96
+
97
+ module InstanceMethods
98
+ # Adds a new foreign key to the table. +to_table+ can be a single Symbol, or
99
+ # an Array of Symbols. See SchemaStatements#add_foreign_key
100
+ #
101
+ # ===== Examples
102
+ # ====== Creating a simple foreign key
103
+ # t.foreign_key(:person)
104
+ # ====== Defining the column
105
+ # t.foreign_key(:person, :column => :sender_id)
106
+ # ====== Creating a named foreign key
107
+ # t.foreign_key(:person, :column => :sender_id, :name => 'sender_foreign_key')
108
+ # ====== Defining the column of the +to_table+.
109
+ # t.foreign_key(:person, :column => :sender_id, :primary_key => :person_id)
110
+ def foreign_key(to_table, options = {})
111
+ @base.add_foreign_key(@table_name, to_table, options)
112
+ end
113
+
114
+ # Remove the given foreign key from the table.
115
+ #
116
+ # ===== Examples
117
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
118
+ # t.remove_foreign_key :companies
119
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
120
+ # remove_foreign_key :column => :branch_id
121
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
122
+ # remove_index :name => :party_foreign_key
123
+ def remove_foreign_key(options = {})
124
+ @base.remove_foreign_key(@table_name, options)
125
+ end
126
+
127
+ # Adds a :foreign_key option to TableDefinition.references.
128
+ # If :foreign_key is true, a foreign key constraint is added to the table.
129
+ # You can also specify a hash, which is passed as foreign key options.
130
+ #
131
+ # ===== Examples
132
+ # ====== Add goat_id column and a foreign key to the goats table.
133
+ # t.references(:goat, :foreign_key => true)
134
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
135
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
136
+ #
137
+ # Note: No foreign key is created if :polymorphic => true is used.
138
+ def references_with_foreign_keys(*args)
139
+ options = args.extract_options!
140
+ polymorphic = options[:polymorphic]
141
+ fk_options = options.delete(:foreign_key)
142
+
143
+ references_without_foreign_keys(*(args.dup << options))
144
+
145
+ if fk_options && !polymorphic
146
+ fk_options = {} if fk_options == true
147
+ args.each { |to_table| foreign_key(to_table, fk_options) }
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
@@ -0,0 +1,75 @@
1
+ module Foreigner
2
+ module ConnectionAdapters
3
+ module SchemaStatements
4
+ def self.included(base)
5
+ base::AbstractAdapter.class_eval do
6
+ include Foreigner::ConnectionAdapters::AbstractAdapter
7
+ end
8
+ end
9
+ end
10
+
11
+ module AbstractAdapter
12
+ def supports_foreign_keys?
13
+ false
14
+ end
15
+
16
+ # Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
17
+ #
18
+ # The foreign key will be named after the from and to tables unless you pass
19
+ # <tt>:name</tt> as an option.
20
+ #
21
+ # ===== Examples
22
+ # ====== Creating a foreign key
23
+ # add_foreign_key(:comments, :posts)
24
+ # generates
25
+ # ALTER TABLE `comments` ADD CONSTRAINT
26
+ # `comments_post_id_fk` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
27
+ #
28
+ # ====== Creating a named foreign key
29
+ # add_foreign_key(:comments, :posts, :name => 'comments_belongs_to_posts')
30
+ # generates
31
+ # ALTER TABLE `comments` ADD CONSTRAINT
32
+ # `comments_belongs_to_posts` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
33
+ #
34
+ # ====== Creating a cascading foreign_key on a custom column
35
+ # add_foreign_key(:people, :people, :column => 'best_friend_id', :dependent => :nullify)
36
+ # generates
37
+ # ALTER TABLE `people` ADD CONSTRAINT
38
+ # `people_best_friend_id_fk` FOREIGN KEY (`best_friend_id`) REFERENCES `people` (`id`)
39
+ # ON DELETE SET NULL
40
+ #
41
+ # === Supported options
42
+ # [:column]
43
+ # Specify the column name on the from_table that references the to_table. By default this is guessed
44
+ # to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
45
+ # as the default <tt>:column</tt>.
46
+ # [:primary_key]
47
+ # Specify the column name on the to_table that is referenced by this foreign key. By default this is
48
+ # assumed to be "id".
49
+ # [:name]
50
+ # Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
51
+ # [:dependent]
52
+ # If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
53
+ # If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
54
+ def add_foreign_key(from_table, to_table, options = {})
55
+ end
56
+
57
+ # Remove the given foreign key from the table.
58
+ #
59
+ # ===== Examples
60
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
61
+ # remove_foreign_key :suppliers, :companies
62
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
63
+ # remove_foreign_key :accounts, :column => :branch_id
64
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
65
+ # remove_foreign_key :accounts, :name => :party_foreign_key
66
+ def remove_foreign_key(from_table, options)
67
+ end
68
+
69
+ # Return the foreign keys for the schema_dumper
70
+ def foreign_keys(table_name)
71
+ []
72
+ end
73
+ end
74
+ end
75
+ end