Class: Nomen::Item

Inherits:
Object
  • Object
show all
Defined in:
lib/nomen/item.rb

Overview

An item of a nomenclature is the core data.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nomenclature, name, options = {}) ⇒ Item

New item


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/nomen/item.rb', line 11

def initialize(nomenclature, name, options = {})
  @nomenclature = nomenclature
  @name = name.to_s
  @left, @right = @nomenclature.new_boundaries
  @depth = 0
  parent = options.delete(:parent)
  if parent.is_a?(Symbol) || parent.is_a?(String)
    @parent_name = parent.to_s
  else
    self.parent = parent
  end
  @attributes = {}.with_indifferent_access
  @children = Set.new
  options.each do |k, v|
    set(k, v)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args) ⇒ Object

Returns property descriptor


298
299
300
301
# File 'lib/nomen/item.rb', line 298

def method_missing(method_name, *args)
  return property(method_name) if has_property?(method_name)
  super
end

Instance Attribute Details

#aliasesObject (readonly)

Returns the value of attribute aliases


6
7
8
# File 'lib/nomen/item.rb', line 6

def aliases
  @aliases
end

#attributesObject Also known as: properties

Returns the value of attribute attributes


7
8
9
# File 'lib/nomen/item.rb', line 7

def attributes
  @attributes
end

#depthObject (readonly)

Returns the value of attribute depth


6
7
8
# File 'lib/nomen/item.rb', line 6

def depth
  @depth
end

#leftObject (readonly)

Returns the value of attribute left


6
7
8
# File 'lib/nomen/item.rb', line 6

def left
  @left
end

#nameObject

Returns the value of attribute name


7
8
9
# File 'lib/nomen/item.rb', line 7

def name
  @name
end

#nomenclatureObject (readonly)

Returns the value of attribute nomenclature


6
7
8
# File 'lib/nomen/item.rb', line 6

def nomenclature
  @nomenclature
end

#parent_nameObject

Returns the value of attribute parent_name


6
7
8
# File 'lib/nomen/item.rb', line 6

def parent_name
  @parent_name
end

#rightObject (readonly)

Returns the value of attribute right


6
7
8
# File 'lib/nomen/item.rb', line 6

def right
  @right
end

Instance Method Details

#<(other) ⇒ Object


207
208
209
210
# File 'lib/nomen/item.rb', line 207

def <(other)
  other = item_for_comparison(other)
  (other.left < @left && @right < other.right)
end

#<=(other) ⇒ Object


217
218
219
220
# File 'lib/nomen/item.rb', line 217

def <=(other)
  other = item_for_comparison(other)
  (other.left <= @left && @right <= other.right)
end

#<=>(other) ⇒ Object


202
203
204
205
# File 'lib/nomen/item.rb', line 202

def <=>(other)
  other = item_for_comparison(other)
  nomenclature.name <=> other.nomenclature.name && name <=> other.name
end

#==(other) ⇒ Object


197
198
199
200
# File 'lib/nomen/item.rb', line 197

def ==(other)
  other = item_for_comparison(other)
  nomenclature == other.nomenclature && name == other.name
end

#>(other) ⇒ Object


212
213
214
215
# File 'lib/nomen/item.rb', line 212

def >(other)
  other = item_for_comparison(other)
  (@left < other.left && other.right < @right)
end

#>=(other) ⇒ Object


222
223
224
225
# File 'lib/nomen/item.rb', line 222

def >=(other)
  other = item_for_comparison(other)
  (@left <= other.left && other.right <= @right)
end

#add_child(item) ⇒ Object


59
60
61
# File 'lib/nomen/item.rb', line 59

def add_child(item)
  @children << item
end

#children(options = {}) ⇒ Object

Returns children recursively by default


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/nomen/item.rb', line 108

def children(options = {})
  if options[:index].is_a?(FalseClass)
    if options[:recursively].is_a?(FalseClass)
      @children.to_a
    else
      children(index: false, recursive: false).each_with_object([]) do |item, list|
        list << item
        list += item.children(index: false, recursive: true)
        list
      end
    end
  else
    if options[:recursively].is_a?(FalseClass)
      nomenclature.list.select do |item|
        @left < item.left && item.right < @right && item.depth == @depth + 1
      end
    else
      # @children ||=
      nomenclature.list.select do |item|
        @left < item.left && item.right < @right
      end
    end
  end
