dr_nic_magic_models 0.8.1 → 0.9.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/History.txt +10 -0
- data/Manifest.txt +55 -0
- data/README +268 -0
- data/Rakefile +82 -107
- data/lib/base.rb +1 -5
- data/lib/dr_nic_magic_models.rb +4 -1
- data/lib/dr_nic_magic_models/inflector.rb +14 -0
- data/lib/dr_nic_magic_models/magic_model.rb +125 -0
- data/lib/dr_nic_magic_models/schema.rb +224 -196
- data/lib/dr_nic_magic_models/validations.rb +6 -6
- data/lib/dr_nic_magic_models/version.rb +2 -2
- data/lib/module.rb +33 -0
- data/scripts/txt2html +3 -2
- data/scripts/txt2js +3 -2
- data/test.db +0 -0
- data/test/abstract_unit.rb +4 -0
- data/test/connections/native_postgresql/connection.rb +0 -2
- data/test/connections/native_sqlite/connection.rb +10 -0
- data/test/fixtures/.DS_Store +0 -0
- data/test/fixtures/db_definitions/mysql.sql +0 -2
- data/test/fixtures/db_definitions/postgresql.sql +0 -1
- data/test/fixtures/db_definitions/sqlite.sql +49 -0
- data/test/invisible_model_access_test.rb +8 -2
- data/test/invisible_model_assoc_test.rb +17 -13
- data/test/magic_module_test.rb +20 -0
- data/website/index.html +283 -297
- data/website/index.txt +4 -16
- data/website/version-raw.js +1 -1
- data/website/version.js +1 -1
- metadata +53 -56
- data/scripts/http-access2-2.0.6.gem +0 -0
- data/scripts/rubyforge-orig +0 -217
- data/test/fixtures/db_definitions/postgresql.drop.sql +0 -4
data/lib/base.rb
CHANGED
@@ -3,14 +3,10 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
class Base
|
5
5
|
class << self
|
6
|
-
|
7
6
|
public
|
8
|
-
|
9
7
|
def get_unique_index_columns
|
10
8
|
self.connection.indexes(self.table_name, "#{self.name} Indexes").select { |index| index.unique && index.columns.size == 1 }.map{ |index| index.columns.first }
|
11
|
-
end
|
9
|
+
end
|
12
10
|
end
|
13
|
-
|
14
|
-
|
15
11
|
end
|
16
12
|
end
|
data/lib/dr_nic_magic_models.rb
CHANGED
@@ -14,15 +14,18 @@ module DrNicMagicModels
|
|
14
14
|
Logger = RAILS_DEFAULT_LOGGER rescue Logger.new(STDERR)
|
15
15
|
end
|
16
16
|
|
17
|
+
require 'dr_nic_magic_models/magic_model'
|
17
18
|
require 'dr_nic_magic_models/schema'
|
18
19
|
require 'dr_nic_magic_models/validations'
|
20
|
+
require 'dr_nic_magic_models/inflector'
|
19
21
|
require 'base'
|
22
|
+
require 'module'
|
20
23
|
require 'rails' rescue nil
|
21
24
|
require 'connection_adapters/abstract_adapter'
|
22
25
|
require 'connection_adapters/mysql_adapter'
|
23
26
|
require 'connection_adapters/postgresql_adapter'
|
24
27
|
|
25
28
|
# load the schema
|
26
|
-
DrNicMagicModels::Schema.load_schema
|
29
|
+
# TODO - add this to README - DrNicMagicModels::Schema.load_schema(true)
|
27
30
|
|
28
31
|
|
@@ -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,125 @@
|
|
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
|
+
fkc =
|
51
|
+
begin
|
52
|
+
self.class.connection.foreign_key_constraints(self.class.table_name, method)
|
53
|
+
rescue NotImplementedError
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
if not fkc.nil? and fkc.length > 0
|
57
|
+
foreign_key = fkc.first.foreign_key
|
58
|
+
options = {:dependent => :destroy, :foreign_key => fkc.first.foreign_key, :class_name => self.class.class_name(fkc.first.reference_table)}
|
59
|
+
else
|
60
|
+
foreign_key = self.class.columns.select {|column| column.name == method.to_s.foreign_key}.first
|
61
|
+
end
|
62
|
+
options ||= {}
|
63
|
+
return add_belongs_to(method, options, *args, &block) if foreign_key
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_belongs_to(method, options, *args, &block)
|
67
|
+
self.class.send 'belongs_to', method, options rescue puts $!
|
68
|
+
self.send(method, *args, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_has_some(method, *args, &block)
|
72
|
+
fkc = [method.to_s.pluralize, method.to_s.singularize].inject({}) do |pair, table_name|
|
73
|
+
fkc = begin
|
74
|
+
self.class.connection.foreign_key_constraints(table_name)
|
75
|
+
rescue NotImplementedError
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
pair[table_name] = fkc if not fkc.blank?
|
79
|
+
pair
|
80
|
+
end
|
81
|
+
if not fkc.blank?
|
82
|
+
# assumes there is only one table found - that schema doesn't have a singular and plural table of same name
|
83
|
+
foreign_key = fkc.values.first.find {|fk| fk.reference_table == self.class.table_name}
|
84
|
+
if foreign_key
|
85
|
+
foreign_key = foreign_key.foreign_key
|
86
|
+
table_name = fkc.keys.first
|
87
|
+
klass = Module.const_get table_name.singularize.camelize rescue nil
|
88
|
+
options = {:foreign_key => foreign_key, :class_name => klass.name}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
unless foreign_key
|
92
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue nil
|
93
|
+
foreign_key = klass.columns.select {|column| column.name == self.class.name.foreign_key}.first if klass
|
94
|
+
end
|
95
|
+
options ||= {}
|
96
|
+
return add_has_some(method, options, *args, &block) if foreign_key
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_has_some(method, options, *args, &block)
|
100
|
+
_method = method.to_s
|
101
|
+
association = _method.singularize == _method ? 'has_one' : 'has_many'
|
102
|
+
self.class.send association, method, options rescue puts $!
|
103
|
+
self.send(method, *args, &block)
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_has_some_indirect(method, *args, &block)
|
107
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue return
|
108
|
+
join_table = nil
|
109
|
+
self.connection.tables.each do |table|
|
110
|
+
unless [self.class.table_name, klass.table_name].include? table
|
111
|
+
columns = self.connection.columns(table).map(&:name)
|
112
|
+
join_table = table if columns.include?(self.class.to_s.foreign_key) and columns.include?(klass.to_s.foreign_key)
|
113
|
+
end
|
114
|
+
break if join_table
|
115
|
+
end
|
116
|
+
return add_has_some_through(join_table, method, *args, &block) if join_table
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_has_some_through(join_table, method, *args, &block)
|
120
|
+
puts "#{self.class}.has_many #{method.inspect}, #{join_table.inspect}, #{args.inspect}"
|
121
|
+
self.class.send 'has_many', method, :through => join_table.to_sym
|
122
|
+
self.send(method, *args, &block)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -1,227 +1,255 @@
|
|
1
1
|
module DrNicMagicModels
|
2
|
-
|
2
|
+
|
3
|
+
# ONE Schema per namespace module
|
4
|
+
# Person, Company, etc share the Object namespace module, ie. ::Person, ::Company
|
5
|
+
# Blog::Post, Blog::Comment, share the Blog namespace module
|
3
6
|
class Schema
|
4
|
-
|
7
|
+
attr_reader :modul
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
def initialize(modul)
|
10
|
+
@modul = modul
|
11
|
+
@table_name_prefix = modul.instance_variable_get("@table_name_prefix") rescue ''
|
12
|
+
logger.info "Create Schema for #{@modul}, table_name_prefix '#{@table_name_prefix}'"
|
13
|
+
end
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
15
|
+
cattr_accessor :inflector
|
16
|
+
cattr_accessor :superklass
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
# Need to store models etc per-module, not in @ @models
|
19
|
+
def inflector
|
20
|
+
@inflector ||= Inflector.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# all in lower case please
|
24
|
+
ReservedTables = [:schema_info, :sessions]
|
25
|
+
@models = nil
|
26
|
+
|
27
|
+
def logger
|
28
|
+
@logger ||= DrNicMagicModels::Logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def models
|
32
|
+
load_schema if @models.nil?
|
33
|
+
@models
|
34
|
+
end
|
35
|
+
|
36
|
+
def tables
|
37
|
+
load_schema if @tables.nil?
|
38
|
+
@tables
|
39
|
+
end
|
40
|
+
|
41
|
+
def table_names
|
42
|
+
load_schema if @table_names.nil?
|
43
|
+
@table_names
|
44
|
+
end
|
45
|
+
|
46
|
+
def fks_on_table(table_name)
|
47
|
+
load_schema if @models.nil?
|
48
|
+
@fks_by_table[table_name.to_s] || []
|
49
|
+
end
|
50
|
+
|
51
|
+
# active record only support 2 column link tables, otherwise use a model table, has_many and through
|
52
|
+
def is_link_table?(table_name)
|
53
|
+
load_schema if @models.nil?
|
54
|
+
return @link_tables[table_name] if ! @link_tables[table_name].nil?
|
55
|
+
column_names = @conn.columns(table_name).map{|x| x.name }
|
56
|
+
@link_tables[table_name] = ! column_names.include?("id") && column_names.length == 2 && column_names.select { |x| x =~ /_id$/ } == column_names
|
57
|
+
return @link_tables[table_name]
|
58
|
+
end
|
59
|
+
|
60
|
+
def link_tables_for_class(klass)
|
61
|
+
load_schema if @models.nil?
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_schema(preload = false)
|
65
|
+
return if !@models.nil?
|
18
66
|
|
19
|
-
|
20
|
-
|
21
|
-
@@fks_by_table[table_name.to_s] || []
|
22
|
-
end
|
67
|
+
@superklass ||= ActiveRecord::Base
|
68
|
+
raise "No database connection" if !(@conn = @superklass.connection)
|
23
69
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
column_names = @conn.columns(table_name).map{|x| x.name }
|
29
|
-
@@link_tables[table_name] = ! column_names.include?("id") && column_names.length == 2 && column_names.select { |x| x =~ /_id$/ } == column_names
|
30
|
-
return @@link_tables[table_name]
|
31
|
-
end
|
32
|
-
|
33
|
-
def link_tables_for_class(klass)
|
34
|
-
load_schema if @@models.nil?
|
35
|
-
end
|
36
|
-
|
37
|
-
def load_schema
|
70
|
+
@models = ModelHash.new
|
71
|
+
@tables = Hash.new
|
72
|
+
@fks_by_table = Hash.new
|
73
|
+
@link_tables = Hash.new
|
38
74
|
|
39
|
-
|
75
|
+
@table_names = @conn.tables
|
76
|
+
@table_names = @table_names.grep(/^#{@table_name_prefix}/) if @table_name_prefix
|
77
|
+
@table_names = @table_names.sort
|
40
78
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@@fks_by_table = Hash.new
|
46
|
-
@@link_tables = Hash.new
|
47
|
-
|
48
|
-
tables = @conn.tables.sort
|
49
|
-
|
50
|
-
# Work out which tables are in the model and which aren't
|
51
|
-
tables.each do |table_name|
|
52
|
-
|
53
|
-
# deal with reserved tables & link_tables && other stray id-less tables
|
54
|
-
next if ReservedTables.include?(table_name.downcase.to_sym) || is_link_table?(table_name) || ! @conn.columns(table_name).map{ |x| x.name}.include?("id")
|
55
|
-
|
56
|
-
# a model table then...
|
57
|
-
model_class_name = ActiveRecord::Base.class_name(table_name)
|
58
|
-
|
59
|
-
logger.debug "Got a model table: #{table_name} => class #{model_class_name}"
|
60
|
-
|
61
|
-
@@models[model_class_name] = table_name
|
62
|
-
@@tables[table_name] = model_class_name
|
63
|
-
|
64
|
-
# create by MAGIC!
|
65
|
-
begin
|
66
|
-
klass = model_class_name.constantize
|
67
|
-
rescue Exception => e
|
68
|
-
# create our class!
|
69
|
-
class_def = <<-end_eval
|
70
|
-
class #{model_class_name} < ActiveRecord::Base
|
71
|
-
set_table_name('#{table_name}')
|
72
|
-
end
|
73
|
-
end_eval
|
74
|
-
eval(class_def, TOPLEVEL_BINDING)
|
75
|
-
klass = model_class_name.constantize
|
76
|
-
end
|
77
|
-
|
78
|
-
# magic up some validation
|
79
|
-
klass.send(:extend, DrNicMagicModels::Validations)
|
80
|
-
klass.send(:generate_validations)
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
# Process FKs?
|
85
|
-
if @conn.supports_fetch_foreign_keys?
|
79
|
+
logger.info "For #{modul} tables are #{@table_names.inspect}"
|
80
|
+
|
81
|
+
# Work out which tables are in the model and which aren't
|
82
|
+
@table_names.each do |table_name|
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
# Try to work out our link tables now...
|
99
|
-
@@models.keys.sort.each{|klass| process_table(@@models[klass.to_s])}
|
100
|
-
@@link_tables.keys.sort.each{|table_name| process_link_table(table_name) if @@link_tables[table_name]}
|
101
|
-
|
102
|
-
|
103
|
-
end
|
84
|
+
# deal with reserved tables & link_tables && other stray id-less tables
|
85
|
+
#key = 'id'
|
86
|
+
#case ActiveRecord::Base.primary_key_prefix_type
|
87
|
+
# when :table_name
|
88
|
+
# key = Inflector.foreign_key(table_name, false)
|
89
|
+
# when :table_name_with_underscore
|
90
|
+
# key = Inflector.foreign_key(table_name)
|
91
|
+
#end
|
92
|
+
#next if ReservedTables.include?(table_name.downcase.to_sym) ||
|
93
|
+
# is_link_table?(table_name) ||
|
94
|
+
# ! @conn.columns(table_name).map{ |x| x.name}.include?(key)
|
104
95
|
|
105
|
-
|
106
|
-
|
107
|
-
logger.debug "Processing model table #{table_name}"
|
108
|
-
|
109
|
-
# ok, so let's look at the foreign keys on the table...
|
110
|
-
belongs_to_klass = @@tables[table_name].constantize rescue return
|
111
|
-
|
112
|
-
processed_columns = Hash.new
|
113
|
-
|
114
|
-
fks_on_table(table_name).each do |fk|
|
115
|
-
logger.debug "Found FK column by suffix _id [#{fk.foreign_key}]"
|
116
|
-
has_some_klass = Inflector.classify(fk.reference_table).constantize rescue next
|
117
|
-
processed_columns[fk.foreign_key] = { :has_some_klass => has_some_klass }
|
118
|
-
processed_columns[fk.foreign_key].merge! add_has_some_belongs_to(belongs_to_klass, fk.foreign_key, has_some_klass) rescue next
|
119
|
-
end
|
96
|
+
table_name_clean = table_name.gsub(/^#{@table_name_prefix}/,'')
|
120
97
|
|
121
|
-
|
122
|
-
|
123
|
-
next if not column_name =~ /_id$/
|
124
|
-
logger.debug "Found FK column by suffix _id [#{column_name}]"
|
125
|
-
if processed_columns.key?(column_name)
|
126
|
-
logger.debug "Skipping, already processed"
|
127
|
-
next
|
128
|
-
end
|
129
|
-
has_some_klass = Inflector.classify(column_name.sub(/_id$/,"")).constantize rescue next
|
130
|
-
processed_columns[column_name] = { :has_some_klass => has_some_klass }
|
131
|
-
processed_columns[column_name].merge! add_has_some_belongs_to(belongs_to_klass, column_name, has_some_klass) rescue next
|
132
|
-
end
|
98
|
+
# a model table then...
|
99
|
+
model_class_name = inflector.class_name(table_name_clean)
|
133
100
|
|
134
|
-
|
101
|
+
logger.debug "Got a model table: #{table_name} => class #{model_class_name}"
|
135
102
|
|
136
|
-
|
137
|
-
|
103
|
+
@models[model_class_name] = table_name
|
104
|
+
@tables[table_name] = model_class_name
|
138
105
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
106
|
+
if preload
|
107
|
+
# create by MAGIC!
|
108
|
+
klass = model_class_name.constantize
|
109
|
+
|
110
|
+
# Process FKs?
|
111
|
+
if @conn.supports_fetch_foreign_keys?
|
146
112
|
|
113
|
+
tables.each do |table_name|
|
114
|
+
logger.debug "Getting FKs for #{table_name}"
|
115
|
+
@fks_by_table[table_name] = Array.new
|
116
|
+
@conn.foreign_key_constraints(table_name).each do |fk|
|
117
|
+
logger.debug "Got one: #{fk}"
|
118
|
+
@fks_by_table[table_name].push(fk)
|
119
|
+
end # do each fk
|
120
|
+
|
121
|
+
end # each table
|
122
|
+
end
|
123
|
+
|
124
|
+
# Try to work out our link tables now...
|
125
|
+
@models.keys.sort.each{|klass| process_table(@models[klass.to_s])}
|
126
|
+
@link_tables.keys.sort.each{|table_name| process_link_table(table_name) if @link_tables[table_name]}
|
127
|
+
end
|
147
128
|
end
|
148
|
-
|
149
|
-
|
150
|
-
def add_has_some_belongs_to(belongs_to_klass, belongs_to_fk, has_some_klass)
|
151
129
|
|
152
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
def process_table(table_name)
|
153
133
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
134
|
+
logger.debug "Processing model table #{table_name}"
|
135
|
+
|
136
|
+
# ok, so let's look at the foreign keys on the table...
|
137
|
+
belongs_to_klass = @tables[table_name].constantize rescue return
|
138
|
+
|
139
|
+
processed_columns = Hash.new
|
140
|
+
|
141
|
+
fks_on_table(table_name).each do |fk|
|
142
|
+
logger.debug "Found FK column by suffix _id [#{fk.foreign_key}]"
|
143
|
+
has_some_klass = Inflector.classify(fk.reference_table).constantize rescue next
|
144
|
+
processed_columns[fk.foreign_key] = { :has_some_klass => has_some_klass }
|
145
|
+
processed_columns[fk.foreign_key].merge! add_has_some_belongs_to(belongs_to_klass, fk.foreign_key, has_some_klass) rescue next
|
146
|
+
end
|
147
|
+
|
148
|
+
column_names = @conn.columns(table_name).map{ |x| x.name}
|
149
|
+
column_names.each do |column_name|
|
150
|
+
next if not column_name =~ /_id$/
|
151
|
+
logger.debug "Found FK column by suffix _id [#{column_name}]"
|
152
|
+
if processed_columns.key?(column_name)
|
153
|
+
logger.debug "Skipping, already processed"
|
154
|
+
next
|
155
|
+
end
|
156
|
+
has_some_klass = Inflector.classify(column_name.sub(/_id$/,"")).constantize rescue next
|
157
|
+
processed_columns[column_name] = { :has_some_klass => has_some_klass }
|
158
|
+
processed_columns[column_name].merge! add_has_some_belongs_to(belongs_to_klass, column_name, has_some_klass) rescue next
|
159
|
+
end
|
160
|
+
|
161
|
+
#TODO: what if same classes in table?
|
162
|
+
|
163
|
+
# is this a link table with attributes? (has_many through?)
|
164
|
+
return if processed_columns.keys.length < 2
|
165
|
+
|
166
|
+
processed_columns.keys.each do |key1|
|
167
|
+
processed_columns.keys.each do |key2|
|
168
|
+
next if key1 == key2
|
169
|
+
logger.debug "\n*** #{processed_columns[key1][:has_some_class]}.send 'has_many', #{processed_columns[key2][:belongs_to_name].to_s.pluralize.to_sym}, :through => #{processed_columns[key2][:has_some_name]}\n\n"
|
170
|
+
processed_columns[key1][:has_some_class].send 'has_many', processed_columns[key2][:belongs_to_name].to_s.pluralize.to_sym, :through => processed_columns[key2][:has_some_name].to_sym
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
def add_has_some_belongs_to(belongs_to_klass, belongs_to_fk, has_some_klass)
|
177
|
+
|
178
|
+
logger.debug "Trying to add a #{belongs_to_klass} belongs_to #{has_some_klass}..."
|
179
|
+
|
180
|
+
# so this is a belongs_to & has_some style relationship...
|
181
|
+
# is it a has_many, or a has_one? Well, let's assume a has_one has a unique index on the column please... good db design, haha!
|
182
|
+
unique = belongs_to_klass.get_unique_index_columns.include?(belongs_to_fk)
|
183
|
+
belongs_to_name = belongs_to_fk.sub(/_id$/, '').to_sym
|
184
|
+
|
185
|
+
logger.debug "\n*** #{belongs_to_klass}.send 'belongs_to', #{belongs_to_name}, :class_name => #{has_some_klass}, :foreign_key => #{belongs_to_fk}\n"
|
186
|
+
belongs_to_klass.send(:belongs_to, belongs_to_name, :class_name => has_some_klass.to_s, :foreign_key => belongs_to_fk.to_sym)
|
187
|
+
|
188
|
+
# work out if we need a prefix
|
189
|
+
has_some_name = (
|
190
|
+
(unique ? belongs_to_klass.table_name.singularize : belongs_to_klass.table_name) +
|
191
|
+
(belongs_to_name.to_s == has_some_klass.table_name.singularize ? "" : "_as_"+belongs_to_name.to_s)
|
192
|
+
).downcase.to_sym
|
193
|
+
method = unique ? :has_one : :has_many
|
194
|
+
logger.debug "\n*** #{has_some_klass}.send(#{method}, #{has_some_name}, :class_name => #{belongs_to_klass.to_s}, :foreign_key => #{belongs_to_fk.to_sym})\n\n"
|
195
|
+
has_some_klass.send(method, has_some_name, :class_name => belongs_to_klass.to_s, :foreign_key => belongs_to_fk.to_sym)
|
196
|
+
|
197
|
+
return { :method => method, :belongs_to_name => belongs_to_name, :has_some_name => has_some_name, :has_some_class => has_some_klass }
|
167
198
|
|
168
|
-
|
199
|
+
end
|
200
|
+
|
201
|
+
def process_link_table(table_name)
|
202
|
+
|
203
|
+
logger.debug "Processing link table #{table_name}"
|
169
204
|
|
205
|
+
classes_map = Hash.new
|
206
|
+
column_names = @conn.columns(table_name).map{ |x| x.name}
|
207
|
+
|
208
|
+
# use foreign keys first
|
209
|
+
fks_on_table(table_name).each do |fk|
|
210
|
+
logger.debug "Processing fk: #{fk}"
|
211
|
+
klass = Inflector.classify(fk.reference_table).constantize rescue logger.debug("Cannot find model #{class_name} for table #{fk.reference_table}") && return
|
212
|
+
classes_map[fk.foreign_key] = klass
|
170
213
|
end
|
214
|
+
|
215
|
+
logger.debug "Got #{classes_map.keys.length} references from FKs"
|
216
|
+
|
217
|
+
if classes_map.keys.length < 2
|
171
218
|
|
172
|
-
|
173
|
-
|
174
|
-
logger.debug "Processing link table #{table_name}"
|
175
|
-
|
176
|
-
classes_map = Hash.new
|
177
|
-
column_names = @conn.columns(table_name).map{ |x| x.name}
|
178
|
-
|
179
|
-
# use foreign keys first
|
180
|
-
fks_on_table(table_name).each do |fk|
|
181
|
-
logger.debug "Processing fk: #{fk}"
|
182
|
-
klass = Inflector.classify(fk.reference_table).constantize rescue logger.debug("Cannot find model #{class_name} for table #{fk.reference_table}") && return
|
183
|
-
classes_map[fk.foreign_key] = klass
|
184
|
-
end
|
185
|
-
|
186
|
-
logger.debug "Got #{classes_map.keys.length} references from FKs"
|
187
|
-
|
188
|
-
if classes_map.keys.length < 2
|
189
|
-
|
190
|
-
#Fall back on good ol _id recognition
|
219
|
+
#Fall back on good ol _id recognition
|
191
220
|
|
192
|
-
|
221
|
+
column_names.each do |column_name|
|
193
222
|
|
194
|
-
|
195
|
-
|
196
|
-
|
223
|
+
# check we haven't processed by fks already
|
224
|
+
next if ! classes_map[column_name].nil?
|
225
|
+
referenced_table = column_name.sub(/_id$/, '')
|
197
226
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
227
|
+
begin
|
228
|
+
klass = Inflector.classify(referenced_table).constantize
|
229
|
+
# fall back on FKs here
|
230
|
+
if ! klass.nil?
|
231
|
+
classes_map[column_name] = klass
|
232
|
+
end
|
233
|
+
rescue
|
234
|
+
end
|
207
235
|
end
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# not detected the link table?
|
239
|
+
logger.debug "Got #{classes_map.keys.length} references"
|
240
|
+
logger.debug "Cannot detect both tables referenced in link table" && return if classes_map.keys.length != 2
|
241
|
+
|
242
|
+
logger.debug "Adding habtm relationship"
|
243
|
+
|
244
|
+
logger.debug "\n*** #{classes_map[column_names[0]]}.send 'has_and_belongs_to_many', #{column_names[1].sub(/_id$/,'').pluralize.to_sym}, :class_name => #{classes_map[column_names[1]].to_s}, :join_table => #{table_name.to_sym}\n"
|
245
|
+
logger.debug "\n*** #{classes_map[column_names[1]]}.send 'has_and_belongs_to_many', #{column_names[0].sub(/_id$/,'').pluralize.to_sym}, :class_name => #{classes_map[column_names[0]].to_s}, :join_table => #{table_name.to_sym}\n\n"
|
246
|
+
|
247
|
+
classes_map[column_names[0]].send 'has_and_belongs_to_many', column_names[1].sub(/_id$/,'').pluralize.to_sym, :class_name => classes_map[column_names[1]].to_s, :join_table => table_name.to_sym
|
248
|
+
classes_map[column_names[1]].send 'has_and_belongs_to_many', column_names[0].sub(/_id$/,'').pluralize.to_sym, :class_name => classes_map[column_names[0]].to_s, :join_table => table_name.to_sym
|
249
|
+
|
250
|
+
end
|
223
251
|
end
|
224
|
-
|
252
|
+
|
225
253
|
class ModelHash < Hash
|
226
254
|
def unenquire(class_id)
|
227
255
|
@enquired ||= {}
|