تخطيط الصفحات والتصيير في ريلز

من موسوعة حسوب
اذهب إلى: تصفح، ابحث

يغطي هذا الدليل ميزات التخطيط الأساسية لوحدتي التحكم والعرض. بعد قراءة هذا الدليل، ستتعلم:

  • كيفية استخدام توابع التصيير المختلفة المضمنة ‎في ريلز.
  • كيفية إنشاء تخطيطات (layouts) تحتوي على أقسام محتوى متعددة.
  • كيفية استخدام الأجزاء مع واجهات العرض.
  • كيفية استخدام تخطيطات متداخلة (قوالب فرعية).

محتويات

نظرة عامة: كيف تتراكب قطع الأحجية معًا

يركز هذا الدليل على التفاعل بين المتحكم والعرض في نمط التصميم Model-View-Controller. كما تعلم، فإنَّ المتحكم مسؤول عن تنظيم العملية الكاملة للتعامل مع الطلب في ريلز، على الرغم من أنه عادة لا يتعامل مع أي شيفرة ثقيلة في النموذج. ولكن بعد ذلك، عندما يحين وقت إرسال رد مرة أخرى إلى المستخدم، يسلم المتحكم بعض الأشياء إلى وحدة العرض. إنه التبادل الذي هو موضوع هذا الدليل.

عمومًا، يتضمن ذلك تحديد ما يجب إرساله كاستجابة واستدعاء وظيفة مناسبة لإنشاء تلك الاستجابة. إذا كانت الاستجابة عبارة عن عرض كامل، فإن ريلز يقوم أيضًا ببعض العمل الإضافي لتغليف العرض في تخطيط وربما السحب إلى العروض الجزئية. سترى كل هذه الأمور لاحقًا في هذا الدليل.

إنشاء الردود

من وجهة نظر المتحكم، هناك ثلاث طرق لإنشاء استجابة HTTP:

  1. استدعاء render لإنشاء استجابة كاملة لإرسالها مرة أخرى إلى المتصفح، أو
  2. استدعاء redirect_to لإرسال شيفرة إعادة التوجيه HTTP إلى المتصفح، أو
  3. استدعاء head لإنشاء استجابة تتكون من ترويسات HTTP فقط لإرسالها مرة أخرى إلى المتصفح.

التصيير افتراضيًا: العرف فوق الضبط في الإجراء

كنت سمعت أن ريلز يعزز العرف (convention) على الضبط (configuration). التصيير الافتراضي هو مثال ممتاز على هذا. بشكل افتراضي، يصيِّر المتحكم في ريلز تلقائيًا العرض بأسماء تتوافق مع المسارات الموجهة الصالحة. مثلًا، إذا كان لديك هذه الشيفرة في الصنف BooksController الخاصة بك:
class BooksController < ApplicationController
end
وما يلي في ملف مساراتك الموجهة:
resources :books
ولديك ملف العرض app/views/books/index.html.erb الذي يحوي:
<h1>Books are coming soon!</h1>
ستصيِّر ريلز العرض app/views/books/index.html.erb عند الانتقال إلى books/ وستشاهد العبارة "Books are coming soon!‎" على شاشتك. ومع ذلك، لا تكون هذه الشاشة مفيدةً إلا قليلًا، لذلك ستُنشئ قريبًا النموذج Book الخاص بك وتضيف الإجراء index إلى BooksController:
class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end
لاحظ أننا لا نملك تصييرًا صريحًا في نهاية إجراء الفهرسة (index) وفقًا لمبدأ "العرف فوق الضبط" (convention over configuration). القاعدة هي أنك إذا لم تصيِّر صراحةً شيئًا في نهاية إجراء المتحكم، فسيبحث ريلز تلقائيًا عن القالب action_name.html.erb في مسار عرض المتحكم ويصيِّره. في هذه الحالة، سيصيِّر ريلز الملف app/views/books/index.html.erb. إذا أردنا إظهار خصائص جميع الكتب في عرضنا، فيمكننا القيام بذلك باستخدام القالب ERB على هذا النحو:
<h1>Listing Books</h1>
 
<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>
 
  <tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= book.title %></td>
        <td><%= book.content %></td>
        <td><%= link_to "Show", book %></td>
        <td><%= link_to "Edit", edit_book_path(book) %></td>
        <td><%= link_to "Destroy", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
      </tr>
    <% end %>
  </tbody>
</table>
 
<br>
 
<%= link_to "New book", new_book_path %>
ملاحظة: يجرى التصيير الفعلي بواسطة أصناف مشتعبة من النموذج ActionView::Template::Handlers. لا يتعمق هذا الدليل في هذه العملية، ولكن من المهم معرفة أن امتداد الملف في عرضك يتحكم في اختيار معالج القالب.

استخدام التابع render

في معظم الحالات، ينفِّقذ التابع ActionController::Base.render المهام الشاقَّة في تصيير محتوى التطبيق الخاص بك لاستخدامه من قبل المستعرض. هناك مجموعة متنوعة من الطرق لتخصيص سلوك التابع render. يمكنك تصيير العرض الافتراضي لقالب ريلز، أو قالب محدد، أو ملف، أو شيفرة مضمنة، أو لا شيء على الإطلاق. تستطيع تصيير نص أو JSON أو XML. يمكنك تحديد نوع المحتوى أو حالة HTTP للاستجابة المصيَّرة أيضًا.

فائدة: إذا كنت تريد رؤية النتائج الدقيقة لاستدعاء render دون الحاجة إلى فحصها في متصفح، فيمكنك استدعاء render_to_string. يأخذ هذا التابع بالضبط نفس الخيارات التي يقبلها التابع render، لكنه يعيد سلسلة نصية بدلًا من إعادة إرسال استجابة إلى المتصفح.

عرض شكل الحدث

إذا أردت تصيير العرض الذي يطابق قالبًا مختلفًا داخل وحدة التحكم نفسها، فيمكنك استخدام render مع اسم العرض نفسه:
def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render "edit"
  end
end
في حالة فشل استدعاء update، يؤدي استدعاء الإجراء update في هذا المتحكم إلى تصيير القالب edit.html.erb المنتمي إلى نفس المتحكم. إذا فَضَّلْتَ ذلك، فيمكنك استخدام رمز بدلًا من سلسلة نصية لتحديد الإجراء المطلوب تصييره:
def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render :edit
  end
end

تصيير قالب إجراء من متحكم آخر

ماذا لو أردتَ تصيير قالبٍ من متحكم مختلف تمامًا عن ذلك الذي يحتوي على شيفرة الإجراء؟ يمكنك أيضًا فِعل ذلك باستخدام التابع render الذي يقبل المسار الكامل (نسبة إلى app/views) للقالب لتصييره. على سبيل المثال، إذا كنت تُشغِّل شيفرة في المتحكم AdminProductsController توجد في المسار app/controllers/admin، فيمكنك تصيير نتائج الإجراء على قالب في المسار app/views/products بالشكل التالي:
render "products/show"
يعرف ريلز أن هذا العرض ينتمي إلى متحكم مختلف بسبب محرف الخط المائل المضمن في السلسلة النصية. إذا كنت تريد أن تكون صريحًا، فيمكنك استخدام الخيار :template (الذي كان مطلوبًا في ريلز 2.2 والإصدارات الأقدم):
render template: "products/show"

