amatsuda-i18n_generators 0.3.1 → 0.4.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.
@@ -63,7 +63,10 @@ class I18nGenerator < Rails::Generator::NamedBase
63
63
  private
64
64
  def defined_in_rails_i18n_repository?
65
65
  begin
66
- OpenURI.open_uri("http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale/#{locale_name}.yml").status == %w[200 OK]
66
+ uri = "http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale/#{locale_name}.yml"
67
+ OpenURI.open_uri(uri) do |res|
68
+ (res.base_uri.to_s == uri) && (res.status == %w[200 OK])
69
+ end
67
70
  rescue
68
71
  false
69
72
  end
@@ -0,0 +1,185 @@
1
+ require 'yaml'
2
+
3
+ module I18nLocaleGeneratorModule
4
+ class Node
5
+ attr_reader :document, :indent_level
6
+ attr_accessor :line
7
+
8
+ def initialize(parent, line_index, text)
9
+ @document, @line, @text = parent.document, line_index, text.to_s
10
+ @text =~ /(^\s*)/
11
+ @indent_level = $1.nil? ? 0 : $1.size
12
+ @yaml = YAML.load(@text.to_s + ' ')
13
+ end
14
+
15
+ def parent
16
+ @parent ||= document.parent_of self
17
+ end
18
+
19
+ def children
20
+ @children ||= document.children_of(self)
21
+ end
22
+ alias :nodes :children
23
+
24
+ def [](node_name)
25
+ if node = nodes.detect {|n| n.key.to_s == node_name.to_s}
26
+ node
27
+ else
28
+ nodes.add "#{' ' * (@indent_level + 2)}#{node_name}: "
29
+ nodes.last
30
+ end
31
+ end
32
+
33
+ def key
34
+ @yaml.is_a?(Hash) ? @yaml.keys.first : nil
35
+ end
36
+
37
+ def value
38
+ @yaml.is_a?(Hash) ? @yaml.values.first : nil
39
+ end
40
+
41
+ def value=(val)
42
+ if @yaml[self.key] != val
43
+ @yaml[self.key] = val
44
+ @changed = true
45
+ end
46
+ end
47
+
48
+ def text
49
+ if @changed
50
+ v = if self.value.is_a?(Array)
51
+ "[#{self.value * ', '}]"
52
+ else
53
+ %Q["#{self.value}"]
54
+ end
55
+ "#{' ' * self.indent_level}#{self.key}: #{v}"
56
+ else
57
+ @text
58
+ end
59
+ end
60
+ alias :to_s :text
61
+
62
+ def changed?
63
+ @changed
64
+ end
65
+
66
+ def is_blank_or_comment?
67
+ @text.sub(/#.*$/, '').gsub(/\s/, '').empty?
68
+ end
69
+
70
+ def path
71
+ @path ||= "#{self.parent.path}/#{self.key}"
72
+ end
73
+
74
+ def descendant_nodes(&block)
75
+ yield self if self.value
76
+ self.children.each {|child| child.descendant_nodes(&block)} if self.children
77
+ end
78
+
79
+ def <=>(other)
80
+ self.line <=> other.line
81
+ end
82
+ end
83
+
84
+ class YamlDocument < Node
85
+ attr_accessor :lines
86
+ alias :nodes :lines
87
+
88
+ def initialize(yml_path, locale_name)
89
+ @locale_name, @lines, @current_line, @indent_level = locale_name, Nodes.new(self), -1, -2
90
+ if File.exists? yml_path
91
+ File.open(yml_path) do |file|
92
+ file.each_with_index do |line_text, i|
93
+ n = Node.new(self, i, line_text.chomp)
94
+ @lines << ((((n.key == 'en-US') || (n.key == 'en')) && n.value.blank?) ? Node.new(self, i, "#{locale_name}:") : n)
95
+ end
96
+ @lines.delete_at(-1) if @lines[-1].text.blank?
97
+ end
98
+ end
99
+ end
100
+
101
+ def next
102
+ return false if @lines.size == 0
103
+ @current_line += 1
104
+ return false if @current_line >= @lines.size
105
+ @lines[@current_line].is_blank_or_comment? ? self.next : @lines[@current_line]
106
+ end
107
+
108
+ def prev
109
+ return false if @current_line == 0
110
+ @current_line -= 1
111
+ @lines[@current_line].is_blank_or_comment? ? self.prev : @lines[@current_line]
112
+ end
113
+
114
+ def parent_of(child)
115
+ @current_line = child.line
116
+ while n = self.prev
117
+ return n if n.indent_level == child.indent_level - 2
118
+ end
119
+ self
120
+ end
121
+
122
+ def children_of(parent)
123
+ nodes = Nodes.new(parent)
124
+ @current_line = parent.line
125
+ while n = self.next
126
+ if n.indent_level < parent.indent_level + 2
127
+ break
128
+ elsif n.indent_level == parent.indent_level + 2
129
+ nodes << n
130
+ end
131
+ end
132
+ nodes
133
+ end
134
+
135
+ def document
136
+ self
137
+ end
138
+
139
+ def path
140
+ ''
141
+ end
142
+
143
+ def line
144
+ @current_line
145
+ end
146
+
147
+ def to_s
148
+ @lines.inject('') do |ret, n|
149
+ ret << n.text + "\n"
150
+ end
151
+ end
152
+ end
153
+
154
+ class Nodes < Array
155
+ def initialize(parent)
156
+ super()
157
+ @parent = parent
158
+ end
159
+
160
+ def [](index)
161
+ if index.is_a?(String) || index.is_a?(Symbol)
162
+ return self.detect {|node| node.key == index} || add(index)
163
+ end
164
+ super
165
+ end
166
+
167
+ def last_leaf
168
+ c = @parent
169
+ loop do
170
+ return c if c.children.blank?
171
+ c = c.children.last
172
+ end
173
+ end
174
+
175
+ def add(node_name)
176
+ target_line = self.last_leaf.line + 1
177
+ @parent.document.nodes.each {|n| n.line += 1 if n.line >= target_line}
178
+ node = Node.new(@parent, target_line, node_name)
179
+ @parent.document.lines << node
180
+ @parent.document.lines.sort!
181
+ self << node unless @parent.is_a? YamlDocument
182
+ end
183
+ end
184
+ end
185
+
@@ -1,19 +1 @@
1
- <%= locale_name %>:
2
- activerecord:
3
- models:
4
- <% models.each do |model| -%>
5
- <%= "#{model.english_name}: #{model.translated_name}" %>
6
- <% end -%>
7
- attributes:
8
- <% models.each do |model| -%>
9
- <%= "#{model.english_name}:" %>
10
- <% model.content_columns.each do |col| -%>
11
- <% unless %w[created_at updated_at].include?(col.name) -%>
12
- <%= "#{col.name}: #{col.translated_name}" %>
13
- <% end -%>
14
- <% end -%>
15
- <% end -%>
16
- <% translations.each do |key, value| -%>
17
- <%= "#{key}: #{value}" %>
18
- <% end -%>
19
-
1
+ <%= translations %>
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rails_generator'
3
3
  require 'rails_generator/commands'
4
4
  require 'gettext'
5
- require File.join(File.dirname(__FILE__), 'lib/yaml')
5
+ require File.join(File.dirname(__FILE__), '../i18n/lib/yaml')
6
6
  require File.join(File.dirname(__FILE__), 'lib/cldr')
7
7
  include I18nLocaleGeneratorModule
8
8
 
@@ -95,8 +95,13 @@ module I18nGenerator::Generator
95
95
  return arr.join("\n")
96
96
  end
97
97
  end
98
- # hope you're all using Ruby >= 1.8.7...
99
- end_row = arr.respond_to?(:rindex) ? arr.rindex {|l| l =~ /^\s*end\s*/} : arr.size - 1
98
+ arr.each_with_index do |l, i|
99
+ if l =~ /Rails::Initializer\.run do \|config\|/
100
+ arr[i] = "Rails::Initializer.run do |config|\n config.i18n.default_locale = '#{locale_name}'"
101
+ return arr.join("\n")
102
+ end
103
+ end
104
+ end_row = RUBY_VERSION >= '1.8.7' ? arr.rindex {|l| l =~ /^\s*end\s*/} : arr.size - 1
100
105
  ((arr[0...end_row] << " config.i18n.default_locale = '#{locale_name}'") + arr[end_row..-1]).join("\n")
101
106
  end
102
107
 
@@ -104,7 +109,7 @@ module I18nGenerator::Generator
104
109
  original_yml = I18n.load_path.detect {|lp| lp =~ /\/lib\/#{filename_base}\/locale\/(en|en-US)\.yml$/}
105
110
  doc = YamlDocument.new(original_yml, locale_name)
106
111
  yield doc
107
- file('i18n:base.yml', "config/locale/#{filename_base}_#{locale_name}.yml") do |f|
112
+ file('i18n:base.yml', "config/locales/#{filename_base}_#{locale_name}.yml") do |f|
108
113
  doc.to_s
109
114
  end
110
115
  end
@@ -6,7 +6,10 @@ module I18nLocaleGeneratorModule
6
6
  class CldrDocument
7
7
  def initialize(locale_name)
8
8
  @locale_name = locale_name
9
- @summaries = [load_cldr_data(locale_name.tr('-', '_')), load_cldr_data(locale_name.sub(/-.*/, ''))]
9
+ @summaries = [load_cldr_data(locale_name.tr('-', '_'))]
10
+ if locale_name =~ /^[a-zA-Z]{2}[-_][a-zA-Z]{2}$/
11
+ @summaries << load_cldr_data(locale_name.to(1))
12
+ end
10
13
  end
11
14
 
12
15
  def lookup(path)
@@ -108,10 +111,11 @@ module I18nLocaleGeneratorModule
108
111
  def search(n1, n2, g)
109
112
  pattern = Regexp.new /<tr><td>\d*<\/td><td class='[ng]'>#{Regexp.quote(n1)}<\/td><td class='[ng]'>#{Regexp.quote(n2)}<\/td><td class='[ng]'>#{Regexp.quote(g)}<\/td>/
110
113
  extract_pattern = /<td class='v'>(?:<span.*?>)?(.*?)(?:<\/span>)?<\/td><td>/
111
- _, value = *extract_pattern.match(@summaries[0].grep(pattern).first)
112
- return value unless value.nil?
113
- _, value = *extract_pattern.match(@summaries[1].grep(pattern).first)
114
- value
114
+ @summaries.each do |summary|
115
+ _, value = *extract_pattern.match(summary.grep(pattern).first)
116
+ return value unless value.nil?
117
+ end
118
+ nil
115
119
  end
116
120
 
117
121
  def convert_date_pattern(val)
@@ -3,6 +3,7 @@ require 'rails_generator/commands'
3
3
  require File.join(File.dirname(__FILE__), 'lib/translator')
4
4
  require File.join(File.dirname(__FILE__), 'lib/recording_backend')
5
5
  require File.join(File.dirname(__FILE__), 'lib/erb_executer')
6
+ require File.join(File.dirname(__FILE__), '../i18n/lib/yaml')
6
7
  include I18nTranslationGeneratorModule
7
8
 
8
9
  module I18nGenerator::Generator
@@ -10,56 +11,41 @@ module I18nGenerator::Generator
10
11
  module Create
11
12
  def translation_yaml
12
13
  I18n.locale = locale_name
13
- threads = []
14
- now = Time.now
15
14
  models = model_filenames.map do |model_name|
16
15
  model = begin
17
16
  m = model_name.camelize.constantize
18
17
  next unless m.respond_to?(:content_columns)
18
+ m.class_eval %Q[def self.english_name; "#{model_name}"; end]
19
19
  m
20
20
  rescue
21
21
  next
22
22
  end
23
- threads << Thread.new do
24
- Thread.pass
25
- registered_t_name = I18n.t("activerecord.models.#{model_name}", :default => model_name, :locale => locale_name)
26
-
27
- model.class_eval <<-END
28
- def self.english_name
29
- "#{model_name}"
30
- end
31
-
32
- def self.translated_name
33
- "#{registered_t_name != model_name ? registered_t_name : self.translator.translate(model_name)}"
34
- end
35
- END
36
- model.content_columns.each do |col|
37
- next if %w[created_at updated_at].include? col.name
38
- registered_t_name = I18n.t("activerecord.attributes.#{model_name}.#{col.name}", :default => col.name, :locale => locale_name)
39
- col.class_eval <<-END
40
- def translated_name
41
- "#{registered_t_name != col.name ? registered_t_name : self.translator.translate(col.name)}"
42
- end
43
- END
44
- end
45
- end
46
- model
47
23
  end.compact
48
- threads.each {|t| t.join}
49
- logger.debug "took #{Time.now - now} secs to translate #{models.count} models."
24
+ translation_keys = []
25
+ translation_keys += models.map {|m| "activerecord.models.#{m.english_name}"}
26
+ models.each do |model|
27
+ translation_keys += model.content_columns.map {|c| "activerecord.attributes.#{model.english_name}.#{c.name}"}
28
+ end
29
+ logger.debug "#{models.size} models found."
30
+
50
31
  # pick all translated keywords from view files
32
+ original_backend = I18n.backend
51
33
  I18n.backend = RecordingBackend.new
52
34
 
53
35
  Dir["#{RAILS_ROOT}/app/views/**/*.erb"].each do |f|
54
36
  ErbExecuter.new.exec_erb f
55
37
  end
56
- keys_in_view = I18n.backend.keys
57
- keys_in_view -= models.map {|m| m.english_name.to_sym}
58
- keys_in_view -= models.inject([]) {|a, m| a + m.content_columns.map {|c| "#{m.english_name}.#{c.name}".to_sym}}
38
+ logger.debug "#{I18n.backend.keys.size} translation keys found in views."
39
+ (translation_keys += I18n.backend.keys).uniq!
40
+ I18n.backend = original_backend
41
+
42
+ # translate all keys and generate the YAML file
59
43
  now = Time.now
60
- translated_hash = translate_all keys_in_view
61
- logger.debug "took #{Time.now - now} secs to translate #{keys_in_view.count} words."
62
- generate_yaml(locale_name, models, translated_hash)
44
+ translations = translate_all(translation_keys)
45
+ logger.debug "took #{Time.now - now} secs to translate."
46
+
47
+ yaml = generate_yaml(locale_name, translations)
48
+ template 'i18n:translation.yml', "config/locales/translation_#{locale_name}.yml", :assigns => {:locale_name => locale_name, :translations => yaml.to_s}
63
49
  end
64
50
 
65
51
  private
@@ -69,22 +55,56 @@ module I18nGenerator::Generator
69
55
  end
70
56
  end
71
57
 
72
- def generate_yaml(locale_name, models, translations)
73
- template 'i18n:translation.yml', "config/locales/translation_#{locale_name}.yml", :assigns => {:locale_name => locale_name, :models => models, :translations => translations}
58
+ # mixin translations into existing yaml file
59
+ def generate_yaml(locale_name, translations)
60
+ yaml = YamlDocument.new("config/locales/translation_#{locale_name}.yml", locale_name)
61
+ each_value [], translations do |parents, value|
62
+ node = parents.inject(yaml[locale_name]) {|node, parent| node[parent]}
63
+ node.value = value
64
+ end
65
+ yaml
74
66
  end
75
67
 
76
68
  # receives an array of keys and returns :key => :translation hash
77
69
  def translate_all(keys)
78
- threads = []
79
- ret = keys.inject({}) do |hash, key|
80
- threads << Thread.new do
81
- Thread.pass
82
- hash[key] = translator.translate key
70
+ returning ActiveSupport::OrderedHash.new do |oh|
71
+ # fix the order first(for multi thread translating)
72
+ keys.each do |key|
73
+ if key.to_s.include? '.'
74
+ key_prefix, key_suffix = key.to_s.split('.')[0...-1], key.to_s.split('.')[-1]
75
+ key_prefix.inject(oh) {|h, k| h[k] ||= ActiveSupport::OrderedHash.new}[key_suffix] = nil
76
+ else
77
+ oh[key] = nil
78
+ end
79
+ end
80
+ threads = []
81
+ keys.each do |key|
82
+ threads << Thread.new do
83
+ logger.debug "translating #{key} ..."
84
+ Thread.pass
85
+ if key.to_s.include? '.'
86
+ key_prefix, key_suffix = key.to_s.split('.')[0...-1], key.to_s.split('.')[-1]
87
+ existing_translation = I18n.t(key, :default => key_suffix, :locale => locale_name)
88
+ key_prefix.inject(oh) {|h, k| h[k]}[key_suffix] = existing_translation != key_suffix ? existing_translation : translator.translate(key_suffix)
89
+ else
90
+ existing_translation = I18n.t(key, :default => key, :locale => locale_name)
91
+ oh[key] = existing_translation != key ? existing_translation : translator.translate(key)
92
+ end
93
+ end
94
+ end
95
+ threads.each {|t| t.join}
96
+ end
97
+ end
98
+
99
+ # iterate through all values
100
+ def each_value(parents, src, &block)
101
+ src.each do |k, v|
102
+ if v.is_a?(ActiveSupport::OrderedHash)
103
+ each_value parents + [k], v, &block
104
+ else
105
+ yield parents + [k], v
83
106
  end
84
- hash
85
107
  end
86
- threads.each {|t| t.join}
87
- ret
88
108
  end
89
109
  end
90
110
  end
@@ -9,10 +9,12 @@ module I18nTranslationGeneratorModule
9
9
  class Executer
10
10
  extend ERB::DefMethod
11
11
  include ActionView::Helpers::TranslationHelper
12
- include ThroughRyoku
12
+ include I18nTranslationGeneratorModule::ThroughRyoku
13
+
13
14
  nil.class_eval do
14
15
  def method_missing(method, *args, &block); nil; end
15
16
  end
17
+
16
18
  def_erb_method 'execute', '#{filename}'
17
19
  end
18
20
  EOS
@@ -7,7 +7,8 @@ module I18nTranslationGeneratorModule
7
7
  end
8
8
 
9
9
  def translate(locale, key, options = {})
10
- @keys << key.to_sym
10
+ # @keys << key.to_sym
11
+ @keys << (Array(options[:scope]) + [key]).map.flatten.join('.')
11
12
  end
12
13
  alias :t :translate
13
14
  end
@@ -1,6 +1,8 @@
1
- module ThroughRyoku
2
- def method_missing(method, *args, &block)
3
- nil
1
+ module I18nTranslationGeneratorModule
2
+ module ThroughRyoku
3
+ def method_missing(method, *args, &block)
4
+ nil
5
+ end
4
6
  end
5
7
  end
6
8
 
@@ -51,9 +51,9 @@ end"
51
51
  it 'adds the default_locale config inside the config block and sets locale_name value' do
52
52
  @command.send(:add_locale_config, @config).should == "
53
53
  Rails::Initializer.run do |config|
54
+ config.i18n.default_locale = 'ja'
54
55
  something goes here.
55
56
  bla bla bla...
56
- config.i18n.default_locale = 'ja'
57
57
  end"
58
58
  end
59
59
  end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), '/../generators/i18n_translation/i18n_translation_command')
