module PIM::DataModel

Constants

AVAILABLE_OPTIONS

Attributes

default_managing_user_roles[R]
managing_user_roles_block[R]

Public Instance Methods

add_item_to_hierarchy(hierarchy, primary_key, status, opts = {}) click to toggle source

Adds all contained items to a hierarchy. Return true, if all items in the hierarchy exist and are valid, false otherwise. Should not be overridden in data model!

# File pim.rb, line 2759
def add_item_to_hierarchy hierarchy, primary_key, status, opts = {}

  # Hierarchy is complete, if the same item was already added
  if hierarchy.include?(primary_key)
    return true
  end

  contained_items = get_contained_items(primary_key, opts)
  return false if contained_items.nil?

  # Add item with status to hierarchy
  add_to_collection hierarchy, primary_key, status

  # Iterate over contained items and add them to hierarchy
  contained_items.each do |contained_item_primary_key, status|
    return false if not add_item_to_hierarchy(hierarchy, contained_item_primary_key, status, opts)
  end

  return true

end
attribute_rules_as_json() click to toggle source
# File pim.rb, line 2492
def attribute_rules_as_json
  attribute_rules_as_json = {}
  all_attributes.each_value do |attribute|
    attribute_rules_as_json[attribute.name] = all_rules_by_attribute(attribute.name).keys
    attribute_rules_as_json[attribute.name] += all_rules_by_params(*attribute.params.keys).keys
  end
  attribute_rules_as_json
end
attribute_to_xml(attribute, values, parent_node, no_ignore = false) click to toggle source
# File pim.rb, line 2797
def attribute_to_xml attribute, values, parent_node, no_ignore = false

  attribute = attribute(attribute) unless attribute.is_a?(PIM::Attribute)

  # Only write out known/existing attributes
  return if attribute.nil?

  # Don't write out meta/system attributes
  return if attribute.is_meta_attribute?

  # Ignore attribute, if specified
  return if not no_ignore and attribute.param(:xml_ignore)

  value = PIM.get_value(values, attribute.name)
  context = XmlContext.new(
    parent_node: parent_node,
    attribute: attribute,
    value: value,
    values: values
  )

  to_xml = attribute.param(:to_xml) || PIM::Attributes::DEFAULT_TO_XML
  begin
    to_xml.call(context, XmlUtils)
  rescue Exception => e
    log_error("Error in to_xml of attribute '#{attribute.name}'", e)
    raise e
  end
end
attributes_to_xml(attributes, values, parent_node) click to toggle source
# File pim.rb, line 2827
def attributes_to_xml attributes, values, parent_node

  return if PIM.is_empty?(attributes)

  parent_node ||= XmlUtils.create_xml_document

  attributes.each do |attribute|
    attribute_to_xml(attribute, values, parent_node)
  end

  return parent_node

end
cache() click to toggle source
# File pim.rb, line 2465
def cache
  PIM.__fiber_local_cache(:__data_model_cache)
end
contact_attributes(*attributes) click to toggle source
# File pim.rb, line 2409
def contact_attributes *attributes
  @contact_attributes ||= []
  if attributes
    attributes.each do |attribute|
      check_attribute!(attribute, "Attribute '#{attribute}' used as contact attribute is not defined")
    end
    @contact_attributes.concat(attributes)
  end
  @contact_attributes
end
create_xml_item(values, parent_node = nil) click to toggle source
# File pim.rb, line 2846
def create_xml_item values, parent_node = nil

  parent_node ||= XmlUtils.create_xml_document

  return PIM::Utils.timed(group: "validation",
                          key: "create_xml_item",
                          message: "Creating XmlItem from values") do

    category_name = PIM.get_value(values, 'category__')
    if PIM.is_empty?(category_name)

      category = PIM::Item
      xml = attributes_to_xml(all_attributes.values, values, parent_node)

    else

      category = category(category_name)
      raise "Category '#{category_name}' does not exist" if category.nil?
      xml = category.to_xml(values, parent_node)

    end

    primary_key = PIM.get_value(values, :primaryKey__)
    category.create_xml_item(xml, category: category.name, primary_key: primary_key, values: values)

  end
end
dashboard_attributes(*attributes) click to toggle source
# File pim.rb, line 2431
def dashboard_attributes *attributes
  @dashboard_attributes ||= []
  if attributes
    attributes.each do |attribute|
      check_attribute!(attribute, "Attribute '#{attribute}' used as dashboard attribute is not defined")
    end
    @dashboard_attributes.concat(attributes)
  end
  @dashboard_attributes
