Skip to content

Commit

Permalink
Basic reference resolution #150
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Jan 28, 2016
1 parent d41772f commit fa64c0d
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/i18n/tasks/base_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'i18n/tasks/key_pattern_matching'
require 'i18n/tasks/logging'
require 'i18n/tasks/plural_keys'
require 'i18n/tasks/references'
require 'i18n/tasks/html_keys'
require 'i18n/tasks/used_keys'
require 'i18n/tasks/ignore_keys'
Expand All @@ -23,6 +24,7 @@ class BaseTask
include SplitKey
include KeyPatternMatching
include PluralKeys
include References
include HtmlKeys
include UsedKeys
include IgnoreKeys
Expand Down
63 changes: 63 additions & 0 deletions lib/i18n/tasks/references.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true
module I18n::Tasks
module References
# Given a tree of key usages, return all the reference keys in the tree in their resolved form.
# @param usages [Data::Tree::Siblings]
# @param references [Data::Tree::Siblings]
# @return [Array<String>] a list of all references and their resolutions.
def resolve_references(usages, references)
usages.each.flat_map do |node|
references.key_to_node.flat_map do |ref_key_part, ref_node|
if node.key == ref_key_part
if ref_node.leaf?
[ref_node.full_key(root: false)] +
if node.leaf?
[ref_node.value.to_s]
else
node.children.flat_map { |child|
collect_referenced_keys(child, [ref_node.value.to_s])
}
end
else
resolve_references(node.children, ref_node.children)
end
else
[]
end
end
end
end

# Given a node, return the keys of all the leaves up to the given node prefixed with the given prefix.
# @param node [Data::Tree::Node]
# @param prefix [Array<String>]
# @return Array<String> full keys
def collect_referenced_keys(node, prefix)
if node.leaf?
(prefix + [node.key]) * '.'
else
node.children.flat_map { |child| collect_referenced_keys(child, prefix + [node.key]) }
end
end

# Given a forest of references, merge trees into one tree, ensuring there are no conflicting references.
# @param roots [Data::Tree::Siblings]
# @return [Data::Tree::Siblings]
def merge_reference_trees(roots)
roots.inject(empty_forest) do |forest, root|
root.keys { |full_key, node|
::I18n::Tasks::Logging.log_warn(
"Self-referencing node: #{node.full_key.inspect} is #{node.value.inspect} in #{node.data[:locale]}"
) if full_key == node.value.to_s
}
forest.merge!(
root.children,
leaves_merge_guard: -> (node, other) {
::I18n::Tasks::Logging.log_warn(
"Conflicting references: #{node.full_key.inspect} is #{node.value.inspect} in #{node.data[:locale]}, but #{other.value.inspect} in #{other.data[:locale]}"
) if node.value != other.value
})
end
end
end
end
55 changes: 55 additions & 0 deletions spec/references_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'spec_helper'

RSpec.describe 'Reference keys' do
let(:task) { ::I18n::Tasks::BaseTask.new }

describe '#resolve_references' do
it 'resolves plain references' do
result = task.resolve_references(
build_tree('en' => {
'reference' => nil,
'not-a-reference' => nil
}),
build_tree('en' => {
'reference' => :resolved
}))
expect(result).to(eq %w(reference resolved))
end

it 'resolves nested references' do
result = task.resolve_references(
build_tree('en' => {
'reference' => {'a' => nil, 'b' => {'c' => nil}},
'not-a-reference' => nil
}),
build_tree('en' => {
'reference' => :resolved
}))
expect(result).to(eq %w(reference resolved.a resolved.b.c))
end

it 'resolves nested references with nested keys' do
result = task.resolve_references(
build_tree('en' => {
'nested' => {'reference' => {'a' => nil, 'b' => {'c' => nil}}},
'not-a-reference' => nil
}),
build_tree('en' => {
'nested' => {'reference' => :resolved}
}))
expect(result).to(eq %w(nested.reference resolved.a resolved.b.c))
end

it 'returns empty array when nothing to resolve' do
result = task.resolve_references(
build_tree('en' => {
'not-a-reference' => nil
}),
build_tree('en' => {
'reference' => :resolved
}))
expect(result).to(eq [])
end
end
end

0 comments on commit fa64c0d

Please sign in to comment.