تصيير ملف عشوائي

يمكن للتابع render أيضًا استخدام عرض يقع خارج تطبيقك كليًّا:
render file: "/u/apps/warehouse_app/current/app/views/products/show"
يأخذ الخيار :file مسار نظام ملفات مطلق. بالطبع، يجب أن يكون لديك حقوق العرض الذي تريد استخدامه لتصيير محتواه.

ملاحظة: يمكن أن يؤدي استخدام الخيار :file بالتزامن مع إدخالات المستخدمين إلى مشاكل أمنية إذ يمكن للمهاجم استخدام هذا الإجراء للوصول إلى ملفات أمنية حساسة في نظام ملفاتك.

ملاحظة: افتراضيًّا، يصيَّر الملف باستخدام التخطيط الحالي.

فائدة: إذا كنت تُشغَّل ريلز على نظام ويندوز ، يجب استخدام الخيار :file لتصيير ملف، لأن أسماء الملفات في ويندوز لا تملك نفس تنسيق أسماء الملفات في الأنظمة الشبيهة بيونكس.

التغليف

الطرق الثلاث أعلاه للتصيير (تصيير قالب آخر داخل المتحكم، وتصيير قالب داخل متحكم آخر وتصيير ملف عشوائي موجود في نظام الملفات) هي في الواقع حالات مختلفة لنفس الإجراء.

في الواقع، في الصنف BooksController داخل الإجراء update حيث نريد تصيير قالب التعديل (edit template)، إذا لم يُحَدَّث الكتاب (book) بنجاح، فإن جميع إستدعاءات التابع render التالية ستصيِّر القالب edit.html.erb في المسار views/books:
render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"
أيٌّ منهم تستخدمه هو في الحقيقة مسألة أسلوب وتوافق، ولكن القاعدة الأساسية هي استخدام أبسط طريقة منطقية للشيفرة التي تكتبها.

استخدام render مع الخيار :inline

يمكن استخدام التابع render بدون عرض تمامًا، وذلك إذا كنت ترغب في استخدام الخيار :inline لتزويد القالب ERB كجزء من استدعاء الدالة. هذا صحيح تمامًا:
render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>"
تحذير: نادرًا ما يكون هناك أي سبب وجيه لاستخدام هذا الخيار. مزج القالب ERB مع المتحكم الخاص بك يُغير اتجاه النمط MVC في ريلز وسيجعل من الصعب على مطورين آخرين تتبع منطق المشروع الخاص بك. استخدم طريقة عرض قالب ERB منفصلة بدلًا من ذلك. بشكل افتراضي، يستخدم التصيير المضمَّن القالب ERB. يمكنك إجباره على استخدام القالب Builder بدلًا من ذلك باستخدام الخيار :type:
render inline: "xml.p {'Horrid coding practice!'}", type: :builder

تصيير النصوص

يمكنك إرسال نص مجرد - بدون تنسيق على الإطلاق - إلى المتصفح مرة أخرى باستخدام الخيار :plain مع التابع render:
render plain: "OK"
فائدة: تصيير النص المجرد مفيدٌ للغاية عندما ترد على طلبات Ajax أو خدمة الويب التي تتوقع شيئًا غير شيفرة HTML.

ملاحظة: بشكل افتراضي، إذا استخدمت الخيار :plain، فسيُصيَّر النص بدون استخدام التخطيط الحالي. إذا كنت تريد أن يضع ريلز النص في التخطيط الحالي، فأنت بحاجة إلى إضافة الخيار layout: true واستخدام اللاحقة ‎.text.erb لملف التخطيط.

تصيير شيفرة HTML

يمكنك إرسال سلسلة HTML نصية مرة أخرى إلى المتصفح باستخدام الخيار :html مع التابع render:
render html: helpers.tag.strong('Not Found')
فائدة: هذا الخيار مفيد عند تصيير قطعة صغير من شيفرة HTML. ومع ذلك، قد ترغب في النظر في نقلها إلى ملف قالب إذا كانت الشيفرة معقدةً.

ملاحظة: عند استخدام الخيار :html، ستُهْرَب كيانات HTML إذا لم تكن السلسلة النصية مكونة (composed) مع واجهات برمجة التطبيقات html_safe-aware.

تصيير بيانات بتنسيق JSON

إنَّ JSON هو تنسيق بيانات جافاسكربت تستخدمه العديد من مكتبات Ajax. يحتوي ريلز على دعم مُضمَّن لتحويل الكائنات إلى JSON وتصييرها مجددًا للمتصفح:
render json: @product
تنويه: لا تحتاج إلى استدعاء to_json في الكائن الذي تريد تصييره. إذا كنت تستخدم الخيار :json ، فسيستدعي التابع render التابعَ to_json لك تلقائيًا.

تصيير بيانات بتنسيق XML

يحتوي ريلز أيضًا على دعم مضمن لتحويل الكائنات إلى XML وتصييره مرة أخرى للمستدعِي:
render xml: @product
تنويه: لا تحتاج إلى استدعاء to_xml في الكائن الذي تريد تصييره. إذا كنت تستخدم الخيار :xml، فسوف يستدعي التابع render تلقائيًا to_xml نيابةً عنك.

تصيير شيفرات جافاسكربت الصرفة (VanillaJS)

بإمكان ريلز أن تصيِّر شيفرات جافاسكربت الصرفة (تدعى vanilla JavaScript أو VanillaJS اختصارًا):
render js: "alert('Hello Rails');"
سيرسل هذا السلسلة النصية المعطاة إلى المتصفح باستخدام نوع MIME text/javascript.

تصيير محتوى خام

يمكنك إعادة إرسال محتوى خام إلى المتصفح، دون تعيين أي نوع للمحتوى، باستخدام الخيار :body مع التابع render:
render body: "raw"
تنويه: يجب استخدام هذا الخيار فقط إذا كنت لا تهتم بنوع محتوى الرد (الاستجابة). استخدام الخيار :plain أو :html قد يكون أكثر ملاءمة معظم الأوقات.

ملاحظة: ما لم يُستبدَل، فإن ردك المُعاد من استعمال الخيار هذا مع التابع render سيكون من النوع text/plain، لأن هذا هو نوع المحتوى الافتراضي لرد Action Dispatch.

خيارات أخرى

يقبل التابع render عمومًا -إضافةً للخيارات السابقة- خمسةَ خيارات هي:

  1. :content_type
  2. :layout
  3. :location
  4. :status
  5. :formats
