Class: Pubid::Core::Identifier::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/pubid/core/identifier/base.rb

Constant Summary collapse

TYPED_STAGES =
{}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(publisher:, number:, copublisher: nil, part: nil, year: nil, edition: nil, language: nil, amendments: nil, corrigendums: nil, stage: nil, all_parts: false) ⇒ Base

Creates new identifier from options provided:

Parameters:

  • publisher (String)

    document’s publisher, eg. “ISO”

  • copublisher (String, Array<String>) (defaults to: nil)

    document’s copublisher, eg. “IEC”

  • number (Integer)

    document’s number, eg. “1234”

  • part (String) (defaults to: nil)

    document’s part and subparts, eg. “1”, “1-1A”, “2-3D”

  • type (String)

    document’s type, eg. “TR”, “TS”

  • year (Integer) (defaults to: nil)

    document’s year, eg. “2020”

  • edition (Integer) (defaults to: nil)

    document’s edition, eg. “1”

  • language (String) (defaults to: nil)

    document’s translation language (available languages: “ru”, “fr”, “en”, “ar”)

  • amendments (Array<Amendment>, Array<Hash>) (defaults to: nil)

    document’s amendments

  • corrigendums (Array<Corrigendum>, Array<Hash>) (defaults to: nil)

    document’s corrigendums

See Also:



23
24
25
26
27
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
# File 'lib/pubid/core/identifier/base.rb', line 23

def initialize(publisher:, number:, copublisher: nil, part: nil,
               year: nil, edition: nil, language: nil, amendments: nil,
               corrigendums: nil, stage: nil, all_parts: false)

  if amendments
    @amendments = amendments.map do |amendment|
      if amendment.is_a?(Hash)
        self.class.get_transformer_class.new.apply(:amendments => [amendment])[:amendments].first
      else
        amendment
      end
    end
  end

  if corrigendums
    @corrigendums = corrigendums.map do |corrigendum|
      if corrigendum.is_a?(Hash)
        self.class.get_transformer_class.new.apply(:corrigendums => [corrigendum])[:corrigendums].first
      else
        corrigendum
      end
    end
  end

  @publisher = publisher.to_s
  @number = number&.to_s
  @copublisher = copublisher if copublisher
  @part = part.to_s if part
  @year = year.to_i if year
  @edition = edition.to_i if edition
  @language = language.to_s if language

  @stage = resolve_stage(stage) if stage
  @all_parts = all_parts if all_parts
end

Instance Attribute Details

#all_partsObject

Returns the value of attribute all_parts.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def all_parts
  @all_parts
end

#amendmentsObject

Returns the value of attribute amendments.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def amendments
  @amendments
end

#copublisherObject

Returns the value of attribute copublisher.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def copublisher
  @copublisher
end

#corrigendumsObject

Returns the value of attribute corrigendums.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def corrigendums
  @corrigendums
end

#editionObject

Returns the value of attribute edition.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def edition
  @edition
end

#languageObject

Returns the value of attribute language.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def language
  @language
end

#numberObject

Returns the value of attribute number.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def number
  @number
end

#partObject

Returns the value of attribute part.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def part
  @part
end

#publisherObject

Returns the value of attribute publisher.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def publisher
  @publisher
end

#stageObject

Returns the value of attribute stage.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def stage
  @stage
end

#yearObject

Returns the value of attribute year.



4
5
6
# File 'lib/pubid/core/identifier/base.rb', line 4

def year
  @year
end

Class Method Details

.array_to_hash(params) ⇒ Object

Converts array of hashes into single hash array like [{ publisher: “ISO” }, { number: 1 }] to hash { publisher: “ISO”, number: 1 }

Parameters:

  • params (Array<Hash>)

    input array of hashes, eg. [{ a: 1 }, { b: 2 }]



217
218
219
220
221
222
223
224
225
# File 'lib/pubid/core/identifier/base.rb', line 217

def array_to_hash(params)
  params.inject({}) do |r, i|
    result = r
    i.each do |k, v|
      result = result.merge(k => r.key?(k) ? [r[k], v].flatten : v)
    end
    result
  end
