<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://skryukov.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://skryukov.github.io/" rel="alternate" type="text/html" /><updated>2023-10-13T08:25:35+00:00</updated><id>https://skryukov.github.io/feed.xml</id><title type="html">Svyatoslav Kryukov</title><subtitle>Svyatoslav Kryukov's personal page</subtitle><author><name>Svyatoslav Kryukov</name><email>s.g.kryukov@yandex.ru</email></author><entry><title type="html">An Unofficial Active Admin Guide</title><link href="https://skryukov.github.io/rails/activeadmin/2020/09/29/an-unofficial-active-admin-guide.html" rel="alternate" type="text/html" title="An Unofficial Active Admin Guide" /><published>2020-09-29T20:32:11+00:00</published><updated>2020-09-29T20:32:11+00:00</updated><id>https://skryukov.github.io/rails/activeadmin/2020/09/29/an-unofficial-active-admin-guide</id><content type="html" xml:base="https://skryukov.github.io/rails/activeadmin/2020/09/29/an-unofficial-active-admin-guide.html">&lt;p&gt;Recently I bumped into &lt;strong&gt;Rails Survey 2020&lt;/strong&gt; results and saw &lt;a href=&quot;https://rails-hosting.com/2020/#which-ruby-gems-frustrate-you-the-most&quot;&gt;the top 10 gems frustrate one the most&lt;/a&gt;.
On the 5th place, there was the &lt;strong&gt;Active Admin&lt;/strong&gt; gem. I would not say this was an unexpected result.
I often come across the opinion that Active Admin is only suitable for a 15-minute blog, but there
is much more with this library.&lt;/p&gt;

&lt;p&gt;Here are some approaches my colleagues and I take when working with Active Admin.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/main.jpg&quot; alt=&quot;An Unofficial Active Admin Guide&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Active Admin is based on several libraries, among which I would highlight &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arbre&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formtastic&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inherited_resources&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransack&lt;/code&gt;. Each of them is responsible for its part and deserves separate
consideration. Let’s start alphabetically with the library extracted from Active Admin itself.&lt;/p&gt;

&lt;h2 id=&quot;arbre-custom-components&quot;&gt;Arbre: custom components&lt;/h2&gt;

&lt;p&gt;One of the problems with Active Admin is rapidly growing resource files: filters, additional
actions, templates, forms, and so on – everything is in one file. I can hear a lonely moan somewhere
in the distance: “what about the single responsibility principle?” There is none. Let me show you
how you can isolate some templates in separate classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arbre&lt;/strong&gt; is a library for defining templates using Ruby objects. Here’s an example of a basic page
written with Arbre DSL:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Welcome page'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;para&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Hello, world'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;DSL can be extended with components. For example, in Active Admin, these are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tabs&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;table_for&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paginated_collection&lt;/code&gt;, and even resource pages themselves.  Next, we’ll dive in and explore the structure
of the basic Arbre component.&lt;/p&gt;

&lt;h3 id=&quot;arbre-hello-world-component&quot;&gt;Arbre: hello world component&lt;/h3&gt;

&lt;p&gt;Like all Arbre components, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Admin::Components::HelloWorld&lt;/code&gt; inherits from
&lt;a href=&quot;https://github.com/activeadmin/arbre/blob/master/lib/arbre/component.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Arbre::Component&lt;/code&gt;&lt;/a&gt; class:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/components/hello_world.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Admin&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Components&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorld&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Arbre&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Component&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;builder_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:hello_world&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;text_node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Hello world!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;add_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'hello-world'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tag_name&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;'h1'&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Starting from the top: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder_method&lt;/code&gt; defines a method to create a component using DSL.
Arguments passed to the component will be passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#build&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Each Arbre component is a separate DOM element (similar to the way modern frontend frameworks work,
only dates back to 2012). All components are rendered as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; DOM elements by default. You can
override &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#tag_name&lt;/code&gt; method to change this behavior. As you might guess, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#add_class&lt;/code&gt; method adds a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; attribute to the root DOM element.&lt;/p&gt;

&lt;p&gt;At this point, the only thing left is to call our new component.
For example, let’s do this in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/admin/dashboard.rb&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/dashboard.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_page&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Dashboard'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;menu&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;priority: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;proc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active_admin.dashboard'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;hello_world&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/01-hello-world-component.jpg&quot; alt=&quot;Hello world component&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Up next is an example of a small refactoring of the admin panel using a custom component.&lt;/p&gt;

