Ruby: cheatsheet to learn

  • Tutorial
This is an article-roadmap-cheatsheet for Ruby learners. Instead of telling the next how-to, I’ll try to paint all the things that, in my opinion, can be stated briefly and concisely, with the emphasis on the fact that a programmer who comes from other languages ​​and platforms may not seem obvious, anti-search and simply costly on time to study without clues - the rest will just give links. Despite the fact that in nature there are many tutorials / castes / books / anything for anything - this is the format I myself always miss. And in fact, these are the things that I most often tell when asked “how is it at all?”, “Where to start?”, “How is such a thing done?”, “And which gem is better?”. By the way, while working on this article on Habré, a similar one about Python appeared - apparently, the idea is in the air.

Around Ruby - rumors, intrigues, investigations
Immediately make a reservation that the text under this spoiler differs from the main "objective" part of the article, because Here is my personal opinion and impression.

A programming language is not only syntax, a garbage collector, not only a language paradigm, and even not so much its philosophy, in the first place, it is the community and the code base that this community created. Especially now, in the era of OpenSource. And here at Ruby the first fat plus in karma. One of the features - pragmatic laziness is read throughout, starting with the optional brackets when calling methods and semicolons at the end of the line, continuing with a rich and expressive syntax, and ending with the general feel of the projects - many of them are made out of the box and require minimal effort to configure them.

Many choose Ruby because it is comfortable and pleasant. The pleasure and joy of programming can be obtained in different languages ​​- and in Ruby it looks like driving a good car, sitting behind the wheel of which you will think not so much about the road, but about more important things - at least about the route and the final purpose of the trip.
You can also compare with the game in Lego (and this is largely due to Gems). Although someone likes welding fittings, but someone needs cardboard and glue.

There are a few things you can sometimes find about Ruby and Rails - I’ll try to clarify them.

It is known that Ruby is slow. And it's hard to argue, because Ruby is an interpreted language. And which is typical, most often I hear this from those who write (exclusively) in PHP, which is also interpretable, and in terms of speed in synthetic tests is at about the same level. Most likely, these are echoes of the notoriety of old versions. In fact, I had to work with both Rails and Symfony - on real Rails applications it’s faster with the same level of optimization (and both caching is the key to success). If you need speed and compactness in memory - write in assembleruse Node.js. But the paradox is that rubists often write on it - when it is really justified. And the thing is this: not only the speed of work is important, but also the speed of writing the application. And Ruby, and everything that can be found on github in every possible way helps to achieve really high productivity of the programmer - including the possibilities of optimizing the application. And do not forget that the main load and bottlenecks are often databases, and web applications are just a layer of business logic. And such a layer scales well.

There is still a peculiar myth about the high salaries of Ruby developers on the one hand, and on the other, that there is little work on Ruby. Usually compared with salaries with the average market in PHP, and the amount of work on it. Average wages in the PHP - is a classic "average WACH the hospital temperature '. If we compare the specialists in Yii / Symfony / Zend and Rails (you can also add Django in Python), the picture will be completely different, namely: both salaries and market size are approximately the same. Indeed, good programmers are paid well. And what's more, when you take on a project from scratch - often the choice of platform is up to you and your team, rather than the customer / boss.

So, there are many beautiful languages and frameworks for writing Web applications, and Ruby with Rails is not the silver bullet that will kill all the birds with rabbits werewolves immediately. Simply, in my opinion, if you take a set of important criteria, and not try to choose one or two of them, RoR really gets a lot of points. Including in maturity - this is no longer a hipster platform in which there is only potential, but also not an old man (but there are a lot of examples of both). And the old man, I think, will not become a long time, because both Ruby and Rails still have the potential for growth and development - but this is a topic for another article.

And of course, despite the focus of the article on Rails, and the popularity of this particular platform - Ruby is far from Rails.

In any case, I am convinced that a programmer should not sit in his little world of what he is paid today for. In this sense, Ruby provides not only a +1 language in the resume, but also, due to its oop-focus and wide metaprogramming capabilities, an experience that will be useful in different languages ​​and platforms.

Ruby language: history of development and development prospects


Ruby



Start
How to install Ruby on # {os_name}?

The links themselves:
  • Win . Follow the same link to find DevKit, which is useful for working with the database and installing Native Extensions
  • The Rails Installer will install Ruby + DevKit, Git, Rails, Bundler and SQLite immediately on Windows or MacOS. Ruby, however, is 1.9.3, and the installer with 2.0 is still in alpha. (as advised by Jabher )
  • * nix - search in your repositories, or legacy

