schema_comments 0.4.3 → 0.5.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/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.2.0
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "schema_comments"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here
data/init.rb DELETED
@@ -1,4 +0,0 @@
1
- unless ENV['SCHEMA_COMMENTS_DISABLED']
2
- require 'schema_comments'
3
- SchemaComments.setup
4
- end
@@ -1,234 +0,0 @@
1
-
2
- # fork from
3
- # http://github.com/rotuka/annotate_models/blob/d2afee82020dbc592b147d92f9beeadbf665a9e0/lib/annotate_models.rb
4
-
5
- require 'rails'
6
-
7
- require "config/environment" if File.exist?("config/environment")
8
-
9
- MODEL_DIR = Rails.root.join("app/models" )
10
- UNIT_TEST_DIR = Rails.root.join("test/unit" )
11
- SPEC_MODEL_DIR = Rails.root.join("spec/models")
12
- FIXTURES_DIR = Rails.root.join("test/fixtures")
13
- SPEC_FIXTURES_DIR = Rails.root.join("spec/fixtures")
14
-
15
- module AnnotateModels
16
-
17
- PREFIX_AT_BOTTOM = "== Schema Info"
18
- SUFFIX_AT_BOTTOM = ""
19
- PREFIX_ON_TOP = "== Schema Info =="
20
- SUFFIX_ON_TOP = "=================\n# "
21
-
22
- # ENV options
23
- {:position => 'top', :model_dir => MODEL_DIR}.each do |name, default_value|
24
- mattr_accessor name.to_sym
25
- self.send("#{name}=", ENV[name.to_s.upcase] || default_value)
26
- end
27
-
28
- def self.models
29
- (ENV['MODELS'] || '').split(',')
30
- end
31
-
32
- def self.sort_columns
33
- ENV['SORT'] =~ /yes|on|true/i
34
- end
35
-
36
- def self.output_prefix
37
- bottom? ? PREFIX_AT_BOTTOM : PREFIX_ON_TOP
38
- end
39
-
40
- def self.output_suffix
41
- bottom? ? SUFFIX_AT_BOTTOM : SUFFIX_ON_TOP
42
- end
43
-
44
- def self.bottom?
45
- self.position =~ /bottom/i
46
- end
47
-
48
- def self.separate?
49
- ENV['SEPARATE'] =~ /yes|on|true/i
50
- end
51
-
52
-
53
- # Simple quoting for the default column value
54
- def self.quote(value)
55
- case value
56
- when NilClass then "NULL"
57
- when TrueClass then "TRUE"
58
- when FalseClass then "FALSE"
59
- when Float, Fixnum, Bignum then value.to_s.inspect
60
- # BigDecimals need to be output in a non-normalized form and quoted.
61
- when BigDecimal then value.to_s('F')
62
- else
63
- value.inspect
64
- end
65
- end
66
-
67
- # Use the column information in an ActiveRecord class
68
- # to create a comment block containing a line for
69
- # each column. The line contains the column name,
70
- # the type (and length), and any optional attributes
71
- def self.get_schema_info(klass)
72
- table_info = "# Table name: #{klass.table_name}"
73
- table_info << " # #{klass.table_comment}" unless klass.table_comment.blank?
74
- table_info << "\n#\n"
75
- max_size = klass.column_names.collect{|name| name.size}.max + 1
76
-
77
- columns = klass.columns
78
-
79
- cols = if self.sort_columns
80
- pk = columns.find_all { |col| col.name == klass.primary_key }.flatten
81
- assoc = columns.find_all { |col| col.name.match(/_id$/) }.sort_by(&:name)
82
- dates = columns.find_all { |col| col.name.match(/_on$/) }.sort_by(&:name)
83
- times = columns.find_all { |col| col.name.match(/_at$/) }.sort_by(&:name)
84
- pk + assoc + (columns - pk - assoc - times - dates).compact.sort_by(&:name) + dates + times
85
- else
86
- columns
87
- end
88
-
89
- col_lines = append_comments(cols.map{|col| [col, annotate_column(col, klass, max_size)]})
90
- cols_text = col_lines.join("\n")
91
-
92
- result = "# #{self.output_prefix}\n# \n# Schema version: #{get_schema_version}\n#\n"
93
- result << table_info
94
- result << cols_text
95
- result << "\n# \n# #{self.output_suffix}" unless self.output_suffix.blank?
96
- result << "\n"
97
- result
98
- end
99
-
100
- def self.annotate_column(col, klass, max_size)
101
- col_type = col.type.to_s
102
- attrs = []
103
- attrs << "not null" unless col.null
104
- if col.default
105
- default_value =
106
- case col_type
107
- when "decimal" then col.default.to_s.sub(/\.0+\z/, '.0')
108
- else col.default
109
- end
110
- attrs << "default(#{quote(default_value)})"
111
- end
112
- attrs << "primary key" if col.name == klass.primary_key
113
-
114
- case col_type
115
- when "decimal" then
116
- col_type << "(#{col.precision}, #{col.scale})"
117
- else
118
- col_type << "(#{col.limit})" if col.limit
119
- end
120
- sprintf("# %-#{max_size}s:%-15s %s", col.name, col_type, attrs.join(", ")).rstrip
121
- end
122
-
123
- def self.append_comments(col_and_lines)
124
- max_length = col_and_lines.map{|(col, line)| line.length}.max
125
- col_and_lines.map do |(col, line)|
126
- if col.comment.blank?
127
- line
128
- else
129
- "%-#{max_length}s # %s" % [line, col.comment]
130
- end
131
- end
132
- end
133
-
134
- # Add a schema block to a file. If the file already contains
135
- # a schema info block (a comment starting
136
- # with "Schema as of ..."), remove it first.
137
- # Mod to write to the end of the file
138
- def self.annotate_one_file(file_name, info_block)
139
- if File.exist?(file_name)
140
- content = File.read(file_name)
141
-
142
- encoding_comment = content.scan(/^\#\s*-\*-(.+?)-\*-/).flatten.first
143
- content.sub!(/^\#\s*-\*-(.+?)-\*-/, '')
144
-
145
- # Remove old schema info
146
- content.sub!(/(\n)*^# #{PREFIX_ON_TOP}.*?\n(#.*\n)*# #{SUFFIX_ON_TOP}/, '')
147
- content.sub!(/(\n)*^# #{PREFIX_AT_BOTTOM}.*?\n(#.*\n)*#.*(\n)*/, '')
148
- content.sub!(/^[\n\s]*/, '')
149
-
150
- # Write it back
151
- File.open(file_name, "w") do |f|
152
- f.print "# -*- #{encoding_comment.strip} -*-\n\n" unless encoding_comment.blank?
153
- if self.bottom?
154
- f.print content
155
- f.print "\n\n"
156
- f.print info_block
157
- else
158
- f.print info_block
159
- f.print "\n" if self.separate?
160
- f.print content
161
- end
162
- end
163
- end
164
- end
165
-
166
-
167
- # Given the name of an ActiveRecord class, create a schema
168
- # info block (basically a comment containing information
169
- # on the columns and their types) and put it at the front
170
- # of the model and fixture source files.
171
- def self.annotate(klass)
172
- info = get_schema_info(klass)
173
- model_name = klass.name.underscore
174
- fixtures_name = "#{klass.table_name}.yml"
175
-
176
- [
177
- File.join(self.model_dir, "#{model_name}.rb"), # model
178
- File.join(UNIT_TEST_DIR, "#{model_name}_test.rb"), # test
179
- File.join(FIXTURES_DIR, fixtures_name), # fixture
180
- File.join(SPEC_MODEL_DIR, "#{model_name}_spec.rb"), # spec
181
- File.join(SPEC_FIXTURES_DIR, fixtures_name), # spec fixture
182
- Rails.root.join( 'test', 'factories.rb'), # factories file
183
- Rails.root.join( 'spec', 'factories.rb'), # factories file
184
- ].each { |file| annotate_one_file(file, info) }
185
- end
186
-
187
- # Return a list of the model files to annotate. If we have
188
- # command line arguments, they're assumed to be either
189
- # the underscore or CamelCase versions of model names.
190
- # Otherwise we take all the model files in the
191
- # app/models directory.
192
- def self.get_model_names
193
- result = nil
194
- if self.models.empty?
195
- Dir.chdir(self.model_dir) do
196
- result = Dir["**/*.rb"].map do |filename|
197
- filename.sub(/\.rb$/, '').camelize
198
- end
199
- end
200
- else
201
- result = self.models.dup
202
- end
203
- result
204
- end
205
-
206
- # We're passed a name of things that might be
207
- # ActiveRecord models. If we can find the class, and
208
- # if its a subclass of ActiveRecord::Base,
209
- # then pas it to the associated block
210
- def self.do_annotations
211
- annotated = self.get_model_names.inject([]) do |list, class_name|
212
- begin
213
- # klass = class_name.split('::').inject(Object){ |klass,part| klass.const_get(part) }
214
- klass = class_name.constantize
215
- if klass < ActiveRecord::Base && !klass.abstract_class?
216
- list << class_name
217
- self.annotate(klass)
218
- end
219
- rescue Exception => e
220
- puts "Unable to annotate #{class_name}: #{e.message}"
221
- end
222
- list
223
- end
224
- puts "Annotated #{annotated.join(', ')}"
225
- end
226
-
227
- def self.get_schema_version
228
- unless @schema_version
229
- version = ActiveRecord::Migrator.current_version rescue 0
230
- @schema_version = version > 0 ? version : ''
231
- end
232
- @schema_version
233
- end
234
- end
@@ -1,69 +0,0 @@
1
- module SchemaComments
2
- module Base
3
- def self.prepended(mod)
4
- mod.singleton_class.prepend ClassMethods
5
- mod.ignore_pattern_to_export_i18n = /\[.*\]/
6
- end
7
-
8
- module ClassMethods
9
- def table_comment
10
- @table_comment ||= connection.table_comment(table_name)
11
- end
12
-
13
- def columns
14
- result = super
15
- unless @column_comments_loaded
16
- column_comment_hash = connection.column_comments(table_name)
17
- result.each do |column|
18
- column.comment = column_comment_hash[column.name.to_s]
19
- end
20
- @column_comments_loaded = true
21
- end
22
- result
23
- end
24
-
25
- def reset_column_comments
26
- @column_comments_loaded = false
27
- end
28
-
29
- def reset_table_comments
30
- @table_comment = nil
31
- end
32
-
33
- attr_accessor :ignore_pattern_to_export_i18n
34
-
35
- def export_i18n_models
36
- subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
37
- (klass != SchemaComments::SchemaComment) and
38
- klass.respond_to?(:table_exists?) and klass.table_exists?
39
- end
40
- subclasses.inject({}) do |d, m|
41
- comment = m.table_comment || ''
42
- comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
43
- d[m.name.underscore] = comment
44
- d
45
- end
46
- end
47
-
48
- def export_i18n_attributes(connection = ActiveRecord::Base.connection)
49
- subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
50
- (klass != SchemaComments::SchemaComment) and
51
- klass.respond_to?(:table_exists?) and klass.table_exists?
52
- end
53
- subclasses.inject({}) do |d, m|
54
- attrs = {}
55
- m.columns.each do |col|
56
- next if col.name == 'id'
57
- comment = (col.comment || '').dup
58
- comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
59
- attrs[col.name] = comment
60
- end
61
- d[m.name.underscore] = attrs
62
- d
63
- end
64
- end
65
-
66
- end
67
-
68
- end
69
- end
@@ -1,16 +0,0 @@
1
- module SchemaComments
2
- module Migration
3
- def self.prepended(mod)
4
- mod.singleton_class.prepend(ClassMethods)
5
- end
6
-
7
- module ClassMethods
8
- def migrate(*args, &block)
9
- SchemaComments::SchemaComment.yaml_access do
10
- super(*args, &block)
11
- end
12
- end
13
- end
14
-
15
- end
16
- end
@@ -1,16 +0,0 @@
1
- module SchemaComments
2
- module Migrator
3
- def self.prepend(mod)
4
- mod.singleton_class.prepend(ClassMethods)
5
- end
6
-
7
- module ClassMethods
8
- def migrate(*args, &block)
9
- SchemaComments::SchemaComment.yaml_access do
10
- super(*args, &block)
11
- end
12
- end
13
- end
14
-
15
- end
16
- end
@@ -1,53 +0,0 @@
1
- namespace :db do
2
- namespace :schema do
3
- namespace :comments do
4
-
5
- desc "update #{SchemaComments.yaml_path} from mysql comments of tables and columns"
6
- task :dump => :environment do
7
- conn = ActiveRecord::Base.connection
8
- db_table_comments = conn.select_all("SHOW TABLE STATUS").each_with_object({}){|row, d| d[row["Name"]] = row["Comment"]}
9
- column_comments = {}
10
- db_table_comments.keys.each do |t|
11
- column_comments[t] = conn.select_all("SHOW FULL COLUMNS FROM #{t}").each_with_object({}){|row, d| d[row["Field"]] = row["Comment"]}
12
- end
13
- root = {"table_comments" => db_table_comments, "column_comments" => column_comments}
14
- SchemaComments::SchemaComment::SortedStore.sort_yaml_content!(root)
15
- open(SchemaComments.yaml_path, "w"){|f| f.puts root.to_yaml }
16
- end
17
-
18
- desc "load #{SchemaComments.yaml_path} and alter table for mysql comments"
19
- task :load => :environment do
20
- conn = ActiveRecord::Base.connection
21
- creation = ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation.new(conn)
22
-
23
- db_tables = conn.tables
24
- db_table_comments = conn.select_all("SHOW TABLE STATUS").each_with_object({}){|row, d| d[row["Name"]] = row["Comment"]}
25
- root = YAML.load_file(SchemaComments.yaml_path)
26
- column_comments_root = root["column_comments"]
27
- root["table_comments"].each do |t, t_comment|
28
- unless db_tables.include?(t)
29
- $stderr.puts "\e[33mtable #{t.inspect} doesn't exist\e[0m"
30
- next
31
- end
32
- unless t_comment == db_table_comments[t]
33
- conn.execute("ALTER TABLE #{t} COMMENT '#{t_comment}'")
34
- end
35
-
36
- column_hash = conn.columns(t).each_with_object({}){|c, d| d[c.name] = c}
37
- db_column_comments = conn.select_all("SHOW FULL COLUMNS FROM #{t}").each_with_object({}){|row, d| d[row["Field"]] = row["Comment"]}
38
- column_comments = column_comments_root[t]
39
- column_comments.each do |c, c_comment|
40
- unless c_comment == db_column_comments[t]
41
- if col = column_hash[c]
42
- opts = col.sql_type.dup
43
- creation.send(:add_column_options!, opts, column: col, null: col.null, default: col.default, auto_increment: (col.extra == "auto_increment"))
44
- conn.execute("ALTER TABLE #{t} MODIFY #{c} #{opts} COMMENT '#{c_comment}'")
45
- end
46
- end
47
- end
48
- end
49
- end
50
-
51
- end
52
- end
53
- end
@@ -1,12 +0,0 @@
1
- # Original file
2
- # http://github.com/rotuka/annotate_models/blob/d2afee82020dbc592b147d92f9beeadbf665a9e0/tasks/annotate_models_tasks.rake
3
- namespace :db do
4
- desc "Add schema information (as comments) to model files"
5
- task :annotate => :environment do
6
- require File.join(File.dirname(__FILE__), "../lib/annotate_models.rb")
7
- AnnotateModels.do_annotations
8
- end
9
-
10
- desc "Updates database (migrate and annotate models)"
11
- task :update => %w(migrate annotate)
12
- end