norman / friendly_id

FriendlyId is the “Swiss Army bulldozer” of slugging and permalink plugins for ActiveRecord. It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models.
http://norman.github.io/friendly_id/
MIT License
6.14k stars 589 forks source link

Conditional disabling FriendlyId #691

Closed fabn closed 8 years ago

fabn commented 9 years ago

I've added FriendlyId to an existing project which uses ActiveAdmin for backend and I encountered lot of errors in my test suite (I'm using scoped module). I already saw other issues (e.g. #526 and #653) and while some of them can be solved by overriding AA find_resource scoping issues can't be solved.

Also in admin interfaces one may want to retain url with id instead of slugs for debugging purposes.

Finally I have lot of code and since I'm lazy I don't want to rewrite all of it :-)

For these reason I wrote a small monkeypatch to disable FriendlyId in active admin code. Here's it:

module FriendlyId

  # This module is used to conditionally enable friendly_id #to_param since it may cause issues when
  # friendly id is used with :scope and other frameworks that rely on `link_to`
  module Disabler
    # Used as key in `Thread.current` hash, if not set this module does nothing
    THREAD_LOCAL_KEY = :__friendly_id_enabler_disabled

    class << self

      # Return true iff friendly_id is currently disabled
      def disabled?
        !!Thread.current[THREAD_LOCAL_KEY]
      end

      # Run a block with friendly id disabled
      def disable_friendly_id(&block)
        begin
          old_value, Thread.current[THREAD_LOCAL_KEY] = Thread.current[THREAD_LOCAL_KEY], true
          block.call
        ensure
          Thread.current[THREAD_LOCAL_KEY] = old_value
        end
      end
    end
  end
end

In the model:

      # Override again the to_param method to provide conditional support for friendly id
      def to_param
        # the expression (id && id.to_s) is the original active record implementation
        ::FriendlyId::Disabler.disabled? ? (id && id.to_s) : super
      end

And finally activate it in ActiveAdmin with this code:

module ActiveAdmin::FriendlyIdDisabler
  extend ActiveSupport::Concern
  included do
    # Disable friendly id in all active admin controllers
    prepend_around_action :disable_friendly_id
  end

  def disable_friendly_id(&action)
    FriendlyId::Disabler.disable_friendly_id(&action)
  end
end

ActiveAdmin::BaseController.include(ActiveAdmin::FriendlyIdDisabler)

Now I have two questions:

ASnow commented 8 years ago

Greate!

kimrgrey commented 8 years ago

It could be useful in some cases to disable friendly_id. But as for me this behaviour could be easily and nicely implemented like, for example, separated gem and should not be a part of core functionality.

jtomaszewski commented 7 years ago

@fabn are you still using the code sample u gave here? is it working well?

fabn commented 7 years ago

@jtomaszewski that project was dismissed some time ago, but that code worked well in production on a high traffic website with no issues for more than one year.

Just keep in mind that it was a Rails 4.2.x project running AA 1.0.0.pre, however since the code is pretty simple it should work also in other versions.

shamimevatix commented 2 years ago

@fabn Above code worked well in rails 4 But recently I had to update the rails to 7.0.3 and ruby 2.7.2 and I am getting this error:

ActiveAdmin::BaseController.include is throwing .rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/activeadmin-2.13.1/lib/active_admin/base_controller/authorization.rb:3:in `module:ActiveAdmin': uninitialized constant InheritedResources::Base (NameError)

because of the line ActiveAdmin::BaseController.include(ActiveAdmin::FriendlyIdDisabler)