Expresiones de Ruby comunes
idioms (15)
Una de las cosas que me encanta de ruby es que, en su mayoría, es un lenguaje muy legible (lo cual es genial para el código de auto-documentación)
Sin embargo, inspirado por esta pregunta: Ruby Code explicó y la descripción de cómo funciona ||=
en rubí, estaba pensando en los modismos de rubí que no uso, como francamente, no los asimilo completamente.
Entonces mi pregunta es, similar al ejemplo de la pregunta a la que se hace referencia, ¿qué expresiones de rubí comunes, pero no obvias, debo tener en cuenta para ser un programador de rubí verdaderamente competente?
Por cierto, de la pregunta a la que se hace referencia
a ||= b
es equivalente a
if a == nil || a == false
a = b
end
(Gracias a Ian Terrell por la corrección)
Editar: Resulta que este punto no es totalmente indiscutible. La expansión correcta es de hecho
(a || (a = (b)))
Vea estos enlaces para saber por qué:
- http://DABlog.RubyPAL.Com/2008/3/25/a-short-circuit-edge-case/
- http://DABlog.RubyPAL.Com/2008/3/26/short-circuit-post-correction/
- http://ProcNew.Com/ruby-short-circuit-edge-case-response.html
Gracias a Jörg W Mittag por señalar esto.
Por cierto, de la pregunta a la que se hace referencia
a ||= b
es equivalente a
if a == nil a = b end
Eso es sutilmente incorrecto, y es una fuente de errores en las aplicaciones Ruby de los recién llegados.
Como ambos (y solo) nil
y false
evalúan a booleano falso, a ||= b
es en realidad (casi *) equivalente a:
if a == nil || a == false
a = b
end
O, para reescribirlo con otro idioma Ruby:
a = b unless a
(* Debido a que cada enunciado tiene un valor, estos no son técnicamente equivalentes a a ||= b
. Pero si no está confiando en el valor de la declaración, no verá una diferencia).
¡Buena pregunta!
Como creo, cuanto más intuitivo y rápido sea el código, mejor será el software que estamos creando. Te mostraré cómo expreso mis pensamientos usando Ruby en pequeños fragmentos de código. Leer más aquí
Mapa
Podemos usar el método de mapa de diferentes maneras:
user_ids = users.map { |user| user.id }
O:
user_ids = users.map(&:id)
Muestra
Podemos usar el método rand:
[1, 2, 3][rand(3)]
Barajar:
[1, 2, 3].shuffle.first
Y la manera idiomática, simple y fácil ... ¡muestra!
[1, 2, 3].sample
Equivalencia / Memocion de doble tubo
Como dijiste en la descripción, podemos usar la memorización:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Método Estático / Método de Clase
Me gusta usar métodos de clase, siento que es una forma realmente idiomática de crear y usar clases:
GetSearchResult.call(params)
Sencillo. Hermosa. Intuitivo. ¿Qué pasa en el fondo?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Para obtener más información para escribir el código de Ruby idiomático, lea aquí
Algunos modismos más:
Uso de %w
, %r
y %(
delimitadores
%w{ An array of strings %}
%r{ ^http:// }
%{ I don''t care if the string has ''single'' or "double" strings }
Tipo de comparación en declaraciones de casos
def something(x)
case x
when Array
# Do something with array
when String
# Do something with string
else
# You should really teach your objects how to ''quack'', don''t you?
end
end
... y abuso general del método ===
en declaraciones de casos
case x
when ''something concrete'' then ...
when SomeClass then ...
when /matches this/ then ...
when (10...20) then ...
when some_condition >= some_value then ...
else ...
end
Algo que debería parecer natural para los Rubyistas, pero tal vez no para las personas que provienen de otros idiomas: el uso de each
a favor de for .. in
some_iterable_object.each{|item| ... }
En Ruby 1.9+, Rails, o al aplicar el parche al método Symbol # to_proc, this está convirtiendo en un idioma cada vez más popular:
strings.map(&:upcase)
Método condicional / definición constante
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Métodos de consulta y métodos destructivos (bang)
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Parámetros splat implícitos
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
Aquí hay algunos, seleccionados de varias fuentes:
utilice "a menos" y "hasta" en lugar de "si no" y "mientras no". Sin embargo, intente no utilizar "a menos" cuando exista una condición de "else".
Recuerde que puede asignar múltiples variables a la vez:
a,b,c = 1,2,3
e incluso cambiar variable sin una temperatura:
a,b = b,a
Utilice los condicionales finales cuando sea apropiado, por ejemplo
do_something_interesting unless want_to_be_bored?
Tenga en cuenta una forma comúnmente utilizada pero no obvia al instante (al menos para mí) de definir los métodos de clase:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Algunas referencias:
Array.pack y String.unpack para trabajar con archivos binarios:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
Esta slideshow es bastante completa en los principales modismos de Ruby, como en:
Cambiar dos valores:
x, y = y, x
Parámetros que, si no se especifican, toman algún valor predeterminado
def somemethod(x, y=nil)
Combina parámetros extraños en una matriz
def substitute(re, str, *rest)
Y así...
La cláusula magic if que permite que el mismo archivo sirva como una biblioteca o un script:
if __FILE__ == $0
# this library may be run as a standalone script
end
Paquetes de embalaje y desempaquetado:
# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
rest.map { |word| first + word }
end
catall( ''franken'', ''stein'', ''berry'', ''sense'' ) #=> [ ''frankenstein'', ''frankenberry'', ''frankensense'' ]
El azúcar sintáctico para hashes como argumentos de método
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
Iniciales de hash:
# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}
sintaxis de metaclase
x = Array.new
y = Array.new
class << x
# this acts like a class definition, but only applies to x
def custom_method
:pow
end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError
variables de instancia de clase
class Ticket
@remaining = 3
def self.new
if @remaining > 0
@remaining -= 1
super
else
"IOU"
end
end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"
Bloques, procs y lambdas. Vive y respira.
# know how to pack them into an object
block = lambda { |e| puts e }
# unpack them for a method
%w{ and then what? }.each(&block)
# create them as needed
%w{ I saw a ghost! }.each { |w| puts w.upcase }
# and from the method side, how to call them
def ok
yield :ok
end
# or pack them into a block to give to someone else
def ok_dokey_ok(&block)
ok(&block)
block[:dokey] # same as block.call(:dokey)
ok(&block)
end
# know where the parentheses go when a method takes arguments and a block.
%w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
pusher = lambda { |array, word| array.unshift(word) }
%w{ eat more fish }.inject([], &pusher) #=> [''fish'', ''more'', ''eat'' ]
Mantengo una página wiki que cubre algunas expresiones idiomáticas y el formato de Ruby:
Me gusta cómo If-then-elses o case-when se pueden acortar porque devuelven un valor:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
podría ser reescrito
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
Lo mismo podría aplicarse a caso-cuando:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Me gusta esto:
str = "Something evil this way comes!"
regexp = /(/w[aeiou])/
str[regexp, 1] # <- This
Que es (aproximadamente) equivalente a:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
O al menos eso es lo que he usado para reemplazar esos bloques.
Puede hacer una copia profunda con el objeto Marshaling fácilmente. - tomado de The Ruby Programming Language
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Tenga en cuenta que los archivos y las secuencias de E / S, así como los objetos de Método y Enlace, son demasiado dinámicos para ser calculados; no habría una manera confiable de restaurar su estado.
Siempre me olvido de la sintaxis exacta de esta abreviatura if else statement (y el nombre del operador ¿comenta alguien?) Creo que es ampliamente utilizado fuera de ruby, pero en caso de que alguien más quiera la sintaxis aquí está:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
se expande a
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
actualizar
llamado el operador ternario:
devolver myvar? myvar.size: 0
Sugiero leer el código de plugins o gemas populares y bien diseñados de personas que admiras y respetas.
Algunos ejemplos que me he encontrado:
if params[:controller] == ''discussions'' or params[:controller] == ''account''
# do something here
end
correspondiente a
if [''account'', ''discussions''].include? params[:controller]
# do something here
end
que luego sería refactorizado para
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
método falta magia
class Dummy
def method_missing(m, *args, &block)
"You just called method with name #{m} and arguments- #{args}"
end
end
Dummy.new.anything(10, 20)
=> "You just called method with name anything and arguments- [10, 20]"
si llama a métodos que no existen en los objetos ruby, el intérprete de ruby llamará al método llamado ''method_missing'' si está definido, puede usar esto para algunos trucos, como escribir api wrappers, o dsl, donde no conoce todos los métodos y parámetros nombres
a = (b && b.attribute) || "default"
es aproximadamente:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
Utilizo esto cuando b es un registro que puede o no haber sido encontrado, y necesito obtener uno de sus atributos.