ruby-on-railsrubysetdynamic-attributes

Ruby Best Practice - Dynamic Attributes vs Hash for RoR application


I'm developing a model which holds configuration settings for server options. The number and type of server options change, and I need some input on the pros/cons of two ways of doing it. I'm doing this separate from the rails ActiveRecord database. All my database interactions are going to be manually with the file system.

First, having the model dynamically create attributes as needed for each server specified. (as long as this is possible... I've never done it before)

Second, creating a single Hash, which has Keys of the server options, and values of the server settings.

I think the second would be easier to implement, but I'm not sure if it is the right way to go? It seems to be cleaner to go with dynamic attributes.

Is there a rule of thumb to go with for this?


Solution

  • Made something for you.

    Here is how it would work.

    s = Settings.new(SettingsFileManager.new(some_file))
    
    s.ip # returns whats in the file.
    s.ip = 10.0.0.1 # overrides the new value.
    

    I haven't written the way it interacts with the files yet so you'll have to write a proper SettingsFileManager.

    If anything it should give you a good base to get started. But I wrote it primarily as a test to see what I know about ruby. Since it uses some semi-tricky stuff.

    Hosted it on github too with some tests.

    class Settings
    
      attr_accessor :settings
    
      def initialize(file_writer)
        @file_writer = file_writer
        read_settings_to_new_hash
      end
    
      def read_settings_to_new_hash
        @settings = fabricate_hash
        @settings.replace(@file_writer.read_keys)
      end
    
      def fabricate_hash
        InterceptedHash.new( &hash_saved_proc )
      end
    
      def hash_saved_proc
        Proc.new do |key, value|
          @file_writer.write_key(key, value)
        end
      end
    
      def method_missing(m, *args, &block)
        if @settings.has_key?(m) || args[0]
          if args[0]
            @settings[m.to_s.sub("=","").to_sym] = args[0]
          else
            @settings[m]
          end
        else
          raise "unknown setting"
        end
      end
    
    end
    
    # Not implemented. This should continue the logic to interact with whatever you want.
    class SettingsFileManager
      def initialize(file)
        @file = file
      end
    
      # write the key and its value somewhere.
      def write_key(key, value)
        puts 'write_key', key, value
      end
    
      # should return hash of keys.
      def read_keys
        puts 'read_keys'
        Hash.new
      end
    end
    
    class InterceptedHash < Hash
      def initialize(&block)
        super
        @block = block
      end
    
      def store(key, value)
        super(key, value)
        @block.call(key, value)
      end
    
      def []=(key, value)
        store(key, value)
      end
    end