Class: Pubid::Nist::Identifiers::Base
- Inherits:
-
Identifier
- Object
- Lutaml::Model::Serializable
- Identifier
- Pubid::Nist::Identifiers::Base
- Defined in:
- lib/pubid/nist/identifiers/base.rb
Overview
Base NIST/NBS identifier class Each series type inherits from this and overrides series_code
Direct Known Subclasses
Circular, CommercialStandard, CommercialStandardEmergency, CommercialStandardsMonthly, CrplReport, FederalInformationProcessingStandards, GrantContractorReport, Handbook, InteragencyReport, LetterCircular, MiscellaneousPublication, Monograph, Ncstar, Nsrds, Owmwp, Report, SpecialPublication, TechnicalNote, SupplementIdentifier
Constant Summary collapse
- EQUALITY_IGNORED_ATTRS =
Attributes that are build artifacts or rendering aliases, not part of an identifier’s logical identity. They diverge between equally- valid spellings of the same id (e.g. long “Rev. 1” vs short “r1”):
- edition_component: redundant alias of :edition - first_number/second_number: decomposed parts of the canonical :number, retained from the parse for building - parsed_format: records the input format for round-trip rendering %i[ edition_component first_number second_number parsed_format ].freeze
Class Method Summary collapse
-
.typed_stages ⇒ Object
Default: no typed stages.
Instance Method Summary collapse
-
#==(other) ⇒ Object
(also: #eql?)
Logical identity comparison: equal when every attribute except the build artifacts/aliases above matches.
-
#edition_greater?(edition1, edition2) ⇒ Boolean
Helper to compare edition values numerically.
-
#exclude(*args) ⇒ Object
Return a copy with the named attributes nil’d.
-
#extract_edition_number(edition) ⇒ Integer?
Extract numeric value from edition (r3 -> 3, r5 -> 5, e2 -> 2).
- #hash ⇒ Object
-
#initialize(**attributes) ⇒ Base
constructor
A new instance of Base.
-
#language ⇒ Object
Backward compatibility: language method returns translation_component This allows tests to use parsed.language instead of parsed.translation_component.
-
#matches?(candidate) ⇒ Boolean
Wildcard / partial-identifier match.
-
#merge(document) ⇒ Base
Merge another document into this one Used for combining document data, preferring more specific values.
-
#publisher ⇒ String
Generate URN for this identifier.
-
#revision ⇒ String?
Compute revision from edition component for backward compatibility.
-
#series_code ⇒ Object
Default series_code — subclasses override to provide a normalized series name.
-
#supplement_short ⇒ Object
Short-form supplement fragment (“sup”, “sup1924”, “supJan1924”, “suprev”, “ supJun1925-Jun1926”), rendered from the structured component.
-
#to_s(format = nil) ⇒ Object
Generate identifier string in specified format.
-
#translation ⇒ Object
Backward compatibility: translation method returns translation_component This allows tests to use parsed.translation.language instead of parsed.translation_component.language.
-
#weight ⇒ Integer
Returns weight based on amount of defined attributes Used for ranking identifiers by specificity for conflict resolution.
Methods inherited from Identifier
#base_identifier, #mr_number, #mr_number_with_part, #mr_part, #mr_publisher, #mr_type, #mr_year, #new_edition_of?, polymorphic_name, #render, #resolve_urn_generator, #root, #to_mr_string, #to_supplement_s, #to_urn, #urn_supplement_type, #urn_type_code, #year
Constructor Details
#initialize(**attributes) ⇒ Base
Returns a new instance of Base.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/pubid/nist/identifiers/base.rb', line 74 def initialize(**attributes) super() attrs = self.class.attributes attributes.each do |key, value| next if value.nil? setter = :"#{key}=" public_send(setter, value) if attrs.key?(key) end # NOTE: Compound number building is handled by the Builder class # Do NOT build compound numbers here - let the builder apply special patterns first # See lib/pubid/nist/builder.rb lines 368-472 for compound number logic end |
Class Method Details
.typed_stages ⇒ Object
Default: no typed stages. Subclasses override as needed.
10 11 12 |
# File 'lib/pubid/nist/identifiers/base.rb', line 10 def self.typed_stages [] end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
Logical identity comparison: equal when every attribute except the build artifacts/aliases above matches. (Edition#== already ignores its rendering-only original_prefix.)
104 105 106 107 108 109 110 |
# File 'lib/pubid/nist/identifiers/base.rb', line 104 def ==(other) return false unless other.instance_of?(self.class) self.class.attributes.each_key.all? do |name| EQUALITY_IGNORED_ATTRS.include?(name) || send(name) == other.send(name) end end |
#edition_greater?(edition1, edition2) ⇒ Boolean
Helper to compare edition values numerically
272 273 274 275 276 |
# File 'lib/pubid/nist/identifiers/base.rb', line 272 def edition_greater?(edition1, edition2) num1 = extract_edition_number(edition1) num2 = extract_edition_number(edition2) num1 && num2 && num1 > num2 end |
#exclude(*args) ⇒ Object
Return a copy with the named attributes nil’d. Overrides Pubid::Identifier#exclude because NIST’s initialize is keyword-only (initialize(**attributes)) while the inherited exclude rebuilds via the positional self.class.new(attrs) form — passing a positional hash to a keyword-only initializer raises ArgumentError. Rebuild with the keyword splat instead.
151 152 153 154 155 156 157 158 159 |
# File 'lib/pubid/nist/identifiers/base.rb', line 151 def exclude(*args) excluded_args = args.dup excluded_args << :date if excluded_args.delete(:year) attrs = self.class.attributes.each_with_object({}) do |(name, _), h| h[name] = excluded_args.include?(name) ? nil : send(name) end self.class.new(**attrs) end |
#extract_edition_number(edition) ⇒ Integer?
Extract numeric value from edition (r3 -> 3, r5 -> 5, e2 -> 2)
280 281 282 283 284 285 286 |
# File 'lib/pubid/nist/identifiers/base.rb', line 280 def extract_edition_number(edition) # Handle both String and Edition component edition_str = edition.to_s # Match patterns like r3, r5, e2, etc. match = edition_str.match(/^[er]?(\d+)$/) match ? match[1].to_i : nil end |
#hash ⇒ Object
114 115 116 117 118 119 |
# File 'lib/pubid/nist/identifiers/base.rb', line 114 def hash vals = self.class.attributes.each_key.reject do |name| EQUALITY_IGNORED_ATTRS.include?(name) end.map { |name| send(name) } [self.class, *vals].hash end |
#language ⇒ Object
Backward compatibility: language method returns translation_component This allows tests to use parsed.language instead of parsed.translation_component
193 194 195 |
# File 'lib/pubid/nist/identifiers/base.rb', line 193 def language translation_component end |
#matches?(candidate) ⇒ Boolean
Wildcard / partial-identifier match. Treats self as a QUERY pattern and candidate as a concrete document: every ID part SET on the query must equal the candidate’s, while parts left unset (nil/empty) are wildcards that match any value. So a query carrying no edition and no supplement matches that document across ALL editions, years, and supplements — the basis for “select docs by ID parts”.
Asymmetric (unlike ==): “NBS CIRC 25”.matches?(“NBS CIRC 25sup1924”) is true, but not the reverse. The candidate must be the same class or a subclass so series-level identity still holds.
131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/pubid/nist/identifiers/base.rb', line 131 def matches?(candidate) return false unless candidate.is_a?(self.class) self.class.attributes.each_key.all? do |name| next true if EQUALITY_IGNORED_ATTRS.include?(name) query_val = public_send(name) next true if query_val.nil? || (query_val.respond_to?(:empty?) && query_val.empty?) query_val == candidate.public_send(name) end end |
#merge(document) ⇒ Base
Merge another document into this one Used for combining document data, preferring more specific values
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/pubid/nist/identifiers/base.rb', line 236 def merge(document) return self unless document.is_a?(Base) attrs = self.class.attributes attrs.each_key do |var_name| next if var_name == :rendering_style next if var_name == :parsed_format current_val = public_send(var_name) new_val = document.public_send(var_name) next unless new_val should_merge = case var_name when :publisher, :series, :number true when :edition current_val.nil? || edition_greater?(new_val, current_val) when :volume, :part, :version, :revision current_val.nil? || (new_val.to_s.length > current_val.to_s.length) when :supplement, :errata, :index, :insert, :section, :appendix, :translation true when :year, :month, :update, :draft true else false end public_send(:"#{var_name}=", new_val) if should_merge end self end |
#publisher ⇒ String
Generate URN for this identifier
18 |
# File 'lib/pubid/nist/identifiers/base.rb', line 18 attribute :publisher, Components::Publisher |
#revision ⇒ String?
Compute revision from edition component for backward compatibility
176 177 178 179 180 181 182 183 |
# File 'lib/pubid/nist/identifiers/base.rb', line 176 def revision return @revision if @revision # Compute from edition component if available if edition&.type && edition.id "#{edition.type}#{edition.id}" end end |
#series_code ⇒ Object
Default series_code — subclasses override to provide a normalized series name.
70 71 72 |
# File 'lib/pubid/nist/identifiers/base.rb', line 70 def series_code nil end |
#supplement_short ⇒ Object
Short-form supplement fragment (“sup”, “sup1924”, “supJan1924”, “suprev”, “ supJun1925-Jun1926”), rendered from the structured component. A present-but-empty component is the bare “sup” marker; a number-less date range gets the leading space the number would have supplied. Shared by base and the per-series to_short_style overrides.
166 167 168 169 170 171 172 |
# File 'lib/pubid/nist/identifiers/base.rb', line 166 def supplement_short return "" unless supplement prefix = (supplement.range? && !number) ? " " : "" rendered = supplement.to_s(:short) prefix + (rendered.empty? ? "sup" : rendered) end |
#to_s(format = nil) ⇒ Object
Generate identifier string in specified format
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/pubid/nist/identifiers/base.rb', line 199 def to_s(format = nil) # Handle both keyword argument (hash) and positional argument (symbol/string) format = format[:format] if format.is_a?(Hash) # Default to parsed_format if available (preserves input format on round-trip) # Falls back to :short format for output (normalization) # Explicit format parameter always overrides parsed_format effective_format = format || parsed_format&.to_sym || :short effective_format = effective_format.to_sym if effective_format.is_a?(String) case effective_format when :full, :long to_full_style when :abbreviated, :abbrev to_abbreviated_style when :short to_short_style when :mr to_mr_style else to_short_style end end |
#translation ⇒ Object
Backward compatibility: translation method returns translation_component This allows tests to use parsed.translation.language instead of parsed.translation_component.language
187 188 189 |
# File 'lib/pubid/nist/identifiers/base.rb', line 187 def translation translation_component end |
#weight ⇒ Integer
Returns weight based on amount of defined attributes Used for ranking identifiers by specificity for conflict resolution
225 226 227 228 229 230 |
# File 'lib/pubid/nist/identifiers/base.rb', line 225 def weight self.class.attributes.keys.inject(0) do |sum, key| val = public_send(key) val && !val.to_s.empty? ? sum + 1 : sum end end |