2
+
3
+ describe I18nGenerator::Generator::Commands::Create do
4
+ before do
5
+ (@command = Object.new).extend I18nGenerator::Generator::Commands::Create
6
+ @command.stub!(:locale_name).and_return('ja')
7
+ end
8
+
9
+ describe 'each_value' do
10
+ it 'iterates through each value' do
11
+ hash = ActiveSupport::OrderedHash.new
12
+ hash[:parent1] = ActiveSupport::OrderedHash.new
13
+ hash[:parent1][:child1] = 'child one'
14
+ hash[:parent2] = ActiveSupport::OrderedHash.new
15
+ hash[:parent2][:child2] = 'child two'
16
+ hash[:parent2][:child3] = 'child three'
17
+ @command.__send__(:each_value, [], hash) do |parents, value|
18
+ p "#{parents.join('.')} #{value}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
data/spec/yaml_spec.rb CHANGED
@@ -1,20 +1,20 @@
1
- require File.join(File.dirname(__FILE__), '/../generators/i18n_locale/lib/yaml')
1
+ require File.join(File.dirname(__FILE__), '/../generators/i18n/lib/yaml')
2
2
  include I18nLocaleGeneratorModule
3
3
 
4
4
  describe 'Yaml' do
5
- before do
6
- @yaml = YamlDocument.new File.join(File.dirname(__FILE__), 'data/yml/active_record/en-US.yml'), 'ja-JP'
5
+ before :each do
6
+ @yaml = YamlDocument.new File.join(File.dirname(__FILE__), 'data/yml/active_record/en-US.yml'), 'ja'
7
7
  end
