---
title: "Rack، الأساس الخفي لكل تطبيق ويب رائع بلغة Ruby"
description: "يُستخدم Rack في كل إطار عمل تقريبًا يعمل على Ruby، ومع ذلك لا يفهم كثير من مطوري Rails ما هو أو كيف يعمل. وفهم Rack يجعل هذه الأطر أقل رهبة، وهو أسهل مما تتوقعون حين تلقون نظرة عليه."
canonical: https://oozou.com/ar/blog/rack-the-underbelly-of-every-great-ruby-web-app-137
locale: ar
date: 2020-11-18
author: "OOZOU Team"
---

# Rack، الأساس الخفي لكل تطبيق ويب رائع بلغة Ruby

> يُستخدم Rack في كل إطار عمل تقريبًا يعمل على Ruby، ومع ذلك لا يفهم كثير من مطوري Rails ما هو أو كيف يعمل. وفهم Rack يجعل هذه الأطر أقل رهبة، وهو أسهل مما تتوقعون حين تلقون نظرة عليه.

Published: 2020-11-18 · Author: OOZOU Team (Engineering) · 4 min read

واجهة Rack ومبادئها بسيطة جدًا في الواقع، كل ما في الأمر أننا لا نحتاج عادة إلى التفكير فيها عند العمل مع Rails أو Sinatra أو Hanami أو [غيرها](https://github.com/rack/rack#label-Supported+web+frameworks) من الأطر المبنية فوق Rack.

# إذن ما الذي يفعله Rack فعلًا؟

إنه واجهة بين خادم الويب وكود تطبيقكم! يستمع Rack إلى خادم الويب عبر handler، ثم يستدعي تطبيقكم بوسيط واحد هو [Rack Environment](https://github.com/rack/rack/blob/master/SPEC.rdoc#label-The+Environment). وللالتزام بهذه الواجهة، يجب أن يُرجع تطبيقكم مصفوفة من 3 عناصر: الحالة (status) والـ headers والمحتوى (body). يجب أن تكون الحالة عددًا صحيحًا يطابق رمز حالة HTTP صالحًا. ويجب أن تكون الـ headers عبارة عن hash من أزواج المفاتيح والقيم. وأخيرًا يجب أن يكون الـ body كائنًا يستجيب لـ each، وعادة ما يكون مصفوفة من السلاسل النصية.

# مرحبًا يا عالم؟

لنبنِ أول تطبيق Rack. يأتي Rack مزودًا بـ [handlers](https://github.com/rack/rack#label-Supported+web+servers) جاهزة لعدة خوادم ويب، وسنستخدم أحدها.

```ruby
# hello_world.rb
require 'rack'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
  end
end
Rack::Handler::WEBrick.run HelloWorld.new
```

شغّلوه الآن وزوروا localhost:8080

```
ruby hello_world.rb
```

# لنحوّل تطبيق Hello, World! إلى تطبيق Rack حقيقي

التطبيق أعلاه يُستدعى عبر الملف التنفيذي ruby، لكن Rack يوفر ملفًا تنفيذيًا اسمه rackup لتشغيل تطبيقات Rack. يتوقع هذا الملف وجود ملف في جذر تطبيقكم اسمه config.ru (اختصار r[ack]u[p])، ويُستخدم لإعداد سلسلة الـ middleware في Rack.

```ruby
require 'thin'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, World!\n"]]
  end
end

class GoodbyeWorld
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    body << "Goodbye, World!\n"
    [status, headers, body]
  end
end

use GoodbyeWorld
run HelloWorld.new
```

حوّلنا الآن سكربت Rack إلى إعداد تطبيق Rack، وأجرينا أيضًا بعض التغييرات. استدعينا خادم الويب thin الذي يبدّل الـ handler تلقائيًا. وأضفنا أول middleware إلى السلسلة، وهو GoodbyeWorld. وأخيرًا صار بإمكاننا استخدام methods لغة Rack الخاصة (DSL)، وهي use وrun.
فما الذي يفعله هذا الـ middleware؟ في هذه الحالة نشغّل التطبيق أولًا، ثم نضيف عبارة "Goodbye, World!" إلى محتوى الاستجابة.
ويمكننا أن نفعل بالـ middleware أشياء أخرى أكثر واقعية. لنضف أداة بسيطة لقياس الأداء (profiler).

```ruby
require 'thin'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, World!\n"]]
  end
end
class GoodbyeWorld
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    body << "Goodbye, World!\n"
    [status, headers, body]
  end
end

class Benchmarker
  def initialize(app)
    @app = app
  end

  def call(env)
    start_time = current_time
    status, headers, body = @app.call(env)
    end_time = current_time
    body << "Took #{end_time - start_time} seconds to complete"
    [status, headers, body]
  end

  def current_time
    Time.now.to_f * 1000
  end
end

use Benchmarker
use GoodbyeWorld
run HelloWorld.new
```

# تعديل الـ Environment

حتى الآن عرضنا فقط كيف نغلّف التطبيق، فلنغيّر ذلك بحيث نعدّل الـ environment فقط. الأمر بسيط: بدلًا من استدعاء التطبيق ثم تعديل الاستجابة، نعدّل env أولًا ثم نستدعي التطبيق.

```ruby
require 'thin'

class HelloWorld
  def call(env)
    if env['flip'] == 'heads'
      [200, {"Content-Type" => "text/plain"}, ["Hello, World!\n"]]
    else
      [401, {"Content-Type" => "text/plain"}, ["Naughty naughty\n"]]
    end
  end
end

class CoinFlip
  def initialize(app)
    @app = app
  end

  def call(env)
    env['flip'] = %w[heads tails].sample
    @app.call(env)
  end
end

use CoinFlip
run HelloWorld.new
```

# إيقاف التنفيذ

إذا أرجع أي middleware استجابة، فلن تُعالَج بقية السلسلة. وتستفيد من ذلك بعض أدوات middleware في Rack مثل rack-attack، التي تمنع المهاجمين من إرسال طلبات خبيثة إلى تطبيقنا.
لنعدّل المنطق بحيث تفوز طلباتنا عشوائيًا.

```ruby
require 'thin'

class HelloWorld
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, World!\n"]]
  end
end

class CoinFlip
  def initialize(app)
    @app = app
  end

  def call(env)
    if %w[heads tails].sample == 'heads'
      @app.call(env)
    else
      [200, {"Content-Type" => "text/plain"}, ["Winner winner chicken dinner!\n"]]
    end
  end
end

class GoodbyeWorld
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    body << "Goodbye, World!\n"
    [status, headers, body]
  end
end

use CoinFlip
use GoodbyeWorld
run HelloWorld.new
```

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
