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) ⇒ 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
# 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)

  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
end

Instance Attribute Details

#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 }]



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

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



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

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



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

def get_amendment_class
  Amendment
end

.get_corrigendum_classObject



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

def get_corrigendum_class
  Corrigendum
end

.get_identifierObject



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

def get_identifier
  Identifier
end

.get_renderer_classObject



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

def get_renderer_class
  Renderer::Base
end

.get_transformer_classObject



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

def get_transformer_class
  Transformer
end

.get_update_codesHash?

Returns replacement patterns.

Returns:

  • (Hash, nil)

    replacement patterns



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

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



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

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



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

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:



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

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



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

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



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

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)


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

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

.update_old_code(code) ⇒ Object



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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

#to_yamlObject



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

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

#typed_stage_abbrevObject



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

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”



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

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



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

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