hatogaya 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hatogaya.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Akifumi NAKAMURA
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Hatogaya
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'hatogaya'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install hatogaya
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/hatogaya.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "hatogaya"
7
+ spec.version = "0.1"
8
+ spec.authors = ["Akifumi NAKAMURA"]
9
+ spec.email = ["tmpz84@gmail.com"]
10
+ spec.description = %q{print database schema and entity relation}
11
+ spec.summary = %q{printoutput database schema and entiry relation}
12
+ spec.homepage = "https://github.com/nakamura-akifumi/hatogaya"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rake"
22
+
23
+ spec.add_dependency("ruby-graphviz", "~> 1.0")
24
+ spec.add_dependency("axlsx")
25
+
26
+ end
@@ -0,0 +1,212 @@
1
+ # coding: utf-8
2
+ module Hatogaya
3
+ module Db
4
+ class Desc
5
+ FONT_NAME = 'MS ゴシック'
6
+ CONST_COLUMN = {"created_at"=>I18n.t('page.created_at'),
7
+ "updated_at"=>I18n.t('page.updated_at'),
8
+ "id"=>'ID',
9
+ }
10
+
11
+ def initialize
12
+ @association_names = {:has_one=>"1:1", :has_many=>"1:N", :belongs_to=>"N:1"}
13
+ @association_vectors = {:has_one=>"<=>", :has_many=>"=>", :belongs_to=>"<="}
14
+ @association_macros = [:has_one, :has_many, :belongs_to]
15
+ end
16
+
17
+ def parse_arg
18
+
19
+ display_association = ENV['association'] || 'off'
20
+ unless display_association == 'off' || display_association == 'on'
21
+ STDERR.puts "association invalid. #{format}"
22
+ STDERR.puts "association 'on' or 'off' (default:'off') "
23
+ raise
24
+ end
25
+ @display_association = display_association
26
+
27
+ target_models = ENV['models'].split(',') if ENV['models']
28
+
29
+ Dir.glob(Rails.root.join('app/models/**/*.rb')).each { |path| require path }
30
+ tables = ActiveRecord::Base.connection.tables.sort
31
+ models = tables.map{|table| Object.const_get(table.classify) rescue nil}.compact
32
+
33
+ if target_models.blank?
34
+ target_models = models.inject([]) {|a, b| a << b.to_s.downcase }
35
+ end
36
+ if target_models.present?
37
+ STDERR.puts "target_models=#{target_models}"
38
+ end
39
+
40
+ @target_models = target_models
41
+ @tables = tables
42
+ @models = models
43
+
44
+ end
45
+
46
+ def text_output
47
+ @models.each do |model|
48
+ puts ""
49
+ puts "======="
50
+ print "Model=#{model.to_s} "
51
+ print "Table=#{model.table_name} "
52
+ print I18n.t("activerecord.models.#{model.to_s.underscore}")
53
+ puts ""
54
+ puts ""
55
+ model.columns.each do |column|
56
+ print sprintf("|%s|%s|%s|", column.name, column.type, column.sql_type)
57
+ msg = I18n.t("activerecord.attributes.#{model.to_s.underscore}.#{column.name}")
58
+ if msg.index("translation missing:") == 0
59
+ msg = CONST_COLUMN[column.name]
60
+ if msg.blank?
61
+ if column.name.match("(.*)_id$")
62
+ msg = I18n.t("activerecord.models.#{$1}") + " ID"
63
+ end
64
+ end
65
+ end
66
+ print msg
67
+ puts ""
68
+ end
69
+
70
+ # table index
71
+ puts ""
72
+ puts "database indexes: "
73
+ puts ""
74
+ #puts ActiveRecord::Base.connection.indexes(model.table_name).inspect
75
+ puts "pk : #{ActiveRecord::Base.connection.primary_key(model.table_name)}"
76
+ ActiveRecord::Base.connection.indexes(model.table_name).each_with_index do |index, i|
77
+ puts "#{i+1} : #{index.name} columns=#{index.columns.join(',')}"
78
+ end
79
+
80
+ # associations
81
+ @association_macros.each do |macro|
82
+ puts ""
83
+ puts "association: #{@association_names[macro]}"
84
+ puts ""
85
+ begin
86
+ model.reflect_on_all_associations(macro).each_with_index do |r, i|
87
+ #puts "#{i+1} #{r.name} #{r.class_name} #{r.table_name} #{r.association_primary_key} #{r.a ssociation_foreign_key}"
88
+ puts "#{i+1} : #{r.association_foreign_key} #{@association_vectors[macro]} #{r.table_name} #{r.association_primary_key} "
89
+
90
+ end
91
+ rescue
92
+ STDERR.puts "error(#{$!})"
93
+ #puts $@
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def excel_output
100
+ require 'axlsx'
101
+
102
+ Axlsx::Package.new do |pkg|
103
+ wb = pkg.workbook
104
+
105
+ sty = wb.styles.add_style :font_name => FONT_NAME
106
+ wh_cell = wb.styles.add_style :border => { :style => :thin, :color => "00" },
107
+ :font_name => FONT_NAME
108
+ gray_cell = wb.styles.add_style :bg_color => "aa",
109
+ :border => { :style => :thin, :color => "00" },
110
+ :font_name => FONT_NAME
111
+
112
+ wb.add_worksheet(:name => "DB Shemes") do |sheet|
113
+ @models.each do |model|
114
+ next unless @target_models.include?(model.to_s.downcase)
115
+
116
+ sheet.add_row ["#{model.to_s}", "#{model.table_name}", I18n.t("activerecord.models.#{model.to_s.underscore}")], :types => :string, :style => [sty, sty, sty]
117
+
118
+ sheet.add_row ["アトリビュート名","型","型","名称"], :style => [gray_cell, gray_cell, gray_cell, gray_cell]
119
+ model.columns.each do |column|
120
+ cols = [column.name, column.type, column.sql_type]
121
+ msg = I18n.t("activerecord.attributes.#{model.to_s.underscore}.#{column.name}")
122
+ if msg.index("translation missing:") == 0
123
+ msg = CONST_COLUMN[column.name]
124
+ if msg.blank?
125
+ if column.name.match("(.*)_id$")
126
+ msg = I18n.t("activerecord.models.#{$1}") + " ID"
127
+ end
128
+ end
129
+ end
130
+ cols << msg
131
+ sheet.add_row cols, :types => :string, :style => [wh_cell, wh_cell, wh_cell, wh_cell]
132
+ end
133
+
134
+ if @display_association == 'on'
135
+ # table index
136
+ sheet.add_row []
137
+ sheet.add_row ["database indexes"], :style => [gray_cell, gray_cell]
138
+ sheet.add_row ["pk", "#{ActiveRecord::Base.connection.primary_key(model.table_name)}"], :types => :string, :style => [wh_cell, wh_cell]
139
+ ActiveRecord::Base.connection.indexes(model.table_name).each_with_index do |index, i|
140
+ sheet.add_row ["#{i+1}", "#{index.name}", "#{index.columns.join(',')}"], :types => :string, :style => [wh_cell, wh_cell, wh_cell]
141
+ end
142
+
143
+ # associations
144
+ @association_macros.each do |macro|
145
+ sheet.add_row []
146
+ sheet.add_row ["association", "#{@association_names[macro]}"], :types => :string, :style => [gray_cell, gray_cell]
147
+ begin
148
+ model.reflect_on_all_associations(macro).each_with_index do |r, i|
149
+ sheet.add_row ["#{i+1}", "#{r.association_foreign_key}", "#{r.table_name}", "#{r.association_primary_key}"], :types => :string, :style => [wh_cell, wh_cell, wh_cell, wh_cell]
150
+
151
+ end
152
+ rescue
153
+ puts "error(#{$!})"
154
+ #puts $@
155
+ end
156
+ end
157
+ end
158
+ sheet.add_row []
159
+ end
160
+ end
161
+ pkg.serialize("#{Rails.application.class.parent_name}_db_schema.xlsx")
162
+ end
163
+ end
164
+
165
+ def graph_output
166
+ # gem install ruby-graphviz
167
+ require 'graphviz'
168
+ # Create a new graph
169
+ g = ::GraphViz.new( :G, :type => :digraph )
170
+ nodes = {}
171
+
172
+ @models.each do |model|
173
+ next unless @target_models.include?(model.to_s.downcase)
174
+
175
+ # Create two nodes
176
+ nodes[model.to_s.downcase] = g.add_nodes("#{model.to_s}")
177
+
178
+ end
179
+
180
+ @models.each do |model|
181
+ next unless @target_models.include?(model.to_s.downcase)
182
+
183
+ # associations
184
+ @association_macros.each do |macro|
185
+ begin
186
+ model.reflect_on_all_associations(macro).each_with_index do |r, i|
187
+ #sheet.add_row ["#{i+1}", "#{r.association_foreign_key}", "#{r.table_name}", "#{r.association_primary_key}"], :types => :string, :style => [wh_cell, wh_cell, wh_cell, wh_cell]
188
+ node1_name = model.to_s.downcase
189
+ node2_name = r.name.to_s.singularize.camelize.downcase
190
+ STDERR.puts r.inspect
191
+ STDERR.puts "node1_name=#{node1_name}"
192
+ STDERR.puts "node2_name=#{node2_name}"
193
+ node1 = nodes[node1_name]
194
+ node2 = nodes[node2_name]
195
+ # Create an edge between the two nodes
196
+ g.add_edges( node1, node2 )
197
+ end
198
+ rescue
199
+ STDERR.puts "error(#{$!})"
200
+ STDERR.puts $@
201
+ #raise
202
+ end
203
+ end
204
+ end
205
+ if nodes.size > 0
206
+ # Generate output image
207
+ g.output( :png => "#{Rails.application.class.parent_name}_erd.png" )
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ namespace :hatogaya do
3
+ namespace :db do
4
+ desc 'print database description (text)'
5
+ task :desc => :environment do
6
+ desc = Hatogaya::Db::Desc.new
7
+ begin
8
+ desc.parse_arg
9
+ rescue => e
10
+ STDERR.puts e.message
11
+ end
12
+
13
+ desc.text_output
14
+ end
15
+
16
+ desc 'print database description (ER graph)'
17
+ task :desc_graph => :environment do
18
+ desc = Hatogaya::Db::Desc.new
19
+ begin
20
+ desc.parse_arg
21
+ rescue => e
22
+ STDERR.puts e.message
23
+ end
24
+
25
+ desc.graph_output
26
+ end
27
+
28
+ desc 'print database description (Excel format)'
29
+ task :desc_excel => :environment do
30
+ puts "print attributes"
31
+
32
+ desc = Hatogaya::Db::Desc.new
33
+ begin
34
+ desc.parse_arg
35
+ rescue => e
36
+ STDERR.puts e.message
37
+ end
38
+
39
+ desc.excel_output
40
+ end
41
+ end
42
+ end
data/lib/hatogaya.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "hatogaya/task"
2
+ require "hatogaya/desc"
3
+
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hatogaya
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Akifumi NAKAMURA
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ruby-graphviz
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: axlsx
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: print database schema and entity relation
79
+ email:
80
+ - tmpz84@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - hatogaya.gemspec
91
+ - lib/hatogaya.rb
92
+ - lib/hatogaya/desc.rb
93
+ - lib/hatogaya/task.rb
94
+ homepage: https://github.com/nakamura-akifumi/hatogaya
95
+ licenses:
96
+ - MIT
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.23
116
+ signing_key:
117
+ specification_version: 3
118
+ summary: printoutput database schema and entiry relation
119
+ test_files: []
120
+ has_rdoc: