class I18n::Processes::Data::Tree::Siblings

Siblings represents a subtree sharing a common parent in case of an empty parent (nil) it represents a forest【也就是整棵树】 siblings' keys are unique

Attributes

key_to_node[R]
parent[R]

Public Class Methods

new(opts = {}) click to toggle source
Calls superclass method I18n::Processes::Data::Tree::Nodes::new
# File lib/i18n/processes/data/tree/siblings.rb, line 17
def initialize(opts = {})
  super(nodes: opts[:nodes])
  @parent = opts[:parent] || first.try(:parent)
  @list.map! { |node| node.parent == @parent ? node : node.derive(parent: @parent) }
  @key_to_node = @list.each_with_object({}) { |node, h| h[node.key] = node }
  @warn_about_add_children_to_leaf = opts.fetch(:warn_about_add_children_to_leaf, true)
end

Private Class Methods

[](hash, opts = {})
Alias for: from_nested_hash
build_forest(opts = {}) { |forest| ... } click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 256
def build_forest(opts = {}, &block)
  opts[:nodes] ||= []
  parse_parent_opt!(opts)
  forest = Siblings.new(opts)
  yield(forest) if block
  # forest.parent.children = forest
  forest
end
from_flat_pairs(pairs) click to toggle source

build forest from [[Full Key, Value]]

