Typowe Idiomy Ruby

Jedną z rzeczy, które kocham w Rubim jest to, że w większości jest to bardzo czytelny język (który świetnie nadaje się do samodzielnego dokumentowania kodu)

Jednak zainspirowany tym pytaniem: Ruby Code explained a opis jak ||= działa w ruby, myślałem o idiomach ruby, których nie używam, ponieważ szczerze mówiąc, nie do końca je grokuję.

Więc moje pytanie jest podobne do przykładu z przywołanego pytania, co powszechne, ale nie oczywiste, idiomy ruby muszę być świadomy, aby być naprawdę biegły programista ruby?

Nawiasem mówiąc, z przywołanego pytania

a ||= b 

Jest równoważne

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

(podziękowania dla Iana Terrella za korektę)

Edit: okazuje się, że ten punkt nie jest całkowicie niekontrowersyjny. Poprawne rozszerzenie jest w rzeczywistości

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

Zobacz te linki dla dlaczego:

Podziękowania dla Jörga W Mittagu za zwrócenie na to uwagi.

 62
Author: Community, 2009-03-05

15 answers

Magiczna klauzula if, która pozwala temu samemu plikowi służyć jako Biblioteka lub skrypt:

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

Pakowanie i rozpakowywanie tablic:

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

Składniowy cukier dla hashów jako argumenty metody

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

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

Składnia Metaclass

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

Zmienne instancji klasy

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"
Klocki, PROCKI i lambdy. Żyj i oddychaj nimi.
 # 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' ]
 50
Author: rampion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-22 16:03:32

Ten pokaz slajdów jest całkiem kompletny na temat głównych idiomów Ruby, jak w:

  • Zamień dwie wartości:

    x, y = y, x

  • Parametry, które, jeśli nie są określone, przyjmują jakąś wartość domyślną

    def somemethod(x, y=nil)

  • Szereguje zewnętrzne parametry do tablicy

    def substitute(re, str, *rest)

I tak dalej...

 10
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-25 16:05:53

Jeszcze kilka idiomów:

Użycie %w, %r i %( ograniczniki

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

Porównanie typu w przypadku wypowiedzi

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

... i ogólne nadużywanie metody === W przypadku wypowiedzi

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

Coś, co powinno wyglądać naturalnie dla Rubyistów, ale może nie tak dla osób pochodzących z innych języków: użycie each na rzecz for .. in

some_iterable_object.each{|item| ... }

W Ruby 1.9+, Rails, lub poprzez łatanie metody symbolu#To_proc, to staje się coraz bardziej popularny idiom:

strings.map(&:upcase)

Metoda warunkowa / definicja stałej

SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)

Metody zapytań i metody destrukcyjne (bang)

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

def make_awesome!
  # Modify the state of the object
end

Implicit splat parameters

