module Ably::Modules::Enum

Enum brings Enum like functionality used in other languages to Ruby

@example

class House
  extend Ably::Moduels::Enum
  CONSTRUCTION = ruby_enum('CONSTRUCTION',
    :brick,
    :steel,
    :wood
  )
end

House::CONSTRUCTION(:brick).to_i # => 0
House::CONSTRUCTION('Wood').to_i # => 2
House::CONSTRUCTION.Wood == :wood # => true

Public Class Methods

new(name, symbol, index) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 145
def initialize(name, symbol, index)
  @name   = name
  @index  = index
  @symbol = symbol
end

Private Class Methods

[](*args) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 68
def [](*args)
  get(*args)
end
by_index() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 101
def by_index
  @by_index
end
by_symbol() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 105
def by_symbol
  @by_symbol
end
define_values(values) click to toggle source

Define constants for each of the Enum values e.g. define_constants(:dog) creates Enum::Dog

# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 111
def define_values(values)
  raise RuntimeError, "#{name} Enum cannot be modified" if by_index.frozen?

  # Allow another Enum to be used as a set of values
  if values.length == 1 && klass = values.first
    if klass.kind_of?(Class) && klass.ancestors.include?(Enum::Base)
      values = values.first.map(&:to_sym)
    end
  end

  values.map do |value|
    # Convert any key => index_value pairs into array pairs
    Array(value)
  end.flatten(1).each_with_index do |name, index|
    name, index = name if name.kind_of?(Array) # name is key => index_value pair
    raise ArgumentError, "Index value '#{index}' is invalid" unless index.kind_of?(Numeric)

    camel_name  = convert_to_mixed_case(name, force_camel: true)
    name_symbol = convert_to_snake_case_symbol(name)
    enum        = new(camel_name, name_symbol, index.to_i)

    by_index[index.to_i]   = enum
    by_symbol[name_symbol] = enum

    define_singleton_method camel_name do
      enum
    end
  end

  by_index.freeze
  by_symbol.freeze
end
each(&block) click to toggle source

Method ensuring this {Enum} is {ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}

# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 82
def each(&block)
  return to_enum(:each) unless block_given?
  by_symbol.values.each(&block)
end
get(identifier) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 49
def get(identifier)
  case identifier
  when Symbol
    by_symbol.fetch(identifier) { raise KeyError, "#{name} key not found: :#{identifier}" }
  when String
    by_symbol.fetch(convert_to_snake_case_symbol(identifier)) { raise KeyError, "#{name} key not found: '#{identifier}'" }
  when Numeric
    by_index.fetch(identifier) { raise KeyError, "#{name} key not found: #{identifier}" }
  when ancestors.first
    identifier
  else
    if identifier.class.ancestors.include?(Enum::Base)
      by_symbol.fetch(identifier.to_sym)
    else
      raise KeyError, "Cannot find Enum matching identifier '#{identifier}' argument as it is an unacceptable type: #{identifier.class}"
    end
  end
end
length()
Alias for: size
name() click to toggle source

The name provided in the constructor for this Enum

# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 88
def name
  @enum_name
end
size() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 76
def size
  by_symbol.keys.length
end
Also aliased as: length
to_s() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 72
def to_s
  name
end
to_sym_arr() click to toggle source

Array of Enum values as symbols

@return [Array<Symbol>]

# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 96
def to_sym_arr
  @by_symbol.keys
end

Private Instance Methods

==(other) click to toggle source

Allow comparison of Enum objects based on:

  • Other equivalent Enum objects compared by Symbol (not Integer value)

  • Symbol

  • String

  • Integer index of Enum

# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 174
def ==(other)
  case other
  when Symbol
    self.to_sym == convert_to_snake_case_symbol(other)
  when String
    self.to_sym == convert_to_snake_case_symbol(other)
  when Numeric
    self.to_i == other.to_i
  else
    if other.kind_of?(Ably::Modules::Enum::Base)
      self.to_sym == other.to_sym
    end
  end
end
index() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 198
def index
  @index
end
match_any?(*enums) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 189
def match_any?(*enums)
  enums.any? { |enum| self.==(enum) }
end
name() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 194
def name
  @name
end
ruby_enum(enum_name, *values) click to toggle source

