0

first time asking a question on stack overflow :)

I'm having a conflict between friendly_id and active admin (it's an assumption), as discussed in many threads here. I've looked at all those threads, but I'm not entirely sure they solve my problem. Sorry for the really long post!

I'm trying to create friendly links to products on my website. I've added the friendly_id gem and everything works fine in my dev and staging environments, but friendly links fail on production. Here is all my code:

Model:

class Product < ActiveRecord::Base
  extend FriendlyId
  friendly_id :name, use: :slugged
  ...
end

Controller:

class ProductsController < ApplicationController
  before_filter :get_product, only: [:show]
  ...

  private

  def get_product
    @product = Product.friendly.find(params[:id])
  end
end

All my product records have a completed slug field at this point. I don't want to use slugs in my admin interface, so when I came across a solution here, I went ahead and modified it a bit to get active admin to work together with friendly_id.

config/initializers/active_admin.rb:

ActiveAdmin.setup do |config|
  ...

  config.before_filter :revert_friendly_id
end

I've defined revert_friendly_id in the application controller:

class ApplicationController < ActionController::Base
  ...
  protected

  def revert_friendly_id
    model_name = self.class.name.match(/::(.*)Controller$/)[1].singularize

    # Will throw a NameError if the class does not exist
    Module.const_get model_name

    eval(model_name).class_eval do
      def to_param
        id.to_s
      end
    end
    rescue NameError
  end
end

I've noticed that when I first deploy to production via capistrano, the friendly links work as expected. So my product links are accessible with: http://website.com/products/my-product-slug. But the minute I access the admin interface on production, the links immediately switch back to product ids instead: http://website.com/products/12345. I'm not entirely sure how to resolve this problem, though I understand why it might be happening, can someone help me please?

Community
  • 1
  • 1
harini
  • 21
  • 3

4 Answers4

2

Here is how I solved the problem. Based on armstrjare's fix at this link.

I removed the revert_friendly_id function from my application controller and the before_filter from my config. Then just added the following to app/admin/product.rb:

ActiveAdmin.register Product do

  around_filter do |controller, action|
    Product.class_eval do
      alias :__active_admin_to_param :to_param
      def to_param() id.to_s end
    end

    begin
      action.call
    ensure
      Product.class_eval do
        alias :to_param :__active_admin_to_param
      end
    end
  end
...
end

And everything worked as expected. Hope this helps someone else!

harini
  • 21
  • 3
  • You can redefine `find_resource` method in `controller`: ``` controller do def find_resource scoped_collection.friendly.find(params[:id]) end end ``` you need to use the `finder:` option (from https://github.com/activeadmin/inherited_resources/blob/master/lib/inherited_resources/belongs_to_helpers.rb#L17) For example: `belongs_to :content, finder: :find_by_slug! ` – Andrew Zelenets Jun 08 '21 at 07:42
2

I found a very simple solution: Just overwrite the to_param in your model and check if it is called from active_admin.

app/models/product.rb:

class Product < ActiveRecord::Base
  def to_param
    if caller.to_s.include?"active_admin"
      id && id.to_s
    else
      slug
    end
  end
end
René Meye
  • 173
  • 1
  • 1
  • 6
0

When you set the to_param method, it will be set on the entire application. So you have to check if the requested controller is in the Admin namespace or not. Based on that you have to switch back the return of the to_param method.

nistvan
  • 2,940
  • 1
  • 16
  • 20
  • But since I'm calling `revert_friendly_id` only for the active admin models via `before_filter` in the active admin config - why is it affecting the other models in my application? – harini Aug 19 '14 at 17:38
  • `to_param` method belongs to the common (!!) ActiveRecord model. It doesn't matter which controller use that model. When you register a model in ActiveAdmin won't be created a new ActiveRecord model. – nistvan Aug 19 '14 at 17:54
  • Thanks, I'll check for how I can solve this and update this thread if it works :) – harini Aug 19 '14 at 18:10
  • would you know why this doesn't break on development/staging and only happens on production? I'm just trying to understand this problem a little better. – harini Aug 19 '14 at 18:31
  • In development mode there isn't any caching. Every request will reload all of the classes (including models). In production mode classes will be cashed by default. You can set this in config/environments/*.rb files. – nistvan Aug 20 '14 at 09:04
  • Thanks for all your answers, I was able to solve this problem using a solution detailed at this [link](https://github.com/activeadmin/activeadmin/issues/279) using armstrjare's fix. I'll update that as an answer to this question, hope it will help someone else! – harini Aug 20 '14 at 21:18
0

You can redefine find_resource method in controller:

   controller do
      def find_resource
        scoped_collection.friendly.find(params[:id])
      end
   end

For the active_admin belongs_to association you can to use the finder: option (from https://github.com/activeadmin/inherited_resources/blob/master/lib/inherited_resources/belongs_to_helpers.rb#L17)

For example: belongs_to :content, finder: :find_by_slug!