Managing serialized settings/preferences in a rails model

34 Views Asked by At

Years ago I created Rails application to manage a Golf Group I joined. It managed games, computes player quotas(handicap),etc. I moved but kept the web site since I'd occasionally visited. When I joined a new course I convinced a few groups to use my application. I updated the application to allow multiple groups.

Each group may use different scoring methods, allow limiting new players, do this or that. I decided to user serialized setting/preferences to the Group model to account for these differences. I struggled over the years when I added a new setting (and code to handle the setting). Once the settings are set, they seldom change but it's a pain when they do!

Being a hobbyist developer that learned how to code on a Apple II in the late 70's I'm not real proficient with ruby/rails - but I try! A year ago or so I adopted a new scheme where I used a default_setting method to define a hash, serialized it into a stored settings record attribute, then sent each key/value to a new ActiveModel:Attributes. I long ago wished I didn't use 'HashWithIndifferentAccess' to serialize the settings, or serialize settings with YAML, but I live with it.

Excuse the length of the below scaled down model, but need to explain the process. (there are currently 35 setting!)

class Group < ApplicationRecord
  #has_many :players,games,rounds,etc
  serialize :settings, coder: YAML, type: ActiveSupport::HashWithIndifferentAccess
  after_initialize :set_attributes

  # lets just set all settings to a model attribute 
  attribute :alert, :text
  attribute :dues, :integer
  attribute :limit_new_player, :boolean
  attribute :limit_rounds, :integer
  attribute :limit_points, :integer
  attribute :score_place_dist, :string
  attribute :score_place_perc, :integer

  def set_attributes
    if self.settings.blank?
      # new record, set settings from default options
      self.settings = self.default_settings
    elsif self.settings.keys != self.default_settings.keys 
      # sync settings - add new keys/value, delete old keys"
      self.default_settings.each do |k,v|
        if !self.settings.has_key?(k)
          settings[k] = v
        end
      end
      self.settings.each do |k,v|
        if !self.default_settings.has_key?(k)
          settings.delete(k)
        end
      end
      # self.save
    end
    self.settings.each do |k,v|
      # set attributes to settings
      self.send("#{k.to_sym}=", v)
    end
  end
  
  def default_settings
    # default settings control valid setting
    # if you add or remove a key, add or remove the attribute
    {
      alert:'',
      dues:6,
      limit_new_player:false,
      limit_rounds:2,
      limit_points:2,
      score_place_dist:'mid',
      score_place_perc:50
    }.with_indifferent_access  
  end

  def update_group(params)
    self.assign_attributes(params)
    # updates record without saving, just set attributes
    self.default_settings.each do |k,v|
      # now take the set attributes and update to serialized settings
      self.settings[k] = self.send(k.to_sym) 
    end
    self.save 
  end
end
  • The default_setting method defines the default settings
  • after_initialize :set_attributes will:
    • initialize the settings if empty
    • add(or remove sync) a setting/attribute if the default_setting keys don't match the settings keys
    • then send the synced setting to a model attribute

If I add a new setting I have to add it the the default_setting, define a new attribute and add code somewhere to handle the new setting. I have to do the same if I remove a setting.

This works fine in development, but what do I do when I deploy the application? I've commented out the save call if the setting changed, but it work locally and I think would work after deployment.

Again, it will probably work after deployment, but the new setting won't be added until the Group is initialized. current_group is alway set on signin or just visiting a group.

I use Capistrano to deploy (that I don't totally understand) and it just moves the current version and then restarts Puma. You can't modify the code base by calling some task that does a cleanup (update all groups). I added a method that will do that, but I either need to use the production console to call it or a restricted controller action to do the cleanup.

I only have 4 groups and the easiest option is just to do the save in the set attributes method. I will usually post a notice that something was added, but is there a better way?

0

There are 0 best solutions below