Module: I18n::Tasks::Data::Tree::Traversal

Included in:
Node, Nodes
Defined in:
lib/i18n/tasks/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



38
39
40
41
42
43
44
45
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 38

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



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 47

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::Tasks::Data::Tree::Node>



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 139

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



163
164
165
166
167
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 163

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:



152
153
154
155
156
157
158
159
160
161
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 152

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

#key_names(root: false) ⇒ Object



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

def key_names(root: false)
  keys(root: root).map { |key, _node| key }
end

#key_values(root: false) ⇒ Object



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

def key_values(root: false)
  keys(root: root).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



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

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
20
# File 'lib/i18n/tasks/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



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

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
      Nodes.new(nodes: nodes.children).levels(&block)
    end
  end
  self
end

#nodes(&block) ⇒ Object

rubocop:disable Metrics/ModuleLength



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

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

#root_key_value_data(sort = false) ⇒ Object



83
84
85
86
87
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 83

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[1] <=> b[1] : a[0] <=> b[0] } if sort
  result
end

#root_key_values(sort = false) ⇒ Object



77
78
79
80
81
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 77

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[1] <=> b[1] : a[0] <=> b[0] } if sort
  result
end

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

Returns:



123
124
125
126
127
128
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 123

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:



131
132
133
134
135
136
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 131

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:



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 93

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:



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 108

def select_nodes!(&block)
  to_remove = []
  each do |node|
    if block.yield(node)
      node.children&.select_nodes!(&block)
    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



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/i18n/tasks/data/tree/traversal.rb', line 169

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::Tasks::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