Skip to content

Commit

Permalink
Added changes to work with termium
Browse files Browse the repository at this point in the history
  • Loading branch information
HassanAkbar committed Nov 21, 2023
1 parent 915cf00 commit fde673d
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 13 deletions.
9 changes: 9 additions & 0 deletions lib/glossarist/concept.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class Concept < Model
# @return [String]
attr_reader :id

attr_writer :uuid

# Concept designations.
# @todo Alias +terms+ exists only for legacy reasons and will be removed.
# @return [Array<Designations::Base>]
Expand Down Expand Up @@ -64,6 +66,13 @@ def initialize(*args)
super
end

def uuid
@uuid ||= Glossarist::Utilities::UUID.uuid_v5(
Glossarist::Utilities::UUID::OID_NAMESPACE,
to_h.to_s,
)
end

def id=(id)
raise(Glossarist::Error, "Expect id to be a string, Got #{id.class} (#{id})") unless id.is_a?(String) || id.nil?

Expand Down
11 changes: 6 additions & 5 deletions lib/glossarist/concept_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ def load_concept_from_file(filename)
end

def load_localized_concept(id)
Config.class_for(:localized_concept).new(
Psych.safe_load(
File.read(localized_concept_path(id)),
permitted_classes: [Date],
),
concept_hash = Psych.safe_load(
File.read(localized_concept_path(id)),
permitted_classes: [Date],
)
concept_hash["uuid"] = id

Config.class_for(:localized_concept).new(concept_hash)
rescue Psych::SyntaxError => e
raise Glossarist::ParseError.new(filename: filename, line: e.line)
end
Expand Down
11 changes: 9 additions & 2 deletions lib/glossarist/managed_concept.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class ManagedConcept < Model

def initialize(attributes = {})
@localizations = {}
@localized_concepts = {}
@localized_concept_class = Config.class_for(:localized_concept)
@uuid_namespace = Glossarist::Utilities::UUID::OID_NAMESPACE

attributes = symbolize_keys(attributes)
@uuid = attributes[:uuid]
Expand All @@ -49,6 +51,10 @@ def initialize(attributes = {})
super(slice_keys(data, managed_concept_attributes))
end

def uuid
@uuid ||= Glossarist::Utilities::UUID.uuid_v5(@uuid_namespace, to_h.to_s)
end

def related=(related)
@related = related&.map { |r| RelatedConcept.new(r) }
end
Expand All @@ -72,7 +78,7 @@ def localized_concepts=(localized_concepts)
localized_concepts.each do |localized_concept|
lang = localized_concept["language_code"].to_s

@localized_concepts[lang] = SecureRandom.uuid
@localized_concepts[lang] = Glossarist::Utilities::UUID.uuid_v5(@uuid_namespace, localized_concept.to_h.to_s)

add_localization(
@localized_concept_class.new(localized_concept["data"] || localized_concept),
Expand Down Expand Up @@ -107,6 +113,7 @@ def localizations_hash
# @param localized_concept [LocalizedConcept]
def add_localization(localized_concept)
lang = localized_concept.language_code
@localized_concepts[lang] = @localized_concepts[lang] || localized_concept.uuid
localizations.store(lang, localized_concept)
end

Expand All @@ -125,7 +132,7 @@ def to_h
{
"data" => {
"identifier" => id,
"localized_concepts" => localized_concepts,
"localized_concepts" => localized_concepts.empty? ? nil : localized_concepts,
"groups" => groups,
}.compact,
}.compact
Expand Down
8 changes: 5 additions & 3 deletions lib/glossarist/managed_concept_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class ManagedConceptCollection

def initialize
@managed_concepts = {}
@managed_concepts_ids = {}
@concept_manager = ConceptManager.new
end

Expand Down Expand Up @@ -39,7 +40,7 @@ def each(&block)
# ManagedConcept ID
# @return [ManagedConcept, nil]
def fetch(id)
@managed_concepts[id]
@managed_concepts[id] || @managed_concepts[@managed_concepts_ids[id]]
end

alias :[] :fetch
Expand All @@ -55,13 +56,14 @@ def fetch_or_initialize(id)
fetch(id) or store(ManagedConcept.new(data: { id: id }))
end

# Adds concept to the collection. If collection contains a concept with
# Adds concept to the collection. If collection contains a concept with
# the same ID already, that concept is replaced.
#
# @param managed_concept [ManagedConcept]
# ManagedConcept about to be added
def store(managed_concept)
@managed_concepts[managed_concept.id] = managed_concept
@managed_concepts[managed_concept.uuid] = managed_concept
@managed_concepts_ids[managed_concept.id] = managed_concept.uuid if managed_concept.id
end

alias :<< :store
Expand Down
1 change: 1 addition & 0 deletions lib/glossarist/utilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
require_relative "utilities/enum"
require_relative "utilities/boolean_attributes"
require_relative "utilities/common_functions"
require_relative "utilities/uuid"
75 changes: 75 additions & 0 deletions lib/glossarist/utilities/uuid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# frozen_string_literal: true

# extracted from https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/digest/uuid.rb
# to generate uuid_v5 for concept files

require "securerandom"
require "openssl"

module Glossarist
module Utilities
module UUID
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:

# Generates a v5 non-random UUID (Universally Unique IDentifier).
#
# Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
# uuid_from_hash always generates the same UUID for a given name and namespace combination.
#
# See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
def self.uuid_from_hash(hash_class, namespace, name)
if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5
version = 3
elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1
version = 5
else
raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}."
end

uuid_namespace = pack_uuid_namespace(namespace)

hash = hash_class.new
hash.update(uuid_namespace)
hash.update(name)

ary = hash.digest.unpack("NnnnnN")
ary[2] = (ary[2] & 0x0FFF) | (version << 12)
ary[3] = (ary[3] & 0x3FFF) | 0x8000

"%08x-%04x-%04x-%04x-%04x%08x" % ary
end

# Convenience method for uuid_from_hash using OpenSSL::Digest::MD5.
def self.uuid_v3(uuid_namespace, name)
uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name)
end

# Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1.
def self.uuid_v5(uuid_namespace, name)
uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name)
end

# Convenience method for SecureRandom.uuid.
def self.uuid_v4
SecureRandom.uuid
end

def self.pack_uuid_namespace(namespace)
if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
namespace
else
match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)

raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?

match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
end
end

private_class_method :pack_uuid_namespace
end
end
end
13 changes: 10 additions & 3 deletions spec/unit/managed_concept_collection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@
end

describe "#fetch" do
it "returns a managed concept" do
managed_concept = Glossarist::ManagedConcept.new("data" => { id: "id" })
managed_concept_collection.store(managed_concept)
let(:managed_concept) { Glossarist::ManagedConcept.new("data" => { id: "id" }) }

it "fetches a managed concept by id" do
managed_concept_collection.store(managed_concept)
expect(managed_concept_collection.fetch("id")).to eq(managed_concept)
end

it "fetches a managed concept by uuid" do
managed_concept_collection.store(managed_concept)
uuid = Glossarist::Utilities::UUID.uuid_v5(Glossarist::Utilities::UUID::OID_NAMESPACE, managed_concept.to_h.to_s)

expect(managed_concept_collection.fetch(uuid)).to eq(managed_concept)
end
end

describe "#[]" do
Expand Down

0 comments on commit fde673d

Please sign in to comment.