【问题标题】:How to store a string identifier to a model attribute如何将字符串标识符存储到模型属性
【发布时间】:2017-03-02 00:18:56
【问题描述】:

我正在使用 Virtus 创建代表 Salesforce 对象的模型。

我正在尝试创建具有友好名称的属性,这些属性用于访问可用于检索该变量的标识符“字符串”的值和方法。

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

友好名称用作getter/setter和一个标识符,我可以存储API名称对应的每个属性。

这是一个例子:

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'

或者,不是在属性本身上有一个方法,而是为每个标识符集创建一个辅助方法:

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

我可以扩展Virtus::Attribute 并添加这个吗?如果是这样,我不确定如何去做。

【问题讨论】:

  • Jaison,这个问题的赏金怎么了?好像就这么凭空消失了?请帮我理解。
  • 我不知道!我试图弄清楚为什么赏金从未完成。我设置了另一个赏金,我会在 24 小时等待时间后将其连接起来

标签: ruby salesforce virtus


【解决方案1】:

猴子修补 Virtus 的 Attribute 类当然是一种选择。
但是,进入库的内部会使您容易受到该库源代码私有部分的重构。

相反,您可以使用封装此功能的辅助模块。这里有一个建议:

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)

您不需要 Virtus 来实现您的 API 标识符。一个类似的帮助模块可以只注册attr_accessors 而不是 Virtus 属性。
但是,Virtus 还具有其他方便的功能,例如散列构造函数和属性强制转换。如果您不介意没有这些功能或寻找替代品,那么放弃 Virtus 应该不是问题。

HTH! :)

【讨论】:

    【解决方案2】:

    是的,你必须扩展 Virtus::Attribute,我可以让它工作:

    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
    

    这可以重构,但你可以c.case_number_identifier

    【讨论】:

    • send(visibility, method_name) 是做什么的?
    • 我从define_reader_method 复制了代码,以使其保持一致。 send 调用方法,可能virtus 会覆盖send 方法在public_send 中进行转换,因为visibility 在这种情况下是公共的。
    • 感谢您提供此代码!这两个答案都解决了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-06
    • 2018-01-04
    • 2013-12-31
    • 2021-10-18
    • 1970-01-01
    • 2012-10-20
    相关资源
    最近更新 更多