Rails Active Admin: from config File to Admin Portal

This blog post is a continuation of part 1 that had focused on the the installation process of Active Admin. In the current post, we show how Active Admin can be used to migrate from a configuration file based approach by an admin portal based approach. We start with a configuration file a la figaro, and migrate a boolean-like variable to Active Admin Portal, allowing admins to manage this variable. We will follow a DRY (don’t repeat yourself) approach by help of a “catch all” method that can handle any future variable of the same kind without the need to change any Ruby code.


  1. first published version
  2. changed model name ‘Config’ by ‘SystemSetting’, since RbConfig used by travisCI has a problem with the ‘Config’ name. Note, that the remaining error of v1 has disappeared with this measure as well: it seems that also Active Admin itself had a problem with the name ‘Config’.

How to use Active Admin to define System Settings

Now let us use go to work and prepare the Active Admin console for allowing to manage system wide variables:


  • We assume that Active Admin is already installed. See part 1 for step by step installation instructions.
  • Rails server is started, e.g. with the command rails s

Step 1: create a model

Let us generate a model that will hold the configuration variables I am managing via figaro today. I want to support Strings and Booleans in a single class, so I have added a value_type column:

$ rails g model SystemSetting name:string value_type:string value_default:string value:string short_description:string description:text
      invoke  active_record
      create    db/migrate/20160205102214_create_system_settings.rb
      create    app/models/system_setting.rb
      invoke    rspec
      create      spec/models/system_setting_spec.rb

We need to migrate the database changes:

rake db:migrate

If you have forgotten to add a column, you can add it later by issuing commands similar to:

rails g migration AddNewColumnToSystemSetting new_column:text
rake db:migrate

Step 2: register the model with Active Admin:

Now let us add this model to Active Admin:

$ rails generate active_admin:resource SystemSetting
      create  app/admin/system_setting.rb

Step 3: log into the Active Admin console and add a SystemSetting entry:

After refreshing the Active Admin console, a new menu item SystemSettings shows up in the Active Admin page (log in; default user:pass = admin@example.com:password). When we follow the link, we see:


No let us click on the Create one link in the SystemSettings page above and we can enter our first variable:




Okay, we have not yet specified in a controller, which attributes are allowed. In a normal model, this would be done in the controller by a command like

def user_params
 params.require(:user).permit(:username, :email, :password, :salt, :encrypted_password)

For Active Admin, it is similar, but different: it is described in the Active Admin documentation (found via this stackoverflow answer):

In app/admin/system_setting.rb add the permit_params line:

ActiveAdmin.register SystemSetting do
   permit_params :name, :value_type, :value_default, :value, :short_description, :description ## add this line

Now, press the browser’s back button and retry to save the entry. We will get:


Step 4: use the new variable in the project source code

Let us make use of the new variable:

The following code snippet shows, how I use the system variable WEBPORTAL_SIMULATION_MODE today:


I would like to replace that snippet by something like:

if SystemSetting.webportal_simulation_mode

This is simpler than the more obvious standard handling of database searches like follows:

values =SystemSetting.where(name: "WEBPORTAL_SIMULATION_MODE")
value = values[0] if values.count == 1 
if value == "true" 

In addition, the SystemSetting.webportal_simulation_mode approach will allow for more functionality as we will see below. The value of the SystemSetting.webportal_simulation_mode method shall be read from the SystemSetting database entry with name=”WEBPORTAL_SIMULATION_MODE”.

Step 4.1: create a catch all method in the model

Since we do not want to create such a method for each and every variable, I have made use of a cool Ruby feature: the method_missing method, which is a kind of catch all method. Any method that is not defined upfront will be handled by the method_missing method.

The code I have created looks like follows:

class SystemSetting < ActiveRecord::Base
    # catch any method like SystemSetting.whatever and do something with it instead of raising an error
    # found on http://stackoverflow.com/questions/185947/ruby-define-method-vs-def?rq=1
    def self.method_missing(*args)
        # assumption:
        # if we call SystemSetting.webportal_simulation_mode, we assume that the associated environment variable reads WEBPORTAL_SIMULATION_MODE (all capitals)
        environment_variable = args[0].to_s.upcase
        # look for database entries matching the method name, but with all capitals:
        foundlist =  self.where(name: environment_variable)
        # return value, if non-ambiguous entry was found; else return environment variable, if it exists:
        if foundlist.count == 1
            # found in the database: return its value as boolean
            foundlist[0].value == "true"
        elsif foundlist.count == 0 && !ENV[environment_variable].nil?
            # not found in the database: try to find corresponding environment variable as a fallback. 
            value = ENV[environment_variable]
            # If found, auto-create a database entry
            self.new(name: environment_variable, value: value, value_type: :boolean).save!
            # return its value as boolean
            value == "true"
        elsif foundlist.count > 1
            # error handling: variable found more than once (should never happen with the right validation)
            abort "Oups, this looks like a bug: Configuration.variable with name #{environment_variable} found more than once in the database."
            # error handling: variable not found:
            message = "#{environment_variable} not found: neither in the database nor as system environment variable." +
                      " As administrator, please create a SystemSetting variable with name #{environment_variable} " +
                      " and the proper value (in most situations: 'true' or 'false') on the Active Admin Console on https://localhost:3000/admin/system_settings " +
                      "(please adapt the host and port to your environment). Alternatively, restart the server. " +
                      "This should reset the environment variable to its default value and the SystemSetting variable will be auto-created."
            abort message
        end # if foundlist.count == 1
    end # def self.method_missing(*args)

