2017-03-02 24 views
9

Używam Virtus do tworzenia modeli reprezentujących obiekty Salesforce.Jak zapisać identyfikator ciągu do atrybutu modelu

Próbuję utworzyć atrybuty, które mają przyjazne nazwy, które są używane w celu uzyskania dostępu do wartości i metody, której można użyć do pobrania identyfikatora "String" dla tej zmiennej.

Object.attribute #=> "BOB" 
Object.get_identifier(:attribute_name) #=> "KEY" 
# OR something like this 
Object.attribute.identifier #=> "KEY" 

Przyjazna nazwa jest używana jako getter/setter i identyfikator, który może przechowywać każdy atrybut odpowiadającą nazwie API.

Oto przykład:

class Case 
include Virtus.model 

attribute :case_number, String, identifier: 'Case_Number__c' 

end 

c = Case.new(case_number: 'XXX') 
c.case_number #=> 'XXX' 
c.case_number.identifier #=> 'Case_Number__c' 

Albo zamiast metodę na samego atrybutu, może metoda wtórny zostanie utworzony dla każdego zestawu identyfikatora:

c.case_number #=> 'XXX' 
c.case_number_identifier #=> 'Case_Number__c' 

Czy mogę przedłużyć Virtus::Attribute i dodać to? Jeśli tak, nie jestem pewien, jak to zrobić.

+0

Jaison, co się stało z nagrodą za to pytanie? Wydaje się, że po prostu zniknął w powietrzu? Proszę, pomóż mi zrozumieć. – Raffael

+0

Nie mam pojęcia! Próbuję się dowiedzieć, dlaczego nagroda nigdy się nie zakończyła. Ustawiam kolejną nagrodę i podłączę ją po 24 godzinnym oczekiwaniu. –

Odpowiedz

5

Monkey łatanie Virtus' Attribute klasę na pewno jest rozwiązaniem .
Jednak sięgnięcie do wnętrza biblioteki czyni ją podatną na refaktoryzacje w prywatnej części kodu źródłowego bibliotek.

Zamiast tego można użyć modułu pomocniczego, który otacza tę funkcję. Oto sugestia:

require 'virtus' 

# Put this helper module somewhere on your load path (e.g. your project's lib directory) 
module ApiThing 

    def self.included(base) 
    base.include Virtus.model 
    base.extend ApiThing::ClassMethods 
    end 

    module ClassMethods 
    @@identifiers = {} 

    def api_attribute(attr_name, *virtus_args, identifier:, **virtus_options) 
     attribute attr_name, *virtus_args, **virtus_options 
     @@identifiers[attr_name.to_sym] = identifier 
    end 

    def identifier_for(attr_name) 
     @@identifiers.fetch(attr_name.to_sym){ raise ArgumentError, "unknown API attribute #{attr_name.inspect}" } 
    end 
    end 

    def identifier_for(attr_name) 
    self.class.identifier_for(attr_name) 
    end 

end 

# And include it in your API classes 
class Balls 
    include ApiThing 

    api_attribute :color, String,  identifier: 'SOME__fancy_identifier' 
    api_attribute :number, Integer, identifier: 'SOME__other_identifier' 
    api_attribute :size, BigDecimal, identifier: 'THAT__third_identifier' 
end 

# The attributes will be registered with Virtus – as usual 
puts Balls.attribute_set[:color].type #=> Axiom::Types::String 
puts Balls.attribute_set[:number].type #=> Axiom::Types::Integer 
puts Balls.attribute_set[:size].type #=> Axiom::Types::Decimal 

# You can use the handy Virtus constructor that takes a hash – as usual 
b = Balls.new(color: 'red', number: 2, size: 42) 

# You can access the attribute values – as usual 
puts b.color  #=> "red" 
puts b.number  #=> 2 
puts b.size  #=> 0.42e2 
puts b.durability #=> undefined method `durability' [...] 

# You can ask the instance about identifiers 
puts b.identifier_for :color  #=> "SOME__fancy_identifier" 
puts b.identifier_for :durability #=> unknown API attribute :durability (ArgumentError) 

# And you can ask the class about identifiers 
puts Balls.identifier_for :color #=> "SOME__fancy_identifier" 
puts Balls.identifier_for :durability #=> unknown API attribute :durability (ArgumentError) 

Nie potrzebujesz Virtusa, aby zaimplementować swoje identyfikatory API. Podobny moduł pomocniczy mógłby po prostu zarejestrować atrybuty Virtus zamiast attr_accessor.
Jednak Virtus ma inne przydatne funkcje, takie jak konstruktory haszujące i współczynnik atrybutu. Jeśli nie masz nic przeciwko życiu bez tych funkcji lub znalezieniu zastępstw, porzucenie Virtusa nie powinno stanowić problemu.

HTH! :)

+0

Problem rozwiązany. to jest świetne! Dziękuję Ci –

3

Tak, masz do przedłużenia Virtus::Attribute, mogę zmusić go do pracy z:

module Virtus 
    class AttributeSet < Module 
    def define_identifier(attribute, method_name, visibility, identifier) 
     define_method(method_name) { identifier } 
     send(visibility, method_name) 
    end 
    end 

    class Attribute 
    def define_accessor_methods(attribute_set) 
     attribute_set.define_reader_method(self, name,  options[:reader]) 
     attribute_set.define_writer_method(self, "#{name}=", options[:writer]) 
     attribute_set.define_identifier(self, "#{name}_identifier", options[:reader], options[:identifier]) 
    end 
    end 
end 

To może być refactored ale można c.case_number_identifier

+0

Co robi "send (visibility, nazwa_metody)? – Raffael

+0

Skopiowałem kod z [define_reader_method] (https://github.com/solnic/virtus/blob/master/lib/virtus/attribute_set.rb#L133), aby był spójny. [send] (http://apidock.com/ruby/Object/send) wywołuje metodę, może virtus zastępuje metodę 'send', która ma przekształcić w' public_send', ponieważ 'visibility' jest w tym przypadku publiczna. – MaicolBen

+1

Dziękuję za ten kod! Obie odpowiedzi rozwiązują ten problem. –