lib/locabulary.rb in locabulary-0.2.0 vs lib/locabulary.rb in locabulary-0.3.0

- old
+ new

@@ -1,45 +1,14 @@ require 'date' require 'json' +require 'locabulary/exceptions' +require 'locabulary/items' # @since 0.1.0 module Locabulary - VERSION='0.2.0'.freeze DATA_DIRECTORY = File.expand_path("../../data/", __FILE__).freeze - class RuntimeError < ::RuntimeError - end - - class Item - include Comparable - ATTRIBUTE_NAMES = [:predicate_name, :term_label, :term_uri, :deposit_label, :description, :grouping, :affiliation, :default_presentation_sequence, :activated_on, :deactivated_on].freeze - - attr_reader(*ATTRIBUTE_NAMES) - - def initialize(attributes={}) - ATTRIBUTE_NAMES.each do |key| - value = attributes.fetch(key) { attributes.fetch(key.to_s, nil) } - send("#{key}=", value) - end - end - - SORT_SEQUENCE_FOR_NIL = 100_000_000 - def <=>(other) - value = presentation_sequence <=> other.presentation_sequence - return value unless value == 0 - term_label <=> other.term_label - end - - def presentation_sequence - default_presentation_sequence || SORT_SEQUENCE_FOR_NIL - end - - private - - attr_writer(*ATTRIBUTE_NAMES) - end - module_function # @api public # @since 0.1.0 # @@ -48,31 +17,74 @@ # about the local controlled vocabulary exposing a date that has expired. # When we next deploy the server changes, the deactivated will go away. def active_items_for(options = {}) predicate_name = options.fetch(:predicate_name) as_of = options.fetch(:as_of) { Date.today } + builder = Items.builder_for(predicate_name: predicate_name) active_cache[predicate_name] ||= begin - filename = filename_for_predicate_name(predicate_name: predicate_name) - json = JSON.parse(File.read(filename)) - predicate_name = json.fetch('predicate_name') - json.fetch('values').each_with_object([]) do |item_values, mem| - activated_on = Date.parse(item_values.fetch('activated_on')) - next unless activated_on < as_of - deactivated_on_value = item_values.fetch('deactivated_on', nil) - if deactivated_on_value.nil? - mem << Item.new(item_values.merge('predicate_name' => predicate_name)) - else - deactivated_on = Date.parse(deactivated_on_value) - next unless deactivated_on >= as_of - mem << Item.new(item_values.merge('predicate_name' => predicate_name)) - end - mem - end.sort + collector = [] + with_active_extraction_for(predicate_name, as_of) do |data| + collector << builder.call(data.merge('predicate_name' => predicate_name)) + end + collector.sort end end # @api public + # @since 0.2.0 + def active_hierarchical_root(options = {}) + predicate_name = options.fetch(:predicate_name) + as_of = options.fetch(:as_of) { Date.today } + builder = Items.builder_for(predicate_name: predicate_name) + active_hierarchical_root_cache[predicate_name] ||= begin + items = [] + hierarchy_graph_keys = {} + top_level_slugs = Set.new + with_active_extraction_for(predicate_name, as_of) do |data| + item = builder.call(data.merge('predicate_name' => predicate_name)) + items << item + top_level_slugs << item.root_slug + hierarchy_graph_keys[item.term_label] = item + end + associate_parents_and_childrens_for(hierarchy_graph_keys, items, predicate_name) + raise Exceptions::TooManyHierarchicalRootsError.new(predicate_name, top_level_slugs.to_a) if top_level_slugs.size > 1 + hierarchy_graph_keys.fetch(top_level_slugs.first) + end + end + + def associate_parents_and_childrens_for(hierarchy_graph_keys, items, predicate_name) + items.each do |item| + begin + hierarchy_graph_keys.fetch(item.parent_term_label).children << item unless item.parent_slugs.empty? + rescue KeyError => error + raise Exceptions::MissingHierarchicalParentError.new(predicate_name, error) + end + end + end + private :associate_parents_and_childrens_for + + def with_active_extraction_for(predicate_name, as_of) + filename = filename_for_predicate_name(predicate_name: predicate_name) + json = JSON.parse(File.read(filename)) + json.fetch('values').each do |data| + yield(data) if data_is_active?(data, as_of) + end + end + private :with_active_extraction_for + + def data_is_active?(data, as_of) + activated_on = Date.parse(data.fetch('activated_on')) + return false unless activated_on < as_of + deactivated_on_value = data.fetch('deactivated_on', nil) + return true if deactivated_on_value.nil? + deactivated_on = Date.parse(deactivated_on_value) + return false unless deactivated_on >= as_of + true + end + private :data_is_active? + + # @api public # @since 0.1.0 def active_label_for_uri(options = {}) predicate_name = options.fetch(:predicate_name) term_uri = options.fetch(:term_uri) object = active_items_for(predicate_name: predicate_name).detect { |obj| obj.term_uri == term_uri } @@ -85,62 +97,29 @@ def active_labels_for(options = {}) predicate_name = options.fetch(:predicate_name) active_items_for(predicate_name: predicate_name).map(&:term_label) end - - # @api public - def active_nested_labels_for(options = {}) - format_active_items_for(active_labels_for(options)) - end - - # @api public - def properties_for_uri(options = {}) - predicate_name = options.fetch(:predicate_name) - term_uri = options.fetch(:term_uri) - object = active_items_for(predicate_name: predicate_name).detect { |obj| obj.term_uri == term_uri } - return object. if object - term_uri - end - # @api private def filename_for_predicate_name(options = {}) predicate_name = options.fetch(:predicate_name) filename = File.join(DATA_DIRECTORY, "#{File.basename(predicate_name)}.json") return filename if File.exist?(filename) - raise Locabulary::RuntimeError, "Unable to find predicate_name: #{predicate_name}" + raise Locabulary::Exceptions::RuntimeError, "Unable to find predicate_name: #{predicate_name}" end # @api private def active_cache @active_cache ||= {} end # @api private - def reset_active_cache! - @active_cache = nil + def active_hierarchical_root_cache + @active_hierarchical_root_cache ||= {} end # @api private - def format_active_items_for(items) - root = {} - items.each do |item| - key, value = build_key_and_value(item) - root[key] ||= [] - root[key] << value - end - root - end - - # @api private - def build_key_and_value(text) - text_array = text.split(/(::)/) - return text, text if text_array.size == 1 - return text, text_array.last if text_array.size == 3 - key = "" - (0..(text_array.size-3)).each do |index| - key << text_array[index] - end - value = text_array.last - return key, value + def reset_active_cache! + @active_cache = nil + @active_hierarchical_root_cache = nil end end