Module: StewEucen::Acts::FertileForest::Table::States

Defined in:
lib/fertile_forest/modules/states.rb

Overview

This module is for extending into derived class by ActiveRecord.
The caption contains “Instance Methods”, but it means “Class Methods” of each derived class.

Instance Method Summary collapse

Instance Method Details

#ancestor?(base_obj, researches = []) ⇒ Array

Is reserching node ancestor of base node?

Parameters:

  • base_obj (Entity|Integer)

    Entity|int of base node to check.

  • researches (Array) (defaults to: [])

    Research nodes.

Returns:

  • (Array)

    Item of array true is it is ancestor node of base node.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/fertile_forest/modules/states.rb', line 194

def ancestor?(base_obj, researches = [])
  aim_node = ff_resolve_nodes(base_obj)
  return nil if aim_node.blank?   # nil as dubious

  is_plural = researches.is_a?(Array)
  return (is_plural ? [] : nil) if researches.blank?

  # need to be "id => node" for checking grove
  research_nodes = ff_resolve_nodes(
    is_plural ? researches : [researches],
    true    # refresh
  )

  exists_hash = {}
  Array(ancestors(aim_node)).each { |node| exists_hash[node.id] = true }

  res = {}
  research_nodes.each_pair do |the_id, the_node|
    res[the_id] = exists_hash[the_id]
  end

  is_plural ? res : res.values.first
end

#descendant?(base_obj, researches = []) ⇒ Array

Is reserching node descendant of base node?

Parameters:

  • base_obj (Entity|Integer)

    Entity|int of base node to check.

  • researches (Array) (defaults to: [])

    Research nodes.

Returns:

  • (Array)

    Item of array true is it is descendant node of base node.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/fertile_forest/modules/states.rb', line 152

def descendant?(base_obj, researches = [])
  aim_node = ff_resolve_nodes(base_obj)
  return nil if aim_node.blank?   # nil as dubious

  is_plural = researches.is_a?(Array)
  return (is_plural ? [] : nil) if researches.blank?

  # need to be "id => node" for checking grove
  research_nodes = ff_resolve_nodes(
    is_plural ? researches : [researches],
    true    # refresh
  )

  boundary_queue = ff_get_boundary_queue(aim_node)
  aim_tail_queue = (boundary_queue.blank? \
    ? QUEUE_MAX_VALUE
    : boundary_queue - 1
  )

  aim_queue = aim_node.ff_queue
  aim_grove = aim_node.ff_grove

  res = {}

  research_nodes.each_pair do |the_id, the_node|
    if the_node.present? && the_node.ff_grove == aim_grove
      the_queue = the_node.ff_queue
      res[the_id] = aim_queue < the_queue && the_queue <= aim_tail_queue
    else
      res[the_id] = nil
    end
  end

  is_plural ? res : res.values.first
end

#has_descendant?(node_obj) ⇒ Boolean

Has descendant?

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this has descendant node.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/fertile_forest/modules/states.rb', line 64

def has_descendant?(node_obj)
  aim_node = ff_resolve_nodes(node_obj)
  return nil if aim_node.blank?       # nil as dubious

  aim_query = ff_subtree_scope(
      aim_node,
      false,          # without top
      true            # use COALESCE()
    )
    .select(@_id)

  # FIXME: When use COALESCE(), can not act query.count
  # 0 < aim_query.count
  aim_query.first.present?
end

#has_sibling?(node_obj) ⇒ Boolean

Has sibling node?

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this has sibling node.



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/fertile_forest/modules/states.rb', line 110

def has_sibling?(node_obj)
  aim_node = ff_resolve_nodes(node_obj)
  return nil if aim_node.blank?       # nil as dubious

  aim_depth = aim_node.ff_depth
  # root node has no sibling
  return false if aim_depth == ROOT_DEPTH

  parent_node = genitor(aim_node)
  # null as dubious, because no parent is irregular
  return nil if parent_node.blank?

  ffdd = arel_table[@_ff_depth]
  aim_query = ff_subtree_scope(
      parent_node,
      false,          # without top
      false           # use COALESCE()
      # true          # FIXME: COALESCE() true makes error
    )
    .where(ffdd.eq(aim_depth))

  1 < aim_query.count
end

