user3063045
user3063045

Reputation: 2199

Ruby Access Class Variables Easily

I've created a class that I'm using to store configuration data. Currently the class looks like this:

class Configed
    @@username = "[email protected]"
    @@password = "password"
    @@startpage = "http://www.example.com/login"
    @@nextpage = "http://www.example.com/product"
    @@loginfield = "username"
    @@passwordfield = "password"
    @@parser = "button"
    @@testpage = "http://www.example.com/product/1"
    @@button1 = "button1"
    def self.username
        @@username
    end
    def self.password
        @@password
    end
    def self.startpage
        @@startpage
    end
    def self.nextpage
        @@nextpage
    end
    def self.loginfield
        @@loginfield
    end
    def self.passwordfield
        @@passwordfield
    end
    def self.parser
        @@parser
    end
    def self.testpage
        @@testpage
    end
    def self.button1
        @@button1
    end
end

To access the variables I'm using:

# Config file
require_relative 'Configed'

# Parse config
startpage = Configed.startpage
loginfield = Configed.loginfield
passwordfield = Configed.passwordfield
username = Configed.username
password = Configed.password
nextpage = Configed.nextpage
parser = Configed.parser
testpage = Configed.testpage

This is not very modular. Adding additional configuration data needs to be referenced in three places.

Is there a better way of accomplishing this?

Upvotes: 1

Views: 49

Answers (2)

SteveTurczyn
SteveTurczyn

Reputation: 36860

You can make class level instance variables...

class Configed
  @username = "[email protected]"
  @password = "password"
  @startpage = "http://www.example.com/login"
  # ...
  class << self
    attr_reader :username, :password, :startpage # ...
  end
end

It's somewhat more compact, and still gives you

username = Configed.username
# ...

NOTE: there's a lot of good ideas in @philomory 's answer that deserves consideration. The use of YAML in particular would allow you to set up different constants for different environemnts test, development, production etc, and you can load the current environment's configuration options into an OpenStruct created in an initializer. Makes for a more flexible solution.

Upvotes: 4

philomory
philomory

Reputation: 1767

There are a lot of potential improvements. First of all, no reason to use class variables if you don't want their weird specific inheritance-related behavior, and no reason to use a class at all if you're not going to instantiate it.

You could use a module:

module Configed
  module_function
  def username
    'username'
  end
  # etc
end

Configed.username

But frankly, you're almost certainly better off using a hash:

Config = {
  username: 'username'
  # etc
}.freeze

Config[:username]

or, if you prefer method-style access, an OpenStruct:

require 'openstruct' # from standard library
Config = OpenStruct.new(
  username: 'username'
  # etc
).freeze

Config.username

If they need to be modifiable, just don't freeze them. Also, typically a constant which is not a class or a module (such as a hash) would have a name in ALL_CAPS, e.g. CONFIGED, but, that's a stylistic decision with no actual impact on the code.

Your question refers to 'parsing' the config, but of course, you're not; the config data in your setup (and in my examples so far) is just Ruby code. If you'd rather load it from a non-code file, there's always YAML:

config.yaml:

username: username
password: password

config.rb:

require 'yaml' # from Standard Library
Configed = YAML.load_file('config.yaml')
Configed['username']

or JSON:

config.json:

{
  "username": "username",
  "password": "password"
}

config.rb:

require 'json' # from Standard Library
Configed = JSON.parse(File.read('config.json'))
Configed['username']

Upvotes: 3

Related Questions