الخيار :content_type
بشكل افتراضي، ستخدم (serve) ريلز نتائجَ عملية التصيير مع النوع MIME text/html للمحتوى (أو النوع MIME application/json إذا كنت تستخدم الخيار :json أو النوع MIME application/json إن كنت تستعمل الخيار :xml). هناك أوقات قد ترغب في تغيير هذا، ويمكنك فعل ذلك عن طريق الخيار :content_type بالشكل التالي:
render file: filename, content_type: "application/rss"
الخيار :layout

مع معظم خيارات التابع render، يُعرض المحتوى المصيَّر كجزء من التخطيط الحالي. ستتعلم المزيد حول التخطيطات وكيفية استخدامها لاحقًا في هذا الدليل.

يمكنك استخدام الخيار :layout لإخبار ريلز باستخدام ملف معين كتخطيط للإجراء الحالي:
render layout: "special_layout"
يمكنك أيضًا إخبار ريلز بالتصيير دون تخطيط على الإطلاق:
render layout: false
الخيار :location
يمكنك استخدام الخيار :location لضبط الترويسة HTTP Location:
render xml: photo, location: photo_url(photo)
الخيار :status
سينشئ ريلز تلقائيًا استجابةً باستخدام رمز حالة HTTP الصحيحة (تكون في معظم الحالات هي "200 OK"). يمكنك استخدام الخيار :status لتغيير هذا السلوك:
render status: 500
render status: :forbidden
يفهم ريلز كلًا من رمز الحالة الذي هو عددٌ صحيحٌ والرمز المقابل الموضح في الجدول التالي:
صنف الرد (الاستجابة) رمز حالة HTTP الرمز
Informational 100 ‎:continue
101 ‎:switching_protocols
102 ‎:processing
Success 200 ‎:ok
201 ‎:created
202 ‎:accepted
203 ‎:non_authoritative_information
204 ‎:no_content
205 ‎:reset_content
206 ‎:partial_content
207 ‎:multi_status
208 ‎:already_reported
226 ‎:im_used
Redirection 300 ‎:multiple_choices
301 ‎:moved_permanently
302 ‎:found
303 ‎:see_other
304 ‎:not_modified
305 ‎:use_proxy
307 ‎:temporary_redirect
308 ‎:permanent_redirect
Client Error 400 ‎:bad_request
401 ‎:unauthorized
402 ‎:payment_required
403 ‎:forbidden
404 ‎:not_found
405 ‎:method_not_allowed
406 ‎:not_acceptable
407 ‎:proxy_authentication_required
408 ‎:request_timeout
409 ‎:conflict
410 ‎:gone
411 ‎:length_required
412 ‎:precondition_failed
413 ‎:payload_too_large
414 ‎:uri_too_long
415 ‎:unsupported_media_type
416 ‎:range_not_satisfiable
417 ‎:expectation_failed
421 ‎:misdirected_request
422 ‎:unprocessable_entity
423 ‎:locked
424 ‎:failed_dependency
426 ‎:upgrade_required
428 ‎:precondition_required
429 ‎:too_many_requests
431 ‎:request_header_fields_too_large
451 ‎:unavailable_for_legal_reasons
Server Error 500 ‎:internal_server_error
501 ‎:not_implemented
502 ‎:bad_gateway
503 ‎:service_unavailable
504 ‎:gateway_timeout
505 ‎:http_version_not_supported
506 ‎:variant_also_negotiates
507 ‎:insufficient_storage
508 ‎:loop_detected
510 ‎:not_extended
511 ‎:network_authentication_required

ملاحظة: إذا حاولت تصيير محتوى مع رمز حالة انعدام المحتوى (non-content status code، أي 100-199 أو 204 أو 205 أو 304) ، فسيُحذَف من الاستجابة.

الخيار :format
يستخدم ريلز التنسيق المحدد في الطلب (أو :html بشكل افتراضي). يمكنك تغيير هذا بتمرير الخيار :formats مع رمز أو مصفوفة:
render formats: :xml
render formats: [:json, :xml]
في حالة عدم وجود قالب بالتنسيق المحدد، فسيرمى الخطأ ActionView::MissingTemplate.

البحث عن التخطيطات

للعثور على التخطيط الحالي، تبحث ريلز أولًا عن ملف في app/views/layouts بنفس الاسم الأساسي للمتحكم. على سبيل المثال، سيؤدي استخدام Action View من الصنف PhotosController إلى استخدام app/views/layouts/photos.html.erb (أو app/views/layouts/photos.builder). إذا لم يكن هناك تخطيط محدد لهذا المتحكم، فستستخدم ريلز app/views/layouts/application.html.erb أو app/views/layouts/application.builder. إذا لم يكن هناك تخطيط ‎.erb، فسوف يستخدم ريلز التخطيط ‎.builder إذا كان موجودًا. توفر ريلز أيضًا عدة طرق لتعيين تخطيطات محددة بدقة أكبر لمتحكمات وأحداث فردية.

تحديد التخطيطات للمتحكمات
يمكنك تخطي العرف المتعلق بالتخطيط الافتراضي في متحكماتك الخاصة عبر استعمال التصريح layout. فمثلا:
class ProductsController < ApplicationController
  layout "inventory"
  #...
end
باستخدام هذا التصريح، ستستخدم جميع العروض المصيَّرة بواسطة ProductsController الملف app/views/layouts/inventory.html.erb كتخطيط لها. لتعيين تخطيط محدد للتطبيق بالكامل، استخدم التصريح layout في الصنف ApplicationController الخاصة بك:
class ApplicationController < ActionController::Base
  layout "main"
  #...
end
باستخدام هذا التصريح، ستَستخدم جميعُ العروض في التطبيق الملف app/views/layouts/main.html.er تخطيطًا لها.
اختيار التخطيطات في وقت التشغيل
يمكنك استخدام رمز لتأجيل اختيار التخطيط حتى تتم معالجة الطلب:
class ProductsController < ApplicationController
  layout :products_layout
 
  def show
    @product = Product.find(params[:id])
  end
 
  private
    def products_layout
      @current_user.special? ? "special" : "products"
    end
 
end
الآن، إذا كان المستخدم الحالي مستخدمًا خاصًا، فسيحصل على تخطيط خاص عند عرض أحد المنتجات. يمكنك حتى استخدام تابع مضمن، مثل Proc، لتحديد التخطيط. على سبيل المثال، إذا قمت بتمرير كائن من النوع Proc، فستُعطَى الكتلة التي تعطيها الكائن Proc النسخة controller، بحيث يمكن تحديد التخطيط بناءً على الطلب الحالي:
class ProductsController < ApplicationController
  layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end
التخطيطات الشرطية
تدعم التخطيطات المحددة على مستوى المتحكم خيارين هما: :only و :except. تأخذ هذه الخيارات إما اسم تابع، أو مصفوفة من أسماء تابع، يقابل أسماء تابع في المتحكم:
class ProductsController < ApplicationController
  layout "product", except: [:index, :rss]
end
باستخدام هذا التعريف، سيُستخدَم التخطيط product لكل شيء باستثناء التابعين rss و index.
وراثة التخطيط

