# File lib/frodata/entity.rb, line 216 def primary_key schema.primary_key_for(name) end
class FrOData::Entity
An FrOData::Entity
represents a single record returned by the service. All Entities have a type and belong to a specific namespace. They are written back to the service via the EntitySet
they came from. FrOData::Entity
instances should not be instantiated directly; instead, they should either be read or instantiated from their respective FrOData::EntitySet
.
Constants
- PROPERTY_NOT_LOADED
- XML_NAMESPACES
Attributes
The entity set this entity belongs to
List of errors on entity
The FrOData::Service's identifying name
The Entity
type name
Public Class Methods
Create Entity
from JSON document with provided options. @param json [Hash|to_s] @param options [Hash] @return [FrOData::Entity]
# File lib/frodata/entity.rb, line 144 def self.from_json(json, options = {}) return nil if json.nil? json = JSON.parse(json.to_s) unless json.is_a?(Hash) metadata = extract_metadata(json) options.merge!(context: metadata['@odata.context']) entity = with_properties(json, options) process_metadata(entity, metadata) entity end
Create Entity
from XML document with provided options. @param xml_doc [Nokogiri::XML] @param options [Hash] @return [FrOData::Entity]
# File lib/frodata/entity.rb, line 158 def self.from_xml(xml_doc, options = {}) return nil if xml_doc.nil? entity = FrOData::Entity.new(options) process_properties(entity, xml_doc) process_links(entity, xml_doc) entity end
Initializes a bare Entity
@param options [Hash]
# File lib/frodata/entity.rb, line 29 def initialize(options = {}) @id = options[:id] @type = options[:type] @service_name = options[:service_name] @entity_set = options[:entity_set] @context = options[:context] @links = options[:links] @errors = [] end
Create Entity
with provided properties and options. @param new_properties [Hash] @param options [Hash] @param [FrOData::Entity]
# File lib/frodata/entity.rb, line 126 def self.with_properties(new_properties = {}, options = {}) entity = FrOData::Entity.new(options) entity.instance_eval do service.properties_for_entity(type).each do |property_name, instance| set_property(property_name, instance) end new_properties.each do |property_name, property_value| self[property_name] = property_value end end entity end
Private Class Methods
# File lib/frodata/entity.rb, line 311 def self.extract_metadata(json) metadata = json.select { |key, val| key =~ /@odata/ } json.delete_if { |key, val| key =~ /@odata/ } metadata end
# File lib/frodata/entity.rb, line 293 def self.process_links(entity, xml_doc) entity.instance_eval do new_links = instance_variable_get(:@links) || {} schema.navigation_properties[name].each do |nav_name, details| xml_doc.xpath("./link[@title='#{nav_name}']").each do |node| next if node.attributes['type'].nil? next unless node.attributes['type'].value =~ /^application\/atom\+xml;type=(feed|entry)$/i link_type = node.attributes['type'].value =~ /type=entry$/i ? :entity : :collection new_links[nav_name] = { type: link_type, href: node.attributes['href'].value } end end instance_variable_set(:@links, new_links) end end
# File lib/frodata/entity.rb, line 317 def self.process_metadata(entity, metadata) entity.instance_eval do new_links = instance_variable_get(:@links) || {} schema.navigation_properties[name].each do |nav_name, details| href = metadata["#{nav_name}@odata.navigationLink"] next if href.nil? new_links[nav_name] = { type: details.nav_type, href: href } end instance_variable_set(:@links, new_links) unless new_links.empty? end end
# File lib/frodata/entity.rb, line 279 def self.process_properties(entity, xml_doc) entity.instance_eval do unless instance_variable_get(:@context) context = xml_doc.xpath('/entry').first.andand['context'] instance_variable_set(:@context, context) end xml_doc.xpath('./content/properties/*').each do |property_xml| # Doing lazy loading here because instantiating each object takes a long time set_property_lazy_load(property_xml.name, property_xml) end end end
Public Instance Methods
Get property value @param property_name [to_s] @return [*]
# File lib/frodata/entity.rb, line 58 def [](property_name) if get_property(property_name).is_a?(::FrOData::Properties::Complex) get_property(property_name) else get_property(property_name).value end end
Set property value @param property_name [to_s] @param value [*]
# File lib/frodata/entity.rb, line 69 def []=(property_name, value) get_property(property_name).value = value end
# File lib/frodata/entity.rb, line 224 def any_errors? !errors.empty? end
Returns context URL for this entity @return [String]
# File lib/frodata/entity.rb, line 51 def context @context ||= context_url end
# File lib/frodata/entity.rb, line 73 def get_property(property_name) prop_name = property_name.to_s # Property is lazy loaded if properties_xml_value.has_key?(prop_name) property = instantiate_property(prop_name, properties_xml_value[prop_name]) set_property(prop_name, property.dup) properties_xml_value.delete(prop_name) end if properties.has_key? prop_name properties[prop_name] elsif navigation_properties.has_key? prop_name navigation_properties[prop_name] else raise ArgumentError, "Unknown property: #{property_name}" end end
Returns the canonical URL for this entity @return [String]
# File lib/frodata/entity.rb, line 206 def id @id ||= lambda { entity_set = self.entity_set.andand.name entity_set ||= context.split('#').last.split('/').first "#{entity_set}(#{self[primary_key]})" }.call end
# File lib/frodata/entity.rb, line 220 def is_new? self[primary_key].nil? end
Links to other FrOData
entitites @return [Hash]
# File lib/frodata/entity.rb, line 113 def links @links ||= schema.navigation_properties[name].map do |nav_name, details| [ nav_name, { type: details.nav_type, href: "#{id}/#{nav_name}" } ] end.to_h end
# File lib/frodata/entity.rb, line 39 def namespace @namespace ||= type.rpartition('.').first end
Returns the primary key for the Entity
. @return [String]
# File lib/frodata/entity.rb, line 91 def property_names [ @properties_xml_value.andand.keys, @properties.andand.keys ].compact.flatten end
# File lib/frodata/entity.rb, line 232 def schema @schema ||= service.schemas[namespace] end
# File lib/frodata/entity.rb, line 228 def service @service ||= FrOData::ServiceRegistry[service_name] end
Converts Entity
to a hash. @return [Hash]
# File lib/frodata/entity.rb, line 198 def to_hash property_names.map do |name| [name, get_property(name).json_value] end.to_h end
Converts Entity
to its JSON representation. @return [String]
# File lib/frodata/entity.rb, line 191 def to_json # TODO: add @odata.context to_hash.to_json end
Converts Entity
to its XML representation. @return [String]
# File lib/frodata/entity.rb, line 168 def to_xml namespaces = XML_NAMESPACES.merge('xml:base' => service.service_url) builder = Nokogiri::XML::Builder.new do |xml| xml.entry(namespaces) do xml.category(term: type, scheme: 'http://docs.oasis-open.org/odata/ns/scheme') xml.author { xml.name } xml.content(type: 'application/xml') do xml['metadata'].properties do property_names.each do |name| next if name == primary_key get_property(name).to_xml(xml) end end end end end builder.to_xml end
Private Instance Methods
Computes the entity's canonical context URL
# File lib/frodata/entity.rb, line 266 def context_url "#{service.service_url}/$metadata##{entity_set.name}/$entity" end
# File lib/frodata/entity.rb, line 238 def instantiate_property(property_name, value_xml) prop_type = schema.get_property_type(name, property_name) prop_type, value_type = prop_type.split(/\(|\)/) if prop_type == 'Collection' klass = ::FrOData::Properties::Collection options = { value_type: value_type } else klass = ::FrOData::PropertyRegistry[prop_type] options = {} end if klass.nil? raise RuntimeError, "Unknown property type: #{prop_type}" else klass.from_xml(value_xml, options.merge(service: service)) end end
# File lib/frodata/entity.rb, line 257 def properties @properties ||= {} end
# File lib/frodata/entity.rb, line 261 def properties_xml_value @properties_xml_value ||= {} end
# File lib/frodata/entity.rb, line 270 def set_property(name, property) properties[name.to_s] = property end
Instantiating properties takes time, so we can lazy load properties by passing xml_value and lookup when needed
# File lib/frodata/entity.rb, line 275 def set_property_lazy_load(name, xml_value ) properties_xml_value[name.to_s] = xml_value end