# File lib/i18n/processes/data/tree/siblings.rb, line 311
def from_flat_pairs(pairs)
  Siblings.new.tap do |siblings|
    pairs.each do |full_key, value|
      value.gsub!(/'|\n/, '') if value.include?("\n")
      siblings[full_key] = ::I18n::Processes::Data::Tree::Node.new(key: split_key(full_key).last, value: value)
    end
  end
end
from_key_attr(key_attrs, opts = {}) { |full_key, node| ... } click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 278
def from_key_attr(key_attrs, opts = {}, &block)
  build_forest(opts) do |forest|
    key_attrs.each do |(full_key, attr)|
      fail "Invalid key #{full_key.inspect}" if full_key.end_with?('.')
      node = ::I18n::Processes::Data::Tree::Node.new(attr.merge(key: split_key(full_key).last))
      yield(full_key, node) if block
      forest[full_key] = node
    end
  end
end
from_key_names(keys, opts = {}) { |full_key, node| ... } click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 289
def from_key_names(keys, opts = {}, &block)
  build_forest(opts) do |forest|
    keys.each do |full_key|
      node = ::I18n::Processes::Data::Tree::Node.new(key: split_key(full_key).last)
      yield(full_key, node) if block
      forest[full_key] = node
    end
  end
end
from_key_occurrences(key_occurrences) click to toggle source

@param key_occurrences [I18n::Processes::Scanners::KeyOccurrences] @return [Siblings]

# File lib/i18n/processes/data/tree/siblings.rb, line 267
def from_key_occurrences(key_occurrences)
  build_forest(warn_about_add_children_to_leaf: false) do |forest|
    key_occurrences.each do |key_occurrence|
      forest[key_occurrence.key] = ::I18n::Processes::Data::Tree::Node.new(
        key:  split_key(key_occurrence.key).last,
        data: { occurrences: key_occurrence.occurrences }
      )
    end
  end
end
from_nested_hash(hash, opts = {}) click to toggle source

build forest from nested hash, e.g. {'es' => { 'common' => { name => 'Nombre', 'age' => 'Edad' } } } this is the native i18n gem format

# File lib/i18n/processes/data/tree/siblings.rb, line 301
def from_nested_hash(hash, opts = {})
  parse_parent_opt!(opts)
  fail I18n::Processes::CommandError, "invalid tree #{hash.inspect}" unless hash.respond_to?(:map)
  opts[:nodes] = hash.map { |key, value| Node.from_key_value key, value }
  Siblings.new(opts)
end
Also aliased as: []
null() click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 252
def null
  new
end
parse_parent_opt!(opts) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 322
def parse_parent_opt!(opts)
  opts[:parent] = ::I18n::Processes::Data::Tree::Node.new(key: opts[:parent_key]) if opts[:parent_key]
  opts[:parent] = ::I18n::Processes::Data::Tree::Node.new(opts[:parent_attr]) if opts[:parent_attr]
  if opts[:parent_locale]
    opts[:parent] = ::I18n::Processes::Data::Tree::Node.new(
      key: opts[:parent_locale], data: { locale: opts[:parent_locale] }
    )
  end
end

Public Instance Methods

[](full_key)
Alias for: get
[]=(full_key, node)
Alias for: set
append(nodes) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 153
def append(nodes)
  derive.append!(nodes)
end
append!(nodes) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 144
def append!(nodes)
  nodes = nodes.map do |node|
    fail "already has a child with key '#{node.key}'" if key_to_node.key?(node.key)
    key_to_node[node.key] = (node.parent == parent ? node : node.derive(parent: parent))
  end
  super(nodes)
  self
end
attributes() click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 25
def attributes
  super.merge(parent: @parent)
end
get(full_key) click to toggle source

@return [Node] by full key

# File lib/i18n/processes/data/tree/siblings.rb, line 96
def get(full_key)
  first_key, rest = split_key(full_key.to_s, 2)
  node            = key_to_node[first_key]
  node = node.children.try(:get, rest) if rest && node
  node
end
Also aliased as: []
merge(nodes) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 166
def merge(nodes)
  derive.merge!(nodes)
end
merge!(nodes, on_leaves_merge: nil) click to toggle source

@param on_leaves_merge [Proc] invoked when a leaf is merged with another leaf

# File lib/i18n/processes/data/tree/siblings.rb, line 158
def merge!(nodes, on_leaves_merge: nil)
  nodes = Siblings.from_nested_hash(nodes) if nodes.is_a?(Hash)
  nodes.each do |node|
    merge_node! node, on_leaves_merge: on_leaves_merge
  end
  self
end
merge_node!(node, on_leaves_merge: nil) click to toggle source

@param on_leaves_merge [Proc] invoked when a leaf is merged with another leaf

# File lib/i18n/processes/data/tree/siblings.rb, line 194
def merge_node!(node, on_leaves_merge: nil) # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity
  if key_to_node.key?(node.key)
    our = key_to_node[node.key]
    return if our == node
    our.value = node.value if node.leaf?
    our.data.merge!(node.data) if node.data?
    if node.children?
      if our.children
        our.children.merge!(node.children)
      else
        warn_add_children_to_leaf our
        our.children = node.children
      end
    elsif on_leaves_merge
      on_leaves_merge.call(our, node)
    end
  else
    @list << (key_to_node[node.key] = node.derive(parent: parent))
    dirty!
  end
end
mv_key!(from_pattern, to_pattern, root: false) click to toggle source

@param from_pattern [Regexp] @param to_pattern [Regexp] @param root [Boolean] @return {old key => new key}

# File lib/i18n/processes/data/tree/siblings.rb, line 53
def mv_key!(from_pattern, to_pattern, root: false) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
  moved_forest = Siblings.new
  moved_nodes = []
  old_key_to_new_key = {}
  nodes do |node|
    full_key = node.full_key(root: root)
    if from_pattern =~ full_key
      moved_nodes << node
      if to_pattern.empty?
        old_key_to_new_key[full_key] = nil
        next
      end
      match = $~
      new_key = to_pattern.gsub(/\\\d+/) { |m| match[m[1..-1].to_i] }
      old_key_to_new_key[full_key] = new_key
      moved_forest.merge!(Siblings.new.tap do |forest|
        forest[[(node.root.try(:key) unless root), new_key].compact.join('.')] =
          node.derive(key: split_key(new_key).last)
      end)
    end
  end
  # Adjust references
  # TODO: support nested references better
  nodes do |node|
    next unless node.reference?
    old_target = [(node.root.key if root), node.value.to_s].compact.join('.')
    new_target = old_key_to_new_key[old_target]
    if new_target
      new_target = new_target.sub(/\A[^.]*\./, '') if root
      node.value = new_target.to_sym
    end
  end
  remove_nodes_and_emptied_ancestors! moved_nodes
  merge! moved_forest
  old_key_to_new_key
end
remove!(node) click to toggle source

methods below change state

# File lib/i18n/processes/data/tree/siblings.rb, line 138
def remove!(node)
  super
  key_to_node.delete(node.key)
  self
end
remove_nodes_and_emptied_ancestors(nodes) click to toggle source

@param nodes [Enumerable] Modified in-place.

# File lib/i18n/processes/data/tree/siblings.rb, line 217
def remove_nodes_and_emptied_ancestors(nodes)
  add_ancestors_that_only_contain_nodes! nodes
  select_nodes { |node| !nodes.include?(node) }
end
remove_nodes_and_emptied_ancestors!(nodes) click to toggle source

@param nodes [Enumerable] Modified in-place.

# File lib/i18n/processes/data/tree/siblings.rb, line 223
def remove_nodes_and_emptied_ancestors!(nodes)
  add_ancestors_that_only_contain_nodes! nodes
  select_nodes! { |node| !nodes.include?(node) }
end
rename_each_key!(full_key_pattern, new_key_tpl) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 35
def rename_each_key!(full_key_pattern, new_key_tpl)
  pattern_re = I18n::Processes::KeyPatternMatching.compile_key_pattern(full_key_pattern)
  nodes do |node|
    next if node.full_key(root: true) !~ pattern_re
    new_key = new_key_tpl.gsub('%{key}', node.key)
    if node.parent == parent
      rename_key(node.key, new_key)
    else
      node.parent.children.rename_key(node.key, new_key)
    end
  end
  self
end
rename_key(key, new_key) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 29
def rename_key(key, new_key)
  node = key_to_node.delete(key)
  replace_node! node, node.derive(key: new_key)
  self
end
replace_node!(node, new_node) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 90
def replace_node!(node, new_node)
  @list[@list.index(node)]  = new_node
  key_to_node[new_node.key] = new_node
end
set(full_key, node) click to toggle source

add or replace node by full key

# File lib/i18n/processes/data/tree/siblings.rb, line 106
def set(full_key, node)
  fail 'value should be a I18n::Processes::Data::Tree::Node' unless node.is_a?(Node)
  key_part, rest = split_key(full_key, 2)
  child          = key_to_node[key_part]

  if rest
    unless child
      child = Node.new(
        key: key_part,
        parent: parent,
        children: [],
        warn_about_add_children_to_leaf: @warn_add_children_to_leaf
      )
      append! child
    end
    unless child.children
      warn_add_children_to_leaf child if @warn_about_add_children_to_leaf
      child.children = []
    end
    child.children.set rest, node
  else
    remove! child if child
    append! node
  end
  dirty!
  node
end
Also aliased as: []=
set_root_key!(new_key, data = nil) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 186
def set_root_key!(new_key, data = nil)
  return self if empty?
  rename_key first.key, new_key
  leaves { |node| node.data.merge! data } if data
  self
end
subtract_by_key(other) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 178
def subtract_by_key(other)
  subtract_keys other.key_names(root: true)
end
subtract_by_key!(other) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 182
def subtract_by_key!(other)
  subtract_keys! other.key_names(root: true)
end
subtract_keys(keys) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 170
def subtract_keys(keys)
  remove_nodes_and_emptied_ancestors(find_nodes(keys))
end
subtract_keys!(keys) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 174
def subtract_keys!(keys)
  remove_nodes_and_emptied_ancestors!(find_nodes(keys))
end

Private Instance Methods

add_ancestors_that_only_contain_nodes!(nodes) click to toggle source

Adds all the ancestors that only contain the given nodes as descendants to the given nodes. @param nodes [Set] Modified in-place.

# File lib/i18n/processes/data/tree/siblings.rb, line 239
def add_ancestors_that_only_contain_nodes!(nodes)
  levels.reverse_each do |level_nodes|
    level_nodes.each { |node| nodes << node if node.children? && node.children.all? { |c| nodes.include?(c) } }
  end
end
find_nodes(keys) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 230
def find_nodes(keys)
  keys.each_with_object(Set.new) do |key, set|
    node = get(key)
    set << node if node
  end
end
warn_add_children_to_leaf(node) click to toggle source
# File lib/i18n/processes/data/tree/siblings.rb, line 245
def warn_add_children_to_leaf(node)
  ::I18n::Processes::Logging.log_warn "'#{node.full_key}' was a leaf, now has children (value <- scope conflict)"
end