end

.find_typed_stage(typed_stage) ⇒ [Symbol, Stage]

Returns typed stage and stage with assigned harmonized codes.

Parameters:

  • typed_stage (String, Symbol)

    eg. “DTR” or :dtr

Returns:

  • ([Symbol, Stage])

    typed stage and stage with assigned harmonized codes



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/pubid/core/identifier/base.rb', line 309

def find_typed_stage(typed_stage)
  if typed_stage.is_a?(Symbol)
    return get_identifier
        .build_typed_stage(
          harmonized_code:
            get_identifier.build_harmonized_stage_code(self::TYPED_STAGES[typed_stage][:harmonized_stages]),
          abbr: typed_stage,
        )
  end

  typed_stage = self::TYPED_STAGES.find do |_, v|
    if v[:abbr].is_a?(Hash)
      v[:abbr].value?(typed_stage)
    elsif v.key?(:legacy_abbr)
      v[:legacy_abbr].include?(typed_stage) || v[:abbr] == typed_stage
    else
      v[:abbr] == typed_stage
    end
  end

  get_identifier.build_typed_stage(harmonized_code:
                               get_identifier.build_harmonized_stage_code(typed_stage[1][:harmonized_stages]),
                             abbr: typed_stage.first)
end

.get_amendment_classObject



250
251
252
# File 'lib/pubid/core/identifier/base.rb', line 250

def get_amendment_class
  Amendment
end

.get_corrigendum_classObject



254
255
256
# File 'lib/pubid/core/identifier/base.rb', line 254

def get_corrigendum_class
  Corrigendum
end

.get_identifierObject



271
272
273
# File 'lib/pubid/core/identifier/base.rb', line 271

def get_identifier
  Identifier
end

.get_renderer_classObject



258
259
260
# File 'lib/pubid/core/identifier/base.rb', line 258

def get_renderer_class
  Renderer::Base
end

.get_transformer_classObject



262
263
264
# File 'lib/pubid/core/identifier/base.rb', line 262

def get_transformer_class
  Transformer
end

.get_update_codesHash?

Returns replacement patterns.

Returns:

  • (Hash, nil)

    replacement patterns



267
268
269
# File 'lib/pubid/core/identifier/base.rb', line 267

def get_update_codes
  nil
end

.has_type?(type) ⇒ Boolean

Returns true if provided type matches with identifier’s class type.

Parameters:

  • type (Symbol, String)

    eg. :tr, :ts, “TS”

Returns:

  • (Boolean)

    true if provided type matches with identifier’s class type



244
245
246
247
248
# File 'lib/pubid/core/identifier/base.rb', line 244

def has_type?(type)
  return type == self.type[:key] if type.is_a?(Symbol)

  self.type.key?(:values) ? self.type[:values].include?(type) : type.to_s.downcase.to_sym == self.type[:key]
end

.has_typed_stage?(typed_stage) ⇒ Boolean

Returns true when identifier has associated typed stage.

Parameters:

  • typed_stage (String, Symbol)

    typed stage, eg. “DTR” or :dtr

Returns:

  • (Boolean)

    true when identifier has associated typed stage



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/pubid/core/identifier/base.rb', line 286

def has_typed_stage?(typed_stage)
  return self::TYPED_STAGES.key?(typed_stage) if typed_stage.is_a?(Symbol)

  self::TYPED_STAGES.any? do |_, v|
    if v[:abbr].is_a?(Hash)
      v[:abbr].value?(typed_stage)
    else
      if v.key?(:legacy_abbr)
        v[:legacy_abbr].include?(typed_stage) || v[:abbr] == typed_stage
      else
        v[:abbr] == typed_stage
      end
    end
  end
end

.parse(code_or_params) ⇒ Pubid::Core::Identifier

Parses given identifier

Parameters:

  • code_or_params (String, Hash)

    code or hash from parser eg. “ISO 1234”, { }

Returns:



