📜 ⬆️ ⬇️

Ruby: cheatsheet to explore

This is a roadmap-cheatsheet article for Ruby learners. Instead of telling the next how-to, I will try to paint all those things that, in my opinion, can be summarized briefly and succinctly, with the emphasis that the programmer who came from other languages ​​and platforms may not seem obvious, anti-search and simply expensive. on time to study without prompts - just give the rest for the rest. Despite the fact that in nature there are many tutorials / castes / books / anything on anything, I myself always lack this format. And in fact, these are the things that I most often talk about with questions like “how is it at all?”, “Where to start?”, “How is such a thing done?”, “And which gem is better?”. By the way, while I was working on this article on Habré, a similar one about Python appeared - apparently, the idea is in the air.

About Ruby - Rumors, Intrigues, Investigations
I’ll just say that the text under this spoiler is different from the main “objective” part of the article, because here is my personal opinion and impression.

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

Many people choose Ruby, because it is comfortable and enjoyable. The pleasure and joy of programming can be obtained in different languages ​​- and in Ruby it looks like riding a good car, sitting behind the wheel of which you will think not so much about the road, as about more important things - at least about the route and the final goal of the trip.
You can also compare with the game in Lego (and this is largely due to Gems). Although someone loves welding fittings, and someone enough cardboard and glue.
')
There are several statements that can sometimes be found about Ruby and Rails - I will try to clarify them.

It is known that Ruby is slow. And it's hard to argue, because Ruby is an interpreted language. And that is typical, most often I hear it from those who write (exclusively) in PHP, which we also interpret, and in terms of speed in synthetic tests is approximately at 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, with the same level of optimization faster (and in both, proper caching is the key to success). If you need speed and compactness in memory - write in assembly language, use Node.js. But the paradox is that rubists often write on it when it is truly 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 all that can be found on github in every way helps to achieve a really high productivity of the programmer - including in the application optimization capabilities. And we should 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 also 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 for PHP, and the amount of work on it. The average level of wages for PHP is the classic “average wage temperature in a hospital”. If we compare the specialists in Yii / Symfony / Zend and Rails (you can also add Django to Python) - the picture will be completely different, namely: both salaries and market size are about the same. Indeed, good programmers are paid well. And what's more, when you start a project from scratch - often the choice of platform is yours and your team, and not the customer / boss.

So, there are a lot of great languages ​​and frameworks for writing web applications, and Ruby with Rails is not a silver bullet that will kill all werewolf rabbits at once. Simply, in my opinion, if you take a set of important criteria, and not try to choose one or two of them, RoR is really gaining a lot of points. Including maturity is no longer a hipster platform, in which there is only potential, but also not an old man (there are a lot of examples of those and others). And I’m not going to be an old man for a long time, because Ruby and Rails still have potential for growth and development - but this is a topic for a separate article.

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

In any case, I am convinced that a programmer should not sit in his own little world of what he is paid for today. In this sense, Ruby provides not only +1 language in the summary, but also, due to its focus and the wide possibilities of metaprogramming, experience that is 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 . For the same link you can find DevKit, which is useful for working with databases and installing Native Extensions.
  • The Rails Installer installs Ruby + DevKit, Git, Rails, Bundler, and SQLite right away on Windows or MacOS. Ruby, however, is 1.9.3, and the installer with 2.0 is still in alpha. (on the advice of 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 the same OS and switch between them. At the start, there is no need for it, but it is useful if there are already several projects on the local machine or on the server - it’s not particularly great to update at once to the new version. For now - just keep in mind that it is.
Details about RVM on Habré.

It is also worth mentioning the development environment. I’m an adherent of JetBrains products, so I recommend RubyMine , but due to the fact that a free Aptana Studio may suit someone’s commercial product. If it is more pleasant to use lightweight text editors, Ruby syntax is supported by many.

Directly in the browser in the interactive mode of Ruby you can try on tryruby.org

All - object
Including numbers, strings, and even nil — they are all inherited from the Object class. Can anything call methods like nil ?, class, methods and respond_to ?:
'Hello world'.class # String nil.class # NilClass String.class # Class String.ancestors # [String, Comparable, Object, Kernel, BasicObject];    nil.nil? # true Object.new.methods #     Object;        -    nil.respond_to?('nil?') # true 
Regarding the latter: it is important for duck typing .

Syntax and Powder
Ruby is heavily seasoned with “syntactic sugar”, due to which it is often not necessary to make porridge out of various brackets, semicolons, and so on. And under it is simple and logical, and preserves the paradigm “everything is an object”.
 a == b #  ,   a.==(b) #   .==() -   

In method calls, in if you can not put brackets, 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 yet - 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:
 a = ['value1', 'value2'] #   s = 'String' s = "Double-quoted #{s}" # "Double-quoted String" - , ,  

And we will finish off the dusting topic with a slightly ugly, but convenient way of recording:
 %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 are many painfully familiar semicolons - it can be useful to write several expressions in one line.
 a = 10; puts a #    10 if nil.respond_to? 'nil?'; puts 'ok'; end #    -    
But it is better to write single-line if
 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, and the main friend and helper ruby-doc.org .
It is better to immediately see 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)
Rather simple examples are parsed in Pokor Ruby together! Drop the first Drop the second Drop the 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 power and beauty 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 as an impurity, and as a namespace. Suddenly? In fact, it is a set of classes, methods and constants, and you can use it at your 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 be careful here: here the very opportunities begin, with the help of which you can “shoot yourself in the foot” - so, doing something like this you should always give an account: why, what will happen, who is to blame and what to do and if anything - are 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 
And what if to make instance_eval 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 with it enough, just remember - 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 this approach is used in practice - in fact, it turns out something like an impurity, in which you can transfer parameters. It seems like magic if you don’t know how to do it. Patching is possible and base classes are especially loved by Array and String - but you should always think three times before torturing them: are methods like .blank one thing? (it is added by Rails: something like def blank ?; nil? || empty? end), another - when the method code is specific to the project, then it is logical to assume that it belongs to some classes within the project.