8
8
 
9
9
  describe YamlDocument do
10
10
  it 'should return the top level node with the square bracket method' do
11
- node = @yaml['ja-JP']
11
+ node = @yaml['ja']
12
12
  node.should be_an_instance_of(Node)
13
- node.key.should == 'ja-JP'
13
+ node.key.should == 'ja'
14
14
  end
15
15
 
16
16
  it 'should generate a path string on the top node' do
17
- @yaml['ja-JP'].path.should == '/ja-JP'
17
+ @yaml['ja'].path.should == '/ja'
18
18
  end
19
19
  end
20
20
 
@@ -32,7 +32,7 @@ describe 'Yaml' do
32
32
  end
33
33
 
34
34
  it 'should generate a path string on any node' do
35
- @yaml['ja-JP']['activerecord']['errors']['messages'].path.should == '/ja-JP/activerecord/errors/messages'
35
+ @yaml['ja']['activerecord']['errors']['messages'].path.should == '/ja-JP/activerecord/errors/messages'
36
36
  end
37
37
  end
38
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amatsuda-i18n_generators
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akira Matsuda
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-08 00:00:00 -08:00
12
+ date: 2008-12-17 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -35,6 +35,7 @@ files:
35
35
  - Rakefile
36
36
  - generators/i18n/USAGE