&lt;h3 id=&quot;arbre-almost-a-real-life-example&quot;&gt;Arbre: (almost) a real-life example&lt;/h3&gt;

&lt;p&gt;To understand how to use Arbre in a production environment, let’s assume that we have a blog with
posts (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Post&lt;/code&gt;) and comments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Comment&lt;/code&gt;) with a 1:M relationship. We need to display the last ten
comments on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; page of a post.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;permit_params&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active_admin.posts.new_comments'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;table_for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;created_at: :desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/02-new-comments.jpg&quot; alt=&quot;New comments component&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we’ll move the table with comments into a separate component. Create a new class and inherit it
from &lt;a href=&quot;https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/views/components/panel.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveAdmin::Views::Panel&lt;/code&gt;&lt;/a&gt;.
If you create a new component from scratch (as in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello_world&lt;/code&gt; example above) and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;panel&lt;/code&gt; from
it, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;panel&lt;/code&gt; will be wrapped by another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;, and this will probably break the layout.&lt;/p&gt;

&lt;p&gt;Put our new class in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/admin/components/posts/new_comments.rb&lt;/code&gt;, since Active Admin automatically
requires everything inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/admin/**/*&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/components/posts/new_comments.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Admin&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Components&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Posts&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;NewComments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Panel&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;builder_method&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts_new_comments&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active_admin.posts.new_comments'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;table_for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

        &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;last_comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;comments&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;panel&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/admin/posts.rb&lt;/code&gt; with our new component
and pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; object as an argument:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;permit_params&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;posts_new_comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Awesome! Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; is also available from the component’s context. However, by explicitly
passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; to the builder, we achieve loose coupling, which allows us to reuse the component
in the future.&lt;/p&gt;

&lt;p&gt;Speaking of reuse, we can extract everything from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt; block (as well as other template
blocks) into partial:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;show&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'show'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;post: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/views/admin/posts/_show.html.arb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Localizers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;active_admin_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:details&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;attributes_table_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;posts_new_comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you can use familiar &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.erb&lt;/code&gt; and other templating engines instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.arb&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;arbre-whats-next&quot;&gt;Arbre: what’s next&lt;/h3&gt;

&lt;p&gt;First of all, I do advise you to read Active Admin components’
&lt;a href=&quot;https://activeadmin.info/12-arbre-components.html&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides, you can read code for &lt;a href=&quot;https://github.com/activeadmin/arbre/blob/master/lib/arbre/element.rb&quot;&gt;base components from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arbre&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://github.com/activeadmin/activeadmin/tree/master/lib/active_admin/views/components&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activeadmin&lt;/code&gt; components&lt;/a&gt;.
Those are the components your custom ones will inherit from. Also, check out the gem
&lt;a href=&quot;https://github.com/platanus/activeadmin_addons&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activeadmin_addons&lt;/code&gt;&lt;/a&gt; – it has a lot of interesting
custom components.&lt;/p&gt;

&lt;p&gt;Well, if you still write code with errors (this is still a thing for some reason), check out how to
&lt;a href=&quot;https://github.com/activeadmin/activeadmin/blob/master/spec/unit/views/components/panel_spec.rb&quot;&gt;test custom components&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;formtastic-custom-forms&quot;&gt;Formtastic: custom forms&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Formtastic&lt;/strong&gt; is a library for describing forms using DSL. The simplest form looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;semantic_form_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inputs&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;actions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In the example, Formtastic automatically extracts all attributes from the passed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt; and
inserts them into a form with default input types. A list of available input types can be found in
the &lt;a href=&quot;https://github.com/formtastic/formtastic#the-available-inputs&quot;&gt;README&lt;/a&gt;. Like Arbre, Formtastic
can be extended by creating custom component classes. To understand the basics, we’ll create
a hello world component.&lt;/p&gt;

&lt;h3 id=&quot;formtastic-hello-world-component&quot;&gt;Formtastic: hello world component&lt;/h3&gt;

