schema_comments 0.1.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/.gitignore +4 -0
- data/LICENSE.txt +60 -0
- data/README +149 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +10 -0
- data/init.rb +4 -0
- data/lib/annotate_models.rb +224 -0
- data/lib/schema_comments/base.rb +72 -0
- data/lib/schema_comments/connection_adapters.rb +170 -0
- data/lib/schema_comments/migration.rb +20 -0
- data/lib/schema_comments/migrator.rb +20 -0
- data/lib/schema_comments/schema.rb +20 -0
- data/lib/schema_comments/schema_comment.rb +195 -0
- data/lib/schema_comments/schema_dumper.rb +160 -0
- data/lib/schema_comments.rb +53 -0
- data/spec/.gitignore +3 -0
- data/spec/annotate_models_spec.rb +56 -0
- data/spec/database.yml +13 -0
- data/spec/fixtures/.gitignore +0 -0
- data/spec/i18n_export_spec.rb +48 -0
- data/spec/migration_spec.rb +96 -0
- data/spec/migrations/valid/001_create_products.rb +17 -0
- data/spec/migrations/valid/002_rename_products.rb +10 -0
- data/spec/migrations/valid/003_rename_products_again.rb +10 -0
- data/spec/migrations/valid/004_remove_price.rb +10 -0
- data/spec/migrations/valid/005_change_products_name.rb +10 -0
- data/spec/migrations/valid/006_change_products_name_with_comment.rb +10 -0
- data/spec/resources/models/product.rb +2 -0
- data/spec/resources/models/product_name.rb +2 -0
- data/spec/schema.rb +2 -0
- data/spec/schema_dumper_spec.rb +74 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/yaml_export_spec.rb +52 -0
- data/tasks/annotate_models_tasks.rake +12 -0
- data/tasks/schema_comments.rake +204 -0
- metadata +115 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
class ChangeProductsNameWithComment < ActiveRecord::Migration
|
3
|
+
def self.up
|
4
|
+
change_column "products", 'name', :string, :limit => 100, :comment => "名称"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
change_column "products", 'name', :string, :limit => 50, :comment => "商品名"
|
9
|
+
end
|
10
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
describe ActiveRecord::SchemaDumper do
|
5
|
+
|
6
|
+
IGNORED_TABLES = %w(schema_migrations)
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
SchemaComments.yaml_path = File.expand_path(File.join(File.dirname(__FILE__), 'schema_comments.yml'))
|
10
|
+
FileUtils.rm(SchemaComments.yaml_path, :verbose => true) if File.exist?(SchemaComments.yaml_path)
|
11
|
+
|
12
|
+
(ActiveRecord::Base.connection.tables - IGNORED_TABLES).each do |t|
|
13
|
+
ActiveRecord::Base.connection.drop_table(t) rescue nil
|
14
|
+
end
|
15
|
+
ActiveRecord::Base.connection.initialize_schema_migrations_table
|
16
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "dump" do
|
20
|
+
(ActiveRecord::Base.connection.tables - %w(schema_migrations)).should == []
|
21
|
+
|
22
|
+
migration_path = File.join(MIGRATIONS_ROOT, 'valid')
|
23
|
+
Dir.glob('*.rb').each do |file|
|
24
|
+
require(file) if /^\d+?_.*/ =~ file
|
25
|
+
end
|
26
|
+
|
27
|
+
Product.reset_table_comments
|
28
|
+
Product.reset_column_comments
|
29
|
+
|
30
|
+
ActiveRecord::Migrator.up(migration_path, 1)
|
31
|
+
ActiveRecord::Migrator.current_version.should == 1
|
32
|
+
|
33
|
+
ActiveRecord::Base.export_i18n_models.keys.include?('product').should == true
|
34
|
+
ActiveRecord::Base.export_i18n_models['product'].should == '商品'
|
35
|
+
|
36
|
+
ActiveRecord::Base.export_i18n_attributes.keys.include?('product').should == true
|
37
|
+
ActiveRecord::Base.export_i18n_attributes['product'].should == {
|
38
|
+
'product_type_cd' => '種別コード',
|
39
|
+
"price" => "価格",
|
40
|
+
"name" => "商品名",
|
41
|
+
"created_at" => "登録日時",
|
42
|
+
"updated_at" => "更新日時"
|
43
|
+
}
|
44
|
+
|
45
|
+
dest = StringIO.new
|
46
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, dest)
|
47
|
+
dest.rewind
|
48
|
+
dest.read.should == <<EOS
|
49
|
+
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
50
|
+
# please use the migrations feature of Active Record to incrementally modify your database, and
|
51
|
+
# then regenerate this schema definition.
|
52
|
+
#
|
53
|
+
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
54
|
+
# to create the application database on another system, you should be using db:schema:load, not running
|
55
|
+
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
56
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
57
|
+
#
|
58
|
+
# It's strongly recommended to check this file into your version control system.
|
59
|
+
|
60
|
+
ActiveRecord::Schema.define(:version => 1) do
|
61
|
+
|
62
|
+
create_table "products", :force => true, :comment => '商品' do |t|
|
63
|
+
t.string "product_type_cd", :comment => "種別コード"
|
64
|
+
t.integer "price", :comment => "価格"
|
65
|
+
t.string "name", :comment => "商品名"
|
66
|
+
t.datetime "created_at", :comment => "登録日時"
|
67
|
+
t.datetime "updated_at", :comment => "更新日時"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
EOS
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
$KCODE='u'
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] ||= 'test'
|
4
|
+
unless defined?(RAILS_ENV)
|
5
|
+
FIXTURES_ROOT = File.join(File.dirname(__FILE__), 'fixtures') unless defined?(FIXTURES_ROOT)
|
6
|
+
|
7
|
+
RAILS_ENV = 'test'
|
8
|
+
RAILS_ROOT = File.dirname(__FILE__) unless defined?(RAILS_ROOT)
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'spec'
|
12
|
+
|
13
|
+
require 'active_support'
|
14
|
+
require 'active_record'
|
15
|
+
# require 'action_mailer'
|
16
|
+
require 'action_controller'
|
17
|
+
require 'action_view'
|
18
|
+
require 'initializer'
|
19
|
+
|
20
|
+
require 'yaml'
|
21
|
+
begin
|
22
|
+
require 'yaml_waml'
|
23
|
+
rescue
|
24
|
+
$stderr.puts "yaml_waml not found. You should [sudo] gem install kakutani-yaml_waml"
|
25
|
+
end
|
26
|
+
|
27
|
+
config = YAML.load(IO.read(File.join(File.dirname(__FILE__), 'database.yml')))
|
28
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), 'debug.log'))
|
29
|
+
ActionController::Base.logger = ActiveRecord::Base.logger
|
30
|
+
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
|
31
|
+
|
32
|
+
|
33
|
+
load(File.join(File.dirname(__FILE__), 'schema.rb'))
|
34
|
+
|
35
|
+
%w(resources/models).each do |path|
|
36
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), path)
|
37
|
+
ActiveSupport::Dependencies.load_paths << File.join(File.dirname(__FILE__), path)
|
38
|
+
end
|
39
|
+
Dir.glob("resources/**/*.rb") do |filename|
|
40
|
+
require filename
|
41
|
+
end
|
42
|
+
|
43
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
44
|
+
require File.join(File.dirname(__FILE__), '..', 'init')
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
describe SchemaComments::SchemaComment do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
SchemaComments.yaml_path = File.join(File.dirname(__FILE__), 'human_readable_schema_comments.yml')
|
8
|
+
FileUtils.rm(SchemaComments.yaml_path, :verbose => true) if File.exist?(SchemaComments.yaml_path)
|
9
|
+
|
10
|
+
(ActiveRecord::Base.connection.tables - IGNORED_TABLES).each do |t|
|
11
|
+
ActiveRecord::Base.connection.drop_table(t) rescue nil
|
12
|
+
end
|
13
|
+
ActiveRecord::Base.connection.initialize_schema_migrations_table
|
14
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should export human readable yaml" do
|
18
|
+
ActiveRecord::Schema.define(:version => 0) do
|
19
|
+
create_table(:person, :comment => '人') do |t|
|
20
|
+
t.string :name, :comment => '名前'
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table(:addresses, :comment => '住所') do |t|
|
24
|
+
t.integer :person_id, :comment => '人'
|
25
|
+
t.text :descriptions, :comment => '記述'
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table(:emails, :comment => 'メール') do |t|
|
29
|
+
t.integer :person_id, :comment => '人'
|
30
|
+
t.string :address, :comment => 'アドレス'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
File.read(SchemaComments.yaml_path).split(/$/).map(&:strip).should == %{
|
35
|
+
---
|
36
|
+
table_comments:
|
37
|
+
addresses: "住所"
|
38
|
+
emails: "メール"
|
39
|
+
person: "人"
|
40
|
+
column_comments:
|
41
|
+
addresses:
|
42
|
+
person_id: "人"
|
43
|
+
descriptions: "記述"
|
44
|
+
emails:
|
45
|
+
person_id: "人"
|
46
|
+
address: "アドレス"
|
47
|
+
person:
|
48
|
+
name: "名前"
|
49
|
+
}.split(/$/).map(&:strip)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'yaml'
|
3
|
+
require 'yaml_waml'
|
4
|
+
require 'activerecord'
|
5
|
+
|
6
|
+
# テストを実行する際はschema_commentsのschema_comments.ymlへの出力を抑制します。
|
7
|
+
namespace :db do
|
8
|
+
Rake.application.send(:eval, "@tasks.delete('db:migrate')")
|
9
|
+
desc "Migrate the database through scripts in db/migrate and update db/schema.rb by invoking db:schema:dump. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
|
10
|
+
task :migrate => :environment do
|
11
|
+
SchemaComments::SchemaComment.yaml_access do
|
12
|
+
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
13
|
+
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
14
|
+
SchemaComments.quiet = true
|
15
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Rake.application.send(:eval, "@tasks.delete('db:rollback')")
|
20
|
+
desc 'Rolls the schema back to the previous version. Specify the number of steps with STEP=n'
|
21
|
+
task :rollback => :environment do
|
22
|
+
SchemaComments::SchemaComment.yaml_access do
|
23
|
+
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
24
|
+
ActiveRecord::Migrator.rollback('db/migrate/', step)
|
25
|
+
SchemaComments.quiet = true
|
26
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
namespace :migrate do
|
31
|
+
desc 'Runs the "up" for a given migration VERSION.'
|
32
|
+
task :up => :environment do
|
33
|
+
SchemaComments::SchemaComment.yaml_access do
|
34
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
35
|
+
raise "VERSION is required" unless version
|
36
|
+
ActiveRecord::Migrator.run(:up, "db/migrate/", version)
|
37
|
+
SchemaComments.quiet = true
|
38
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Runs the "down" for a given migration VERSION.'
|
43
|
+
task :down => :environment do
|
44
|
+
SchemaComments::SchemaComment.yaml_access do
|
45
|
+
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
46
|
+
raise "VERSION is required" unless version
|
47
|
+
ActiveRecord::Migrator.run(:down, "db/migrate/", version)
|
48
|
+
SchemaComments.quiet = true
|
49
|
+
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :test do
|
55
|
+
Rake.application.send(:eval, "@tasks.delete('db:test:prepare')")
|
56
|
+
desc 'Check for pending migrations and load the test schema'
|
57
|
+
task :prepare => 'db:abort_if_pending_migrations' do
|
58
|
+
SchemaComments::SchemaComment.yaml_access do
|
59
|
+
SchemaComments.quiet = true
|
60
|
+
if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
|
61
|
+
Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load"
|
62
|
+
}[ActiveRecord::Base.schema_format]].invoke
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
class ActiveRecord::Base
|
73
|
+
class << self
|
74
|
+
attr_accessor_with_default :ignore_pattern_to_export_i18n, /\(\(\(.*\)\)\)/
|
75
|
+
|
76
|
+
def export_i18n_models
|
77
|
+
subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
|
78
|
+
(klass != SchemaComments::SchemaComment) and
|
79
|
+
klass.respond_to?(:table_exists?) and klass.table_exists?
|
80
|
+
end
|
81
|
+
result = subclasses.inject({}) do |d, m|
|
82
|
+
comment = (m.table_comment || '').dup
|
83
|
+
comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
|
84
|
+
# テーブル名(複数形)をモデル名(単数形)に
|
85
|
+
model_name = (comment.scan(/\[\[\[(?:model|class)(?:_name)?:\s*?([^\s]+?)\s*?\]\]\]/).flatten.first || m.name).underscore
|
86
|
+
comment.gsub!(/\[\[\[.*?\]\]\]/)
|
87
|
+
d[model_name] = comment
|
88
|
+
d
|
89
|
+
end
|
90
|
+
result.instance_eval do
|
91
|
+
def each_with_order(*args, &block)
|
92
|
+
self.keys.sort.each do |key|
|
93
|
+
yield(key, self[key])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
alias :each_without_order :each
|
97
|
+
alias :each :each_with_order
|
98
|
+
end
|
99
|
+
result
|
100
|
+
end
|
101
|
+
|
102
|
+
def export_i18n_attributes
|
103
|
+
subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
|
104
|
+
(klass != SchemaComments::SchemaComment) and
|
105
|
+
klass.respond_to?(:table_exists?) and klass.table_exists?
|
106
|
+
end
|
107
|
+
result = subclasses.inject({}) do |d, m|
|
108
|
+
attrs = {}
|
109
|
+
m.columns.each do |col|
|
110
|
+
next if col.name == 'id'
|
111
|
+
comment = (col.comment || '').dup
|
112
|
+
comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
|
113
|
+
|
114
|
+
# カラム名を属性名に
|
115
|
+
attr_name = (comment.scan(/\[\[\[(?:attr|attribute)(?:_name)?:\s*?([^\s]+?)\s*?\]\]\]/).flatten.first || col.name)
|
116
|
+
comment.gsub!(/\[\[\[.*?\]\]\]/)
|
117
|
+
attrs[attr_name] = comment
|
118
|
+
end
|
119
|
+
|
120
|
+
column_names = m.columns.map(&:name) - ['id']
|
121
|
+
column_order_modeule = Module.new do
|
122
|
+
def each_with_column_order(*args, &block)
|
123
|
+
@column_names.each do |column_name|
|
124
|
+
yield(column_name, self[column_name])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.extended(obj)
|
129
|
+
obj.instance_eval do
|
130
|
+
alias :each_without_column_order :each
|
131
|
+
alias :each :each_with_column_order
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
attrs.instance_variable_set(:@column_names, column_names)
|
136
|
+
attrs.extend(column_order_modeule)
|
137
|
+
|
138
|
+
# テーブル名(複数形)をモデル名(単数形)に
|
139
|
+
model_name = ((m.table_comment || '').scan(/\[\[\[(?:model|class)(?:_name)?:\s*?([^\s]+?)\s*?\]\]\]/).flatten.first || m.name).underscore
|
140
|
+
d[model_name] = attrs
|
141
|
+
d
|
142
|
+
end
|
143
|
+
|
144
|
+
result.instance_eval do
|
145
|
+
def each_with_order(*args, &block)
|
146
|
+
self.keys.sort.each do |key|
|
147
|
+
yield(key, self[key])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
alias :each_without_order :each
|
151
|
+
alias :each :each_with_order
|
152
|
+
end
|
153
|
+
result
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
namespace :i18n do
|
159
|
+
namespace :schema_comments do
|
160
|
+
task :load_all_models => :environment do
|
161
|
+
Dir.glob(File.join(RAILS_ROOT, 'app', 'models', '**', '*.rb')) do |file_name|
|
162
|
+
require file_name
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
desc "Export i18n model resources from schema_comments. you can set locale with environment variable LOCALE"
|
167
|
+
task :export_models => :"i18n:schema_comments:load_all_models" do
|
168
|
+
locale = (ENV['LOCALE'] || I18n.locale).to_s
|
169
|
+
obj = {locale => {'activerecord' => {'models' => ActiveRecord::Base.export_i18n_models}}}
|
170
|
+
puts YAML.dump(obj)
|
171
|
+
end
|
172
|
+
|
173
|
+
desc "Export i18n attributes resources from schema_comments. you can set locale with environment variable LOCALE"
|
174
|
+
task :export_attributes => :"i18n:schema_comments:load_all_models" do
|
175
|
+
locale = (ENV['LOCALE'] || I18n.locale).to_s
|
176
|
+
obj = {locale => {'activerecord' => {'attributes' => ActiveRecord::Base.export_i18n_attributes}}}
|
177
|
+
puts YAML.dump(obj)
|
178
|
+
end
|
179
|
+
|
180
|
+
desc "update i18n YAML. you can set locale with environment variable LOCALE"
|
181
|
+
task :update_config_locale => :"i18n:schema_comments:load_all_models" do
|
182
|
+
require 'yaml/store'
|
183
|
+
locale = (ENV['LOCALE'] || I18n.locale).to_s
|
184
|
+
path = (ENV['YAML_PATH'] || File.join(RAILS_ROOT, "config/locales/#{locale}.yml"))
|
185
|
+
print "updating #{path}..."
|
186
|
+
|
187
|
+
begin
|
188
|
+
db = YAML::Store.new(path)
|
189
|
+
db.transaction do
|
190
|
+
locale = db[locale] ||= {}
|
191
|
+
activerecord = locale['activerecord'] ||= {}
|
192
|
+
activerecord['models'] = ActiveRecord::Base.export_i18n_models
|
193
|
+
activerecord['attributes'] = ActiveRecord::Base.export_i18n_attributes
|
194
|
+
end
|
195
|
+
puts "Complete!"
|
196
|
+
rescue Exception
|
197
|
+
puts "Failure!!!"
|
198
|
+
puts $!.to_s
|
199
|
+
puts " " << $!.backtrace.join("\n ")
|
200
|
+
raise
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|