end

#degree_of_kinship_with(other) ⇒ Object


89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/nomen/item.rb', line 89

def degree_of_kinship_with(other)
  other_item = item_for_comparison(other)
  a = self_and_parents.reverse
  b = other_item.self_and_parents.reverse
  return nil if a.first != b.first
  common_lineage = 0
  a.size.times do |index|
    break if a[index].nil? || b[index].nil? || a[index] != b[index]
    common_lineage += 1
  end
  a.size + b.size - 2 * common_lineage
end

#delete_child(item) ⇒ Object


63
64
65
# File 'lib/nomen/item.rb', line 63

def delete_child(item)
  @children.delete(item)
end

#find_parentObject


85
86
87
# File 'lib/nomen/item.rb', line 85

def find_parent
  @nomenclature.find(@parent_name)
end

#has_property?(name) ⇒ Boolean

Checks if item has property with given name

Returns:

  • (Boolean)

293
294
295
# File 'lib/nomen/item.rb', line 293

def has_property?(name)
  !@nomenclature.properties[name].nil?
end

#human_name(options = {}) ⇒ Object Also known as: humanize, localize

Return human name of item


180
181
182
183
184
185
186
187
188
# File 'lib/nomen/item.rb', line 180

def human_name(options = {})
  "nomenclatures.#{I18n.escape_key(nomenclature.name)}.items.#{I18n.escape_key(name)}"
    .t(options.merge(default: [
                       "items.#{I18n.escape_key(name)}".to_sym,
                       "enumerize.#{I18n.escape_key(nomenclature.name)}.#{I18n.escape_key(name)}".to_sym,
                       "labels.#{I18n.escape_key(name)}".to_sym,
                       name.humanize
                     ]))
end

#human_notion_name(notion_name, options = {}) ⇒ Object


193
194
195
# File 'lib/nomen/item.rb', line 193

def human_notion_name(notion_name, options = {})
  "nomenclatures.#{nomenclature.name}.notions.#{notion_name}.#{name}".t(options.merge(default: ["labels.#{name}".to_sym]))
end

#include?(other) ⇒ Boolean

Returns true if the given item name match the current item or its children

Returns:

  • (Boolean)

175
176
177
# File 'lib/nomen/item.rb', line 175

def include?(other)
  self >= other
end

#inspectObject


227
228
229
# File 'lib/nomen/item.rb', line 227

def inspect
  "#{@nomenclature.name}-#{@name}(#{@left}-#{@right})"
end

#original_nomenclature_nameObject


102
103
104
105
# File 'lib/nomen/item.rb', line 102

def original_nomenclature_name
  return parent.name.to_sym unless root?
  nil
end

#parentObject Also known as: fetch_parent


77
78
79
80
81
82
# File 'lib/nomen/item.rb', line 77

def parent
  return @parent if @parent
  @parent = find_parent
  @parent.add_child(self) if @parent
  @parent
end

#parent=(item) ⇒ Object


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/nomen/item.rb', line 33

def parent=(item)
  old_parent_name = @parent_name
  old_parent = @parent
  if item.nil?
    @parent = nil
    @parent_name = nil
  else
    if item.is_a?(Symbol) || item.is_a?(String)
      item = nomenclature.find!(item.to_s)
    end
    if item.nomenclature != nomenclature
      raise 'Item must come from same nomenclature'
    end
    if item.parents.any? { |p| self == p } || self == item
      raise 'Circular dependency. Item can be parent of itself.'
    end
    @parent = item
    @parent_name = @parent.name.to_s
  end
  if old_parent_name != @parent_name
    old_parent.delete_child(self) if old_parent
    @parent.add_child(self) if @parent
    @nomenclature.rebuild_tree!
  end
end

#parent?Boolean

Returns:

  • (Boolean)

73
74
75
# File 'lib/nomen/item.rb', line 73

def parent?
  parent.present?
end

#parentsObject

Returns direct parents from the closest to the farthest


