relaton/relaton-bib

View on GitHub
lib/relaton_bib/contributor.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# frozen_string_literal: true

require "uri"

module RelatonBib
  # Address class.
  class Address
    # @return [Array<String>]
    attr_reader :street

    # @return [String, nil]
    attr_reader :city, :state, :country, :postcode, :formatted_address

    # @param street [Array<String>] streets
    # @param city [String, nil] city, should be present or formatted address provided
    # @param state [String, nil] state
    # @param country [String, nil] country, should be present or formatted address provided
    # @param postcode [String, nil] postcode
    # @param formatted_address [String, nil] formatted address, should be present or city and country provided
    def initialize(**args) # rubocop:disable Metrics/CyclomaticComplexity
      unless args[:formatted_address] || (args[:city] && args[:country])
        raise ArgumentError, "Either formatted address or city and country must be provided"
      end

      @street   = args[:street] || []
      @city     = args[:city]
      @state    = args[:state]
      @country  = args[:country]
      @postcode = args[:postcode]
      @formatted_address = args[:formatted_address] unless args[:city] && args[:country]
    end

    def ==(other)
      street == other.street && city == other.city && state == other.state &&
        country == other.country && postcode == other.postcode &&
        formatted_address == other.formatted_address
    end

    # @param doc [Nokogiri::XML::Document]
    def to_xml(doc) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
      doc.address do
        if formatted_address
          doc.formattedAddress formatted_address
        else
          street.each { |str| doc.street str }
          doc.city city
          doc.state state if state
          doc.country country
          doc.postcode postcode if postcode
        end
      end
    end

    # @return [Hash]
    def to_hash # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
      hash = { "address" => {} }
      if formatted_address
        hash["address"]["formatted_address"] = formatted_address
      else
        hash["address"]["street"] = street if street.any?
        hash["address"]["city"] = city
        hash["address"]["state"] = state if state
        hash["address"]["country"] = country
        hash["address"]["postcode"] = postcode if postcode
      end
      hash
    end

    # @param prefix [String]
    # @param count [Integer] number of addresses
    # @return [String]
    def to_asciibib(prefix = "", count = 1) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
      pref = prefix.empty? ? "address" : "#{prefix}.address"
      if formatted_address
        "#{pref}.formatted_address:: #{formatted_address}\n"
      else
        out = count > 1 ? "#{pref}::\n" : ""
        street.each { |st| out += "#{pref}.street:: #{st}\n" }
        out += "#{pref}.city:: #{city}\n"
        out += "#{pref}.state:: #{state}\n" if state
        out += "#{pref}.country:: #{country}\n"
        out += "#{pref}.postcode:: #{postcode}\n" if postcode
        out
      end
    end
  end

  # Contact class.
  class Contact
    # @return [String] allowed "phone", "email" or "uri"
    attr_reader :type

    # @return [String, nil]
    attr_reader :subtype

    # @return [String]
    attr_reader :value

    # @param type [String] allowed "phone", "email" or "uri"
    # @param subtype [String, nil] i.e. "fax", "mobile", "landline" for "phone"
    #                              or "work", "personal" for "uri" type
    # @param value [String]
    def initialize(type:, value:, subtype: nil)
      @type     = type
      @subtype  = subtype
      @value    = value
    end

    def ==(other)
      type == other.type && subtype == other.subtype && value == other.value
    end

    # @param builder [Nokogiri::XML::Document]
    def to_xml(builder)
      node = builder.send type, value
      node["type"] = subtype if subtype
    end

    # @return [Hash]
    def to_hash
      hash = { type => value }
      hash["type"] = subtype if subtype
      hash
    end

    # @param prefix [String]
    # @param count [Integer] number of contacts
    # @return [string]
    def to_asciibib(prefix = "", count = 1)
      pref = prefix.empty? ? prefix : "#{prefix}."
      out = count > 1 ? "#{pref}contact::\n" : ""
      out += "#{pref}contact.#{type}:: #{value}\n"
      out += "#{pref}contact.type:: #{subtype}\n" if subtype
      out
    end
  end

  # Affiliation.
  class Affiliation
    include RelatonBib

    # @return [RelatonBib::LocalizedString, nil]
    attr_reader :name

    # @return [RelatonBib::Organization]
    attr_reader :organization

    # @param organization [RelatonBib::Organization, nil]
    # @param name [RelatonBib::LocalizedString, nil]
    # @param description [Array<RelatonBib::FormattedString>]
    def initialize(organization: nil, name: nil, description: [])
      @name = name
      @organization = organization
      @description  = description
    end

    def ==(other)
      name == other.name && organization == other.organization && description == other.description
    end

    # @param opts [Hash]
    # @option opts [Nokogiri::XML::Builder] :builder XML builder
    # @option opts [String] :lang language
    def to_xml(**opts)
      return unless organization || name || description&.any?

      opts[:builder].affiliation do |builder|
        name_xml builder
        description_xml builder
        organization&.to_xml(**opts)
      end
    end

    def name_xml(builder)
      builder.name { name.to_xml builder } if name
    end

    def description_xml(builder)
      description.each { |d| builder.description { d.to_xml builder } }
    end

    #
    # Get description.
    #
    # @param [String, nil] lang language if nil return all description
    #
    # @return return [Array<RelatonBib::FormattedString>] description
    #
    def description(lang = nil)
      return @description unless lang

      @description.select { |d| d.language&.include? lang }
    end

    #
    # Render affiliation as hash.
    #
    # @return [Hash]
    #
    def to_hash
      hash = organization&.to_hash || {}
      hash["name"] = name.to_hash if name
      if description&.any?
        hash["description"] = single_element_array(description)
      end
      hash
    end

    # @param prefix [String]
    # @param count [Integer]
    # @return [String]
    def to_asciibib(prefix = "", count = 1) # rubocop:disable Metrics/AbcSize
      pref = prefix.empty? ? prefix : "#{prefix}."
      out = count > 1 ? "#{pref}affiliation::\n" : ""
      out += name.to_asciibib "#{pref}affiliation.name" if name
      description.each do |d|
        out += d.to_asciibib "#{pref}affiliation.description", description.size
      end
      out += organization.to_asciibib "#{pref}affiliation.*" if organization
      out
    end
  end

  # Contributor.
  class Contributor
    include RelatonBib

    # @return [URI]
    attr_reader :uri

    # @return [Array<RelatonBib::Address, RelatonBib::Contact>]
    attr_reader :contact

    # @param url [String, nil]
    # @param contact [Array<RelatonBib::Address, RelatonBib::Contact>]
    def initialize(url: nil, contact: [])
      @uri = URI url if url
      @contact = contact
    end

    def ==(other)
      uri == other.uri && contact == other.contact
    end

    # Returns url.
    # @return [String]
    def url
      @uri.to_s
    end

    # @params builder [Nokogiri::XML::Builder]
    def to_xml(builder)
      contact.each { |contact| contact.to_xml builder }
    end

    # @return [Hash]
    def to_hash
      hash = {}
      hash["url"] = uri.to_s if uri
      hash["contact"] = single_element_array(contact) if contact&.any?
      hash
    end

    # @param prefix [String]
    # @return [String]
    def to_asciibib(prefix = "") # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
      pref = prefix.empty? ? prefix : "#{prefix}."
      out = ""
      out += "#{pref}url:: #{uri}\n" if uri
      addr = contact.select { |c| c.is_a? RelatonBib::Address }
      addr.each { |ct| out += ct.to_asciibib prefix, addr.size }
      cont = contact.select { |c| c.is_a? RelatonBib::Contact }
      cont.each { |ct| out += ct.to_asciibib prefix, cont.size }
      out
    end
  end
end