1

Let's say that I plan to write a horse race betting application, and I want a RaceResult class that can have four discrete values: Win, Place, Show and the "null object" class Lose.

I come to Ruby from a C# background, and I would normally use an enum for this, or perhaps a series of static readonly fields created by a private constructor, like so:

public class RaceResult {
  public static RaceResult Win = new RaceResult(1);
  // ...

  private int lowestPositionToWin;

  private RaceResult(int position) {
    lowestPositionToWin = position;
  }

  private bool PaysOut(int placement) {
    return placement <= lowestPositionToWin; // logic may be flawed here
  }
}

If the classes get too complicated, I'd refactor to use a Strategy pattern.

What is a good Ruby approach to solving this requirement? Can I create a class that can only have four instances? I'm having trouble phrasing a Google query to find the information I'm looking for.

EDIT: In the first answers, I've gotten some good ways to handle the case where I just want a named primitive values. However, if I'm interested in extending its behavior, I'd need a custom class. Is there a way I could modify the constant approach to use a class?

neontapir
  • 4,698
  • 3
  • 37
  • 52

2 Answers2

2

Not sure if I understand well your requirement, but how about a Hash which is more ruby-ish?

race_results = { win: 1, place: 2, show: 3, lose: 4 }
S. A.
  • 3,714
  • 2
  • 20
  • 31
  • A Hash solves the case where all I have is a value. I updated my question to show there might be some behavior involved as well. – neontapir Jul 16 '14 at 17:18
1

According to this StackOverflow answer, found when searching "Ruby equivalent of enums", you can build enum-like structures using modules and constants:

module RaceResult
    LOSE = 0
    WIN = 1
    PLACE = 2
    SHOW = 4
end

result = RaceResult::WIN

This has the bonust points of being able to use bitwise operators on the values (e.g. RaceResult::WIN & RaceResult::SHOW), even though it may not be necessary in this specific application.

EDIT: You could even go further and define a enum-creating function for your code:

def Enum(values)
    result = Module.new
    i = 0
    values.each do |name|
        result.const_set name, 2**i
        i += 1
    end
    return result.freeze
end

RaceResults = Enum ["LOSE", "WIN", "PLACE", "SHOW"]

*Though with this minimal implementation, LOSE isn't a default zero-value/null-object, though.

Actual C#-like enums

As mentioned in your edit, you also need some behaviour (functions), so you might want to actually use a class. I'm going to mix the first snippet (which uses a manually defined module) with this answer so we still have the usual bitwise operators available, just like normal enums. In the snippet below, the first class definition creates a generic Enum class, that should be inherited by any real enums (this is the implementation of a enum). The second class is the actual enum, in our case, RaceResults. Note that it inherits Enum, and defines its constants using the constructor (more to this later). It also defines a method that may be called on it, just as an example.

class Enum
    def initialize(n)
        @value = n
    end

    def method_missing(name, *args, &blk)
        ret = @value.send(name, *args, &blk)
        ret.is_a? Numeric ? Enum.new(ret) : ret
    end
end

class RaceResults < Enum
    LOSE = self.new 0
    WIN = self.new 1
    PLACE = self.new 2
    SHOW = self.new 4

    def doSomething
        puts "did something useful, and btw, numerically I am #{@value}"
    end
end

RaceResults::WIN.doSomething

In the last line you can see how to use the method defined, and also how to access the constant values.

Why use the constructor to define the possible values, you may ask? Well, mainly so you can still use bitwise operators (&, |, etcetera), while being able to call any instance methods from the same value. It's a bit more transparent than using conversion methods, and a bit more lazy than actually implementing the operators.

Community
  • 1
  • 1