Ruby on Rails - Storing application configuration

I have a relatively simple Rails app and I would like to store various configuration settings that administrator users can change whilst the application is running, for example, allowing comments on posts or changing the display format of the date.

I know I can store constants etc in the environment.rb file, however these appear to be loaded only when the server is restarted.

Is there an alternative place I can define this information or would it be better to keep it in the database?

Any advice appreciated.

Thanks.

Answers


You can use database. Create a separate table "settings" that stores key/value parameters you need. The downside of this solution is a performance hit (querying DB every time you need a setting). To fix this issue you could you read/write through cache like "cache_money" or create your own by using "Rails.cache"


You could use the rails-settings-cached gem which is a fork of the rails-settings gem (linked by Yi-Ru Lin in another answer).

Once setup, you'll be able to do things such as:

Setting.foo = 123
Setting.foo # returns 123

You can also manage settings on models, ex:

user.settings.color = :red
user.settings.color # returns :red

Try looking at it might be what you need.

http://github.com/ledermann/rails-settings


The best way is to use a database table. Each row should contain a keyword and a value. Simples.


I have used app_config gem for a while myself, but it fails with rails 2.3.9 (and probably also with rails 3.x), so I found this blog that mentions rails-settings and configuration, rails-settings stores values in DB, but configuration have namespaces built-in. I haven't tried them, but I think I will switch to rails-settings.

I notice now that the branch of rails-settings that Yi-Ru Lin mentions seems to be more featureful than the other rails-settings

Jarl


For rails 4, if you are using postgresql, you can use HStore, which is just like a serializable attribute, but you do SQL queries with it.

For rails 3, you can use activerecord-postgres-hstore gem.


I tried https://github.com/huacnlee/rails-settings-cached, but it does not work as described. Obviously the author forgot to mention some additional tune-ups in the gem usage description. I failed to write a controller for settings manipulation.

Instead I succeeded to utilize https://github.com/paulca/configurable_engine - despite some minor problems, this gem is by far more reasonable than rails-settings-cached.

The configurable_engine gem has a drawback: it has the hard-coded routes which are obscure and non-convenient. The gem's author promised to correct it, but said that he currently didn't have time for it.

So this problem was easily arranged by simply creating my own routes. Here is my code (added to make this gem really work):

routes.rb

 namespace :admin do
   resources :configurables, only: [:index, :show, :edit, :update, :destroy]
 end

admin/configurables_controller.rb

class Admin::ConfigurablesController < Admin::ApplicationController
      # include the engine controller actions
  include ConfigurableEngine::ConfigurablesController

  before_action :set_configurable, only: [:show, :edit, :update, :destroy]

  def index

    @configurables = (Configurable.all.size > 0 ? Configurable.all : []) + 
    (Configurable.defaults.keys - Configurable.all.collect { |c| c.name })

  end

  def show
  end

  def edit
    new = params[:new]
  end

  def new

    respond_to do |format|

      name = params[:name]

        if name

            @configurable = Configurable.create!(name: name, value: nil)

            if @configurable
            format.html { redirect_to edit_admin_configurable_path(@configurable, new: true), notice: 'The setting was successfully created.' }
        else
            format.html { redirect_to admin_configurables_url, notice: 'Failed to create the setting.' }
        end
        else
            format.html { redirect_to admin_configurables_url, notice: 'The name of the new setting was not specified.' }
        end

    end

  end

  def update
    respond_to do |format|
      if @configurable.update(configurable_params)
        format.html { redirect_to [:admin, @configurable], notice: 'The setting was successfully updated.' }
        format.json { render :show, status: :ok, location: @configurable }
      else
        format.html { render :edit }
        format.json { render json: @configurable.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @configurable.destroy
    respond_to do |format|
      format.html { redirect_to admin_configurables_url, notice: 'The setting was successfully destroyed.' }
      format.json { head :no_content }
    end
  end  

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_configurable
      @configurable = Configurable.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def configurable_params
      params.require(:configurable).permit(:name, :value)
    end  

end

index.html.erb

<h1 class="page-header">Settings</h1>

<div class="table-responsive">
<table class="table table-striped">
  <thead>
    <tr>
      <th>Name</th>   
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @configurables.each do |configurable| %>
      <tr>
        <% if configurable.try(:name) %>
          <td><%= Configurable.defaults[configurable.name][:name]%></td>
          <td></td>   
          <td><%= link_to 'Show', [:admin, configurable] %></td>
          <td><%= link_to 'Edit', edit_admin_configurable_path(configurable) %></td>
          <td><%= link_to 'Destroy', [:admin, configurable], method: :delete, data: { confirm: 'Are you sure?' } %></td>
        <% else %>
          <td><%= Configurable.defaults[configurable][:name] %></td>     
          <td><%= link_to 'Create', new_admin_configurable_path(name: configurable) %></td>
          <td colspan="3"></td>
        <% end %>
      </tr>
    <% end %>
  </tbody>
</table>
</div>

edit.html.erb

<h1>Editing <%= @new ? "new " : "" %>setting</h1>

<%= render 'form', configurable: @configurable %>

<%= link_to 'Show', [:admin, @configurable] %> |
<%= link_to 'Back', admin_configurables_path %>

show.html.erb

<p>
  <strong>Name:</strong>
  <%= Configurable.defaults[@configurable.name][:name] %>
</p>

<p>
  <strong>Value:</strong>
  <%= @configurable.value %>
</p>


<%= link_to 'Edit', edit_admin_configurable_path(@configurable) %> |
<%= link_to 'Back', admin_configurables_path %>

_form.html.erb

<%= form_for([:admin, configurable]) do |f| %>

  <div class="field">
    <%= f.label "Name" %>
    <%= Configurable.defaults[@configurable.name][:name] %>
  </div>

  <div class="field">
    <%= f.label "Value" %>
    <%= f.text_area :value %>
  </div>  

  <div class="actions">
    <%= f.submit "Submit" %>
  </div>
<% end %>

Due to the hard-coded routes my controller is not completely REST-compliant, but it is pretty close to. My new action actually creates a (database-stored) setting (only to override its yml-file value).

So this code added to the gem description code lets you practically utilize the run-time-changeable RoR settings.

The gem requires you to set some default values in an yml file in advance, which you may override later at the run-time. But you can't create a new setting at run-time (not-yml-file-existent) - only modify an existent (in yml file) one - which is quite logical.

Or you may restore (at the run-time) the default value of any setting (by deleting its database-stored overriden value).

This code was checked to work with Rails 5.


Need Your Help

End loop if image appears inside region

python sikuli-script

I am trying to make my script end the loop or exit if a certain image appears inside certain region.

Are all of the default scripts loaded by Magento really needed?

magento prototypejs javascript

Here's a listing of all the scripts loaded by Magento by default: