schemard 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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