تتوالى (cascade) تصريحات التخطيط لأسفل في التسلسل الهرمي، وتحل تصريحات التخطيط الأكثر تحديدًا دائمًا مكان التصريحات الأكثر عامية. فمثلًا:

  • الملف application_controller.rb
class ApplicationController < ActionController::Base
  layout "main"
end
  • الملف articles_controller.rb
class ArticlesController < ApplicationController
end
  • الملف special_articles_controller.rb
class SpecialArticlesController < ArticlesController
  layout "special"
end
  • الملف old_articles_controller.rb
class OldArticlesController < SpecialArticlesController
  layout false
 
  def show
    @article = Article.find(params[:id])
  end
 
  def index
    @old_articles = Article.older
    render layout: "old"
  end
  # ...
end
في هذا التطبيق:
  • بشكل عام، ستصيَّر العروض في التخطيط main.
  • سيستخدم ArticlesController.index التخطيط main.
  • سيستخدم SpecialArticlesController.index التخطيط special.
  • لن يستخدم OldArticlesController.show أي تخطيط على الإطلاق.
  • سيستخدم OldArticlesController.index التخطيط old.
وراثة القالب
كما هو الحال مع منطق "وراثة التخطيط"، إذا لم يُعثَر على قالب أو جزء منه في المسار التقليدي، فسيبحث المتحكم عن قالب أو جزء منه ليصيِّره في سلسلة الوراثة الخاصة به. فمثلًا:
# app/controllers/application_controller في
class ApplicationController < ActionController::Base
end
 
# app/controllers/admin_controller في
class AdminController < ApplicationController
end
 
# app/controllers/admin/products_controller في
class Admin::ProductsController < AdminController
  def index
  end
end
سيكون ترتيب البحث للإجراء index/products.index هو:
  • /app/views/admin/products
  • /app/views/admin
  • /app/views/application
وهذا يجعل app/views/application/ مكانًا رائعًا لجزئياتك (partials) المشتركة، والتي يمكن تصييرها في القالب ERB كالتالي:
<%# app/views/admin/products/index.html.erb %>
<%= render @products || "empty_list" %>
 
<%# app/views/application/_empty_list.html.erb %>
There are no items in this list <em>yet</em>.
تجنب أخطاء التصيير المضاعف

عاجلًا أم آجلًا، سيشاهد معظم مطوري ريلز رسالة الخطأ "Can only render or redirect once per action" (أي لا يمكن عرض أو إعادة التوجيه إلا مرة واحدة لكل إجراء). في حين أن هذا أمر مزعج، فإنه من السهل نسبيًا إصلاحه. عادة ما يحدث ذلك بسبب سوء فهم أساسي للطريقة التي تعمل التابع render بها.

على سبيل المثال، إليك بعض الشيفرات التي ستؤدي إلى ظهور هذا الخطأ:
def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
  render action: "regular_show"
end
إذا كان الاستدعاء ‎@book.special?‎ يعيد القيمة true، سيبدأ ريلز عملية التصيير لتفريغ المتغير book@ في العرض special_show. ولكن هذا لن يوقف بقية الشيفرة في الإجراء show من العمل، وعندما تصل ريلز إلى نهاية الإجراء، فإنه سيبدأ في بتصيير العرض regular_show، وإلقاء خطأ. الحل بسيط: تأكد من استدعاء render أو redirect مرةً واحدةً في الوقت ذاته في الشيفرة نفسها. شيء آخر يمكن أن يساعد هو استعمال and return. وإليك نسخة مصححة من المثال السابق:
def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show" and return
  end
  render action: "regular_show"
end
تأكد من استخدام and return بدلًا من return && لأن الأخيرة لن تعمل نتيجة أسبقية المعامل في لغة روبي. لاحظ أنَّ التصيير الضمني الذي قام به المتحكم ActionController يكتشف إذا استُدعي render، لذلك ستعمل الشيفرة التالية دون أخطاء:
def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
end
هذا سيصيِّر كتابًا (book) مع ضبط special?‎ مع القالب special_show، بينما ستصيَّر الكتب الأخرى باستخدام القالب show الافتراضي.

استخدام redirect_to

هناك طريقة أخرى لمعالجة الاستجابات العائدة من طلبية HTTP وهي redirect_to. كما شاهدت، يخبر التابع render ريلز أي عرض (أو أصل آخر) سيُستخدَم في إنشاء استجابة. يفعل التابع redirect_to شيئًا مختلفًا تمامًا؛ فهو يخبر المتصفح بإرسال طلبية جديدة لعنوان URL مختلف. على سبيل المثال، يمكنك إعادة التوجيه من أي مكان توجد فيه في شيفرتك إلى فهرس الصور في تطبيقك باستخدام هذا الاستدعاء:
redirect_to photos_url
يمكنك استخدام redirect_back لإعادة المستخدم إلى الصفحة التي جاء منها للتو. يُسحَب هذا الموقع من الترويسة HTTP_REFERER التي لا يُضمَن أن يضبطها المتصفح، لذلك يجب تمرير fallback_location لاستخدامه في هذه الحالة.
redirect_back(fallback_location: root_path)
ملاحظة: لا يتوقف كل من redirect_to و redirect_back ويعودان فورًا من تنفيذ التابع، ولكن ببساطة يعينان استجابات HTTP. ستنفَّذ التعليمات البرمجية التي بعدهما في تابعٍ. يمكنك إيقاف التنفيذ عن طريق استعمال return بصراحة أو باستعمال آلية إيقاف أخرى، إذا لزم الأمر.

الحصول على رمز حالة مختلف لإعادة التوجيه

يستخدم ريلز رمز الحالة HTTP 302 الذي يشير إلى إعادة توجيه مؤقتة (temporary redirect)، عندما تستدعي redirect_to. إذا كنت ترغب في استخدام رمز حالة مختلف، ربما 301، أو إعادة توجيه دائمة، فيمكنك استخدام الخيار :status:
redirect_to photos_path, status: 301
تمامًا مثل الخيار :status للتابع render، يقبل الخيار :status في التابع redirect_to تسميات الترويسة الرقمية أو الرمزية.

الفرق بين render و redirect_to

يعتقد المطورون عديمو الخبرة أحيانًا أنَّ التابع redirect_to هو شيء شبيه بالأمر goto، الذي ينقل التنفيذ من مكان إلى آخر في شيفرة ريلز وهذا خطأ بالتأكيد. تتوقف آنذاك شيفرتك عن العمل وتنتظر طلبًا جديدًا للمتصفح؛ ما يحدث هو فقط أنك أخبرت المتصفح بالطلب الذي يجب أن تقدمه بعد ذلك، عن طريق إرسال رمز الحالة HTTP 302.

فكر في الإجراءين التاليين لمعرفة الفرق:
def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    render action: "index"
  end
end
مع الشيفرة في هذا النموذج، من المحتمل أن يكون هناك مشكلة إذا كان المتغير book@ فارغًا. تذكر أن render :action لا يُشغِّل أي شيفرة في الإجراء المستهدف، لذلك لن يقوم أي شيء بإعداد المتغير books@ الذي قد يتطلبه العرض index على الأرجح. إحدى طرق حل هذه المشكلة هي إعادة التوجيه بدلاً من التصيير:
def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    redirect_to action: :index
  end
