Advanced Java-like enums in Ruby

后端 未结 3 2086
梦毁少年i
梦毁少年i 2021-02-02 03:04

First of all, this is not a duplicate of Enums in Ruby :)

The accepted answer of that question suggests this as a good way to represent enums in Ruby:

cl         


        
相关标签:
3条回答
  • 2021-02-02 03:25

    This is my suggested code...

    EnumModule.rb

    module EnumModule
      CONVERT_PROC = Proc.new do
        @values = constants.collect{|c| const_get(c)}.freeze
    
        @values.each_with_index do |value, idx|
          the_symbol = constants.find{|c| const_get(c) == value}
          sig = class << value ; self end
          sig.send :define_method, :name, proc{the_symbol}
          sig.send :define_method, :ordinal, proc{idx}
    
          if value.is_a? Hash
            value.each do |k, v|
              sig.send :define_method, k, (v.is_a?(Proc) ? v : proc{v})
            end
          end
          value.freeze
        end
    
        class << self
          alias :value_of :const_get
        end
    
        module_function
        def each
          @values.each { |v| yield v }
        end
        def values
          @values
        end
        extend Enumerable
    
        freeze
      end
    
      def self.extended extending_obj
        extending_obj.module_eval &CONVERT_PROC
      end
    end
    

    SampleEnum.rb

    require 'EnumModule'
    
    module SampleEnum
      VALUE_1 = {
        to_s: 'Value_1_str',
        get_value: proc{'Value 1'}
      }
    
      VALUE_2 = {
        to_s: 'Value_2_str',
        get_value: proc{'Value 2'}
      }
    
      extend EnumModule
    end
    
    #defined method
    p SampleEnum::VALUE_1.get_value #=> "Value 1"
    p SampleEnum::VALUE_2.get_value #=> "Value 2"
    
    p SampleEnum::VALUE_1.to_s      #=> "Value_1_str"
    
    #name (returns the symbol of the constant)
    p SampleEnum::VALUE_1.name #=> :VALUE_1
    p SampleEnum::VALUE_2.name #=> :VALUE_2
    
    #ordinal
    p SampleEnum::VALUE_1.ordinal #=> 0
    p SampleEnum::VALUE_2.ordinal #=> 1
    
    #emulates Java Enum's valueOf(is an alias of const_get)
    p SampleEnum.value_of('VALUE_1').get_value  #=> "Value 1"
    p SampleEnum.value_of(:VALUE_1).get_value  #=> "Value 1"
    p SampleEnum.const_get('VALUE_1').get_value  #=> "Value 1"
    
    #emulates Java Enum's values
    SampleEnum.values.each do |m|
      p m.ordinal, m.name, m.get_value, m.to_s
    end
    
    #an Enumerable
    p SampleEnum.map{|m| m.get_value} #=> ["Value 1","Value 2"]
    

    By extending the EnumModule, Hash constants' contents (key & value) become Singleton methods.

    0 讨论(0)
  • 2021-02-02 03:33
    class MyEnum
      attr_accessor :value
      def initialize(value)
        @value = value
      end
    
      VALUE1 = new("Value 1")
      VALUE2 = new("Value 2")
    
      class << self
        private :new
      end
    end
    
    MyEnum::VALUE2 # Enum with value "Value 2"
    MyEnum.new # Error
    

    A more elaborate solution that allows you to define arbitrary "enum classes" and also gives you ordinal():

    def enum(*values, &class_body)
      Class.new( Class.new(&class_body) ) do
        attr_reader :ordinal
    
        def initialize(ordinal, *args, &blk)
          super(*args, &blk)
          @ordinal = ordinal
        end
    
        values.each_with_index do |(name, *parameters), i|
          const_set(name, new(i, *parameters))
        end
    
        class <<self
          private :new
        end
      end
    end
    
    # Usage:
    MyEnum = enum([:VALUE1, "Value 1"], [:VALUE2, "Value 2"]) do
      attr_reader :str
      def initialize(str)
        @str = str
      end
    end
    
    MyEnum::VALUE1.str #=> "Value 1"
    MyEnum::VALUE2.ordinal #=> 1
    
    0 讨论(0)
  • 2021-02-02 03:39

    You can always create a system that's like the Java version:

    module Foo
      class Value
        attr_reader :value
    
        def initialize(value)
          # Save a frozen, immutable copy
          @value = value.dup.freeze
        end
    
        # Patch in methods to make it appear more friendly and string-like
        alias_method :to_s, :value
        alias_method :inspect, :value
      end
    
      # Define constants
      BAR = Value.new('bar')
      BAZ = Value.new('baz')
      BIZ = Value.new('biz')
    end
    
    puts Foo::BAR
    # => bar
    
    0 讨论(0)
提交回复
热议问题