This principle works, for example, accessor. What will we do to add a public parameter to the Ruby class?
 class Foo def bar #  @bar #   end def bar=(val) #  @bar = val #    end end 

Imagine writing for a dozen or two parameters? In Ruby, a lot of code on the thumb - a mortal sin: DRY . So, you can make it shorter.
 class Foo attr_accessor :bar, :baz #     attr_reader :boo #   attr_writer :booo #   end 


Ready to go further? Then:
Understanding Ruby Metaclasses
Metaprogramming patterns - about monkey patching , Reuse in small - bang! , eval
We delve into include and extend

Arguments of methods
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 mandatory 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 confusion - why so much? In short - in fact, there is only Proc.
Details - links below, and now - what you should pay attention.
First consider the blocks. A block is just a piece of 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 a class Proc.
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 “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 pass a 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 is worth noting that the block is always the last parameter, and if you need to transfer several blocks, you need to transfer them as usual parameters, which means to create Proc.new, or lambda.

An object of the Proc class is always obtained from the block, but the block itself is a 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 we cannot just write the block to 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 in different ways, we read the article .
Here the closures and blocks in Ruby are parted up and down
On Habré about blocks , about closures

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


RubyGems - batch manager
The Ruby world would probably be completely different if it were 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 on pure Ruby (without C)
  • we enter in the console gem install json_pure
  • and in our rb-file:
     require 'json/pure' #         ,   github 


For the convenience of dependency management there is a bundler :
  • gem install bundler
  • bundler init
  • in the appeared Gemfile we add 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 a project to a server, it is much more convenient to perform bundle install than manually watching what you need and doing gem install for each gem. Well, in Rails bundler is used out of the box.

The Gems themselves will definitely be covered in the Rails article.

RubyGems - detail
Manage Versions with Bundler
Meet the Gem. Part One Part Two
We write your first gem
Making a gem for RubyGems
Creating Gems - Guide

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

And it just needs to be borne in mind: Inline C , it can be useful. In short, in Ruby code, you can execute code in C, which is useful, for example, when implementing numerical algorithms.

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

Streams
A simple example with a stream:
 thread = Thread.new do #    a = 0 1000000.times { a+= 1 } #      puts a #   end puts 'thread started' #       thread.join #     

The code will display “thread started 100000” in the console.

Lower-level fibers offer a slightly different syntax that allows you to control the sequence of execution, and also have lower memory and initialization costs 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 negates the parallelization of calculations to nothing, but the use of threads in it makes sense for the implementation of asynchrony. The simplest example: we can listen to the user interface while performing some operations in parallel. 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 in the total execution time there will be no winnings, since in fact, only one core will be loaded at a time.

To circumvent this limitation, you can use versions without GIL - jRuby or Rubinius (and at the same time remember that thread safety is much lower in them - GIL is needed for it). Another option is to run several separate copies of the program, or use UNIX forks (of course, a pool of such workers is quite possible to manage the Ruby script).

Having learned all this, a novice rubist can be confused (wanting to use the full power of his 48-line server without an extra headache). In practice, real multithreading is not always needed (and sometimes, if needed, 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 on 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 print 1,000,000 in the MRI, and the devil knows what (from 100,000 to 1,000,000) in jRuby and Rubinius.

We use flows in Ruby

Zoo versions
MRI (Matz's Ruby Interpreter) is the main Ruby thread 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 combined with the project YARV (Yet another Ruby VM). One of its main features is the so-called GIL (Global Interpreter Lock), on Habré they wrote about it ( with continuation ). Now the current version of Ruby 2.0 UPD: Ruby 2.1 has been released . What can we expect from Ruby 2.1?

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

Rubinius , which is based on MRI, but uses operating system threads, as well as being written to the maximum on Ruby itself. According to the version usually up do date.

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

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

IronRuby - implementation on the .NET platform. Throughout his life, then abandoned, then resumes development.

Opal is a Ruby to JavaScript translator. But you don’t have to expect anything extraordinary from it, it’s not jRuby for Node.js - it just gives you the opportunity 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 has completed its existence, because new versions and no drugs run well.

MagLev can be mentioned - a specific version that can be useful for deploying cloud infrastructure.

Also interesting is the mruby project, in which Matz participates. This is embedded Ruby. The project is not finished yet, but looks very promising. So, we wait for ruby ​​on Arduino. mruby-arduino . MobiRuby for mobile application development is based on it.

Well, for a snack, KidsRuby , the meaning resembles the old man Logo.


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

Source: https://habr.com/ru/post/206416/


All Articles