rails - que significa en ruby
Características ocultas de Ruby (30)
Continuando con el meme de "Funciones ocultas de ...", compartamos las características menos conocidas pero útiles del lenguaje de programación Ruby.
Trata de limitar esta discusión con el núcleo de Ruby, sin nada de Ruby on Rails.
Ver también:
- Funciones ocultas de C #
- Funciones ocultas de Java
- Funciones ocultas de JavaScript
- Funciones ocultas de Ruby on Rails
- Funciones ocultas de Python
(Por favor, solo una característica oculta por respuesta).
Gracias
module_function
Los métodos de módulo que se declaran como función_de_módulo crearán copias de ellos mismos como métodos de instancia privada en la clase que incluye el Módulo:
module M
def not!
''not!''
end
module_function :not!
end
class C
include M
def fun
not!
end
end
M.not! # => ''not!
C.new.fun # => ''not!''
C.new.not! # => NoMethodError: private method `not!'' called for #<C:0x1261a00>
Si usa module_function sin ningún argumento, entonces cualquier método de módulo que venga después de la sentencia module_function se convertirá automáticamente en module_functions.
module M
module_function
def not!
''not!''
end
def yea!
''yea!''
end
end
class C
include M
def fun
not! + '' '' + yea!
end
end
M.not! # => ''not!''
M.yea! # => ''yea!''
C.new.fun # => ''not! yea!''
Advertencia: este artículo fue votado como el truco número 1 más horrendo de 2008 , así que úselo con cuidado. En realidad, evítelo como la peste, pero sin duda es Hidden Ruby.
Los superadores agregan nuevos operadores a Ruby
¿Alguna vez ha querido un operador súper secreto de handshake para alguna operación única en su código? Me gusta jugar al golf de código? Pruebe operadores como - ~ + ~ - o <--- Ese último se usa en los ejemplos para invertir el orden de un artículo.
No tengo nada que ver con el Proyecto Superators más allá de admirarlo.
De Ruby 1.9 Proc # === es un alias de la llamada Proc #, lo que significa que los objetos Proc se pueden usar en sentencias case como esta:
def multiple_of(factor)
Proc.new{|product| product.modulo(factor).zero?}
end
case number
when multiple_of(3)
puts "Multiple of 3"
when multiple_of(7)
puts "Multiple of 7"
end
Descargue la fuente de Ruby 1.9 y publique make golf
, entonces puede hacer cosas como esta:
make golf
./goruby -e ''h''
# => Hello, world!
./goruby -e ''p St''
# => StandardError
./goruby -e ''p 1.tf''
# => 1.0
./goruby19 -e ''p Fil.exp(".")''
"/home/manveru/pkgbuilds/ruby-svn/src/trunk"
Lee el golf_prelude.c
para obtener más cosas ordenadas que se esconden.
El binario "ruby" (al menos las imágenes de resonancia magnética) es compatible con muchos de los conmutadores que hacen que los one-liners de Perl sean bastante populares.
Significativos:
- -n Configura un bucle externo con solo "obtiene", que mágicamente funciona con el nombre de archivo dado o STDIN, configurando cada línea de lectura en $ _
- -p Similar a -n pero con una posición automática s al final de cada ciclo de iteración
- - Una llamada automática a .split en cada línea de entrada, almacenada en $ F
- -i Archivos de entrada de edición in situ
- -l Llamada automática a .chomp en la entrada
- -e Ejecutar un fragmento de código
- -c Verificar el código fuente
- -w Con advertencias
Algunos ejemplos:
# Print each line with its number:
ruby -ne ''print($., ": ", $_)'' < /etc/irbrc
# Print each line reversed:
ruby -lne ''puts $_.reverse'' < /etc/irbrc
# Print the second column from an input CSV (dumb - no balanced quote support etc):
ruby -F, -ane ''puts $F[1]'' < /etc/irbrc
# Print lines that contain "eat"
ruby -ne ''puts $_ if /eat/i'' < /etc/irbrc
# Same as above:
ruby -pe ''next unless /eat/i'' < /etc/irbrc
# Pass-through (like cat, but with possible line-end munging):
ruby -p -e '''' < /etc/irbrc
# Uppercase all input:
ruby -p -e ''$_.upcase!'' < /etc/irbrc
# Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN:
ruby -i.bak -p -e ''$_.upcase!'' /etc/irbrc
Siéntase libre de googlear "ruby one-liners" y "perl one-liners" para obtener más ejemplos utilizables y prácticos. En esencia, le permite usar ruby como un reemplazo bastante poderoso para awk y sed.
El método send () es un método de uso general que se puede usar en cualquier clase u objeto en Ruby. Si no se reemplaza, send () acepta una cadena y llama al nombre del método cuya cadena se pasa. Por ejemplo, si el usuario hace clic en el botón "Clr", la cadena "press_clear" se enviará al método send () y se llamará al método "press_clear". El método send () permite una forma divertida y dinámica de llamar funciones en Ruby.
%w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
button btn, :width => 46, :height => 46 do
method = case btn
when /[0-9]/: ''press_''+btn
when ''Clr'': ''press_clear''
when ''='': ''press_equals''
when ''+'': ''press_add''
when ''-'': ''press_sub''
when ''*'': ''press_times''
when ''/'': ''press_div''
end
number.send(method)
number_field.replace strong(number)
end
end
Hablo más sobre esta característica en Blogging Shoes: The Simple-Calc Application
Engañar a alguna clase o módulo diciéndole que ha requerido algo que realmente no ha requerido:
$" << "something"
Esto es útil, por ejemplo, cuando se requiere A que, a su vez, requiere B, pero no necesitamos B en nuestro código (y A no lo usará a través de nuestro código):
Por ejemplo, bdrb_test_helper requires
de bdrb_test_helper requires
''test/spec''
, pero no lo usa en absoluto, entonces en su código:
$" << "test/spec"
require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper")
Gran parte de la magia que ves en Rubyland tiene que ver con la metaprogramación, que es simplemente escribir código que escribe código para ti. El attr_accessor
, attr_reader
y attr_writer
son todos metaprogramación simple, ya que crean dos métodos en una línea, siguiendo un patrón estándar. Rails realiza una gran cantidad de metaprogramación con sus métodos de administración de relaciones como has_one
y belongs_to
.
Pero es bastante simple crear tus propios trucos de metaprogramación usando class_eval
para ejecutar código escrito dinámicamente.
El siguiente ejemplo permite que un objeto envoltorio reenvíe ciertos métodos a un objeto interno:
class Wrapper
attr_accessor :internal
def self.forwards(*methods)
methods.each do |method|
define_method method do |*arguments, &block|
internal.send method, *arguments, &block
end
end
end
forwards :to_i, :length, :split
end
w = Wrapper.new
w.internal = "12 13 14"
w.to_i # => 12
w.length # => 8
w.split(''1'') # => ["", "2 ", "3 ", "4"]
El método Wrapper.forwards
toma símbolos para los nombres de los métodos y los almacena en la matriz de methods
. Luego, para cada uno de los dados, usamos define_method
para crear un nuevo método cuyo trabajo es enviar el mensaje, incluidos todos los argumentos y bloques.
Un gran recurso para los problemas de metaprogramación es por qué The Lucky Stiff "Ver Metaprogramación Claramente" .
Hashes con valores predeterminados! Una matriz en este caso.
parties = Hash.new {|hash, key| hash[key] = [] }
parties["Summer party"]
# => []
parties["Summer party"] << "Joe"
parties["Other party"] << "Jane"
Muy útil en la metaprogramación.
La función Symbol # to_proc que proporciona Rails es realmente genial.
En lugar de
Employee.collect { |emp| emp.name }
Puedes escribir:
Employee.collect(&:name)
Llego tarde a la fiesta, pero:
Puede tomar fácilmente dos matrices de igual longitud y convertirlas en hash con una matriz que proporciona las claves y la otra los valores:
a = [:x, :y, :z]
b = [123, 456, 789]
Hash[a.zip(b)]
# => { :x => 123, :y => 456, :z => 789 }
(Esto funciona porque Array # zip "comprime" los valores de las dos matrices:
a.zip(b) # => [[:x, 123], [:y, 456], [:z, 789]]
Y Hash [] puede tomar una matriz así. He visto a gente hacer esto también:
Hash[*a.zip(b).flatten] # unnecessary!
Lo cual produce el mismo resultado, pero el splat y flatten son totalmente innecesarios, ¿quizás no estaban en el pasado?
Me parece que usar el comando define_method para generar dinámicamente métodos es bastante interesante y no tan conocido. Por ejemplo:
((0..9).each do |n|
define_method "press_#{n}" do
@number = @number.to_i * 10 + n
end
end
El código anterior usa el comando ''define_method'' para crear dinámicamente los métodos "press1" a "press9". En lugar de escribir los 10 métodos que contienen el mismo código, el comando definir método se usa para generar estos métodos sobre la marcha según sea necesario.
No sé qué tan oculto es esto, pero lo he encontrado útil cuando necesito hacer un hash fuera de una matriz unidimensional:
fruit = ["apple","red","banana","yellow"]
=> ["apple", "red", "banana", "yellow"]
Hash[*fruit]
=> {"apple"=>"red", "banana"=>"yellow"}
Operadores booleanos en valores no booleanos
&&
y ||
Ambos devuelven el valor de la última expresión evaluada.
Por eso, ||=
actualizará la variable con la expresión de valor devuelto en el lado derecho si la variable no está definida. Esto no está documentado explícitamente, sino que es de conocimiento común.
Sin embargo, el &&=
no es tan conocido.
string &&= string + "suffix"
es equivalente a
if string
string = string + "suffix"
end
Es muy útil para operaciones destructivas que no deberían continuar si la variable no está definida.
Otra adición divertida en la funcionalidad 1.9 Proc es Proc # curry, que le permite convertir un Proc aceptando n argumentos en uno aceptando n-1. Aquí se combina con la sugerencia de Proc # === que mencioné anteriormente:
it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week }
it_is_saturday = it_is_day_of_week.curry[6]
it_is_sunday = it_is_day_of_week.curry[0]
case Time.now
when it_is_saturday
puts "Saturday!"
when it_is_sunday
puts "Sunday!"
else
puts "Not the weekend"
end
Otra pequeña característica: convertir un Fixnum
en cualquier base de hasta 36:
>> 1234567890.to_s(2)
=> "1001001100101100000001011010010"
>> 1234567890.to_s(8)
=> "11145401322"
>> 1234567890.to_s(16)
=> "499602d2"
>> 1234567890.to_s(24)
=> "6b1230i"
>> 1234567890.to_s(36)
=> "kf12oi"
Y como ha comentado Huw Walters, la conversión al revés es igual de simple:
>> "kf12oi".to_i(36)
=> 1234567890
Peter Cooper tiene una buena lista de trucos de Ruby. Quizás mi favorito es permitir que se enumeren tanto los artículos individuales como las colecciones. (Es decir, tratar un objeto que no es de colección como una colección que contiene solo ese objeto). Se ve así:
[*items].each do |item|
# ...
end
Un truco que me gusta es usar el expansor splat ( *
) en objetos que no sean Arrays. Aquí hay un ejemplo de una coincidencia de expresión regular:
match, text, number = *"Something 981".match(/([A-z]*) ([0-9]*)/)
Otros ejemplos incluyen:
a, b, c = *(''A''..''Z'')
Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom
Una de las mejores cosas de Ruby es que puedes invocar métodos y ejecutar código en lugares que otros lenguajes ignorarían, como en el método o las definiciones de clases.
Por ejemplo, para crear una clase que tenga una superclase desconocida hasta el tiempo de ejecución, es decir, que sea aleatoria, podría hacer lo siguiente:
class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample
end
RandomSubclass.superclass # could output one of 6 different classes.
Esto usa el método de Array#sample
1.9 Array#sample
(en 1.8.7-only, vea la Array#choice
), y el ejemplo es bastante artificial, pero puede ver el poder aquí.
Otro buen ejemplo es la capacidad de poner valores de parámetros predeterminados que no son fijos (como suelen exigir otros lenguajes):
def do_something_at(something, at = Time.now)
# ...
end
Por supuesto, el problema con el primer ejemplo es que se evalúa en tiempo de definición, no en tiempo de llamada. Entonces, una vez que se ha elegido una superclase, permanece esa superclase para el resto del programa.
Sin embargo, en el segundo ejemplo, cada vez que llame a do_something_at
, la variable at
será el momento en que se llamó al método (bueno, muy muy cerca)
Uno final: en ruby puede usar cualquier caracter que quiera para delimitar cadenas. Toma el siguiente código:
message = "My message"
contrived_example = "<div id=/"contrived/">#{message}</div>"
Si no quiere escapar de las comillas dobles dentro de la cadena, puede simplemente usar un delimitador diferente:
contrived_example = %{<div id="contrived-example">#{message}</div>}
contrived_example = %[<div id="contrived-example">#{message}</div>]
Además de evitar tener que escanear delimitadores, puede usar estos delimitadores para cadenas multilínea más bonitas:
sql = %{
SELECT strings
FROM complicated_table
WHERE complicated_condition = ''1''
}
Use un objeto Range como una lista infinita de perezosos:
Inf = 1.0 / 0
(1..Inf).take(5) #=> [1, 2, 3, 4, 5]
Más información aquí: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/
Wow, nadie mencionó el operador de flip flop:
1.upto(100) do |i|
puts i if (i == 3)..(i == 15)
end
crea una matriz de números consecutivos:
x = [*0..5]
establece x a [0, 1, 2, 3, 4, 5]
use cualquier cosa que responda a ===(obj)
para las comparaciones de casos:
case foo
when /baz/
do_something_with_the_string_matching_baz
when 12..15
do_something_with_the_integer_between_12_and_15
when lambda { |x| x % 5 == 0 }
# only works in Ruby 1.9 or if you alias Proc#call as Proc#===
do_something_with_the_integer_that_is_a_multiple_of_5
when Bar
do_something_with_the_instance_of_Bar
when some_object
do_something_with_the_thing_that_matches_some_object
end
Module
(y, por lo tanto, la Class
), Regexp
, Date
y muchas otras clases definen un método de instancia: === (otro), y todos pueden utilizarse.
Gracias a Farrel por el recordatorio de que Proc#call
recibió un alias como Proc#===
en Ruby 1.9.
Class.new()
Crea una nueva clase en tiempo de ejecución. El argumento puede ser una clase de la cual derivar, y el bloque es el cuerpo de la clase. Es posible que también desee ver const_set/const_get/const_defined?
para obtener su nueva clase debidamente registrada, para que inspect
imprima un nombre en lugar de un número.
No es algo que necesita todos los días, pero es bastante útil cuando lo hace.
Fixnum#to_s(base)
puede ser realmente útil en algún caso. Uno de estos casos es la generación de tokens aleatorios (pseudo) únicos mediante la conversión de un número aleatorio a una cadena con una base de 36.
Token de longitud 8:
rand(36**8).to_s(36) => "fmhpjfao"
rand(36**8).to_s(36) => "gcer9ecu"
rand(36**8).to_s(36) => "krpm0h9r"
Token de longitud 6:
rand(36**6).to_s(36) => "bvhl8d"
rand(36**6).to_s(36) => "lb7tis"
rand(36**6).to_s(36) => "ibwgeh"
Definir un método que acepte cualquier cantidad de parámetros y que simplemente los descarte a todos
def hello(*)
super
puts "hello!"
end
El método hello
anterior solo necesita puts
"hello"
en la pantalla y llamar a super
- pero dado que la superclase hello
define los parámetros que tiene, sin embargo, dado que en realidad no necesita usar los parámetros en sí, no tiene para darles un nombre.
Destructuring a Array
(a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ]
Dónde:
a #=> :a
b #=> :b
c #=> :c
d #=> [:d1, :d2]
Usando esta técnica podemos usar la asignación simple para obtener los valores exactos que queremos de la matriz anidada de cualquier profundidad.
Hash auto vivificante en Ruby
def cnh # silly name "create nested hash"
Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}
end
my_hash = cnh
my_hash[1][2][3] = 4
my_hash # => { 1 => { 2 => { 3 =>4 } } }
Esto puede ser muy útil.