2

I've been working on a game (more of a web toy if anything) that requires a long list of countries to simulate, and I've managed to get it to work, but I can't help but feel that my solution is neither a Rubyish nor elegant way of doing it.

The code looks a bit like this:

class Countries
    include Singleton

    def get(tag)
        return eval "@#{tag}"
    end

    def initialize
        @array = []

        @afghanistan = Country.new("Afghanistan", [:authoritarian, :reactionary, :sunni, :capitalist, :militarist, :southern_asia, :unstable])
        @afghanistan.gdp = 20444
        @afghanistan.population = 26023
        @array << :afghanistan

        @albania = Country.new("Albania", [:hybrid, :conservative, :sunni, :capitalist, :pacifist, :southern_europe])
        @albania.gdp = 13276
        @albania.population = 2893
        @array << :albania
    #and so on and so forth
    end
    attr_accessor :array
end

countries = Countries.instance
puts countries.get("usa").name
puts
for i in 0..(countries.array.size-1)
    puts countries.get(countries.array[i]).name
end

And I get the expected output of

United States

Afghanistan
Albania
Algeria
...

But ideally an elegant solution would not require .get(), and this really doesn't seem like a Ruby-like way of solving this issue. Is there a better-practice way of doing this?

I mostly learned the little bit I know from Stack Overflow, the Ruby docs, and testing, so it's very possible I have violated a lot of best practices along the way. The Country class's initializer takes a string for the name and an array of tags to add, while other properties are intended to be added in separate lines.

3
  • The first thing you absolutely want to do is store the countries inside a hash Commented Dec 16, 2015 at 20:05
  • @DamianoStoffie Do you mean replacing the array or replacing the class? Commented Dec 16, 2015 at 20:06
  • The solution by @spickermann is one that I would recommend, but there are some bigger issues here regarding class design. It might be worth reading up on the SOLID principles, especially the Single Responsibility Principle and DRY. Commented Dec 17, 2015 at 14:52

2 Answers 2

3

I would store the countries` details in a file (e.q. countries.yml or a csv file) or a database:

# in countries.yml
afganistan:
  name: Afganistan
  tags:
    - authoritarian
    - reactionary
    - sunni
    - capitalist
    - militarist
    - southern_asia
    - unstable
  gdp: 20444
  population: 26023
albania:
  name: Albania
  tags:
    ...
    ...

Then your class simplified to:

require 'yaml'

class Countries
  include Singleton

  def get(country)
    @countries[country]
  end

  def initialize
    @countries = {}

    YAML.load_file('countries.yml').each do |country_key, options|
      country = Country.new(options['name'], options['tags'])
      country.gdp = options['gdp']
      country.population = options['population']

      @countries[country_key] = country
    end

    @countries.keys # just to keep the interface compatible with your example 
  end
end
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! I considered using a JSON file, but it seemed like more hassle than it was worth.
2

There are hundreds of ways you can DRY your code, but your error is essentially not using a hash data stucture (or an external file, as suggested).

This is how I would do it, I made some assumptions, hope this helps!

# I'll assume that Country is an actual class with a purpose, and not a simple
# structure.
Country = Struct.new(:name, :tags, :gdp, :population)

# list of all the countries
COUNTRIES_TABLE = [
  ["Afghanistan", [:authoritarian, :reactionary], 20444, 26023],
  ["Albania", [:hybrid, :conservative], 13276, 2893]
  # etc..
]

COUNTRIES = COUNTRIES_TABLE.map { |e| Country.new(*e) }
# we could have directly defined the hash instead of a table, but this keeps
# everything more DRY
COUNTRIES_HASH = COUNTRIES.map {|e| [e.name, e]}.to_h

puts COUNTRIES_HASH["Albania"].name

COUNTRIES_HASH.map do |k,v|
  puts v.name
end

1 Comment

Thank you very much for the help, although I think having 3 separate objects to get from the table to the hash seems like a bit much. You know better, though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.