voraz-dr_nic_magic_models 0.9.2
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/CHANGELOG +9 -0
- data/History.txt +33 -0
- data/Manifest.txt +56 -0
- data/README +0 -0
- data/Rakefile +149 -0
- data/install.rb +30 -0
- data/lib/base.rb +12 -0
- data/lib/connection_adapters/abstract/schema_statements.rb +0 -0
- data/lib/connection_adapters/abstract_adapter.rb +32 -0
- data/lib/connection_adapters/mysql_adapter.rb +42 -0
- data/lib/connection_adapters/postgresql_adapter.rb +45 -0
- data/lib/dr_nic_magic_models/inflector.rb +14 -0
- data/lib/dr_nic_magic_models/magic_model.rb +135 -0
- data/lib/dr_nic_magic_models/schema.rb +270 -0
- data/lib/dr_nic_magic_models/validations.rb +46 -0
- data/lib/dr_nic_magic_models/version.rb +9 -0
- data/lib/dr_nic_magic_models.rb +34 -0
- data/lib/module.rb +33 -0
- data/lib/rails.rb +19 -0
- data/scripts/txt2html +66 -0
- data/scripts/txt2js +58 -0
- data/test/abstract_unit.rb +70 -0
- data/test/connections/native_mysql/connection.rb +14 -0
- data/test/connections/native_postgresql/connection.rb +12 -0
- data/test/connections/native_sqlite/connection.rb +10 -0
- data/test/dummy_test.rb +13 -0
- data/test/env_test.rb +10 -0
- data/test/fixtures/.DS_Store +0 -0
- data/test/fixtures/adjectives.yml +3 -0
- data/test/fixtures/adjectives_fun_users.yml +3 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +4 -0
- data/test/fixtures/db_definitions/mysql.sql +58 -0
- data/test/fixtures/db_definitions/postgresql.sql +56 -0
- data/test/fixtures/db_definitions/sqlite.sql +49 -0
- data/test/fixtures/fun_users.yml +14 -0
- data/test/fixtures/group_memberships.yml +4 -0
- data/test/fixtures/group_tag.yml +11 -0
- data/test/fixtures/groups.yml +12 -0
- data/test/foreign_keys_test.rb +0 -0
- data/test/fun_user_plus.rb +2 -0
- data/test/invisible_model_access_test.rb +71 -0
- data/test/invisible_model_assoc_test.rb +61 -0
- data/test/invisible_model_classes_test.rb +23 -0
- data/test/magic_module_test.rb +20 -0
- data/test/test_existing_model.rb +20 -0
- data/test.db +0 -0
- data/website/index.html +409 -0
- data/website/index.txt +291 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +106 -0
- data/website/template.js +3 -0
- data/website/template.rhtml +55 -0
- data/website/version-raw.js +3 -0
- data/website/version-raw.txt +2 -0
- data/website/version.js +4 -0
- data/website/version.txt +3 -0
- metadata +123 -0
data/CHANGELOG
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
* 0.2.5 * - Initial public release
|
|
2
|
+
- ActiveRecords can now be auto-created in memory when first referenced
|
|
3
|
+
from their table name, without an explicit class definition.
|
|
4
|
+
- ActiveRecords will automatically include validates_presence_of on
|
|
5
|
+
each field with :null => false
|
|
6
|
+
- ActiveRecords will automatically generate simple has_many, has_one,
|
|
7
|
+
belongs_to assocations based upon assumed foreign keys. E.g.
|
|
8
|
+
foreign key to products table is assumed to be product_id.
|
|
9
|
+
|
data/History.txt
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
== TRUNK 2007-06-30
|
|
2
|
+
|
|
3
|
+
* Allows ActiveRecord::StatementInvalid for attempts to find pluralised tables that don't exist
|
|
4
|
+
* Refactored Rakefile into namespaces by database
|
|
5
|
+
|
|
6
|
+
== 0.9.2 / 2007-4-30
|
|
7
|
+
|
|
8
|
+
* 3 major bugfixes:
|
|
9
|
+
* #generate_validations now works if you haven't already created a connection to the database; previously
|
|
10
|
+
validations wouldn't get created until you had already established the connection; now it does it for
|
|
11
|
+
you if its not already established
|
|
12
|
+
* Associations can be generated via the assignment methods, e.g. @membership.group= will generate the "belongs_to :group" association now. This allows the website tutorial to work correctly! Yay. That is, you can now do: Membership.create(:person => person, :group => group)
|
|
13
|
+
* has_many's should work now
|
|
14
|
+
|
|
15
|
+
== 0.9.1 / 2007-4-11
|
|
16
|
+
|
|
17
|
+
* 1 minor enhancement:
|
|
18
|
+
* ActiveRecord::Base includes all the magic model functionality via the MagicModel module
|
|
19
|
+
* Existing ARs can get magic validation via #generate_validations call
|
|
20
|
+
* Website tutorial works :D
|
|
21
|
+
|
|
22
|
+
== 0.9.0 / 2007-4-9
|
|
23
|
+
|
|
24
|
+
* 1 major enhancement:
|
|
25
|
+
* Support for dynamic loading of classes again
|
|
26
|
+
* 2 new DB supported:
|
|
27
|
+
* Tests run on sqlite (no fk support)
|
|
28
|
+
* Tests run on postgresql (fk support)
|
|
29
|
+
* Including FK bug fix
|
|
30
|
+
* Many fixes that I've lost track of
|
|
31
|
+
* History.txt to keep track of changes like these
|
|
32
|
+
* Using Hoe for Rakefile
|
|
33
|
+
* Use modules to specify common table prefixes
|
data/Manifest.txt
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
CHANGELOG
|
|
2
|
+
History.txt
|
|
3
|
+
Manifest.txt
|
|
4
|
+
README
|
|
5
|
+
Rakefile
|
|
6
|
+
install.rb
|
|
7
|
+
lib/base.rb
|
|
8
|
+
lib/connection_adapters/abstract/schema_statements.rb
|
|
9
|
+
lib/connection_adapters/abstract_adapter.rb
|
|
10
|
+
lib/connection_adapters/mysql_adapter.rb
|
|
11
|
+
lib/connection_adapters/postgresql_adapter.rb
|
|
12
|
+
lib/dr_nic_magic_models.rb
|
|
13
|
+
lib/dr_nic_magic_models/inflector.rb
|
|
14
|
+
lib/dr_nic_magic_models/magic_model.rb
|
|
15
|
+
lib/dr_nic_magic_models/schema.rb
|
|
16
|
+
lib/dr_nic_magic_models/validations.rb
|
|
17
|
+
lib/dr_nic_magic_models/version.rb
|
|
18
|
+
lib/module.rb
|
|
19
|
+
lib/rails.rb
|
|
20
|
+
scripts/txt2html
|
|
21
|
+
scripts/txt2js
|
|
22
|
+
test.db
|
|
23
|
+
test/abstract_unit.rb
|
|
24
|
+
test/connections/native_mysql/connection.rb
|
|
25
|
+
test/connections/native_postgresql/connection.rb
|
|
26
|
+
test/connections/native_sqlite/connection.rb
|
|
27
|
+
test/dummy_test.rb
|
|
28
|
+
test/env_test.rb
|
|
29
|
+
test/fixtures/.DS_Store
|
|
30
|
+
test/fixtures/adjectives.yml
|
|
31
|
+
test/fixtures/adjectives_fun_users.yml
|
|
32
|
+
test/fixtures/db_definitions/mysql.drop.sql
|
|
33
|
+
test/fixtures/db_definitions/mysql.sql
|
|
34
|
+
test/fixtures/db_definitions/postgresql.sql
|
|
35
|
+
test/fixtures/db_definitions/sqlite.sql
|
|
36
|
+
test/fixtures/fun_users.yml
|
|
37
|
+
test/fixtures/group_memberships.yml
|
|
38
|
+
test/fixtures/group_tag.yml
|
|
39
|
+
test/fixtures/groups.yml
|
|
40
|
+
test/foreign_keys_test.rb
|
|
41
|
+
test/fun_user_plus.rb
|
|
42
|
+
test/invisible_model_access_test.rb
|
|
43
|
+
test/invisible_model_assoc_test.rb
|
|
44
|
+
test/invisible_model_classes_test.rb
|
|
45
|
+
test/magic_module_test.rb
|
|
46
|
+
test/test_existing_model.rb
|
|
47
|
+
website/index.html
|
|
48
|
+
website/index.txt
|
|
49
|
+
website/javascripts/rounded_corners_lite.inc.js
|
|
50
|
+
website/stylesheets/screen.css
|
|
51
|
+
website/template.js
|
|
52
|
+
website/template.rhtml
|
|
53
|
+
website/version-raw.js
|
|
54
|
+
website/version-raw.txt
|
|
55
|
+
website/version.js
|
|
56
|
+
website/version.txt
|
data/README
ADDED
|
File without changes
|
data/Rakefile
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/clean'
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
require 'rake/rdoctask'
|
|
6
|
+
require 'rake/packagetask'
|
|
7
|
+
require 'rake/gempackagetask'
|
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
|
9
|
+
require 'hoe'
|
|
10
|
+
require File.join(File.dirname(__FILE__), 'lib', 'dr_nic_magic_models', 'version')
|
|
11
|
+
|
|
12
|
+
AUTHOR = "nicwilliams" # can also be an array of Authors
|
|
13
|
+
EMAIL = "drnicwilliams@gmail.com"
|
|
14
|
+
DESCRIPTION = "Dr Nic's Magic Models - Invisible validations, assocations and Active Record models themselves!"
|
|
15
|
+
GEM_NAME = "dr_nic_magic_models" # what ppl will type to install your gem
|
|
16
|
+
RUBYFORGE_PROJECT = "magicmodels" # The unix name for your project
|
|
17
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
NAME = "magic_multi_connections"
|
|
21
|
+
REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
|
22
|
+
VERS = ENV['VERSION'] || (DrNicMagicModels::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
|
23
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
|
24
|
+
RDOC_OPTS = ['--quiet', '--title', "dr_nic_magic_models documentation",
|
|
25
|
+
"--opname", "index.html",
|
|
26
|
+
"--line-numbers",
|
|
27
|
+
"--main", "README",
|
|
28
|
+
"--inline-source"]
|
|
29
|
+
|
|
30
|
+
class Hoe
|
|
31
|
+
def extra_deps
|
|
32
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Generate all the Rake tasks
|
|
37
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
|
38
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
|
39
|
+
p.author = AUTHOR
|
|
40
|
+
p.description = DESCRIPTION
|
|
41
|
+
p.email = EMAIL
|
|
42
|
+
p.summary = DESCRIPTION
|
|
43
|
+
p.url = HOMEPATH
|
|
44
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
|
45
|
+
p.test_globs = ["test/**/test_*.rb"]
|
|
46
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
|
47
|
+
|
|
48
|
+
# == Optional
|
|
49
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
|
50
|
+
|
|
51
|
+
#p.extra_deps - An array of rubygem dependencies.
|
|
52
|
+
#p.spec_extras - A hash of extra values to set in the gemspec.
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Run the unit tests
|
|
56
|
+
ADAPTERS = %w( sqlite mysql postgresql ) # UNTESTED - postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase )
|
|
57
|
+
for adapter in ADAPTERS
|
|
58
|
+
Rake::TestTask.new("test_#{adapter}") { |t|
|
|
59
|
+
t.libs << "test" << "test/connections/native_#{adapter}"
|
|
60
|
+
t.pattern = "test/*_test{,_#{adapter}}.rb"
|
|
61
|
+
t.verbose = true
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
task :default do
|
|
66
|
+
puts "To run tests, call the explicit test_<adapter> task"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
|
|
70
|
+
|
|
71
|
+
namespace :mysql do
|
|
72
|
+
desc 'Build the MySQL test databases'
|
|
73
|
+
task :build_databases do
|
|
74
|
+
puts File.join(SCHEMA_PATH, 'mysql.sql')
|
|
75
|
+
user = 'root'
|
|
76
|
+
sh %{ mysqladmin -u #{user} -p create "#{GEM_NAME}_unittest" }
|
|
77
|
+
sh %{ mysql -u #{user} -p "#{GEM_NAME}_unittest" < #{File.join(SCHEMA_PATH, 'mysql.sql')} }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
desc 'Drop the MySQL test databases'
|
|
81
|
+
task :drop_databases do
|
|
82
|
+
user = 'root'
|
|
83
|
+
sh %{ mysqladmin -u #{user} -p -f drop "#{GEM_NAME}_unittest" }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
desc 'Rebuild the MySQL test databases'
|
|
87
|
+
|
|
88
|
+
task :rebuild_databases => [:drop_databases, :build_databases]
|
|
89
|
+
|
|
90
|
+
task :test => :test_mysql
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
namespace :sqlite do
|
|
94
|
+
desc 'Build the sqlite test databases'
|
|
95
|
+
task :build_databases do
|
|
96
|
+
file = File.join(SCHEMA_PATH, 'sqlite.sql')
|
|
97
|
+
cmd = "sqlite3 test.db < #{file}"
|
|
98
|
+
puts cmd
|
|
99
|
+
sh %{ #{cmd} }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
desc 'Drop the sqlite test databases'
|
|
103
|
+
task :drop_databases do
|
|
104
|
+
sh %{ rm -f test.db }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
desc 'Rebuild the sqlite test databases'
|
|
108
|
+
task :rebuild_databases => [:drop_databases, :build_databases]
|
|
109
|
+
|
|
110
|
+
task :test => :test_sqlite
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
namespace :postgresql do
|
|
114
|
+
desc 'Build the PostgreSQL test databases'
|
|
115
|
+
task :build_databases do
|
|
116
|
+
sh %{ createdb "#{GEM_NAME}_unittest" }
|
|
117
|
+
sh %{ psql "#{GEM_NAME}_unittest" -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
desc 'Drop the PostgreSQL test databases'
|
|
121
|
+
task :drop_databases do
|
|
122
|
+
sh %{ dropdb "#{GEM_NAME}_unittest" }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
desc 'Rebuild the PostgreSQL test databases'
|
|
126
|
+
task :rebuild_databases => [:drop_databases, :build_databases]
|
|
127
|
+
|
|
128
|
+
task :test => :test_postgresql
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
desc 'Generate website files'
|
|
133
|
+
task :website_generate do
|
|
134
|
+
sh %{ ruby scripts/txt2html website/index.txt > website/index.html }
|
|
135
|
+
sh %{ ruby scripts/txt2js website/version.txt > website/version.js }
|
|
136
|
+
sh %{ ruby scripts/txt2js website/version-raw.txt > website/version-raw.js }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
desc 'Upload website files to rubyforge'
|
|
140
|
+
task :website_upload do
|
|
141
|
+
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
|
142
|
+
host = "#{config["username"]}@rubyforge.org"
|
|
143
|
+
remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
|
144
|
+
local_dir = 'website'
|
|
145
|
+
sh %{rsync -av --delete #{local_dir}/ #{host}:#{remote_dir}}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
desc 'Generate and upload website files'
|
|
149
|
+
task :website => [:website_generate, :website_upload]
|
data/install.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'rbconfig'
|
|
2
|
+
require 'find'
|
|
3
|
+
require 'ftools'
|
|
4
|
+
|
|
5
|
+
include Config
|
|
6
|
+
|
|
7
|
+
# this was adapted from rdoc's install.rb by ways of Log4r
|
|
8
|
+
|
|
9
|
+
$sitedir = CONFIG["sitelibdir"]
|
|
10
|
+
unless $sitedir
|
|
11
|
+
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
|
|
12
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
|
13
|
+
$sitedir = $:.find {|x| x =~ /site_ruby/ }
|
|
14
|
+
if !$sitedir
|
|
15
|
+
$sitedir = File.join($libdir, "site_ruby")
|
|
16
|
+
elsif $sitedir !~ Regexp.quote(version)
|
|
17
|
+
$sitedir = File.join($sitedir, version)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# the acual gruntwork
|
|
22
|
+
Dir.chdir("lib")
|
|
23
|
+
|
|
24
|
+
Find.find("dr_nic_magic_models", "dr_nic_magic_models.rb") { |f|
|
|
25
|
+
if f[-3..-1] == ".rb"
|
|
26
|
+
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
|
|
27
|
+
else
|
|
28
|
+
File::makedirs(File.join($sitedir, *f.split(/\//)))
|
|
29
|
+
end
|
|
30
|
+
}
|
data/lib/base.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#TODO: Use :dependent for FK cascade?
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class Base
|
|
5
|
+
class << self
|
|
6
|
+
public
|
|
7
|
+
def get_unique_index_columns
|
|
8
|
+
self.connection.indexes(self.table_name, "#{self.name} Indexes").select { |index| index.unique && index.columns.size == 1 }.map{ |index| index.columns.first }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module ConnectionAdapters # :nodoc:
|
|
4
|
+
|
|
5
|
+
# Generic holder for foreign key constraint meta-data from the database schema.
|
|
6
|
+
class ForeignKeyConstraint < Struct.new(:name, :table, :foreign_key, :reference_table, :reference_column, :on_update, :on_delete)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class AbstractAdapter
|
|
10
|
+
|
|
11
|
+
# Does this adapter support the ability to fetch foreign key information?
|
|
12
|
+
# Backend specific, as the abstract adapter always returns +false+.
|
|
13
|
+
def supports_fetch_foreign_keys?
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def foreign_key_constraints(table, name = nil)
|
|
18
|
+
raise NotImplementedError, "foreign_key_constraints is not implemented for #{self.class}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def remove_foreign_key_constraint(table_name, constraint_name)
|
|
22
|
+
raise NotImplementedError, "rename_table is not implemented for #{self.class}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
def symbolize_foreign_key_constraint_action(constraint_action)
|
|
27
|
+
return nil if constraint_action.nil?
|
|
28
|
+
constraint_action.downcase.gsub(/\s/, '_').to_sym
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Foreign Key support from http://wiki.rubyonrails.org/rails/pages/Foreign+Key+Schema+Dumper+Plugin
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
class MysqlAdapter < AbstractAdapter
|
|
6
|
+
def supports_fetch_foreign_keys?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def foreign_key_constraints(table, name = nil)
|
|
11
|
+
constraints = []
|
|
12
|
+
execute("SHOW CREATE TABLE #{table}", name).each do |row|
|
|
13
|
+
row[1].each do |create_line|
|
|
14
|
+
if create_line.strip =~ /CONSTRAINT `([^`]+)` FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)([^,]*)/
|
|
15
|
+
constraint = ForeignKeyConstraint.new(Regexp.last_match(1), table, Regexp.last_match(2), Regexp.last_match(3), Regexp.last_match(4), nil, nil)
|
|
16
|
+
|
|
17
|
+
constraint_params = {}
|
|
18
|
+
|
|
19
|
+
unless Regexp.last_match(5).nil?
|
|
20
|
+
Regexp.last_match(5).strip.split('ON ').each do |param|
|
|
21
|
+
constraint_params[Regexp.last_match(1).upcase] = Regexp.last_match(2).strip.upcase if param.strip =~ /([^ ]+) (.+)/
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
constraint.on_update = symbolize_foreign_key_constraint_action(constraint_params['UPDATE']) if constraint_params.include? 'UPDATE'
|
|
26
|
+
constraint.on_delete = symbolize_foreign_key_constraint_action(constraint_params['DELETE']) if constraint_params.include? 'DELETE'
|
|
27
|
+
|
|
28
|
+
constraints << constraint
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
constraints
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def remove_foreign_key_constraint(table_name, constraint_name)
|
|
37
|
+
execute "ALTER TABLE #{table_name} DROP FOREIGN KEY #{constraint_name}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Foreign Key support from http://wiki.rubyonrails.org/rails/pages/Foreign+Key+Schema+Dumper+Plugin
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
class PostgreSQLAdapter < AbstractAdapter
|
|
6
|
+
|
|
7
|
+
def supports_fetch_foreign_keys?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def foreign_key_constraints(table, name = nil)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
sql = "SELECT conname, pg_catalog.pg_get_constraintdef(oid) AS consrc FROM pg_catalog.pg_constraint WHERE contype='f' "
|
|
15
|
+
sql += "AND conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='#{table}')"
|
|
16
|
+
|
|
17
|
+
result = query(sql, name)
|
|
18
|
+
|
|
19
|
+
keys = []
|
|
20
|
+
re = /(?i)^FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)(?: ON UPDATE (\w+))?(?: ON DELETE (\w+))?$/
|
|
21
|
+
result.each do |row|
|
|
22
|
+
# pg_catalog.pg_get_constraintdef returns a string like this:
|
|
23
|
+
# FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE
|
|
24
|
+
if match = re.match(row[1])
|
|
25
|
+
|
|
26
|
+
keys << ForeignKeyConstraint.new(row[0],
|
|
27
|
+
table,
|
|
28
|
+
match[1],
|
|
29
|
+
match[2],
|
|
30
|
+
match[3],
|
|
31
|
+
symbolize_foreign_key_constraint_action(match[4]),
|
|
32
|
+
symbolize_foreign_key_constraint_action(match[5]))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
keys
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def remove_foreign_key_constraint(table_name, constraint_name)
|
|
40
|
+
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint_name}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module DrNicMagicModels
|
|
2
|
+
class Inflector
|
|
3
|
+
def table_names ; DrNicMagicModels::Schema.table_names; end
|
|
4
|
+
def tables ; DrNicMagicModels::Schema.tables; end
|
|
5
|
+
def models ; DrNicMagicModels::Schema.model; end
|
|
6
|
+
|
|
7
|
+
def class_name(table_name)
|
|
8
|
+
ActiveRecord::Base.class_name(table_name)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def post_class_creation(klass)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Mixed into a class that is dynamically created, unless
|
|
2
|
+
# the class was created by the Schema.load_schema process
|
|
3
|
+
# which builds the whole class, thus no magicalness is
|
|
4
|
+
# needed
|
|
5
|
+
module DrNicMagicModels::MagicModel
|
|
6
|
+
def self.append_features(base)
|
|
7
|
+
super
|
|
8
|
+
base.send(:include, InstanceMethods)
|
|
9
|
+
class << base
|
|
10
|
+
# Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
|
|
11
|
+
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
|
12
|
+
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
|
13
|
+
def reflect_on_association(association)
|
|
14
|
+
unless reflections[association]
|
|
15
|
+
# See if an assocation can be generated
|
|
16
|
+
self.new.send(association) rescue nil
|
|
17
|
+
end
|
|
18
|
+
reflections[association].is_a?(ActiveRecord::Reflection::AssociationReflection) ? reflections[association] : nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module InstanceMethods
|
|
24
|
+
|
|
25
|
+
def method_missing(method, *args, &block)
|
|
26
|
+
begin
|
|
27
|
+
super
|
|
28
|
+
rescue
|
|
29
|
+
if unknown_method? method
|
|
30
|
+
result = find_belongs_to method, *args, &block
|
|
31
|
+
result = find_has_some method, *args, &block if not result
|
|
32
|
+
result = find_has_some_indirect method, *args, &block if not result
|
|
33
|
+
return result if result
|
|
34
|
+
end
|
|
35
|
+
add_known_unknown method
|
|
36
|
+
raise
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def add_known_unknown(method)
|
|
41
|
+
@known_unknowns ||= {}
|
|
42
|
+
@known_unknowns[method] = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def unknown_method?(method)
|
|
46
|
+
@known_unknowns.nil? or @known_unknowns.include? method
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def find_belongs_to(method, *args, &block)
|
|
50
|
+
method_clean = clean_method method
|
|
51
|
+
fkc =
|
|
52
|
+
begin
|
|
53
|
+
self.class.connection.foreign_key_constraints(self.class.table_name, method_clean)
|
|
54
|
+
rescue NotImplementedError
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
if !fkc.nil? && fkc.length > 0
|
|
58
|
+
foreign_key = fkc.first.foreign_key
|
|
59
|
+
options = {:dependent => :destroy,
|
|
60
|
+
:foreign_key => fkc.first.foreign_key,
|
|
61
|
+
:class_name => self.class.class_name(fkc.first.reference_table)}
|
|
62
|
+
else
|
|
63
|
+
foreign_key = self.class.columns.select {|column| column.name == method_clean.to_s.foreign_key}.first
|
|
64
|
+
end
|
|
65
|
+
options ||= {}
|
|
66
|
+
return add_belongs_to(method, method_clean, options, *args, &block) if foreign_key
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add_belongs_to(method, method_clean, options, *args, &block)
|
|
70
|
+
self.class.send 'belongs_to', method_clean.to_sym, options rescue puts $!
|
|
71
|
+
self.send(method, *args, &block)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def find_has_some(method, *args, &block)
|
|
75
|
+
method_clean = clean_method method
|
|
76
|
+
fkc = [method_clean.to_s.pluralize, method_clean.to_s.singularize].inject({}) do |pair, table_name|
|
|
77
|
+
fkc = begin
|
|
78
|
+
self.class.connection.foreign_key_constraints(table_name)
|
|
79
|
+
rescue NotImplementedError
|
|
80
|
+
nil
|
|
81
|
+
rescue ActiveRecord::StatementInvalid
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
pair[table_name] = fkc if not fkc.blank?
|
|
85
|
+
pair
|
|
86
|
+
end
|
|
87
|
+
if not fkc.blank?
|
|
88
|
+
# assumes there is only one table found - that schema doesn't have a singular and plural table of same name
|
|
89
|
+
foreign_key = fkc.values.first.find {|fk| fk.reference_table == self.class.table_name}
|
|
90
|
+
if foreign_key
|
|
91
|
+
foreign_key = foreign_key.foreign_key
|
|
92
|
+
table_name = fkc.keys.first
|
|
93
|
+
klass = Module.const_get table_name.singularize.camelize rescue nil
|
|
94
|
+
options = {:foreign_key => foreign_key, :class_name => klass.name}
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
unless foreign_key
|
|
98
|
+
klass = Module.const_get method_clean.to_s.downcase.singularize.camelize rescue nil
|
|
99
|
+
foreign_key = klass.columns.select {|column| column.name == self.class.name.foreign_key}.first if klass
|
|
100
|
+
end
|
|
101
|
+
options ||= {}
|
|
102
|
+
return add_has_some(method, method_clean, options, *args, &block) if foreign_key
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def add_has_some(method, method_clean, options, *args, &block)
|
|
106
|
+
association = method_clean.singularize == method_clean ? 'has_one' : 'has_many'
|
|
107
|
+
self.class.send association, method_clean.to_sym, options rescue puts $!
|
|
108
|
+
self.send(method, *args, &block)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def find_has_some_indirect(method, *args, &block)
|
|
112
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue return
|
|
113
|
+
join_table = nil
|
|
114
|
+
self.connection.tables.each do |table|
|
|
115
|
+
unless [self.class.table_name, klass.table_name].include? table
|
|
116
|
+
columns = self.connection.columns(table).map(&:name)
|
|
117
|
+
join_table = table if columns.include?(self.class.to_s.foreign_key) and columns.include?(klass.to_s.foreign_key)
|
|
118
|
+
end
|
|
119
|
+
break if join_table
|
|
120
|
+
end
|
|
121
|
+
return add_has_some_through(join_table, method, *args, &block) if join_table
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def add_has_some_through(join_table, method, *args, &block)
|
|
125
|
+
self.class.send 'has_many', method, :through => join_table.to_sym
|
|
126
|
+
self.send(method, *args, &block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def clean_method(method)
|
|
132
|
+
method.to_s.gsub(/=$/,'') # remove any = from the end of the method name
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|