简体   繁体   中英

Accessing class property to define instance methods in rails concern

I'm trying my hands on metaprogramming after a long pause. I found a few questions but could not get an input to solve my problem so I hope someone can enlighten me.

In a rails 5 app, I am trying to write a concern that provides a class method to set configuration options. With those options, I want to define instance methods.

module Base64Attachable
  extend ActiveSupport::Concern

  class_methods do
    attr_reader :base64_attachable_property

    private

    def base64_attachable(property)
      @base64_attachable_property = property
    end
  end

  included do
    # ?
  end
end

The concern above is used inside a User model:

class User < ApplicationRecord
  include Base64Attachable
  base64_attachable :image
end

In my understanding, the concern sets up the class method that is being called in the user model. However I do not seem to be able to get the base64_attachable_property inside the included block to define further methods based on the value of it. I thought I would find anything I need in self.class inside the included block, but that's not the case.

The aim in this case is to use define_method to define setters, getters and other methods for image in the user model.

What am I missing here?

The included block is run at the moment the concern is included on the class, the base64_attachable :image line is not run yet.

I'd suggest you follow what official gems does. Check ActiveStorage for example https://github.com/rails/rails/blob/530f7805ed5790af1d472a041bc74089dc183f47/activestorage/lib/active_storage/attached/model.rb#L35 . It defines the methods that depends on that property right inside the class method (it uses class_eval, but I guess you can use define_method too):

  def has_one_attached(name, dependent: :purge_later)
    generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
      def #{name}
        @active_storage_attached_#{name} ||= ActiveStorage::Attached::One.new("#{name}", self)
      end
      def #{name}=(attachable)
        attachment_changes["#{name}"] =
          if attachable.nil?
            ActiveStorage::Attached::Changes::DeleteOne.new("#{name}", self)
          else
            ActiveStorage::Attached::Changes::CreateOne.new("#{name}", self, attachable)
          end
      end
    CODE

    has_one :"#{name}_attachment", ......etc....

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM