रूबी में एनम मुहावर को लागू करने का सबसे अच्छा तरीका क्या है? मैं एक ऐसी चीज की तलाश कर रहा हूं जिसका उपयोग मैं (लगभग) जावा / सी # एनम की तरह कर सकता हूं।
रूबी में एनम मुहावर को लागू करने का सबसे अच्छा तरीका क्या है? मैं एक ऐसी चीज की तलाश कर रहा हूं जिसका उपयोग मैं (लगभग) जावा / सी # एनम की तरह कर सकता हूं।
जवाबों:
दो तरीके। प्रतीक ( :foo
संकेतन) या स्थिरांक ( FOO
संकेतन)।
जब आप शाब्दिक तार के साथ कोडिंग के बिना पठनीयता को बढ़ाना चाहते हैं तो प्रतीक उपयुक्त हैं।
postal_code[:minnesota] = "MN"
postal_code[:new_york] = "NY"
जब आपके पास एक अंतर्निहित मूल्य है जो महत्वपूर्ण है, तो कॉन्स्टेंट उपयुक्त हैं। बस अपने स्थिरांक को रखने के लिए एक मॉड्यूल घोषित करें और उसके बाद स्थिरांक घोषित करें।
module Foo
BAR = 1
BAZ = 2
BIZ = 4
end
flags = Foo::BAR | Foo::BAZ # flags = 3
:minnesota.to_s
प्रतीक के स्ट्रिंग संस्करण को बचाने के लिए डेटाबेस में सहेजते समय आप कुछ का उपयोग भी कर सकते हैं । रेल, मेरा मानना है कि इसमें से कुछ से निपटने के लिए कुछ सहायक तरीके हैं।
मुझे आश्चर्य है कि किसी ने निम्नलिखित की तरह कुछ की पेशकश नहीं की है ( आरएपीआई मणि से काटा गया ):
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
जिसका उपयोग इस तरह किया जा सकता है:
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
उदाहरण:
>> 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
यह डेटाबेस परिदृश्यों में अच्छी तरह से खेलता है, या जब सी स्टाइल स्थिरांक / एनमों के साथ काम करता है (जैसा कि एफएफआई का उपयोग करते समय होता है , जिसे आरएपीआई व्यापक उपयोग करता है)।
इसके अलावा, आपको टाइपो के बारे में चिंता करने की ज़रूरत नहीं है, क्योंकि आप हैश-टाइप समाधान का उपयोग कर सकते हैं।
ऐसा करने का सबसे मुहावरेदार तरीका प्रतीकों का उपयोग करना है। उदाहरण के लिए, इसके बजाय:
enum {
FOO,
BAR,
BAZ
}
myFunc(FOO);
... आप सिर्फ प्रतीकों का उपयोग कर सकते हैं:
# 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)
यह एनम की तुलना में थोड़ा अधिक खुला है, लेकिन यह रूबी भावना के साथ अच्छी तरह से फिट बैठता है।
प्रतीक भी बहुत अच्छा प्रदर्शन करते हैं। समानता के लिए दो प्रतीकों की तुलना, उदाहरण के लिए, दो तार की तुलना में बहुत तेज है।
मैं निम्नलिखित दृष्टिकोण का उपयोग करता हूं:
class MyClass
MY_ENUM = [MY_VALUE_1 = 'value1', MY_VALUE_2 = 'value2']
end
मुझे यह निम्नलिखित फायदों के लिए पसंद है:
MY_ENUM
MY_VALUE_1
प्रतीक बेहतर कारण हो सकते हैं कि आपको बाहरी वर्ग का नाम नहीं लिखना है, यदि आप इसे किसी अन्य कक्षा में उपयोग कर रहे हैं ( MyClass::MY_VALUE_1
)
यदि आप रेल्स 4.2 या अधिक का उपयोग कर रहे हैं तो आप रेल एनम का उपयोग कर सकते हैं।
रेल में अब किसी भी रत्न को शामिल किए बिना डिफ़ॉल्ट रूप से दुश्मनी है।
यह जावा, C ++ एनम के समान (और सुविधाओं के साथ अधिक) समान है।
Http://edgeapi.rubyonrails.org/classes/ActiveRecord/Emum.html से उद्धृत :
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
Conversation
कक्षा के कई उदाहरणों को बनाने की अनुमति देता है - मेरा मानना है कि इसे केवल 1 उदाहरण की अनुमति देनी चाहिए।
यह रूबी में दुश्मनी के लिए मेरा दृष्टिकोण है। मैं छोटे और मीठे के लिए जा रहा था, जरूरी नहीं कि सबसे सी-लाइक हो। कोई विचार?
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
रूबी-एनम रत्न, https://github.com/dblock/ruby-enum देखें ।
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
Gender.all
Gender::MALE
शायद सबसे अच्छा हल्के दृष्टिकोण होगा
module MyConstants
ABC = Class.new
DEF = Class.new
GHI = Class.new
end
इस तरह मानों के संबद्ध नाम हैं, जैसा कि जावा / सी # में:
MyConstants::ABC
=> MyConstants::ABC
सभी मान प्राप्त करने के लिए, आप कर सकते हैं
MyConstants.constants
=> [:ABC, :DEF, :GHI]
यदि आप एक एनुम का क्रमिक मूल्य चाहते हैं, तो आप कर सकते हैं
MyConstants.constants.index :GHI
=> 2
class ABC; end
मुझे पता है कि इस सवाल को पोस्ट किए हुए एक लंबा समय हो गया है, लेकिन मेरे पास एक ही सवाल था और इस पोस्ट ने मुझे जवाब नहीं दिया। मैं यह देखने के लिए एक आसान तरीका चाहता था कि संख्या क्या दर्शाती है, आसान तुलना है, और एनम का प्रतिनिधित्व करने वाले कॉलम का उपयोग करके लुकअप के लिए सभी ActiveRecord समर्थन का समर्थन करता है।
मुझे कुछ भी नहीं मिला, इसलिए मैंने यिनम नामक एक भयानक कार्यान्वयन किया, जिसमें मुझे वह सब कुछ मिल गया जिसकी मुझे तलाश थी। ऐनक के टन, तो मुझे यकीन है कि यह सुरक्षित है।
कुछ उदाहरण विशेषताएं:
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
यदि आप प्रतीकों के साथ टाइपोस के बारे में चिंतित हैं, तो सुनिश्चित करें कि जब आप किसी गैर-मौजूद कुंजी के साथ एक मूल्य का उपयोग करते हैं तो आपका कोड एक अपवाद उठाता है। आप इसके fetch
बजाय इसका उपयोग करके कर सकते हैं []
:
my_value = my_hash.fetch(:key)
या यदि आप एक गैर-मौजूद कुंजी की आपूर्ति करते हैं, तो डिफ़ॉल्ट रूप से एक अपवाद बढ़ाएँ:
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
यदि हैश पहले से मौजूद है, तो आप अपवाद-बढ़ाने वाले व्यवहार को जोड़ सकते हैं:
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
आम तौर पर, आपको स्थिरांक के साथ टाइपो सुरक्षा के बारे में चिंता करने की ज़रूरत नहीं है। यदि आप एक स्थिर नाम को मिस करते हैं, तो यह आमतौर पर एक अपवाद बढ़ाएगा।
FOO_VALUES = {missing: 0, something: 1, something_else: 2, ...}
यह परिभाषित करता है कुंजी प्रतीकों। missing
, something
, आदि, और भी उन्हें जुड़े मूल्यों के माध्यम से तुलनीय बनाता है।)
किसी ने आगे बढ़कर, रेणु नामक एक माणिक रत्न लिखा । यह व्यवहार की तरह निकटतम जावा / सी # प्राप्त करने का दावा करता है। व्यक्तिगत रूप से मैं अभी भी रूबी सीख रहा हूं, और जब मैं एक विशिष्ट वर्ग को स्थिर एनम, संभवत: एक हैश बनाना चाहता था, तो मैं थोड़ा चौंक गया था, यह बिल्कुल Google के माध्यम से आसानी से नहीं मिला था।
यह सब निर्भर करता है कि आप जावा या सी # एनम का उपयोग कैसे करते हैं। आप इसे कैसे उपयोग करते हैं यह आप रूबी में चुने गए समाधान को निर्धारित करेंगे।
Set
उदाहरण के लिए, मूल प्रकार आज़माएँ :
>> 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"}>
Set[:a, :b, :c]
?
हाल ही में हमने एक रत्न जारी किया जो रूबी में एनम को लागू करता है । मेरी पोस्ट में आपको अपने सवालों के जवाब मिलेंगे। इसके अलावा मैंने वहां बताया कि हमारा कार्यान्वयन मौजूदा लोगों की तुलना में बेहतर क्यों है (वास्तव में रूबी में इस सुविधा के कई कार्यान्वयन अभी भी रत्नों के रूप में हैं)।
एक अन्य समाधान OpenStruct का उपयोग कर रहा है। अपने सुंदर सीधे आगे और साफ।
https://ruby-doc.org/stdlib-2.3.1/libdoc/ostruct/rdoc/OpenStruct.html
उदाहरण:
# 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'
प्रतीक माणिक्य मार्ग है। हालांकि, कभी-कभी किसी को कुछ सी कोड या कुछ या जावा से बात करने की ज़रूरत होती है जो विभिन्न चीजों के लिए कुछ एनम को उजागर करता है।
#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
यह तो इस तरह इस्तेमाल किया जा सकता है
require 'server_roles'
sSymb, sEnum =EnumLike.server_role()
foreignvec[sEnum[:SERVER_WORKSTATION]]=8
यह निश्चित रूप से अमूर्त बनाया जा सकता है और आप हमारे अपने एनम वर्ग को रोल कर सकते हैं
server_Symb
किसी विशेष कारण के लिए दूसरे शब्द को चर (जैसे ) में बड़ा कर रहे हैं ? जब तक कोई विशेष कारण नहीं है, यह चर के लिए मुहावरेदार है snake_case_with_all_lower_case
, और प्रतीकों के लिए है :lower_case
।
server_Symb.each_with_index { |e,i| server_Enum[e] = i}
। की कोई जरूरत नहीं i = 0
।
मैंने उस तरह से एनम लागू किया है
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
फिर ऑपरेशन करना आसान है
EnumType.ENUM_1.label
...
enum = EnumType.find_by_id 1
...
valueArray = EnumType.values
यह थोड़ा अतिश्योक्तिपूर्ण लगता है, लेकिन यह एक ऐसी पद्धति है जिसका मैंने कुछ बार उपयोग किया है, विशेष रूप से जहां मैं xml या कुछ इस तरह के साथ एकीकरण कर रहा हूं।
#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
यह मुझे एसी # एनम की कठोरता देता है और यह मॉडल से बंधा हुआ है।
:VAL
। एक सरणी से शुरू करना और हैश का निर्माण करना बेहतर होगा.map.with_index
.key
या .invert
बल्कि एक से :VAL
कुंजी ( stackoverflow.com/a/10989394/2208016 )
key
याinvert
अधिकांश लोग प्रतीकों का उपयोग करते हैं (यह :foo_bar
वाक्यविन्यास है)। वे अद्वितीय अपारदर्शी मूल्यों की तरह हैं। प्रतीक किसी भी एनुम-शैली के प्रकार के नहीं होते हैं, इसलिए वे वास्तव में सी के एनम प्रकार का एक वफादार प्रतिनिधित्व नहीं करते हैं, लेकिन यह बहुत अच्छा है जितना इसे मिलता है।
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
आउटपुट:
1 - एक
2 - बी
3 - सी
4 - डी
to_enum
आप एक enumera देता टो , जबकि enum
में सी # / जावा भावना एक enumera है tion
कभी-कभी मुझे बस इतना करना चाहिए कि एनम के मूल्य को प्राप्त करने और जावा दुनिया के समान इसके नाम की पहचान करने में सक्षम होना चाहिए।
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'
यह मुझे एनम के उद्देश्य को पूरा करता है और इसे बहुत अधिक विस्तार से रखता है। आप एनम वर्ग के लिए और अधिक विधियाँ जोड़ सकते हैं और वायोला उन्हें सभी परिभाषित एनमों में मुफ्त में प्राप्त कर सकते हैं। उदाहरण के लिए। get_all_names और सामान की तरह।
एक अन्य दृष्टिकोण रूबी वर्ग का उपयोग करना है जिसमें निम्न रूबेलीबी ब्लॉग पोस्ट में वर्णित नाम और मूल्यों वाले हैश हैं । यह आपको मूल्यों और स्थिरांक के बीच आसानी से परिवर्तित करने की अनुमति देता है (खासकर यदि आप किसी दिए गए मूल्य के लिए नाम देखने के लिए एक वर्ग विधि जोड़ते हैं)।
मुझे लगता है कि एन्यूमरेशन को लागू करने का सबसे अच्छा तरीका प्रतीकों के साथ है क्योंकि बहुत अधिक पूर्णांक के रूप में व्यवहार करता है (जब यह प्रदर्शन की बात आती है, तो object_id का उपयोग तुलना करने के लिए किया जाता है); आपको अनुक्रमण के बारे में चिंता करने की आवश्यकता नहीं है और वे आपके कोड xD में वास्तव में साफ दिखते हैं
लगातार समानता से निपटने के लिए एक दुश्मनी की नकल करने का दूसरा तरीका (बेशर्मी से डेव थॉमस से अपनाया गया)। खुले एनमों (प्रतीकों की तरह) और बंद (पूर्वनिर्धारित) एनमों की अनुमति देता है।
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
इनम ट्राई करें। 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