X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/51b662b3ff003363dede20cfe2041f5502906910..67be3a5b8cab46709969902e51a839661631404d:/app/models/orvos_base.rb diff --git a/app/models/orvos_base.rb b/app/models/orvos_base.rb index 34b723a3bc..824c466657 100644 --- a/app/models/orvos_base.rb +++ b/app/models/orvos_base.rb @@ -1,10 +1,42 @@ class OrvosBase < ActiveRecord::Base - @@orvos_v1_base = Rails.configuration.orvos_v1_base + self.abstract_class = true + attr_accessor :attribute_sortkey + + def self.uuid_infix_object_kind + @@uuid_infix_object_kind ||= { + '4zz18' => 'orvos#collection', + 'tpzed' => 'orvos#user', + 'ozdt8' => 'orvos#api_client', + '57u5n' => 'orvos#log', + 'j58dm' => 'orvos#specimen', + 'ldvyl' => 'orvos#project' + } + end + + def initialize + super + @attribute_sortkey ||= { + 'id' => nil, + 'uuid' => '000', + 'owner' => '001', + 'created_at' => '002', + 'modified_at' => '003', + 'modified_by_user' => '004', + 'modified_by_client' => '005', + 'tail_kind' => '100', + 'tail_uuid' => '100', + 'head_kind' => '101', + 'head_uuid' => '101', + 'info' => 'zzz-000', + 'updated_at' => 'zzz-999' + } + end + def self.columns return @columns unless @columns.nil? @columns = [] - return @columns if orvos_schema[self.to_s.to_sym].nil? - orvos_schema[self.to_s.to_sym].each do |coldef| + return @columns if $orvos_api_client.orvos_schema[self.to_s.to_sym].nil? + $orvos_api_client.orvos_schema[self.to_s.to_sym].each do |coldef| k = coldef[:name].to_sym if coldef[:type] == coldef[:type].downcase @columns << column(k, coldef[:type].to_sym) @@ -21,19 +53,23 @@ class OrvosBase < ActiveRecord::Base def self.column(name, sql_type = nil, default = nil, null = true) ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null) end - def self.all - thelist = api('') - thelist[:items].collect { |x| new(x) } - end def self.find(uuid) - new(api('/' + uuid)) - end - def self.where(cond) - all.select do |o| - 0 == cond.select do |k,v| - o.send(k) != v - end.size + if uuid.class != String or uuid.length < 27 then + raise 'argument to find() must be a uuid string. Acceptable formats: warehouse locator or string with format xxxxx-xxxxx-xxxxxxxxxxxxxxx' end + new.private_reload(uuid) + end + def self.where(*args) + OrvosResourceList.new(self).where(*args) + end + def self.limit(*args) + OrvosResourceList.new(self).limit(*args) + end + def self.eager(*args) + OrvosResourceList.new(self).eager(*args) + end + def self.all(*args) + OrvosResourceList.new(self).all(*args) end def save obdata = {} @@ -45,59 +81,130 @@ class OrvosBase < ActiveRecord::Base postdata = { self.class.to_s.underscore => obdata } if etag postdata['_method'] = 'PUT' - resp = self.class.api('/' + uuid, postdata) + resp = $orvos_api_client.api(self.class, '/' + uuid, postdata) else - resp = self.class.api('', postdata) + resp = $orvos_api_client.api(self.class, '', postdata) end return false if !resp[:etag] || !resp[:uuid] + + # set read-only non-database attributes @etag = resp[:etag] @kind = resp[:kind] - self.uuid ||= resp[:uuid] + + # these attrs can be modified by "save" -- we should update our copies + %w(uuid owner created_at + modified_at modified_by_user modified_by_client + ).each do |attr| + self.send(attr + '=', resp[attr.to_sym]) + end + self end def save! self.save or raise Exception.new("Save failed") end - def initialize(h={}) - @etag = h.delete :etag - @kind = h.delete :kind - super + def links(*args) + o = {} + o.merge!(args.pop) if args[-1].is_a? Hash + o[:link_class] ||= args.shift + o[:name] ||= args.shift + o[:head_kind] ||= args.shift + o[:tail_kind] = self.kind + o[:tail_uuid] = self.uuid + if all_links + return all_links.select do |m| + ok = true + o.each do |k,v| + if !v.nil? + test_v = m.send(k) + if (v.respond_to?(:uuid) ? v.uuid : v.to_s) != (test_v.respond_to?(:uuid) ? test_v.uuid : test_v.to_s) + ok = false + end + end + end + ok + end + end + @links = $orvos_api_client.api Link, '', { _method: 'GET', where: o, eager: true } + @links = $orvos_api_client.unpack_api_response(@links) end - - def kind_uuid - self.kind + '#' + self.uuid + def all_links + return @all_links if @all_links + res = $orvos_api_client.api Link, '', { + _method: 'GET', + where: { + tail_kind: self.kind, + tail_uuid: self.uuid + }, + eager: true + } + @all_links = $orvos_api_client.unpack_api_response(res) end - - protected - def self.api(action, data=nil, o={}) - dataargs = [] - if !data.nil? - data.each do |k,v| - dataargs << '-d' - if v.is_a? String - dataargs << "#{k}=#{v}" - else - dataargs << "#{k}=#{JSON.generate v}" + def reload + private_reload(self.uuid) + end + def private_reload(uuid_or_hash) + raise "No such object" if !uuid_or_hash + if uuid_or_hash.is_a? Hash + hash = uuid_or_hash + else + hash = $orvos_api_client.api(self.class, '/' + uuid_or_hash) + end + hash.each do |k,v| + if self.respond_to?(k.to_s + '=') + self.send(k.to_s + '=', v) + else + # When OrvosApiClient#schema starts telling us what to expect + # in API responses (not just the server side database + # columns), this sort of awfulness can be avoided: + self.instance_variable_set('@' + k.to_s, v) + if !self.respond_to? k + singleton = class << self; self end + singleton.send :define_method, k, lambda { instance_variable_get('@' + k.to_s) } end end end - json = nil - IO.popen([ENV, - 'curl', - '-sk', - *dataargs, - "#{@@orvos_v1_base}/#{o[:resource_path] || self.to_s.underscore.pluralize}#{action}"], - 'r') do |io| - json = io.read + @all_links = nil + self + end + def dup + super.forget_uuid! + end + + def attributes_for_display + self.attributes.reject { |k,v| + attribute_sortkey.has_key?(k) and !attribute_sortkey[k] + }.sort_by { |k,v| + attribute_sortkey[k] or k + } + end + + def self.resource_class_for_uuid(uuid, attr_name=nil, object=nil) + if uuid.is_a? OrvosBase + return uuid.class + end + unless uuid.is_a? String + return nil + end + if uuid.match /^[0-9a-f]{32}(\+[^,]+)*(,[0-9a-f]{32}(\+[^,]+)*)*$/ + return Collection end - resp = JSON.parse json, :symbolize_names => true - if resp[:errors] - raise "API errors:\n#{json[:errors].join "\n"}\n" + resource_class = nil + uuid.match /^[0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15}$/ do |re| + resource_class ||= $orvos_api_client. + kind_class(self.uuid_infix_object_kind[re[1]]) end - resp + if object and attr_name and attr_name.match /_uuid$/ + resource_class ||= $orvos_api_client.kind_class(object.attributes[attr_name.sub(/_uuid$/, '_kind')]) + end + resource_class end - def self.orvos_schema - $orvos_schema ||= api '', nil, {resource_path: 'schema'} + protected + + def forget_uuid! + self.uuid = nil + @etag = nil + self end end