Module: I18n::Processes::Data::Tree::Traversal

Included in:
Node, Nodes
Defined in:
lib/i18n/processes/data/tree/traversal.rb

Overview

Any Enumerable that yields nodes can mix in this module

Instance Method Summary collapse

Instance Method Details

#breadth_first(&visitor) ⇒ Object



36
37
38
39
40
41
42
# File 'lib/i18n/processes/data/tree/traversal.rb', line 36

def breadth_first(&visitor)
  return to_enum(:breadth_first) unless visitor
  levels do |nodes|
    nodes.each { |node| visitor.yield(node) }
  end
  self
end

#depth_first(&visitor) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/i18n/processes/data/tree/traversal.rb', line 44

def depth_first(&visitor)
  return to_enum(:depth_first) unless visitor
  each do |node|
    visitor.yield node
    next unless node.children?
    node.children.each do |child|
      child.depth_first(&visitor)
    end
  end
  self
end

#get_nodes_by_key_filter(root: false, &block) ⇒ Set<I18n::Processes::Data::Tree::Node>



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/i18n/processes/data/tree/traversal.rb', line 134

def get_nodes_by_key_filter(root: false, &block)
  matches = Set.new
  keys(root: root) do |full_key, node|
    if block.yield(full_key, node)
      node.walk_to_root do |p|
        break unless matches.add?(p)
      end
    end
  end
  matches
end

#grep_keys(match, opts = {}) ⇒ Object



158
159
160
161
162
# File 'lib/i18n/processes/data/tree/traversal.rb', line 158

def grep_keys(match, opts = {})
  select_keys(opts) do |full_key, _node|
    match === full_key # rubocop:disable Style/CaseEquality
  end
end

#intersect_keys(other_tree, key_opts = {}, &block) ⇒ Siblings

Returns:



147
148
149
150
151
152
153
154
155
156
# File 'lib/i18n/processes/data/tree/traversal.rb', line 147

def intersect_keys(other_tree, key_opts = {}, &block)
  if block
    select_keys(key_opts) do |key, node|
      other_node = other_tree[key]
      other_node && yield(key, node, other_node)
    end
  else
    select_keys(key_opts) { |key, _node| other_tree[key] }
  end
end

#key_names(opts = {}) ⇒ Object



63
64
65
66
# File 'lib/i18n/processes/data/tree/traversal.rb', line 63

def key_names(opts = {})
  opts[:root] = false unless opts.key?(:root)
  keys(opts).map { |key, _node| key }
end

#key_values(opts = {}) ⇒ Object



68
69
70
71
# File 'lib/i18n/processes/data/tree/traversal.rb', line 68

def key_values(opts = {})
  opts[:root] = false unless opts.key?(:root)
  keys(opts).map { |key, node| [key, node.value] }
end

#keys(root: false, &visitor) ⇒ Object

Parameters:

  • root (Hash) (defaults to: false)

    a customizable set of options

Options Hash (root:):

  • include (Object)

    root in full key



57
58
59
60
61
# File 'lib/i18n/processes/data/tree/traversal.rb', line 57

def keys(root: false, &visitor)
  return to_enum(:keys, root: root) unless visitor
  leaves { |node| visitor.yield(node.full_key(root: root), node) }
  self
end

#leaves(&visitor) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/i18n/processes/data/tree/traversal.rb', line 13

def leaves(&visitor)
  return to_enum(:leaves) unless visitor
  nodes do |node|
    visitor.yield(node) if node.leaf?
  end
  self
end

#levels(&block) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/i18n/processes/data/tree/traversal.rb', line 21

def levels(&block)
  return to_enum(:levels) unless block
  nodes = to_nodes
  unless nodes.empty?
    block.yield nodes
    if nodes.size == 1
      node = first
      node.children.levels(&block) if node.children?
    else
      I18n::Processes::Data::Tree::Nodes.new(nodes: nodes.children).levels(&block)
    end
  end
  self
end

#nodes(&block) ⇒ Object

rubocop:disable Metrics/ModuleLength



9
10
11
# File 'lib/i18n/processes/data/tree/traversal.rb', line 9

def nodes(&block)
  depth_first(&block)
end

#root_key_value_data(sort = false) ⇒ Object



79
80
81
82
83
# File 'lib/i18n/processes/data/tree/traversal.rb', line 79

def root_key_value_data(sort = false)
  result = keys(root: false).map { |key, node| [node.root.key, key, node.value, node.data] }
  result.sort! { |a, b| a[0] != b[0] ? a[0] <=> b[0] : a[1] <=> b[1] } if sort
  result
end

#root_key_values(sort = false) ⇒ Object



73
74
75
76
77
# File 'lib/i18n/processes/data/tree/traversal.rb', line 73

def root_key_values(sort = false)
  result = keys(root: false).map { |key, node| [node.root.key, key, node.value] }
  result.sort! { |a, b| a[0] != b[0] ? a[0] <=> b[0] : a[1] <=> b[1] } if sort
  result
end

#select_keys(root: false, &block) ⇒ Siblings

Returns:



118
119
120
121
122
123
# File 'lib/i18n/processes/data/tree/traversal.rb', line 118

def select_keys(root: false, &block)
  matches = get_nodes_by_key_filter(root: root, &block)
  select_nodes do |node|
    matches.include?(node)
  end
end

#select_keys!(root: false, &block) ⇒ Siblings

Returns:



126
127
128
129
130
131
# File 'lib/i18n/processes/data/tree/traversal.rb', line 126

def select_keys!(root: false, &block)
  matches = get_nodes_by_key_filter(root: root, &block)
  select_nodes! do |node|
    matches.include?(node)
  end
end

#select_nodes(&block) ⇒ Siblings

Select the nodes for which the block returns true. Pre-order traversal.

Returns:



89
90
91
92
93
94
95
96
97
98
99
# File 'lib/i18n/processes/data/tree/traversal.rb', line 89

def select_nodes(&block)
  tree = Siblings.new
  each do |node|
    next unless block.yield(node)
    tree.append! node.derive(
      parent: tree.parent,
      children: (node.children.select_nodes(&block).to_a if node.children)
    )
  end
  tree
end

#select_nodes!(&block) ⇒ Siblings

Select the nodes for which the block returns true. Pre-order traversal.

Returns:



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/i18n/processes/data/tree/traversal.rb', line 103

def select_nodes!(&block)
  to_remove = []
  each do |node|
    if block.yield(node)
      node.children.select_nodes!(&block) if node.children
    else
      # removing during each is unsafe
      to_remove << node
    end
  end
  to_remove.each { |node| remove! node }
  self
end

#set_each_value!(val_pattern, key_pattern = nil, &value_proc) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/i18n/processes/data/tree/traversal.rb', line 164

def set_each_value!(val_pattern, key_pattern = nil, &value_proc)
  value_proc ||= proc do |node|
    node_value = node.value
    next node_value if node.reference?
    human_key = ActiveSupport::Inflector.humanize(node.key.to_s)
    full_key = node.full_key
    default = (node.data[:occurrences] || []).detect { |o| o.default_arg.presence }.try(:default_arg)
    StringInterpolation.interpolate_soft(
      val_pattern,
      value: node_value,
      human_key: human_key,
      key: full_key,
      default: default,
      value_or_human_key: node_value.presence || human_key,
      value_or_default_or_human_key: node_value.presence || default || human_key
    )
  end
  pattern_re = I18n::Processes::KeyPatternMatching.compile_key_pattern(key_pattern) if key_pattern.present?
  keys.each do |key, node|
    next if pattern_re && key !~ pattern_re
    node.value = value_proc.call(node)
  end
  self
end