sqlite-foreigner 0.5.0
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/README.textile +126 -0
- data/lib/foreigner.rb +22 -0
- data/lib/foreigner/connection_adapters/abstract/schema_definitions.rb +84 -0
- data/lib/foreigner/connection_adapters/abstract/schema_statements.rb +23 -0
- data/lib/foreigner/connection_adapters/sql_2003.rb +28 -0
- data/lib/foreigner/connection_adapters/sqlite3_adapter.rb +46 -0
- data/lib/foreigner/schema_dumper.rb +122 -0
- metadata +63 -0
data/README.textile
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
h1. Sqlite Foreigner
|
2
|
+
|
3
|
+
Sqlite Foreigner is a Rails foreign key migration helper which supports adding
|
4
|
+
AND enforcing foreign key constraints on Sqlite3 databases.
|
5
|
+
|
6
|
+
h2. The Story
|
7
|
+
|
8
|
+
With a lack of support for easily adding foreign key constraints to sqlite databases
|
9
|
+
I decided to create my own based on "Matt Higgins Foreigner":http://github.com/matthuhiggins/foreigner/
|
10
|
+
|
11
|
+
h2. Some Examples
|
12
|
+
|
13
|
+
Sqlite Foreigner allows you to do the following in your migration files
|
14
|
+
<pre>
|
15
|
+
create_table :comments do |t|
|
16
|
+
t.references :posts, :foreign_key => true, :null => false
|
17
|
+
end
|
18
|
+
</pre>
|
19
|
+
Which will generate the following SQL:
|
20
|
+
<pre>
|
21
|
+
CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
22
|
+
"post_id" integer NOT NULL,
|
23
|
+
FOREIGN KEY ("post_id") REFERENCES "posts"(id));
|
24
|
+
</pre>
|
25
|
+
|
26
|
+
Go a different column name?
|
27
|
+
<pre>
|
28
|
+
create_table :comments do |t|
|
29
|
+
t.references :article, :null => false
|
30
|
+
t.foreign_key :posts, :column => :article_id
|
31
|
+
end
|
32
|
+
</pre>
|
33
|
+
Which generates:
|
34
|
+
<pre>
|
35
|
+
CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
36
|
+
"article_id" integer NOT NULL,
|
37
|
+
FOREIGN KEY ("article_id") REFERENCES "posts"(id));
|
38
|
+
</pre>
|
39
|
+
|
40
|
+
Want to specify a dependency (nullify or delete)?
|
41
|
+
<pre>
|
42
|
+
create_table :comments do |t|
|
43
|
+
t.references :posts, :foreign_key => {:dependent => :delete}, :null => false
|
44
|
+
end
|
45
|
+
</pre>
|
46
|
+
Generates:
|
47
|
+
<pre>
|
48
|
+
CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
49
|
+
"post_id" integer NOT NULL,
|
50
|
+
FOREIGN KEY ("post_id") REFERENCES "posts"(id) ON DELETE CASCADE);
|
51
|
+
</pre>
|
52
|
+
Or:
|
53
|
+
<pre>
|
54
|
+
create_table :comments do |t|
|
55
|
+
t.references :article, :null => false
|
56
|
+
t.foreign_key :posts, :column => :article_id, :dependent => :nullify
|
57
|
+
end
|
58
|
+
</pre>
|
59
|
+
Which generates:
|
60
|
+
<pre>
|
61
|
+
CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
62
|
+
"article_id" integer NOT NULL,
|
63
|
+
FOREIGN KEY ("article_id") REFERENCES "posts"(id) ON DELETE SET NULL);
|
64
|
+
</pre>
|
65
|
+
|
66
|
+
h2. Enforcing constraints
|
67
|
+
|
68
|
+
SQLite does not enforce database constraints out of the box
|
69
|
+
This provides you with the flexibility in choosing whether or not to enforce
|
70
|
+
constraints at the DB level or not.
|
71
|
+
|
72
|
+
In order to enforce your constraints:
|
73
|
+
<pre>
|
74
|
+
script/dbconsole
|
75
|
+
.genfkey --exec
|
76
|
+
</pre>
|
77
|
+
|
78
|
+
While your in the console run:
|
79
|
+
<pre>
|
80
|
+
.schema
|
81
|
+
</pre>
|
82
|
+
to see your constraints implemented as triggers
|
83
|
+
|
84
|
+
h2. schema.rb
|
85
|
+
|
86
|
+
All of the constrants are updated in schema.rb
|
87
|
+
when you run:
|
88
|
+
<pre>
|
89
|
+
rake db:migrate
|
90
|
+
rake db:schema:dump
|
91
|
+
</pre>
|
92
|
+
|
93
|
+
This allows you to see the state of your migratons and
|
94
|
+
take advantage of using <pre>rake db:schema:load</pre>
|
95
|
+
|
96
|
+
h2. Limitations
|
97
|
+
|
98
|
+
Since SQLite does not have complete ALTER TABLE support
|
99
|
+
you cannot use the following syntax:
|
100
|
+
<pre>
|
101
|
+
add_foreign_key
|
102
|
+
remove_foreign_key
|
103
|
+
</pre>
|
104
|
+
|
105
|
+
Therefore you must add your foreign keys when you define your table,
|
106
|
+
which may involve editing existing migration files instead of generating new ones
|
107
|
+
|
108
|
+
h2. Installation
|
109
|
+
|
110
|
+
Add the following to environment.rb:
|
111
|
+
<pre>
|
112
|
+
config.gem "sqlite-foreigner", :lib => "foreigner", :source => "http://gemcutter.org"
|
113
|
+
</pre>
|
114
|
+
|
115
|
+
Then run:
|
116
|
+
<pre>
|
117
|
+
sudo rake gems:install
|
118
|
+
</pre>
|
119
|
+
|
120
|
+
h2. See also
|
121
|
+
|
122
|
+
Need support for other databases?
|
123
|
+
Check out "dwilkie-foreigner":http://github.com/dwilkie/foreigner/tree/master
|
124
|
+
|
125
|
+
Copyright (c) 2009 David Wilkie, released under the MIT license
|
126
|
+
|
data/lib/foreigner.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'foreigner/connection_adapters/abstract/schema_definitions'
|
2
|
+
require 'foreigner/connection_adapters/abstract/schema_statements'
|
3
|
+
require 'foreigner/connection_adapters/sql_2003'
|
4
|
+
require 'foreigner/schema_dumper'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters
|
8
|
+
include Foreigner::ConnectionAdapters::SchemaStatements
|
9
|
+
include Foreigner::ConnectionAdapters::SchemaDefinitions
|
10
|
+
end
|
11
|
+
|
12
|
+
SchemaDumper.class_eval do
|
13
|
+
include Foreigner::SchemaDumper
|
14
|
+
end
|
15
|
+
|
16
|
+
Base.class_eval do
|
17
|
+
if connection_pool.spec.config[:adapter].downcase == "sqlite3"
|
18
|
+
require "foreigner/connection_adapters/sqlite3_adapter"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,84 @@
|
|
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
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module TableDefinition
|
15
|
+
class ForeignKey < Struct.new(:base, :to_table, :options)
|
16
|
+
def to_sql
|
17
|
+
base.foreign_key_definition(to_table, options)
|
18
|
+
end
|
19
|
+
alias to_s :to_sql
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(base)
|
23
|
+
base.class_eval do
|
24
|
+
include InstanceMethods
|
25
|
+
alias_method_chain :references, :foreign_keys
|
26
|
+
alias_method_chain :to_sql, :foreign_keys
|
27
|
+
end
|
28
|
+
end
|
29
|
+
module InstanceMethods
|
30
|
+
# Adds a :foreign_key option to TableDefinition.references.
|
31
|
+
# If :foreign_key is true, a foreign key constraint is added to the table.
|
32
|
+
# You can also specify a hash, which is passed as foreign key options.
|
33
|
+
#
|
34
|
+
# ===== Examples
|
35
|
+
# ====== Add goat_id column and a foreign key to the goats table.
|
36
|
+
# t.references(:goat, :foreign_key => true)
|
37
|
+
# ====== Add goat_id column and a cascading foreign key to the goats table.
|
38
|
+
# t.references(:goat, :foreign_key => {:dependent => :delete})
|
39
|
+
#
|
40
|
+
# Note: No foreign key is created if :polymorphic => true is used.
|
41
|
+
def references_with_foreign_keys(*args)
|
42
|
+
options = args.extract_options!
|
43
|
+
fk_options = options.delete(:foreign_key)
|
44
|
+
|
45
|
+
if fk_options && !options[:polymorphic]
|
46
|
+
fk_options = {} if fk_options == true
|
47
|
+
args.each { |to_table| foreign_key(to_table, fk_options) }
|
48
|
+
end
|
49
|
+
|
50
|
+
references_without_foreign_keys(*(args << options))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Defines a foreign key for the table. +to_table+ can be a single Symbol, or
|
54
|
+
# an Array of Symbols.
|
55
|
+
#
|
56
|
+
# ===== Examples
|
57
|
+
# ====== Creating a simple foreign key
|
58
|
+
# t.foreign_key(:people)
|
59
|
+
# ====== Defining the column
|
60
|
+
# t.foreign_key(:people, :column => :sender_id)
|
61
|
+
# ====== Specify cascading foreign key
|
62
|
+
# t.foreign_key(:people, :dependent => :delete)
|
63
|
+
def foreign_key(to_table, options = {})
|
64
|
+
if @base.supports_foreign_keys?
|
65
|
+
to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
|
66
|
+
foreign_keys << ForeignKey.new(@base, to_table, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_sql_with_foreign_keys
|
71
|
+
sql = to_sql_without_foreign_keys
|
72
|
+
sql << ', ' << (foreign_keys * ', ') if foreign_keys.present?
|
73
|
+
sql
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def foreign_keys
|
78
|
+
@foreign_keys ||= []
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,23 @@
|
|
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
|
+
# Return the foreign keys for the schema_dumper
|
17
|
+
def foreign_keys(table_name)
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sql2003
|
4
|
+
def supports_foreign_keys?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def foreign_key_definition(to_table, options = {})
|
9
|
+
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
10
|
+
dependency = dependency_sql(options[:dependent])
|
11
|
+
|
12
|
+
sql = "FOREIGN KEY (#{quote_column_name(column)}) REFERENCES #{quote_table_name(to_table)}(id)"
|
13
|
+
sql << " #{dependency}" unless dependency.blank?
|
14
|
+
sql
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def dependency_sql(dependency)
|
19
|
+
case dependency
|
20
|
+
when :nullify then "ON DELETE SET NULL"
|
21
|
+
when :delete then "ON DELETE CASCADE"
|
22
|
+
else ""
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'foreigner/connection_adapters/sql_2003'
|
2
|
+
|
3
|
+
module Foreigner
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3Adapter
|
6
|
+
include Foreigner::ConnectionAdapters::Sql2003
|
7
|
+
|
8
|
+
def foreign_keys(table_name)
|
9
|
+
foreign_keys = []
|
10
|
+
create_table_info = select_value %{
|
11
|
+
SELECT sql
|
12
|
+
FROM sqlite_master
|
13
|
+
WHERE sql LIKE '%FOREIGN KEY%'
|
14
|
+
AND name = '#{table_name}'
|
15
|
+
}
|
16
|
+
unless create_table_info.nil?
|
17
|
+
fk_columns = create_table_info.scan(/FOREIGN KEY\s*\(\"([^\"]+)\"\)/)
|
18
|
+
fk_tables = create_table_info.scan(/REFERENCES\s*\"([^\"]+)\"/)
|
19
|
+
fk_references = create_table_info.scan(/REFERENCES[^\,]+/)
|
20
|
+
if fk_columns.size == fk_tables.size && fk_references.size == fk_columns.size
|
21
|
+
fk_columns.each_with_index do |fk_column, index|
|
22
|
+
if fk_references[index] =~ /ON DELETE CASCADE/
|
23
|
+
fk_references[index] = :delete
|
24
|
+
elsif fk_references[index] =~ /ON DELETE SET NULL/
|
25
|
+
fk_references[index] = :nullify
|
26
|
+
else
|
27
|
+
fk_references[index] = nil
|
28
|
+
end
|
29
|
+
foreign_keys << ForeignKeyDefinition.new(table_name, fk_tables[index][0], :column => fk_column[0], :dependent => fk_references[index])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
foreign_keys
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ActiveRecord
|
40
|
+
module ConnectionAdapters
|
41
|
+
SQLite3Adapter.class_eval do
|
42
|
+
include Foreigner::ConnectionAdapters::SQLite3Adapter
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module SchemaDumper
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include InstanceMethods
|
6
|
+
alias_method_chain :table, :foreign_keys
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
def table_with_foreign_keys(table, stream)
|
13
|
+
if @connection.class == ActiveRecord::ConnectionAdapters::SQLite3Adapter
|
14
|
+
foreign_key_table(table, stream)
|
15
|
+
else
|
16
|
+
table_without_foreign_keys(table, stream)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
# This is almost direct copy from
|
22
|
+
# active_record/schema.dumper with the add_foreign_keys method
|
23
|
+
# inserted into the middle
|
24
|
+
def foreign_key_table(table, stream)
|
25
|
+
columns = @connection.columns(table)
|
26
|
+
begin
|
27
|
+
tbl = StringIO.new
|
28
|
+
|
29
|
+
# first dump primary key column
|
30
|
+
if @connection.respond_to?(:pk_and_sequence_for)
|
31
|
+
pk, pk_seq = @connection.pk_and_sequence_for(table)
|
32
|
+
elsif @connection.respond_to?(:primary_key)
|
33
|
+
pk = @connection.primary_key(table)
|
34
|
+
end
|
35
|
+
pk ||= 'id'
|
36
|
+
|
37
|
+
tbl.print " create_table #{table.inspect}"
|
38
|
+
if columns.detect { |c| c.name == pk }
|
39
|
+
if pk != 'id'
|
40
|
+
tbl.print %Q(, :primary_key => "#{pk}")
|
41
|
+
end
|
42
|
+
else
|
43
|
+
tbl.print ", :id => false"
|
44
|
+
end
|
45
|
+
tbl.print ", :force => true"
|
46
|
+
tbl.puts " do |t|"
|
47
|
+
|
48
|
+
# then dump all non-primary key columns
|
49
|
+
column_specs = columns.map do |column|
|
50
|
+
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
51
|
+
next if column.name == pk
|
52
|
+
spec = {}
|
53
|
+
spec[:name] = column.name.inspect
|
54
|
+
spec[:type] = column.type.to_s
|
55
|
+
spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
|
56
|
+
spec[:precision] = column.precision.inspect if !column.precision.nil?
|
57
|
+
spec[:scale] = column.scale.inspect if !column.scale.nil?
|
58
|
+
spec[:null] = 'false' if !column.null
|
59
|
+
spec[:default] = default_string(column.default) if column.has_default?
|
60
|
+
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
|
61
|
+
spec
|
62
|
+
end.compact
|
63
|
+
|
64
|
+
# find all migration keys used in this table
|
65
|
+
keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
|
66
|
+
|
67
|
+
# figure out the lengths for each column based on above keys
|
68
|
+
lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
|
69
|
+
|
70
|
+
# the string we're going to sprintf our values against, with standardized column widths
|
71
|
+
format_string = lengths.map{ |len| "%-#{len}s" }
|
72
|
+
|
73
|
+
# find the max length for the 'type' column, which is special
|
74
|
+
type_length = column_specs.map{ |column| column[:type].length }.max
|
75
|
+
|
76
|
+
# add column type definition to our format string
|
77
|
+
format_string.unshift " t.%-#{type_length}s "
|
78
|
+
|
79
|
+
format_string *= ''
|
80
|
+
|
81
|
+
column_specs.each do |colspec|
|
82
|
+
values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
|
83
|
+
values.unshift colspec[:type]
|
84
|
+
tbl.print((format_string % values).gsub(/,\s*$/, ''))
|
85
|
+
tbl.puts
|
86
|
+
end
|
87
|
+
|
88
|
+
# add the foreign keys
|
89
|
+
add_foreign_keys(table, tbl)
|
90
|
+
|
91
|
+
tbl.puts " end"
|
92
|
+
tbl.puts
|
93
|
+
|
94
|
+
indexes(table, tbl)
|
95
|
+
|
96
|
+
tbl.rewind
|
97
|
+
stream.print tbl.read
|
98
|
+
rescue => e
|
99
|
+
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
100
|
+
stream.puts "# #{e.message}"
|
101
|
+
stream.puts
|
102
|
+
end
|
103
|
+
|
104
|
+
stream
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_foreign_keys(table_name, stream)
|
108
|
+
if (foreign_keys = @connection.foreign_keys(table_name)).any?
|
109
|
+
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
110
|
+
statement_parts = [" t.foreign_key " + foreign_key.to_table.inspect]
|
111
|
+
statement_parts << (':column => ' + foreign_key.options[:column].inspect)
|
112
|
+
statement_parts << (':dependent => ' + foreign_key.options[:dependent].inspect)
|
113
|
+
' ' + statement_parts.join(', ')
|
114
|
+
end
|
115
|
+
|
116
|
+
stream.puts add_foreign_key_statements.sort.join("\n")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sqlite-foreigner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Wilkie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-01 00:00:00 +07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Enforcable foreign key constraints for Rails migrations and SQLite databases
|
17
|
+
email: dwilkie@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.textile
|
24
|
+
files:
|
25
|
+
- README.textile
|
26
|
+
- lib/foreigner.rb
|
27
|
+
- lib/foreigner/schema_dumper.rb
|
28
|
+
- lib/foreigner/connection_adapters/sql_2003.rb
|
29
|
+
- lib/foreigner/connection_adapters/sqlite3_adapter.rb
|
30
|
+
- lib/foreigner/connection_adapters/abstract/schema_definitions.rb
|
31
|
+
- lib/foreigner/connection_adapters/abstract/schema_statements.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/dwilkie/sqlite3-foreigner/tree/master
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --line-numbers
|
39
|
+
- --main
|
40
|
+
- README.textile
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.3.5
|
59
|
+
signing_key:
|
60
|
+
specification_version: 1
|
61
|
+
summary: Enforcable foreign key constraints for Rails migrations and SQLite databases
|
62
|
+
test_files: []
|
63
|
+
|