views:

61

answers:

3

I used hash of hashes to store settings in my code. I wonder if I can have something like class variable that is the same for all instances of the class for my @profile hash. So both profiles below would have general profile variable equal to both of them. I want the solution to use hash.

@profile = {
  "vB4Discussions" => {    #profile name
     "urls"  => ["vB4 General Discussions"],
     "process?" => false,
     "type" => 'Discussion'
   },
  "vB4Design".downcase => {          #profile name      
     "urls"  => ["vB4 Design and Graphics Discussions"],
     "process?" => false,
      "type" => 'Design'
   }
} 
+1  A: 

One way is to use a constant for the common bits, like so:

#!/usr/bin/ruby1.8

require 'pp'

COMMON_PROFILE = {
  "urls"  => ["vB4 General Discussions"],
  "process?" => false,
  "type" => 'Discussion'
}

@profile = {
  "vB4Discussions" => COMMON_PROFILE,
  "vB4Design".downcase => COMMON_PROFILE,
}

pp @profile

# => {"vB4Discussions"=>
# =>   {"vB4Discussions"=>
# =>     {"urls"=>["vB4 General Discussions"],
# =>      "process?"=>false,
# =>      "type"=>"Discussion"}},
# =>  "vb4design"=>
# =>   {"vB4Discussions"=>
# =>     {"urls"=>["vB4 General Discussions"],
# =>      "process?"=>false,
# =>      "type"=>"Discussion"}}}
Wayne Conrad
I got it, it took me some time but I understood :-) yes, that would one possible way. Let us see if somebody else can think of any other... Thank you Wayne
Radek
Keep in mind, this solution is only good if you won't be changing the configurations on the fly. To find out why, Create this hash, modify one of the configurations: @profile['VB4Discussions']['type'] = 'chit chat' and then print the hash out. When you understand what you then see, you'll know something important.
Wayne Conrad
do you mean that modifying common bit will affect all configuration profiles? It seems to me that I will use your idea but in slightly modified way. I will add to all profiles new COMMON_PROFILE hash.
Radek
Yep, that's what I meant. You're way ahead of me.
Wayne Conrad
@Wayne Conrad: I wish I was :-)))))))))) I like your solution it is already implemented and working.Bit different though. I like the one below too.
Radek
A: 

Assuming I understand your question:

Yes, Ruby has class variables (@@a_class_variable) but the preferred way is to use an instance variable of the singleton class:

class MyClass
  class << self  # open the single class
    attr_reader :profiles
  end

  @profiles = {
    #...
  }

  def foo
    if self.class.profiles["vB4Discussions"]["process?"]
      # process...
    end
  end
end
Marc-André Lafortune
@Marc-Andre Lafortune: I edited my question so it is clear that I want to use only hash. Thank you.
Radek
What confused me is that your are (still) talking about two instances of the class... You mean the Hash class?
Marc-André Lafortune
Posted (completely) different response
Marc-André Lafortune
I meant custom class, like class profile end
Radek
+3  A: 

Did you know that hashes can have a special proc called when a key is not found?

This could be used here very nicely.

require "backports"  # Needed in Ruby 1.8.6
SETTINGS = {
  "default" => {
    "urls"  => [],
    "process?" => false,
    "type" => 'Discussion'
  },
  "vB4Discussions" => {
    "urls"  => ["vB4 General Discussions"],
  },
  "vB4Design".downcase => {
    "urls"  => ["vB4 Design and Graphics Discussions"],
    "type" => 'Design'
  }
}

# Use defaults 
SETTINGS["vb4design"].default_proc = lambda{|h, k| SETTINGS["default"][k]}
SETTINGS["vB4Discussions"].default_proc = lambda{|h, k| SETTINGS["default"][k]}

# Now the defaults are used if needed:
SETTINGS["vB4Discussions"]["type"]  # ==> 'Discussion'
SETTINGS["vB4Discussions"]["process?"]  # ==> false

# Defaults can be edited later:
SETTINGS["default"]["process?"] = true
SETTINGS["vB4Discussions"]["process?"]  # ==> true
SETTINGS["vb4design"]["process?"]  # ==> true

# Specific value can be changed too
SETTINGS["vb4design"]["process?"] = false # ==> true
SETTINGS["vB4Discussions"]["process?"] # ==> true

Note: Unless you have a valid reason to use strings, you should use symbols for your keys (i.e. :vB4Discussions instead of "vB4Discussions".

The Hash.default_proc= is new to Ruby 1.8.7, so you need to require "backports" to use it. If you don't want this, you could instead specify the default proc when creating the hashes as follows:

DEFAULTS = {
  "urls"  => [],
  "process?" => false,
  "type" => 'Discussion'
}

SETTINGS = {
  "default" => DEFAULTS,
  "vB4Discussions" => Hash.new{|h, k| DEFAULTS[k]}.merge!{
    "urls"  => ["vB4 General Discussions"],
  },
  "vB4Design".downcase => Hash.new{|h, k| DEFAULTS[k]}.merge!{
    "urls"  => ["vB4 Design and Graphics Discussions"],
    "type" => 'Design'
  }
}
Marc-André Lafortune
@Marc-Andre Lafortune: very interesting concept. Definitely answered my question.Pity I cannot use 'accept' twice.
Radek