end
باستخدام هذه الشيفرة، سيقدم المتصفح طلبًا جديدًا للصفحة index، وستُشغَّل الشيفرة في التابع index، وسيكون كل شيء على ما يرام.

الجانب السلبي الوحيد لهذه الشيفرة هو أنه يتطلب رحلة ذهابًا وإيابًا إلى المتصفح: طَلَبَ المتصفح الإجراء show مع ‎/books/1، فوجد المتحكم أنه لا توجد كتب، لذلك يرسل المتحكم استجابة لإعادة التوجيه بالرمز ‎302 إلى المتصفح لإخباره بالذهاب إلى /books/؛ يمتثل المتصفح ويرسل طلبًا جديدًا مرةً أخرى إلى المتحكم يطلب فيه الإجراء index، ثم يحصل المتحكم على جميع الكتب من قاعدة البيانات ويصيِّر القالب index، ويعيد إرساله إلى المتصفح الذي يعرضه على شاشتك.

في تطبيق صغير، قد لا يمثل وقت التأخير المضاف في هذه الحالة مشكلةً، إلا أنه أمر يجب التفكير فيه إذا كان وقت الاستجابة مصدر قلق. يمكننا أن نوضح طريقةً واحدةً للتعامل مع هذا الأمر بالشكل التالي:
def index
  @books = Book.all
end
 
def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    @books = Book.all
    flash.now[:alert] = "Your book was not found"
    render "index"
  end
end
سيكشف هذا عن عدم وجود كتب مع المعرّف المحدد، وينشر (populate) متغير النسخة ‎@books بكافة الكتب في النموذج، ثم يصيِّر القالب index.html.erb مباشرة ويعيده إلى المتصفح برسالة تنبيه (flash alert message) تخبر المستخدم بما قد حدث.

استخدام head لإنشاء استجابات من الترويسات فقط

يمكن استخدام التابع head لإرسال ردود مع ترويسات فقط للمتصفح. يقبل التابع head عددًا أو رمزًا (اطلع على الجدول المرجعي الموجود في قسم الخيار :status) الذي يمثل رمز حالة HTTP. يفسَر وسيط الخيارات (options argument) على أنه جدول Hash يحوي أسماء وقيم الترويسة. على سبيل المثال، يمكنك إعادة ترويسة خطأٍ فقط عبر:
head :bad_request
سُينتج هذا الترويسة التالية:
HTTP/1.1 400 Bad Request
Connection: close
Date: Sun, 24 Jan 2010 12:15:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Runtime: 0.013483
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache
أو يمكنك استخدام ترويسات HTTP أخرى لنقل معلومات أخرى عبر:
head :created, location: photo_path(@photo)
التي ستنتج:
HTTP/1.1 201 Created
Connection: close
Date: Sun, 24 Jan 2010 12:16:44 GMT
Transfer-Encoding: chunked
Location: /photos/1
Content-Type: text/html; charset=utf-8
X-Runtime: 0.083496
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

هيكلة التخطيطات

عندما تصيِّر ريلز عرضًا على شكل استجابة، فإنها تقوم بذلك عن طريق دمج العرض مع التخطيط الحالي، باستخدام قواعد البحث عن التخطيط الحالي التي تحدثنا عنها مسبقًا في هذا الدليل. داخل أي تخطيط، يمكنك الوصول إلى ثلاثة أدوات للجمع بين أجزاء مختلفة من المخرجات لتشكيل الاستجابة الكلية:

  • وسوم الأصول
  • yield و content_for
  • الجزئيات

مساعدو وسم الأصول

يوفر مساعدو وسم الأصول (Asset tag helpers) توابعًا لتوليد شيفرة HTML تربط عروضًا بتغذية (feed)، وشيفرة جافاسكربت، وأوراق الأنماط، والصور، ومقاطع الفيديو، وتسجيلات الصوت. يوجد ستة مساعدين لوسوم الأصول في ريلز هي:

  • auto_discovery_link_tag
  • javascript_include_tag
  • stylesheet_link_tag
  • image_tag
  • video_tag
  • audio_tag

يمكنك استخدام هذه الوسوم في التخطيطات أو عروض أخرى، على الرغم من أنَّ auto_discovery_link_tag و javascript_include_tag و stylesheet_link_tag تستخدم بشكل شائع ضمن <head> في أي تخطيط.

تحذير: لا يتحقق مساعدو وسم الأصول من وجود الأصول في المواقع المحددة، إذ يفترضون ببساطة أنك تعرف ما تفعله ويولدون الرابط.

ربط التغذيات مع المساعد auto_discovery_link_tag

ينشئ المساعد auto_discovery_link_tag شيفرة HTML يمكن لمعظم المتصفحات وقارئات التغذية استخدامها للكشف عن وجود تغذية RSS أو Atom أو JSON. يأخذ المساعد نوع الرابط (‎:rss، أو ‎:atom، أو ‎:json)، وجدول Hash من الخيارات التي تُمَرَّر إلى url_for، وجدول Hash من الخيارات للوسم:
<%= auto_discovery_link_tag(:rss, {action: "feed"},
  {title: "RSS Feed"}) %>
هناك ثلاثة خيارات متاحة للوسم يمكن استعمالها مع المساعد auto_discovery_link_tag هي:
  • :rel تحدد القيمة rel في الرابط. القيمة الافتراضية هي "alternate".
  • :type يحدد النوع MIME الواضح. سينشئ ريلز نوع MIME مناسبًا تلقائيًا.
  • :title يحدد عنوان الرابط. القيمة الافتراضية هي الأحرف الكبيرة للقيمة :type مثل "ATOM" أو "RSS".

ربط ملفات جافاسكربت مع javascript_include_tag

يعيد المساعد javascript_include_tag الوسم <script> الخاص بلغة HTML لكل مصدر معطى.

إذا كنت تستخدم ريلز مع تمكين خط أنابيب الأصول، سينشئ هذا المساعد رابطًا إلى /assets/javascripts/ بدلاً من public/javascripts الذي كان مستخدَمًا في الإصدارات السابقة من ريلز. ثم هذا الرابط يُخدَم بواسطة خطا أنابيب الأصول (asset pipeline).

يوضع ملف جافاسكربت داخل أحد تطبيقات ريلز أو محرك ريلز في أحد المواقع الثلاثة:

  • app/assets، أو
  • lib/assets، أو
  • vendor/assets.

شُرحَت هذه المواقع بالتفصيل في قسم تنظيم الأصول في دليل خط أنابيب الأصول.