206
207
208
209
210
211
212
# File 'lib/pubid/core/identifier/base.rb', line 206

def parse(code_or_params)
  params = code_or_params.is_a?(String) ?
             get_parser_class.new.parse(update_old_code(code_or_params)) : code_or_params
  transform(params.is_a?(Array) ? array_to_hash(params) : params)
rescue Parslet::ParseFailed => failure
  raise Errors::ParseError, "#{failure.message}\ncause: #{failure.parse_failure_cause.ascii_tree}"
end

.resolve_typed_stage(harmonized_code) ⇒ Symbol?

Resolve typed stage using stage harmonized stage code

Parameters:

Returns:

  • (Symbol, nil)

    typed stage or nil



337
338
339
340
341
342
343
344
# File 'lib/pubid/core/identifier/base.rb', line 337

def resolve_typed_stage(harmonized_code)
  self::TYPED_STAGES.each do |k, v|
    if (v[:harmonized_stages] & harmonized_code.stages) == harmonized_code.stages
      return get_identifier.build_typed_stage(abbr: k, harmonized_code: harmonized_code)
    end
  end
  nil
end

.transform(params) ⇒ Object

Transform parameters hash or array or hashes to identifier



228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/pubid/core/identifier/base.rb', line 228

def transform(params)
  # run transform through each element,
  # like running transformer.apply(number: 1) and transformer.apply(year: 1999)
  # instead of running transformer on whole hash, like running transformer.apply({ number: 1, year: 1999 })
  # where rule for number or year only will be not applied
  # transformation only applied to rules matching the whole hash

  identifier_params = params.map do |k, v|
                        get_transformer_class.new.apply(k => v).to_a.first
                      end.to_h

  new(**identifier_params)
end

.type_match?(parameters) ⇒ Boolean

Returns true when identifier’s type match with provided parameters

Returns:

  • (Boolean)


303
304
305
# File 'lib/pubid/core/identifier/base.rb', line 303

def type_match?(parameters)
  parameters[:type] ? has_type?(parameters[:type]) : has_typed_stage?(parameters[:stage])
end

.update_old_code(code) ⇒ Object



275
276
277
278
279
280
281
282
# File 'lib/pubid/core/identifier/base.rb', line 275

