Accediendo a una tabla de seteos globales con ActiveRecord

Las dos clases que aparecen a continuación proveen un método simple para manipular datos de la forma (clave, valor) de manera simple y rubística. No es muy ortodoxo, pero me viene resultando útil en los proyectos en los que lo usé.

Este ‘pattern’ surgió de la necesidad de acceder y modificar seteos globales de una aplicacion, de la forma “background_color = ”. Los pares se almacenan en una tabla settings:

CREATE TABLE `settings` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(100) NOT NULL default '',
  `value` text NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `name` (`name`)
)

El primer modelo ActiveRecord que necesitamos es la clase Setting:

class Setting < ActiveRecord::Base
  validates_presence_of :name
  validates_uniqueness_of :name

  def self.find_or_create(name)
    unless setting = Setting.find_by_name(name)
      setting = Setting.new("name" => name)
    end
    setting
  end
end

Setting es una clase heredera de ActiveRecord::Base, con el agregado del método Setting.find_or_create que retorna un registro de la tabla settings dado su nombre, creándolo en caso de no existir. Vale aclarar que esta clase la tomé del código fuente de Typo.

El acceso a los registros de la tabla settings lo vamos a realizar mediante la clase WizardSettings:

class WizardSettings
  def self.method_missing(method_id, *arguments)
    attr_name = method_id.id2name
    setting_name = attr_name.gsub(/[\?\=\!]/,'')
    if attr_name =~ /^.+=$/ # writer
      if arguments[0].nil? and s = Setting.find_by_name(setting_name)
        s.destroy
      else
        s = Setting.find_or_create(setting_name)
        s.value = arguments[0]
        return s 
      end
    elsif attr_name =~ /^.+\!$/ # assignment, immediate save
      if arguments[0].nil?
        WizardSettings.send("#{setting_name}=", nil)
      else
        s = Setting.find_or_create(setting_name)
        s.value = arguments[0]
        s.save
      end
    elsif attr_name =~ /^.+\?$/ # boolean 
      Setting.find_or_create(setting_name).value?
    else
      Setting.find_or_create(setting_name).value
    end
  end
end

Como se puede ver, WizardSettings no define métodos propios si no que redefine method_missing. Voy a explicar en detalle la implementación de este metodo en una futura versión de este documento :). Pero la idea es el acceso a los valores guardados en settings mediante WizardSettings. Supongamos que settings contiene los siguientes registros:

+----+-------------------+-----------------------------+
| id | name              | value                       |
+----+-------------------+-----------------------------+
|  1 | meta_keywords     | foo, bar, baz, quux, quux0r |
|  2 | date_format       | %d-%m-%Y                    |
|  3 | use_visual_editor | 1                           |
+----+-------------------+-----------------------------+

Ejemplo de uso de WizardSettings:


WizardSettings.use_visual_editor # -> "1" 

WizardSettings.use_visual_editor? # -> true

WizardSettings.use_visual_editor = nil # -> nil

WizardSettings.use_visual_editor? # -> false

WizardSetting.nuevo_seteo # -> "" 

WizardSetting.nuevo_seteo! "foo" # Inserta un nuevo registro en settings

WizardSetting.nuevo_seteo # -> "foo" 

El código, tal como está, presenta varias desventajas e inconsistencias que serán desarrolladas en una próxima versión de éste artículo :)

Manuel Aristarán