تستطيع تحديد مسار كامل يتعلق بجذر المستند، أو عنوان URL، إذا أردت ذلك. على سبيل المثال، للارتباط بملف JavaScript موجود داخل مجلد يسمى javascripts داخل app/assets، أو lib/assets، أو vendor/assets، يمكنك القيام بذلك:
<%= javascript_include_tag "main" %>
بعد ذلك، سيولد ريلز الوسم <script> التالي:
<script src='/assets/main.js'></script>
ثم يُقدَّم الطلب إلى هذا الأصل بواسطة الجوهرة Sprockets. لتضمين ملفات متعددة مثل app/assets/javascripts/main.js و app/assets/javascripts/columns.js في الوقت نفسه، جرب ما يلي:
<%= javascript_include_tag "main", "columns" %>
لتضمين app/assets/javascripts/main.js و app/assets/javascripts/photos/columns.js:
<%= javascript_include_tag "main", "/photos/columns" %>
لتضمين http://example.com/main.js:
<%= javascript_include_tag "http://example.com/main.js" %>

ربط ملفات CSS باستخدام stylesheet_link_tag

يُعيد المساعد stylesheet_link_tag الوسم <link> لكل مصدر معطى.

إذا كنت تستخدم ريلز مع تمكين خط أنابيب الأصول، فسينشئ هذا المساعد رابطًا إلى /assets/stylesheets/ ثم يُعالَج هذا الرابط بعد ذلك بواسطة الجوهرة Sprockets. يمكن تخزين ملف ورقة الأنماط في أحد المواقع الثلاثة:

  • app/assets، أو
  • lib/assets، أو
  • vendor/assets.
تستطيع تحديد مسار كامل نسبةً إلى جذر المستند، أو عنوان URL، على سبيل المثال، للارتباط بملف ورقة أنماط موجود داخل مجلد يسمى stylesheets داخل المجلد app/assets، أو lib/assets، أو vendor/assets، يمكنك القيام بذلك:
<%= stylesheet_link_tag "main" %>
لتضمين app/assets/stylesheets/main.css و app/assets/stylesheets/columns.css:
<%= stylesheet_link_tag "main", "columns" %>
لتضمين app/assets/stylesheets/main.css و app/assets/stylesheets/photos/columns.css:
<%= stylesheet_link_tag "main", "photos/columns" %>
لتضمين http://example.com/main.css:
<%= stylesheet_link_tag "http://example.com/main.css" %>
بشكل افتراضي، يُنشئ stylesheet_link_tag روابط مع media="screen" rel="stylesheet"‎. يمكنك تجاوز أيَّا من هذه القيم الافتراضية عن طريق تحديد الخيار المناسب (:media، أو ):
<%= stylesheet_link_tag "main_print", media: "print" %>

ربط الصور باستخدام image_tag

ينشئ المساعد image_tag الوسم <img /‎> إلى الملف المحدد. بشكل افتراضي، تُحمَّل الملفات من public/images.

تحذير: لاحظ أنه يجب عليك تحديد امتداد الصورة.
<%= image_tag "header.png" %>
يمكنك توفير مسار للصورة إذا أردت:
<%= image_tag "icons/delete.gif" %>
يمكنك توفير جدول Hash من خيارات HTML الإضافية:
<%= image_tag "icons/delete.gif", {height: 45} %>
يمكنك توفير نص بديل للصورة يُستخدَم إذا أوقف المستخدِم تشغيل الصور في المتصفح. إذا لم تُحدد نص بديل بشكل صريح، فسيُعيَّن افتراضيًا باسم الملف بأحرف كبيرة وبدون اللاحقة. على سبيل المثال، قد تعيد هاتان الصورتان نفس الشيفرة:
<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>
يمكنك أيضًا تحديد حجم خاص للصورة عبر التنسيق "{width} x {height}":
<%= image_tag "home.gif", size: "50x20" %>
بالإضافة إلى الخيارات الخاصة المذكورة أعلاه، يمكنك توفير جدول Hash في النهاية لخيارات HTML القياسية، مثل :class، أو :id، أو :name:
<%= image_tag "home.gif", alt: "Go Home",
                          id: "HomeImage",
                          class: "nav_bar" %>

ربط مقاطع الفيديو مع video_tag

ينشئ المساعد video_tag الوسم <video> الذي وفرته HTML5 لتضمين مقاطع الفيديو. بشكل افتراضي، تُحمَّل الملفات الفيديو من المجلد public/videos.
<%= video_tag "movie.ogg" %>
يُنتِج:
<video src="/videos/movie.ogg" />
مثل image_tag، يمكنك توفير مسار، إما مطلق، أو نسبي إلى المجلد public/videos. بالإضافة إلى ذلك، يمكنك تحديد الحجم عبر التنسيق "‎#{width}x#{height‎}‎" تمامًا مثل image_tag. يمكن أن تحتوي وسوم الفيديو أيضًا على أي من خيارات HTML المحددة في النهاية (مثل الخيار :class، أو :id، أو :name).

يدعم وسم الفيديو أيضًا جميع خيارات <video> من خلال وضع خيارات HTML هذه ضمن جدول Hash من ضمنها:

  • poster: "image_name.png"‎، يوفر صورة لعرضها في واجهة الفيديو قبل بدء تشغيله.
  • autoplay: true، يبدأ تشغيل الفيديو عند تحميل الصفحة.
  • loop: true، يكرر الفيديو بمجرد انتهاء تشغليه.
  • controls: true، يوفر عناصر التحكم التي يوفرها المتصفح للمستخدم للتفاعل مع الفيديو.
  • autobuffer: true، سيحمِّل الفيديو الملف مسبقًا للمستخدم أثناء تحميل الصفحة.
يمكنك أيضًا تحديد عدة مقاطع فيديو لتشغيلها بتمرير مصفوفة من مقاطع الفيديو إلى video_tag:
<%= video_tag ["trailer.ogg", "movie.ogg"] %>
هذا سينتج:
<video>
  <source src="/videos/trailer.ogg">
  <source src="/videos/movie.ogg">
</video>

ربط الملفات الصوتية مع audio_tag

ينشئ المساعد audio_tag الوسم <audio>  على الملف المحدد. بشكل افتراضي، تُحمَّل الملفات من المجلد public/audios.
<%= audio_tag "music.mp3" %>
يمكنك توفير مسار للملف الصوتي إذا كنت ترغب بذلك بالشكل:
<%= audio_tag "music/first_song.mp3" %>
يمكنك أيضًا توفير مجموعة متنوعة من الخيارات، مثل :class و :id ...إلخ.

مثل المساعد video_tag، يحتوي audio_tag على خيارات خاصة هي:

  • autoplay: true، يبدأ تشغيل الصوت عند تحميل الصفحة.
  • controls: true، يوفر عناصر التحكم التي يوفرها المتصفح للمستخدم للتفاعل مع الصوت.
  • autobuffer: true، سيحمل ملف الصوت الملف للمستخدم أثناء تحميل الصفحة.

فهم yield

في سياق أي تخطيط، يعرِّف yield قسمًا حيث يجب إدراج المحتوى من العرض. تتمثل أبسط طريقة لاستخدام هذا في الحصول على yield واحدٍ يُدرَج فيه محتويات العرض قيد التصيير حاليًا:
<html>
  <head>
  </head>
  <body>
  <%= yield %>
  </body>