end
data_model_as_json() click to toggle source
# File pim.rb, line 2501
def data_model_as_json
  return {
    :name => self.to_s,
    :label => (@label || self.to_s),
    :optionLists => option_lists_as_json,
    :attributes => attributes_as_json,
    :categories => categories_as_json,
    :layouts => layouts_as_json,
    :organizationAttributes => organization_attributes,
    :userAttributes => user_attributes,
    :contactAttributes => contact_attributes,
    :reviewAttributes => review_attributes,
    :dashboardAttributes => dashboard_attributes,
    :mappingFunctions => mapping_functions_as_json,
    :communicationPlans => communication_plans_as_json,
    :filters => filters_as_json,
    :roles => roles_as_json,
    :attributeRules => attribute_rules_as_json,
    :taskTags => task_tags,
    :inPlatformPublication => as_json(in_platform_publication)
  }
end
data_model_definitions_yaml(location = nil) click to toggle source
# File pim.rb, line 2289
def data_model_definitions_yaml location = nil
  if location

    if @data_model_definitions_yaml
      raise "Data model definitions YAML location for data model '#{self.to_s}' is already defined"
    end

    extname = File.extname(location)
    if extname != '.json'

      # Use JSON definition file if it exists
      basename = File.basename(location, '.*')
      json_location = File.expand_path(basename + '.json', File.dirname(location))
      location = json_location if File.readable?(json_location)

    end

    @data_model_definitions_yaml = location

  end
  @data_model_definitions_yaml
end
data_model_manager(&block) click to toggle source
# File pim.rb, line 2258
def data_model_manager &block
  data_model_manager = DataModelManager.new data_module
  if block
    data_model_manager.instance_eval(&block)
  end
end
data_model_opts(options = nil)
Alias for: options
data_module() click to toggle source
# File pim.rb, line 2265
def data_module
  self
end
extension(type, &block) click to toggle source
# File pim.rb, line 2331
def extension type, &block
  @extensions ||= {}
  @extensions[type] ||= []
  @extensions[type] << block
end
extension_point(type) click to toggle source
# File pim.rb, line 2337
def extension_point type
  unless @extensions.nil? or @extensions.empty?
    blocks = @extensions[type]
    return false if blocks.nil? or blocks.empty?
    blocks.each do |block|
      self.instance_eval &block
    end
    true
  end
end
find_first_in_parent_modules(&block) click to toggle source
# File pim.rb, line 2484
def find_first_in_parent_modules &block
  ([self] | parent_modules).each do |m|
    value = block.call(m)
    return value unless value.nil?
  end
  return nil
end
get_attribute_options_validator(attribute, param_name, option_groups_param_name = nil) click to toggle source
# File pim.rb, line 2874
def get_attribute_options_validator attribute, param_name, option_groups_param_name = nil

  options = attribute.param(to_sym(param_name))
  option_groups = attribute.param(to_sym(option_groups_param_name)) unless option_groups_param_name.nil?

  if PIM.is_symbol_or_string?(options)

    option_list = option_list(options)
    raise "Option list '#{options}' referenced in param '#{param_name}' of attribute '#{attribute.name}' does not exist" if option_list.nil?

    if !PIM.is_empty?(option_list.service)

      lambda do |option|

        # Load option via data model service and check if option includes any expected group
        data_model_option = PIM::Services::DataModelService.get_data_model_options(option_list.service, option_list.name, option).first
        return !data_model_option.nil? && data_model_option.contains_any_group(*PIM.stringify(option_groups))

      end

    else

      lambda do |option|
        return option_list.includes_option?(option, *option_groups)
      end

    end

  else

    lambda do |option|
      return PIM.includes_value?(option, options)
    end

  end

end
get_contained_items(primary_key, opts = {}) click to toggle source

Return a list of items directly contained in an item. Return nil, if item itself does not exist or is not valid. Should be overridden in data model! Default implementation: Just return an empty list.

# File pim.rb, line 2726
def get_contained_items primary_key, opts = {}
  return []
end
get_converter(attribute, converter_param) click to toggle source
# File pim.rb, line 2657
def get_converter attribute, converter_param

  return nil if attribute.nil?

  converter = attribute.param(converter_param)
  return nil if converter.nil?

  if not converter.respond_to?(:call)

    # If converter was specified as String, we expect it to contain the module and the method
    converter_module, converter_method = converter.to_s.split('.')
    converter_module = PIM.constantize(converter_module)
    converter = converter_module.method(PIM.to_sym(converter_method))

  end

  if converter.arity == 0
    PIM.log_error("Converter '#{converter_param}' for attribute '#{attribute.name}' has no arguments and will be ignored")
    converter = nil
  end

  converter

end
get_data_model_opt(option, default = false)
Alias for: get_option
get_item_hierarchy(primary_key, opts = {}) click to toggle source

Return a hierarchical list of items which are contained in an item. Return nil, if any item in the hierarchy does not exist or is not valid. Should not be overridden in data model!

# File pim.rb, line 2733
def get_item_hierarchy primary_key, opts = {}

  # For compatibility with old data model which (illegally!) overwrite 'add_item_hierarchy'
  if self.respond_to?(:add_item_hierarchy)

    # Old method only works with hierachy Array
    hierarchy = []
    has_hierarchy = add_item_hierarchy(primary_key, hierarchy, opts)

  else

    # Create hierarchy either as Array or Hash, depending on 'status'
    status = PIM.get_value(opts, :status)
    hierarchy = status.nil? ? [] : {}

    has_hierarchy = add_item_to_hierarchy(hierarchy, primary_key, status, opts)

  end

  return has_hierarchy ? hierarchy : nil

end
get_item_value(item, attribute) click to toggle source

Convenience method to return the attribute value for either a Java item, a Hash or a PIM::Item This method should be used, if the origin of the item is unknown.

# File pim.rb, line 2684
def get_item_value item, attribute
  return nil if attribute.nil?
  attribute_name = (attribute.is_a?(PIM::Attribute) ? attribute.name : attribute).to_s
  if item.is_a?(PIM::Item)
    return item[attribute_name]
  elsif is_hash?(item)
    return (item[attribute_name.to_sym] || item[attribute_name])
  else
    return nil
  end
end
get_option(option, default = false) click to toggle source
# File pim.rb, line 2321
def get_option option, default = false
  return PIM::Utils.get_opt(@options, option, default)
end
Also aliased as: get_data_model_opt
get_top_level_items(primary_keys, opts = {}) click to toggle source

Return a list of unique top level items for a list of items. Should be overridden in data model! Default implementation: Just return the specified primary keys. NOTE: This method ONLY returns ‘valid’ items, i.e. the data model MUST override it if it wants to allow the publication to differentiate between valid, invalid and skipped items!

# File pim.rb, line 2702
def get_top_level_items primary_keys, opts = {}

  item_cache = opts[:item_cache]
  only_valid = opts[:only_valid]
  top_level_items = Set.new

  primary_keys.each do |primary_key|

    # Ignore item if it is not "compliant"
    item = load_item(primary_key, item_cache)
    next if not item or (only_valid and not item.is_validation_compliant)

    top_level_items << primary_key

  end

  return top_level_items

end
has_additional_attribute?(item, additional_module, additional_category, additional_attribute) click to toggle source

Checks if an additional attribute should actually be added to a category. Should be overridden in data model! Default implementation: All additional attributes will added to any category

# File pim.rb, line 2793
def has_additional_attribute? item, additional_module, additional_category, additional_attribute
  true
end
has_data_model_opt?(option, default = false)
Alias for: has_option?
has_initialization_point?(type) click to toggle source
# File pim.rb, line 2366
def has_initialization_point? type
  initialization = PIM.get_value(@initializations, type)
  return !initialization.nil?
end
has_option?(option, default = false) click to toggle source
# File pim.rb, line 2326
def has_option? option, default = false
  return PIM::Utils.is_opt?(@options, option, default)
end
Also aliased as: has_data_model_opt?
in_platform_publication(&block) click to toggle source
# File pim.rb, line 2455
def in_platform_publication &block
  unless block.nil?

    raise "In-Platform-Publication already defined" unless @in_platform_publication.nil?
    @in_platform_publication = InPlatformPublication.build(&block)

  end
  @in_platform_publication
end
initialization(type, &block) click to toggle source
# File pim.rb, line 2348
def initialization type, &block
  @initializations ||= {}
  @initializations[type] ||= []
  @initializations[type] << block
end
initialization_point(type) click to toggle source
# File pim.rb, line 2354
def initialization_point type
  unless @initializations.nil? or @initializations.empty?
    blocks = @initializations[type]
    return false if blocks.nil? or blocks.empty?
    while !blocks.empty? do
      block = blocks.shift
      self.instance_eval &block
    end
    true
  end
end
item_cache(type: item_caches = PIM.__fiber_local_cache(:__item_caches)) click to toggle source
# File pim.rb, line 2469
def item_cache type:
  item_caches = PIM.__fiber_local_cache(:__item_caches)
  item_caches[type] ||= LRUCache.new
  item_caches[type]
