Вопрос

Что мне нравится в ruby, так это то, что в основном это очень читаемый язык (который отлично подходит для самодокументируемого кода).

Однако, вдохновленный этим вопросом: Объясненный код Ruby и описание того, как ||= работает на ruby, я думал об идиомах ruby, которые я не использую, так как, честно говоря, я не полностью их понимаю.

Итак, мой вопрос, аналогичный примеру из приведенного вопроса, о каких распространенных, но не очевидных идиомах ruby мне нужно знать, чтобы быть действительно опытным программистом ruby?

Кстати, из упомянутого вопроса

a ||= b 

эквивалентно

if a == nil || a == false
  a = b
end

(Спасибо Иэну Терреллу за исправление)

Редактировать: Оказывается, этот момент не является полностью бесспорным.Правильное расширение на самом деле

(a || (a = (b))) 

Смотрите эти ссылки, чтобы узнать, почему:

Спасибо Йоргу В Миттагу за то, что указал на это.

Это было полезно?

Решение

Волшебное предложение if, которое позволяет одному и тому же файлу служить библиотекой или скриптом:

if __FILE__ == $0
  # this library may be run as a standalone script
end

Упаковка и распаковка массивов:

# 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' ]

Синтаксический сахар для хэшей в качестве аргументов метода

this(:is => :the, :same => :as)
this({:is => :the, :same => :as})

Инициализаторы хэша:

# 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]}

синтаксис метакласса

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

переменные экземпляра класса

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"

Блоки, процедуры и лямбды.Живи и дыши ими.

 # 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' ]

Другие советы

Это слайд- шоу является достаточно полным по основным идиомам Ruby, как в:

  • Поменяйте местами два значения:

    x, y = y, x

  • Параметры, которые, если не указаны, принимают некоторое значение по умолчанию

    def somemethod(x, y=nil)

  • Преобразует посторонние параметры в массив

    def substitute(re, str, *rest)

И так далее...

Еще несколько идиом:

Использование %w, %r и %( разделители

%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }

Сравнение типов в операторах case

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

...и общее злоупотребление === метод в операторах case

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

Что-то, что должно выглядеть естественно для любителей рубинов, но, возможно, не так для людей, говорящих на других языках:использование each в пользу for .. in

some_iterable_object.each{|item| ... }

В Ruby 1.9+, Rails или путем исправления метода Symbol#to_proc, это становится все более популярной идиомой:

strings.map(&:upcase)

Условный метод / определение константы

SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)

Методы запроса и деструктивные методы (bang)

def is_awesome?
  # Return some state of the object, usually a boolean
end

def make_awesome!
  # Modify the state of the object
end

Неявные параметры splat

[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }

Мне нравится это:

str = "Something evil this way comes!"
regexp = /(\w[aeiou])/

str[regexp, 1] # <- This

Что (примерно) эквивалентно:

str_match = str.match(regexp)
str_match[1] unless str_match.nil?

Или, по крайней мере, это то, что я использовал для замены таких блоков.

Я бы посоветовал ознакомиться с кодом популярных и хорошо разработанных плагинов или драгоценных камней от людей, которыми вы восхищаетесь и уважаете.

Некоторые примеры, с которыми я сталкивался:

if params[:controller] == 'discussions' or params[:controller] == 'account'
  # do something here
end

соответствующий

if ['account', 'discussions'].include? params[:controller]
  # do something here
end

который позже будет реорганизован в

if ALLOWED_CONTROLLERS.include? params[:controller]
  # do something here
end

Вот несколько, взятых из разных источников:

используйте "если" и "до тех пор, пока" вместо "если нет" и "пока нет".Однако старайтесь не использовать "если", когда существует условие "else".

Помните, что вы можете назначить несколько переменных одновременно:

a,b,c = 1,2,3

и даже переменная подкачки без значения temp:

a,b = b,a

Используйте завершающие условные обозначения там, где это уместно, например

do_something_interesting unless want_to_be_bored?

Имейте в виду часто используемый, но не сразу очевидный (по крайней мере, для меня) способ определения методов класса:

