it-swarm-tr.com

Ruby'deki Enums

Enum deyimini Ruby'de uygulamanın en iyi yolu nedir? Java/C # enums gibi (neredeyse) kullanabileceğim bir şey arıyorum.

290
auramo

İki yol. Semboller (:foo notasyonu) veya sabitleri (FOO notasyonu).

Semboller, değişmez dizelerle kodları karıştırmadan okunabilirliği artırmak istediğinizde uygundur.

postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"

Sabitler, önemli olan bir değeriniz olduğunda uygundur. Sadece sabitlerinizi tutacak bir modül ve daha sonra bunun içindeki sabitleri bildirin.

module Foo
  BAR = 1
  BAZ = 2
  BIZ = 4
end

flags = Foo::BAR | Foo::BAZ # flags = 3
289
mlibby

Bunu yapmanın en deyimsel yolu sembolleri kullanmaktır. Örneğin, yerine:

enum {
  FOO,
  BAR,
  BAZ
}

myFunc(FOO);

... sadece sembolleri kullanabilirsiniz:

# You don't actually need to declare these, of course--this is
# just to show you what symbols look like.
:foo
:bar
:baz

my_func(:foo)

Bu, enums'lerden biraz daha açık uçlu, ama Ruby ruhu ile iyi uyuyor.

Semboller de çok iyi performans gösterir. Örneğin, eşitlik için iki sembolün karşılaştırılması, iki karakterin karşılaştırılmasından çok daha hızlıdır.

52
emk

Hiç kimsenin aşağıdaki gibi bir şey önermediğine şaşırdım ( RAPI gem'den hasat edildi):

class Enum

  private

  def self.enum_attr(name, num)
    name = name.to_s

    define_method(name + '?') do
      @attrs & num != 0
    end

    define_method(name + '=') do |set|
      if set
        @attrs |= num
      else
        @attrs &= ~num
      end
    end
  end

  public

  def initialize(attrs = 0)
    @attrs = attrs
  end

  def to_i
    @attrs
  end
end

Hangi gibi kullanılabilir:

class FileAttributes < Enum
  enum_attr :readonly,       0x0001
  enum_attr :hidden,         0x0002
  enum_attr :system,         0x0004
  enum_attr :directory,      0x0010
  enum_attr :archive,        0x0020
  enum_attr :in_rom,         0x0040
  enum_attr :normal,         0x0080
  enum_attr :temporary,      0x0100
  enum_attr :sparse,         0x0200
  enum_attr :reparse_point,  0x0400
  enum_attr :compressed,     0x0800
  enum_attr :rom_module,     0x2000
end

Örnek:

>> example = FileAttributes.new(3)
=> #<FileAttributes:0x629d90 @attrs=3>
>> example.readonly?
=> true
>> example.hidden?
=> true
>> example.system?
=> false
>> example.system = true
=> true
>> example.system?
=> true
>> example.to_i
=> 7

Bu, veritabanı senaryolarında veya C tarzı sabitleri/enums'lerle çalışırken (RAPI'nin kapsamlı olarak kullandığı FFI kullanılırken olduğu gibi) iyi oynar.

Ayrıca, karma tipi bir çözüm kullanarak yaptığınız gibi sessiz hatalara neden olan yazım hataları için endişelenmenize gerek yok.

52
Charles

Aşağıdaki yaklaşımı kullanıyorum:

class MyClass
  MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end

Aşağıdaki avantajlar için hoşuma gidiyor:

  1. Değerleri görsel olarak bir bütün olarak gruplandırır
  2. Bazı derleme zamanı kontrolü yapar (sadece sembollerin kullanılmasının aksine).
  3. Olası tüm değerlerin listesine kolayca erişebilirim: sadece MY_ENUM
  4. Farklı değerlere kolayca erişebilirim: MY_VALUE_1
  5. Sadece Symbol değil, her türlü değere sahip olabilir.

Semboller daha iyi olabilir, çünkü başka bir sınıfta kullanıyorsanız, dış sınıfın adını yazmanıza gerek kalmaz (MyClass::MY_VALUE_1)

30
Alexey

Rails 4.2 veya daha üstü kullanıyorsanız, Rails enums'i kullanabilirsiniz.

Rails artık varsayılan olarak herhangi bir mücevher eklemeye gerek kalmadan numaralandırmaya sahiptir.

Bu, Java, C++ enums için çok benzer (ve daha fazla özellik ile).

http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html adresinden alıntılanmıştır.

