Ahmed Khattab
Ahmed Khattab

Reputation: 2799

Rails, issue with auto loading

So im having this weird issue for some reason i dont know why it is happening.

So, i have this extension in conifg/initializers/has_friendly_token.rb

ActiveRecord::Base.define_singleton_method :has_friendly_token do
  extend FriendlyId
  friendly_id :token
  before_validation :set_unique_token, if: :set_token?
  define_method :set_token? do
    new_record? || saved_change_to_token?
  end
  define_method :set_unique_token do
    table_name = self.model_name.route_key
    scope = persisted? ? self.class.where("#{table_name}.id != ?", id) : self.class.default_scoped
    while token.nil? || scope.where(token: token).exists?
      self.token = SecureRandom.base58(24)
    end
  end
end

And I'm using it in two different models

class Business < ApplicationRecord
  include Colorful, Channels, Rails.application.routes.url_helpers

  has_friendly_token

end

and

class Conversation < ApplicationRecord
  has_friendly_token

  include AASM, Colorful, Rails.application.routes.url_helpers
end

the thing is, whenver i try to to run i get this error

`method_missing': undefined local variable or method `has_friendly_token' for Business (call 'Business.connection' to establish a
connection):Class (NameError)
Did you mean?  has_secure_token

but it works in conversation. Any ideas why this happenes?

Upvotes: 0

Views: 55

Answers (1)

max
max

Reputation: 102423

I can't see a good reason to use an initializer to monkeypatch ActiveRecord::Base here in the first place. Instead create a module and extend your ApplicationRecord class with it as this is simply a "macro" method.

module FriendlyToken
  def has_friendly_token 
    extend FriendlyId
    friendly_id :token
    before_validation :set_unique_token, if: :set_token?
    define_method :set_token? do
      new_record? || saved_change_to_token?
    end
    define_method :set_unique_token do
      table_name = self.model_name.route_key
      scope = persisted? ? self.class.where("#{table_name}.id != ?", id) : self.class.default_scoped
      while token.nil? || scope.where(token: token).exists?
        self.token = SecureRandom.base58(24)
      end
    end
  end
end
class ApplicationRecord < ActiveRecord::Base
  extend FriendlyToken
end

Unlike a monkeypatch its easy to test this code and it will be picked up by documentation tools like rdoc or yard as well as coverage tools.

You could also just simply write it as a concern since your macro method does not take any arguments anyways:

module FriendlyToken
  extend ActiveSupport::Concern

  included do
    extend FriendlyId
    friendly_id :token
    before_validation :set_unique_token, if: :set_token?
  end

  def set_token?
    new_record? || saved_change_to_token?
  end
  
  def set_unique_token?
    table_name = self.model_name.route_key
    scope = persisted? ? self.class.where("#{table_name}.id != ?", id) : self.class.default_scoped
    while token.nil? || scope.where(token: token).exists?
      self.token = SecureRandom.base58(24)
    end
  end
end

class Business < ApplicationRecord
  include Colorful, 
          Channels, 
          Rails.application.routes.url_helpers, 
          FriendlyToken
end

The advantage here is easier debugging since the methods are not defined dynamically.

See 4 Ways To Avoid Monkey Patching.

Upvotes: 1

Related Questions