end
label(label = nil) click to toggle source
# File pim.rb, line 2269
def label label = nil
  if label
    if @label
      raise "Label for data model '#{self.to_s}' is already defined"
    end
    @label = label
  end
  @label
end
managing_user_roles(*default_roles, &block) click to toggle source
# File pim.rb, line 2442
def managing_user_roles *default_roles, &block
  unless default_roles.empty? and block.nil?

    raise "Default managing user roles for data model '#{self.to_s}' are already defined" unless @default_managing_user_roles.nil?
    raise "Block to determine managing user roles for data model '#{self.to_s}' is already defined" unless @managing_user_roles_block.nil?

    @default_managing_user_roles = default_roles.empty? ? nil : default_roles
    @managing_user_roles_block = block

  end
  return @default_managing_user_roles
end
managing_user_roles_as_json(user, contact) click to toggle source
# File pim.rb, line 2533
def managing_user_roles_as_json user, contact

  # Get defined block to determine the support roles
  roles_block = find_first_in_parent_modules { |m| m.managing_user_roles_block }
  managing_roles = roles_block.call(user, contact) unless roles_block.nil?

  # Use default support roles or even user's roles, if no roles were determined
  managing_roles = find_first_in_parent_modules { |m| m.default_managing_user_roles } if is_empty?(managing_roles)
  managing_roles = user.roles if is_empty?(managing_roles)

  return managing_roles
end
media_asset_naming_patterns(patterns = nil) click to toggle source
# File pim.rb, line 2371
def media_asset_naming_patterns patterns = nil
  @media_asset_naming_patterns ||= {}
  if patterns
    @media_asset_naming_patterns.merge! patterns
  end
  @media_asset_naming_patterns
end
normalize_value(value, disable_value_normalization = false, &normalize_block) click to toggle source
# File pim.rb, line 2649
def normalize_value value, disable_value_normalization = false, &normalize_block
  return nil if is_empty?(value)
  # Normalize value to ensure value is of the correct type
  normalized_value = normalize_block.call(value)
  return value if disable_value_normalization
  return normalized_value
end
options(options = nil) click to toggle source
# File pim.rb, line 2312
def options options = nil
  @options ||= {}
  if options
    @options.merge!(options)
  end
  @options
end
Also aliased as: data_model_opts
organization_attributes(*attributes) click to toggle source
# File pim.rb, line 2387
def organization_attributes *attributes
  @organization_attributes ||= []
  if attributes
    attributes.each do |attribute|
      check_attribute!(attribute, "Attribute '#{attribute}' used as organization attribute is not defined")
    end
    @organization_attributes.concat(attributes)
  end
  @organization_attributes
end
parent_modules() click to toggle source
# File pim.rb, line 2475
def parent_modules
  modules = []
  (@parent_modules || []).each do |p|
    modules << p
    modules.concat p.parent_modules if p.respond_to?(:parent_modules)
  end
  modules
end
review_attributes(*attributes) click to toggle source
# File pim.rb, line 2420
def review_attributes *attributes
  @review_attributes ||= []
  if attributes
    attributes.each do |attribute|
      check_attribute!(attribute, "Attribute '#{attribute}' used as review attribute is not defined")
    end
    @review_attributes.concat(attributes)
  end
  @review_attributes
end
service_name(service_name = nil) click to toggle source
# File pim.rb, line 2279
def service_name service_name = nil
  if service_name
    if @service_name
      raise "Service name for data model '#{self.to_s}' is already defined"
    end
    @service_name = service_name
  end
  @service_name
end
task_tags(*tags) click to toggle source
# File pim.rb, line 2379
def task_tags *tags
  @task_tags ||= []
  if tags
    @task_tags.concat(tags)
  end
  @task_tags
end
update_hierarchy(hierarchy, opts = {}) click to toggle source

Called after “computing” a hierarchy via ‘get_top_level_items’ and ‘get_contained_items’. Used to update the hierarchy based on the contained items. May be overridden in data model! Return the updated hierarchy or nil, to skip it. Default implementation: Keep the hierarchy as-is.

# File pim.rb, line 2786
def update_hierarchy hierarchy, opts = {}
  hierarchy
end
user_attributes(*attributes) click to toggle source
# File pim.rb, line 2398
def user_attributes *attributes
  @user_attributes ||= []
  if attributes
    attributes.each do |attribute|
      check_attribute!(attribute, "Attribute '#{attribute}' used as user attribute is not defined")
    end
    @user_attributes.concat(attributes)
  end
  @user_attributes