&lt;p&gt;By analogy with Arbre components, we’ll place the new class in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/admin/inputs&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/inputs/hello_world_input.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorldInput&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Formtastic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Inputs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_html&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;Input for #&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To apply a new input type, simply specify its name as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:as&lt;/code&gt; parameter, for example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inputs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;actions&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/03-hello-world-input.jpg&quot; alt=&quot;Hello world input&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All the parameters required to draw the form (including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;method&lt;/code&gt;) are passed to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#initialize&lt;/code&gt; defined in the module &lt;a href=&quot;https://github.com/formtastic/formtastic/blob/master/lib/formtastic/inputs/base.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Formtastic::Inputs::Base&lt;/code&gt;&lt;/a&gt;.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#to_html&lt;/code&gt; method is responsible for rendering the input.&lt;/p&gt;

&lt;p&gt;The example may seem useless, but in fact, we use it to render read-only fields. To turn our hello
world to a useful read-only input, we only need to add a couple of methods from
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Formtastic::Inputs::Base&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/inputs/hello_world_input.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HelloWorldInput&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Formtastic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Inputs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_html&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_wrapping&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;label_html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;format_attribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/04-read-only-input.jpg&quot; alt=&quot;Read-only input&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_wrapping&lt;/code&gt; from &lt;a href=&quot;https://github.com/formtastic/formtastic/blob/master/lib/formtastic/inputs/base/wrapping.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Formtastic::Inputs::Base::Wrapping&lt;/code&gt;&lt;/a&gt;
module is responsible for input wrapping and rendering error output and hints. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;label_html&lt;/code&gt;
from &lt;a href=&quot;https://github.com/formtastic/formtastic/blob/master/lib/formtastic/inputs/base/labelling.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Formtastic::Inputs::Base::Labelling&lt;/code&gt;&lt;/a&gt;
module renders the label for input. These two helpers instantly turn our hello world into
a combat-applicable input. Last but not least, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format_attribute&lt;/code&gt; from
&lt;a href=&quot;https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/view_helpers/display_helper.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveAdmin::ViewHelpers::DisplayHelper&lt;/code&gt;&lt;/a&gt;
is a helper method for rendering input values.&lt;/p&gt;

&lt;p&gt;Now we’ll move to a slightly more complex example that demonstrates how to integrate a JavaScript
library with a form.&lt;/p&gt;

&lt;h3 id=&quot;formtastic-almost-a-real-life-example&quot;&gt;Formtastic: (almost) a real-life example&lt;/h3&gt;

&lt;p&gt;We’ll take another made-up example to demonstrate how to work with HTML, CSS, and JS. In other
words, it will cover all the steps of writing a new input.&lt;/p&gt;

&lt;p&gt;Say we have received a request from our blog editor: when writing a post, he would like to see
the number of words directly in the input form. As you know, the world of JavaScript has libraries
for everything, and there is one for our task too: &lt;a href=&quot;https://github.com/RadLikeWhoa/Countable&quot;&gt;Countable.js&lt;/a&gt;.
Let’s take the standard input for text (textarea) and extend it with a word counter.&lt;/p&gt;

&lt;p&gt;To implement the new input, we need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;take the existing text input and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; to it to output the number of words;&lt;/li&gt;
  &lt;li&gt;add CSS styles for the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;;&lt;/li&gt;
  &lt;li&gt;initialize Countable.js and use it to write the number of words in the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firstly, we need to create a new class and inherit it from
&lt;a href=&quot;https://github.com/formtastic/formtastic/blob/master/lib/formtastic/inputs/text_input.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Formtastic::Inputs::TextInput&lt;/code&gt;&lt;/a&gt;.
Add attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class=&quot;countable-input&quot;&lt;/code&gt; to the element &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;textarea&lt;/code&gt; and define a new empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; with
the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class=&quot;countable-content&quot;&lt;/code&gt; next to it:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/inputs/countable_input.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CountableInput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Formtastic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Inputs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TextInput&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_html&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_wrapping&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;label_html&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text_area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_html_options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;class: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'countable-input'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;content_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;class: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'countable-content'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, have a look at what we have added. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input_html_options&lt;/code&gt; is
&lt;a href=&quot;https://github.com/formtastic/formtastic/blob/master/lib/formtastic/inputs/text_input.rb&quot;&gt;a parent class&lt;/a&gt;
method, which returns HTML attributes for the input.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder&lt;/code&gt; - is an instance of the class
&lt;a href=&quot;https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/form_builder.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveAdmin::FormBuilder&lt;/code&gt;&lt;/a&gt;,
inherited from
&lt;a href=&quot;https://github.com/rails/rails/blob/master/actionview/lib/action_view/helpers/form_helper.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActionView::Helpers::FormBuilder&lt;/code&gt;&lt;/a&gt;.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&lt;/code&gt; is the context in which the templates are executed (basically, a huge set of
view-helpers). Thus, if we need to create a piece of form, we’ll call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builder&lt;/code&gt;. While if we want
to use something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;link_to&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;template&lt;/code&gt; will help us.&lt;/p&gt;

