sqld4r 0.0.1

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.
@@ -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