The method_missing catch all trick works like follows:

  1. the method_missing catch all method will handle any occurrence of e.g. SystemSetting.webportal_simulation_mode used anywhere in the project.
  2. The first argument handed over to method_missing (i.e. arg[0]) will be a symbol like :webportal_simulation_mode.
  3. We convert this argument to “WEBPORTAL_SIMULATION_MODE” and search for this name pattern in the SystemSetting database.
  4. If there is one and only one matching entry in the database, we return its value as a boolean. If there is no matching entry in the database, but the environment variable WEBPORTAL_SIMULATION_MODE is set, we auto-generate a corresponding entry in the database.

Now we can replace all occurrences of ENV[WEBPORTAL_SIMULATION_MODE] == "true" within the project by SystemSetting.webportal_simulation_mode.

Step 4.2: correct the destroy behavior

If we now want to destroy a SystemSetting that is being used in the project code, it will be auto-created, when SystemSetting.webportal_simulation_mode is called. This might not be the desired behavior. If I delete a SystemSetting, I do not want it to pop up again. Let us fix that now:


class SystemSetting < ActiveRecord::Base
    def self.method_missing(*args)
        # If found, auto-create a database entry
        @@autocreate = {} unless defined?(@@autocreate)
        @@autocreate[environment_variable] = true if @@autocreate[environment_variable].nil?
        self.new(name: environment_variable, value: value, value_type: :boolean).save! if @@autocreate[environment_variable]
    def destroy
        # if we destroy a database entry, we do not want it to be auto-created again:
        @@autocreate = {} unless defined?(@@autocreate)
        @@autocreate[name] = false

Note, that I had defined a fallback, i.e. the SystemSetting.webportal_simulation_mode will still return a meaningful value, if the environment variable WEBPORTAL_SIMULATION_MODE is defined via configuration file (not visible to the Active Admin portal user). Therefore, the code  SystemSetting.webportal_simulation_mode within the project will not raise an exception in this case.

Step 4.3 add validations

A SystemSetting myvar will be mapped to an environment variable MYVAR and another SystemSetting MyVar will be mapped to the same environment variable MYVAR. Therefore we need to make sure we do not allow for two SystemSettings with the same meaning. We will allow for SystemSetting names with all capital letters only. For that we add following validations:

class SystemSetting < ActiveRecord::Base
    # prevent that a name can exist twice:
    validates :name, uniqueness: true
    # allow only variables with capital letters and underscores:
    validates_format_of :name, :with => /\A[A-Z0-9_\.]{1,255}\Z/, message: "needs to consist of 1 to 255 characters: A-Z, 0-9, . and/or _"

We have achieved our goal: our first configuration variable WEBPORTAL_SIMULATION_MODE has been moved from a configuration file to the database and can be controlled by the administrator via the Active Admin console.



We have migrated a boolean configuration setting from a configuration file to the database in order to allow the manipulation of its setting from Active Admin console. We have encountered a Active Admin problem with the view of a single entry, but this did not prevent us to reach our goal (troubleshooting and/or error report to active Admin is postponed…).

Instead of requiring a standard database reading syntax like

values = SystemSetting.where(name: "WEBPORTAL_SIMULATION_MODE")
value = values[0] if values.count == 1
if value == "true"

we allow for a simpler syntax like

if SystemSetting.webportal_simulation_mode

In order to prepare the solution for more upcoming variables of the same kind, we use the  method_missing method as a catch all method. With this approach, any new variable like


can be handled with the same piece of code. An administrator can add new variables in the Active Admin portal without the need to write a single line of ruby code. Okay, the SystemSetting.whatever must be used somewhere in the project’s ruby code to have a function. 😉

For convenience and backwards compatibility with the configuration file, I had added an auto-create function (the line starting with self.new in the  method_missing method): for any already defined environment variable, a database entry is created automatically from the environment variable. This will help for a quick migration of all variables from the configuration file to the database. Moreover, it allows us to keep the configuration file as a fallback, e.g. if the database is destroyed. However, in future, it will be better to automatically export the system_setting table from the database and re-import it upon server start if the system_setting table is found empty (or allow to retrieve it from some SW repository).

