Class: MESH::Mesh

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/MESH/mesh.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#childrenObject

Returns the value of attribute children.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def children
  @children
end

#descriptor_classObject

Returns the value of attribute descriptor_class.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def descriptor_class
  @descriptor_class
end

#parentsObject

Returns the value of attribute parents.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def parents
  @parents
end

#rootsObject

Returns the value of attribute roots.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def roots
  @roots
end

#tree_numbersObject

Returns the value of attribute tree_numbers.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def tree_numbers
  @tree_numbers
end

#unique_idObject

Returns the value of attribute unique_id.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def unique_id
  @unique_id
end

#usefulObject

Returns the value of attribute useful.



5
6
7
# File 'lib/MESH/mesh.rb', line 5

def useful
  @useful
end

Class Method Details

.cluster(headings) ⇒ Object



204
205
206
# File 'lib/MESH/mesh.rb', line 204

def self.cluster(headings)
  return headings
end

.configure(args) ⇒ Object

Raises:

  • (ArgumentError)


28
29
30
31
32
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
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
89
90
91
92
93
94
95
# File 'lib/MESH/mesh.rb', line 28

def self.configure(args)
  return if @@configured
  raise ArgumentError.new('MeshHeadingGraph requires a filename in order to configure itself') unless not args[:filename].nil?

  gzipped_file = File.open(args[:filename])
  file = Zlib::GzipReader.new(gzipped_file)

  current_heading = Mesh.new
  file.each_line do |line|

    case

      when matches = line.match(/^\*NEWRECORD$/)
        unless current_heading.unique_id.nil?
          current_heading.entries.sort!
          @@headings << current_heading
          @@by_unique_id[current_heading.unique_id] = current_heading
          @@by_original_heading[current_heading.original_heading] = current_heading
          current_heading.tree_numbers.each do |tree_number|
            @@by_tree_number[tree_number] = current_heading
          end
        end
        current_heading = Mesh.new

      when matches = line.match(/^UI = (.*)/)
        current_heading.unique_id = matches[1]

      when matches = line.match(/^MN = (.*)/)
        current_heading.tree_numbers << matches[1]
        current_heading.roots << matches[1][0] unless current_heading.roots.include?(matches[1][0])

      when matches = line.match(/^MS = (.*)/)
        current_heading.set_summary(matches[1])

      when matches = line.match(/^DC = (.*)/)
        current_heading.descriptor_class = @@descriptor_classes[matches[1].to_i]

      when matches = line.match(/^MH = (.*)/)
        mh = matches[1]
        current_heading.set_original_heading(mh)
        current_heading.entries << mh
        librarian_parts = mh.match(/(.*), (.*)/)
        nln = librarian_parts.nil? ? mh : "#{librarian_parts[2]} #{librarian_parts[1]}"
        current_heading.set_natural_language_name(nln)

      when matches = line.match(/^(?:PRINT )?ENTRY = ([^|]+)/)
        entry = matches[1].chomp
        current_heading.entries << entry

    end

  end

  @@by_unique_id.each do |id, heading|
    heading.tree_numbers.each do |tree_number|
      #D03.438.221.173
      parts = tree_number.split('.')
      if parts.size > 1
        parts.pop
        parent_tree_number = parts.join '.'
        parent = @@by_tree_number[parent_tree_number]
        heading.parents << parent unless parent.nil?
        parent.children << heading unless parent.nil?
      end
    end
  end
  @@configured = true
end

.eachObject



133
134
135
136
137
# File 'lib/MESH/mesh.rb', line 133

def self.each
  for i in 0 ... @@headings.size
    yield @@headings[i] if @@headings[i].useful
  end
end

.find(unique_id) ⇒ Object



110
111
112
113
# File 'lib/MESH/mesh.rb', line 110

def self.find(unique_id)
  raise 'MeshHeadingGraph.configure must be called before use' unless @@configured
  return @@by_unique_id[unique_id]
end

.find_by_original_heading(heading) ⇒ Object



120
121
122
123
# File 'lib/MESH/mesh.rb', line 120

def self.find_by_original_heading(heading)
  raise 'MeshHeadingGraph.configure must be called before use' unless @@configured
  return @@by_original_heading[heading]
end

.find_by_tree_number(tree_number) ⇒ Object



115
116
117
118
# File 'lib/MESH/mesh.rb', line 115

def self.find_by_tree_number(tree_number)
  raise 'MeshHeadingGraph.configure must be called before use' unless @@configured
  return @@by_tree_number[tree_number]
end

.match_in_text(text) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/MESH/mesh.rb', line 139

