首页 > 解决方案 > 为什么在活动记录模型中包含模块会导致模型被重新定义为普通的 ruby​​ 类?

问题描述

我有一个 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

当一个人完成保存一个 activerecord 对象,然后尝试类似Model.countor的东西时,就会出现问题Model.all

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

但是,当我进行类似的后续调用Listing.count时,失败并显示以下错误消息NoMethodError: undefined method count for Listing:Class,并且当我检查Listing我得到的祖先时,[Listing, ActiveSupport::ToJsonWithActiveSupportEncoder, Object, PP::ObjectMixin, MakeMakefile, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, Kernel, BasicObject]这意味着它不再从活动记录库继承

为什么在模块中覆盖此 activerecord .save 方法会导致Listing模型不再从 ActiveRecord::Base 继承,如何解决?

标签: ruby-on-railsrubyblockchain

解决方案


推荐阅读