end
user_data_model_as_json(user) click to toggle source
# File pim.rb, line 2524
def user_data_model_as_json user
  categories_overview = user_categories_overview(user)
  layout_mappings = user_layout_mapping(user)
  return {
    :categoriesOverview => categories_overview.as_json,
    :layoutMapping => layout_mappings.as_json
  }
end
value_as_json(attribute, value) click to toggle source
# File pim.rb, line 2546
def value_as_json attribute, value

  return nil if value.nil?
  return value.error_value if value.is_a?(ErrorValue)
  return value if attribute.nil?

  if attribute.is_a?(Class)

    base_class = attribute
    formatter = nil

  else

    attribute = attribute(attribute) unless attribute.is_a?(PIM::Attribute)
    return value if attribute.nil?

    base_class = attribute.base_class
    formatter = get_converter(attribute, :formatter)
    uniq = attribute.param(:uniq)

    PIM.log_trace "value_as_json: attribute.name=#{attribute.name}, base_classclass=#{base_class}, uniq=#{uniq}"

  end

  case
    when formatter
      return formatter.call(value) if formatter.arity == 1
      return formatter.call(value, attribute)
    when base_class <= Array
      return array_value_as_json(attribute, value, uniq)
    when base_class <= Set
      return array_value_as_json(attribute, value, true)
    when base_class <= Hash
      return hash_value_as_json(attribute, value)
    when base_class <= DateTime
      return datetime_value_as_json(attribute, value)
    when base_class <= Date
      return date_value_as_json(attribute, value)
    when value.respond_to?(:as_json)
      return value.as_json
    else
      return value
  end

end
value_from_json(attribute, value) click to toggle source
# File pim.rb, line 2592
def value_from_json attribute, value

  return nil if value.nil?
  return value if value.is_a?(ErrorValue)
  return value if attribute.nil?

  if attribute.is_a?(Class)

    base_class = attribute
    parser = nil
    disable_value_normalization = false

  else

    attribute = attribute(attribute) unless attribute.is_a?(PIM::Attribute)
    return value if attribute.nil?

    base_class = attribute.base_class
    parser = get_converter(attribute, :parser)
    disable_value_normalization = attribute.param(:disable_value_normalization)
    uniq = attribute.param(:uniq)

    PIM.log_trace "value_from_json: attribute.name=#{attribute.name}, base_class=#{base_class}, uniq=#{uniq}"

  end

  begin
    case
      when parser
        return parser.call(value) if parser.arity == 1
        return parser.call(value, attribute)
      when base_class <= Float
        return normalize_value(value, disable_value_normalization) { |v| Float(v.to_s) }
      when base_class <= Integer
        return normalize_value(value, disable_value_normalization) { |v| Integer(v.to_s, 10) }
      when base_class <= DateTime
        return normalize_value(value, disable_value_normalization) { |v| parse_datetime(v) }
      when base_class <= Date
        return normalize_value(value, disable_value_normalization) { |v| Date.parse(v.to_s) }
      when base_class <= Array
        return collection_value_from_json attribute, value, uniq
      when base_class <= Set
        return collection_value_from_json attribute, value, true
      when base_class <= Hash
        return hash_value_from_json attribute, value
      when base_class.respond_to?(:from_json)
        return base_class.from_json(value)
      else
        return value
    end
  rescue Exception => e
    log_warn "Could not convert value #{value_msg(value)}for attribute #{attribute.name} to base class #{base_class}", e
    return PIM::ErrorValue.new(value, base_class, e.message.length > 0 ? e.message.force_encoding(Encoding::UTF_8) : "")
  end

end
values_to_xml(values, parent_node = nil) click to toggle source
# File pim.rb, line 2841
def values_to_xml values, parent_node = nil
  xml_item = create_xml_item(values, parent_node)
  return xml_item.xml
end

Protected Instance Methods

add_parent_module(parent_module) click to toggle source
# File pim.rb, line 2937
def add_parent_module parent_module
  @parent_modules ||= []
  @parent_modules << parent_module
end
extended(submodule) click to toggle source
# File pim.rb, line 2914
def extended submodule
  return if self == submodule
  return if PIM.has_module?(submodule)
  PIM.add_module submodule
  submodule.extend XmlUtils
  submodule.extend Logging
  submodule.extend DataModel
  submodule.extend OptionLists
  submodule.extend Attributes
  submodule.extend AttributeTemplates
  submodule.extend Categories
  submodule.extend Layouts
  submodule.extend Authorization
  submodule.extend Filters
  submodule.extend Validations
  submodule.extend Calculations
  submodule.extend CommunicationChannels
  submodule.extend CommunicationPlans
  submodule.extend Migrations
  submodule.extend MappingFunctions
  submodule.add_parent_module self
end