rare_map 0.9.7 → 1.0.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/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
|