The results of the measure can be reviewed in the open source on Github, e.g. on this specific commit reflecting the work status at the end of the blog post.

Next steps: TODO: allow for non-boolean-like variables


  • re-write the code that methods like SystemSetting.what_is_the_weather_today? with a question mark are handled as booleans.
  • handle methods without question mark (e.g. SystemSetting.weather_report) as strings.
  • convert such string values to a numbers, it the string values are convertible.

Rails Active Admin Installation Experiences / HowTo

In this post, I have recorded my experiences with ActiveAdmin, a Rails Engine that has following goals (copied from their readme file):

  1. Enable developers to quickly create good-looking administration interfaces.
  2. Build a DSL for developers and an interface for businesses.
  3. Ensure that developers can easily customize every nook and cranny.

The path to success is described step by step

while the troubleshooting and resolution steps you should not need are indented to the right and kept as reference only.

I am applying Active Admin to my ProvisioningEngine Frontend proof of concept, because I want to move some configurations from a figaro based config file to the database, and I want to make sure that only authorized administrators can change those settings. Moreover, Active Admin offers nice tables with pagination, sorting and filtering. And an already present model can be migrated to active Admin simply by a single rails g command (not part of this blog post).

I am closely following the officially installation documentation on http://activeadmin.info/docs/0-installation.html. However, because I have run into a rails generator bug (falsely reported as activeadmin issue#4295), I had to roll back (git stash) my changes, which in turn did not restore the database (is ignored with git) and that had caused additional challenges. But first things first:

Step 1: install activeadmin

For installation of Active, we need to add following lines to the Gemfile

gem 'devise'
gem 'activeadmin', '1.0.0.pre2'

Devise is a requirement, and is not automatically installed as dependency. The activeadmin version statement '1.0.0.pre2' is important as well: I was running into several additional issues, when I first tried without version statement, and later with '1.0.0.pre1'. Only '1.0.0.pre2' worked, apart from issue#4295, but this is an issue that is unlikely to hit.
Then we perform:


Step 2: generate the base admin user model and routing

The next command

rails generate active_admin:install

should add the following files (among others):


and add routes to the existing config/routes.rb file.

Note: it did not work in my case, see activeadmin issue#4295). However, this was caused  by following statement at the end of my  config/routes.rb file:

# config/routes.rb
Rails.application.routes.draw do
...(many statements)...
end # Rails.application.routes.draw do

I often insert such comments after end statements to better see, what this is the end of. In this case, this comment has caused the rails generator to insert ActiveAdmin statments twice, like follows:

Rails.application.routes.draw do
 devise_for :admin_users, ActiveAdmin::Devise.config
 ...(many statements)...
end # Rails.application.routes.draw do
 devise_for :admin_users, ActiveAdmin::Devise.config

It seem to be looking for a pattern similar to /Rails.application.routes.draw do$/ and inserts the three lines thereafter. It does not recognize that it should ignore the second occurence of the pattern, since it is a comment only.

Okay, I do not recommend adding comments like # Rails.application.routes.draw do after the end statment in the routes.rb file for now.

After rolling back with git stash, removing the empty lines after the end statement and starting from scratch, the problem disappeared. However, through the rollback process, I have introduced (minor) other problems, as we will see.


Step 3: prepare the database and start the Web Server

Now, we migrate our database and start the server:

rake db:migrate
rails server

Note: In my case, since I had to roll back and the database changes are ignored and thus not rolled back properly, the rake db:migrate command did not work anymore: the corresponding object(s) had already been created in the database the first attempt.

To resolve this, I had done it the hard way and removed db/development.sqlite3 file and tried to  rake db:migrate again. That worked, seemingly.

Then we can visit http://localhost:3000/admin and log in as the default user (in my case, I was using a cloud9 URL instead, since I recently moved part of my development to that online IDE). The initial credentials are:

  • User: admin@example.com
  • Password: password

I guess, because of the fact that I had to reset the database, I ran into an error “invalid email or password”. In my case, the following workaround has worked fine:

$ rails console
AdminUser.create :email => 'admin@example.com', :password => 'password', :password_confirmation => 'password'

After successful login, we see following Dashboard:


Finally, ActiveAdmin is installed properly.


Installation Summary

I have shown step by step how ActiveAdmin can be installed. If you circumvent all the trip hazards I have run into, it can be done in 30 minutes or less.

In my case, the installation process was very much like trial and error. First, you need to add the ‘devise’ dependency not described in the official installation guide. Then, the right version has to be picked; the pre-release: '1.0.0.pre2'. Unfortunately I ran into a rails bug, but it is unlikely to run into the same one. Everybody has the right of his own bugs ;-).

The rollback process I was enforced to perform was incomplete, since the database is not saved in the repository. This has caused additional minor problems, but those could be worked around with the help of google and stackoverflow.

Happy Coding!

Next post: Active Admin: from config File to Admin Portal