class Conversation < ActiveRecord::Base
  enum status: [ :active, :archived ]
end

# conversation.update! status: 0
conversation.active!
conversation.active? # => true
conversation.status  # => "active"

# conversation.update! status: 1
conversation.archived!
conversation.archived? # => true
conversation.status    # => "archived"

# conversation.update! status: 1
conversation.status = "archived"

# conversation.update! status: nil
conversation.status = nil
conversation.status.nil? # => true
conversation.status      # => nil
17
vedant

Bu benim Ruby'deki enums yaklaşımım. Kısa ve tatlı olacaktım, en fazla C benzeri değil. Düşüncesi olan var mı?

module Kernel
  def enum(values)
    Module.new do |mod|
      values.each_with_index{ |v,i| mod.const_set(v.to_s.capitalize, 2**i) }

      def mod.inspect
        "#{self.name} {#{self.constants.join(', ')}}"
      end
    end
  end
end

States = enum %w(Draft Published Trashed)
=> States {Draft, Published, Trashed} 

States::Draft
=> 1

States::Published
=> 2

States::Trashed
=> 4

States::Draft | States::Trashed
=> 3
7
johnnypez

Adam bu soruyu yayınladığından bu yana çok zaman geçtiğini biliyorum, ama aynı sorum vardı ve bu yazı bana cevap vermedi. Sayının neyi temsil ettiğini, kolay karşılaştırmayı ve enum'u temsil eden sütunu kullanarak arama için ActiveRecord desteğinin çoğunu görmek için kolay bir yol istiyorum.

Hiçbir şey bulamadım, bu yüzden yinum adında harika bir uygulama yaptım; Bir sürü gözlük yaptım, bu yüzden güvenli olduğundan eminim.

Bazı örnek özellikler:

COLORS = Enum.new(:COLORS, :red => 1, :green => 2, :blue => 3)
=> COLORS(:red => 1, :green => 2, :blue => 3)
COLORS.red == 1 && COLORS.red == :red
=> true

class Car < ActiveRecord::Base    
  attr_enum :color, :COLORS, :red => 1, :black => 2
end
car = Car.new
car.color = :red / "red" / 1 / "1"
car.color
=> Car::COLORS.red
car.color.black?
=> false
Car.red.to_sql
=> "SELECT `cars`.* FROM `cars` WHERE `cars`.`color` = 1"
Car.last.red?
=> true
7
Oded Niv

Ruby-enum mücevherine göz atın, https://github.com/dblock/Ruby-enum .

class Gender
  include Enum

  Gender.define :MALE, "male"
  Gender.define :FEMALE, "female"
end

Gender.all
Gender::MALE
7
dB.

Sembolleri olan yazım hataları konusunda endişeleniyorsanız, olmayan bir anahtarla bir değere erişirken kodunuzun bir istisna oluşturduğundan emin olun. Bunu [] yerine fetch kullanarak yapabilirsiniz:

my_value = my_hash.fetch(:key)

veya mevcut olmayan bir anahtar sağlarsanız karma değerini varsayılan olarak bir istisna haline getirerek:

my_hash = Hash.new do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

Karma zaten varsa, istisna artırıcı davranışlar ekleyebilirsiniz:

my_hash = Hash[[[1,2]]]
my_hash.default_proc = proc do |hash, key|
  raise "You tried to access using #{key.inspect} when the only keys we have are #{hash.keys.inspect}"
end

Normalde, sabit yazım hatası güvenliği konusunda endişelenmenize gerek yoktur. Sabit bir ismi yanlış yazarsanız, genellikle bir istisna oluşturur.

5
Andrew Grimm

Biri devam etti ve Renum adında bir Yakut taşı yazdı. En yakın Java/C # davranışına sahip olduğunu iddia ediyor. Şahsen ben hala Ruby'yi öğreniyorum ve belirli bir sınıfı statik bir enum, muhtemelen karma, google üzerinden kolayca bulunamadığını söylemek istediğimde biraz şok oldum.

4
dlamblin

Belki de en iyi hafif yaklaşım

module MyConstants
  ABC = Class.new
  DEF = Class.new
  GHI = Class.new
end

Bu şekilde değerler Java/C # 'da olduğu gibi ilişkili isimlere sahiptir:

MyConstants::ABC
=> MyConstants::ABC

Tüm değerleri elde etmek için

MyConstants.constants
=> [:ABC, :DEF, :GHI] 