138
139
140
# File 'lib/nomen/item.rb', line 138

def parents
  @parents ||= (parent.nil? ? [] : [parent] + parent.parents)
end

#property(name) ⇒ Object

Returns property value


257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/nomen/item.rb', line 257

def property(name)
  return @name.to_sym if name == :name
  property = @nomenclature.properties[name]
  value = @attributes[name]
  if property
    if value.nil? && property.fallbacks
      property.fallbacks.each do |fallback|
        value ||= @attributes[fallback]
        break if value
      end
    end
    value ||= cast_property(name, property.default) if property.default
  end
  value
end

#rebuild_tree(left = 0, depth = 0) ⇒ Object

Computes left/right value for nested set Returns right index


164
165
166
167
168
169
170
171
172
# File 'lib/nomen/item.rb', line 164

def rebuild_tree(left = 0, depth = 0)
  @depth = depth
  @left = left
  @right = @left + 1
  children(index: false, recursively: false).each do |child|
    @right = child.rebuild_tree(@right, @depth + 1) + 1
  end
  @right
end

#rebuild_tree!Object

Computes left/right value for nested set Returns right index


158
159
160
# File 'lib/nomen/item.rb', line 158

def rebuild_tree!
  @nomenclature.forest_right = rebuild_tree(@nomenclature.forest_right + 1)
end

#rise(&block) ⇒ Object


150
151
152
153
154
# File 'lib/nomen/item.rb', line 150

def rise(&block)
  result = yield(self)
  return result if result
  parent ? parent.rise(&block) : nil
end

#rootObject


133
134
135
# File 'lib/nomen/item.rb', line 133

def root
  parent? ? parent.root : self
end

#root?Boolean

Returns:

  • (Boolean)

29
30
31
# File 'lib/nomen/item.rb', line 29

def root?
  !parent
end

#selection(name) ⇒ Object


273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/nomen/item.rb', line 273

def selection(name)
  property = @nomenclature.properties[name]
  if property.list?
    return property(name).collect do |i|
      ["nomenclatures.#{@nomenclature.name}.item_lists.#{self.name}.#{name}.#{i}".t, i]
    end
  elsif property.nomenclature?
    target_nomenclature = Nomen.find(property(name).to_sym)
    unless target_nomenclature
      raise "Cannot find nomenclature: for #{property(name).inspect}"
    end
    return target_nomenclature.list.collect do |i|
      [i.human_name, i.name]
    end
  else
    raise StandardError, 'Cannot call selection for a non-list property'
  end
end

#self_and_children(options = {}) ⇒ Object


142
143
144
# File 'lib/nomen/item.rb', line 142

def self_and_children(options = {})
  [self] + children(options)
end

#self_and_parentsObject


146
147
148
# File 'lib/nomen/item.rb', line 146

def self_and_parents
  [self] + parents
end

#set(name, value) ⇒ Object


303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/nomen/item.rb', line 303

def set(name, value)
  raise "Invalid property: #{name.inspect}" if %i[name parent].include?(name.to_sym)
  # # TODO: check format
  # if property = nomenclature.properties[name]
  #   value ||= [] if property.list?
  # end
  if value.nil?
    @attributes.delete(name)
  else
    @attributes[name] = value
  end
end

#to_xml_attrsObject


239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/nomen/item.rb', line 239

def to_xml_attrs
  attrs = {}
  attrs[:name] = name
  attrs[:parent] = @parent_name if parent?
  properties.each do |pname, pvalue|
    if p = nomenclature.properties[pname.to_s]
      if p.type == :decimal
        pvalue = pvalue.to_s.to_f
      elsif p.list?
        pvalue = pvalue.join(', ')
      end
    end
    attrs[pname] = pvalue.to_s
  end
  attrs
end

#tree(depth = 0) ⇒ Object


231
232
233
234
235
236
237
# File 'lib/nomen/item.rb', line 231

def tree(depth = 0)
  text = "#{left.to_s.rjust(4)}-#{right.to_s.ljust(4)} #{'  ' * depth}#{@name}:\n"
  text << children(index: false, recursively: false).collect do |c|
    c.tree(depth + 1)
  end.join("\n")
  text
end