rare_map 0.9.7 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +183 -2
- data/bin/raremap +5 -0
- data/lib/rare_map/config_loader.rb +108 -0
- data/lib/rare_map/model_builder.rb +112 -0
- data/lib/rare_map/model_generator.rb +66 -0
- data/lib/rare_map/rails_locator.rb +16 -16
- data/lib/rare_map/schema_parser.rb +93 -0
- data/lib/rare_map/schema_reader.rb +19 -0
- data/lib/rare_map/version.rb +3 -3
- data/lib/rare_map.rb +46 -32
- metadata +15 -13
- data/lib/rare_map/database_to_schema_builder.rb +0 -27
- data/lib/rare_map/inheritor_util.rb +0 -50
- data/lib/rare_map/schema_to_hash_mapping.rb +0 -53
- data/lib/rare_map/schema_to_model_builder.rb +0 -74
- data/lib/rare_map/schema_to_relation_builder.rb +0 -104
data/README.rdoc
CHANGED
@@ -1,6 +1,187 @@
|
|
1
|
-
= rare_map
|
1
|
+
= rare_map >= 1.0.0
|
2
|
+
|
3
|
+
Relational db to ActiveREcord models MAPper
|
4
|
+
|
5
|
+
RareMap can be used for BOTH standalone application & Rails
|
6
|
+
|
7
|
+
WARN: rare_map >= 1.0.0 is totally not compatible with 0.9.x
|
8
|
+
|
9
|
+
* Installation:
|
10
|
+
|
11
|
+
gem install rare_map
|
12
|
+
|
13
|
+
== Basic RareMap use
|
14
|
+
|
15
|
+
==== Standalone:
|
16
|
+
Create a new database.yml with following lines in the root of your application
|
17
|
+
|
18
|
+
==== Rails:
|
19
|
+
Add following lines to config/database.yml of rails
|
20
|
+
|
21
|
+
* database.yml
|
22
|
+
|
23
|
+
rare_map:
|
24
|
+
legacy_db:
|
25
|
+
adapter: sqlite3
|
26
|
+
database: db/db1.sqlite3
|
27
|
+
|
28
|
+
your_db:
|
29
|
+
adapter: mysql2
|
30
|
+
host: localhost
|
31
|
+
database: db_name
|
32
|
+
port: 3306
|
33
|
+
username: user
|
34
|
+
password: pw
|
35
|
+
|
36
|
+
* Run following command in the root of your application or rails
|
37
|
+
|
38
|
+
$ raremap
|
39
|
+
|
40
|
+
* Standalone: A demo.rb example is generated for you in the root of your applicaiton
|
41
|
+
|
42
|
+
* Rails: Add following line to your config/application.rb
|
43
|
+
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
|
44
|
+
|
45
|
+
== Advanced RareMap use
|
46
|
+
|
47
|
+
==== Seperate databases to groups(highly recommended)
|
48
|
+
|
49
|
+
rare_map:
|
50
|
+
her_group:
|
51
|
+
-
|
52
|
+
db1:
|
53
|
+
adapter: sqlite3
|
54
|
+
database: db/db1.sqlite3
|
55
|
+
-
|
56
|
+
db2:
|
57
|
+
adapter: sqlite3
|
58
|
+
database: db/db1.sqlite3
|
59
|
+
|
60
|
+
his_group:
|
61
|
+
-
|
62
|
+
db1:
|
63
|
+
adapter: sqlite3
|
64
|
+
database: db/db3.sqlite3
|
65
|
+
-
|
66
|
+
db2:
|
67
|
+
adapter: sqlite3
|
68
|
+
database: db/db4.sqlite3
|
69
|
+
|
70
|
+
There are benefits by separating databases into groups
|
71
|
+
1. Associations are built between databases within a group
|
72
|
+
2. Model name is prepended with group name
|
73
|
+
3. Models of a group are organized within a folder
|
74
|
+
|
75
|
+
If all your data reside in several legacy databases, it is important to build back those associations across databases
|
76
|
+
|
77
|
+
If there are 2 or more tables with the same name, giving them group names could avoid naming collision
|
78
|
+
|
79
|
+
If there are tons of tables, it is better to organize them well
|
80
|
+
|
81
|
+
|
82
|
+
==== Set up RareMap Options
|
83
|
+
rare_map_opts:
|
84
|
+
foreign_key:
|
85
|
+
suffix: fk
|
86
|
+
alias:
|
87
|
+
abnormal_fk1: table1
|
88
|
+
abnormal_fk2: table2
|
89
|
+
primary_key:
|
90
|
+
table1: abnormal_pk
|
91
|
+
|
92
|
+
* rare_map_opts[foreign_key][suffix]: If your foreign keys are not ended with 'id', you can specify the suffix you want here
|
93
|
+
* rare_map_opts[foreign_key][alias]: If naming convention is not followed by some foreign keys, you can list them here
|
94
|
+
* rare_map_opts[primary_key]: Usually rare_map can identify the primary key of a table, if it fails, please list primary keys here
|
95
|
+
|
96
|
+
==== RareMap Options Precedence
|
97
|
+
|
98
|
+
You can place rare_map options in 3 ways
|
99
|
+
|
100
|
+
rare_map:
|
101
|
+
rare_map_opts: # Global options
|
102
|
+
...
|
103
|
+
group1:
|
104
|
+
-
|
105
|
+
rare_map_opts: # Group options
|
106
|
+
...
|
107
|
+
-
|
108
|
+
db1:
|
109
|
+
...
|
110
|
+
legacy_db:
|
111
|
+
adapter: sqlite3
|
112
|
+
database: db/db.sqlite3
|
113
|
+
rare_map_opts: # DB options
|
114
|
+
...
|
115
|
+
|
116
|
+
* Precedence: DB > Group > Global
|
117
|
+
|
118
|
+
|
119
|
+
= rare_map ~> 0.9.x(deprecated)
|
120
|
+
|
121
|
+
Relational DB to ActiveRecord models Mapper
|
122
|
+
|
123
|
+
* Installation:
|
124
|
+
|
125
|
+
gem install rare_map
|
126
|
+
|
127
|
+
== Basic RareMap use
|
128
|
+
|
129
|
+
Set up your legacay db under your Rails applicaiton
|
130
|
+
|
131
|
+
* database.yml
|
132
|
+
|
133
|
+
mylegacy_db1:
|
134
|
+
adapter: sqlite3
|
135
|
+
database: db/db1.sqlite3
|
136
|
+
|
137
|
+
mylegacy_db2:
|
138
|
+
adapter: sqlite3
|
139
|
+
database: db/db2/sqlite3
|
140
|
+
|
141
|
+
hislegacy_db1:
|
142
|
+
adapter: sqlite3
|
143
|
+
database: db/db1.sqlite3
|
144
|
+
|
145
|
+
"mylegacy" is treated as a group name.
|
146
|
+
|
147
|
+
You MUST give all your legacy databases a group name.
|
148
|
+
|
149
|
+
The associations of databases with the same group name will be built together.
|
150
|
+
|
151
|
+
|
152
|
+
* Create a {RAILS_ROOT}/demo.rb file with following content under your Rails application
|
153
|
+
|
154
|
+
RareMap.mapping
|
155
|
+
|
156
|
+
* Under terminal
|
157
|
+
|
158
|
+
ruby demo.rb
|
159
|
+
|
160
|
+
* Add following line to your config/application.rb
|
161
|
+
|
162
|
+
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
|
163
|
+
|
164
|
+
== Advanced RareMap use
|
165
|
+
|
166
|
+
If the foreign key of your legacy db is not end with '_id', you can change it manually.
|
167
|
+
|
168
|
+
foreign_keys = { :mylegacy => 'id', :hislegacy => 'id' }
|
169
|
+
|
170
|
+
If the foreign key is not started with regular table names, you can create aliases for them.
|
171
|
+
|
172
|
+
aliases = { :mylegacy => { :pclid => :protocol, personid => people }, :hislegacy => { :measid => measurement } }
|
173
|
+
|
174
|
+
Some of the databases like Oracle won't show the primary key on the dump of schema. If your primary key of a table is not 'id' and it didn't show the schema either. You can specify your special primary key beside 'id' if you want.
|
175
|
+
|
176
|
+
primary_keys = { :mylegacy => { :table1 => 'table1id' }, :hislegacy => { :table2 => 'table2id' } }
|
177
|
+
|
178
|
+
When you using the RareMap
|
179
|
+
|
180
|
+
info = RareMap.mapping(:foreign_key => foreign_keys, :alias => aliases, :primary_key => primary_keys)
|
181
|
+
|
182
|
+
[info]
|
183
|
+
It contains all details of your tables.
|
2
184
|
|
3
|
-
Description goes here.
|
4
185
|
|
5
186
|
== Contributing to rare_map
|
6
187
|
|
data/bin/raremap
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module RareMap
|
4
|
+
module ConfigLoader
|
5
|
+
OPTS_KEY = 'rare_map_opts'
|
6
|
+
|
7
|
+
def load_config(path, file_name = 'database.yml')
|
8
|
+
config = YAML.load_file "#{path}#{file_name}"
|
9
|
+
organize_config_properties config['rare_map'] || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def organize_config_properties(raw_config)
|
14
|
+
db_profiles = []
|
15
|
+
global_opts = Options.new(raw_config.delete OPTS_KEY) if raw_config[OPTS_KEY]
|
16
|
+
|
17
|
+
raw_config.each do |k, v|
|
18
|
+
case v.class.name
|
19
|
+
when 'Hash'
|
20
|
+
if v[OPTS_KEY]
|
21
|
+
db_profiles << DatabaseProfile.new(remove_opts(v), Options.new(v[OPTS_KEY]))
|
22
|
+
else
|
23
|
+
db_profiles << DatabaseProfile.new(v, global_opts)
|
24
|
+
end
|
25
|
+
when 'Array'
|
26
|
+
v = v.reduce(:merge)
|
27
|
+
group_opts = Options.new(v.delete(OPTS_KEY), k)
|
28
|
+
v.each do |db, config|
|
29
|
+
if config[OPTS_KEY]
|
30
|
+
db_profiles << DatabaseProfile.new(remove_opts(config), Options.new(config[OPTS_KEY], k))
|
31
|
+
else
|
32
|
+
db_profiles << DatabaseProfile.new(config, group_opts || global_opts)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
db_profiles
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_opts(db)
|
42
|
+
db.select { |k, _| k != OPTS_KEY }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class DatabaseProfile
|
47
|
+
attr_reader :connection, :options
|
48
|
+
attr_accessor :schema, :tables
|
49
|
+
|
50
|
+
def initialize(connection, options)
|
51
|
+
@connection = connection
|
52
|
+
@options = options || Options.new
|
53
|
+
@tables = []
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Options
|
58
|
+
attr_reader :opts
|
59
|
+
|
60
|
+
def initialize(raw_opts = nil, group = nil)
|
61
|
+
@opts = { 'group' => 'default',
|
62
|
+
'primary_key' => {},
|
63
|
+
'foreign_key' => { 'suffix' => nil, 'alias' => {} } }
|
64
|
+
|
65
|
+
if raw_opts and raw_opts.kind_of? Hash
|
66
|
+
if raw_opts['group']
|
67
|
+
@opts['group'] = raw_opts['group']
|
68
|
+
end
|
69
|
+
if raw_opts['primary_key'].kind_of? Hash
|
70
|
+
@opts['primary_key'] = raw_opts['primary_key'].select { |k, v| k.kind_of? String and v.kind_of? String }
|
71
|
+
end
|
72
|
+
if raw_opts['foreign_key'] and raw_opts['foreign_key']['suffix'].kind_of? String
|
73
|
+
@opts['foreign_key']['suffix'] = raw_opts['foreign_key']['suffix']
|
74
|
+
end
|
75
|
+
if raw_opts['foreign_key'] and raw_opts['foreign_key']['alias'].kind_of? Hash
|
76
|
+
@opts['foreign_key']['alias'] = raw_opts['foreign_key']['alias'].select { |k, v| k.kind_of? String and v.kind_of? String }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@opts['group'] = group if group.kind_of? String
|
80
|
+
end
|
81
|
+
|
82
|
+
def group?
|
83
|
+
@opts['group'] != 'default'
|
84
|
+
end
|
85
|
+
|
86
|
+
def group
|
87
|
+
@opts['group'] || 'default'
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_primary_key_by_table(table_name)
|
91
|
+
@opts['primary_key'].each { |k, v| return v if k == table_name }
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_table_by_foreign_key(column_name)
|
96
|
+
@opts['foreign_key']['alias'].each { |k, v| return v if k == column_name }
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def fk_suffix
|
101
|
+
@opts['foreign_key']['suffix']
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
@opts.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module RareMap
|
4
|
+
module ModelBuilder
|
5
|
+
def build_models(db_profiles)
|
6
|
+
models = []
|
7
|
+
|
8
|
+
default_id = 1
|
9
|
+
db_profiles.each do |db_prof|
|
10
|
+
db_prof.tables.each do |table|
|
11
|
+
opts = db_prof.options
|
12
|
+
set_primary_key_by_options(table, opts)
|
13
|
+
set_foreign_keys_by_options(table, opts)
|
14
|
+
set_fk_suffix_by_options(table, opts)
|
15
|
+
if opts.group?
|
16
|
+
models << Model.new(db_prof.connection, table, opts.group)
|
17
|
+
else
|
18
|
+
models << Model.new(db_prof.connection, table, 'default', default_id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
default_id += 1 unless db_prof.options.group?
|
22
|
+
end
|
23
|
+
|
24
|
+
build_relations models
|
25
|
+
|
26
|
+
models
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def build_relations(models)
|
31
|
+
models.each do |model|
|
32
|
+
group_models = models.select { |m| m.group == model.group && m.default_id == model.default_id }
|
33
|
+
|
34
|
+
model.table.columns.each do |col|
|
35
|
+
group_models.each do |gm|
|
36
|
+
if gm.table.match_foreign_key(col) && model != gm
|
37
|
+
model.relations << Relation.new(:belongs_to, col.name, gm.table.name)
|
38
|
+
gm.relations << Relation.new(col.unique? ? :has_one : :has_many, col.name, model.table.name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
models.each do |model|
|
45
|
+
group_models = models.select { |m| m.group == model.group && m.default_id == model.default_id }
|
46
|
+
|
47
|
+
model.relations.each do |rel_from|
|
48
|
+
model.relations.each do |rel_to|
|
49
|
+
if rel_from != rel_to &&
|
50
|
+
rel_from.type == :belongs_to &&
|
51
|
+
rel_to.type == :belongs_to &&
|
52
|
+
rel_from.table != rel_to.table
|
53
|
+
model_from = models.find { |m| m.table.name == rel_from.table }
|
54
|
+
model_to = models.find { |m| m.table.name == rel_to.table }
|
55
|
+
model_from.relations << Relation.new(:has_many_through, rel_to.foreign_key, model_to.table.name, model.table.name)
|
56
|
+
model_to.relations << Relation.new(:has_many_through, rel_from.foreign_key, model_from.table.name, model.table.name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
models.each do |model|
|
63
|
+
model.relations.uniq! { |rel| "#{rel.type} #{rel.table} #{rel.through}" }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_fk_suffix_by_options(table, options)
|
68
|
+
table.fk_suffix = options.fk_suffix if options.fk_suffix
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_foreign_keys_by_options(table, options)
|
72
|
+
table.columns.each do |col|
|
73
|
+
ref = options.find_table_by_foreign_key col.name
|
74
|
+
col.references = ref if ref
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_primary_key_by_options(table, options)
|
79
|
+
pk = options.find_primary_key_by_table table.name
|
80
|
+
table.primary_key = pk if pk
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Model
|
85
|
+
attr_reader :connection, :table, :group, :relations, :default_id
|
86
|
+
|
87
|
+
def initialize(connection, table, group = 'default', default_id = nil)
|
88
|
+
@connection, @table, @group, @default_id = connection, table, group, default_id
|
89
|
+
@relations = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def group?
|
93
|
+
group != 'default'
|
94
|
+
end
|
95
|
+
|
96
|
+
def classify
|
97
|
+
if group?
|
98
|
+
"#{group}_#{table.name}".pluralize.classify
|
99
|
+
else
|
100
|
+
"#{table.name}".pluralize.classify
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Relation
|
106
|
+
attr_reader :type, :foreign_key, :table, :through
|
107
|
+
|
108
|
+
def initialize(type, foreign_key, table, through = nil)
|
109
|
+
@type, @foreign_key, @table, @through = type, foreign_key, table, through
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module RareMap
|
4
|
+
module ModelGenerator
|
5
|
+
def generate_models(models, root = './')
|
6
|
+
root ||= './'
|
7
|
+
path = root + 'app/models/'
|
8
|
+
|
9
|
+
models.each do |model|
|
10
|
+
output = ''
|
11
|
+
output <<
|
12
|
+
"class #{model.classify} < ActiveRecord::Base\n" <<
|
13
|
+
" establish_connection #{model.connection.to_s[1..-2]}\n" <<
|
14
|
+
" self.table_name = '#{model.table.name}'\n" <<
|
15
|
+
" self.inheritance_column = 'ruby_type'\n" <<
|
16
|
+
" #{ "self.primary_key = '#{model.table.primary_key}'\n" if model.table.primary_key }\n" <<
|
17
|
+
" attr_accessible #{model.table.columns.map { |col| ":#{col.name}" }.join(', ')}\n\n"
|
18
|
+
|
19
|
+
belongs_to = model.relations.select { |rel| rel.type == :belongs_to }
|
20
|
+
unless belongs_to.empty?
|
21
|
+
output << belongs_to.
|
22
|
+
map { |rel| " belongs_to :#{rel.table.pluralize.singularize}, :foreign_key => '#{rel.foreign_key}', :class_name => '#{classify_by_table(rel.table, models)}'" }.
|
23
|
+
join("\n") << "\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
has_one = model.relations.select { |rel| rel.type == :has_one }
|
27
|
+
unless has_one.empty?
|
28
|
+
output << has_one.
|
29
|
+
map { |rel| " has_one :#{rel.table.pluralize.singularize}, :foreign_key => '#{rel.foreign_key}', :class_name => '#{classify_by_table(rel.table, models)}'" }.
|
30
|
+
join("\n") << "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
has_many = model.relations.select { |rel| rel.type == :has_many }
|
34
|
+
unless has_many.empty?
|
35
|
+
output << has_many.
|
36
|
+
map { |rel| " has_many :#{rel.table.pluralize}, :foreign_key => '#{rel.foreign_key}', :class_name => '#{classify_by_table(rel.table, models)}'" }.
|
37
|
+
join("\n") << "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
has_many_through = model.relations.select { |rel| rel.type == :has_many_through }
|
41
|
+
unless has_many_through.empty?
|
42
|
+
output << has_many_through.
|
43
|
+
map { |rel| " has_many :#{rel.table.pluralize}#{"_by_#{rel.through}, :source => :#{rel.table.pluralize.singularize}" if has_many_through.count { |hmt| hmt.table == rel.table } > 1}, :through => :#{rel.through.pluralize}, :foreign_key => '#{rel.foreign_key}', :class_name => '#{classify_by_table(rel.table, models)}'" }.
|
44
|
+
join("\n") << "\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
output << 'end'
|
48
|
+
|
49
|
+
Dir.mkdir root + 'app' unless Dir.exist? root + 'app'
|
50
|
+
Dir.mkdir path unless Dir.exist? path
|
51
|
+
Dir.mkdir path + "#{model.group}" unless Dir.exist? path + "#{model.group}"
|
52
|
+
f = File.new(path + "#{model.group}/#{model.classify.underscore}.rb", 'w')
|
53
|
+
f.write output
|
54
|
+
f.close
|
55
|
+
end
|
56
|
+
|
57
|
+
models
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def classify_by_table(table, models)
|
62
|
+
model = models.find { |m| m.table.name == table }
|
63
|
+
model.classify
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,22 +1,22 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
level.times do |i|
|
7
|
-
found = true
|
8
|
-
path = ''
|
1
|
+
module RareMap
|
2
|
+
module RailsLocator
|
3
|
+
def locate_rails_root(depth = 5)
|
4
|
+
rails_dirs = ['app', 'config', 'db', 'lib', 'log', 'public', 'script']
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
depth.times do |level|
|
7
|
+
found = true
|
8
|
+
path = ''
|
9
|
+
|
10
|
+
level.times { path << '../' }
|
11
|
+
|
12
|
+
rails_dirs.each do |dir|
|
13
|
+
found = false unless Dir.exist?(path + dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
return path if found
|
14
17
|
end
|
15
18
|
|
16
|
-
|
19
|
+
nil
|
17
20
|
end
|
18
|
-
|
19
|
-
nil
|
20
21
|
end
|
21
|
-
|
22
22
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
module RareMap
|
4
|
+
module SchemaParser
|
5
|
+
def parse_schema(schema)
|
6
|
+
tables = []
|
7
|
+
|
8
|
+
schema.split(/\n/).each do |line|
|
9
|
+
case line.strip!
|
10
|
+
when /^create_table/
|
11
|
+
name = line.match(/create_table\s+['"]([^'"]+)['"]/)[1]
|
12
|
+
id = line.match(/:id\s*=>\s*false/) ? false : true
|
13
|
+
primary_key = pk[1] if (pk = line.match(/:primary_key\s*=>\s*['"](.+)['"]/))
|
14
|
+
tables << Table.new(name, :id => id, :primary_key => primary_key)
|
15
|
+
when /^t\./
|
16
|
+
name = line.match(/t\.\w+\s+['"]([^'"]+)['"]/)[1]
|
17
|
+
type = line.match(/t\.(\w+)\s+/)[1]
|
18
|
+
tables.last.columns << Column.new(name, type)
|
19
|
+
when /^add_index\s+.*\[\s*['"]([^'"]+)['"]\s*\].*:unique\s*=>\s*true/
|
20
|
+
unique_column = line.match(/add_index\s+.*\[\s*['"]([^'"]+)['"]\s*\].*:unique\s*=>\s*true/)[1]
|
21
|
+
column = tables.last.columns.find { |col| col.name == unique_column }
|
22
|
+
column.unique = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
tables
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Table
|
31
|
+
attr_reader :name, :id, :columns
|
32
|
+
attr_writer :primary_key
|
33
|
+
attr_accessor :fk_suffix
|
34
|
+
|
35
|
+
def initialize(name, opts = { :id => true, :primary_key => nil })
|
36
|
+
@name = name
|
37
|
+
@id = opts[:id]
|
38
|
+
@primary_key = opts[:primary_key]
|
39
|
+
@columns = []
|
40
|
+
@fk_suffix = 'id'
|
41
|
+
end
|
42
|
+
|
43
|
+
def primary_key
|
44
|
+
return 'id' if @id
|
45
|
+
|
46
|
+
candidates = @columns.find_all { |col| col.unique }.map { |col| col.name }
|
47
|
+
return @primary_key if candidates.include? @primary_key
|
48
|
+
return 'id' if candidates.include? 'id'
|
49
|
+
candidates.find { |c| c =~ eval("/^#{@name}.*id$/") } ||
|
50
|
+
candidates.find { |c| c =~ eval("/^#{singularize}.*id$/") } ||
|
51
|
+
candidates.find { |c| c =~ eval("/^#{pluralize}.*id$/") } ||
|
52
|
+
candidates.first
|
53
|
+
end
|
54
|
+
|
55
|
+
def singularize
|
56
|
+
@name.pluralize.singularize
|
57
|
+
end
|
58
|
+
|
59
|
+
def pluralize
|
60
|
+
@name.pluralize
|
61
|
+
end
|
62
|
+
|
63
|
+
def match_foreign_key(column)
|
64
|
+
if column.references == @name || ["#{@name}_#{fk_suffix}",
|
65
|
+
"#{@name}#{fk_suffix}",
|
66
|
+
"#{singularize}_#{fk_suffix}",
|
67
|
+
"#{singularize}#{fk_suffix}",
|
68
|
+
"#{pluralize}_#{fk_suffix}",
|
69
|
+
"#{pluralize}#{fk_suffix}"].include?(column.name)
|
70
|
+
@name if primary_key
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Column
|
76
|
+
attr_reader :name, :type
|
77
|
+
attr_accessor :unique, :references
|
78
|
+
|
79
|
+
def initialize(name, type)
|
80
|
+
@name = name
|
81
|
+
@type = type
|
82
|
+
@unique = false
|
83
|
+
end
|
84
|
+
|
85
|
+
def unique?
|
86
|
+
@unique
|
87
|
+
end
|
88
|
+
|
89
|
+
def foreign_key?
|
90
|
+
@references ? true : false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module RareMap
|
4
|
+
module SchemaReader
|
5
|
+
def read_schema(db_profile)
|
6
|
+
conn = db_profile.connection.map { |k, v| v.kind_of?(Integer) ? "'#{k}'=>#{v}" : "'#{k}'=>'#{v}'" }.join(', ')
|
7
|
+
if RUBY_PLATFORM == "java"
|
8
|
+
%x[jruby -e "require 'active_record'; ActiveRecord::Base.establish_connection(#{conn}); ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection);"]
|
9
|
+
else
|
10
|
+
%x[ruby -e "require 'active_record'; ActiveRecord::Base.establish_connection(#{conn}); ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection);"]
|
11
|
+
end
|
12
|
+
# schema = StringIO.new
|
13
|
+
# ActiveRecord::Base.establish_connection db_profile.connection
|
14
|
+
# ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, schema)
|
15
|
+
# schema.close
|
16
|
+
# schema.string
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/rare_map/version.rb
CHANGED
data/lib/rare_map.rb
CHANGED
@@ -1,42 +1,56 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rare_map/schema_to_model_builder'
|
1
|
+
# $:.unshift File.dirname(__FILE__)
|
2
|
+
|
4
3
|
require 'rare_map/rails_locator'
|
4
|
+
require 'rare_map/config_loader'
|
5
|
+
require 'rare_map/schema_reader'
|
6
|
+
require 'rare_map/schema_parser'
|
7
|
+
require 'rare_map/model_builder'
|
8
|
+
require 'rare_map/model_generator'
|
5
9
|
|
6
|
-
|
10
|
+
module RareMap
|
11
|
+
def self.mapping
|
12
|
+
Mapper.new.mapping
|
13
|
+
end
|
7
14
|
|
8
|
-
|
9
|
-
|
10
|
-
$stderr.puts 'Run RareMap under Rails root directory'
|
11
|
-
return
|
12
|
-
end
|
15
|
+
class Mapper
|
16
|
+
include RailsLocator, ConfigLoader, SchemaReader, SchemaParser, ModelBuilder, ModelGenerator
|
13
17
|
|
14
|
-
|
18
|
+
def initialize
|
19
|
+
@rails_root = locate_rails_root
|
20
|
+
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def mapping
|
23
|
+
@db_profiles = load_config @rails_root ? @rails_root + 'config/' : './'
|
24
|
+
@db_profiles.each do |profile|
|
25
|
+
profile.schema = read_schema profile
|
26
|
+
profile.tables = parse_schema profile.schema
|
27
|
+
end
|
28
|
+
@models = build_models @db_profiles
|
29
|
+
generate_models @models, @rails_root
|
30
|
+
if @rails_root
|
31
|
+
puts '*****************************************************************************'
|
32
|
+
puts ' Add following line to your config/application.rb'
|
33
|
+
puts " config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]"
|
34
|
+
puts '*****************************************************************************'
|
35
|
+
else
|
36
|
+
puts '*****************************************************************************'
|
37
|
+
puts ' A demo.rb is generated'
|
38
|
+
puts '*****************************************************************************'
|
39
|
+
generate_demo unless File.exist?('demo.rb')
|
26
40
|
end
|
27
|
-
|
28
|
-
SchemaToModelBuilder.build(schemata, relations, group_name, databases)
|
29
|
-
|
30
|
-
info[group_name] ||= {}
|
31
|
-
info[group_name][:schemata] = schemata
|
32
|
-
info[group_name][:relations] = relations
|
41
|
+
@models
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
private
|
45
|
+
def generate_demo
|
46
|
+
f = File.new('demo.rb', 'w')
|
47
|
+
f.write "require 'active_record'\n"
|
48
|
+
f.write "Dir[File.dirname(__FILE__) + '/app/models/**/*.rb'].each { |file| require file }\n"
|
49
|
+
f.close
|
50
|
+
end
|
40
51
|
end
|
41
|
-
|
42
52
|
end
|
53
|
+
|
54
|
+
if __FILE__ == $0
|
55
|
+
RareMap.mapping
|
56
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rare_map
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 3.2.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 3.2.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: shoulda
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -107,24 +107,26 @@ dependencies:
|
|
107
107
|
- - ~>
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 0.9.11
|
110
|
-
description:
|
110
|
+
description: Relational db to ActiveREcord models MAPper
|
111
111
|
email: wnameless@gmail.com
|
112
|
-
executables:
|
112
|
+
executables:
|
113
|
+
- raremap
|
113
114
|
extensions: []
|
114
115
|
extra_rdoc_files:
|
115
116
|
- LICENSE.txt
|
116
117
|
- README.rdoc
|
117
118
|
files:
|
118
119
|
- lib/rare_map.rb
|
119
|
-
- lib/rare_map/
|
120
|
-
- lib/rare_map/
|
120
|
+
- lib/rare_map/config_loader.rb
|
121
|
+
- lib/rare_map/model_builder.rb
|
122
|
+
- lib/rare_map/model_generator.rb
|
121
123
|
- lib/rare_map/rails_locator.rb
|
122
|
-
- lib/rare_map/
|
123
|
-
- lib/rare_map/
|
124
|
-
- lib/rare_map/schema_to_relation_builder.rb
|
124
|
+
- lib/rare_map/schema_parser.rb
|
125
|
+
- lib/rare_map/schema_reader.rb
|
125
126
|
- lib/rare_map/version.rb
|
126
127
|
- LICENSE.txt
|
127
128
|
- README.rdoc
|
129
|
+
- bin/raremap
|
128
130
|
homepage: http://github.com/wnameless/rare_map
|
129
131
|
licenses:
|
130
132
|
- Apache License, Version 2.0
|
@@ -140,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
142
|
version: '0'
|
141
143
|
segments:
|
142
144
|
- 0
|
143
|
-
hash:
|
145
|
+
hash: 2391284993457456649
|
144
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
147
|
none: false
|
146
148
|
requirements:
|
@@ -152,5 +154,5 @@ rubyforge_project:
|
|
152
154
|
rubygems_version: 1.8.24
|
153
155
|
signing_key:
|
154
156
|
specification_version: 3
|
155
|
-
summary:
|
157
|
+
summary: rare_map-
|
156
158
|
test_files: []
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'rare_map/rails_locator'
|
2
|
-
require 'rare_map/schema_to_hash_mapping'
|
3
|
-
|
4
|
-
module DatabaseToSchemaBuilder
|
5
|
-
|
6
|
-
def self.build
|
7
|
-
file = File.open(RailsLocator.locate + 'config/database.yml') { |f| f.read }
|
8
|
-
database = file.split("\n").select do |line|
|
9
|
-
line.match(/^[a-z]+/) && line != 'development:' && line != 'test:' && line != 'production:'
|
10
|
-
end
|
11
|
-
database.map! { |db| db.delete(':') }
|
12
|
-
|
13
|
-
database_hash = Hash.new { |hash, key| hash[key] = [] }
|
14
|
-
database.each { |db| database_hash[db.split('_').first.to_sym] << { :dbname => db } }
|
15
|
-
|
16
|
-
database_hash.each do |group, dbs|
|
17
|
-
dbs.each do |db|
|
18
|
-
puts "db:schema:dump RAILS_ENV=#{db[:dbname]}"
|
19
|
-
system "rake db:schema:dump RAILS_ENV=#{db[:dbname]}"
|
20
|
-
schema = File.open(RailsLocator.locate + 'db/schema.rb') { |f| f.read }
|
21
|
-
db[:schema] = SchemaToHashMapping.convert(schema.split("\n"))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
database_hash
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'rare_map/rails_locator'
|
2
|
-
|
3
|
-
module InheritorUtil
|
4
|
-
|
5
|
-
def self.to_class_name(name)
|
6
|
-
name.to_s.split('_').map { |word| word.capitalize }.join
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.remove_underline_between_numbers(line)
|
10
|
-
line = line.to_s
|
11
|
-
md = line.match(/(\d+)(_)(\d+)/)
|
12
|
-
md.size.times { line.gsub!(/(\d+)_(\d+)/, '\1\2') } unless md.nil?
|
13
|
-
line.gsub!(/([a-zA-Z])(_)(\d)/, '\1\3')
|
14
|
-
line.to_sym
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.write_model_file(file_name, content, group_name)
|
18
|
-
write_file(file_name, content, group_name, 'models')
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.write_controller_file(file_name, content, group_name)
|
22
|
-
write_file(file_name, content, group_name, 'controllers')
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.complex_flatten(node, flatAry = [])
|
26
|
-
if node.kind_of?(Array)
|
27
|
-
node.map { |leaf| complex_flatten(leaf, flatAry) }
|
28
|
-
elsif node.kind_of?(Hash)
|
29
|
-
node.each do |key, val|
|
30
|
-
flatAry << key
|
31
|
-
complex_flatten(val, flatAry)
|
32
|
-
end
|
33
|
-
else
|
34
|
-
flatAry << node
|
35
|
-
end
|
36
|
-
flatAry
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
|
43
|
-
def self.write_file(file_name, content, group_name, mvc)
|
44
|
-
Dir.mkdir(RailsLocator.locate + "app/#{mvc}/#{group_name}") unless Dir.exist?(RailsLocator.locate + "app/#{mvc}/#{group_name}")
|
45
|
-
f = File.new(RailsLocator.locate + "app/#{mvc}/#{group_name}/#{group_name}_#{remove_underline_between_numbers(file_name)}.rb", 'w')
|
46
|
-
f.write(content)
|
47
|
-
f.close
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module SchemaToHashMapping
|
2
|
-
|
3
|
-
def self.convert(schema)
|
4
|
-
schema_hash = {}
|
5
|
-
table_name_sym = :Blank
|
6
|
-
|
7
|
-
schema.each do |line|
|
8
|
-
case line.strip!
|
9
|
-
when /^create_table/
|
10
|
-
table_name_sym = filter_table_name_sym(line)
|
11
|
-
schema_hash[table_name_sym] = {}
|
12
|
-
if line.match(/:primary_key\s*=>\s*"[^"]+",/)
|
13
|
-
schema_hash[table_name_sym][:primary_key] = line.match(/:primary_key\s*=>\s*"[^"]+",/).to_s.delete('",').split(/\s+/)[2].to_sym
|
14
|
-
end
|
15
|
-
when /^t\./
|
16
|
-
column_name_sym = filter_column_name_sym(line)
|
17
|
-
column_type_sym = filter_column_type_sym(line)
|
18
|
-
schema_hash[table_name_sym][column_name_sym] ||= {}
|
19
|
-
schema_hash[table_name_sym][column_name_sym][:type] = table_name_sym
|
20
|
-
schema_hash[table_name_sym][column_name_sym][:unique] = false
|
21
|
-
when /^add_index.*:unique\s*=>\s*true/
|
22
|
-
uniqueColumns = filter_unique_columns(line)
|
23
|
-
uniqueColumns.each do |column|
|
24
|
-
schema_hash[table_name_sym][column][:unique] = true
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
return schema_hash
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
|
36
|
-
def self.filter_unique_columns(line)
|
37
|
-
cols = line.match(/\[[^\]]*\]/).to_s.delete('"[],').split(/\s+/).map { |col| col.to_sym }
|
38
|
-
cols.size == 1 ? cols : []
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.filter_table_name_sym(line)
|
42
|
-
line.delete('",').split(/\s+/)[1].to_sym
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.filter_column_name_sym(line)
|
46
|
-
line.delete('",').split(/\s+/)[1].to_sym
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.filter_column_type_sym(line)
|
50
|
-
line.delete('",').split(/\s+/)[0][2 .. -1].to_sym
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'rare_map/inheritor_util'
|
2
|
-
|
3
|
-
module SchemaToModelBuilder
|
4
|
-
|
5
|
-
def self.build(schema_hash, rel_hash, group_name, database_hash)
|
6
|
-
schema_hash.each do |table_name, table_content|
|
7
|
-
model_content = generate_header(table_name, schema_hash, group_name, database_hash)
|
8
|
-
model_content << generate_attributes_permission(table_content, 'attr_accessible') unless table_content.size == 0
|
9
|
-
model_content << generate_relationships(table_name, rel_hash, group_name)
|
10
|
-
model_content << generate_footer
|
11
|
-
InheritorUtil.write_model_file(table_name, model_content, group_name)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
|
19
|
-
def self.generate_relationships(table_name, rel_hash, group_name)
|
20
|
-
return '' if rel_hash.count { |_, hash| hash[table_name] != nil } == 0
|
21
|
-
|
22
|
-
has_one_ary = []
|
23
|
-
rel_hash[:has_one][table_name].each do |table, foreign_key|
|
24
|
-
has_one_ary << " has_one :#{table}, :foreign_key => '#{foreign_key}', :class_name => '#{group_name.to_s.capitalize}#{InheritorUtil.to_class_name(table)}'"
|
25
|
-
end if rel_hash[:has_one][table_name] != nil
|
26
|
-
|
27
|
-
has_many_ary = []
|
28
|
-
rel_hash[:has_many][table_name].each do |table, foreign_key|
|
29
|
-
has_many_ary << " has_many :#{table}, :foreign_key => '#{foreign_key}', :class_name => '#{group_name.to_s.capitalize}#{InheritorUtil.to_class_name(table)}'"
|
30
|
-
end if rel_hash[:has_many][table_name] != nil
|
31
|
-
|
32
|
-
belongs_to_ary = []
|
33
|
-
rel_hash[:belongs_to][table_name].each do |table, foreign_key|
|
34
|
-
belongs_to_ary << " belongs_to :#{table}, :foreign_key => '#{foreign_key}', :class_name => '#{group_name.to_s.capitalize}#{InheritorUtil.to_class_name(table)}'"
|
35
|
-
end if rel_hash[:belongs_to][table_name] != nil
|
36
|
-
|
37
|
-
has_many_through_ary = []
|
38
|
-
rel_hash[:has_many_through][table_name].each do |table, foreign_key, through|
|
39
|
-
has_many_through_ary << " has_many :#{table}, :foreign_key => '#{foreign_key}', :class_name => '#{group_name.to_s.capitalize}#{InheritorUtil.to_class_name(table)}', :through => :#{through}"
|
40
|
-
end if rel_hash[:has_many_through][table_name] != nil
|
41
|
-
|
42
|
-
has_one_ary.join("\n") << "\n" <<
|
43
|
-
has_many_ary.join("\n") << "\n" <<
|
44
|
-
belongs_to_ary.join("\n") + "\n" <<
|
45
|
-
has_many_through_ary.join("\n") << "\n"
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.generate_attributes_permission(table_content, permission)
|
49
|
-
table_content.keys.inject(' ' + permission) { |o, i| o = o + " :#{i}," }[0 .. -2] + "\n"
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.generate_header(table_name, schema_hash, group_name, database_hash)
|
53
|
-
"class #{group_name.to_s.capitalize}#{InheritorUtil.to_class_name(table_name)} < ActiveRecord::Base\n" <<
|
54
|
-
" establish_connection :#{get_connection_name(table_name, group_name, database_hash)}\n" <<
|
55
|
-
" self.table_name = '#{table_name}'\n" <<
|
56
|
-
" self.inheritance_column = 'ruby_type'\n" <<
|
57
|
-
" self.primary_key = '#{check_primary_key(table_name, schema_hash)}'\n"
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.get_connection_name(table_name, group_name, database_hash)
|
61
|
-
database_hash[group_name].each do |db|
|
62
|
-
return db[:dbname] unless db[:schema][table_name].nil?
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.check_primary_key(table_name, schema_hash)
|
67
|
-
schema_hash[table_name][:primary_key].nil? ? 'id' : schema_hash[table_name][:primary_key]
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.generate_footer
|
71
|
-
"end\n"
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
module BelongsTo2HasManyThroughtRelationBuilder
|
2
|
-
|
3
|
-
def self.build(belongs_to_hash, has_one_hash, has_many_hash)
|
4
|
-
belongs_to_hash = belongs_to_hash.clone
|
5
|
-
has_many_through_hash = {}
|
6
|
-
|
7
|
-
belongs_to_hash = belongs_to_hash.delete_if { |table, rels| rels.size < 2 }
|
8
|
-
belongs_to_hash.each do |table, rels|
|
9
|
-
rels.each do |from_table, from_table_key|
|
10
|
-
rels.each do |to_table, to_table_key|
|
11
|
-
if from_table != to_table
|
12
|
-
has_many_through_hash[from_table] ||= []
|
13
|
-
rel = [to_table, to_table_key, table]
|
14
|
-
has_many_through_hash[from_table] << rel
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
final_has_many_through_hash = {}
|
21
|
-
has_many_through_hash.each do |table, rels|
|
22
|
-
rels.each do |rel|
|
23
|
-
if has_many_hash[table] != nil && has_many_hash[table].count { |i| i.first == rel.first } == 0
|
24
|
-
final_has_many_through_hash[table] ||= []
|
25
|
-
final_has_many_through_hash[table] << rel
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
has_many_through_hash = final_has_many_through_hash
|
30
|
-
|
31
|
-
final_has_many_through_hash = {}
|
32
|
-
has_many_through_hash.each do |table, rels|
|
33
|
-
rels.each do |rel|
|
34
|
-
if has_one_hash[table] != nil && has_one_hash[table].count { |i| i.first == rel.first } == 0
|
35
|
-
final_has_many_through_hash[table] ||= []
|
36
|
-
final_has_many_through_hash[table] << rel
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
has_many_through_hash = final_has_many_through_hash
|
41
|
-
|
42
|
-
final_has_many_through_hash = {}
|
43
|
-
has_many_through_hash.each do |table, rels|
|
44
|
-
rels.each do |rel|
|
45
|
-
if belongs_to_hash[table] != nil && belongs_to_hash[table].count { |i| i.first == rel.first } == 0
|
46
|
-
final_has_many_through_hash[table] ||= []
|
47
|
-
final_has_many_through_hash[table] << rel
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
has_many_through_hash = final_has_many_through_hash
|
52
|
-
|
53
|
-
has_many_through_hash
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
module SchemaToRelationBuilder
|
59
|
-
|
60
|
-
def self.build(schema_hash, relation_mark, alias_hash = {})
|
61
|
-
relation_mark ||= '_id'
|
62
|
-
has_one_hash = {}; has_many_hash = {}; belongs_to_hash = {}
|
63
|
-
|
64
|
-
schema_hash.each do |table_name, table_content|
|
65
|
-
table_content.each do |column, type|
|
66
|
-
if alias_hash[column] != nil
|
67
|
-
belongs_to = alias_hash[column]
|
68
|
-
if schema_hash[belongs_to] != nil
|
69
|
-
if schema_hash[table_name][column][:unique] == true
|
70
|
-
has_one_hash[belongs_to] ||= []
|
71
|
-
has_one_hash[belongs_to] << [table_name, column]
|
72
|
-
else
|
73
|
-
has_many_hash[belongs_to] ||= []
|
74
|
-
has_many_hash[belongs_to] << [table_name, column]
|
75
|
-
end
|
76
|
-
belongs_to_hash[table_name] ||= []
|
77
|
-
belongs_to_hash[table_name] << [belongs_to, column]
|
78
|
-
end
|
79
|
-
elsif column.to_s.match(eval("/#{relation_mark}$/")) && column.to_s[0 .. -(relation_mark.length + 1)].to_sym != table_name
|
80
|
-
belongs_to = column.to_s[0 .. -(relation_mark.length + 1)].to_sym
|
81
|
-
if schema_hash[belongs_to] != nil
|
82
|
-
if schema_hash[table_name][column][:unique] == true
|
83
|
-
has_one_hash[belongs_to] ||= []
|
84
|
-
has_one_hash[belongs_to] << [table_name, column]
|
85
|
-
else
|
86
|
-
has_many_hash[belongs_to] ||= []
|
87
|
-
has_many_hash[belongs_to] << [table_name, column]
|
88
|
-
end
|
89
|
-
belongs_to_hash[table_name] ||= []
|
90
|
-
belongs_to_hash[table_name] << [belongs_to, column]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
has_many_through_hash = BelongsTo2HasManyThroughtRelationBuilder.build(belongs_to_hash, has_one_hash, has_many_hash)
|
97
|
-
|
98
|
-
return { :has_one => has_one_hash,
|
99
|
-
:has_many => has_many_hash,
|
100
|
-
:belongs_to => belongs_to_hash,
|
101
|
-
:has_many_through => has_many_through_hash }
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|