</html>
يمكنك أيضًا استعمال yield أكثر من مرة ضمن التخطيط:
<html>
  <head>
  <%= yield :head %>
  </head>
  <body>
  <%= yield %>
  </body>
</html>
سيصيَّر دائمًا الجسم الرئيسي في yield غير المسمى. لتصيير محتوى إلى yield محدد، تستطيع استخدم التابع content_for.

استخدام التابع content_for

يسمح لك التابع content_for بإدراج محتوى في كتلة yield محددة في تخطيطك. على سبيل المثال، قد يعمل هذا العرض مع التخطيط التي شاهدته للتو:
<% content_for :head do %>
  <title>A simple page</title>
<% end %>
 
<p>Hello, Rails!</p>
ستكون نتيجة تصيير هذه الصفحة في التخطيط المعطى هي شيفرة HTML التالية:
<html>
  <head>
  <title>A simple page</title>
  </head>
  <body>
  <p>Hello, Rails!</p>
  </body>
</html>
التابع content_for مفيد للغاية عندما يحتوي تخطيطك على مناطق مميزة مثل الأقسام الجانبية (sidebars) والتذييلات التي يجب أن تُدرَج كتلها الخاصة بها. كما يفيد أيضًا في إدراج الوسوم التي تحمّل ملفات جافاسكربت أو ملفات CSS الخاصة بالصفحة في رأس تخطيط آخر عام.

استخدام الجزئيات

القوالب الجزئية - عادة ما تسمى "الجزئيات" (partials) - هي أداة أخرى لتجزئة عملية التصيير إلى أجزاء أكثر قابلية للإدارة. باستخدام جزئية، يمكنك نقل الشيفرة لتصيير جزء معين من الاستجابة لملفها الخاص.

تسمية الجزئيات

لتصيير جزئية كجزء من عرض، فاستخدم التابع render في العرض:
<%= render "menu" %>
سيؤدي ذلك إلى تصيير ملف يدعى ‎_menu.html.erb في تلك النقطة داخل العرض قيد التصيير. لاحظ الشرطة السفلية البادئة لاسم الملف: تبدأ أسماء الجزئيات بشرطة سفلية لتمييزها عن العروض العادية، رغم أنه يشار إليها بدون الشرطة السفلية. هذا صحيح حتى عند سحب جزء من مجلد آخر:
<%= render "shared/menu" %>
ستسحب (pull) هذه الشيفرة في الجزئية من app/views/shared/_menu.html.erb.

استخدام الجزئيات لتبسيط العروض

إحدى الطرق لاستخدام الجزئيات هي معاملتهم بشكل مكافئ للمهام الفرعية (subroutines): كطريقة لنقل التفاصيل خارج العرض بحيث يمكنك التحكم بزمام الأمور بسهولة أكبر. على سبيل المثال، قد يكون لديك عرض يبدو كالتالي:
<%= render "shared/ad_banner" %>
<h1>Products</h1>
<p>Here are a few of our fine products:</p>
...
<%= render "shared/footer" %>
هنا، يمكن أن تحتوي كل من الجزئية ‎_ad_banner.html.erb و ‎_footer.html.erb على محتوى مشترك من قبل العديد من الصفحات في تطبيقك. لست بحاجة إلى الاطلاع على تفاصيل هذه الأقسام عند التركيز على صفحة معينة.

كما هو موضح في الأقسام السابقة من هذا الدليل، تعدُّ yield أداةً قويةً جدًا لتنظيف تخطيطاتك. ضع في اعتبارك أنها شيفرة روبي صرفة، لذلك يمكنك استخدامها في كل مكان تقريبا. على سبيل المثال، يمكننا استخدام الجزئيات لتقليل تعريفات تخطيط نموذج لعدة مصادر متشابهة:

  • الملف users/index.html.erb
<%= render "shared/search_filters", search: @q do |f| %>
  <p>
    Name contains: <%= f.text_field :name_contains %>
  </p>
<% end %>
  • الملف roles/index.html.erb
<%= render "shared/search_filters", search: @q do |f| %>
  <p>
    Title contains: <%= f.text_field :title_contains %>
  </p>
<% end %>
  • الملف shared/_search_filters.html.erb
<%= form_for(search) do |f| %>
  <h1>Search form:</h1>
  <fieldset>
    <%= yield f %>
  </fieldset>
  <p>
    <%= f.submit "Search" %>
  </p>
<% end %>
بالنسبة للمحتوى الذي جرى مشاركته بين جميع الصفحات في تطبيقك، يمكنك استخدام الجزئيات مباشرة من التخطيطات.

التخطيطات الجزئية

بإمكان الجزئية استخدام ملف تخطيط خاص بها فقط، تمامًا كما يمكن للعرض استخدام تخطيط محدد خاص به. على سبيل المثال، يمكنك استدعاء جزئية بالشكل التالي:
<%= render partial: "link_area", layout: "graybar" %>
سيبحث هذا عن جزئية اسمها ‎_link_area.html.erb ويصيِّرها باستخدام التخطيط ‎_graybar.html.erb. لاحظ أن التخطيط الخاص بالجزئيات يتبع نفس نمط تسمية الجزئيات نفسها (البدء بشرطة سفلية)، ويوضع في نفس المجلد مع الجزئية التي ينتمي إليها (وليس في مجلد التخطيطات الرئيسي).

لاحظ أيضًا التحديد :‎partial الصريح هذا مطلوب عند تمرير خيارات إضافية مثل الخيار :layout.

تمرير متغيرات محلية

يمكنك أيضًا تمرير متغيرات محلية إلى الجزئيات، مما يجعلها أكثر قوة ومرونة. على سبيل المثال، يمكنك استخدام هذه التقنية لتقليل التكرار بين الصفحات الجديدة والمعدلَّة، مع الاحتفاظ بقليل من المحتوى المميز:

  • الملف new.html.erb
<h1>New zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
  • الملف edit.html.erb
<h1>Editing zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
  • الملف form.html.erb_
<%= form_for(zone) do |f| %>
  <p>
    <b>Zone name</b><br>
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit %>
  </p>
<% end %>
رغم أن الجزئية نفسها ستصيِّر في كلا العرضين، فسيعيد مساعد إرسال Action View القيمة "Create Zone" للإجراء new والقيمة "Update Zone" للإجراء edit.

لتمرير متغير محلي إلى جزئية في حالات محددة فقط، استخدم local_assigns.

  • الملف index.html.erb
<%= render user.articles %>
  • الملف show.html.erb
<%= render article, full: true %>
  • الملف article.html.erb_
<h2><%= article.title %></h2>
 
<% if local_assigns[:full] %>
  <%= simple_format article.body %>
<% else %>
  <%= truncate article.body %>