#height(base_obj) ⇒ Integer?

Calculate height of subtree.

When want to get root height as:
 (1) get height of any node.
 (2) root height = height of the node + depth of the node.

Height of empty tree is “-1”
en.wikipedia.org/wiki/Tree_(data_structure)

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to check.

Returns:

  • (Integer)

    Height of subtree of base node.

  • (nil)

    Invalid input (base node is nil).



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/fertile_forest/modules/states.rb', line 229

def height(base_obj)
  aim_node = ff_resolve_nodes(base_obj)
  return nil if aim_node.blank?   # nil as dubious

  ffdd = arel_table[@_ff_depth]

  # with top, use COALESCE()
  height_res = ff_subtree_scope(aim_node, SUBTREE_WITH_TOP_NODE, true)
    .select(ffdd.maximum.as('ff_height'))
    .first

  return nil if height_res.blank?   # nil as dubious

  height_res.ff_height - aim_node.ff_depth
end

#internal?(node_obj) ⇒ Boolean

Is internal node?

"internal" means non-leaf and non-root.

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this is leaf node.



98
99
100
101
102
103
# File 'lib/fertile_forest/modules/states.rb', line 98

def internal?(node_obj)
  aim_node = ff_resolve_nodes(node_obj)
  return nil if aim_node.blank?       # nil as dubious

  aim_node.ff_depth != ROOT_DEPTH && has_descendant?(node_obj)
end

#leaf?(node_obj) ⇒ Boolean

Is leaf node?

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this is leaf node.



85
86
87
88
89
90
# File 'lib/fertile_forest/modules/states.rb', line 85

def leaf?(node_obj)
  result = has_descendant?(node_obj)    # nil as dubious
  return nil if result.nil?

  !result
end

#only_child?(node_obj) ⇒ Boolean

Is only child?

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this is only child node.



139
140
141
142
143
144
# File 'lib/fertile_forest/modules/states.rb', line 139

def only_child?(node_obj)
  has_sibling = has_sibling?(node_obj)
  return nil if has_sibling.nil?    # nil as dubious

  !has_sibling
end

#root?(node_obj) ⇒ Boolean

Is root node?

Parameters:

  • node_obj (Entity|Integer)

    Node of Entity|int to check.

Returns:

  • (Boolean)

    Returns true is this is root node.



52
53
54
55
56
57
# File 'lib/fertile_forest/modules/states.rb', line 52

def root?(node_obj)
  aim_node = ff_resolve_nodes(node_obj)
  return nil if aim_node.blank?       # nil as dubious

  aim_node.ff_depth == ROOT_DEPTH     # never ===
end

#siblings?(*args) ⇒ Boolean

TODO:

is full flag

Are all nodes siblings?

Parameters:

  • args (Array)

    Nodes.

Returns:

  • (Boolean)

    Returns true is those are sibling nodes.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/fertile_forest/modules/states.rb', line 23

def siblings?(*args)
  sibling_nodes = ff_resolve_nodes(args.flatten)

  # get id hash by nested information
  eldest_node = sibling_nodes.values.first
  full_sibling_nodes = siblings(eldest_node, [@_id]).all

  child_hash = {}
  bingo_count = sibling_nodes.length
  full_sibling_nodes.each do |the_node|
    the_id = the_node.id
    child_hash[the_id] = the_node
    # ruby has no --xxxx
    bingo_count -= 1 if sibling_nodes.has_key?(the_id)
  end

  # return value
  if bingo_count == 0
    child_hash
  else
    false
  end
end

#size(base_obj) ⇒ Integer?

Calculate size of subtree.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to check.

Returns:

  • (Integer)

    Size of subtree of base node.

  • (nil)

    Invalid input (base node is nil).



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/fertile_forest/modules/states.rb', line 251

def size(base_obj)
  aim_node = ff_resolve_nodes(base_obj)
  return nil if aim_node.blank?   # nil as dubious

  ffdd = arel_table[@_ff_depth]

  # with top, use COALESCE()
  size_res = ff_subtree_scope(aim_node, SUBTREE_WITH_TOP_NODE, true)
      .select(ffdd.count.as('ff_count'))
      .first

  return nil if size_res.blank?   # nil as dubious

  size_res.ff_count
end