

Мы рады сообщить о выпуске Ruby 3.2.0. В Ruby 3.2 добавлено множество новых функций и улучшений производительности.


## Поддержка WebAssembly на базе WASI

Это начальный порт поддержки WebAssembly на базе WASI. Он позволяет использовать бинарный файл CRuby в веб-браузере, в среде Serverless Edge или других типах встраиваемых систем WebAssembly/WASI. В настоящее время этот порт проходит базовые и загрузочные (bootstrap) наборы тестов без использования Thread API.

![](https://i.imgur.com/opCgKy2.png)

### История вопроса

[WebAssembly (Wasm)](https://webassembly.org/) изначально был представлен для безопасного и быстрого запуска программ в веб-браузере. Но его цель — эффективное и безопасное выполнение программ в различных средах — давно востребована не только для веба, но и для обычных приложений.

[WASI (The WebAssembly System Interface)](https://wasi.dev/) разработан для таких случаев использования. Хотя таким приложениям необходимо взаимодействовать с операционными системами, WebAssembly работает на виртуальной машине, у которой не было системного интерфейса. WASI стандартизирует его.

Поддержка WebAssembly/WASI в Ruby призвана задействовать эти проекты. Она позволяет Ruby-разработчикам писать приложения, которые работают на этих перспективных платформах.

### Варианты использования

Эта поддержка побуждает разработчиков использовать CRuby в среде WebAssembly. Примером использования является поддержка CRuby в [песочнице TryRuby](https://try.ruby-lang.org/playground/). Теперь вы можете попробовать настоящий CRuby прямо в своем веб-браузере.

### Технические подробности

В современных WASI и самом WebAssembly отсутствуют некоторые функции для реализации Fiber, исключений и GC, так как они все еще развиваются, а также по соображениям безопасности. Поэтому CRuby восполняет этот пробел, используя Asyncify — технику преобразования бинарных файлов для управления выполнением в пространстве пользователя.

Кроме того, мы создали [VFS поверх WASI](https://github.com/kateinoigakukun/wasi-vfs/wiki/Getting-Started-with-CRuby), чтобы можно было легко упаковывать приложения Ruby в один файл .wasm. Это немного упрощает распространение Ruby-приложений.

### Полезные ссылки

* [Add WASI based WebAssembly support #5407](https://github.com/ruby/ruby/pull/5407)
* [An Update on WebAssembly/WASI Support in Ruby](https://itnext.io/final-report-webassembly-wasi-support-in-ruby-4aface7d90c9)


## Готовый к промышленной эксплуатации YJIT

![](https://i.imgur.com/X9ulfac.png)

* YJIT больше не является экспериментальным
    * Он тестировался на рабочих нагрузках в течение года и доказал свою стабильность.
* YJIT теперь поддерживает процессоры x86-64 и arm64/aarch64 на Linux, MacOS, BSD и других UNIX-платформах.
    * В этот релиз вошла поддержка Apple M1/M2, AWS Graviton, Raspberry Pi 4 и других.
* Для сборки YJIT теперь требуется Rust 1.58.0+. [[Feature #18481]]
    * Чтобы гарантировать, что CRuby собран с YJIT, пожалуйста, установите `rustc` >= 1.58.0 перед запуском скрипта `./configure`.
    * Пожалуйста, свяжитесь с командой YJIT, если у вас возникнут какие-либо проблемы.
* Релиз YJIT 3.2 быстрее, чем 3.1, и имеет примерно на треть меньше накладных расходов на память.
  * В целом YJIT на 41% быстрее (среднее геометрическое), чем интерпретатор Ruby на [yjit-bench](https://github.com/Shopify/yjit-bench).
  * Физическая память для JIT-кода выделяется лениво. В отличие от Ruby 3.1, RSS процесса Ruby минимизирован, так как страницы виртуальной памяти, выделенные через `--yjit-exec-mem-size`, не будут отображаться на физические страницы памяти до тех пор, пока они действительно не будут использованы JIT-кодом.
  * Представлен Code GC, который освобождает все кодовые страницы, когда потребление памяти JIT-кодом достигает значения `--yjit-exec-mem-size`.
  * `RubyVM::YJIT.runtime_stats` возвращает метрики Code GC в дополнение к существующим ключам `inline_code_size` и `outlined_code_size`: `code_gc_count`, `live_page_count`, `freed_page_count` и `freed_code_size`.
* Большинство статистических данных, генерируемых `RubyVM::YJIT.runtime_stats`, теперь доступны в релизных сборках.
    * Просто запустите ruby с параметром `--yjit-stats`, чтобы вычислить и вывести статистику (это влечет за собой небольшие накладные расходы во время выполнения).
* YJIT теперь оптимизирован для использования форм объектов (object shapes). [[Feature #18776]]
* Используется более мелкозернистая инвалидация констант, чтобы инвалидировать меньше кода при определении новых констант. [[Feature #18589]]
* Значение по умолчанию для `--yjit-exec-mem-size` изменено на 64 (МиБ).
* Значение по умолчанию для `--yjit-call-threshold` изменено на 30.

## Улучшения Regexp для защиты от ReDoS

Известно, что сопоставление с регулярным выражением может занимать неожиданно много времени. Если ваш код пытается сопоставить потенциально неэффективное регулярное выражение с ненадежными входными данными, злоумышленник может использовать это для организации отказа в обслуживании (так называемый Regular expression DoS, или ReDoS).

Мы представили два улучшения, которые значительно снижают риск ReDoS.

### Улучшенный алгоритм сопоставления регулярных выражений

Начиная с Ruby 3.2, алгоритм сопоставления Regexp был значительно улучшен благодаря использованию техники мемоизации.

```
# Это сопоставление занимает 10 сек. в Ruby 3.1 и 0.003 сек. в Ruby 3.2

/^a*b?a*$/ =~ "a" * 50000 + "x"
```

![](https://cache.ruby-lang.org/pub/media/ruby320_regex_1.png)
![](https://cache.ruby-lang.org/pub/media/ruby320_regex_2.png)

Улучшенный алгоритм сопоставления позволяет выполнять большинство проверок регулярных выражений (около 90% в наших экспериментах) за линейное время.

Эта оптимизация может потреблять память, пропорциональную длине входных данных для каждого сопоставления. Мы не ожидаем возникновения практических проблем, так как это выделение памяти обычно откладывается, а обычное сопоставление Regexp должно потреблять не более чем в 10 раз больше памяти, чем длина входных данных. Если в реальном приложении у вас закончится память при сопоставлении регулярных выражений, пожалуйста, сообщите об этом.

Оригинальное предложение: <https://bugs.ruby-lang.org/issues/19104>

### Таймаут Regexp

Вышеупомянутая оптимизация не может быть применена к некоторым типам регулярных выражений, например, содержащим расширенные функции (такие как обратные ссылки или просмотры вперед/назад) или с огромным фиксированным количеством повторений. В качестве резервной меры также введена функция таймаута для сопоставлений Regexp.

```ruby
Regexp.timeout = 1.0

/^a*b?a*()\1$/ =~ "a" * 50000 + "x"
#=> Через одну секунду будет вызвано исключение Regexp::TimeoutError
```

Обратите внимание, что `Regexp.timeout` — это глобальная настройка. Если вы хотите использовать разные настройки таймаута для определенных регулярных выражений, вы можете использовать ключевое слово `timeout` в `Regexp.new`.

```ruby
Regexp.timeout = 1.0

# Это регулярное выражение не имеет таймаута
long_time_re = Regexp.new('^a*b?a*()\1$', timeout: Float::INFINITY)

long_time_re =~ "a" * 50000 + "x" # никогда не прерывается
```

Оригинальное предложение: <https://bugs.ruby-lang.org/issues/17837>.

## Другие заметные новые функции

### SyntaxSuggest

* Функция `syntax_suggest` (ранее `dead_end`) интегрирована в Ruby. Она помогает найти местоположение ошибок, таких как отсутствующие или лишние `end`, чтобы вы могли быстрее вернуться к работе, как показано в следующем примере:

  ```
  Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?

    1  class Dog
  > 2    defbark
  > 3    end
    4  end
  ```

  [[Feature #18159]]


### ErrorHighlight

* Теперь он указывает на соответствующие аргументы для TypeError и ArgumentError

```
test.rb:2:in `+': nil can't be coerced into Integer (TypeError)

sum = ary[0] + ary[1]
               ^^^^^^
```

### Язык

* Анонимные аргументы rest и keyword rest теперь можно передавать в качестве аргументов, а не только использовать в параметрах метода.
  [[Feature #18351]]

    ```ruby
    def foo(*)
      bar(*)
    end
    def baz(**)
      quux(**)
    end
    ```

* Proc, принимающий один позиционный аргумент и ключевые слова, больше не будет выполнять autosplat. [[Bug #18633]]

  ```ruby
  proc{|a, **k| a}.call([1, 2])
  # Ruby 3.1 и ранее
  # => 1
  # Ruby 3.2 и позже
  # => [1, 2]
  ```

* Порядок вычисления при присваивании констант для констант, заданных в явных объектах, приведен в соответствие с порядком вычисления при присваивании одного атрибута. В этом коде:

    ```ruby
    foo::BAR = baz
    ```

  `foo` теперь вызывается перед `baz`. Аналогично, для множественных присваиваний константам используется порядок вычисления слева направо. В этом коде:

    ```ruby
    foo1::BAR1, foo2::BAR2 = baz1, baz2
    ```

  Теперь используется следующий порядок вычисления:

  1. `foo1`
  2. `foo2`
  3. `baz1`
  4. `baz2`

  [[Bug #15928]]

* Find-паттерн больше не является экспериментальным.
  [[Feature #18585]]

* Методы, принимающие параметр rest (например, `*args`) и желающие делегировать аргументы-ключевые слова через `foo(*args)`, теперь должны быть помечены как `ruby2_keywords` (если это еще не сделано). Иными словами, все методы, желающие делегировать аргументы-ключевые слова через `*args`, теперь должны быть помечены как `ruby2_keywords` без исключений. Это облегчит переход на другие способы делегирования, когда библиотека сможет требовать Ruby 3+. Ранее флаг `ruby2_keywords` сохранялся, если принимающий метод принимал `*args`, но это было ошибкой и непоследовательностью. Хороший способ найти потенциально отсутствующие `ruby2_keywords` — запустить набор тестов, найти последний метод, который должен получать именованные аргументы для каждого места, где тесты не проходят, и использовать там `puts nil, caller, nil`. Затем проверьте, что каждый метод/блок в цепочке вызовов, который должен делегировать ключевые слова, правильно помечен как `ruby2_keywords`. [[Bug #18625]] [[Bug #16466]]

    ```ruby
    def target(**kw)
    end

    # Случайно работало без ruby2_keywords в Ruby 2.7-3.1, ruby2_keywords
    # требуется в 3.2+. Точно так же (*args, **kwargs) или (...) потребуется
    # и в #foo, и в #bar при переходе от ruby2_keywords.
    ruby2_keywords def bar(*args)
      target(*args)
    end

    ruby2_keywords def foo(*args)
      bar(*args)
    end

    foo(k: 1)
    ```

## Улучшения производительности

### MJIT

* Компилятор MJIT переписан на Ruby как `ruby_vm/mjit/compiler`.
* Компилятор MJIT теперь выполняется в форкнутом процессе, а не в нативном потоке, называемом MJIT-воркером. [[Feature #18968]]
    * В результате Microsoft Visual Studio (MSWIN) больше не поддерживается.
* MinGW больше не поддерживается. [[Feature #18824]]
* Переименован параметр `--mjit-min-calls` в `--mjit-call-threshold`.
* Значение по умолчанию `--mjit-max-cache` возвращено с 10000 к 100.

### PubGrub

* Bundler 2.4 теперь использует резолвер [PubGrub](https://github.com/jhawthorn/pub_grub) вместо [Molinillo](https://github.com/CocoaPods/Molinillo).

  * PubGrub — это алгоритм решения следующего поколения, используемый пакетным менеджером `pub` для языка программирования Dart.
  * После этого изменения результаты разрешения зависимостей могут отличаться. Пожалуйста, сообщайте о таких случаях в [RubyGems/Bundler issues](https://github.com/rubygems/rubygems/issues)

* RubyGems в Ruby 3.2 все еще использует резолвер Molinillo. Мы планируем заменить его на PubGrub в будущем.

## Другие заметные изменения по сравнению с 3.1

* Data
    * Новый базовый класс для представления простых неизменяемых объектов-значений. Этот класс похож на Struct и частично разделяет его реализацию, но имеет более лаконичный и строгий API. [[Feature #16122]]

        ```ruby
        Measure = Data.define(:amount, :unit)
        distance = Measure.new(100, 'km')            #=> #<data Measure amount=100, unit="km">
        weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg">
        weight.with(amount: 40)                      #=> #<data Measure amount=40, unit="kg">
        weight.amount                                #=> 50
        weight.amount = 40                           #=> NoMethodError: undefined method `amount='
        ```

* Hash
    * `Hash#shift` теперь всегда возвращает nil, если хеш пуст, вместо того чтобы возвращать значение по умолчанию или вызывать proc по умолчанию. [[Bug #16908]]

* MatchData
    * Добавлен метод `MatchData#byteoffset`. [[Feature #13110]]

* Module
    * Добавлен метод `Module.used_refinements`. [[Feature #14332]]
    * Добавлен метод `Module#refinements`. [[Feature #12737]]
    * Добавлен метод `Module#const_added`. [[Feature #17881]]

* Proc
    * `Proc#dup` возвращает экземпляр подкласса. [[Bug #17545]]
    * `Proc#parameters` теперь принимает ключевое слово lambda. [[Feature #15357]]

* Refinement
    * Добавлен метод `Refinement#refined_class`. [[Feature #12737]]

* RubyVM::AbstractSyntaxTree
    * Добавлена опция `error_tolerant` для `parse`, `parse_file` и `of`. [[Feature #19013]]
      С этой опцией:
        1. SyntaxError подавляется
        2. AST возвращается для некорректного ввода
        3. `end` дополняется, когда парсер доходит до конца ввода, но `end` не хватает
        4. `end` обрабатывается как ключевое слово на основе отступа

        ```ruby
        # Без опции error_tolerant
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
        def m
          a = 10
          if
        end
        RUBY
        # => <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)

        # С опцией error_tolerant
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
        def m
          a = 10
          if
        end
        RUBY
        p root # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-4:3>

        # `end` обрабатывается как ключевое слово на основе отступа
        root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
        module Z
          class Foo
            foo.
          end

          def bar
          end
        end
        RUBY
        p root.children[-1].children[-1].children[-1].children[-2..-1]
        # => [#<RubyVM::AbstractSyntaxTree::Node:CLASS@2:2-4:5>, #<RubyVM::AbstractSyntaxTree::Node:DEFN@6:2-7:5>]
        ```

    * Добавлена опция `keep_tokens` для `parse`, `parse_file` и `of`. [[Feature #19070]]

        ```ruby
        root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
        root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
        root.tokens.map{_1[2]}.join # => "x = 1 + 2"
        ```

* Set
    * Set теперь доступен как встроенный класс, без необходимости вызова `require "set"`. [[Feature #16989]]
      В настоящее время он подгружается автоматически через константу `Set` или вызов `Enumerable#to_set`.

* String
    * Добавлены `String#byteindex` и `String#byterindex`. [[Feature #13110]]
    * Unicode обновлен до версии 15.0.0, а Emoji — до версии 15.0. [[Feature #18639]]
      (также относится к Regexp)
    * Добавлен метод `String#bytesplice`. [[Feature #18598]]

* Struct
    * Класс Struct теперь также может быть инициализирован именованными аргументами без `keyword_init: true` в `Struct.new`. [[Feature #16806]]

        ```ruby
        Post = Struct.new(:id, :name)
        Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
        # Начиная с Ruby 3.2 следующий код также работает без keyword_init: true.
        Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
        ```

## Проблемы совместимости

Примечание: Исключая исправления ошибок в функциях.

### Удаленные константы

Следующие устаревшие константы были удалены.

* `Fixnum` и `Bignum` [[Feature #12005]]
* `Random::DEFAULT` [[Feature #17351]]
* `Struct::Group`
* `Struct::Passwd`

### Удаленные методы

Следующие устаревшие методы были удалены.

* `Dir.exists?` [[Feature #17391]]
* `File.exists?` [[Feature #17391]]
* `Kernel#=~` [[Feature #15231]]
* `Kernel#taint`, `Kernel#untaint`, `Kernel#tainted?`
  [[Feature #16131]]
* `Kernel#trust`, `Kernel#untrust`, `Kernel#untrusted?`
  [[Feature #16131]]

## Проблемы совместимости со стандартной библиотекой

### Сторонние исходные коды больше не поставляются в комплекте

* Мы больше не поставляем в комплекте сторонние исходные коды, такие как `libyaml`, `libffi`.

    * Исходный код libyaml был удален из psych. Вам может потребоваться установить `libyaml-dev` в Ubuntu/Debian. Имя пакета отличается для каждой платформы.

    * Входящий в комплект исходный код libffi также удален из `fiddle`.

* Psych и fiddle поддерживали статические сборки с конкретными версиями исходников libyaml и libffi. Вы можете собрать psych с libyaml-0.2.5 следующим образом:

    ```bash
    $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
    ```

    И вы можете собрать fiddle с libffi-3.4.4 следующим образом:

    ```bash
    $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
    ```

  [[Feature #18571]]

## Обновления C API

### Обновленные C API

Следующие API были обновлены.

* Обновление PRNG
  * `rb_random_interface_t` обновлен и получил версию.
    Библиотеки расширений, использующие этот интерфейс, должны быть собраны для старых версий.
    Также необходимо определить функцию `init_int32`.

### Удаленные C API

Следующие устаревшие API были удалены.

* Переменная `rb_cData`.
* Функции "taintedness" и "trustedness". [[Feature #16131]]

## Обновления стандартной библиотеки

* Bundler

    * Добавлена поддержка `--ext=rust` в `bundle gem` для создания простых гемов с расширениями на Rust.
      [[GH-rubygems-6149]]
    * Ускорена клонирование git-репозиториев [[GH-rubygems-4475]]

* RubyGems

    * Добавлена поддержка mswin для cargo builder. [[GH-rubygems-6167]]

* ERB

    * `ERB::Util.html_escape` теперь работает быстрее, чем `CGI.escapeHTML`.
        * Он больше не выделяет объект String, если не требуется экранирование символов.
        * Он пропускает вызов метода `#to_s`, если аргумент уже является строкой.
        * `ERB::Escape.html_escape` добавлен как псевдоним для `ERB::Util.html_escape`, который не подвергался monkey-patching со стороны Rails.

* IRB

    * Добавлены команды интеграции с `debug.gem`: `debug`, `break`, `catch`,
      `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info`.
        * Они работают, даже если в вашем Gemfile нет гема `debug`.
        * Смотрите также: [Что нового в IRB в Ruby 3.2?](https://st0012.dev/whats-new-in-ruby-3-2-irb)
    * Добавлено больше команд и функций в стиле Pry.
        * Добавлены команды `edit` и `show_cmds` (аналог `help` в Pry).
        * `ls` принимает опции `-g` или `-G` для фильтрации вывода.
        * `show_source` является псевдонимом для `$` и принимает ввод без кавычек.
        * `whereami` является псевдонимом для `@`.

*   Следующие встроенные гемы были обновлены.

    * RubyGems 3.4.1
    * abbrev 0.1.1
    * benchmark 0.2.1
    * bigdecimal 3.1.3
    * bundler 2.4.1
    * cgi 0.3.6
    * csv 3.2.6
    * date 3.3.3
    * delegate 0.3.0
    * did_you_mean 1.6.3
    * digest 3.1.1
    * drb 2.1.1
    * english 0.7.2
    * erb 4.0.2
    * error_highlight 0.5.1
    * etc 1.4.2
    * fcntl 1.0.2
    * fiddle 1.1.1
    * fileutils 1.7.0
    * forwardable 1.3.3
    * getoptlong 0.2.0
    * io-console 0.6.0
    * io-nonblock 0.2.0
    * io-wait 0.3.0
    * ipaddr 1.2.5
    * irb 1.6.2
    * json 2.6.3
    * logger 1.5.3
    * mutex_m 0.1.2
    * net-http 0.3.2
    * net-protocol 0.2.1
    * nkf 0.1.2
    * open-uri 0.3.0
    * open3 0.1.2
    * openssl 3.1.0
    * optparse 0.3.1
    * ostruct 0.5.5
    * pathname 0.2.1
    * pp 0.4.0
    * pstore 0.1.2
    * psych 5.0.1
    * racc 1.6.2
    * rdoc 6.5.0
    * readline-ext 0.1.5
    * reline 0.3.2
    * resolv 0.2.2
    * resolv-replace 0.1.1
    * securerandom 0.2.2
    * set 1.0.3
    * stringio 3.0.4
    * strscan 3.0.5
    * syntax_suggest 1.0.2
    * syslog 0.1.1
    * tempfile 0.1.3
    * time 0.2.1
    * timeout 0.3.1
    * tmpdir 0.1.3
    * tsort 0.1.1
    * un 0.2.1
    * uri 0.12.0
    * weakref 0.1.2
    * win32ole 1.8.9
    * yaml 0.2.1
    * zlib 3.0.0

*   Следующие поставляемые гемы были обновлены.

    * minitest 5.16.3
    * power_assert 2.0.3
    * test-unit 3.5.7
    * net-ftp 0.2.0
    * net-imap 0.3.3
    * net-pop 0.1.2
    * net-smtp 0.3.3
    * rbs 2.8.2
    * typeprof 0.21.3
    * debug 1.7.1

Подробности о встроенных или поставляемых гемах смотрите в релизах на GitHub, например, [релизы logger на GitHub](https://github.com/ruby/logger/releases), или в заметках о релизе.

Для получения подробной информации смотрите [NEWS](https://github.com/ruby/ruby/blob/v3_2_0/NEWS.md)
или [журнал коммитов](https://github.com/ruby/ruby/compare/v3_1_0...v3_2_0).

С учетом этих изменений было [изменено 3048 файлов, добавлено 218253 строк(+), удалено 131067 строк(-)](https://github.com/ruby/ruby/compare/v3_1_0...v3_2_0#file_bucket)
с момента выхода Ruby 3.1.0!

Счастливого Рождества, веселых праздников и приятного программирования на Ruby 3.2!

## Скачать

* <https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0.tar.gz>

      SIZE: 20440715
      SHA1: fb4ab2ceba8bf6a5b9bc7bf7cac945cc94f94c2b
      SHA256: daaa78e1360b2783f98deeceb677ad900f3a36c0ffa6e2b6b19090be77abc272
      SHA512: 94203051d20475b95a66660016721a0457d7ea57656a9f16cdd4264d8aa6c4cd8ea2fab659082611bfbd7b00ebbcf0391e883e2ebf384e4fab91869e0a877d35

* <https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0.tar.xz>

      SIZE: 15058364
      SHA1: bcdae07183d66fd902cb7bf995545a472d2fefea
      SHA256: d2f4577306e6dd932259693233141e5c3ec13622c95b75996541b8d5b68b28b4
      SHA512: 733ecc6709470ee16916deeece9af1c76220ae95d17b2681116aff7f381d99bc3124b1b11b1c2336b2b29e468e91b90f158d5ae5fca810c6cf32a0b6234ae08e

* <https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.0.zip>

      SIZE: 24583271
      SHA1: 581ec7b9289c2a85abf4f41c93993ecaa5cf43a5
      SHA256: cca9ddbc958431ff77f61948cb67afa569f01f99c9389d2bbedfa92986c9ef09
      SHA512: b7d2753825cc0667e8bb391fc7ec59a53c3db5fa314e38eee74b6511890b585ac7515baa2ddac09e2c6b6c42b9221c82e040af5b39c73e980fbd3b1bc622c99d

## Что такое Ruby

Ruby был впервые разработан Matz (Yukihiro Matsumoto) в 1993 году и сейчас развивается как open source. Он работает на многих платформах и используется по всему миру, особенно для веб-разработки.

[Feature #12005]:     https://bugs.ruby-lang.org/issues/12005
[Feature #12084]:     https://bugs.ruby-lang.org/issues/12084
[Feature #12655]:     https://bugs.ruby-lang.org/issues/12655
[Feature #12737]:     https://bugs.ruby-lang.org/issues/12737
[Feature #13110]:     https://bugs.ruby-lang.org/issues/13110
[Feature #14332]:     https://bugs.ruby-lang.org/issues/14332
[Feature #15231]:     https://bugs.ruby-lang.org/issues/15231
[Feature #15357]:     https://bugs.ruby-lang.org/issues/15357
[Bug #15928]:         https://bugs.ruby-lang.org/issues/15928
[Feature #16122]:     https://bugs.ruby-lang.org/issues/16122
[Feature #16131]:     https://bugs.ruby-lang.org/issues/16131
[Bug #16466]:         https://bugs.ruby-lang.org/issues/16466
[Feature #16663]:     https://bugs.ruby-lang.org/issues/16663
[Feature #16806]:     https://bugs.ruby-lang.org/issues/16806
[Bug #16889]:         https://bugs.ruby-lang.org/issues/16889
[Bug #16908]:         https://bugs.ruby-lang.org/issues/16908
[Feature #16989]:     https://bugs.ruby-lang.org/issues/16989
[Feature #17351]:     https://bugs.ruby-lang.org/issues/17351
[Feature #17391]:     https://bugs.ruby-lang.org/issues/17391
[Bug #17545]:         https://bugs.ruby-lang.org/issues/17545
[Bug #17767]:         https://bugs.ruby-lang.org/issues/17767
[Feature #17837]:     https://bugs.ruby-lang.org/issues/17837
[Feature #17881]:     https://bugs.ruby-lang.org/issues/17881
[Feature #18033]:     https://bugs.ruby-lang.org/issues/18033
[Feature #18159]:     https://bugs.ruby-lang.org/issues/18159
[Feature #18239]:     https://bugs.ruby-lang.org/issues/18239#note-17
[Feature #18351]:     https://bugs.ruby-lang.org/issues/18351
[Feature #18367]:     https://bugs.ruby-lang.org/issues/18367
[Bug #18435]:         https://bugs.ruby-lang.org/issues/18435
[Feature #18462]:     https://bugs.ruby-lang.org/issues/18462
[Feature #18481]:     https://bugs.ruby-lang.org/issues/18481
[Bug #18487]:         https://bugs.ruby-lang.org/issues/18487
[Feature #18564]:     https://bugs.ruby-lang.org/issues/18564
[Feature #18571]:     https://bugs.ruby-lang.org/issues/18571
[Feature #18585]:     https://bugs.ruby-lang.org/issues/18585
[Feature #18589]:     https://bugs.ruby-lang.org/issues/18589
[Feature #18595]:     https://bugs.ruby-lang.org/issues/18595
[Feature #18598]:     https://bugs.ruby-lang.org/issues/18598
[Bug #18625]:         https://bugs.ruby-lang.org/issues/18625
[Feature #18630]:     https://bugs.ruby-lang.org/issues/18630
[Bug #18633]:         https://bugs.ruby-lang.org/issues/18633
[Feature #18639]:     https://bugs.ruby-lang.org/issues/18639
[Feature #18685]:     https://bugs.ruby-lang.org/issues/18685
[Bug #18729]:         https://bugs.ruby-lang.org/issues/18729
[Bug #18751]:         https://bugs.ruby-lang.org/issues/18751
[Feature #18774]:     https://bugs.ruby-lang.org/issues/18774
[Feature #18776]:     https://bugs.ruby-lang.org/issues/18776
[Bug #18782]:         https://bugs.ruby-lang.org/issues/18782
[Feature #18788]:     https://bugs.ruby-lang.org/issues/18788
[Feature #18798]:     https://bugs.ruby-lang.org/issues/18798
[Feature #18809]:     https://bugs.ruby-lang.org/issues/18809
[Feature #18821]:     https://bugs.ruby-lang.org/issues/18821
[Feature #18822]:     https://bugs.ruby-lang.org/issues/18822
[Feature #18824]:     https://bugs.ruby-lang.org/issues/18824
[Feature #18832]:     https://bugs.ruby-lang.org/issues/18832
[Feature #18875]:     https://bugs.ruby-lang.org/issues/18875
[Feature #18925]:     https://bugs.ruby-lang.org/issues/18925
[Feature #18944]:     https://bugs.ruby-lang.org/issues/18944
[Feature #18949]:     https://bugs.ruby-lang.org/issues/18949
[Feature #18968]:     https://bugs.ruby-lang.org/issues/18968
[Feature #19008]:     https://bugs.ruby-lang.org/issues/19008
[Feature #19013]:     https://bugs.ruby-lang.org/issues/19013
[Feature #19026]:     https://bugs.ruby-lang.org/issues/19026
[Feature #19036]:     https://bugs.ruby-lang.org/issues/19036
[Feature #19060]:     https://bugs.ruby-lang.org/issues/19060
[Feature #19070]:     https://bugs.ruby-lang.org/issues/19070
[Feature #19071]:     https://bugs.ruby-lang.org/issues/19071
[Feature #19078]:     https://bugs.ruby-lang.org/issues/19078
[Bug #19087]:         https://bugs.ruby-lang.org/issues/19087
[Bug #19100]:         https://bugs.ruby-lang.org/issues/19100
[Feature #19104]:     https://bugs.ruby-lang.org/issues/19104
[Feature #19135]:     https://bugs.ruby-lang.org/issues/19135
[Feature #19138]:     https://bugs.ruby-lang.org/issues/19138
[Feature #19194]:     https://bugs.ruby-lang.org/issues/19194
[Molinillo]:          https://github.com/CocoaPods/Molinillo
[PubGrub]:            https://github.com/jhawthorn/pub_grub
[GH-net-protocol-14]: https://github.com/ruby/net-protocol/pull/14
[GH-pathname-20]:     https://github.com/ruby/pathname/pull/20
[GH-6791]:            https://github.com/ruby/ruby/pull/6791
[GH-6868]:            https://github.com/ruby/ruby/pull/6868
[GH-rubygems-4475]:   https://github.com/rubygems/rubygems/pull/4475
[GH-rubygems-6149]:   https://github.com/rubygems/rubygems/pull/6149
[GH-rubygems-6167]:   https://github.com/rubygems/rubygems/pull/6167
[sec-156615]:         https://hackerone.com/reports/156615
[CVE-2021-33621]:     https://www.ruby-lang.org/ru/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/
[wasm/README.md]:     https://github.com/ruby/ruby/blob/master/wasm/README.md
[ruby.wasm]:          https://github.com/ruby/ruby.wasm
