Last Updated: February 25, 2016
·
8.826K
· royletron

ActiveAdmin vs Nested Models vs Formtastic vs Acts as Relation

Picture

So lets set the scene. You are using Acts as Relation because it allows you to define inheritance within your models, in my case I had a Presentation which was extending a Resource

# app/models/presentation.rb
class Presentation < ActiveRecord::Base
  acts_as :resource
  attr_accessible :markup
end

# app/models/resource.rb
class Resource < ActiveRecord::Base
 acts_as_superclass

 belongs_to :topic
 belongs_to :level

 attr_accessible :name, :topic, :level
end

You will also notice that a Resource has a Topic and a Level that it belongs to.

# app/models/topic.rb
class Topic < ActiveRecord::Base
  attr_accessible :name
  has_many :resources
end

# app/models/level.rb
class Level < ActiveRecord::Base
  attr_accessible :name
  has_many :resources
end

Finally we want all of this to work nicely with the awesome ActiveAdmin gem, which creates a lovely administration front end to whichever models you define. In this case I wanted to have a 'Presentation' section where I could create a Presentation with a name, set its level and topic from drop downs of the currently available records for those two types, and finally write some markup. So there is stuff coming from a lot of different models, into the same display, and in ActiveAdmin's case this means working with Formtastic. The first thing that you need to do is generate your ActiveAdmin model

rails generate active_admin:resource Presentation

If you then jump into your admin area you will find it has create a Presentation section, but currently only contains the markup field that comes directly from the Presentation model. Adding name from the Resource model is quite simple, you just need to add as it hasn't been picked up by the ActiveAdmin generator.

# app/admin/presentations.rb
ActiveAdmin.register Presentation do
  form do |f|
    f.inputs do
      f.input :name
      f.input :markup
    end
    f.buttons
  end
end

So now we want to add the level and topic selectors, which should pull in all possible entries in their respective models. Thankfully Formtastic has a rather nice way of doing this in a single line for each model

# app/admin/presentations.rb
...
      f.input :name
      f.input :level, :as => :select, :collection => Level.all, :include_blank => false, :selected => (presentation.level.id if !presentation.level.nil?)
      f.input :topic, :as => :select, :collection => Topic.all, :include_blank => false, :selected => (presentation.topic.id if !presentation.topic.nil?)

      f.input :markup
...

If you try the form at this point you will probably find that you get an ActiveRecord::AssociationTypeMismatch error, and this is down to Formtastic using the level and topic id reference within the form. In order to fix this one, you need to actually go into the controller of ActiveAdmin and modify its create method, again thankfully quite simple. At this point I will also add the index function of ActiveAdmin so that we see the correct fields in the index as well:

ActiveAdmin.register Presentation do
  form do |f|
    f.inputs do
      f.input :name
      f.input :level, :as => :select, :collection => Level.all, :include_blank => false, :selected => (presentation.level.id if !presentation.level.nil?)
      f.input :topic, :as => :select, :collection => Topic.all, :include_blank => false, :selected => (presentation.topic.id if !presentation.topic.nil?)

      f.input :markup
    end
    f.buttons
  end
  index do                            
    column :name                     
    column :level        
    column :topic           
    column :markup             
    default_actions                   
  end
  controller do
    def create
      @level = Level.find(params[:presentation][:level])
      @topic = Topic.find(params[:presentation][:topic])
      params[:presentation][:level] = @level
      params[:presentation][:topic] = @topic
      @presentation = Presentation.new(params[:presentation])
      super
    end
  end
end

And that'll do it! Although this looks fairly overly complicated, it actually represents quite an intricate and easily extendable basis to create lots of different models made of the same bits.