schema_comments 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,72 @@
|
|
1
|
+
module SchemaComments
|
2
|
+
module Base
|
3
|
+
def self.included(mod)
|
4
|
+
mod.extend ClassMethods
|
5
|
+
mod.instance_eval do
|
6
|
+
alias :columns_without_schema_comments :columns
|
7
|
+
alias :columns :columns_with_schema_comments
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def table_comment
|
13
|
+
@table_comment ||= connection.table_comment(table_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def columns_with_schema_comments
|
17
|
+
result = columns_without_schema_comments
|
18
|
+
unless @column_comments_loaded
|
19
|
+
column_comment_hash = connection.column_comments(table_name)
|
20
|
+
result.each do |column|
|
21
|
+
column.comment = column_comment_hash[column.name.to_s]
|
22
|
+
end
|
23
|
+
@column_comments_loaded = true
|
24
|
+
end
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_column_comments
|
29
|
+
@column_comments_loaded = false
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_table_comments
|
33
|
+
@table_comment = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_accessor_with_default :ignore_pattern_to_export_i18n, /\[.*\]/
|
37
|
+
|
38
|
+
def export_i18n_models
|
39
|
+
subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
|
40
|
+
(klass != SchemaComments::SchemaComment) and
|
41
|
+
klass.respond_to?(:table_exists?) and klass.table_exists?
|
42
|
+
end
|
43
|
+
subclasses.inject({}) do |d, m|
|
44
|
+
comment = m.table_comment
|
45
|
+
comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
|
46
|
+
d[m.name.underscore] = comment
|
47
|
+
d
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def export_i18n_attributes(connection = ActiveRecord::Base.connection)
|
52
|
+
subclasses = ActiveRecord::Base.send(:subclasses).select do |klass|
|
53
|
+
(klass != SchemaComments::SchemaComment) and
|
54
|
+
klass.respond_to?(:table_exists?) and klass.table_exists?
|
55
|
+
end
|
56
|
+
subclasses.inject({}) do |d, m|
|
57
|
+
attrs = {}
|
58
|
+
m.columns.each do |col|
|
59
|
+
next if col.name == 'id'
|
60
|
+
comment = (col.comment || '').dup
|
61
|
+
comment.gsub!(ignore_pattern_to_export_i18n, '') if ignore_pattern_to_export_i18n
|
62
|
+
attrs[col.name] = comment
|
63
|
+
end
|
64
|
+
d[m.name.underscore] = attrs
|
65
|
+
d
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module SchemaComments
|
3
|
+
module ConnectionAdapters
|
4
|
+
|
5
|
+
module Column
|
6
|
+
attr_accessor :comment
|
7
|
+
end
|
8
|
+
|
9
|
+
module ColumnDefinition
|
10
|
+
attr_accessor :comment
|
11
|
+
end
|
12
|
+
|
13
|
+
module TableDefinition
|
14
|
+
def self.included(mod)
|
15
|
+
mod.module_eval do
|
16
|
+
alias_method_chain(:column, :schema_comments)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
attr_accessor :comment
|
20
|
+
|
21
|
+
def column_with_schema_comments(name, type, options = {})
|
22
|
+
column_without_schema_comments(name, type, options)
|
23
|
+
column = self[name]
|
24
|
+
column.comment = options[:comment]
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Adapter
|
30
|
+
def column_comment(table_name, column_name, comment = nil) #:nodoc:
|
31
|
+
if comment
|
32
|
+
SchemaComment.save_column_comment(table_name, column_name, comment) unless SchemaComments.quiet
|
33
|
+
return comment
|
34
|
+
else
|
35
|
+
SchemaComment.column_comment(table_name, column_name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Mass assignment of comments in the form of a hash. Example:
|
40
|
+
# column_comments {
|
41
|
+
# :users => {:first_name => "User's given name", :last_name => "Family name"},
|
42
|
+
# :tags => {:id => "Tag IDentifier"}}
|
43
|
+
def column_comments(contents)
|
44
|
+
if contents.is_a?(Hash)
|
45
|
+
contents.each_pair do |table, cols|
|
46
|
+
cols.each_pair do |col, comment|
|
47
|
+
column_comment(table, col, comment) unless SchemaComments.quiet
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
SchemaComment.column_comments(contents)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def table_comment(table_name, comment = nil) #:nodoc:
|
56
|
+
if comment
|
57
|
+
comment = (comment[:comment] || comment['comment']) if comment.is_a?(Hash)
|
58
|
+
SchemaComment.save_table_comment(table_name, comment) unless SchemaComments.quiet
|
59
|
+
return comment
|
60
|
+
else
|
61
|
+
SchemaComment.table_comment(table_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete_schema_comments(table_name, column_name = nil)
|
66
|
+
SchemaComment.destroy_of(table_name, column_name) unless SchemaComments.quiet
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_schema_comments_table_name(table_name, new_name)
|
70
|
+
SchemaComment.update_table_name(table_name, new_name) unless SchemaComments.quiet
|
71
|
+
end
|
72
|
+
|
73
|
+
def update_schema_comments_column_name(table_name, column_name, new_name)
|
74
|
+
SchemaComment.update_column_name(table_name, column_name, new_name) unless SchemaComments.quiet
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module ConcreteAdapter
|
79
|
+
def self.included(mod)
|
80
|
+
mod.module_eval do
|
81
|
+
alias_method_chain :columns, :schema_comments
|
82
|
+
alias_method_chain :create_table, :schema_comments
|
83
|
+
alias_method_chain :drop_table, :schema_comments
|
84
|
+
alias_method_chain :rename_table, :schema_comments
|
85
|
+
alias_method_chain :remove_column, :schema_comments
|
86
|
+
alias_method_chain :add_column, :schema_comments
|
87
|
+
alias_method_chain :change_column, :schema_comments
|
88
|
+
alias_method_chain :rename_column, :schema_comments
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def columns_with_schema_comments(table_name, name = nil, &block)
|
93
|
+
result = columns_without_schema_comments(table_name, name, &block)
|
94
|
+
column_comment_hash = column_comments(table_name)
|
95
|
+
result.each do |column|
|
96
|
+
column.comment = column_comment_hash[column.name]
|
97
|
+
end
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_table_with_schema_comments(table_name, options = {}, &block)
|
102
|
+
table_def = nil
|
103
|
+
result = create_table_without_schema_comments(table_name, options) do |t|
|
104
|
+
table_def = t
|
105
|
+
yield(t)
|
106
|
+
end
|
107
|
+
table_comment(table_name, options[:comment]) unless options[:comment].blank?
|
108
|
+
table_def.columns.each do |col|
|
109
|
+
column_comment(table_name, col.name, col.comment) unless col.comment.blank?
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
|
114
|
+
def drop_table_with_schema_comments(table_name, options = {}, &block)
|
115
|
+
result = drop_table_without_schema_comments(table_name, options)
|
116
|
+
delete_schema_comments(table_name) unless @ignore_drop_table
|
117
|
+
result
|
118
|
+
end
|
119
|
+
|
120
|
+
def rename_table_with_schema_comments(table_name, new_name)
|
121
|
+
result = rename_table_without_schema_comments(table_name, new_name)
|
122
|
+
update_schema_comments_table_name(table_name, new_name)
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
def remove_column_with_schema_comments(table_name, *column_names)
|
127
|
+
# sqlite3ではremove_columnがないので、以下のフローでスキーマ更新します。
|
128
|
+
# 1. CREATE TEMPORARY TABLE "altered_xxxxxx" (・・・)
|
129
|
+
# 2. PRAGMA index_list("xxxxxx")
|
130
|
+
# 3. DROP TABLE "xxxxxx"
|
131
|
+
# 4. CREATE TABLE "xxxxxx"
|
132
|
+
# 5. PRAGMA index_list("altered_xxxxxx")
|
133
|
+
# 6. DROP TABLE "altered_xxxxxx"
|
134
|
+
#
|
135
|
+
# このdrop tableの際に、schema_commentsを変更しないようにフラグを立てています。
|
136
|
+
@ignore_drop_table = true
|
137
|
+
remove_column_without_schema_comments(table_name, *column_names)
|
138
|
+
column_names.each do |column_name|
|
139
|
+
delete_schema_comments(table_name, column_name)
|
140
|
+
end
|
141
|
+
ensure
|
142
|
+
@ignore_drop_table = false
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_column_with_schema_comments(table_name, column_name, type, options = {})
|
146
|
+
comment = options.delete(:comment)
|
147
|
+
result = add_column_without_schema_comments(table_name, column_name, type, options)
|
148
|
+
column_comment(table_name, column_name, comment) if comment
|
149
|
+
result
|
150
|
+
end
|
151
|
+
|
152
|
+
def change_column_with_schema_comments(table_name, column_name, type, options = {})
|
153
|
+
comment = options.delete(:comment)
|
154
|
+
@ignore_drop_table = true
|
155
|
+
result = change_column_without_schema_comments(table_name, column_name, type, options)
|
156
|
+
column_comment(table_name, column_name, comment) if comment
|
157
|
+
result
|
158
|
+
ensure
|
159
|
+
@ignore_drop_table = false
|
160
|
+
end
|
161
|
+
|
162
|
+
def rename_column_with_schema_comments(table_name, column_name, new_column_name)
|
163
|
+
result = rename_column_without_schema_comments(table_name, column_name, new_column_name)
|
164
|
+
comment = update_schema_comments_column_name(table_name, column_name, new_column_name)
|
165
|
+
result
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SchemaComments
|
2
|
+
module Migration
|
3
|
+
def self.included(mod)
|
4
|
+
mod.extend(ClassMethods)
|
5
|
+
mod.instance_eval do
|
6
|
+
alias :migrate_without_schema_comments :migrate
|
7
|
+
alias :migrate :migrate_with_schema_comments
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def migrate_with_schema_comments(*args, &block)
|
13
|
+
SchemaComments::SchemaComment.yaml_access do
|
14
|
+
migrate_without_schema_comments(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SchemaComments
|
2
|
+
module Migrator
|
3
|
+
def self.included(mod)
|
4
|
+
mod.extend(ClassMethods)
|
5
|
+
mod.instance_eval do
|
6
|
+
alias :migrate_without_schema_comments :migrate
|
7
|
+
alias :migrate :migrate_with_schema_comments
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def migrate_with_schema_comments(*args, &block)
|
13
|
+
SchemaComments::SchemaComment.yaml_access do
|
14
|
+
migrate_without_schema_comments(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SchemaComments
|
2
|
+
module Schema
|
3
|
+
def self.included(mod)
|
4
|
+
mod.extend(ClassMethods)
|
5
|
+
mod.instance_eval do
|
6
|
+
alias :define_without_schema_comments :define
|
7
|
+
alias :define :define_with_schema_comments
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def define_with_schema_comments(*args, &block)
|
13
|
+
SchemaComments::SchemaComment.yaml_access do
|
14
|
+
define_without_schema_comments(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'yaml/store'
|
3
|
+
|
4
|
+
module SchemaComments
|
5
|
+
# 現在はActiveRecord::Baseを継承していますが、将来移行が完全に終了した
|
6
|
+
# 時点で、ActiveRecord::Baseの継承をやめます。
|
7
|
+
#
|
8
|
+
# それまではDBからのロードは可能ですが、YAMLにのみ保存します。
|
9
|
+
class SchemaComment < ActiveRecord::Base
|
10
|
+
set_table_name('schema_comments')
|
11
|
+
|
12
|
+
TABLE_KEY = 'table_comments'
|
13
|
+
COLUMN_KEY = 'column_comments'
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def table_comment(table_name)
|
17
|
+
if yaml_exist?
|
18
|
+
@table_names ||= yaml_access{|db| db[TABLE_KEY]}.dup
|
19
|
+
return @table_names[table_name.to_s]
|
20
|
+
end
|
21
|
+
return nil unless table_exists?
|
22
|
+
connection.select_value(sanitize_conditions("select descriptions from schema_comments where table_name = '%s' and column_name is null" % table_name))
|
23
|
+
end
|
24
|
+
|
25
|
+
def column_comment(table_name, column_name)
|
26
|
+
if yaml_exist?
|
27
|
+
@column_names ||= yaml_access{|db| db[COLUMN_KEY] }.dup
|
28
|
+
column_hash = @column_names[table_name.to_s] || {}
|
29
|
+
return column_hash[column_name.to_s]
|
30
|
+
end
|
31
|
+
return nil unless table_exists?
|
32
|
+
connection.select_value(sanitize_conditions("select descriptions from schema_comments where table_name = '%s' and column_name = '%s'" % [table_name, column_name]))
|
33
|
+
end
|
34
|
+
|
35
|
+
def column_comments(table_name)
|
36
|
+
if yaml_exist?
|
37
|
+
result = nil
|
38
|
+
@column_names ||= yaml_access{|db| db[COLUMN_KEY] }.dup
|
39
|
+
result = @column_names[table_name.to_s]
|
40
|
+
return result || {}
|
41
|
+
end
|
42
|
+
return {} unless table_exists?
|
43
|
+
hash_array = connection.select_all(sanitize_conditions("select column_name, descriptions from schema_comments where table_name = '%s' and column_name is not null" % table_name))
|
44
|
+
hash_array.inject({}){|dest, r| dest[r['column_name']] = r['descriptions']; dest}
|
45
|
+
end
|
46
|
+
|
47
|
+
def save_table_comment(table_name, comment)
|
48
|
+
yaml_access do |db|
|
49
|
+
db[TABLE_KEY][table_name.to_s] = comment
|
50
|
+
end
|
51
|
+
@table_names = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def save_column_comment(table_name, column_name, comment)
|
55
|
+
yaml_access do |db|
|
56
|
+
db[COLUMN_KEY][table_name.to_s] ||= {}
|
57
|
+
db[COLUMN_KEY][table_name.to_s][column_name.to_s] = comment
|
58
|
+
end
|
59
|
+
@column_names = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def destroy_of(table_name, column_name)
|
63
|
+
yaml_access do |db|
|
64
|
+
column_hash = db[COLUMN_KEY][table_name.to_s]
|
65
|
+
column_hash.delete(column_name) if column_hash
|
66
|
+
end
|
67
|
+
@column_names = nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def update_table_name(table_name, new_name)
|
71
|
+
if yaml_exist?
|
72
|
+
yaml_access do |db|
|
73
|
+
db[TABLE_KEY][new_name.to_s] = db[TABLE_KEY].delete(table_name.to_s)
|
74
|
+
db[COLUMN_KEY][new_name.to_s] = db[COLUMN_KEY].delete(table_name.to_s)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@table_names = nil
|
78
|
+
@column_names = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def update_column_name(table_name, column_name, new_name)
|
82
|
+
if yaml_exist?
|
83
|
+
yaml_access do |db|
|
84
|
+
table_cols = db[COLUMN_KEY][table_name.to_s]
|
85
|
+
if table_cols
|
86
|
+
table_cols[new_name.to_s] = table_cols.delete(column_name.to_s)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
@table_names = nil
|
91
|
+
@column_names = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def yaml_exist?
|
95
|
+
File.exist?(SchemaComments.yaml_path)
|
96
|
+
end
|
97
|
+
|
98
|
+
def yaml_access(&block)
|
99
|
+
if @yaml_transaction
|
100
|
+
yield(@yaml_transaction) if block_given?
|
101
|
+
else
|
102
|
+
db = SortedStore.new(SchemaComments.yaml_path)
|
103
|
+
result = nil
|
104
|
+
# t = Time.now.to_f
|
105
|
+
db.transaction do
|
106
|
+
@yaml_transaction = db
|
107
|
+
begin
|
108
|
+
db[TABLE_KEY] ||= {}
|
109
|
+
db[COLUMN_KEY] ||= {}
|
110
|
+
result = yield(db) if block_given?
|
111
|
+
ensure
|
112
|
+
@yaml_transaction = nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
# puts("SchemaComment#yaml_access %fms from %s" % [Time.now.to_f - t, caller[0].gsub(/^.+:in /, '')])
|
116
|
+
result
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
class SortedStore < YAML::Store
|
123
|
+
module ColumnNamedHash
|
124
|
+
def each
|
125
|
+
@column_names.each do |column_name|
|
126
|
+
yield(column_name, self[column_name])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def dump(table)
|
132
|
+
root = nil
|
133
|
+
StringIO.open do |io|
|
134
|
+
YAML.dump(@table, io)
|
135
|
+
io.rewind
|
136
|
+
root = YAML.load(io)
|
137
|
+
end
|
138
|
+
|
139
|
+
table_comments = root['table_comments']
|
140
|
+
column_comments = root['column_comments']
|
141
|
+
# 大元は
|
142
|
+
# table_comments:
|
143
|
+
# ...
|
144
|
+
# column_comments:
|
145
|
+
# ...
|
146
|
+
# その他
|
147
|
+
# ...
|
148
|
+
# の順番です。
|
149
|
+
root.instance_eval do
|
150
|
+
def each
|
151
|
+
yield('table_comments', self['table_comments'])
|
152
|
+
yield('column_comments', self['column_comments'])
|
153
|
+
(self.keys - ['table_comments', 'column_comments']).each do |key|
|
154
|
+
yield(key, self[key])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
# table_comments はテーブル名のアルファベット順
|
159
|
+
table_names = ActiveRecord::Base.connection.tables.sort - ['schema_migrations']
|
160
|
+
table_comments.instance_variable_set(:@table_names, table_names)
|
161
|
+
table_comments.instance_eval do
|
162
|
+
def each
|
163
|
+
@table_names.each do |key|
|
164
|
+
yield(key, self[key])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
# column_comments もテーブル名のアルファベット順
|
169
|
+
column_comments.instance_variable_set(:@table_names, table_names)
|
170
|
+
column_comments.instance_eval do
|
171
|
+
def each
|
172
|
+
@table_names.each do |key|
|
173
|
+
yield(key, self[key])
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
# column_comments の各値はテーブルのカラム順
|
178
|
+
column_comments.each do |table_name, column_hash|
|
179
|
+
column_names = nil
|
180
|
+
begin
|
181
|
+
columns = ActiveRecord::Base.connection.columns_without_schema_comments(table_name, "#{table_name.classify} Columns")
|
182
|
+
column_names = columns.map(&:name)
|
183
|
+
rescue ActiveRecord::ActiveRecordError
|
184
|
+
column_names = column_hash.keys.sort
|
185
|
+
end
|
186
|
+
column_names.delete('id')
|
187
|
+
column_hash.instance_variable_set(:@column_names, column_names)
|
188
|
+
column_hash.extend(ColumnNamedHash)
|
189
|
+
end
|
190
|
+
root.to_yaml(@opt)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|