class VersionOne::Asset

Constants

ASSET_FORM_MAP
ASSET_TYPE_MAP

Attributes

attributes[R]
href[R]
meta[R]
type[R]

Public Class Methods

from_xml(xml) click to toggle source
# File lib/version-one/asset.rb, line 237
def self.from_xml(xml)
  meta = nil
  case xml.name
    when 'Asset'
      Asset.new(xml: xml)
    when 'Assets', 'History'
      xml.find('Asset').map do |el|
        a = Asset.new(xml: el, meta: meta)
        meta ||= a.meta
        a
      end
    else
      raise "XML element #{xml.name} cannot be converted to an asset"
  end
end
get(id_or_href, *fields) click to toggle source
# File lib/version-one/asset.rb, line 253
def self.get(id_or_href, *fields)
  client = VersionOne::Client.new
  if id_or_href =~ /^[A-Za-z]+:\d+$/
    xml = client.get "/rest.v1/Data/#{id_or_href.sub(':', '/')}", *fields
  else
    xml = client.get id_or_href, *fields
  end
  Asset.from_xml(xml)
end
new(options={}) click to toggle source
# File lib/version-one/asset.rb, line 23
def initialize(options={})
  raise ArgumentError unless options.is_a?(Hash)
  xml = options.delete(:xml)
  @type = options.delete(:type)
  @initialized = false
  @href = nil

  if xml
    xml = xml.root if xml.is_a?(XML::Document)
    @id = normalized_id(xml.attributes['id'])
    @href = normalized_href(xml.attributes['href'])
    unless @type
      @type = xml.find_first('Attribute[@name="AssetType"]')
      @type = @type.content
    end
  else
    @id = normalized_id(options[:id]) if options[:id]
  end

  class_name = self.class.name.split('::').last
  if class_name != 'Asset'
    @type ||= class_name
    raise ArgumentError, "Incompatible class #{class_name} for asset type #{@type}" unless @type == class_name
  end

  case @type
    when String, Symbol
      @type = @type.to_s
    when NilClass
      raise ArgumentError, 'No asset type specified' unless @type
    else
      raise ArgumentError, "Invalid asset type argument: #{@type.inspect}"
  end

  @meta = options[:meta]
  @meta ||= options[:meta_cache][@type] if options.key?(:meta_cache)
  @meta ||= Meta.get(@type)

  @attributes = {}
  @changed_attributes = {}
  @setters = {}

  @meta.attributes.each do |a|
    simple_type = ASSET_TYPE_MAP[a.type]
    if simple_type
      add_simple_attribute(a, simple_type)
    elsif a.type == :relation
      add_relation_attribute(a)
    else
      add_simple_attribute(a, String)
    end
  end

  if xml
    xml.each {|el| init_attribute_value(el) }
    @changed_attributes.clear
  else
    options.each do |name,val|
      send("#{name}=", val)
    end
  end

  @initialized = true

  self
end
reply_to(asset, msg) click to toggle source
# File lib/version-one/asset.rb, line 167
def self.reply_to(asset, msg)
  asset_id = case asset
               when Asset
                 asset.id
               when String
                 asset
               else
                 raise ArgumentError, 'Invalid asset argument'
             end

  raise ArgumentError, 'Cannot reply to this asset type' unless asset_id.match(/^Expression\:\d+$/)

  reply = VersionOne::Expression.new(
      content:      msg,
      in_reply_to:  AssetRef.new(id: asset_id)
  )

  reply.save!
end

Public Instance Methods

add_comment(msg) click to toggle source
# File lib/version-one/asset.rb, line 147
def add_comment(msg)
  raise 'Cannot comment on an unsaved asset' if new_record?

  conversation = VersionOne::Asset.new(type: 'Conversation')
  conversation.save!

  expression = VersionOne::Asset.new(type: 'Expression')
  expression.content = msg
  expression.belongs_to = conversation
  expression.mentions = [self]
  expression.save!
rescue
  conversation.delete if conversation
  raise
end
comments(options={}) click to toggle source
# File lib/version-one/asset.rb, line 187
def comments(options={})
  deep = options.key?(:deep) ? options[:deep] : true

  query = VersionOne::Query.new('Expression').select(*%w{Author.Name Author.Email AuthoredAt Mentions InReplyTo Replies Content})
  where = "Mentions='#{self.id}'"
  if deep
    where = "(#{where})|(InReplyTo.Mentions='#{self.id}')"
  end

  results = query.where(where).all

  # Move reply information to the corresponding
  # parent conversation
  if deep
    comments_by_id = results.index_by(&:id)

    replies = results.select{|c| c.in_reply_to && comments_by_id.key?(c.in_reply_to.id)}
    replies_by_id = replies.index_by(&:id)

    results.map do |c|
      if replies_by_id.key?(c.id)
        nil
      elsif c.replies.nil?
        c
      else
        c.replies.each do |ref|
          reply = replies_by_id[ref.id]
          ref.set(reply) if reply
        end
        c
      end
    end

    results.compact!
  end

  results