37
37
  - generators/i18n/i18n_generator.rb
38
+ - generators/i18n/lib/yaml.rb
38
39
  - generators/i18n/templates/base.yml
39
40
  - generators/i18n/templates/i18n_config.rb
40
41
  - generators/i18n/templates/translation.yml
@@ -42,7 +43,6 @@ files:
42
43
  - generators/i18n_locale/i18n_locale_command.rb
43
44
  - generators/i18n_locale/i18n_locale_generator.rb
44
45
  - generators/i18n_locale/lib/cldr.rb
45
- - generators/i18n_locale/lib/yaml.rb
46
46
  - generators/i18n_scaffold/i18n_scaffold_generator.rb
47
47
  - generators/i18n_scaffold/templates/controller.rb
48
48
  - generators/i18n_scaffold/templates/functional_test.rb
@@ -65,6 +65,7 @@ files:
65
65
  - spec/data/cldr/ja.html
66
66
  - spec/data/yml/active_record/en-US.yml
67
67
  - spec/i18n_locale_command_spec.rb
68
+ - spec/i18n_translation_command_spec.rb
68
69
  - spec/translator_spec.rb
69
70
  - spec/yaml_spec.rb
70
71
  has_rdoc: false
@@ -1,141 +0,0 @@
1
- require 'yaml'
2
-
3
- module I18nLocaleGeneratorModule
4
- class YamlDocument
5
- def initialize(yml_path, locale_name)
6
- @locale_name, @nodes = locale_name, []
7
- File.read(yml_path).each_with_index do |line_text, i|
8
- n = Node.new(self, i, line_text.chomp)
9
- @nodes << (((n.key == 'en-US') || (n.key == 'en')) ? Node.new(self, i, "#{locale_name}:") : n)
10
- end
11
- end
12
-
13
- def next
14
- @current_line += 1
15
- return false if @current_line == @nodes.size
16
- @nodes[@current_line].is_blank_or_comment? ? self.next : @nodes[@current_line]
17
- end
18
-
19
- def prev
20
- return false if @current_line == 0
21
- @current_line -= 1
22
- @nodes[@current_line].is_blank_or_comment? ? self.prev : @nodes[@current_line]
23
- end
24
-
25
- def parent_of(child)
26
- @current_line = child.line
27
- while n = self.prev
28
- return n if n.indent_level == child.indent_level - 2
29
- end
30
- self
31
- end
32
-
33
- def children_of(parent)
34
- nodes = Nodes.new
35
- @current_line = parent.line
36
- while n = self.next
37
- if n.indent_level < parent.indent_level + 2
38
- break
39
- elsif n.indent_level == parent.indent_level + 2
40
- nodes << n
41
- end
42
- end
43
- nodes
44
- end
45
-
46
- def [](node_name)
47
- @current_line = @nodes.detect {|n| (n.indent_level == 0) && (n.key == node_name)}.line
48
- @nodes[@current_line]
49
- end
50
-
51
- def document
52
- self
53
- end
54
-
55
- def path
56
- ''
57
- end
58
-
59
- def to_s
60
- @nodes.inject('') do |ret, n|
61
- ret << n.text + "\n"
62
- end
63
- end
64
- end
65
-
66
- class Node
67
- attr_reader :line, :indent_level
68
-
69
- def initialize(doc, line_index, text)
70
- @doc, @line, @text = doc, line_index, text
71
- @text =~ /(^\s*)/
72
- @indent_level = $1.nil? ? 0 : $1.size
73
- @yaml = YAML.load(@text + ' ')
74
- end
75
-
76
- def parent
77
- @parent ||= @doc.parent_of self
78
- end
79
-
80
- def children
81
- @children ||= @doc.children_of(self)
82
- end
83
-
84
- def [](node_name)
85
- children.detect {|c| c.key == node_name}
86
- end
87
-
88
- def key
89
- @yaml.is_a?(Hash) ? @yaml.keys.first : nil
90
- end
91
-
92
- def value
93
- @yaml.is_a?(Hash) ? @yaml.values.first : nil
94
- end
95
-
96
- def value=(val)
97
- if @yaml[self.key] != val
98
- @yaml[self.key] = val
99
- @changed = true
100
- end
101
- end
102
-
103
- def text
104
- if @changed
105
- v = if self.value.is_a?(Array)
106
- "[#{self.value * ', '}]"
107
- else
108
- %Q["#{self.value}"]
109
- end
110
- "#{' ' * self.indent_level}#{self.key}: #{v}"
111
- else
112
- @text
113
- end
114
- end
115
-
116
- def changed?
117
- @changed
118
- end
119
-
120
- def is_blank_or_comment?
121
- @text.sub(/#.*$/, '').gsub(/\s/, '').empty?
122
- end
123
-
124
- def path
125
- @path ||= "#{self.parent.path}/#{self.key}"
126
- end
127
-
128
- def descendant_nodes(&block)
129
- yield self if self.value
130
- self.children.each {|child| child.descendant_nodes(&block)} if self.children
131
- end
132
- end
133
-
134
- class Nodes < Array
135
- def[](index)
136
- return detect {|node| node.key == index} if index.is_a?(String)
137
- super
138
- end
139
- end
140
- end
141
-