<% end %>
تمكن هذه الطريقة استخدام الجزئية دون الحاجة إلى التصريح عن جميع المتغيرات المحلية. تحتوي كل جزئية أيضًا على متغير محلي بنفس اسم الجزئية (باستثناء الشرطة السفلية البادئة). يمكنك تمرير كائن إلى هذا المتغير المحلي عبر الخيار :object:
<%= render partial: "customer", object: @new_customer %>
داخل الجزئية customer، سيشير المتغير customer إلى ‎@new_customer من العرض الرئيسي. إذا كان لديك نسخةً لنموذج لتصييره إلى جزئية، فيمكنك استخدام صيغة مختصرة بالشكل التالي:
<%= render @customer %>
بافتراض أن متغير النسخة ‎@customer يحتوي على نسخة للنموذج Customer، سيستخدم هذا ‎_customer.html.erb لتصييره ثم سيمرر المتغير المحلي customer إلى الجزئية التي ستشير إلى متغير النسخة ‎@customer في العرض الأصلي.

تصيير المجموعات

الجزئيات مفيدة للغاية في تصيير المجموعات. عند تمريرك مجموعة إلى جزئية عبر الخيار :collection، ستُدرَج الجزئية مرة واحدة لكل عضو في المجموعة:

  • الملف index.html.erb
<h1>Products</h1>
<%= render partial: "product", collection: @products %>
  • الملف product.html.erb_
<p>Product Name: <%= product.name %></p>
عندما تُستدعى جزئية ما مع مجموعة متعددة، فإن النسخ الفردية للجزئية يكون لها حق الوصول إلى عضو المجموعة الذي يجري تصييره عبر متغير مسمى بعد الجزئية. في هذه الحالة، الجزئية هي product_، وضمن الجزئية product_، يمكنك الإشارة إلى product للحصول على النسخة التي يجري تصييرها. هناك أيضا اختزال لهذه العملية. بافتراض أن ‎@products عبارة عن مجموعة من نسخ من product، يمكنك ببساطة كتابة هذا في الملف index.html.erb للحصول على النتيجة نفسها:
<h1>Products</h1>
<%= render @products %>
يحدد ريلز اسم الجزئية المراد استخدامها من خلال النظر إلى اسم النموذج في المجموعة. في الواقع، يمكنك حتى إنشاء مجموعة غير متجانسة وعرضها بهذه الطريقة، وسوف يختار ريلز الجزئية المناسبة لكل عضو في المجموعة:
  • الملف index.html.erb
<h1>Contacts</h1>
<%= render [customer1, employee1, customer2, employee2] %>
  • الملف customers/_customer.html.erb
<p>Customer: <%= customer.name %></p>
  • الملف employees/_employee.html.erb
<p>Employee: <%= employee.name %></p>
في هذه الحالة، سيستخدم ريلز الجزئية customer أو الجزئية employee بما بناسب كل عضو في المجموعة. إذا كانت المجموعة فارغة، سيعيد التابع render القيمة nil، لذلك يجب أن يكون من السهل توفير محتوى بديل.
<h1>Products</h1>
<%= render(@products) || "There are no products available." %>

المتغيرات المحلية

لاستخدام اسم متغير محلي مخصص داخل الجزئية، حدد الخيار :as أثناء استدعاء الجزئية:
<%= render partial: "product", collection: @products, as: :item %>
باستخدام هذا التغيير، يمكنك الوصول إلى نسخة للمجموعة ‎@products باعتباره المتغير المحلي item داخل الجزئية. يمكنك أيضًا تمرير متغيرات محلية عشوائية إلى أي جزئية تصييرها مع الخيار المحليين locals: {}‎:
<%= render partial: "product", collection: @products,
           as: :item, locals: {title: "Products Page"} %>
في هذه الحالة، ستحصل الجزئية على حق الوصول إلى المتغير المحلي title ذي القيمة "Products Page".

تنويه: يجعل ريلز أيضًا متغير عداد (counter variable) متاحًا داخل جزئية تستدعى بواسطة المجموعة، يسمى بعد عنوان الجزئية متبوعًا بالعبارة counter_. على سبيل المثال، عند عرض مجموعة باسم products@، يمكن للجزئية product.html.erb_ الوصول إلى المتغير product_counter الذي يفهرس عدد العناصر التي صُيِّرَت داخل العرض المرفق.

يمكنك أيضًا تحديد جزئية ثانية لتصييرها بين نسخ الجزئية الرئيسية باستخدام الخيار :spacer_template.

قوالب الفصل

<%= render partial: @products, spacer_template: "product_ruler" %>
ستصيِّر ريلز الجزئية product_ruler_ (دون تمرير بيانات إليها) بين كل زوج من الجزئيات product_.

مجموعة التخطيطات الجزئية

عند تصيير المجموعات، يمكن أيضًا استخدام الخيار :layout:
<%= render partial: "product", collection: @products, layout: "special_layout" %>
سيُصيَّر التخطيط مع الجزئية لكل عنصر في المجموعة. سيتوفر المتغيران object و object_counter الحاليان في التخطيط أيضًا، تمامًا كما هما داخل الجزئية.

استخدام التخطيطات المتداخلة

قد تجد أن تطبيقك يتطلب تخطيطًا يختلف قليلًا عن تخطيط تطبيقك العادي لدعم متحكم معين. بدلًا من تكرار التخطيط الرئيسي وتعديله، يمكنك القيام بذلك باستخدام التخطيطات المتداخلة (nested layouts، تسمى أحيانًا القوالب الفرعية [sub-templates]). هنا مثال:

لنفترض أن لديك التخطيط ApplicationController التالي:

  • الملف app/views/layouts/application.html.erb
<html>
<head>
  <title><%= @page_title or "Page Title" %></title>
  <%= stylesheet_link_tag "layout" %>
  <style><%= yield :stylesheets %></style>
</head>
<body>
  <div id="top_menu">Top menu items here</div>
  <div id="menu">Menu items here</div>
  <div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
</body>
</html>
في الصفحات المولدة بواسطة NewsController، تريد إخفاء القائمة العلوية وإضافة قائمة يمينية:
  • الملف app/views/layouts/news.html.erb
<% content_for :stylesheets do %>
  #top_menu {display: none}
  #right_menu {float: right; background-color: yellow; color: black}
<% end %>
<% content_for :content do %>
  <div id="right_menu">Right menu items here</div>
  <%= content_for?(:news_content) ? yield(:news_content) : yield %>
<% end %>
<%= render template: "layouts/application" %>
انتهينا! ستَستخدم العروض News التخطيط الجديد، الذي يخفي القائمة العلوية ويضيف قائمة يمينية جديدة في العنصر <div> ذي المعرف content.

هناك عدة طرق للحصول على نتائج مشابهة مع أنظمة التخطيط الفرعية المختلفة باستخدام هذه التقنية. لاحظ أنه لا يوجد حد في مستويات التداخل. يمكن استخدام التابع ActionView::render عبر render template: 'layouts/news'‎ لتأسيس تخطيط جديدة في التخطيط News. إذا كنت متأكدًا من أنك لن تفرِّع (subtemplate) قالبًا من التخطيط News، فيمكنك تبديل yield مكان content_for?(:news_content) ? yield(:news_content) : yield ببساطة.

مصادر