Class: Onoma::Item

Inherits:
Object
  • Object
show all
Defined in:
lib/onoma/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



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

def initialize(nomenclature, name, options = {})
  @nomenclature = nomenclature
  @name = name.to_s
  @left, @right = @nomenclature.new_boundaries
  @depth = 0
  @parent = nil
  @parent_name = nil
  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



318
319
320
321
322
# File 'lib/onoma/item.rb', line 318

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.



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

def aliases
  @aliases
end

#attributesObject Also known as: properties

Returns the value of attribute attributes.



8
9
10
# File 'lib/onoma/item.rb', line 8

def attributes
  @attributes
end

#depthObject (readonly)

Returns the value of attribute depth.



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

def depth
  @depth
end

#leftObject (readonly)

Returns the value of attribute left.



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

def left
  @left
end

#nameObject

Returns the value of attribute name.



8
9
10
# File 'lib/onoma/item.rb', line 8

def name
  @name
end

#nomenclatureObject (readonly)

Returns the value of attribute nomenclature.



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

def nomenclature
  @nomenclature
end

#parent_nameObject

Returns the value of attribute parent_name.



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

def parent_name
  @parent_name
end

#rightObject (readonly)

Returns the value of attribute right.



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

def right
  @right
end

Instance Method Details

#<(other) ⇒ Object



224
225
226
227
# File 'lib/onoma/item.rb', line 224

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

#<=(other) ⇒ Object



234
235
236
237
# File 'lib/onoma/item.rb', line 234

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

#<=>(other) ⇒ Object



219
220
221
222
# File 'lib/onoma/item.rb', line 219

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

#==(other) ⇒ Object



214
215
216
217
# File 'lib/onoma/item.rb', line 214

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

#>(other) ⇒ Object



229
230
231
232
# File 'lib/onoma/item.rb', line 229

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

#>=(other) ⇒ Object



239
240
241
242
# File 'lib/onoma/item.rb', line 239

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

#add_child(item) ⇒ Object



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

def add_child(item)
  @children << item
end

#children(options = {}) ⇒ Object

Returns children recursively by default



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/onoma/item.rb', line 117

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
  elsif 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

#degree_of_kinship_with(other) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/onoma/item.rb', line 95

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



67
68
69
# File 'lib/onoma/item.rb', line 67

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

#find_parentObject



91
92
93
# File 'lib/onoma/item.rb', line 91

def find_parent
  @nomenclature.find(@parent_name)
end

#has_property?(name) ⇒ Boolean

Checks if item has property with given name

Returns:

  • (Boolean)


313
314
315
# File 'lib/onoma/item.rb', line 313

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

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

Return human name of item



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/onoma/item.rb', line 188

def human_name(options = {})
  scope = options.delete(:scope)
  no_attribute_translation = "nomenclatures.#{Onoma.escape_key(nomenclature.name)}.items.#{Onoma.escape_key(name)}"
  if scope
    scope_attribute = scope
    other_attributes = attributes.except(scope_attribute)
    scope_translation = "nomenclatures.#{Onoma.escape_key(nomenclature.name)}.items.#{Onoma.escape_key(scope_attribute)}.#{Onoma.escape_key(name)}"
    other_translations = other_attributes.map { |attr_name, _value| "nomenclatures.#{Onoma.escape_key(nomenclature.name)}.items.#{Onoma.escape_key(attr_name)}.#{Onoma.escape_key(name)}" }
    root = scope_translation
    defaults = [no_attribute_translation, *other_translations]
  else
    scoped_by_attributes = attributes.map { |attr_name, _value| "nomenclatures.#{Onoma.escape_key(nomenclature.name)}.items.#{Onoma.escape_key(attr_name)}.#{Onoma.escape_key(name)}" }
    root = "nomenclatures.#{Onoma.escape_key(nomenclature.name)}.items.#{Onoma.escape_key(name)}"
    defaults = scoped_by_attributes
  end
  I18n.t(root, options.merge(default: [*defaults.map(&:to_sym), "items.#{Onoma.escape_key(name)}".to_sym, "enumerize.#{Onoma.escape_key(nomenclature.name)}.#{Onoma.escape_key(name)}".to_sym, "labels.#{Onoma.escape_key(name)}".to_sym, name.humanize]))
end

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



210
211
212
# File 'lib/onoma/item.rb', line 210

def human_notion_name(notion_name, options = {})
  I18n.t("nomenclatures.#{nomenclature.name}.notions.#{notion_name}.#{name}", 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)


183
184
185
# File 'lib/onoma/item.rb', line 183

def include?(other)
  self >= other
end

#inspectObject



244
245
246
# File 'lib/onoma/item.rb', line 244

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

#original_nomenclature_nameObject



110
111
112
113
114
# File 'lib/onoma/item.rb', line 110

def original_nomenclature_name
  return parent.name.to_sym unless root?

  nil
end

#parentObject Also known as: fetch_parent



81
82
83
84
85
86
87
# File 'lib/onoma/item.rb', line 81

def parent
  return @parent if @parent

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

#parent=(item) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/onoma/item.rb', line 36

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)


77
78
79
# File 'lib/onoma/item.rb', line 77

def parent?
  parent.present?
end

#parentsObject

Returns direct parents from the closest to the farthest



145
146
147
# File 'lib/onoma/item.rb', line 145

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

#property(name) ⇒ Object

Returns property value



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

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 ||= @name if fallback == :name
        value ||= @attributes[fallback]
        break if value
      end
    end
    value = cast_property(name, property.default) if property.default && value.nil?
  end
  value
end

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

Computes left/right value for nested set Returns right index



172
173
174
175
176
177
178
179
180
# File 'lib/onoma/item.rb', line 172

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



166
167
168
# File 'lib/onoma/item.rb', line 166

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

#rise(&block) ⇒ Object



157
158
159
160
161
162
# File 'lib/onoma/item.rb', line 157

def rise(&block)
  result = yield(self)
  return result if result

  parent ? parent.rise(&block) : nil
end

#rootObject



140
141
142
# File 'lib/onoma/item.rb', line 140

def root
  parent? ? parent.root : self
end

#root?Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/onoma/item.rb', line 32

def root?
  !parent
end

#selection(name) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/onoma/item.rb', line 292

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 = Onoma.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.new('Cannot call selection for a non-list property')
  end
end

#self_and_children(options = {}) ⇒ Object



149
150
151
# File 'lib/onoma/item.rb', line 149

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

#self_and_parentsObject



153
154
155
# File 'lib/onoma/item.rb', line 153

def self_and_parents
  [self] + parents
end

#set(name, value) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/onoma/item.rb', line 324

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



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

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



248
249
250
251
252
253
254
# File 'lib/onoma/item.rb', line 248

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