[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
 8
Author: Chubas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-04 21:47:35

Podoba mi się:

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

str[regexp, 1] # <- This

Co jest (mniej więcej) równoważne:

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

A przynajmniej tego użyłem do zastąpienia takich klocków.

 7
Author: Daemin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-05 09:08:46

Sugerowałbym przeczytanie kodu popularnych i dobrze zaprojektowanych wtyczek lub perełek od ludzi, których podziwiasz i szanujesz.

Kilka przykładów, na które wpadłem:

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

Odpowiadające

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

, które później zmieniłyby się na

if ALLOWED_CONTROLLERS.include? params[:controller]
  # do something here
end
 7
Author: ucron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-05 09:27:51

Oto kilka, pobranych z różnych źródeł:

Użyj "until " I" until "zamiast" if not " I "while not". Staraj się jednak nie używać "chyba", gdy istnieje warunek "else".

Pamiętaj, że możesz przypisać wiele zmiennych jednocześnie:

a,b,c = 1,2,3

I nawet zamiana zmiennej bez temp:

a,b = b,a

W razie potrzeby stosuj warunki końcowe, np.

do_something_interesting unless want_to_be_bored?

Bądź świadomy powszechnie używanego, ale nie od razu oczywistego (przynajmniej dla mnie) sposobu definiowania klasy metody:

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

Niektóre odniesienia:

 5
Author: Mike Woodhouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-05 09:47:55

Przy okazji, z odwołanego pytanie

a ||= b 

Jest równoważne

if a == nil   
  a = b 
end

Jest to subtelnie niepoprawne i jest źródłem błędów w aplikacjach Ruby dla nowych użytkowników.

Ponieważ zarówno (i tylko) nil i false oceniają na wartość logiczną false, a ||= b jest w rzeczywistości (prawie*) równoważne:

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

Lub, aby przepisać go innym idiomem Ruby:

a = b unless a

(*ponieważ każde polecenie ma wartość, nie są one technicznie równoważne a ||= b. Ale jeśli nie polegasz na wartości oświadczenia, nie zobaczysz różnicy.)

 5
Author: Ian Terrell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-05 20:34:40

Prowadzę stronę wiki, która zawiera niektóre idiomy Ruby i formatowanie:

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

 4
Author: tokland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-08 20:01:40

Możesz łatwo wykonać głęboką kopię za pomocą obiektu Marshaling. - zaczerpnięte z języka programowania Ruby

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

Zauważ, że pliki i strumienie We/Wy, jako oraz metody i Obiekty wiążące, są zbyt dynamiczne, aby można było je rozbudowywać; tam to nie będzie niezawodny sposób na przywrócenie ich stan.

 1
Author: Özgür,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-04 21:13:23
a = (b && b.attribute) || "default"

Jest mniej więcej:

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

Używam tego, gdy B jest rekordem, który został znaleziony lub nie, i muszę zdobyć jeden z jego atrybutów.

 1
Author: jakeonrails,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-09 20:13:42

Zawsze zapominam dokładną składnię tego skrótu if else (I nazwę operatora. komentuje ktoś?) Myślę, że jest on powszechnie używany poza Rubim, ale na wypadek, gdyby ktoś inny chciał mieć tutaj składnię:

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

Rozszerza się do

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

update

Nazywany operatorem trójdzielnym:

/ Align = "left" / myvar.rozmiar: 0
 1
Author: Danny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-22 16:45:20

Lubię jak If-then-elses lub case-when mogą być skrócone, ponieważ zwracają wartość:

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

Można przepisać

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

To samo można zastosować do case-when:

result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
 1
Author: marcel massana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 00:51:51

Array.opakowanie i sznurek.rozpakuj do pracy z plikami binarnymi:

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 
 0
Author: SztupY,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-28 09:49:34

Method missing magick

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

Jeśli wywołasz metody, które nie istnieją w obiektach ruby, interpreter ruby wywoła metodę o nazwie 'method_missing', jeśli jest zdefiniowana, możesz użyć jej do pewnych sztuczek, takich jak pisanie owijarek api lub dsl, gdzie nie znasz wszystkich metod i nazw parametrów

 0
Author: edikgat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-03 18:22:09

Ładne pytanie!

Ponieważ myślę, że im bardziej intuicyjny i szybszy jest kod, budujemy lepsze oprogramowanie. Pokażę Ci, jak wyrażam swoje myśli używając Rubiego w małych fragmentach kodu. Czytaj więcej tutaj

Mapa

Możemy używać metody map na różne sposoby:

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

Lub:

user_ids = users.map(&:id)

Próbka

Możemy użyć metody rand:

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

Shuffle:

[1, 2, 3].shuffle.first

I idiomatyczne, proste i najłatwiejsze sposób... próbka!

[1, 2, 3].sample

Podwójna Rura Równa Się / Memoizacja

Jak powiedziałeś w opisie, możemy użyć memoizacji:

some_variable ||= 10
puts some_variable # => 10

some_variable ||= 99
puts some_variable # => 10

Metoda Statyczna / Metoda Klasy

Lubię używać metod klasowych, uważam, że jest to naprawdę idiomatyczny sposób tworzenia i używania klas:

GetSearchResult.call(params)

Proste. Pięknie. Intuicyjne. Co dzieje się w tle?

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

  def initialize(params)
    @params = params
  end

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

Aby uzyskać więcej informacji do napisania idiomatycznego kodu Ruby, przeczytaj Proszę.

 0
Author: leandrotk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-22 00:30:08