sqld4r 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ require "activesupport"
2
+
3
+ class Sqld4r_core
4
+
5
+ def initialize(options)
6
+ super()
7
+ @options = options
8
+ @logfile = File::new( @options["log_file"], "a" ) if @options["logging"]
9
+ end
10
+
11
+ def run
12
+ notice "sqld4r: Starting at " + Time.now.strftime("%Y/%m/%d %H:%M:%S")
13
+ notice "start to parse about SQLDesigner xml file"
14
+ @structure = Sqld4r_parser.new.parse(@options["sqld_path"])
15
+ notice "finish to parse"
16
+ @structure.each do |table|
17
+ generate_model_for(table)
18
+ end
19
+ @structure.each do |table|
20
+ add_relationship_to(table) unless @options["skip-relation"]
21
+ add_index_to(table) unless @options["skip-index"]
22
+ end
23
+ notice "sqld4r: Complete at " + Time.now.strftime("%Y/%m/%d %H:%M:%S")
24
+ @logfile.close if @logfile
25
+ end
26
+
27
+ def paramaters_for_model_generator
28
+ param_string = " "
29
+ param_string << "--svn " if @options["svn"]
30
+ param_string << "--git " if @options["git"]
31
+ param_string << "--skip-timestamps " if @options["skip-timestamps"]
32
+ param_string << "--skip-migration " if @options["skip-migration"]
33
+ param_string << "--skip-fixture " if @options["skip-fixture"]
34
+ param_string << "--quiet " unless @options["verbose"]
35
+ param_string
36
+ end
37
+
38
+ # script/generate model post title:string body:text published:boolean owner:references
39
+ def model_and_columns_from_structure(table)
40
+ model_columns_string = ""
41
+ model_name = tablename2modelname(table["tablename"])
42
+ model_columns_string << model_name + " "
43
+ table["colums"].each do |column|
44
+ #puts column.inspect
45
+ next if "id" == column["name"]
46
+ if column["relation"].nil?
47
+ model_columns_string << column["name"] + ":" + column["datatype"] + " "
48
+ else
49
+ rel_table = Inflector.singularize(column["relation"]["table"])
50
+ model_columns_string << rel_table + ":references "
51
+ end
52
+ end
53
+ model_columns_string
54
+ end
55
+
56
+ =begin
57
+ script/generate model post title:string body:text published:boolean owner:references
58
+ =end
59
+
60
+ def generate_model_for(table)
61
+ notice "generate for " + table["tablename"]
62
+ command = "script/generate "
63
+ if @options["rspec"]
64
+ command << "rspec_model " + paramaters_for_model_generator
65
+ else
66
+ command << "model " + paramaters_for_model_generator
67
+ end
68
+ command << model_and_columns_from_structure(table)
69
+ exec_shell_command(command)
70
+ end
71
+
72
+ =begin
73
+ {
74
+ "tablename" => "comments", # => "has_many :comments" to Item model
75
+ "colums" => [
76
+ {
77
+ "relation" => nil,
78
+ "comment" => "com",
79
+ },
80
+ {
81
+ "relation" => {"table" => "items", "column" => "id"}, # => "belongs_to :item" to Comment model
82
+ "comment" => "has_many",
83
+ },
84
+ ],
85
+ "comment" => "user has many actions"
86
+ },
87
+
88
+ class Post < ActiveRecord::Base
89
+ end
90
+
91
+ relation_models = [
92
+ "app/model/comment.rb",
93
+ "app/model/sometable.rb",
94
+ ]
95
+ =end
96
+ def add_relationship_to(table)
97
+ notice "add relationship to " + table["tablename"]
98
+ current_model_name = tablename2modelname(table["tablename"])
99
+ relation_models = []
100
+ current_model_relations = []
101
+ table["colums"].each do |column|
102
+ next if column["relation"].nil?
103
+ current_model_relations << tablename2modelname(column["relation"]["table"])
104
+ relation_models << tablename2modelname(column["relation"]["table"])
105
+ end
106
+
107
+ # current model
108
+ notice "-add belongs_to relationship to " + table["tablename"] + " about:"
109
+ if 0 == current_model_relations.size
110
+ notice "--skip. becase this model dose not have relationship."
111
+ else
112
+ notice "--" + current_model_relations.join(",")
113
+ text_insert_to(modelname2modelpath(current_model_name)) do |file, line|
114
+ file.puts line
115
+ if /^class\s.+\s<\sActiveRecord::Base$/ =~ line
116
+ file.puts '#' + table["comment"] unless table["comment"].nil?
117
+ current_model_relations.each do |rel|
118
+ file.puts " belongs_to :" + Inflector.singularize(rel)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ # target models
125
+ notice "-add has_one/has_many relationship to:"
126
+ if 0 == relation_models.size
127
+ notice "--skip. because this model dose not have relationship"
128
+ else
129
+ notice "--" + relation_models.join(",")
130
+ relation_models.each do |target_model|
131
+ text_insert_to(modelname2modelpath(target_model)) do |file, line|
132
+ file.puts line
133
+ if /^class\s.+\s<\sActiveRecord::Base$/ =~ line
134
+ single_name = Inflector.singularize(table["tablename"])
135
+ plural_name = table["tablename"]
136
+ file.puts ' # has_one :' + single_name + ' # TODO:CHECK_ME'
137
+ file.puts ' has_many :' + plural_name
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ def tablename2modelname(tablename)
145
+ Inflector.singularize(tablename).classify
146
+ end
147
+
148
+ def modelname2modelpath(modelname)
149
+ "app/models/" + modelname + ".rb"
150
+ end
151
+
152
+ def text_insert_to(filename)
153
+ current_model_contents = []
154
+ File::open(filename, "r") do |file|
155
+ current_model_contents = file.readlines
156
+ end
157
+ File::open(filename, "w") do |file|
158
+ current_model_contents.each do |line|
159
+ yield(file, line)
160
+ end
161
+ end
162
+ end
163
+ =begin
164
+
165
+ script/generate migration add_indexes_for_all_tables
166
+
167
+
168
+
169
+ class CreatePosts < ActiveRecord::Migration
170
+ def self.up
171
+ create_table :posts do |t|
172
+ t.string :title
173
+ t.text :body
174
+ t.boolean :published
175
+
176
+ t.timestamps
177
+ end
178
+ end
179
+
180
+ def self.down
181
+ drop_table :posts
182
+ end
183
+ end
184
+ =end
185
+ def add_index_to(table)
186
+
187
+ end
188
+
189
+ def notice(message)
190
+ puts "* " + message if @options["verbose"]
191
+ @logfile.puts "* " + message if @options["logging"] and @logfile
192
+ end
193
+
194
+ def exec_shell_command(command)
195
+ command << " > /dev/null" unless @options["verbose"]
196
+ command = "sudo " + command if @options["sudo"]
197
+ puts '->' + command if @options["verbose"]
198
+ @logfile.puts '->' + command if @options["logging"]
199
+ result = `#{command}`.chomp unless @options["emulate"]
200
+ puts '-->' + result if @options["verbose"] and not result.nil? and not result.empty?
201
+ @logfile.puts '-->' + result if @options["logging"] and not result.nil? and not result.empty?
202
+ Sqld4r_options::error_with_show_usage "receive error from command." unless 0 == $?
203
+ $?
204
+ end
205
+
206
+ end
@@ -0,0 +1,243 @@
1
+ require 'optparse'
2
+ require "yaml"
3
+ require "readline"
4
+
5
+
6
+ =begin
7
+ script/generate model post title:string body:text published:boolean owner:references
8
+ --svn Modify files with subversion. (Note: svn must be in path)
9
+ --git Modify files with git. (Note: git must be in path)
10
+ --skip-timestamps Don't add timestamps to the migration file for this model
11
+ --skip-migration Don't generate a migration file for this model
12
+ --skip-fixture Don't generation a fixture file for this model
13
+
14
+ script/generate rspec_model Blog name:string
15
+ =end
16
+
17
+
18
+ class Sqld4r_options < Hash
19
+ @@version = Sqld4r::VERSION::STRING
20
+ @@product_name = "sqld4r"
21
+
22
+ @@command_line_params = {}
23
+
24
+ def initialize
25
+ super()
26
+
27
+ # default values
28
+ self["sqld_path"] = ""
29
+ self["sqld4r_conf_path"] = ENV["HOME"] + "/.sqld4r"
30
+ self["sqld4r_conf_path"] = ENV['SQLD4R_CONF'] unless ENV['SQLD4R_CONF'].nil?
31
+ self["init"] = false
32
+ self.merge(Sqld4r_options::static_default_values)
33
+
34
+ end
35
+
36
+
37
+ def parse
38
+ @@opts = OptionParser.new do |opts|
39
+ opts.banner = "Usage: #{@@product_name} SQLDesigner_xml_path [options]"
40
+ opts.separator "Usage: #{@@product_name} --init [options]"
41
+ opts.separator "Usage: #{@@product_name} --check [options]"
42
+ opts.separator "version #{@@version}"
43
+
44
+ opts.separator ""
45
+ opts.separator "Useful Options:"
46
+ opts.separator ""
47
+
48
+ opts.on( '--conf=/path/to/conffile', 'change conf file path($HOME/.sqld4r < conf < command line params)' ) do |p|
49
+ self["sqld4r_conf_path"] = p
50
+ end
51
+
52
+ opts.on( '--svn', 'Modify files with subversion. (Note: svn must be in path/for script/generate)') do
53
+ @@command_line_params["svn"] = true
54
+ end
55
+
56
+ opts.on( '--git', 'Modify files with git. (Note: git must be in path/for script/generate)') do
57
+ @@command_line_params["git"] = true
58
+ end
59
+
60
+ opts.on( '--skip-timestamps', "Don't add timestamps to the migration file for this model") do
61
+ @@command_line_params["skip-timestamps"] = true
62
+ end
63
+
64
+ opts.on( '--skip-migration', "Don't generate a migration file for this model") do
65
+ @@command_line_params["skip-migration"] = true
66
+ end
67
+
68
+ opts.on( '--skip-fixture', "Don't generation a fixture file for this model") do
69
+ @@command_line_params["skip-fixture"] = true
70
+ end
71
+
72
+ opts.on( '--rspec', 'generate model with rspec') do
73
+ @@command_line_params["rspec"] = true
74
+ end
75
+
76
+ opts.on( '--skip-relation', "Don't add belongs_to/has_one/has_many to a model") do
77
+ @@command_line_params["skip-relation"] = true
78
+ end
79
+
80
+ opts.on( '--skip-index', "Don't add index to migration files") do
81
+ @@command_line_params["skip-index"] = true
82
+ end
83
+
84
+ opts.on( '--verbose', 'provide extra output while running') do
85
+ @@command_line_params["verbose"] = true
86
+ end
87
+
88
+ opts.on( '--sudo', 'use sudo command') do
89
+ @@command_line_params["sudo"] = true
90
+ end
91
+
92
+ opts.on( '--log', 'create a log of command activity' ) do
93
+ @@command_line_params["logging"] = true
94
+ end
95
+
96
+ opts.on( '--logfile=/path/to/logfile', 'Set path to logfile. default is ../docs/sqld4r.log' ) do |p|
97
+ @@command_line_params["log_file"] = p
98
+ end
99
+
100
+ opts.on( '--emulate', 'enable emulation mode' ) do
101
+ @@command_line_params["emulate"] = true
102
+ end
103
+
104
+ opts.separator ""
105
+ opts.separator "General Options:"
106
+ opts.separator ""
107
+
108
+ opts.on( '-h', '--help', 'display this information' ) do
109
+ puts opts
110
+ exit
111
+ end
112
+
113
+ opts.on( '-v', '--version', 'display version information' ) do
114
+ puts "The model generator for SQLDesigner: #{@@product_name} version #{@@version}"
115
+ exit
116
+ end
117
+
118
+ opts.on( '--init', 'setup a template of conf file' ) do
119
+ self["init"] = true
120
+ end
121
+
122
+ self["check"] = false
123
+ opts.on( '--check', 'check current variables' ) do
124
+ self["check"] = true
125
+ end
126
+
127
+ opts.separator ""
128
+ opts.separator "Notice:"
129
+ opts.separator ""
130
+ opts.separator "Paramater priority: static < conf < command line"
131
+ opts.separator "You can use a environment variable SQLD4R_CONF for a sqld4r_conf_file"
132
+ opts.separator "conf path priority: env < command line"
133
+ opts.separator "I tested with Rails 2.1.0"
134
+
135
+ end
136
+
137
+ validate_sqld_path
138
+ @@opts.parse!
139
+ marge_values
140
+ validate_options
141
+ if self["check"] or "--check" == self["sqld_path"]
142
+ puts "current variables"
143
+ self.each_pair do |key, value| puts "#{key}:#{value}" end
144
+ exit
145
+ end
146
+ if self["init"] or "--init" == self["sqld_path"]
147
+ put_conf_file
148
+ exit
149
+ end
150
+ self
151
+ end
152
+
153
+ def validate_options
154
+ error_with_show_usage "Can not use --svn option with --git" if self["git"] and self["svn"]
155
+ end
156
+
157
+ # write a template conf file to sqld4r_conf_path
158
+ def put_conf_file
159
+ # check put path
160
+ Sqld4r_options::error_with_show_usage "conf path is empty. set SQL4R_CONF environment variable or use --conf paramater." if self["sqld4r_conf_path"].empty?
161
+
162
+ unless "y" == Readline.readline("Would a template file put to #{self["sqld4r_conf_path"]}? [y/N] > ").downcase
163
+ puts "Set your conf path to SQLD4R_CONF environment variable or use --conf paramater."
164
+ exit
165
+ end
166
+
167
+ begin
168
+ File.open(self["sqld4r_conf_path"]) do |yamlfile|
169
+ # check overwrite?
170
+ exit unless "y" == Readline.readline("Overwrite? [y/N]").downcase
171
+ end
172
+ rescue
173
+ end
174
+
175
+ begin
176
+ File.open(self["sqld4r_conf_path"], "w") do |yamlfile|
177
+ yamlfile.puts "# sqld4r conf file"
178
+ yamlfile.puts ""
179
+ yamlfile.puts Sqld4r_options::static_default_values.to_yaml
180
+ end
181
+ puts "Put template yaml file to #{self["sqld4r_conf_path"]}"
182
+ rescue
183
+ puts "Warning:Could not open for writing. check parmitions."
184
+ end
185
+ end
186
+
187
+ def self.error_with_show_usage(message)
188
+ puts "Error:" + message
189
+ Sqld4r_options::show_usage
190
+ exit(1)
191
+ end
192
+
193
+ def self.show_usage
194
+ puts @@opts
195
+ end
196
+
197
+ def validate_sqld_path
198
+ temp = ARGV.first
199
+ unless temp.nil? or temp.empty?
200
+ @@command_line_params["sqld_path"] = temp.chomp
201
+ else
202
+ Sqld4r_options::error_with_show_usage("Need SQLDesigner_xml_path.")
203
+ end
204
+ end
205
+
206
+ # default values if not use .sqld4r file
207
+ def self.static_default_values
208
+ defaults = {}
209
+ defaults["sqld4r_conf_path"] = ENV["HOME"] + "/.sqld4r"
210
+ defaults["copy_sqld_to"] = "../docs/sqldesigner.xml"
211
+ defaults["svn"] = false
212
+ defaults["git"] = false
213
+ defaults["skip-timestamps"] = false
214
+ defaults["skip-migration"] = false
215
+ defaults["skip-fixture"] = false
216
+ defaults["rspec"] = false
217
+ defaults["skip-relation"] = false
218
+ defaults["skip-index"] = false
219
+ defaults["execute_command_list"] = ENV["HOME"] + "/.sqld4r_execute_commands"
220
+ defaults["verbose"] = false
221
+ defaults["sudo"] = false
222
+ defaults["logging"] = false
223
+ defaults["log_file"] = "./sqld4r.log"
224
+ defaults["emulate"] = false
225
+ defaults
226
+ end
227
+
228
+ # priority static < conf < command line params
229
+ def marge_values
230
+ conf_params = {}
231
+ # can open?
232
+ begin
233
+ File::open(self["sqld4r_conf_path"]) do |conf_file|
234
+ temp = YAML.load(conf_file.read)
235
+ conf_params = temp if temp
236
+ end
237
+ rescue
238
+ conf_params = {}
239
+ end
240
+ self.merge!(conf_params.merge!(@@command_line_params))
241
+ end
242
+
243
+ end
@@ -0,0 +1,124 @@
1
+ require "rexml/document"
2
+
3
+ =begin
4
+ structure = [
5
+ {
6
+ "tablename" => "table1",
7
+ "colums" => [
8
+ {
9
+ "name" => "col1",
10
+ "datatype" => "text",
11
+ "null" => "0",
12
+ "autoincrement" => "1",
13
+ "relation" => nil,
14
+ "comment" => "com",
15
+ },
16
+ {
17
+ "name" => "item_id",
18
+ "datatype" => "string",
19
+ "null" => "0",
20
+ "autoincrement" => "1",
21
+ "relation" => {"table" => "items", "column" => "id"},
22
+ "comment" => "has_many",
23
+ },
24
+ {
25
+ "name" => "col3",
26
+ "datatype" => "integer",
27
+ "null" => "0",
28
+ "autoincrement" => "1",
29
+ "relation" => nil,
30
+ "comment" => "",
31
+ },
32
+ ],
33
+ "comment" => "user has many actions"
34
+ },
35
+
36
+ ]
37
+ =end
38
+
39
+ class Sqld4r_parser
40
+ @@trans_map = {
41
+ "INTEGER" => "integer",
42
+ "DECIMAL" => "integer",
43
+ "FLOAT" => "float",
44
+ "DOUBLE" => "float",
45
+ "CHAR" => "string",
46
+ "VARCHAR" => "string",
47
+ "MEDIUMTEXT" => "text",
48
+ "BINARY" => "binary",
49
+ "VARBINARY" => "binary",
50
+ "BLOB" => "binary",
51
+ "DATE" => "date",
52
+ "TIME" => "time",
53
+ "DATETIME" => "datetime",
54
+ "YEAR" => "date",
55
+ "TIMESTAMP" => "timestamp",
56
+ "ENUM" => "string",
57
+ "SET" => "boolean",
58
+ }
59
+
60
+ def initialize
61
+ super()
62
+ @structure = []
63
+ self
64
+ end
65
+
66
+ def parse(filepath)
67
+ doc = REXML::Document.new File.new(filepath)
68
+ doc.elements.each("sql/table") do |table|
69
+ #puts table.attributes["name"]
70
+ @structure << table_hash(table)
71
+ end
72
+ @structure
73
+ end
74
+
75
+ def table_hash(table_elem)
76
+ buffer_hash = {}
77
+ buffer_hash["tablename"] = table_elem.attributes["name"]
78
+ begin
79
+ buffer_hash["comment"] = table_elem.elements["comment"].text
80
+ rescue
81
+ buffer_hash["comment"] = ""
82
+ end
83
+ buffer_hash["colums"] = []
84
+ table_elem.elements.each("row") do |row|
85
+ buffer_hash["colums"] << colums_hash(row)
86
+ end
87
+ return buffer_hash
88
+ end
89
+
90
+ =begin
91
+ <row name="user_id" null="0" autoincrement="0">
92
+ <datatype>INTEGER</datatype>
93
+ <relation table="users" row="id" />
94
+ </row>
95
+
96
+ {
97
+ "name" => "col2",
98
+ "datatype" => "integer",
99
+ "null" => "0",
100
+ "autoincrement" => "1",
101
+ "relation" => {"table" => "users", "column" => "id"},
102
+ "comment" => "has_many",
103
+ },
104
+ =end
105
+
106
+ def colums_hash(row)
107
+ rowbuffer_hash = {}
108
+ rowbuffer_hash["name"] = row.attributes["name"]
109
+ rowbuffer_hash["null"] = row.attributes["null"]
110
+ rowbuffer_hash["autoincrement"] = row.attributes["autoincrement"]
111
+ rowbuffer_hash["datatype"] = @@trans_map[row.elements["datatype"].text]
112
+ begin
113
+ rowbuffer_hash["relation"] = {"table" => row.elements["relation"].attributes["table"], "column" => row.elements["relation"].attributes["row"]}
114
+ rescue
115
+ rowbuffer_hash["relation"] = nil
116
+ end
117
+ begin
118
+ rowbuffer_hash["comment"] = row.elements["comment"].text
119
+ rescue
120
+ rowbuffer_hash["comment"] = ""
121
+ end
122
+ return rowbuffer_hash
123
+ end
124
+ end