class Animal
  class<<self
    def class_method
      puts "call me using Animal.class_method"
    end
  end
end

Некоторые ссылки:

Кстати, из упомянутого вопрос

a ||= b 

эквивалентно

if a == nil   
  a = b 
end

Это слегка неверно и является источником ошибок в приложениях Ruby для новичков.

Поскольку оба (и только) nil и false вычисляется до логического значения false, a ||= b фактически (почти *) эквивалентно:

if a == nil || a == false
  a = b
end

Или переписать его с помощью другой идиомы Ruby:

a = b unless a

(* Поскольку каждый оператор имеет значение, они технически не эквивалентны a ||= b.Но если вы не полагаетесь на значение инструкции, вы не увидите разницы.)

Я веду вики-страницу, которая охватывает некоторые идиомы Ruby и форматирование:

https://github.com/tokland/tokland/wiki/RubyIdioms

Я всегда забываю точный синтаксис этого сокращенного оператора if else (и имя оператора.кто-нибудь прокомментирует?) Я думаю, что он широко используется за пределами ruby, но на случай, если кому-то еще нужен синтаксис, вот он:

refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a  method")

расширяется до

if refactor < 3
  puts("No need to refactor YET")
else
  puts("You need to refactor this into a  method")
end

Обновить

вызывается троичный оператор:

вернуть мивар ?myvar.размер :0

Вы можете легко выполнить глубокое копирование с помощью объекта маршалинга.- взято из языка программирования Ruby

def deepcopy(o)
  Marshal.load(Marshal.dump(o))
end

Обратите внимание, что файлы и потоки ввода-вывода, а также а также объекты метода и привязки слишком динамичны для маршалирования;там не было бы надежного способа восстановить их состояние.

a = (b && b.attribute) || "default"

это примерно:

if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"

Я использую это, когда b - это запись, которая может быть найдена, а может и не быть найдена, и мне нужно получить один из ее атрибутов.

Мне нравится, как If-then-elses или case-when могут быть сокращены, потому что они возвращают значение:

if test>0
  result = "positive"
elsif test==0
  result = "zero"
else
  result = "negative"
end

может быть переписан

result = if test>0
  "positive"
elsif test==0
  "zero"
else
  "negative"
end

То же самое можно было бы применить к случаю, когда:

result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end

Array.pack и String.unpack для работы с двоичными файлами:

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 

метод отсутствующей магии

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]"

если вы вызываете методы, которых нет в ruby objects, интерпретатор ruby вызовет метод с именем 'method_missing', если он определен, вы могли бы использовать это для некоторых трюков, таких как написание оболочек api или dsl, где вы не знаете имен всех методов и параметров

Хороший вопрос!

Как я думаю, чем интуитивно понятнее и быстрее код, тем лучшее программное обеспечение мы создаем.Я покажу вам, как я выражаю свои мысли, используя Ruby, в небольших фрагментах кода. Подробнее читайте здесь

Карта

Мы можем использовать метод map по-разному:

user_ids = users.map { |user| user.id }

Или:

user_ids = users.map(&:id)

Образец

Мы можем использовать метод rand:

[1, 2, 3][rand(3)]

Перемешать:

[1, 2, 3].shuffle.first

И идиоматический, простой и наилегчайший способ...образец!

[1, 2, 3].sample

Двойная труба Равна / Запоминание

Как вы сказали в описании, мы можем использовать запоминание:

some_variable ||= 10
puts some_variable # => 10

some_variable ||= 99
puts some_variable # => 10

Статический метод / Метод класса

Мне нравится использовать методы класса, я чувствую, что это действительно идиоматический способ создания и использования классов:

GetSearchResult.call(params)

Просто.Красивые.Интуитивно понятный.Что происходит в фоновом режиме?

class GetSearchResult
  def self.call(params)
    new(params).call
  end

  def initialize(params)
    @params = params
  end

  def call
    # ... your code here ...
  end
end

Для получения дополнительной информации о написании идиоматического кода Ruby читайте здесь

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top