Static parsing class responsible for extracting entities from plain text. Class supports 'text/plain' via `plain`. No other formats are supported.
@attr model [Occi::Core::Model, Occi::Infrastructure::Model] model to use as a primary reference point
@author Boris Parak <parak@cesnet.cz>
Regexp constants
Typecasting lambdas
Shortcuts to interesting methods on logger
Constructs an instance of the entity parser. Only entities (their kinds) defined by the model are allowed.
@param args [Hash] constructor arguments in a Hash @option args [Occi::Core::Model] :model model to use as a primary reference point
# File lib/occi/core/parsers/text/entity.rb, line 48 def initialize(args = {}) pre_initialize(args) default_args! args @model = args.fetch(:model) post_initialize(args) end
Constructs a map pointing from expected attribute types to conversion lambdas.
@return [Hash] typecaster hash with conversion lambdas
# File lib/occi/core/parsers/text/entity.rb, line 245 def typecaster Hash.new(DEFAULT_LAMBDA).merge(TYPECASTER_HASH) end
Builds an entity instances from the lines provided as input.
@param lines [Array] list of lines containing a single entity rendering @return [Occi::Core::Entity] constructed instance
# File lib/occi/core/parsers/text/entity.rb, line 61 def plain(lines) cats = plain_categories(lines) kind = cats.detect { |c| c.is_a?(Occi::Core::Kind) } raise Occi::Core::Errors::ParsingError, 'Entity does not specify its kind' unless kind logger.debug "Identified entity kind #{kind.inspect}" if logger_debug? entity = @_ib.build(kind.identifier) cats.each { |cat| cat.is_a?(Occi::Core::Mixin) && entity << cat } plain_attributes! lines, entity.attributes plain_links! lines, entity logger.debug "Created instance #{entity.inspect}" if logger_debug? entity end
Looks up the action mentioned in the given action “link” and assigns it to the given partially constructed entity instance.
@param md [MatchData] Hash-like structure with matched parts of the link @param entity [Occi::Core::Entity] partially constructed entity instance to be updated
# File lib/occi/core/parsers/text/entity.rb, line 162 def plain_action!(md, entity) entity << handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(md[:rel]) } end
Parses attributes from entity lines. Every attribute value is typed according to the attribute specification provided by the model (in the defined kind).
@param lines [Array] list of lines containing a single entity rendering @param attributes [Hash] defined attributes @return [Hash] parsed and typed attributes
# File lib/occi/core/parsers/text/entity.rb, line 96 def plain_attributes!(lines, attributes) lines.each do |line| next unless line.start_with?(TextParser::ATTRIBUTE_KEYS.first) name, value = raw_attribute(line) unless attributes[name] raise Occi::Core::Errors::ParsingError, "Attribute #{name.inspect} is not allowed for this entity" end attributes[name].value = handle(Occi::Core::Errors::ParsingError) do typecast value, attributes[name].attribute_definition.type end end attributes end
Parses categories from entity lines. Every category is looked up in the model.
@param lines [Array] list of lines containing a single entity rendering @return [Array] list of identified category instances
# File lib/occi/core/parsers/text/entity.rb, line 81 def plain_categories(lines) categories = lines.map do |line| next unless line.start_with?(TextParser::CATEGORY_KEYS.first) cat = Category.plain_category(line, false) handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!("#{cat[:scheme]}#{cat[:term]}") } end categories.compact end
Constructs a single link instance. This includes both action “links” and ordinary OCCI links.
@param md [MatchData] Hash-like structure with matched parts of the link @param entity [Occi::Core::Entity] partially constructed entity instance to be updated
# File lib/occi/core/parsers/text/entity.rb, line 153 def plain_link!(md, entity) md[:uri].include?('?action=') ? plain_action!(md, entity) : plain_oglink!(md, entity) end
Parses links attached to the entity instance. This includes both action “links” and ordinary OCCI links between resources.
@param lines [Array] list of lines containing a single entity rendering @param entity [Occi::Core::Entity] partially constructed entity instance to be updated @return [Occi::Core::Entity] updated entity instance
# File lib/occi/core/parsers/text/entity.rb, line 133 def plain_links!(lines, entity) lines.each do |line| next unless line.start_with?(TextParser::LINK_KEYS.first) logger.debug "Parsing link line #{line.inspect}" if logger_debug? matched = line.match(LINK_REGEXP) unless matched raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match expectations for Link" end plain_link! matched, entity end entity end
Constructs a single link instance. Supports only ordinary OCCI links between resources.
@param md [MatchData] Hash-like structure with matched parts of the link @param entity [Occi::Core::Entity] partially constructed entity instance to be updated
# File lib/occi/core/parsers/text/entity.rb, line 170 def plain_oglink!(md, entity) unless entity.respond_to?(:links) raise Occi::Core::Errors::ParsingError, "Cannot assign links to entity #{entity.id} which does not support them" end link = plain_oglink_instance(md) link.location = handle(Occi::Core::Errors::ParsingError) { URI.parse md[:self] } entity.add_link link plain_oglink_attributes! md, link entity end
Attaches attributes to an existing link instance.
@param md [MatchData] Hash-like structure with matched parts of the link @param link [Occi::Core::Link] partially constructed link instance to be updated
# File lib/occi/core/parsers/text/entity.rb, line 211 def plain_oglink_attributes!(md, link) if md[:attributes].blank? raise Occi::Core::Errors::ParsingError, "Link #{link.id} is missing attribute information" end logger.debug "Parsing inline link attributes from line #{md[:attributes].inspect}" if logger_debug? regexp = Regexp.new "(\\s*#{Constants::REGEXP_ATTRIBUTE_REPR})" line = md[:attributes].strip.gsub(/^;\s*/, '') plain_attributes!( line.scan(regexp).map { |attrb| "#{TextParser::ATTRIBUTE_KEYS.first}: #{attrb.first}" }, link.attributes ) link end
Constructs a single link instance based on the provided data. The returned instance does include action instance attributes!
@param md [MatchData] Hash-like structure with matched parts of the link @return [Occi::Core::Link] constructed link instance
# File lib/occi/core/parsers/text/entity.rb, line 190 def plain_oglink_instance(md) if md[:category].blank? || md[:self].blank? raise Occi::Core::Errors::ParsingError, "Link #{md[:uri].inspect} missing type and location information" end categories = md[:category].split target_kind = handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(md[:rel]) } link = @_ib.build(categories.shift, target_kind: target_kind) categories.each do |mxn| logger.debug "Adding mixin #{mxn.inspect} to link instance" if logger_debug? link << handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(mxn) } end logger.debug "Created link instance #{link.inspect} from #{md.inspect}" if logger_debug? link end
Parses a single attribute line to identify name and value.
@param line [String] line containing a single entity attribute @return [Array] two-element array with name and value of the attribute
# File lib/occi/core/parsers/text/entity.rb, line 118 def raw_attribute(line) logger.debug "Parsing attribute line #{line.inspect}" if logger_debug? matched = line.match(ATTRIBUTE_REGEXP) unless matched raise Occi::Core::Errors::ParsingError, "#{line.inspect} does not match expectations for Attribute" end [matched[:name], matched[:string] || matched[:number] || matched[:bool]] end
Typecasts attribute values from String to the desired type.
@param value [String] attribute value @param type [Class,Module] desired attribute type @return [Object] typecasted value
# File lib/occi/core/parsers/text/entity.rb, line 232 def typecast(value, type) if value.nil? || type.nil? raise Occi::Core::Errors::ParsingError, 'Cannot typecast (un)set value to (un)set type' end logger.debug "Typecasting value #{value.inspect} to #{type}" if logger_debug? self.class.typecaster[type].call(value) end