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,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
|