&lt;p&gt;Let’s call the Countable.js library: put it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vendor/assets/javascripts&lt;/code&gt;
directory and add a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.js&lt;/code&gt; file which will call Countable.js and throw the information into
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div.countable-content&lt;/code&gt; (please don’t be harsh on this piece of spaghetti code):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// app/assets/javascripts/inputs/countable_input.js&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//= require countable.min.js&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;countable_initializer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.countable-input&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;Countable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.countable-content&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.countable-content&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;words: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;countable_initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;turbolinks:load&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;countable_initializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we include it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/assets/javascripts/active_admin.js&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// app/assets/javascripts/active_admin.js&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//= require inputs/countable_input&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The last step is to add a CSS file and include it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/assets/stylesheets/active_admin.scss&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// app/assets/stylesheets/inputs/countable_input.scss&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.countable-content&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// app/assets/stylesheets/active_admin.scss&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;//...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;inputs/countable_input&quot;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it! Our new input is ready. Now we can call it in the form:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inputs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :hello_world&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;as: :countable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;actions&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/05-custom-input.jpg&quot; alt=&quot;Custom input&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is how one can make custom components for forms, like file loaders or inputs with tricky
autofill. There is a bit more code in such components, but the approach remains the same.&lt;/p&gt;

&lt;h3 id=&quot;formtastic-warmest-greetings-to-the-dry-principle&quot;&gt;Formtastic: Warmest greetings to the DRY principle&lt;/h3&gt;

&lt;p&gt;As with the Arbre components, forms can be put into partial’s, although the syntax is slightly
different:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'form'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/views/admin/posts/_form.html.arb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;active_admin_form_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The disadvantage of the approach is that forms are placed somewhere deep in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;views&lt;/code&gt; directory.
In my opinion, this makes code navigation a bit more complicated, but that is a matter of taste.&lt;/p&gt;

&lt;h3 id=&quot;formtastic-whats-next&quot;&gt;Formtastic: what’s next&lt;/h3&gt;

&lt;p&gt;Formtastic is a pretty big library, and I would highly recommend reading the detailed
&lt;a href=&quot;https://github.com/formtastic/formtastic&quot;&gt;README&lt;/a&gt; to get acquainted with all customization options.
It will also be useful to see the already mentioned
&lt;a href=&quot;https://github.com/platanus/activeadmin_addons&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activeadmin_addons&lt;/code&gt;&lt;/a&gt; gem. There are lots of
additional inputs in this library that are worth being checked out.&lt;/p&gt;

&lt;p&gt;Needless to say, although I have divided Formtastic and Arbre into different blocks of the article,
they perfectly work together. You can even create forms or parts of forms as Arbre-components.&lt;/p&gt;

&lt;h2 id=&quot;inherited-resources-custom-controllers&quot;&gt;Inherited Resources: custom controllers&lt;/h2&gt;

&lt;p&gt;To understand where does magical &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; come from, how to change the saving behavior, and much
more, we need to get acquainted with another gem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inherited Resources&lt;/strong&gt; is a library designed to reduce the amount of CRUD boilerplate in
controllers.&lt;/p&gt;