ruby_enum returns an Enum-like class that should be assigned to a constant in your class The first ‘enum_name` argument must match the constant name so that the coercion method is available

@example

class House
  extend Ably::Moduels::Enum
  CONSTRUCTION = ruby_enum('CONSTRUCTION', :brick)
end

# ensures the following coercion method is available
House::CONSTRUCTION(:brick) # => CONSTRUCTION.Brick
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 37
def ruby_enum(enum_name, *values)
  enum_class = Class.new(Enum::Base) do
    include Conversions
    extend Conversions

    @enum_name = enum_name
    @by_index  = {}
    @by_symbol = {}

    class << self
      include Enumerable

      def get(identifier)
        case identifier
        when Symbol
          by_symbol.fetch(identifier) { raise KeyError, "#{name} key not found: :#{identifier}" }
        when String
          by_symbol.fetch(convert_to_snake_case_symbol(identifier)) { raise KeyError, "#{name} key not found: '#{identifier}'" }
        when Numeric
          by_index.fetch(identifier) { raise KeyError, "#{name} key not found: #{identifier}" }
        when ancestors.first
          identifier
        else
          if identifier.class.ancestors.include?(Enum::Base)
            by_symbol.fetch(identifier.to_sym)
          else
            raise KeyError, "Cannot find Enum matching identifier '#{identifier}' argument as it is an unacceptable type: #{identifier.class}"
          end
        end
      end

      def [](*args)
        get(*args)
      end

      def to_s
        name
      end

      def size
        by_symbol.keys.length
      end
      alias_method :length, :size

      # Method ensuring this {Enum} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
      def each(&block)
        return to_enum(:each) unless block_given?
        by_symbol.values.each(&block)
      end

      # The name provided in the constructor for this Enum
      def name
        @enum_name
      end

      # Array of Enum values as symbols
      #
      # @return [Array<Symbol>]
      #
      def to_sym_arr
        @by_symbol.keys
      end

      private
      def by_index
        @by_index
      end

      def by_symbol
        @by_symbol
      end

      # Define constants for each of the Enum values
      # e.g. define_constants(:dog) creates Enum::Dog
      def define_values(values)
        raise RuntimeError, "#{name} Enum cannot be modified" if by_index.frozen?

        # Allow another Enum to be used as a set of values
        if values.length == 1 && klass = values.first
          if klass.kind_of?(Class) && klass.ancestors.include?(Enum::Base)
            values = values.first.map(&:to_sym)
          end
        end

        values.map do |value|
          # Convert any key => index_value pairs into array pairs
          Array(value)
        end.flatten(1).each_with_index do |name, index|
          name, index = name if name.kind_of?(Array) # name is key => index_value pair
          raise ArgumentError, "Index value '#{index}' is invalid" unless index.kind_of?(Numeric)

          camel_name  = convert_to_mixed_case(name, force_camel: true)
          name_symbol = convert_to_snake_case_symbol(name)
          enum        = new(camel_name, name_symbol, index.to_i)

          by_index[index.to_i]   = enum
          by_symbol[name_symbol] = enum

          define_singleton_method camel_name do
            enum
          end
        end

        by_index.freeze
        by_symbol.freeze
      end
    end

    def initialize(name, symbol, index)
      @name   = name
      @index  = index
      @symbol = symbol
    end

    def to_s
      "#{self.class}.#{name}"
    end

    def to_i
      index
    end

    def to_sym
      symbol
    end

    def to_json(*args)
      %{"#{symbol}"}
    end

    # Allow comparison of Enum objects based on:
    #
    # * Other equivalent Enum objects compared by Symbol (not Integer value)
    # * Symbol
    # * String
    # * Integer index of Enum
    #
    def ==(other)
      case other
      when Symbol
        self.to_sym == convert_to_snake_case_symbol(other)
      when String
        self.to_sym == convert_to_snake_case_symbol(other)
      when Numeric
        self.to_i == other.to_i
      else
        if other.kind_of?(Ably::Modules::Enum::Base)
          self.to_sym == other.to_sym
        end
      end
    end

    def match_any?(*enums)
      enums.any? { |enum| self.==(enum) }
    end

    private
    def name
      @name
    end

    def index
      @index
    end

    def symbol
      @symbol
    end

    define_values values
  end

  # Convert any comparable object into this Enum
  # @example
  #   class Example
  #     DOGS = ruby_enum('DOGS', :terrier, :labrador, :great_dane)
  #   end
  #
  #   Example.DOGS(:great_dane) # => <DOGS.GreatDane>
  #   Example.DOGS(0) # => <DOGS.Terrier>
  #   Example.new.DOGS(0) # => <DOGS.Terrier>
  #
  define_singleton_method enum_name do |val|
    enum_class.get(val)
  end

  define_method enum_name do |val|
    enum_class.get(val)
  end

  enum_class
end
symbol() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 202
def symbol
  @symbol
end
to_i() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 155
def to_i
  index
end
to_json(*args) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 163
def to_json(*args)
  %{"#{symbol}"}
end
to_s() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 151
def to_s
  "#{self.class}.#{name}"
end
to_sym() click to toggle source
# File lib/submodules/ably-ruby/lib/ably/modules/enum.rb, line 159
def to_sym
  symbol
end