schema_comments 0.2.0.alpha5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,23 +1,12 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "activesupport", ">= 3.0.0"
4
- gem "activerecord", ">= 3.0.0"
5
-
6
3
  group :test do
7
- gem "rails", ">= 3.0.0"
8
- gem "sqlite3"
9
- gem "yaml_waml"
10
- end
4
+ gem "sqlite3", :platform => :ruby
5
+ gem "mysql" , (RUBY_VERSION == '1.9.2' ? "~> 2.8.1" : ">= 0"), :platform => :ruby
6
+ gem "mysql2" , :platform => :ruby
11
7
 
12
- # Add dependencies to develop your gem here.
13
- # Include everything needed to run rake, tests, features, etc.
14
- group :development do
15
- gem "rspec", "~> 2.8.0"
16
- gem "rspec-rails", "~> 2.8.1"
17
- gem "yard", "~> 0.7"
18
- gem "rdoc", "~> 3.12"
19
- gem "bundler", "~> 1.0.0"
20
- gem "jeweler", "~> 1.8.3"
21
- gem "simplecov", ">= 0"
22
- gem "ZenTest"
8
+ # gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
9
+ # gem "activerecord-jdbcmysql-adapter" , :platform => :jruby
23
10
  end
11
+
12
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,125 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ schema_comments (0.2.0)
5
+ activerecord (>= 3.0.0)
6
+ activesupport (>= 3.0.0)
7
+
1
8
  GEM
2
9
  remote: http://rubygems.org/
3
10
  specs:
4
- ZenTest (4.7.0)
5
- actionmailer (3.2.3)
6
- actionpack (= 3.2.3)
7
- mail (~> 2.4.4)
8
- actionpack (3.2.3)
9
- activemodel (= 3.2.3)
10
- activesupport (= 3.2.3)
11
+ ZenTest (4.8.3)
12
+ actionpack (3.2.11)
13
+ activemodel (= 3.2.11)
14
+ activesupport (= 3.2.11)
11
15
  builder (~> 3.0.0)
12
16
  erubis (~> 2.7.0)
13
- journey (~> 1.0.1)
17
+ journey (~> 1.0.4)
14
18
  rack (~> 1.4.0)
15
19
  rack-cache (~> 1.2)
16
20
  rack-test (~> 0.6.1)
17
- sprockets (~> 2.1.2)
18
- activemodel (3.2.3)
19
- activesupport (= 3.2.3)
21
+ sprockets (~> 2.2.1)
22
+ activemodel (3.2.11)
23
+ activesupport (= 3.2.11)
20
24
  builder (~> 3.0.0)
21
- activerecord (3.2.3)
22
- activemodel (= 3.2.3)
23
- activesupport (= 3.2.3)
25
+ activerecord (3.2.11)
26
+ activemodel (= 3.2.11)
27
+ activesupport (= 3.2.11)
24
28
  arel (~> 3.0.2)
25
29
  tzinfo (~> 0.3.29)
26
- activeresource (3.2.3)
27
- activemodel (= 3.2.3)
28
- activesupport (= 3.2.3)
29
- activesupport (3.2.3)
30
+ activesupport (3.2.11)
30
31
  i18n (~> 0.6)
31
32
  multi_json (~> 1.0)
32
33
  arel (3.0.2)
33
- builder (3.0.0)
34
+ autotest (4.4.6)
35
+ ZenTest (>= 4.4.1)
36
+ builder (3.0.4)
34
37
  diff-lcs (1.1.3)
35
38
  erubis (2.7.0)
36
- git (1.2.5)
37
39
  hike (1.2.1)
