voraz-dr_nic_magic_models 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|