In addition, there is such a thing as RVM . It allows you to install multiple versions of Ruby on one OS and switch between them. At the start, there is no need for it, but it can be useful if there are already several projects on the local machine or on the server - updating all at once to the new version is not very cool at once. For now - just keep in mind that it is.
In detail about RVM on a geek magazine

Immediately worth mentioning is the development environment. I am a fan of JetBrains products, so I recommend RubyMine , but since a commercial product may be to some taste, free Aptana Studio . If it is more pleasant to use light text editors, Ruby syntax is supported by many.

Directly in a browser in Ruby interactive mode, you can try at tryruby.org

Everything is an Object
Including numbers, strings, and even nil, they all inherit from the Object class. You can call methods like nil ?, class, methods and respond_to?
'Hello world'.class # String
nil.class # NilClass
String.class # Class
String.anc­estors # [String, Comparable, Object, Kernel, BasicObject]; массив предков класса
nil.nil? # true
Object.new.methods # вернет методы объекта класса Object; тут же видно как создается новый объект - тоже вызовам метода
nil.respond_to?('nil?') # true
Regarding the latter: this is important for duck typing .

Syntax and Powder
Ruby is heavily seasoned with “syntactic sugar,” thanks to which often you don’t need to make porridge out of various brackets, semicolons, and more. And underneath it is simple and logical, and retains the paradigm of “everything is an object”.
a == b # то же, что и
a.==(b) # то есть .==() - это метод

In method calls, in if you can omit parentheses if there is no ambiguity
nil.respond_to?('nil?') # true
nil.respond_to? 'nil?' # true
# все однозначно:
if nil.respond_to? 'nil?'
  puts 'ok'
end
# тоже
if 10.between? 1, 5
  puts 'ok'
end
# а вот так получится не тот приоритет вызовов, поэтому нужны скобки
if 10.between?(1, 50) && 20.between?(1, 50)
  puts 'ok'
end

And also there are symbols . In essence, characters are immutable strings. For example, they are often used as keys in hashes.
a = :nil? # символ
b = 'nil?' # строка
nil.respond_to? a # true
nil.respond_to? b # true
# поиграемся
a == b # false
a.to_s == b && a == b.to_sym # true; символ и строка преобразуются друг в друга

A little more powder is connected with them:
a = {:key1 => 'value1', :key2 => 'value2'} # создаем хэш (ассоциативный массив)
a = {key1: 'value1', key2: 'value2'} # если ключи - символы, то можно так (Ruby >= 1.9.3)
Since the topic has come up:
a = ['value1', 'value2'] # это массив
s = 'String'
s = "Double-quoted #{s}" # "Double-quoted String" - это, думаю, ясно