&lt;p&gt;On the one hand, the library is pretty simple, but on the other hand, it is quite comprehensive. So
let’s have a quick look at a few useful methods:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PostsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;InheritedResources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: :index&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;updated_by&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_user&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;posts_path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.respond_to&lt;/code&gt; is responsible for the available formats. All &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.respond_to&lt;/code&gt; calls are stacked rather
than overriding each other. To reset the formats we need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.clear_respond_to&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.actions&lt;/code&gt; defines available CRUD methods (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;edit&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;,
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;destroy&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt; is one of the available helpers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;#=&amp;gt; @post&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;#=&amp;gt; @posts&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;resource_class&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;#=&amp;gt; Post&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#update!&lt;/code&gt; is just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#update&lt;/code&gt; which can be used instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt; when
overloading methods.&lt;/p&gt;

&lt;p&gt;Next, we’ll have a look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.has_scope&lt;/code&gt; method in action. Let’s presume that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post&lt;/code&gt; class
has a defined scope &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:published&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:published&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;published: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.has_scope&lt;/code&gt; method in the controller:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PostsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;InheritedResources&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:published&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;type: :boolean&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.has_scope&lt;/code&gt; adds filtering using query-parameters. In the given example, we can apply the scope
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:published&lt;/code&gt; by viewing the collection at URL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/posts?published=true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A detailed description of these and other features of the library can be reached at the rich
&lt;a href=&quot;https://github.com/activeadmin/inherited_resources&quot;&gt;README&lt;/a&gt;. I say we stop here and finally move to
the interaction with Active Admin.&lt;/p&gt;

&lt;h3 id=&quot;inherited-resources-controller-modifications&quot;&gt;Inherited Resources: controller modifications&lt;/h3&gt;

&lt;p&gt;All Active Admin controllers are inherited from
&lt;a href=&quot;https://github.com/activeadmin/inherited_resources/blob/master/app/controllers/inherited_resources/base.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InheritedResources::Base&lt;/code&gt;&lt;/a&gt;,
which means that we can modify their behavior using library methods. For example, here is how the
list of available controller actions is defined:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Great, we removed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delete&lt;/code&gt; action from the article. It seems to be obvious: we use the
Active Admin resource as a controller. But let’s not jump to conclusions yet and try to add another
feature.&lt;/p&gt;

&lt;p&gt;By default, Active Admin includes a rendering of all pages as HTML, JSON, and XML (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index&lt;/code&gt; is also
available in CSV format). Let’s try to get rid of XML rendering for our page using methods that
we’ve already learned:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clear_respond_to&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: :index&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/06-name-error.jpg&quot; alt=&quot;NameError&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh, now we got an error &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined method 'clear_respond_to' for #&amp;lt;ActiveAdmin::ResourceDSL&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;clear_respond_to&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: :index&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Voila, now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:3000/admin/posts.xml&lt;/code&gt; returns an error. And what about modifying the action’s
behavior?&lt;/p&gt;

&lt;h3 id=&quot;inherited-resources-method-overloading&quot;&gt;Inherited Resources: method overloading&lt;/h3&gt;

&lt;p&gt;Assume that when saving we need to set the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post#created_by_admin&lt;/code&gt;. To do this, we’ll take
advantage of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#create&lt;/code&gt; method overloading feature:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;build_resource&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;created_by_admin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build_resource&lt;/code&gt;, a method that initializes a new object and assigns it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@post&lt;/code&gt;
variable. Next, set the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_by_admin&lt;/code&gt; and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create!&lt;/code&gt; (aka &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;super&lt;/code&gt;) which
continues to operate on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@post&lt;/code&gt; variable we created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; be careful with the helpers. Inherited Resources actively uses instance variables. In the
example above, it helped us to create and modify the object, but when used carelessly, the results
may be unexpected (I learned that the hard way).&lt;/p&gt;

&lt;p&gt;Now let’s take a few steps back to the point where we’ve turned off XML rendering of articles.
What if we want to remove XML rendering from all the resources? We wouldn’t write the same code in
every new resource, would we?&lt;/p&gt;

&lt;h3 id=&quot;inherited-resources-base-controller-modifications&quot;&gt;Inherited Resources: base controller modifications&lt;/h3&gt;

&lt;p&gt;No, we wouldn’t! Let’s create a module that will adjust the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveAdmin::ResourceController&lt;/code&gt; class
behavior:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# lib/active_admin/remove_xml_rendering_extension.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ActiveAdmin&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;RemoveXmlRenderingExtension&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:clear_respond_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:respond_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:respond_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;only: :index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;An extensible class will be passed to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.included&lt;/code&gt; method, with all needed modifications applied.
We will use the Active Admin initializer and connect the new module to
&lt;a href=&quot;https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/resource_controller.rb&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveAdmin::ResourceController&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# config/initializers/active_admin.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'lib/active_admin/remove_xml_rendering_extension'&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RemoveXmlRenderingExtension&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A bit of metaprogramming magic with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#include&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#included&lt;/code&gt;, and here you go! Now no resource
would respond to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.xml&lt;/code&gt; format.&lt;/p&gt;

&lt;p&gt;By the way, if you think that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#prepend&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#include&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#extend&lt;/code&gt; methods are only useful to pass
tricky interview questions, that’s quite wrong. When it is necessary to modify the code of the
external library, such approaches are often the only available tool.&lt;/p&gt;

&lt;h3 id=&quot;inherited-resources-whats-next&quot;&gt;Inherited Resources: what’s next&lt;/h3&gt;

&lt;p&gt;First of all, take a good look at the detailed &lt;a href=&quot;https://github.com/activeadmin/inherited_resources&quot;&gt;README&lt;/a&gt;.
In addition, pay attention to how the controllers are organized in Active Admin, notice
the authorization logic, and other little things like additional helpers.&lt;/p&gt;

&lt;h2 id=&quot;ransack-custom-filters&quot;&gt;Ransack: custom filters&lt;/h2&gt;

&lt;p&gt;By default, on each index page, Active Admin provides a powerful block with filtering, from which I
often have to remove something rather than add something new. But this filtering block is just the
tip of the iceberg called Ransack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ransack&lt;/strong&gt; – a library for creating search forms, which allows you to build complex SQL queries by
interpreting the passed parameter names. It sounds complicated, but I’m sure the example will
quickly give you an understanding of what I am talking about.&lt;/p&gt;

&lt;p&gt;For example, suppose that we need to filter blog posts (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post&lt;/code&gt;) by a substring in the title
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;title&lt;/code&gt;). With Ransack we can do so like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ransack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title_cont: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'sharp knives'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The postfix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_cont&lt;/code&gt; is one of the many predicates available in Ransack. Predicates determine which
SQL query is to be generated for search. You can read more about all available predicates in the
official &lt;a href=&quot;https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s make it a bit more complicated: a customer asked us to add a filter that would allow
searching by substring of title and/or body (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt;). With Ransack it is as simple as that:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ransack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;title_or_body_cont: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active admin'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In addition, Ransack allows you to search for records by referring to associated models. For
example, let’s add search by comments (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Comment#text&lt;/code&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ransack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;comments_text_cont: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'I hate type annotations!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As you might guess, such things can grow quickly. Using complex parameters in several places can
lead to problems as well. Ransack suggests using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#ransack_alias&lt;/code&gt; as a solution. Let’s add filtering
by an author name to search by comment and give it a short alias: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;comments&lt;/code&gt; which in the future can
be used with the predicates we need:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ransack_alias&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments_text_or_comments_author&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ransack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;comments_cont: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Matz'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;result&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have learned how Ransack allows us to structure requests. Let’s finally move to how we
can use it in Active Admin.&lt;/p&gt;

&lt;h3 id=&quot;ransack-combined-filters&quot;&gt;Ransack: combined filters&lt;/h3&gt;

&lt;p&gt;Let’s take the example above and use them to filter the Active Admin resource:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;preserve_default_filters!&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title_or_body_cont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;as: :string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active_admin.filters.title_or_body_cont'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;as: :string&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/07-combined-filter.jpg&quot; alt=&quot;Combined filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Basically, that’s it, very straightforward. The only thing I’d like to note is the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#preserve_default_filters!&lt;/code&gt; method which renders default filters.&lt;/p&gt;

&lt;h3 id=&quot;ransack-scope-filters&quot;&gt;Ransack: scope-filters&lt;/h3&gt;

&lt;p&gt;By default, Ransack allows you to filter by all attributes and relationships in the model. It can
be dangerous from a security point of view, so please note that it is possible to restrict access to
certain fields and links using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransackable_attributes&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransackable_associations&lt;/code&gt;, and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransackable_scopes&lt;/code&gt; methods. I would leave authorization issues outside the scope of the article
(especially since Active Admin has a detailed section in its
&lt;a href=&quot;https://activeadmin.info/13-authorization-adapter.html&quot;&gt;documentation&lt;/a&gt;), so let’s only pay
attention to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransackable_scopes&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Unlike other authorization methods, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ransackable_scopes&lt;/code&gt; doesn’t allow using any scope by default.
Thus, to be able to filter by scope (or any other method of the model class), you need to return its
name from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ransackable_scopes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, let’s add a filter by the number of comments using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scope&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments_count_gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;comments_count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;joins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:comments&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'posts.id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;having&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'count(comments.id) &amp;gt; ?'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comments_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ransackable_scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:comments_count_gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth_object&lt;/code&gt;: in theory, this is the object by which you can define an authorization strategy.
I would expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;current_user&lt;/code&gt; to be passed here, but Active Admin does not do it.&lt;/p&gt;

&lt;p&gt;We added a scope and returned its name to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ransackable_scopes&lt;/code&gt;, the only thing left is to add
a filter to the Active Admin resource:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# app/admin/posts.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveAdmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:comments_count_gt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;as: :number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;label: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;I18n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'active_admin.filters.comments_count_gt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/08-comments-count-filter.jpg&quot; alt=&quot;Comments count filter&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There’s one little thing left: if we try to filter all the articles with two or more comments,
everything would be fine, but if we try to submit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;, we would get an error:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/an-unofficial-active-admin-guide/09-argument-error.jpg&quot; alt=&quot;ArgumentError&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It is a type conversion that Ransack does for historical reasons. To disable this questionable
feature, we should add an initializer with the specified parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sanitize_custom_scope_booleans&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# /config/initializers/ransack.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Ransack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sanitize_custom_scope_booleans&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There you go, now the filter works even if we submit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; as an argument, and we know how to use
scope-based filters.&lt;/p&gt;

&lt;h3 id=&quot;ransack-whats-next&quot;&gt;Ransack: what’s next&lt;/h3&gt;

&lt;p&gt;First of all, you should take a look at Active Admin’s documentation
&lt;a href=&quot;https://activeadmin.info/3-index-pages.html#index-filters&quot;&gt;regarding filters&lt;/a&gt;. You can continue
your overview with the official &lt;a href=&quot;https://github.com/activerecord-hackery/ransack&quot;&gt;README&lt;/a&gt; and
&lt;a href=&quot;https://github.com/activerecord-hackery/ransack/wiki&quot;&gt;wiki&lt;/a&gt;, where, among other things, you can
find view-helpers to create custom search forms.&lt;/p&gt;

&lt;p&gt;For especially complicated cases, you can consider learning how to create
&lt;a href=&quot;https://github.com/activerecord-hackery/ransack/wiki/Custom-Predicates&quot;&gt;custom predicates&lt;/a&gt; and
&lt;a href=&quot;https://github.com/activerecord-hackery/ransack/wiki/Using-Ransackers&quot;&gt;Ransackers&lt;/a&gt; - extensions
that convert parameters directly into Arel (internal library ActiveRecord, used to build SQL
queries).&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;I hope that the article allowed you to look at Active Admin from a new perspective, and maybe even
inspired you to refactor a class or two in your projects.&lt;/p&gt;

&lt;p&gt;I tried not to repeat the official Active Admin
&lt;a href=&quot;https://activeadmin.info/documentation.html&quot;&gt;documentation&lt;/a&gt;, which describes many interesting
features of the library, such as authorization and the use of decorators. Therefore make sure to
check it once again.&lt;/p&gt;

&lt;p&gt;Russian version of the article is published in the &lt;a href=&quot;https://habr.com/ru/company/domclick/blog/514506/&quot;&gt;Domclick blog&lt;/a&gt;.&lt;/p&gt;</content><author><name>Svyatoslav Kryukov</name><email>s.g.kryukov@yandex.ru</email></author><category term="rails" /><category term="activeadmin" /><summary type="html">Recently I bumped into Rails Survey 2020 results and saw the top 10 gems frustrate one the most. On the 5th place, there was the Active Admin gem. I would not say this was an unexpected result. I often come across the opinion that Active Admin is only suitable for a 15-minute blog, but there is much more with this library.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://skryukov.github.io/assets/an-unofficial-active-admin-guide/main.jpg" /><media:content medium="image" url="https://skryukov.github.io/assets/an-unofficial-active-admin-guide/main.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>