def self.match_in_text(text)
  return [] if text.nil?
  downcased = text.downcase
  matches = []
  @@headings.each do |heading|
    next unless heading.useful
    @@locales.each do |locale|
      heading.entries(locale).each do |entry|
        if downcased.include? entry.downcase #This is a looser check than the regex but much, much faster
          if /^[A-Z0-9]+$/ =~ entry
            regex = /(^|\W)#{Regexp.quote(entry)}(\W|$)/
          else
            regex = /(^|\W)#{Regexp.quote(entry)}(\W|$)/i
          end
          text.to_enum(:scan, regex).map do |m,|
            matches << {heading: heading, matched: entry, index: $`.size}
          end
        end
      end
    end
  end
  confirmed_matches = []
  matches.combination(2) do |l, r|
    if (r[:index] >= l[:index]) && (r[:index] + r[:matched].length <= l[:index] + l[:matched].length)
      #r is within l
      r[:delete] = true
    elsif (l[:index] >= r[:index]) && (l[:index] + l[:matched].length <= r[:index] + r[:matched].length)
      #l is within r
      l[:delete] = true
    end
  end
  matches.delete_if { |match| match[:delete] }
end

.translate(locale, tr) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/MESH/mesh.rb', line 97

def self.translate(locale, tr)
  return if @@locales.include? locale
  @@headings.each_with_index do |h, i|
    h.set_original_heading(tr.translate(h.original_heading), locale)
    h.set_natural_language_name(tr.translate(h.natural_language_name), locale)
    h.set_summary(tr.translate(h.summary), locale)
    h.entries.each { |entry| h.entries(locale) << tr.translate(entry) }
    h.entries(locale).sort!
  end

  @@locales << locale
end

.where(conditions) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/MESH/mesh.rb', line 125

def self.where(conditions)
  matches = []
  @@headings.each do |heading|
    matches << heading if heading.matches(conditions)
  end
  matches
end

Instance Method Details

#<=>(other) ⇒ Object



7
8
9
# File 'lib/MESH/mesh.rb', line 7

def <=> other
  self.unique_id <=> other.unique_id
end

#deepest_position(root = '') ⇒ Object



192
193
194
195
196
# File 'lib/MESH/mesh.rb', line 192

def deepest_position(root = '')
  return nil if tree_numbers.empty?
  deepest_tree_number = tree_numbers.max_by { |tn| tn.start_with?(root) ? tn.length : 0 }
  deepest_tree_number.split('.').length
end

#entries(locale = @@default_locale) ⇒ Object



23
24
25
26
# File 'lib/MESH/mesh.rb', line 23

def entries(locale = @@default_locale)
  @entries[locale] ||= []
  return @entries[locale]
end

#has_ancestor(heading) ⇒ Object



173
174
175
176
177
178
# File 'lib/MESH/mesh.rb', line 173

def has_ancestor(heading)
  return false if parents.empty?
  return true if parents.include? heading
  in_grandparents = parents.map { |p| p.has_ancestor(heading) }
  return in_grandparents.include? true
end

#has_descendant(heading) ⇒ Object



180
181
182
183
184
185
# File 'lib/MESH/mesh.rb', line 180

def has_descendant(heading)
  return false if children.empty?
  return true if children.include? heading
  in_grandchildren = children.map { |p| p.has_descendant(heading) }
  return in_grandchildren.include? true
end

#inspectObject



224
225
226
# File 'lib/MESH/mesh.rb', line 224

def inspect
  return "#{unique_id}, #{original_heading}, [#{tree_numbers.join(',')}]"
end

#matches(conditions) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/MESH/mesh.rb', line 208

def matches(conditions)
  conditions.each do |field, pattern|
    field_content = self.send(field)
    if field_content.kind_of?(Array)
      return false unless field_content.find { |fc| pattern =~ fc }
    elsif field_content.is_a?(TrueClass) || field_content.is_a?(FalseClass)
      return false unless field_content == pattern
    elsif field_content.is_a? Symbol
      return field_content == pattern
    else
      return false unless pattern =~ field_content
    end
  end
  return true
end

#natural_language_name(locale = @@default_locale) ⇒ Object



15
16
17
# File 'lib/MESH/mesh.rb', line 15

def natural_language_name(locale = @@default_locale)
  return @natural_language_name[locale]
end

#original_heading(locale = @@default_locale) ⇒ Object



11
12
13
# File 'lib/MESH/mesh.rb', line 11

def original_heading(locale = @@default_locale)
  return @original_heading[locale]
end

#set_natural_language_name(name, locale = @@default_locale) ⇒ Object



232
233
234
# File 'lib/MESH/mesh.rb', line 232

def set_natural_language_name(name, locale = @@default_locale)
  @natural_language_name[locale] = name
end

#set_original_heading(heading, locale = @@default_locale) ⇒ Object



228
229
230
# File 'lib/MESH/mesh.rb', line 228

def set_original_heading(heading, locale = @@default_locale)
  @original_heading[locale] = heading
end

#set_summary(summary, locale = @@default_locale) ⇒ Object



236
237
238
# File 'lib/MESH/mesh.rb', line 236

def set_summary(summary, locale = @@default_locale)
  @summary[locale] = summary
end

#shallowest_positionObject



198
199
200
201
202
# File 'lib/MESH/mesh.rb', line 198

def shallowest_position
  return nil if tree_numbers.empty?
  shallowest_tree_number = tree_numbers.min_by { |tn| tn.length }
  shallowest_tree_number.split('.').length
end

#sibling?(heading) ⇒ Boolean

Returns:

  • (Boolean)


187
188
189
190
# File 'lib/MESH/mesh.rb', line 187

def sibling?(heading)
  common_parents = parents & heading.parents
  !common_parents.empty?
end

#summary(locale = @@default_locale) ⇒ Object



19
20
21
# File 'lib/MESH/mesh.rb', line 19

def summary(locale = @@default_locale)
  return @summary[locale]
end