def update_old_code(code)
  return code unless get_update_codes

  get_update_codes.each do |from, to|
    code = code.gsub(from.match?(/^\/.*\/$/) ? Regexp.new(from[1..-2]) : /^#{Regexp.escape(from)}$/, to)
  end
  code
end

Instance Method Details

#==(other) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/pubid/core/identifier/base.rb', line 92

def ==(other)
  case other
  when String
    to_s == other
  when Identifier::Base
    to_h == other.to_h
  when Hash
    to_h == other
  else
    raise Errors::WrongTypeError, "cannot compare with #{other.class} type"
  end
end

#exclude(*args) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/pubid/core/identifier/base.rb', line 110

def exclude(*args)
  nested_exclusions, top_level_exclusions = args.partition { |arg| arg.is_a?(Hash) }

  nested_exclusions = nested_exclusions.reduce({}, :merge)

  excluded_hash = to_h(add_type: false)
    .reject { |k, v| top_level_exclusions.include?(k) }
    .each_with_object({}) do |(k, v), memo|
      memo[k] = if v.is_a?(Hash) && nested_exclusions.key?(k)
                  v.reject { |key, _| nested_exclusions[k].include?(key) }
                else
                  v
                end
    end

  self.class.new(**excluded_hash)
end

#new_edition_of?(other) ⇒ Boolean

Checks if another identifier is newer edition of the same document

Parameters:

Returns:

  • (Boolean)

    true if another identifier is newer edition



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/pubid/core/identifier/base.rb', line 174

def new_edition_of?(other)
  if exclude(:year, :edition) != other.exclude(:year, :edition)
    raise Errors::AnotherDocumentError, "cannot compare edition with #{other}"
  end

  if year.nil? || other.year.nil?
    raise Errors::CannotCompareError, "cannot compare identifier without edition year"
  end

  if year == other.year && (edition || other.edition)
    return false if other.edition.nil?

    return true if edition.nil?

    return edition > other.edition
  end

  year > other.year
end

#resolve_stage(stage) ⇒ [nil, Stage], [Symbol, Stage]

Returns typed stage and stage values.

Parameters:

  • stage (Stage, Symbol, String)

    stage or typed stage, e.g. “PWI”, “NP”, “50.00”, Stage.new(abbr: :WD), “DTR”

Returns:

  • ([nil, Stage], [Symbol, Stage])

    typed stage and stage values



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/pubid/core/identifier/base.rb', line 147

def resolve_stage(stage)
  if stage.is_a?(Stage)
    return self.class.resolve_typed_stage(stage.harmonized_code) || stage unless stage.abbr

    return stage
  end

  if self.class.has_typed_stage?(stage)
    return self.class.find_typed_stage(stage)
  end

  parsed_stage = self.class.get_identifier.parse_stage(stage)
  # resolve typed stage when harmonized code provided as stage
  # or stage abbreviation was not resolved
  if /\A[\d.]+\z/.match?(stage) || parsed_stage.empty_abbr?(with_prf: true)
    return self.class.resolve_typed_stage(parsed_stage.harmonized_code) || parsed_stage
  end

  parsed_stage

  # from IEC
  # @typed_stage = self.class::TYPED_STAGES[@typed_stage][:abbr] if @typed_stage
end

#rootObject

returns root identifier



195
196
197
198
199
# File 'lib/pubid/core/identifier/base.rb', line 195

def root
  return base.base if base&.class&.method_defined?(:base) && base&.base

  base || self
end

#to_h(deep: true, add_type: true) ⇒ Hash

Returns Identifier’s parameters.

Returns:

  • (Hash)

    Identifier’s parameters



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/pubid/core/identifier/base.rb', line 65

def to_h(deep: true, add_type: true)
  result = instance_variables.map do |var|
    value = instance_variable_get(var)

    [var.to_s.gsub("@", "").to_sym,
     if value.is_a?(Array)
       value.map { |v| (v.respond_to?(:to_h) && deep) ? v.to_h : v }
     elsif value.nil?
       nil
     else
       (value.respond_to?(:to_h) && deep) ? value.to_h : value
     end
    ]
  end.to_h

  if add_type && respond_to?(:type) && type[:short]
    result[:type] = type[:short]
  end

  result.reject { |k, v| k != :number && v.nil? }
end

#to_sObject

Render identifier using default renderer



106
107
108
# File 'lib/pubid/core/identifier/base.rb', line 106

def to_s
  self.class.get_renderer_class.new(to_h(deep: false)).render
end

#to_yamlObject



87
88
89
90
# File 'lib/pubid/core/identifier/base.rb', line 87

def to_yaml
  # use #to_h for serialization to avoid !ruby/object in output
  to_h.to_yaml
end

#typed_stage_abbrevObject



128
129
130
131
132
133
134
# File 'lib/pubid/core/identifier/base.rb', line 128

def typed_stage_abbrev
  if stage.is_a?(TypedStage)
    return stage.to_s
  end

  stage ? "#{stage.abbr} #{self.class.type[:key].to_s.upcase}" : self.class.type[:key].to_s.upcase
end

#typed_stage_nameObject

Return typed stage name, eg. “Final Draft Technical Report” for “FDTR”



137
138
139
140
141
142
143
# File 'lib/pubid/core/identifier/base.rb', line 137

def typed_stage_name
  if stage.is_a?(TypedStage) && self.class::TYPED_STAGES.key?(stage.abbr)
    return self.class::TYPED_STAGES[stage.abbr][:name]
  end

  stage ? "#{stage.name} #{self.class.type[:title]}" : self.class.type[:title]
end

#urnString

Returns Rendered URN identifier.

Returns:

  • (String)

    Rendered URN identifier



60
61
62
# File 'lib/pubid/core/identifier/base.rb', line 60

def urn
  Renderer::Urn.new(to_h).render
end