Bir enum'un sıra değerini istiyorsanız, yapabilirsiniz

MyConstants.constants.index :GHI
=> 2
4
Daniel Lubarov

Son zamanlarda biz uygulayan bir gem yayımladı Ruby'de Enums. Benim gönderim içinde sorularınızın cevaplarını bulacaksınız. Ayrıca, uygulamalarımızın neden mevcut uygulamalardan daha iyi olduğunu da açıkladım (aslında bu özelliğin Ruby öğesinde henüz mücevher olarak birçok uygulama var).

3
ka8725

Bu biraz gereksiz görünüyor, ancak bu, özellikle xml veya benzeri bir şeyle birleştirdiğim birkaç kez kullandığım bir yöntem.

#model
class Profession
  def self.pro_enum
    {:BAKER => 0, 
     :MANAGER => 1, 
     :FIREMAN => 2, 
     :DEV => 3, 
     :VAL => ["BAKER", "MANAGER", "FIREMAN", "DEV"]
    }
  end
end

Profession.pro_enum[:DEV]      #=>3
Profession.pro_enum[:VAL][1]   #=>MANAGER

Bu bana bir c # enum titizliği verir ve modele bağlanır.

2
jjk

Semboller Ruby yoludur. Bununla birlikte, bazen bir miktar C koduyla veya çeşitli şeyler için biraz enum gösteren bir şey veya Java ile konuşmanız gerekebilir.


#server_roles.rb
module EnumLike

  def EnumLike.server_role
    server_Symb=[ :SERVER_CLOUD, :SERVER_DESKTOP, :SERVER_WORKSTATION]
    server_Enum=Hash.new
    i=0
    server_Symb.each{ |e| server_Enum[e]=i; i +=1}
    return server_Symb,server_Enum
  end

end

Bu daha sonra böyle kullanılabilir


require 'server_roles'

sSymb, sEnum =EnumLike.server_role()

foreignvec[sEnum[:SERVER_WORKSTATION]]=8

Bu elbette soyut yapılabilir ve kendi Enum sınıfımızı yuvarlayabilirsiniz. 

2
Jonke

Ben böyle bir şey uyguladım 

module EnumType

  def self.find_by_id id
    if id.instance_of? String
      id = id.to_i
    end 
    values.each do |type|
      if id == type.id
        return type
      end
    end
    nil
  end

  def self.values
    [@ENUM_1, @ENUM_2] 
  end

  class Enum
    attr_reader :id, :label

    def initialize id, label
      @id = id
      @label = label
    end
  end

  @ENUM_1 = Enum.new(1, "first")
  @ENUM_2 = Enum.new(2, "second")

end

o zaman işlemleri yapmak kolay 

EnumType.ENUM_1.label

...

enum = EnumType.find_by_id 1

...

valueArray = EnumType.values
2
Masuschi

Hepsi Java veya C # enums kullanım şeklinize bağlıdır. Nasıl kullandığınız Ruby'de seçtiğiniz çözümü belirleyecektir.

Yerel Set türünü deneyin, örneğin:

>> enum = Set['a', 'b', 'c']
=> #<Set: {"a", "b", "c"}>
>> enum.member? "b"
=> true
>> enum.member? "d"
=> false
>> enum.add? "b"
=> nil
>> enum.add? "d"
=> #<Set: {"a", "b", "c", "d"}>
2
mislav

Başka bir çözüm OpenStruct kullanıyor. Oldukça yalındır ve temiz.

https://Ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html

Örnek:

# bar.rb
require 'ostruct' # not needed when using Rails

# by patching Array you have a simple way of creating a ENUM-style
class Array
   def to_enum(base=0)
      OpenStruct.new(map.with_index(base).to_h)
   end
end

class Bar

    MY_ENUM = OpenStruct.new(ONE: 1, TWO: 2, THREE: 3)
    MY_ENUM2 = %w[ONE TWO THREE].to_enum

    def use_enum (value)
        case value
        when MY_ENUM.ONE
            puts "Hello, this is ENUM 1"
        when MY_ENUM.TWO
            puts "Hello, this is ENUM 2"
        when MY_ENUM.THREE
            puts "Hello, this is ENUM 3"
        else
            puts "#{value} not found in ENUM"
        end
    end

end

# usage
foo = Bar.new    
foo.use_enum 1
foo.use_enum 2
foo.use_enum 9


# put this code in a file 'bar.rb', start IRB and type: load 'bar.rb'
2
Roger

Çoğu insan sembol kullanır (bu :foo_bar sözdizimidir). Onlar eşsiz bir opak değer. Semboller, herhangi bir enum tarzı türe ait değildir, bu nedenle C'nin enum türünün gerçekten sadık bir temsili değildir, ancak bu, elde ettiği kadar iyidir.

1
Jan Krüger

Bazen tek ihtiyacım enum'un değerini alabilmek ve ismini Java dünyasına benzer şekilde tanımlayabilmek.

module Enum
     def get_value(str)
       const_get(str)
     end
     def get_name(sym)
       sym.to_s.upcase
     end
 end

 class Fruits
   include Enum
   Apple = "Delicious"
   MANGO = "Sweet"
 end

 Fruits.get_value('Apple') #'Delicious'
 Fruits.get_value('MANGO') # 'Sweet'

 Fruits.get_name(:Apple) # 'Apple'
 Fruits.get_name(:mango) # 'MANGO'

Bu benim için enum amacına hizmet eder ve onu da çok genişletilebilir tutar. Enum sınıfına daha fazla yöntem ekleyebilir ve viola tüm tanımlanmış numaralarda ücretsiz olarak alabilirsiniz. Örneğin. get_all_names ve bunun gibi şeyler.

1
dark_src
irb(main):016:0> num=[1,2,3,4]
irb(main):017:0> alph=['a','b','c','d']
irb(main):018:0> l_enum=alph.to_enum
irb(main):019:0> s_enum=num.to_enum
irb(main):020:0> loop do
irb(main):021:1* puts "#{s_enum.next} - #{l_enum.next}"
irb(main):022:1> end

Çıktı:

1 A
2 - b
3 - c
4 - d

1
Anu
module Status
  BAD  = 13
  GOOD = 24

  def self.to_str(status)
    for sym in self.constants
      if self.const_get(sym) == status
        return sym.to_s
      end
    end
  end

end


mystatus = Status::GOOD

puts Status::to_str(mystatus)

Çıktı:

GOOD
1
Hossein

Çabuk ve kirli, C # gibi hissediyor:

class FeelsLikeAnEnum
  def self.Option_1() :option_1 end
  def self.Option_2() :option_2 end
  def self.Option_3() :option_3 end
end

Bir Enum kullanmış gibi kullanın:

method_that_needs_options(FeelsLikeAnEnum.Option_1)
0
David Foley

Numaralandırma gibi türler uygulamanın en iyi yolunun sembollerle olduğunu düşünüyorum çünkü tamsayı gibi davranıyor (performans söz konusu olduğunda object_id karşılaştırma yapmak için kullanılıyor); indeksleme konusunda endişelenmenize gerek yok ve xD kodunuzda çok temiz görünüyorlar

0
goreorto

Diğer bir yaklaşım ise, bir Ruby sınıfını aşağıdaki RubyFleebie blog yazısı 'de açıklandığı gibi ad ve değerleri içeren bir karma değerle kullanmaktır. Bu, değerler ve sabitler arasında kolayca dönüşüm yapmanızı sağlar (özellikle belirli bir değerin adını aramak için bir sınıf yöntemi eklerseniz).

0
Philippe Monnet

Tutarlı bir eşitlik yönetimi olan bir enumu taklit etmenin bir başka yolu (utanmadan Dave Thomas'tan alınmıştır). Açık numaralara (sembollere benzer) ve kapalı (önceden tanımlanmış) numaralara izin verir.

class Enum
  def self.new(values = nil)
    enum = Class.new do
      unless values
        def self.const_missing(name)
          const_set(name, new(name))
        end
      end

      def initialize(name)
        @enum_name = name
      end

      def to_s
        "#{self.class}::#@enum_name"
      end
    end

    if values
      enum.instance_eval do
        values.each { |e| const_set(e, enum.new(e)) }
      end
    end

    enum
  end
end

Genre = Enum.new %w(Gothic Metal) # creates closed enum
Architecture = Enum.new           # creates open enum

Genre::Gothic == Genre::Gothic        # => true
Genre::Gothic != Architecture::Gothic # => true
0
Daniel Doubleday

Inum deneyin . https://github.com/alfa-jpn/inum

class Color < Inum::Base
  define :RED
  define :GREEN
  define :BLUE
end
Color::RED 
Color.parse('blue') # => Color::BLUE
Color.parse(2)      # => Color::GREEN

daha fazlasını görün https://github.com/alfa-jpn/inum#usage

0
horun