end
delete(options={}) click to toggle source
# File lib/version-one/asset.rb, line 139
def delete(options={})
  delete! rescue false
end
delete!(options={}) click to toggle source
# File lib/version-one/asset.rb, line 135
def delete!(options={})
  exec('Delete', options) unless new_record?
end
display_url() click to toggle source
# File lib/version-one/asset.rb, line 103
def display_url
  unless @display_url
    uri = service_uri.dup
    uri.path = File.join(uri.path, "#{ASSET_FORM_MAP[@type] || @type}.mvc/Summary")
    uri.query = URI.encode_www_form('oidToken' => self.id)
    @display_url = uri.to_s
  end
  @display_url
end
exec(op_name, options={}) click to toggle source
# File lib/version-one/asset.rb, line 226
def exec(op_name, options={})
  client = options[:client] || VersionOne::Client.new
  client.post(href, {op: op_name})
end
get_history(fields=nil) click to toggle source
# File lib/version-one/asset.rb, line 231
def get_history(fields=nil)
  path = href.sub(/\/[Dd]ata\//, '/Hist/')
  client = VersionOne::Client.new
  Asset.from_xml(client.get(path, fields))
end
id() click to toggle source
# File lib/version-one/asset.rb, line 90
def id
  @id
end
inspect() click to toggle source
# File lib/version-one/asset.rb, line 117
def inspect
  "<Asset:#{@id || @type || '???'}>"
end
new_record?() click to toggle source
# File lib/version-one/asset.rb, line 143
def new_record?
  @href.nil?
end
ref() click to toggle source
# File lib/version-one/asset.rb, line 113
def ref
  AssertRef.for(self)
end
reload(*fields) click to toggle source
# File lib/version-one/asset.rb, line 263
def reload(*fields)
  Asset.get(self.href, *fields)
end
reply(msg) click to toggle source
# File lib/version-one/asset.rb, line 163
def reply(msg)
  self.class.reply_to(self, msg)
end
save(options={}) click to toggle source
# File lib/version-one/asset.rb, line 121
def save(options={})
  save!(options) rescue false
end
save!(options={}) click to toggle source
# File lib/version-one/asset.rb, line 125
def save!(options={})
  xml = change_xml
  client = options[:client] || VersionOne::Client.new
  response_xml = client.post_xml(save_url, xml)
  @href ||= normalized_href(response_xml.attributes['href'])
  @id ||= normalized_id(response_xml.attributes['id'])
  @changed_attributes.clear
  self
end
url() click to toggle source
# File lib/version-one/asset.rb, line 94
def url
  unless @url
    uri = service_uri.dup
    uri.path = href
    @url = uri.to_s
  end
  @url
end

Private Instance Methods

add_generic_attribute(attr_name, options={}) click to toggle source
# File lib/version-one/asset.rb, line 309
    def add_generic_attribute(attr_name, options={})
      if attr_name.is_a?(VersionOne::AttributeDefinition)
        a = attr_name
        attr_name = a.name
        options = {
            readonly: a.readonly?,
            multivalue: a.multivalue?
        }
      end

      clean_name = attr_name.gsub(/:[^\.]+/, '').gsub('.', '_')
      ruby_name = clean_name.gsub(/([a-z])([A-Z])/){|m|
        $1 + '_' + $2
      }.downcase
      readonly = !!options[:readonly]

      set_script = %{
        def #{ruby_name}=(val)
          raise "Can't set read-only property" if #{readonly.to_s} && @initialized
          #{yield(attr_name, options)}
          @attributes['#{attr_name}'] = val
          @changed_attributes['#{attr_name}'] = true
          val
        end
        }

      get_script = %{
def #{ruby_name}
  @attributes['#{attr_name}']
end

def #{ruby_name}?
  !!#{ruby_name}
end

def #{ruby_name}_definition
  @meta.attributes['#{attr_name}']
end
}

      instance_eval(get_script)
      instance_eval(set_script)
      @setters[attr_name] = "#{ruby_name}="
    end
add_relation_attribute(a, opts={}) click to toggle source
# File lib/version-one/asset.rb, line 385
def add_relation_attribute(a, opts={})
  add_generic_attribute(a, opts) do |attr_name, options|
    script = %{
      val = case val
    }
    if options[:multivalue]
      script << %{
        when Asset, AssetRef, Array, NilClass
          rel = @attributes['#{attr_name}'] || RelationMultiValue.new
          rel.set(val)
        else
          raise ArgumentError, "Invalid argument type: %s" % [val.class.name]
        end
      }
    else
      script << %{
        when NilClass
          val
        when Array
          raise ArgumentError.new("Too many values for single value attribute") if val.size > 1
          val[0]
        when Asset, AssetRef
          val
        else
          raise ArgumentError, "Invalid argument type: %s" % [val.class.name]
        end
        val = AssetRef.for(val) if val
      }
    end
    script
  end
end
add_simple_attribute(_attr_name, native_type, _options={}) click to toggle source
# File lib/version-one/asset.rb, line 354
def add_simple_attribute(_attr_name, native_type, _options={})

  add_generic_attribute(_attr_name, _options) do |attr_name, options|
    convert = "convert_to_#{native_type}"
    if options[:multivalue]
      setter = %{
      val = case val
        when NilClass
          val
        when Array
          val.map{|v| #{convert}('#{attr_name}',v) }
        else
          [#{convert}('#{attr_name}',val)]
      end
    }
    else
      setter = %{
      val = case val
        when NilClass
          val
        else
          #{convert}('#{attr_name}',val)
      end
    }
    end

    setter
  end

end
change_xml() click to toggle source
# File lib/version-one/asset.rb, line 442
def change_xml
  asset = XML::Node.new('Asset')

  @changed_attributes.each_key do |a|
    attr_def = meta[a]
    child = nil
    val = @attributes[a]

    if attr_def.relation?
      child = XML::Node.new('Relation')
      case
        when val.nil?
        when attr_def.multivalue?
          val.added.each do |v|
            v = v.to_xml
            v.attributes['act'] = 'add'
            child << v
          end
          val.removed.each do |v|
            v = v.to_xml
            v.attributes['act'] = 'remove'
            child << v
          end
        else
          child << val.to_xml
      end
    else
      child = XML::Node.new('Attribute')
      case
        when val.nil?
          #noop
        when attr_def.multivalue?
          val.each do |v|
            child << XML::Node.new('Value', v.to_s)
          end
        else
          child.content = val.to_s
      end
    end

    child.attributes['name'] = a
    child.attributes['act'] = 'set'  unless (attr_def.relation? && attr_def.multivalue?)
    asset << child
  end

  asset
end
convert_to_Float(name, val) click to toggle source
# File lib/version-one/asset.rb, line 430
def convert_to_Float(name, val)
  Float(val)
end
convert_to_Integer(name, val) click to toggle source
# File lib/version-one/asset.rb, line 418
def convert_to_Integer(name, val)
  val.to_i
end
convert_to_String(name, val) click to toggle source
# File lib/version-one/asset.rb, line 422
def convert_to_String(name, val)
  val.to_s
end
convert_to_Time(name, val) click to toggle source
# File lib/version-one/asset.rb, line 426
def convert_to_Time(name, val)
  VersionOne.s_to_time(val, utc: !!(name.match(/utc$/i)))
end
init_attribute_value(el) click to toggle source
# File lib/version-one/asset.rb, line 273
def init_attribute_value(el)
  attr_name = el.attributes['name']

  # If no children (text or elements) then treat as nil value
  # If has Value or Asset children then default to multivalue array
  is_nil        = el.children.size == 0
  val           = nil if is_nil
  children      = is_nil ? [] : el.find('Value|Asset').to_a
  is_multivalue = children.size > 0

  case el.name
    when 'Attribute'
      val = is_multivalue ? children.map(&:content) : el.content unless is_nil

      if @setters[attr_name]
        send(@setters[attr_name], val)
      else
        add_simple_attribute(attr_name, String, readonly: true, multivalue: is_multivalue)
        @attributes[attr_name] = val
      end

    when 'Relation'
      val = children.map{|v| AssetRef.new(v)} unless is_nil

      unless @setters[attr_name]
        add_relation_attribute(attr_name, readonly: true, multivalue: is_multivalue)
      end
      send(@setters[attr_name], val)
      @attributes[attr_name].unchanged! if @attributes[attr_name].respond_to?(:unchanged!)

    else
      # Ignore
  end

end
normalized_href(s) click to toggle source

Remove the moment from the href if any

# File lib/version-one/asset.rb, line 491
def normalized_href(s)
  if s =~ /(\/.+\/[A-Z][a-zA-Z]+\/\d+)(\/\d+)?$/
    $1
  else
    raise ArgumentError, "Invalid href '#{s}'"
  end
end
normalized_id(s) click to toggle source

Remove the moment from the ID if any

# File lib/version-one/asset.rb, line 500
def normalized_id(s)
  if s =~ /^([A-Z][a-zA-Z]+:\d+)(:\d+)?$/
    $1
  else
    raise ArgumentError, "Invalid ID '#{s}'"
  end
end
save_url() click to toggle source
# File lib/version-one/asset.rb, line 434
def save_url
  if @href
    @href
  else
    "rest-1.v1/Data/#{@type}"
  end
end
service_uri() click to toggle source
# File lib/version-one/asset.rb, line 269
def service_uri
  VersionOne::Client.service_uri
end