module PIM::DataModel

Constants

AVAILABLE_OPTIONS
DATA_MODEL_DEFINITIONS_FILE_EXTENSIONS
PRELOAD_REFERENCED_ITEMS_DEFAULT_SIZE
PRELOAD_REFERENCED_ITEMS_MAX_SIZE

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 3031
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 2756
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 3069
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 3099
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 2729
def cache
  PIM.__fiber_local_cache(:__data_model_cache)
end
contact_attributes(*attributes) click to toggle source
# File pim.rb, line 2673
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 3118
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 2695
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 2765
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_file(location = nil, force: false) click to toggle source
# File pim.rb, line 2523
def data_model_definitions_file location = nil, force: false
  if location

    # Only set the location unless running on internal JRuby
    return nil if PIM::Services.is_java_services_available?

    if force
      @data_model_definitions_file = location
      return location
    end

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

    extname = File.extname(location)
    if not extname.empty? and not DATA_MODEL_DEFINITIONS_FILE_EXTENSIONS.include?(extname)

      PIM.log_warn "Specified data model definitions file '#{location}' not supported; File must have either of these extensions: #{DATA_MODEL_DEFINITIONS_FILE_EXTENSIONS}"
      return nil

    end

    basename = File.basename(location, extname)
    dirname = File.dirname(location)

    # Determine location via "preferred" extensions
    preferred_location = nil
    DATA_MODEL_DEFINITIONS_FILE_EXTENSIONS.each do |extname|

      test_location = File.expand_path(basename + extname, dirname)
      if File.readable?(test_location)
        preferred_location = test_location
        break
      end

    end

    if preferred_location
      PIM.log_info "Using data model definitions file in '#{preferred_location}'" unless preferred_location == location
      location = preferred_location
    elsif not File.readable?(location)
      PIM.log_warn "Data model definitions location '#{location}' not found or not readable"
      return nil
    end

    @data_model_definitions_file = location

  end
  return @data_model_definitions_file
end
Also aliased as: data_model_definitions_yaml
data_model_definitions_yaml(location = nil, force: false)
data_model_manager(&block) click to toggle source
# File pim.rb, line 2476
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 2483
def data_module
  self
end
extension(type, &block) click to toggle source
# File pim.rb, line 2595
def extension type, &block
  @extensions ||= {}
  @extensions[type] ||= []
  @extensions[type] << block
end
extension_point(type) click to toggle source
# File pim.rb, line 2601
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 2748
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 3146
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|
        return PIM::Services::DataModelService.has_data_model_option?(option_list.service, option_list.name, option, *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 2998
def get_contained_items primary_key, opts = {}
  return []
end
get_converter(attribute, converter_param) click to toggle source
# File pim.rb, line 2921
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_definitions_file(*extensions) click to toggle source
# File pim.rb, line 2507
def get_data_model_definitions_file *extensions

  return nil if data_model_definitions_file.nil?

  filename = data_model_definitions_file
  if not File.readable?(filename)
    PIM.log_warn "Data model definitions not found in '#{filename}'"
    return nil
  end

  extension = File.extname(filename)
  return nil unless extensions.include?(extension)
  return filename

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 3005
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 2956
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 2585
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 2974
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 3065
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 2630
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 2590
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 2719
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 2612
def initialization type, &block
  @initializations ||= {}
  @initializations[type] ||= []
  @initializations[type] << block
end
initialization_point(type) click to toggle source
# File pim.rb, line 2618
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 2733
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 2487
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 2706
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 2797
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 2635
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
merge_item_reviews(new_item_review, old_item_review) click to toggle source

Merges the values from the old_item_review with the values of the new item_review

Note: returning an empty object means, that reviews should be merged by the

old manner
# File pim.rb, line 2950
def merge_item_reviews new_item_review, old_item_review
  nil # default merge
end
normalize_value(value, disable_value_normalization = false, &normalize_block) click to toggle source
# File pim.rb, line 2913
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 2576
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 2651
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 2739
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 2684
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 2497
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 2643
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 3058
def update_hierarchy hierarchy, opts = {}
  hierarchy
end
user_attributes(*attributes) click to toggle source
# File pim.rb, line 2662
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 2788
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 2810
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 2856
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 3113
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 3205
def add_parent_module parent_module
  @parent_modules ||= []
  @parent_modules << parent_module
end
extended(submodule) click to toggle source
# File pim.rb, line 3182
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