schemard 0.2.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.
@@ -0,0 +1,37 @@
1
+ en:
2
+ views:
3
+ edit_checkbox_label: "Edit table position"
4
+ table_summary_title: "Table Information"
5
+ table_name: "Table Name"
6
+ parent_tables: "Parent Tables"
7
+ child_tables: "Child Tables"
8
+ description: "Description"
9
+ column_definition: "Column Information"
10
+ column_name: "Column Name"
11
+ column_type: "Type"
12
+ column_precision: "precision"
13
+ column_default: "Default"
14
+ index_definition: "Index Information"
15
+ index_unique: "Unique"
16
+ index_target_columns: "Columns"
17
+ index_name: "Index Name"
18
+
19
+ ja:
20
+ views:
21
+ edit_checkbox_label: "テーブルの位置を編集"
22
+ table_summary_title: "テーブル基本情報"
23
+ table_name: "テーブル名"
24
+ table_name_en: "テーブル英名"
25
+ parent_tables: "親テーブル"
26
+ child_tables: "子テーブル"
27
+ description: "説明"
28
+ column_definition: "カラム定義"
29
+ column_name: "列名"
30
+ column_name_en: "列名(英)"
31
+ column_type: "型"
32
+ column_precision: "精度"
33
+ column_default: "デフォルト"
34
+ index_definition: "インデックス定義"
35
+ index_unique: "ユニーク"
36
+ index_target_columns: "対象カラム"
37
+ index_name: "インデックス名"
@@ -0,0 +1,160 @@
1
+ require 'optparse'
2
+ require 'yaml'
3
+ require_relative "schema"
4
+ require_relative "schema_parser"
5
+ require_relative "metadata"
6
+ require_relative "utils/localizer"
7
+ require_relative "utils/struct_assigner"
8
+
9
+ module SchemaRD
10
+ class Controller
11
+ attr_reader :config
12
+
13
+ def initialize(config)
14
+ @config = config;
15
+ end
16
+
17
+ def index(req, res)
18
+ locale = localizer(req)
19
+ schema = SchemaRD::SchemaParser.new(config.input_file).parse(with_comment: config.parse_db_comment?)
20
+ SchemaRD::Metadata.load(config: self.config, lang: locale.lang, schema: schema)
21
+ send(req, res, render("index.html.erb", binding))
22
+ end
23
+
24
+ def show(req, res)
25
+ locale = localizer(req)
26
+ match = req.path.match(/\/tables\/(\w+)/)
27
+ unless match
28
+ res.status = 404
29
+ else
30
+ schema = SchemaRD::SchemaParser.new(config.input_file).parse(with_comment: config.parse_db_comment?)
31
+ SchemaRD::Metadata.load(config: self.config, lang: locale.lang, schema: schema)
32
+ table_name = match[1]
33
+ send(req, res, render("show.html.erb", binding))
34
+ end
35
+ end
36
+
37
+ def update(req, res)
38
+ match = req.path.match(/\/tables\/(\w+)/)
39
+ unless match
40
+ res.status = 404
41
+ else
42
+ if req.query['layout']
43
+ pos = req.query['layout'].split(",")
44
+ SchemaRD::Metadata::Writer.new(config.output_file).save(match[1], { "left" => pos[0], "top" => pos[1] })
45
+ end
46
+ send(req, res, "OK")
47
+ end
48
+ end
49
+
50
+ def static_file(req, res)
51
+ send(req, res, File.new(CONTENTS_DIR + req.path).read)
52
+ end
53
+
54
+ TEMPLATES_DIR = "#{File.dirname(File.expand_path(__FILE__))}/../templates/"
55
+ CONTENTS_DIR = "#{File.dirname(File.expand_path(__FILE__))}/../contents/"
56
+
57
+ private
58
+
59
+ def localizer(req)
60
+ SchemaRD::Utils::MessageLocalizer.new(req.accept_language[0] || "en")
61
+ end
62
+
63
+ def send(req, res, body = nil)
64
+ res.status = 200
65
+ res.content_type = case req.path
66
+ when /.*\.js\Z/
67
+ "text/javascript"
68
+ when /.*\.css\Z/
69
+ "text/css"
70
+ when /.*\.ico\Z/
71
+ "image/x-icon"
72
+ else
73
+ "text/html"
74
+ end
75
+ res.body = body
76
+ end
77
+
78
+ def render(filename, current_binding)
79
+ ERB.new(File.new(TEMPLATES_DIR + filename).read, nil, '-').result(current_binding)
80
+ end
81
+ end
82
+
83
+ CONFIG_FILE = ".schamard.config"
84
+ DEFAULT_CONFIG = {
85
+ input_file: "db/schema.rb",
86
+ output_file: "schema.metadata",
87
+ metadata_files: [],
88
+ rdoc_enabled: false,
89
+ parse_db_comment_as: "ignore",
90
+ log_output: STDOUT,
91
+ webserver_host: "127.0.0.1",
92
+ webserver_port: "10080"
93
+ }
94
+
95
+ class Configuration < Struct.new(*DEFAULT_CONFIG.keys)
96
+ include SchemaRD::Utils::StructAssigner
97
+ attr_reader :errors
98
+ def initialize(argv = nil)
99
+ hash = {}.merge(DEFAULT_CONFIG)
100
+ hash.merge(YAML.load_file(CONFIG_FILE)) if File.readable?(CONFIG_FILE)
101
+
102
+ unless argv.nil?
103
+ opt = OptionParser.new
104
+ opt.on('-i VAL', '--input-file=VAL') {|v| hash[:input_file] = v }
105
+ opt.on('-o VAL', '--output-file=VAL') {|v| hash[:output_file] = v }
106
+ opt.on('-f VAL', '-m VAL', '--metadata-file=VAL') {|v| hash[:metadata_files] << v }
107
+ opt.on('--rdoc', '--rdoc-enabled') { hash[:rdoc_enabled] = true }
108
+ opt.on('--parse-db-comment-as=VAL') {|v| hash[:parse_db_comment_as] = v }
109
+ opt.on('-s', '--silent', '--no-log-output') {|v| hash[:log_output] = File.open(File::NULL, 'w') }
110
+ opt.on('-h VAL', '--host=VAL') {|v| hash[:webserver_host] = v }
111
+ opt.on('-p VAL', '--port=VAL') {|v| hash[:webserver_port] = v }
112
+ opt.on('-l VAL', '--log-output=VAL') {|v| hash[:log_output] = self.class.str_to_io(v) }
113
+ opt.parse(argv)
114
+ end
115
+ self.assign(hash)
116
+ end
117
+
118
+ def parse_db_comment?
119
+ self.parse_db_comment_as != "ignore"
120
+ end
121
+
122
+ def valid?
123
+ @errors = []
124
+ unless File.readable?(self.input_file)
125
+ self.errors << "InputFile: \"#{self.input_file}\" is not readable!"
126
+ end
127
+ unless (File.writable?(self.output_file) || File.writable?(File.dirname(self.output_file)))
128
+ self.errors << "OutputFile: \"#{self.output_file}\" is not writable!"
129
+ end
130
+ self.metadata_files.each do |metadata_file|
131
+ unless File.readable?(metadata_file)
132
+ self.errors << "MetadataFile: \"#{metadata_file}\" is not readable!"
133
+ end
134
+ end
135
+ unless %w(ignore name localized_name description custom).include?(self.parse_db_comment_as)
136
+ self.errors << "ParseDBCommentAs: \"#{self.parse_db_comment_as}\" is not allowed!"
137
+ end
138
+ if self.log_output.is_a?(String)
139
+ self.errors << "LogFile: \"#{self.log_output}\" is not writable!"
140
+ end
141
+ unless self.webserver_port =~ /^[0-9]+$/
142
+ self.errors << "WebServerPort: \"#{self.webserver_port}\" is invalid!"
143
+ end
144
+ self.errors.empty?
145
+ end
146
+
147
+ private
148
+
149
+ def self.str_to_io(str)
150
+ case str
151
+ when "stdout", "STDOUT"
152
+ STDOUT
153
+ when "stderr", "STDERR"
154
+ STDERR
155
+ else
156
+ File.open(str, 'w') rescue str
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,134 @@
1
+ require 'yaml'
2
+ require_relative 'utils/localizer'
3
+ require_relative 'rdoc_parser'
4
+
5
+ module SchemaRD
6
+ module Metadata
7
+ def self.load(config:, lang:, schema:)
8
+ metadata = Parser.new(config.output_file, *config.metadata_files).parse()
9
+ localizer = SchemaRD::Utils::SchemaLocalizer.new(lang, metadata)
10
+ # localized_name を設定
11
+ schema.tables.each do |table|
12
+ table.localized_name = localizer.table_name(table.name)
13
+ table.columns.each do |column|
14
+ column.localized_name = localizer.column_name(table.name, column.name)
15
+ end
16
+ end
17
+ # set position, and relations
18
+ (metadata["tables"] || {}).each do |table_name, hash|
19
+ # skip when table name exists in metadata only.
20
+ next unless schema.table(table_name)
21
+ if hash["position_left"] && hash["position_top"]
22
+ schema.table(table_name).position =
23
+ { "left" => hash["position_left"], "top" => hash["position_top"] }
24
+ end
25
+ self.add_relations(table_name, "belongs_to", hash["belongs_to"], schema)
26
+ self.add_relations(table_name, "has_many", hash["has_many"], schema)
27
+ self.add_relations(table_name, "has_one", hash["has_one"], schema)
28
+ end
29
+ # db_comment にメタ情報が含まれる場合に設定
30
+ if config.parse_db_comment?
31
+ schema.tables.each do |table|
32
+ if table.parsed_db_comment && table.parsed_db_comment.strip != ""
33
+ case config.parse_db_comment_as
34
+ when 'name', 'localized_name'
35
+ table.localized_name = table.parsed_db_comment.strip
36
+ when 'description'
37
+ table.description = table.parsed_db_comment.strip
38
+ when 'custom'
39
+ config.db_comment_parser.call(table: table)
40
+ end
41
+ end
42
+ table.columns
43
+ .select{|c| c.parsed_db_comment && c.parsed_db_comment.strip != ""}.each do |column|
44
+ case config.parse_db_comment_as
45
+ when 'name', 'localized_name'
46
+ column.localized_name = column.parsed_db_comment.strip
47
+ when 'description'
48
+ column.description = column.parsed_db_comment.strip
49
+ when 'custom'
50
+ config.db_comment_parser.call(column: column)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ # RDocコメントとしてメタ情報が含まれる場合に設定
56
+ if config.rdoc_enabled
57
+ rdoc = SchemaRD::RDocParser.new(config.input_file)
58
+ schema.tables.select{|t| rdoc.table_comment(t.name) }.each do |table|
59
+ parser = DefaultTableCommentParser.new(rdoc.table_comment(table.name))
60
+ table.localized_name = parser.localized_name if parser.has_localized_name?
61
+ table.description = parser.description if parser.has_description?
62
+
63
+ %i(belongs_to has_many has_one).each do |rel_type|
64
+ if parser.has_relation_of?(rel_type)
65
+ self.add_relations(table.name, rel_type.to_s, parser.relation_of(rel_type), schema)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ # output_file がなければ作成
71
+ Writer.new(config.output_file).save_all(schema.tables) unless File.exist?(config.output_file)
72
+ schema
73
+ end
74
+
75
+ def self.add_relations(table_name, type, relation_table_names, schema)
76
+ return unless relation_table_names
77
+ relation_table_names = relation_table_names.split(",") if relation_table_names.is_a?(String)
78
+
79
+ relation_table_names.map{|rel_table_name| schema.table(rel_table_name) }.compact.each do |rel_table|
80
+ parent_table = type == "belongs_to" ? rel_table : schema.table(table_name)
81
+ child_table = type == "belongs_to" ? schema.table(table_name) : rel_table
82
+
83
+ if parent_table.relation_to(child_table.name).nil?
84
+ schema.add_relation(TableRelation.new(parent_table: parent_table, child_table: child_table))
85
+ end
86
+ parent_table.relation_to(child_table.name).child_cardinality = "1" if type == "has_one"
87
+ end
88
+ end
89
+
90
+ # Writer for metadata yaml
91
+ class Writer
92
+ def initialize(output_file)
93
+ @output_file = output_file
94
+ end
95
+ def save_all(tables)
96
+ hash = tables.each_with_object({}) do |t, hash|
97
+ hash[t.name] = { "position_top" => t.position["top"], "position_left" => t.position["left"] }
98
+ end
99
+ File.write(@output_file, YAML.dump({ "tables" => hash }))
100
+ end
101
+ def save(table_name, position)
102
+ hash = YAML.load_file(@output_file) || {}
103
+ hash["tables"] = {} unless hash.has_key?("tables")
104
+ hash["tables"] = {} unless hash["tables"].is_a?(Hash)
105
+ hash["tables"][table_name] = {} unless hash["tables"][table_name]
106
+ hash["tables"][table_name] = {} unless hash["tables"][table_name].is_a?(Hash)
107
+ hash["tables"][table_name]["position_top"] = position["top"].to_s
108
+ hash["tables"][table_name]["position_left"] = position["left"].to_s
109
+ File.write(@output_file, YAML.dump(hash))
110
+ end
111
+ end
112
+
113
+ # parser for metadata yaml
114
+ class Parser
115
+ def initialize(output_file, *metadata_files)
116
+ @parsed = {}
117
+ metadata_files.select{|metadata_file| File.exist?(metadata_file) }.each do |metadata_file|
118
+ self.class.deep_merge(@parsed, YAML.load_file(metadata_file))
119
+ end
120
+ self.class.deep_merge(@parsed, YAML.load_file(output_file)) if File.exist?(output_file)
121
+ end
122
+ # get hash of metadata
123
+ def parse
124
+ @parsed
125
+ end
126
+ def self.deep_merge(source, other)
127
+ other.each do |k,v|
128
+ next self.deep_merge(source[k], other[k]) if other[k].is_a?(Hash) && source[k].is_a?(Hash)
129
+ source[k] = other[k]
130
+ end
131
+ end
132
+ end # end of Parser
133
+ end # end of Metadata
134
+ end
@@ -0,0 +1,66 @@
1
+ require 'rdoc'
2
+ module SchemaRD
3
+ class RDocParser
4
+ def initialize(filename)
5
+ parse(filename)
6
+ end
7
+
8
+ def table_comment(name)
9
+ method_obj = @clazz.find_method_named(name)
10
+ method_obj ? method_obj.comment.text : ""
11
+ end
12
+
13
+ private
14
+
15
+ def parse(filename)
16
+ file_content = File.read(filename)
17
+ content = "module Schemafile\n#{file_content}\nend"
18
+
19
+ rdoc = RDoc::RDoc.new
20
+ store = RDoc::Store.new
21
+ options = rdoc.load_options
22
+ stats = RDoc::Stats.new(store, 1, options.verbosity)
23
+ top_level = store.add_file(filename)
24
+ RDoc::Parser::Ruby.new(top_level, filename, content, options, stats).scan
25
+ @clazz = top_level.find_module_named("Schemafile")
26
+ end
27
+ end
28
+
29
+ class DefaultTableCommentParser
30
+ def initialize(comment_text)
31
+ @hash = { relations: {} }
32
+ @hash[:description] = comment_text.split("\n").map(&:strip).map{|line|
33
+ if line =~ /^name\:\:/ || line =~ /^localized_name\:\:/
34
+ @hash[:localized_name] = line.match(/^[^:]*name\:\:(.+)$/)[1].strip
35
+ next
36
+ end
37
+ %w(belongs_to has_many has_one).each do |rel_type|
38
+ if line =~ /^#{rel_type}\:\:/
39
+ tables = line.match(/^#{rel_type}\:\:(.+)$/)[1].split(",").map(&:strip).select{|s| s != "" }
40
+ @hash[:relations][rel_type.to_sym] = tables unless tables.empty?
41
+ line = nil # skip this line (by compact)
42
+ end
43
+ end
44
+ line
45
+ }.compact.join("\n")
46
+ end
47
+ def has_localized_name?
48
+ @hash[:localized_name] && @hash[:localized_name] != ""
49
+ end
50
+ def localized_name
51
+ @hash[:localized_name]
52
+ end
53
+ def has_description?
54
+ !!@hash[:description]
55
+ end
56
+ def description
57
+ @hash[:description]
58
+ end
59
+ def has_relation_of?(rel_type)
60
+ !!@hash[:relations][rel_type]
61
+ end
62
+ def relation_of(rel_type)
63
+ @hash[:relations][rel_type]
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,54 @@
1
+ require 'yaml'
2
+ require 'pathname'
3
+ require 'optparse'
4
+
5
+ module SchemaRD
6
+ class RelationGenerator
7
+ def initialize(argv)
8
+ rails_root = Pathname.pwd
9
+ opt = OptionParser.new
10
+ opt.on('-d VAL', 'Rails.root.directory') {|v| rails_root = Pathname.new(v).expand_path }
11
+ opt.parse(argv)
12
+
13
+ require_path = rails_root + "config/environment.rb"
14
+ unless require_path.exist?
15
+ puts "<#{rails_root}> is not Rails.root Directory, Abort!"
16
+ puts "Usage: schemard -d <Rails.root.dir>"
17
+ else
18
+ Dir.chdir(rails_root) do
19
+ require require_path.to_s
20
+ end
21
+ end
22
+ end
23
+ def ready?
24
+ defined?(Rails)
25
+ end
26
+ def run
27
+ Dir.glob(Rails.root + "app/models/**/*")
28
+ .reject{|path| Dir.exist?(path) }.each{|filepath| require filepath }
29
+
30
+ hash = ObjectSpace.each_object(Class)
31
+ .select{|o| o.ancestors.include?(ActiveRecord::Base) && o != ActiveRecord::Base }
32
+ .select{|o| o.table_name }
33
+ .each_with_object({}) do |model, hash|
34
+ hash[model.table_name] = {}
35
+
36
+ relation_selector = ->(klass){ model._reflections.values.select{|r| r.is_a?(klass) } }
37
+ has_one_rels = relation_selector.call(ActiveRecord::Reflection::HasOneReflection)
38
+ has_many_rels = relation_selector.call(ActiveRecord::Reflection::HasManyReflection)
39
+ belongs_to_rels = relation_selector.call(ActiveRecord::Reflection::BelongsToReflection)
40
+
41
+ if has_one_rels.present?
42
+ hash[model.table_name]["has_one"] = has_one_rels.map{|r| r.klass.table_name }
43
+ end
44
+ if has_many_rels.present?
45
+ hash[model.table_name]["has_many"] = has_many_rels.map{|r| r.klass.table_name }
46
+ end
47
+ if belongs_to_rels.present?
48
+ hash[model.table_name]["belongs_to"] = belongs_to_rels.map{|r| r.klass.table_name }
49
+ end
50
+ end
51
+ puts YAML.dump({ "tables" => hash })
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,79 @@
1
+ require_relative 'utils/struct_assigner'
2
+
3
+ module SchemaRD
4
+ class Schema
5
+ attr_reader :relations
6
+ def initialize
7
+ @tables = {}
8
+ @relations = []
9
+ end
10
+ def tables
11
+ @tables.values
12
+ end
13
+ def table(name)
14
+ @tables[name.to_s]
15
+ end
16
+ def add_table(name, table_object)
17
+ @tables[name.to_s] = table_object
18
+ table_object.set_schema(self)
19
+ end
20
+ def add_relation(relation)
21
+ @relations << relation
22
+ end
23
+ end
24
+
25
+ class Table < Struct.new(*%i(columns indexes name localized_name description position parsed_db_comment))
26
+ include SchemaRD::Utils::StructAssigner
27
+ def initialize(hash = nil)
28
+ self.columns = []
29
+ self.indexes = []
30
+ self.position = { "left" => 0, "top" => 0 }
31
+ self.assign(hash)
32
+ end
33
+ def set_schema(schema)
34
+ @schema = schema
35
+ end
36
+ def relations_as_parent
37
+ @schema.relations.select{|r| r.parent_table == self }
38
+ end
39
+ def relations_as_child
40
+ @schema.relations.select{|r| r.child_table == self }
41
+ end
42
+ def relation_to(table_name)
43
+ self.relations_as_parent.find{|r| r.child_table.name == table_name }
44
+ end
45
+ def display_name
46
+ self.localized_name || self.name
47
+ end
48
+ def default_position?
49
+ self.position["left"] == 0 && self.position["top"] == 0
50
+ end
51
+ end
52
+
53
+ class TableRelation < Struct.new(*%i(parent_table child_table parent_cardinality child_cardinality))
54
+ include SchemaRD::Utils::StructAssigner
55
+ def initialize(hash = nil)
56
+ self.parent_cardinality = "1"
57
+ self.child_cardinality = "N"
58
+ self.assign(hash)
59
+ end
60
+ end
61
+
62
+ class TableColumn < Struct.new(
63
+ *%i(name localized_name type null default limit precision scale description parsed_db_comment))
64
+ include SchemaRD::Utils::StructAssigner
65
+ def initialize(hash = nil)
66
+ self.assign(hash)
67
+ end
68
+ def display_name
69
+ self.localized_name || self.name
70
+ end
71
+ end
72
+ class TableIndex < Struct.new(*%i(name columns unique))
73
+ include SchemaRD::Utils::StructAssigner
74
+ def initialize(hash = nil)
75
+ self.columns = []
76
+ self.assign(hash)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,92 @@
1
+ require_relative "schema"
2
+
3
+ module SchemaRD
4
+ module MigrationContext
5
+ class Loader
6
+ class TableDefinition
7
+ [
8
+ :bigint,
9
+ :binary,
10
+ :boolean,
11
+ :date,
12
+ :datetime,
13
+ :decimal,
14
+ :float,
15
+ :integer,
16
+ :string,
17
+ :text,
18
+ :time,
19
+ :timestamp,
20
+ :virtual,
21
+ ].each do |column_type|
22
+ module_eval <<-CODE, __FILE__, __LINE__ + 1
23
+ def #{column_type}(*args, **options)
24
+ args.each { |name| column(name, :#{column_type}, options) }
25
+ end
26
+ CODE
27
+ end
28
+ alias_method :numeric, :decimal
29
+ def initialize(table, with_comment:)
30
+ @table = table
31
+ @parse_db_comment = with_comment
32
+ end
33
+ def method_missing(name, *args)
34
+ self.column(args[0], "unknown", args[1])
35
+ end
36
+ def column(name, type, options = {})
37
+ if options[:comment] && @parse_db_comment
38
+ options[:parsed_db_comment] = options.delete(:comment)
39
+ end
40
+ @table.columns << SchemaRD::TableColumn.new(options.merge({ name: name, type: type }))
41
+ end
42
+ def timestamps
43
+ column("created_at", :timestamp, null: false)
44
+ column("updated_at", :timestamp, null: false)
45
+ end
46
+ def index(column_name, options = {})
47
+ column_name = [ column_name ] unless column_name.is_a?(Array)
48
+ @table.indexes << SchemaRD::TableIndex.new(options.merge({ columns: column_name }))
49
+ end
50
+ end
51
+ def initialize(schema, with_comment:)
52
+ @schema = schema
53
+ @parse_db_comment = with_comment
54
+ end
55
+ def create_table(table_name, options = {})
56
+ if options[:comment] && @parse_db_comment
57
+ options[:parsed_db_comment] = options.delete(:comment)
58
+ end
59
+ table = SchemaRD::Table.new(options.merge(name: table_name))
60
+ @schema.add_table(table_name, table)
61
+ yield TableDefinition.new(table, with_comment: @parse_db_comment)
62
+ end
63
+ def add_index(table_name, column_name, options = {})
64
+ column_name = [ column_name ] unless column_name.is_a?(Array)
65
+ index = SchemaRD::TableIndex.new(options.merge({ columns: column_name }))
66
+ @schema.table(table_name).indexes << index
67
+ end
68
+ def enable_extension(*args); end
69
+
70
+ module ActiveRecord
71
+ class Schema
72
+ def self.define(*args)
73
+ yield
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ class SchemaParser
81
+ def initialize(filename)
82
+ @filename = filename
83
+ end
84
+ def parse(with_comment: false)
85
+ Schema.new.tap do |schema|
86
+ File.open(@filename) do |file|
87
+ MigrationContext::Loader.new(schema, with_comment: with_comment).instance_eval(file.read, @filename)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,43 @@
1
+ require 'yaml'
2
+ require_relative 'singularizer'
3
+
4
+ module SchemaRD::Utils
5
+ class Localizer
6
+ attr_reader :lang
7
+ def initialize(lang)
8
+ @lang = self.dictionary && self.dictionary.has_key?(lang) ? lang : "en"
9
+ end
10
+ def translate(key)
11
+ key.split(".").inject(self.dictionary[lang]) do |dict, k|
12
+ break if dict.nil? || !dict.is_a?(Hash)
13
+ dict[k]
14
+ end
15
+ end
16
+ alias_method :t, :translate
17
+ end
18
+
19
+ class MessageLocalizer < Localizer
20
+ MESSAGES_FILE = "#{File.dirname(File.expand_path(__FILE__))}/../../locales/messages.yml"
21
+
22
+ def initialize(lang)
23
+ super(lang)
24
+ end
25
+ def dictionary
26
+ YAML.load_file(MESSAGES_FILE)
27
+ end
28
+ end
29
+
30
+ class SchemaLocalizer < Localizer
31
+ attr_reader :dictionary
32
+ def initialize(lang, hash)
33
+ super(lang)
34
+ @dictionary = hash
35
+ end
36
+ def table_name(name)
37
+ self.t("activerecord.models.#{name.singularize}")
38
+ end
39
+ def column_name(table_name, column_name)
40
+ self.t("activerecord.attributes.#{table_name.singularize}.#{column_name}")
41
+ end
42
+ end
43
+ end