为什么在活动记录模型中包含模块会导致将模型重新定义为常规的ruby类?

时间:2018-08-21 14:51:32

标签: ruby-on-rails ruby blockchain

我有一个Active Record模型(清单),该模型应该将一些属性存储在数据库中,而其他一些属性将存储在区块链(以太坊)中。

理想情况下,当开发人员执行model_record.save时,应该重写活动记录保存方法,首先将适当的属性保存在区块链中,然后再进行常规的活动记录保存。此过程适用于大多数其他方法,例如.update.destroy.create

通过在模型中添加宏级别声明以将区块链属性与常规活动记录列区分开来实现以下目的

class Listing < ApplicationRecord
   include BlockchainRecord

   self.table_name = "listings"
   blockchain_properties :parcel_no,
                         :description
end

模块BlockchainRecord也已包含在模型中,并包含如下所示的方法

module BlockchainRecord

  extend ActiveSupport::Concern
  ...............


  class_methods do
    def blockchain_properties(*props)
      @@blockchain_attributes = props


      define_method :blockchain_attributes do
        props
      end

      props.each do |prop|
        define_method prop do
          if self.instance_variable_defined?("@#{prop}")
            self.instance_variable_get("@#{prop}") 
          else
            if self.address
              val = self.contract.call.send("#{prop}") 
              self.instance_variable_set("@#{prop}", val)
              val
            else
              nil
            end
          end
        end

        # Setter methods
        define_method "#{prop}=" do |arg|
          self.instance_variable_set("@#{prop}", arg)
        end
      end
    end

    def new(props = {})
      model_record = super(props.except(blockchain_attributes))

      blockchainProps = props.slice(*blockchain_attributes)
      blockchainProps.each do |k, v|
        model_record.send("#{k}=", v)
      end

      return model_record
    end
  end

  def save
    if self.address
      blockchain_attributes.each do |prop|
        self.contract.transact_and_wait.send("set_#{prop}", self.send("#{prop}")) 
        sleep(2)
      end
      super
    else
      # in case we call Class.new then Object.save
      contract = Ethereum::Contract.create(file: self.class.contract_file_path)
      key = Eth::Key.new(priv: ENV["PRIVATE_KEY"])
      contract.key = key
      self.address = contract.deploy_and_wait(*(self.attributes.slice(*blockchain_attributes).values))
      super
    end
  end

end

当完成一个活动记录对象的保存,然后再尝试进行类似Model.countModel.all的操作时,就会发生此问题。

例如,如果我这样做了, l = Listing.new(parcel_no: 1234, description: "...", registration_section: "...")然后保存记录l.save,该记录将正确存储区块链属性(parcel_no和description),然后调用ActiveRecord提供的.save将其余属性保存到数据库。

但是,当我进行类似Listing.count的后续调用时,失败并显示以下错误消息 NoMethodError:Listing:Class的未定义方法计数,并且当我检查{的祖先时{1}}我得到Listing,这意味着它不再从活动记录库中继承

为什么在模块中覆盖该activerecord .save方法会导致[Listing, ActiveSupport::ToJsonWithActiveSupportEncoder, Object, PP::ObjectMixin, MakeMakefile, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, Kernel, BasicObject]模型不再继承自ActiveRecord :: Base以及如何解决?

0 个答案:

没有答案