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?