And finish off the dusting theme with a little ugly but convenient way to write:
%w[value1 value2] # ["value1", "value2"] - тот же массив, что и выше
%i[value1 value2] # [:value1, :value2] - массив символов, (Ruby >= 2.0)
s = %q(String)
s = %Q(Double-quoted #{s})
%x('ls') # команда консоли
`ls` # то же самое
%r(.*) == /.*/ # true; два способа создать регулярное выражение

By the way, in Ruby there is a painfully familiar semicolon to many - it can come in handy to write several expressions on one line
a = 10; puts a # выведет в консоль 10
if nil.respond_to? 'nil?'; puts 'ok'; end # чтобы было понятно - так тоже можно
But single-line ifs are better to write like this
puts 'ok' if nil.respond_to? 'nil?'


Introductory articles and docks
It is worth reading In Ruby from other languages and Ruby in twenty minutes , well, the main friend and assistant ruby-doc.org .
It’s better to immediately look at the methods of the base classes (in all - which are each_ and to_).
String (here - first thing match, sub)
Array (map, join, include?)
Hash (has_key?, Has_value ?, merge)
Quite simple examples are taken into We conquer Ruby together! Drop first Drop second Drop third
If you want to continue the banquet: An interesting public with a selection of magic on Ruby .

Classes, Modules, and Metaprogramming
Classes in Ruby are quite obvious, but in the possibilities of working with them lies all the strength and charm of Ruby.
class Foo
  def bar
    10 # любой метод возвращает значение - результат выполнения последнего выражения
  end
  def baz(a)
    bar + 20
  end
end
puts Foo.new.baz(10) # 30

Module - it can be treated both as an impurity and as a namespace. Suddenly? In fact, this is a set of classes, methods, and constants, and you can use it at your own discretion.
module B # как пространство имен
  class Foo
    # конструктор
    def initialize(k) @k = k end # запишем методы в одну строку
    def bar; @k + 20 end
  end
end
puts B.Foo.new(3).bar # 23
puts B::Foo.new(3).bar # 23; то же самое, но читается лучше
module C # как примесь
  def bar; @k + 25 end
end
class Foo
  include C;
  def initialize(k) @k = k end
end
puts Foo.new(3).bar # 28


In Ruby, classes are mutable and can be patched after they are created. And here be careful: here the very possibilities begin with which you can “shoot yourself in the foot” - so when doing something like this you should always be aware: why, what happens, who is to blame and what to do and if what is to blame yourself.
class Foo
  def bar; 20 end
end
class Foo # патчим
  def baz; bar + 2 end
end
puts Foo.new.baz # 22
# или лучше - так: сразу понятно что это патч
Foo.class_eval do
  def bazz; bar + 3 end
end
puts Foo.new.bazz # 23
# но если нет на то веской причины - используйте примеси или наследование
class Boo < Foo
  def boo; bar + 2 end
end
puts Boo.new.boo # 22
# или же - можно пропатчить только объект, а не сам класс
a = Foo.new
a.instance_eval do # патчим объект
  def booo; bar + 3 end
end
puts a.booo # 23
puts Foo.new.booo rescue puts 'error' # error; кстати, так перехватываются ошибки
puts a.respond_to? :booo # true
puts Foo.new.respond_to? :booo # false
# или - патчим объект в более легком синтаксисе
def a.booboo
  bar + 4
end
puts a.booboo # 24
What if instance_eval is made for a class? Of course, static methods will be added.
class Foo
  def self.bar; 10 end # обычный статический метод
end
puts Foo.bar # 10
Foo.instance_eval do
  def baz; bar + 1 end
end
puts Foo.baz # 11
You can play around with this, remember the main thing - take care of your feet.
class Foo
  def self.add_bar # добавим статический метод, который патчит класс
	self.class_eval do
	  def bar; 'bar' end
	end
  end
end
puts Foo.new.respond_to? :bar # false
class Boo < Foo # отнаследуемся
  add_bar # пропатчим
end
puts Boo.new.bar # bar
# а теперь все то же самое, но пропатчим уже существующий класс
Foo.instance_eval do
  def add_baz
    self.class_eval do
      def baz; 'baz' end
    end	
  end
end
class Baz < Foo
  add_baz
end
puts Baz.new.baz # baz

Just such an approach is used in practice - in fact, it turns out something like an impurity into which parameters can be transferred. It seems magic if you do not know how to do it. You can patch base classes as well, especially Array and String - but you should always think three times before starting to torment them: are things like .blank one thing? (Rails adds it: something like def blank ?; nil? || empty? end), the other is when the method code is specific to the project, then it is logical to assume that it belongs to some classes inside the project.

This is how accessor works, for example. What will we do to add a public parameter to a Ruby class?
class Foo
  def bar # геттер
    @bar # возвращаем параметр
  end
  def bar=(val) # сеттер
    @bar = val # присваиваем значение параметру
  end
end

Can you imagine writing for a dozen or two parameters? In Ruby, a lot of code on the thumb is a deadly sin: DRY . So, you can make it shorter.
class Foo
  attr_accessor :bar, :baz # добавим сразу парочку атрибутов
  attr_reader :boo # только геттер
  attr_writer :booo # только сеттер
end


Ready next? Then:
We delve into the metaclasses of Ruby
Metaprogramming patterns - about monkey patching , Reuse in the small - bang! , eval
We delve into include and extend

Method arguments
Ruby 2.0 supports named method arguments:
# bar обязателен
# barr имеет значение по умолчанию 0
# baz - именованный аргумент со значением по умолчанию true
def foo(bar, barr = 0, baz: true)
  baz && bar + barr
end
p foo 1 # 1
p foo 1, 2 # 3
p foo 1, baz: false # false

In previous versions, you can achieve the same behavior by parsing attributes:
def foo2(bar, *args)
  args
end
p foo2 1, 2, :baz => false # [2, {:baz=>false}]
def foo3(bar, *args)
  options = args.extract_otions! # именованные аргументы вырезаются из args
  p bar
  p args
  p options
end
foo3 1, 2, 3, :baz => false
# 1
# [2, 3]
# {:baz=>false}

Starting with Ruby 2.1, you can now add required named arguments.
def foo4(bar, baz:); bar end
foo4 1 # ошибка
foo4 1, baz: 2 # 1


Closures and Blocks
At first, blocks, closures, and the Proc class cause some bewilderment - why so much? In short - in fact, there is only Proc.
Details - links below, and now - what you should pay attention to.
The first to consider blocks. A block is just a piece to where, and not even an object, just part of the Ruby syntax. A block is used to pass some code to a method. And already in the method, it turns out to be wrapped in the Proc class.
In the section on classes and methods, they have already been used:
Foo.instance_eval do # передаем в метод instance_eval блок с определением метода
  def baz; bar + 1 end
end

But let’s take a more basic example, it’s also “how to make foreach in Ruby”:
[1,2,3].each do |val|
  p val # кстати, p(val) - это shortcut для puts(val.inspect)
end # выводит 1 2 3
# то же, в одну строчку 
[1,2,3].each { |val| p val } # выводит 1 2 3
[1,2,3].each_with_index { |val, i| puts val.to_s + ' ' + i.to_s } # на заметку

If we want to transfer the block to our own method:
def foo
  puts yield # выполняем блок
  puts yield + yield # и еще, и еще выполняем
end
foo { 2 } # 2 4
def bar(&block) # или, если поменьше пудры
  puts yield block # выполняем блок
end
bar { 3 } # 3

It should be noted that a block is always the last parameter, and if you need to pass several blocks, you need to pass them as normal parameters, which means creating Proc.new, or lambda.

An object of the Proc class is always obtained from the block, but the block itself is part of the syntax: we can pass it to the method where it will become Proc, we can pass it to the Proc constructor, or use lambda, but you cannot just write the block into a variable.
proc = Proc.new { |a| a - 1 } # через конструктор
p proc.call(10) #9
p proc.class # Proc
l = lambda { |a| a + 1 } # создаем лямбду
p l.call(10) #11
p l.class # Proc
new_l = ->(a) { a + 2 } # тоже лямбда (Ruby >= 2.0)
p new_l.call(10) #12


There are differences in the behavior of Proc created in different ways, we read the article .
Here closures and blocks in Ruby are sorted along and reproach
On Habré about blocks , about closures

Style
Regarding how it is customary to write in Ruby, there is a whole guide on GitHub
In short, frequently used:
  • Indentation in 2 spaces
  • Lowercase method names with underscore: def method_name
  • Capitalized class and module names: class ClassName
  • If the method returns true / false, the name should end with a question (same, nil?)
  • If there are two methods, one of which modifies the object, and the other returns a new one, the first ends with '!' (for example, the downcase and downcase! methods for a line - the first will return a new one, and the second will change the line itself)
  • Instead of if (! Value) it is better to use alias unless (value)
  • A single-line block is taken in brackets {...}, and a multi-line block is in do ... end


RubyGems - package manager
The world of Ruby would probably be completely different if not for RubyGems.
With them, the process of supplementing the project with libraries looks very simple:
  • choose what we need with rubygems.org (or through ruby-toolbox.com ), for example, json_pure - JSON parser in pure Ruby (without C)
  • enter gem install json_pure console
  • and in our rb file:
    require 'json/pure' # какой именно путь должен быть стоит в доках посмотреть, обычно на github


For convenience, dependency management has a bundler :
  • gem install bundler
  • bundler init
  • in the appeared Gemfile we enter dependencies:
    source 'https://rubygems.org'
    gem 'json_pure'
    
  • bundler install
  • And in your rb file
    require 'rubygems'
    require 'bundler/setup'
    

In real projects, the list of dependencies can grow into several screens, so when transferring the project to the server it is much more convenient to run bundle install than to manually see what you need and for each gem run gem install. Well, in Rails bundler is used out of the box.

Gems themselves will definitely be covered in an article on Rails.

RubyGems - Details
Managing Versions with the Bundler
Introducing Gem. Part One Part Two
Writing Your First Gem
Making a Gem for RubyGems
Creating Gems - A Guide

Ruby and C
Not the least important feature of RubyGems is Native Extensions. Everything is simple - along with the gem, the attached C code can be compiled and called from the code of the gem itself. Due to this, among them there are quite nimble parsers, you can install JavaScript V8 in the form of a gem, and many other goodies.

And you just need to keep this in mind: Inline C may come in handy. In short, you can execute C code in Ruby code, which is useful, for example, when implementing numerical algorithms.

An Introduction to Ruby Extensions in C
Ruby and C. Part 1 , Part 2 , Part 3 .

Streams
A simple stream example:
thread = Thread.new do # создается отдельный поток
  a = 0
  1000000.times { a+= 1 } # загружаем процессор на некоторое время
  puts a # вывод результата
end
puts 'thread started' # текст выводится сразу после запуска потока
thread.join # ожидание завершения работы потока

The code will output to the console "thread started 100000".

Lower-level fibers offer a slightly different syntax that allows you to control the sequence of execution, and also have less memory and initialization overhead than threads.

fiber = Fiber.new do # создание нового волокна
  Fiber.yield "fiber 1" # управление возвращается в контекст
  'fiber 2' # возвращаемое значение
end
puts fiber.resume # запуск волокна
puts 'context'
puts fiber.resume # возобновление работы волокна

The code will output “fiber 1 context fiber 2”.

About GIL and true multithreading
Ruby supports multithreading, and different versions of the interpreter differ in its implementation. GIL in the main branch reduces parallelization of computations to nothing, but using threads in it makes sense for implementing asynchrony. The simplest example: we can listen to the user interface, while simultaneously performing some operations. Or we can send a request to the database and wait for a response in one thread, send an email in another, and without waiting for completion to send a response to the user. But there will be no win in the total execution time, because in fact, only one core will be loaded at any given time.

To get around this restriction, you can use versions without GIL - jRuby or Rubinius (and remember that the thread safety in them is much lower - you need GIL for it). Another option is to run several separate copies of the program, or use UNIX forks (of course, a Ruby-script can be managed by a pool of such workers).

Having learned all this, a beginner hacker may be confused (wanting to use the full power of his 48-threaded server without too much headache). In practice, real multithreading is not always needed (and sometimes - if necessary, sometime later). In addition, for many tasks, there are corresponding gems that implement different approaches, including ready-made HTTP servers (which will be discussed in the article about gems) and asynchronous frameworks.


GIL is needed for this:
a = 0
threads = []
10.times do # создаем 10 потоков
  threads << Thread.new do # добавляем объекты потоков в массив
    100000.times { a+= 1 } # 100 000
  end
end
threads.each(&:join) # ждем завершения всех потоков
puts a # вывод результата

The code will output 1,000,000 in the MRI, and hell knows that (from 100,000 to 1,000,000) in jRuby and Rubinius.

Using threads in Ruby

Zoo versions
MRI (Matz's Ruby Interpreter) is the main Ruby branch from its creator Yukihiro Matsumoto, or simply, Matz. Implemented in C. When they simply say “Ruby” they usually mean it. Starting with version 1.9 merged with the project YARV (Yet another Ruby VM). One of its main features is the so-called GIL (Global Interpreter Lock), they wrote about it on Habré ( with continuation ). The current version of Ruby 2.0 UPD is now: Ruby 2.1 has been released . What can we expect from Ruby 2.1?

jRuby , which is written in Java and runs in the JVM, and integrates accordingly with Java code. Unfortunately, the language version is lagging behind the MRI (Ruby 1.9.3 is now implemented), but the same Rails starts up with it half a turn.

Rubinius, which is based on MRI, but uses operating system threads, and is written to the maximum extent possible with Ruby itself. According to the version, usually up do date.

The speed of all three implementations relative to each other varies from task to task, and the most gluttonous in terms of memory consumption from a trinity is jRuby.

MacRuby - Works in conjunction with Objective-C on LLVM, replacing the earlier RubyCocoa project. For iOS development, there is a fork of MacRuby - RubyMotion .

IronRuby is a .NET implementation. Throughout its life, it is sometimes abandoned, then development resumes.

Opal- Ruby translator in JavaScript. But do not expect anything outstanding from him, this is not jRuby for Node.js - it just makes it possible to write your jQuery code in Ruby. Or Express code under Node.js. In general, a variation on the theme of CoffeeScript.

Ruby Enterprise Edition (REE) - Ruby on steroids. The project ended its existence, as new versions and without drugs run pretty well.

You can mention MagLev - a specific version, it can come in handy for deploying a cloud infrastructure.

Also interesting is the mruby project , in which Matz participates. This is an embedded Ruby. The project is not finished yet, but it looks very promising. So, we are waiting for ruby ​​at Arduino. mruby-arduino . MobiRuby is based on it.designed to develop mobile applications.

Well, for a snack, KidsRuby , meaning reminiscent of the old logo .


upd:
learnxinyminutes.com/docs/ruby - Ruby code examples.

Also popular now: