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.
Files changed (57) hide show
  1. data/CHANGELOG +9 -0
  2. data/History.txt +33 -0
  3. data/Manifest.txt +56 -0
  4. data/README +0 -0
  5. data/Rakefile +149 -0
  6. data/install.rb +30 -0
  7. data/lib/base.rb +12 -0
  8. data/lib/connection_adapters/abstract/schema_statements.rb +0 -0
  9. data/lib/connection_adapters/abstract_adapter.rb +32 -0
  10. data/lib/connection_adapters/mysql_adapter.rb +42 -0
  11. data/lib/connection_adapters/postgresql_adapter.rb +45 -0
  12. data/lib/dr_nic_magic_models/inflector.rb +14 -0
  13. data/lib/dr_nic_magic_models/magic_model.rb +135 -0
  14. data/lib/dr_nic_magic_models/schema.rb +270 -0
  15. data/lib/dr_nic_magic_models/validations.rb +46 -0
  16. data/lib/dr_nic_magic_models/version.rb +9 -0
  17. data/lib/dr_nic_magic_models.rb +34 -0
  18. data/lib/module.rb +33 -0
  19. data/lib/rails.rb +19 -0
  20. data/scripts/txt2html +66 -0
  21. data/scripts/txt2js +58 -0
  22. data/test/abstract_unit.rb +70 -0
  23. data/test/connections/native_mysql/connection.rb +14 -0
  24. data/test/connections/native_postgresql/connection.rb +12 -0
  25. data/test/connections/native_sqlite/connection.rb +10 -0
  26. data/test/dummy_test.rb +13 -0
  27. data/test/env_test.rb +10 -0
  28. data/test/fixtures/.DS_Store +0 -0
  29. data/test/fixtures/adjectives.yml +3 -0
  30. data/test/fixtures/adjectives_fun_users.yml +3 -0
  31. data/test/fixtures/db_definitions/mysql.drop.sql +4 -0
  32. data/test/fixtures/db_definitions/mysql.sql +58 -0
  33. data/test/fixtures/db_definitions/postgresql.sql +56 -0
  34. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  35. data/test/fixtures/fun_users.yml +14 -0
  36. data/test/fixtures/group_memberships.yml +4 -0
  37. data/test/fixtures/group_tag.yml +11 -0
  38. data/test/fixtures/groups.yml +12 -0
  39. data/test/foreign_keys_test.rb +0 -0
  40. data/test/fun_user_plus.rb +2 -0
  41. data/test/invisible_model_access_test.rb +71 -0
  42. data/test/invisible_model_assoc_test.rb +61 -0
  43. data/test/invisible_model_classes_test.rb +23 -0
  44. data/test/magic_module_test.rb +20 -0
  45. data/test/test_existing_model.rb +20 -0
  46. data/test.db +0 -0
  47. data/website/index.html +409 -0
  48. data/website/index.txt +291 -0
  49. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  50. data/website/stylesheets/screen.css +106 -0
  51. data/website/template.js +3 -0
  52. data/website/template.rhtml +55 -0
  53. data/website/version-raw.js +3 -0
  54. data/website/version-raw.txt +2 -0
  55. data/website/version.js +4 -0
  56. data/website/version.txt +3 -0
  57. 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
@@ -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