Class: I18n::Processes::Data::Tree::Siblings

Inherits:
Nodes
  • Object
show all
Extended by:
SplitKey
Includes:
SplitKey
Defined in:
lib/i18n/processes/data/tree/siblings.rb

Overview

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

Instance Attribute Summary collapse

Attributes inherited from Nodes

#list

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SplitKey

key_parts, last_key_part, split_key

Methods inherited from Nodes

#children, #derive, #inspect, #to_hash, #to_nodes

Methods included from Traversal

#breadth_first, #depth_first, #get_nodes_by_key_filter, #grep_keys, #intersect_keys, #key_names, #key_values, #keys, #leaves, #levels, #nodes, #root_key_value_data, #root_key_values, #select_keys, #select_keys!, #select_nodes, #select_nodes!, #set_each_value!

Constructor Details

#initialize(opts = {}) ⇒ Siblings

Returns a new instance of Siblings.



17
18
19
20
21
22
23
# 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

Instance Attribute Details

#key_to_nodeObject (readonly)

Returns the value of attribute key_to_node.



15
16
17
# File 'lib/i18n/processes/data/tree/siblings.rb', line 15

def key_to_node
  @key_to_node
end

#parentObject (readonly)

Returns the value of attribute parent.



15
16
17
# File 'lib/i18n/processes/data/tree/siblings.rb', line 15

def parent
  @parent
end

Class Method Details

.build_forest(opts = {}) {|forest| ... } ⇒ Object

Yields:

  • (forest)


256
257
258
259
260
261
262
263
# 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) ⇒ Object

build forest from [[Full Key, Value]]



311
312
313
314
315
316
317
318
# 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 = {}, &block) ⇒ Object



278
279
280
281
282
283
284
285
286
287
# 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 = {}, &block) ⇒ Object



289
290
291
292
293
294
295
296
297
# 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) ⇒ Siblings

Parameters:

  • key_occurrences (I18n::Processes::Scanners::KeyOccurrences)

Returns:



267
268
269
270
271
272
273
274
275
276
# 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 = {}) ⇒ Object Also known as: []

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



301
302
303
304
305
306
# 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

.nullObject



252
253
254
# File 'lib/i18n/processes/data/tree/siblings.rb', line 252

def null
  new
end

Instance Method Details

#append(nodes) ⇒ Object



153
154
155
# File 'lib/i18n/processes/data/tree/siblings.rb', line 153

def append(nodes)
  derive.append!(nodes)
end

#append!(nodes) ⇒ Object



144
145
146
147
148
149
150
151
# 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

#attributesObject



25
26
27
# File 'lib/i18n/processes/data/tree/siblings.rb', line 25

def attributes
  super.merge(parent: @parent)
end

#get(full_key) ⇒ Node Also known as: []

Returns by full key.

Returns:

  • (Node)

    by full key



96
97
98
99
100
101
# 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

#merge(nodes) ⇒ Object



166
167
168
# File 'lib/i18n/processes/data/tree/siblings.rb', line 166

def merge(nodes)
  derive.merge!(nodes)
end

#merge!(nodes, on_leaves_merge: nil) ⇒ Object

Parameters:

  • on_leaves_merge (Proc) (defaults to: nil)

    invoked when a leaf is merged with another leaf



158
159
160
161
162
163
164
# 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) ⇒ Object

Parameters:

  • on_leaves_merge (Proc) (defaults to: nil)

    invoked when a leaf is merged with another leaf



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# 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) ⇒ old key => new key

Parameters:

  • from_pattern (Regexp)
  • to_pattern (Regexp)
  • root (Boolean) (defaults to: false)

Returns:

  • (old key => new key)


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 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) ⇒ Object

methods below change state



138
139
140
141
142
# 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) ⇒ Object

Parameters:

  • nodes (Enumerable)

    Modified in-place.



217
218
219
220
# 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) ⇒ Object

Parameters:

  • nodes (Enumerable)

    Modified in-place.



223
224
225
226
# 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) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# 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) ⇒ Object



29
30
31
32
33
# 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) ⇒ Object



90
91
92
93
# 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) ⇒ Object Also known as: []=

add or replace node by full key



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 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

#set_root_key!(new_key, data = nil) ⇒ Object



186
187
188
189
190
191
# 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) ⇒ Object



178
179
180
# 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) ⇒ Object



182
183
184
# 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) ⇒ Object



170
171
172
# 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) ⇒ Object



174
175
176
# File 'lib/i18n/processes/data/tree/siblings.rb', line 174

def subtract_keys!(keys)
  remove_nodes_and_emptied_ancestors!(find_nodes(keys))
end