38
- i18n (0.6.0)
39
- jeweler (1.8.3)
40
- bundler (~> 1.0)
41
- git (>= 1.2.5)
42
- rake
43
- rdoc
44
- journey (1.0.3)
45
- json (1.6.6)
46
- mail (2.4.4)
47
- i18n (>= 0.4.0)
48
- mime-types (~> 1.16)
49
- treetop (~> 1.4.8)
50
- mime-types (1.18)
51
- multi_json (1.3.0)
52
- polyglot (0.3.3)
53
- rack (1.4.1)
40
+ i18n (0.6.1)
41
+ journey (1.0.4)
42
+ json (1.7.6)
43
+ multi_json (1.5.0)
44
+ mysql (2.9.0)
45
+ mysql2 (0.3.11)
46
+ rack (1.4.4)
54
47
  rack-cache (1.2)
55
48
  rack (>= 0.4)
56
49
  rack-ssl (1.3.2)
57
50
  rack
58
- rack-test (0.6.1)
51
+ rack-test (0.6.2)
59
52
  rack (>= 1.0)
60
- rails (3.2.3)
61
- actionmailer (= 3.2.3)
62
- actionpack (= 3.2.3)
63
- activerecord (= 3.2.3)
64
- activeresource (= 3.2.3)
65
- activesupport (= 3.2.3)
66
- bundler (~> 1.0)
67
- railties (= 3.2.3)
68
- railties (3.2.3)
69
- actionpack (= 3.2.3)
70
- activesupport (= 3.2.3)
53
+ railties (3.2.11)
54
+ actionpack (= 3.2.11)
55
+ activesupport (= 3.2.11)
71
56
  rack-ssl (~> 1.3.2)
72
57
  rake (>= 0.8.7)
73
58
  rdoc (~> 3.4)
74
- thor (~> 0.14.6)
75
- rake (0.9.2.2)
59
+ thor (>= 0.14.6, < 2.0)
60
+ rake (10.0.3)
76
61
  rdoc (3.12)
77
62
  json (~> 1.4)
78
- rspec (2.8.0)
79
- rspec-core (~> 2.8.0)
80
- rspec-expectations (~> 2.8.0)
81
- rspec-mocks (~> 2.8.0)
82
- rspec-core (2.8.0)
83
- rspec-expectations (2.8.0)
84
- diff-lcs (~> 1.1.2)
85
- rspec-mocks (2.8.0)
86
- rspec-rails (2.8.1)
63
+ rspec (2.10.0)
64
+ rspec-core (~> 2.10.0)
65
+ rspec-expectations (~> 2.10.0)
66
+ rspec-mocks (~> 2.10.0)
67
+ rspec-core (2.10.1)
68
+ rspec-expectations (2.10.0)
69
+ diff-lcs (~> 1.1.3)
70
+ rspec-mocks (2.10.1)
71
+ rspec-rails (2.10.1)
87
72
  actionpack (>= 3.0)
88
73
  activesupport (>= 3.0)
89
74
  railties (>= 3.0)
90
- rspec (~> 2.8.0)
91
- simplecov (0.6.1)
75
+ rspec (~> 2.10.0)
76
+ simplecov (0.7.1)
92
77
  multi_json (~> 1.0)
93
- simplecov-html (~> 0.5.3)
94
- simplecov-html (0.5.3)
95
- sprockets (2.1.2)
78
+ simplecov-html (~> 0.7.1)
79
+ simplecov-html (0.7.1)
80
+ sprockets (2.2.2)
96
81
  hike (~> 1.2)
82
+ multi_json (~> 1.0)
97
83
  rack (~> 1.0)
98
84
  tilt (~> 1.1, != 1.3.0)
99
- sqlite3 (1.3.5)
100
- thor (0.14.6)
85
+ sqlite3 (1.3.7)
86
+ thor (0.17.0)
101
87
  tilt (1.3.3)
102
- treetop (1.4.10)
103
- polyglot
104
- polyglot (>= 0.3.1)
105
- tzinfo (0.3.33)
106
- yaml_waml (0.3.0)
107
- yard (0.7.5)
88
+ tzinfo (0.3.35)
89
+ yard (0.8.3)
108
90
 
