przbadu
przbadu

Reputation: 6049

ActiveAdmin with friendly id

I am using friendly_id in my rails 4 application with slug. Now I am using active_admin gem.

Problem:

When I click on show link from active admin for Group resource, It is throwing the following exception:

ActiveRecord::RecordNotFound at /admin/groups/username20-s-group-1

I guess, I need to override some of the active_admin default functions?

Upvotes: 12

Views: 5208

Answers (7)

Kevin Pérez
Kevin Pérez

Reputation: 25

Really late to the party here, but we were struggling recently with this in a Rails 7.0 app and I wanted to share our fix.

If you see the uninitialized constant InheritedResources::Base (NameError) error, put your class_eval block inside an after_initialize block in config/initializers/active_admin.rb.

Rails.application.configure do
  config.after_initialize do
    ActiveAdmin::ResourceController.class_eval do
      def find_resource
        finder = resource_class.is_a?(FriendlyId) ? :slug : :id
        scoped_collection.find_by(finder => params[:id])
      end
    end
  end
end

This works because it will overwrite #find_resource after Rails has initialized instead of in the middle of it while other classes have not loaded yet.

Upvotes: 1

Silvio Mayolo
Silvio Mayolo

Reputation: 70367

If you've tried some of the other answers here and gotten

uninitialized constant InheritedResources::Base (NameError)

Then you might consider monkey patching FriendlyId rather than ActiveAdmin. Create a new initializer file config/initializers/friendly_id_monkey_patch.rb containing this:

module FriendlyIdModelMonkeyPatch

  def to_param
    if caller.to_s.include? 'active_admin'
      id&.to_s
    else
      super
    end
  end

end

module FriendlyId::Model
  prepend FriendlyIdModelMonkeyPatch
end

Now all of your FriendlyId models will revert to using their ID in ActiveAdmin and their slug everywhere else.

See also this answer, which does the same thing but for only one model (rather than monkey patching for all FriendlyId models)

Upvotes: 2

Timo
Timo

Reputation: 3424

IMHO, it's suboptimal to completely override the find_resource as most of the answers suggest. Better to prepend a module and call super preserving normal behaviour when FriendlyId is not in use. For reference you can check how this method is currently (as of writing) implemented, it is not simply scoped_collection.find(params[:id]) as one might think: https://github.com/activeadmin/activeadmin/blob/b45b1fb05af9a7f6c5e2be94f61cf4a5f60ff3bb/lib/active_admin/resource_controller/data_access.rb#L104

module ActiveAdminFriendlyIdScoping
  def find_resource
    if resource_class.is_a? FriendlyId
      scoped_collection.friendly.find params[:id]
      # Or potentially even
      # scoped_collection.friendly.send method_for_find, params[:id]
      # Or you could do something like this
      # raise "Using FriendlyId, find method configuration ignored" if method_for_find != :find
    else
      super
    end
  end
end

ActiveAdmin.setup do |config|
  #...
  Rails.application.config.to_prepare do
    ActiveAdmin::ResourceController.prepend(ActiveAdminFriendlyIdScoping)
  end
end

Upvotes: 0

Quentin
Quentin

Reputation: 1942

A better approach to @AndreyDeineko's is to override ActiveAdmin::ResourceController's find_resource method in config/initialisers/active_admin.rb and leverage the methods provided by FriendlyId (5.x at this point):

In config/initialisers/active_admin.rb:

ActiveAdmin.setup do |config|
  # == Friendly Id addon
  ActiveAdmin::ResourceController.class_eval do
    def find_resource
      if resource_class.is_a?(FriendlyId)
        scoped_collection.friendly.find(params[:id])
      else
        scoped_collection.find(params[:id])
      end
    end
  end
  # initial config
end

This looks much cleaner to me, than putting it in the application controller, as it is related to the configuration of Active Admin.

Upvotes: 22

Andrey Deineko
Andrey Deineko

Reputation: 52367

There are cases, when application has quit a few resources, hence in order to keep it DRY there is a nice solution requiring few lines of code for whole application - simply override activeadmin's resource controller.

Create config/initializers/active_admin_monkey_patching.rb file with the following content:

ActiveAdmin::ResourceController.class_eval do
  def find_resource
    finder = resource_class.is_a?(FriendlyId) ? :slug : :id
    scoped_collection.find_by(finder => params[:id])
  end
end

Do not forget to restart the server.

Upvotes: 30

obi1kinobi
obi1kinobi

Reputation: 41

  class User < ActiveRecord::Base
     extend FriendlyId
  friendly_id :username, :use => [:slugged, :finders]

Upvotes: 0

przbadu
przbadu

Reputation: 6049

Found solution for the problem:

In your app/admin/[ResourceName.rb] add:

  # app/admin/group.rb

  # find record with slug(friendly_id)
  controller do
    def find_resource
      begin
        scoped_collection.where(slug: params[:id]).first!
      rescue ActiveRecord::RecordNotFound
        scoped_collection.find(params[:id])
      end
    end
  end

This solved my problem.

Upvotes: 6

Related Questions