sparkfly-foreigner 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ *.swp
2
+ *gemspec
3
+ pkg/*
@@ -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
+
@@ -0,0 +1,105 @@
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 2.3.5. Support for Rails 3 will come in the future
13
+ when we actually move our platform to Rails 3. The PostgreSQL 8.3 has the best coverage.
14
+
15
+ PostgreSQL (tested against 8.3.9)
16
+ - "Schema" tests: do the table migrations actually add referential integrity?
17
+ - "Schema Extractor" tests: does the foreign_key() method extract out the foreign key from information_schema?
18
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
19
+
20
+ MySQL (tested against 5.0.90 / Gentoo mysql-5.0.90-r2 hardened x86_64)
21
+ - "Schema" tests: do the table migrations actually add referential integrity?
22
+ - "Schema Extractor" tests: does the foreign_key() method extract out the foreign key from information_schema?
23
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
24
+
25
+ SQLite3 (tested against 3.6.22 in-memory only)
26
+ - "Semantics" tests: do the add/drop constraints emit proper SQL?
27
+
28
+ Schema Dumper
29
+ - Are we emitting the correct schema.rb syntax for adding foreign key constraints?
30
+
31
+ TODO:
32
+ - CHECK Constraints (PostgreSQL-only)
33
+ - CHECK Constraints implemented as triggers for MySQL. This is unlikely to be supported since we don't plan to deploy to MySQL
34
+
35
+ h2. Usage
36
+
37
+ To specify a different column name you can do the following:
38
+ <pre>
39
+ create_table :comments do |t|
40
+ t.integer :article_id, :null => false
41
+ t.foreign_key :post, :column => :article_id
42
+ end
43
+ </pre>
44
+
45
+ To specify dependencies (nullify or delete) you can do the following:
46
+ <pre>
47
+ create_table :comments do |t|
48
+ t.references :post, :foreign_key => {:dependent => :delete}, :null => false
49
+ end
50
+ </pre>
51
+
52
+ Here's another example using a different column name and the dependent option
53
+ <pre>
54
+ create_table :comments do |t|
55
+ t.integer :article_id, :null => false
56
+ t.foreign_key :post, :column => :article_id, :dependent => :nullify
57
+ end
58
+ </pre>
59
+
60
+ To specify a reference with a foreign key constraint using Rails convention:
61
+ <pre>
62
+ create_table :comments do |t|
63
+ t.references :post, :foreign_key => true, :null => false
64
+ end
65
+ </pre>
66
+
67
+ Read the specs for more example usage.
68
+
69
+ h2. schema.rb
70
+
71
+ All of the constrants are updated in schema.rb when you either:
72
+ <pre>
73
+ rake db:migrate
74
+ rake db:schema:dump
75
+ </pre>
76
+ This allows you to see the state of your migratons and
77
+ take advantage of using <pre>rake db:schema:load</pre>
78
+
79
+ h2. Installation
80
+
81
+ h3. Rails 3
82
+
83
+ - Have not tested any of these changes against Rails 3.
84
+ - See the matthuhiggins-foreigner or dwilkie-foreigner if you want a Foreigner that plays nice with Rails 3.
85
+
86
+ h3. Rails 2.3.5
87
+
88
+ Add the following to environment.rb:
89
+ <pre>config.gem "sparkfly-foreigner", :lib => "foreigner"</pre>
90
+ Then run:
91
+ <pre>sudo rake gems:install</pre>
92
+
93
+ h2. Testing
94
+
95
+ You will need rspec and possibly rspec-rails. Run "rake spec" to run all the spec. Make sure
96
+ that all of the PostgreSQL and MySQL databases defined in spec/spec_helper.rb are created
97
+ before you run those tests.
98
+
99
+ h2. Copyrights
100
+
101
+ Copyright (c) 2010 Ho-Sheng Hsiao, released under the MIT license
102
+
103
+ Modified from code written by David Wilkie and Matt Higgins. See http://github.com/sparkfly/foreigner for details on
104
+ what got changed from what.
105
+
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ desc 'Default: run specs.'
7
+ task :default => :spec
8
+
9
+ desc 'Run the specs'
10
+ Spec::Rake::SpecTask.new(:spec) do |t|
11
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
12
+ t.spec_files = FileList['spec/**/*_spec.rb']
13
+ end
14
+
15
+ desc 'Generate documentation for the foreigner plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Foreigner'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gemspec|
27
+ gemspec.name = "sparkfly-foreigner"
28
+ gemspec.summary = "Foreign keys for Rails migrations for PostgreSQL, MySQL and Sqlite3. Based on dwilkie-foreigner"
29
+ gemspec.description = "Allows you to add foreign keys to your migrations and enforce them"
30
+ gemspec.email = "hosh@sparkfly.com"
31
+ gemspec.homepage = "http://github.com/hosh/foreigner"
32
+ gemspec.authors = ["Ho-Sheng Hsiao"]
33
+ end
34
+ Jeweler::GemcutterTasks.new
35
+ rescue LoadError
36
+ puts "Jeweler not available. Install it with: gem install jeweler"
37
+ end
38
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.4
@@ -0,0 +1,58 @@
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
+ unless ActiveRecord::Base.connected?
29
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
30
+ ActiveRecord::Base.establish_connection
31
+ end
32
+ block.call
33
+ end
34
+ else
35
+ yield
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ Foreigner.register 'mysql', 'foreigner/connection_adapters/mysql_adapter'
42
+ Foreigner.register 'sqlite3', 'foreigner/connection_adapters/sqlite3_adapter'
43
+ Foreigner.register 'postgresql', 'foreigner/connection_adapters/postgresql_adapter'
44
+
45
+ Foreigner.on_load do
46
+ module ActiveRecord
47
+ module ConnectionAdapters
48
+ include Foreigner::ConnectionAdapters::SchemaStatements
49
+ include Foreigner::ConnectionAdapters::SchemaDefinitions
50
+ end
51
+
52
+ SchemaDumper.class_eval do
53
+ include Foreigner::SchemaDumper
54
+ end
55
+ end
56
+
57
+ Foreigner.load_adapter! if defined?(Rails) # Audo-load if within Rails
58
+ 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