109
91
  PLATFORMS
110
92
  ruby
111
93
 
112
94
  DEPENDENCIES
113
- ZenTest
114
- activerecord (>= 3.0.0)
115
- activesupport (>= 3.0.0)
116
- bundler (~> 1.0.0)
117
- jeweler (~> 1.8.3)
118
- rails (>= 3.0.0)
119
- rdoc (~> 3.12)
120
- rspec (~> 2.8.0)
121
- rspec-rails (~> 2.8.1)
95
+ ZenTest (= 4.8.3)
96
+ autotest
97
+ bundler
98
+ mysql
99
+ mysql2
100
+ rake
101
+ rspec (~> 2.10.0)
102
+ rspec-rails (~> 2.10.1)
103
+ schema_comments!
122
104
  simplecov
123
105
  sqlite3
124
- yaml_waml
125
- yard (~> 0.7)
106
+ yard
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = SchemaComments
1
+ = SchemaComments {<img src="https://secure.travis-ci.org/akm/schema_comments.png" />}[http://travis-ci.org/akm/schema_comments]
2
2
 
3
3
  == Install
4
4
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0.alpha5
1
+ 0.2.0
@@ -1,18 +1,18 @@
1
1
  require 'active_support/core_ext/module'
2
2
 
3
3
  module SchemaComments
4
- VERSION = '0.1.0'
4
+ VERSION = File.read(File.expand_path("../../VERSION", __FILE__))
5
5
 
6
- autoload :Base, 'schema_comments/base'
6
+ autoload :Base , 'schema_comments/base'
7
7
  autoload :ConnectionAdapters, 'schema_comments/connection_adapters'
8
- autoload :Migration, 'schema_comments/migration'
9
- autoload :Migrator, 'schema_comments/migrator'
10
- autoload :Schema, 'schema_comments/schema'
11
- autoload :SchemaComment, 'schema_comments/schema_comment'
12
- autoload :SchemaDumper, 'schema_comments/schema_dumper'
8
+ autoload :Migration , 'schema_comments/migration'
9
+ autoload :Migrator , 'schema_comments/migrator'
10
+ autoload :Schema , 'schema_comments/schema'
11
+ autoload :SchemaComment , 'schema_comments/schema_comment'
12
+ autoload :SchemaDumper , 'schema_comments/schema_dumper'
13
13
 
14
14
  mattr_accessor :yaml_path
15
- self.yaml_path = Rails.root.join("db/schema_comments.yml").to_s if defined?(Rails)
15
+ self.yaml_path = Rails.root.join("db/schema_comments.yml").to_s if defined?(Rails) && Rails.root
16
16
 
17
17
  mattr_accessor :quiet
18
18
 
@@ -2,27 +2,31 @@
2
2
  module SchemaComments
3
3
  class SchemaDumper < ActiveRecord::SchemaDumper
4
4
 
5
- private
6
- def tables(stream)
7
- result = super(stream)
8
- # ビューはtableの後に実行するようにしないと rake db:schema:load で失敗します。
9
- mysql_views(stream) if adapter_name == "mysql"
10
- result
5
+ autoload :Mysql, 'schema_comments/schema_dumper/mysql'
6
+
7
+ def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
8
+ dumper =
9
+ case connection.adapter_name
10
+ when /mysql/i then
11
+ SchemaComments::SchemaDumper::Mysql.new(connection)
12
+ else
13
+ new(connection)
14
+ end
15
+ dumper.dump(stream)
16
+ stream
11
17
  end
12
18
 
13
- def mysql_view?(table)
14
- return false unless adapter_name == 'mysql'
15
- config = ActiveRecord::Base.configurations[Rails.env]
16
- match_count = @connection.select_value(
17
- "select count(*) from information_schema.TABLES where TABLE_TYPE = 'VIEW' AND TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'" % [
18
- config["database"], table])
19
- match_count.to_i > 0
19
+ def dump(stream)
20
+ header(stream)
21
+ tables(stream)
22
+ selectable_attrs(stream)
23
+ trailer(stream)
24
+ stream
20
25
  end
21
26
 
22
- def table(table, stream)
23
- # MySQLは、ビューもテーブルとして扱うので、一個一個チェックします。
24
- return if mysql_view?(table)
27
+ private
25
28
 
29
+ def table(table, stream)
26
30
  columns = @connection.columns(table)
27
31
  begin
28
32
  tbl = StringIO.new
@@ -118,33 +122,90 @@ module SchemaComments
118
122
  config ? config['adapter'] : ActiveRecord::Base.connection.adapter_name
119
123
  end
120
124
 
121
- def mysql_views(stream)
122
- config = ActiveRecord::Base.configurations[Rails.env]
123
- view_names = @connection.select_values(
124
- "select TABLE_NAME from information_schema.TABLES where TABLE_TYPE = 'VIEW' AND TABLE_SCHEMA = '%s'" % config["database"])
125
- view_names.each do |view_name|
126
- mysql_view(view_name, stream)
125
+ def selectable_attrs(stream)
126
+ return unless defined?(SelectableAttr)
127
+ enum_defs = []
128
+ classes = Dir["app/models/*.rb"].map{|f| File.basename(f, ".*").camelize.constantize}.select{|k| k.respond_to?(:single_selectable_attrs)}
129
+ classes.each do |klass|
130
+ (klass.single_selectable_attrs + klass.multi_selectable_attrs).each do |enum_name|
131
+ enum = klass.send(klass.enum_base_name(enum_name) + "_enum")
132
+ enum_def = enum_defs.detect{|enum_def| enum_def[:enum] === enum && enum_def[:enum_name] == enum_name}
133
+ unless enum_def
134
+ enum_def = {:enum => enum, :enum_name => enum_name, :usages => []}
135
+ enum_defs << enum_def
136
+ end
137
+ enum_def[:usages] << {:class => klass, :enum_name => enum_name}
138
+ end
139
+ end
140
+
141
+ stream.puts
142
+
143
+ enum_defs.each do |enum_def|
144
+ selectable_attr(enum_def[:enum], enum_def[:usages], stream)
145
+ end
146
+ end
147
+
148
+ def selectable_attr(enum, usages, stream)
149
+ buf = StringIO.new
150
+
151
+ specs = []
152
+ usages.each do |usage|
153
+ klass = usage[:class]
154
+ method_caption = nil
155
+ if col = usage[:class].columns.detect{|c| c.name == usage[:enum_name]}
156
+ method_caption = col.comment
157
+ end
158
+ method_caption ||= usage[:enum_name]
159
+ spec = {
160
+ :class_name => klass.name,
161
+ :methos_name => usage[:enum_name],
162
+ :class_caption => klass.table_comment,
163
+ :method_caption => method_caption
164
+ }
165
+ specs << spec
127
166
  end
167
+ keys = [:class_name, :methos_name, :class_caption, :method_caption]
168
+ lengths = keys.map{ |key| specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
169
+ format_string = lengths.map{ |len| "%-#{len}s" }
170
+ format_string *= ''
171
+ specs.each do |spec|
172
+ values = keys.zip(lengths).map{ |key, len| spec.key?(key) ? spec[key] + " " : " " * len }
173
+ buf.print(' # ')
174
+ buf.print((format_string % values).gsub(/,\s*$/, '').strip)
175
+ buf.puts
176
+ end
177
+ buf.puts " #"
178
+
179
+ selectable_attr_with_default(enum, usages, buf)
180
+
181
+ buf.puts " #"
182
+ buf.puts
183
+
184
+ buf.rewind
185
+ stream.print buf.read
128
186
  end
129
187
 
130
- def mysql_view(view_name, stream)
131
- ddl = @connection.select_value("show create view #{view_name}")
132
- ddl.gsub!(/^CREATE .+? VIEW /i, "CREATE OR REPLACE VIEW ")
133
- ddl.gsub!(/AS select/, "AS \n select\n")
134
- ddl.gsub!(/( AS \`.+?\`\,)/){ "#{$1}\n" }
135
- ddl.gsub!(/ from /i , "\n from \n")
136
- ddl.gsub!(/ where /i , "\n where \n")
137
- ddl.gsub!(/ order by /i , "\n order by \n")
138
- ddl.gsub!(/ having /i , "\n having \n")
139
- ddl.gsub!(/ union /i , "\n union \n")
140
- ddl.gsub!(/ and /i , "\n and ")
141
- ddl.gsub!(/ or /i , "\n or ")
142
- ddl.gsub!(/inner join/i , "\n inner join")
143
- ddl.gsub!(/left join/i , "\n left join")
144
- ddl.gsub!(/left outer join/i, "\n left outer join")
145
- stream.print(" ActiveRecord::Base.connection.execute(<<-EOS)\n")
146
- stream.print(ddl.split(/\n/).map{|line| ' ' << line.strip}.join("\n"))
147
- stream.print("\n EOS\n")
188
+ def selectable_attr_with_default(enum, usages, buf)
189
+ entry_specs = enum.entries.map do |e|
190
+ {:id => "#{e.id.inspect} |", :name => e.name.inspect, :options => e.options ? e.options.inspect : ''}
191
+ end
192
+ entry_specs.unshift({
193
+ :id => "val |", :name => "name and options", :options => ""})
194
+ keys = [:id, :name, :options] & entry_specs.map{ |k| k.keys }.flatten
195
+ lengths = keys.map{ |key| entry_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
196
+ format_string = ["%#{lengths[0]}s" ] + lengths[1..-1].map{ |len| "%-#{len}s" }
197
+ format_string *= ''
198
+ entry_specs.each_with_index do |enum_spec, idx|
199
+ values = keys.zip(lengths).map{ |key, len| enum_spec.key?(key) ? enum_spec[key] + " " : " " * len }
200
+ line = (format_string % values).gsub(/\s+\Z/, '')
201
+ buf.print(' # ')
202
+ buf.print(line)
203
+ buf.puts
204
+ if idx == 0
205
+ buf.print(' # ')
206
+ buf.puts("-" * line.length)
207
+ end
208
+ end
148
209
  end
149
210
 
150
211
  end
@@ -0,0 +1,220 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'schema_comments/schema_dumper'
3
+
4
+ module SchemaComments
5
+
6
+ class SchemaDumper::Mysql < SchemaComments::SchemaDumper
7
+
8
+ class << self
9
+ public :new
10
+ end
11
+
12
+ private
13
+ def tables(stream)
14
+ result = super(stream)
15
+ # ビューはtableの後に実行するようにしないと rake db:schema:load で失敗します。
16
+ mysql_views(stream)
17
+ result
18
+ end
19
+
20
+ def mysql_view?(table)
21
+ return false unless adapter_name == 'mysql'
22
+ match_count = @connection.select_value(
23
+ "select count(*) from information_schema.TABLES where TABLE_TYPE = 'VIEW' AND TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'" % [
24
+ config["database"], table])
25
+ match_count.to_i > 0
26
+ end
27
+
28
+ def config
29
+ ActiveRecord::Base.configurations[Rails.env] || ActiveRecord::Base.configurations[ ENV['DB'] ]
30
+ end
31
+
32
+ def adapter_name
33
+ c = ActiveRecord::Base.configurations[Rails.env]
34
+ c ? c['adapter'] : ActiveRecord::Base.connection.adapter_name
35
+ end
36
+
37
+ def header(stream)
38
+ define_params = @version ? ":version => #{@version}" : ""
39
+
40
+ if stream.respond_to?(:external_encoding)
41
+ stream.puts "# encoding: #{stream.external_encoding.name}"
42
+ end
43
+
44
+ stream.puts <<HEADER
45
+ # This file is auto-generated from the current state of the database. Instead
46
+ # of editing this file, please use the migrations feature of Active Record to
47
+ # incrementally modify your database, and then regenerate this schema definition.
48
+ #
49
+ # Note that this schema.rb definition is the authoritative source for your
50
+ # database schema. If you need to create the application database on another
51
+ # system, you should be using db:schema:load, not running all the migrations
52
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
53
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
54
+ #
55
+ # It's strongly recommended to check this file into your version control system.
56
+
57
+ ActiveRecord::Schema.define(#{define_params}) do
58
+
59
+ HEADER
60
+ end
61
+
62
+ def table(table, stream)
63
+ # MySQLは、ビューもテーブルとして扱うので、一個一個チェックします。
64
+ return if mysql_view?(table)
65
+
66
+ columns = @connection.columns(table)
67
+ begin
68
+ tbl = StringIO.new
69
+
70
+ # first dump primary key column
71
+ if @connection.respond_to?(:pk_and_sequence_for)
72
+ pk, _ = @connection.pk_and_sequence_for(table)
73
+ elsif @connection.respond_to?(:primary_key)
74
+ pk = @connection.primary_key(table)
75
+ end
76
+
77
+ tbl.print " create_table #{table.inspect}"
78
+ pk_exist = columns.detect { |c| c.name == pk } # for print ALTER TABLE ADD PRIMARY KEY
79
+ if pk_exist
80
+ if pk != 'id'
81
+ tbl.print %Q(, :primary_key => "#{pk}")
82
+ end
83
+ else
84
+ tbl.print ", :id => false"
85
+ end
86
+ tbl.print ", :force => true"
87
+
88
+ table_comment = @connection.table_comment(table)
89
+ tbl.print ", :comment => '#{table_comment}'" unless table_comment.blank?
90
+
91
+ tbl.puts " do |t|"
92
+
93
+ # then dump all non-primary key columns
94
+ column_specs = columns.map do |column|
95
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
96
+ # next if column.name == pk
97
+ spec = {}
98
+ spec[:name] = column.name.inspect
99
+
100
+ # AR has an optimization which handles zero-scale decimals as integers. This
101
+ # code ensures that the dumper still dumps the column as a decimal.
102
+ # spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
103
+ # 'decimal'
104
+ # else
105
+ # column.type.to_s
106
+ # end
107
+ spec[:type] = column.sql_type.inspect
108
+ # spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
109
+ spec[:precision] = column.precision.inspect if column.precision
110
+ spec[:scale] = column.scale.inspect if column.scale
111
+ spec[:null] = 'false' unless column.null
112
+ spec[:default] = default_string(column.default) if column.has_default?
113
+ if column.name == pk
114
+ spec[:comment] = '"AUTO_INCREMENT PRIMARY KEY by rails"'
115
+ else
116
+ spec[:comment] = '"' << (column.comment || '').gsub(/\"/, '\"') << '"' # ここでinspectを使うと最後の文字だけ文字化け(UTF-8のコード)になっちゃう
117
+ end
118
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
119
+ spec
120
+ end.compact
121
+
122
+ # find all migration keys used in this table
123
+ # keys = [:name, :limit, :precision, :scale, :default, :null, :comment] & column_specs.map{ |k| k.keys }.flatten
124
+ keys = [:name, :type, :default, :null, :comment] & column_specs.map{ |k| k.keys }.flatten
125
+
126
+ # figure out the lengths for each column based on above keys
127
+ lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
128
+
129
+ # the string we're going to sprintf our values against, with standardized column widths
130
+ format_string = lengths.map{ |len| "%-#{len}s" }
131
+
132
+ # find the max length for the 'type' column, which is special
133
+ # type_length = column_specs.map{ |column| column[:type].length }.max
134
+
135
+ # add column type definition to our format string
136
+ # format_string.unshift " t.%-#{type_length}s "
137
+
138
+ format_string.unshift " t.column "
139
+
140
+ format_string *= ''
141
+
142
+ column_specs.each do |colspec|
143
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
144
+ # values.unshift colspec[:type]
145
+ s = (format_string % values).gsub(/,\s*$/, '')
146
+
147
+ if colspec[:name] == pk.inspect
148
+ tbl.print(s.sub(/ t.column /, " #t.column "))
149
+ else
150
+ tbl.print(s)
151
+ end
152
+ tbl.puts
153
+ end
154
+
155
+ tbl.puts " end"
156
+ primary_keys(table, tbl) unless pk_exist
157
+ tbl.puts
158
+
159
+ indexes(table, tbl)
160
+
161
+ tbl.rewind
162
+ stream.print tbl.read
163
+ rescue => e
164
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
165
+ stream.puts "# #{e.message}"
166
+ stream.puts
167
+ end
168
+
169
+ stream
170
+ end
171
+
172
+ def mysql_views(stream)
173
+ view_names = @connection.select_values(
174
+ "select TABLE_NAME from information_schema.TABLES where TABLE_TYPE = 'VIEW' AND TABLE_SCHEMA = '%s'" % config["database"])
175
+ view_names.each do |view_name|
176
+ mysql_view(view_name, stream)
177
+ end
178
+ end
179
+
180
+ def mysql_view(view_name, stream)
181
+ ddl = @connection.select_value("show create view #{view_name}")
182
+ ddl.gsub!(/^CREATE .+? VIEW /i, "CREATE OR REPLACE VIEW ")
183
+ ddl.gsub!(/AS select/, "AS \n select\n")
184
+ ddl.gsub!(/( AS \`.+?\`\,)/){ "#{$1}\n" }
185
+ ddl.gsub!(/ from /i , "\n from \n")
186
+ ddl.gsub!(/ where /i , "\n where \n")
187
+ ddl.gsub!(/ order by /i , "\n order by \n")
188
+ ddl.gsub!(/ having /i , "\n having \n")
189
+ ddl.gsub!(/ union /i , "\n union \n")
190
+ ddl.gsub!(/ and /i , "\n and ")
191
+ ddl.gsub!(/ or /i , "\n or ")
192
+ ddl.gsub!(/inner join/i , "\n inner join")
193
+ ddl.gsub!(/left join/i , "\n left join")
194
+ ddl.gsub!(/left outer join/i, "\n left outer join")
195
+ stream.print(" ActiveRecord::Base.connection.execute(<<-EOS)\n")
196
+ stream.print(ddl.split(/\n/).map{|line| ' ' << line.strip}.join("\n"))
197
+ stream.print("\n EOS\n")
198
+ end
199
+
200
+
201
+ def primary_keys(table, tbl)
202
+ res = @connection.select("SHOW CREATE TABLE #{table}")
203
+ create_table = res.first['Create Table']
204
+ pks = create_table.scan(/\sPRIMARY KEY \((.+)\)\,/).flatten.first.
205
+ split(/,/).map{|s| s.gsub(/^\`|\`$/, '')}
206
+ tbl.puts(" execute \"ALTER TABLE #{table} ADD PRIMARY KEY (#{pks.join(',')})\"")
207
+
208
+ if create_table =~ /`id` (.+) NOT NULL AUTO_INCREMENT,/
209
+ tbl.puts(" execute \"ALTER TABLE #{table} CHANGE COLUMN `id` `id` #{$1} NOT NULL AUTO_INCREMENT\"")
210
+ end
211
+
212
+ if create_table =~ %r{/\*\!50100 (PARTITION .+ )\*/}m
213
+ st = $1.gsub(/\n/, ' ')
214
+ tbl.puts(" execute \"ALTER TABLE #{table} #{st}\"")
215
+ end
216
+ end
217
+
218
+ end
219
+
220
+ end