Class: Occi::Core::Parsers::Text::Entity

Inherits:
Object
  • Object
show all
Includes:
Helpers::ArgumentValidator, Helpers::ErrorHandler, Yell::Loggable
Defined in:
lib/occi/core/parsers/text/entity.rb

Overview

Static parsing class responsible for extracting entities from plain text. Class supports ‘text/plain’ via ‘plain`. No other formats are supported.

Author:

Constant Summary collapse

ATTRIBUTE_REGEXP =

Regexp constants

/#{Constants::REGEXP_ATTRIBUTE}/
/#{Constants::REGEXP_LINK}/
DEFAULT_LAMBDA =

Typecasting lambdas

->(val) { raise "#{self} -> Cannot typecast #{val.inspect} to an unknown type" }
FLOAT_LAMBDA =
->(val) { Float(val) rescue raise(Occi::Core::Errors::ParsingError, "Wrong value #{val}") }
JSON_LAMBDA =
->(val) { JSON.parse(val.gsub('\"', '"'), symbolize_names: true) }
TYPECASTER_HASH =
{
  IPAddr  => ->(val) { IPAddr.new val },
  URI     => ->(val) { URI.parse val },
  String  => ->(val) { val },
  Float   => FLOAT_LAMBDA,
  Numeric => FLOAT_LAMBDA,
  Integer => ->(val) { Integer(val) rescue raise(Occi::Core::Errors::ParsingError, "Wrong value #{val}") },
  Boolean => ->(val) { val.casecmp('true') || val.casecmp('yes') },
  Array   => JSON_LAMBDA,
  Hash    => JSON_LAMBDA
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::ErrorHandler

#handle, included

Constructor Details

#initialize(args = {}) ⇒ Entity

Constructs an instance of the entity parser. Only entities (their kinds) defined by the model are allowed.

Parameters:

  • args (Hash) (defaults to: {})

    constructor arguments in a Hash

Options Hash (args):



44
45
46
47
48
49
50
51
# File 'lib/occi/core/parsers/text/entity.rb', line 44

def initialize(args = {})
  pre_initialize(args)
  default_args! args

  @model = args.fetch(:model)

  post_initialize(args)
end

Instance Attribute Details

#modelOcci::Core::Model, Occi::Infrastructure::Model

model to use as a primary reference point

Returns:



11
12
13
# File 'lib/occi/core/parsers/text/entity.rb', line 11

def model
  @model
end

Class Method Details

.typecasterHash

Constructs a map pointing from expected attribute types to conversion lambdas.

Returns:

  • (Hash)

    typecaster hash with conversion lambdas



241
242
243
# File 'lib/occi/core/parsers/text/entity.rb', line 241

def typecaster
  Hash.new(DEFAULT_LAMBDA).merge(TYPECASTER_HASH)
end

Instance Method Details

#plain(lines) ⇒ Occi::Core::Entity

Builds an entity instances from the lines provided as input.

Parameters:

  • lines (Array)

    list of lines containing a single entity rendering

Returns:

Raises:



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/occi/core/parsers/text/entity.rb', line 57

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}" }

  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}" }
  entity
end

#plain_action!(md, entity) ⇒ Object

Looks up the action mentioned in the given action “link” and assigns it to the given partially constructed entity instance.

Parameters:

  • md (MatchData)

    Hash-like structure with matched parts of the link

  • entity (Occi::Core::Entity)

    partially constructed entity instance to be updated



158
159
160
# File 'lib/occi/core/parsers/text/entity.rb', line 158

def plain_action!(md, entity)
  entity << handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(md[:rel]) }
end

#plain_attributes!(lines, attributes) ⇒ Hash

Parses attributes from entity lines. Every attribute value is typed according to the attribute specification provided by the model (in the defined kind).

Parameters:

  • lines (Array)

    list of lines containing a single entity rendering

  • attributes (Hash)

    defined attributes

Returns:

  • (Hash)

    parsed and typed attributes



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/occi/core/parsers/text/entity.rb', line 92

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

#plain_categories(lines) ⇒ Array

Parses categories from entity lines. Every category is looked up in the model.

Parameters:

  • lines (Array)

    list of lines containing a single entity rendering

Returns:

  • (Array)

    list of identified category instances



77
78
79
80
81
82
83
84
# File 'lib/occi/core/parsers/text/entity.rb', line 77

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

#plain_link!(md, entity) ⇒ Object

Constructs a single link instance. This includes both action “links” and ordinary OCCI links.

Parameters:

  • md (MatchData)

    Hash-like structure with matched parts of the link

  • entity (Occi::Core::Entity)

    partially constructed entity instance to be updated



149
150
151
# File 'lib/occi/core/parsers/text/entity.rb', line 149

def plain_link!(md, entity)
  md[:uri].include?('?action=') ? plain_action!(md, entity) : plain_oglink!(md, entity)
end

#plain_links!(lines, entity) ⇒ Occi::Core::Entity

Parses links attached to the entity instance. This includes both action “links” and ordinary OCCI links between resources.

Parameters:

  • lines (Array)

    list of lines containing a single entity rendering

  • entity (Occi::Core::Entity)

    partially constructed entity instance to be updated

Returns:



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/occi/core/parsers/text/entity.rb', line 129

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}" }

    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

#plain_oglink!(md, entity) ⇒ Object

Constructs a single link instance. Supports only ordinary OCCI links between resources.

Parameters:

  • md (MatchData)

    Hash-like structure with matched parts of the link

  • entity (Occi::Core::Entity)

    partially constructed entity instance to be updated



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/occi/core/parsers/text/entity.rb', line 166

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.

Parameters:

  • md (MatchData)

    Hash-like structure with matched parts of the link

  • link (Occi::Core::Link)

    partially constructed link instance to be updated



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/occi/core/parsers/text/entity.rb', line 207

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}" }

  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!

Parameters:

  • md (MatchData)

    Hash-like structure with matched parts of the link

Returns:



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/occi/core/parsers/text/entity.rb', line 186

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" }
    link << handle(Occi::Core::Errors::ParsingError) { model.find_by_identifier!(mxn) }
  end

  logger.debug { "Created link instance #{link.inspect} from #{md.inspect}" }
  link
end

#raw_attribute(line) ⇒ Array

Parses a single attribute line to identify name and value.

Parameters:

  • line (String)

    line containing a single entity attribute

Returns:

  • (Array)

    two-element array with name and value of the attribute



114
115
116
117
118
119
120
121
# File 'lib/occi/core/parsers/text/entity.rb', line 114

def raw_attribute(line)
  logger.debug { "Parsing attribute line #{line.inspect}" }
  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

#typecast(value, type) ⇒ Object

Typecasts attribute values from String to the desired type.

Parameters:

  • value (String)

    attribute value

  • type (Class, Module)

    desired attribute type

Returns:

  • (Object)

    typecasted value



228
229
230
231
232
233
234
235
# File 'lib/occi/core/parsers/text/entity.rb', line 228

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}" }
  self.class.typecaster[type].call(value)
end