الفرق بين المراجعتين لصفحة: «Rails/engines»

من موسوعة حسوب
لا ملخص تعديل
مراجعة وتدقيق
 
(1 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة)
سطر 123: سطر 123:
يوجد ضمن المجلد app/controllers المجلد blorgh الذي يحتوي على ملف يسمى application_controller.rb. سيوفر هذا الملف أي وظائف مشتركة لوحدات التحكم في المحرك. المجلد blorgh هو المكان الذي سوف تذهب إليه وحدات التحكم الأخرى للمحرك. بوضعها ضمن هذا المجلد الذي يملك مجال اسم، فإنك تمنعهم من التضارب مع وحدات التحكم المسماة باسم مماثل داخل محركات أخرى أو حتى داخل التطبيق.
يوجد ضمن المجلد app/controllers المجلد blorgh الذي يحتوي على ملف يسمى application_controller.rb. سيوفر هذا الملف أي وظائف مشتركة لوحدات التحكم في المحرك. المجلد blorgh هو المكان الذي سوف تذهب إليه وحدات التحكم الأخرى للمحرك. بوضعها ضمن هذا المجلد الذي يملك مجال اسم، فإنك تمنعهم من التضارب مع وحدات التحكم المسماة باسم مماثل داخل محركات أخرى أو حتى داخل التطبيق.


'''ملاحظة''': تُسمى صنف ApplicationController داخل المحرك مثل تطبيق Rails لتسهيل عملية تحويل التطبيقات إلى محركات.
'''ملاحظة''': يُسمى الصنف <code>ApplicationController</code> داخل المحرك مثل أي تطبيق ريلز لتسهيل عملية تحويل التطبيقات إلى محركات.


ملاحظة: نظرًا لطريقة Ruby  في البحث المستمر، قد تواجهك في حالة توارث فيها وحدة تحكم المحرك من وحدة تحكم التطبيق الرئيسية وليس وحدة تحكم التطبيق الخاصة بالمحرك. روبي قادرة على حل ثابت ApplicationController، ومن ثم لا تُشغل آلية التحميل التلقائي. انظر القسم عندما لا تفقد الثوابت و دليل التحميل التلقائي وإعادة التحميل للثابت لمزيد من التفاصيل. أفضل طريقة لمنع حدوث ذلك هي استخدام require_ dependency لضمان أن تُحمل وحدة تحكم التطبيق الخاصة بالمحرك. فمثلا:
'''ملاحظة''': نظرًا لطريقة [[Ruby|روبي]] في البحث المستمر، قد تواجهك حالة يرث فيها متحكم المحرك الخاص بك من وحدة تحكم التطبيق الرئيسية وليس وحدة تحكم التطبيق الخاصة بالمحرك. [[Ruby|روبي]] قادرة على استبيان الثابت <code>ApplicationController</code>، ومن ثم لا تُشغل آلية التحميل التلقائي. انظر القسم «<nowiki/>[[Rails/autoloading and reloading constants#.D8.B9.D9.86.D8.AF.D9.85.D8.A7 .D9.84.D8.A7 .D8.AA.D9.83.D9.88.D9.86 .D8.A7.D9.84.D8.AB.D9.88.D8.A7.D8.A8.D8.AA .D9.85.D9.81.D9.82.D9.88.D8.AF.D8.A9|عندما لا تكون الثوابت مفقودة]]» من دليل «<nowiki/>[[Rails/autoloading and reloading constants|التحميل التلقائي وإعادة تحميل الثوابت]]» لمزيد من التفاصيل. أفضل طريقة لمنع حدوث ذلك هي استخدام <code>require_dependency</code> لضمان تحميل وحدة تحكم التطبيق الخاصة بالمحرك. فمثلًا:<syntaxhighlight lang="rails">
 
# app/controllers/blorgh/articles_controller.rb:
<nowiki>#</nowiki> app/controllers/blorgh/articles_controller.rb:
<syntaxhighlight lang="rails">
require_dependency "blorgh/application_controller"
require_dependency "blorgh/application_controller"
 
module Blorgh
module Blorgh
  class ArticlesController < ApplicationController
    ...
  end
end


 class ArticlesController < ApplicationController
   ...
 end
End
</syntaxhighlight>
</syntaxhighlight>


لا تتطلب الاستخدام لأنها ستفشل إعادة التحميل التلقائي للفئات في بيئة التطوير - باستخدام require_dependency يضمن أن تُحمل الفئات وتفرغ بالطريقة الصحيحة.
'''تحذير''': لا تستعمل <code>require</code> لأنها ستفشل إعادة التحميل التلقائي للأصناف في بيئة التطوير. استعمال <code>require_dependency</code> يضمن أن تُحمَّل الأصناف وتفرَّغ بالطريقة الصحيحة.


وأخيرا، يحتوي دليل app/views على مجلد تخطيطات، والذي يحتوي على ملف في blorgh / application.html.erb. يتيح لك هذا الملف تحديد تخطيط للمحرك. إذا كان سيستخدم هذا المحرك كمحرك مستقل، فستضيف أي تخصيص لتخطيطه في هذا الملف  بدلاً من ملف التطبيق app/views/layouts/application.html.erb.
وأخيرا، يحتوي المجلد app/views على المجلد layouts، والذي يحتوي على ملف في blorgh/application.html.erb. يتيح لك هذا الملف تحديد تخطيط للمحرك. إذا كان سيُستخدَم هذا المحرك كمحرك مستقل (stand-alone engine)، فستضيف أي تخصيص لتخطيطه في هذا الملف بدلًا من ملف التطبيق app/views/layouts/application.html.erb.


إذا كنت لا ترغب في فرض تخطيط على مستخدمي المحرك، يمكنك حذف هذا الملف والإشارة إلى تخطيط مختلف في وحدات التحكم في المحرك الخاص بك.
إذا كنت لا ترغب في فرض تخطيط على مستخدمي المحرك، يمكنك حذف هذا الملف والإشارة إلى تخطيط مختلف في وحدات التحكم في المحرك الخاص بك.


==== دليل bin ====
==== المجلد bin ====
يحتوي هذا الدليل على ملف واحد، bin / rails، والذي يمكّنك من استخدام الأوامر الفرعية ومولدات rails مثلما تفعل داخل التطبيق. هذا يعني أنك ستتمكن من إنشاء وحدات تحكم ونماذج جديدة لهذا المحرك بسهولة بالغة عن طريق تشغيل الأوامر مثل:
يحتوي هذا المجلد على ملف واحد، bin/rails، والذي يمكّنك من استخدام أوامر <code>rails</code> الفرعية والمولدات مثلما تفعل داخل أي تطبيق ريلز. هذا يعني أنك ستتمكن من إنشاء وحدات تحكم ونماذج جديدة لهذا المحرك بسهولة بالغة عن طريق تشغيل الأوامر مثل:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails g model
$ bin/rails g model
</syntaxhighlight>
</syntaxhighlight>


ضع في اعتبارك، بالطبع، أن أي شيء يُنشأ باستخدام هذه الأوامر داخل المحرك يحتوي على isolate_namespace في صنف المحرك سيسمى.
ضع في اعتبارك، بالطبع، أنّ أي شيء يُولَّد باستخدام هذه الأوامر داخل المحرك الذي يحتوي على <code>isolate_namespace</code> في الصنف <code>Engine</code> سيوضع اسمه ضمن مجال اسم.


==== دليل الاختبار ====
==== المجلد test ====
دليل test هو المكان الذي ستذهب إليه اختبارات المحرك. لاختبار المحرك، هناك نسخة مقطوعة من تطبيق Rails مضمنه في test/dummy. سيركب هذا التطبيق المحرك في ملف test / dummy / config / routes.rb:
المجلد test هو المكان الذي ستذهب إليه اختبارات المحرك. لاختبار المحرك، هناك نسخة مختصرة من تطبيق ريلز مضمَّن فيه في test/dummy. سيوصل (mount) هذا التطبيق المحرك في الملف test/dummy/config/routes.rb:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
Rails.application.routes.draw do
Rails.application.routes.draw do
 
  mount Blorgh::Engine => "/blorgh"
 mount Blorgh::Engine => "/blorgh"
end
 
End
</syntaxhighlight>
</syntaxhighlight>


يقوم هذا السطر بتثبيت المحرك على المسار / blorgh، مما يجعل الوصول إليه ممكناً من خلال التطبيق على هذا المسار فقط.
يقوم هذا السطر بوصل المحرك على المسار /blorgh، مما يجعل الوصول إليه ممكنًا من خلال التطبيق على هذا المسار فقط.


يوجد داخل دليل الاختبار دليل test/integration، حيث يجب وضع اختبارات التكامل للمحرك. يمكن إنشاء مجلدات أخرى في دليل test أيضًا. على سبيل المثال، قد ترغب في إنشاء دليل test/models لاختبارات نموذجك.
يوجد داخل المجلد test المجلد test/integration، حيث يجب وضع [[Rails/testing#.D8.A7.D8.AE.D8.AA.D8.A8.D8.A7.D8.B1 .D8.A7.D9.84.D8.AA.D9.83.D8.A7.D9.85.D9.84|اختبارات التكامل]] للمحرك. يمكن إنشاء مجلدات أخرى في المجلد test أيضًا. على سبيل المثال، قد ترغب في إنشاء المجلد test/models لاختبارات نموذجك.


== توفير وظائف المحرك ==
== توفير وظائف المحرك ==
يوفر المحرك الذي يغطي هذا الدليل تقديم المقالات ووظيفة التعليق ويتبع سلسلة مماثلة إلى دليل الخطوات الأولى، مع بعض التعديلات الجديدة.
يوفر المحرك الذي يغطيه هذا الدليل وظيفة إرسال المقالات ووظيفة التعليق ويتبع نفس الأسلوب المذكور في دليل [[Rails/getting started|البدء مع ريلز]]، مع بعض التعديلات الجديدة.


=== توليد مصادر المقال ===
=== توليد مصادر للمقال ===
أول شيء يُنشئ لمحرك المدونة هو نموذج Article ووحدة التحكم ذات الصلة. لتوليد ذلك بسرعة، يمكنك استخدام مولد سقالة Rails.
أول شيء يراد توليده لمحرك مدونة هو النموذج <code>Article</code> ووحدة التحكم ذات الصلة. لتوليد ذلك بسرعة، يمكنك استخدام المولد <code>scaffold</code> في ريلز بالشكل التالي:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails generate scaffold article title:string text:text
$ bin/rails generate scaffold article title:string text:text
سطر 180: سطر 173:


سينتج هذا الأمر هذه المعلومات:
سينتج هذا الأمر هذه المعلومات:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="text">
invoke  active_record
invoke active_record
 
create   db/migrate/[timestamp]_create_blorgh_articles.rb
create    db/migrate/[timestamp]_create_blorgh_articles.rb
create   app/models/blorgh/article.rb
 
invoke   test_unit
create    app/models/blorgh/article.rb
create     test/models/blorgh/article_test.rb
 
create     test/fixtures/blorgh/articles.yml
invoke    test_unit
invoke resource_route
 
route   resources :articles
create      test/models/blorgh/article_test.rb
invoke scaffold_controller
 
create   app/controllers/blorgh/articles_controller.rb
create      test/fixtures/blorgh/articles.yml
invoke   erb
 
create     app/views/blorgh/articles
invoke  resource_route
create     app/views/blorgh/articles/index.html.erb
 
create     app/views/blorgh/articles/edit.html.erb
route    resources :articles
create     app/views/blorgh/articles/show.html.erb
 
create     app/views/blorgh/articles/new.html.erb
invoke  scaffold_controller
create     app/views/blorgh/articles/_form.html.erb
 
invoke   test_unit
create    app/controllers/blorgh/articles_controller.rb
create     test/controllers/blorgh/articles_controller_test.rb
 
invoke   helper
invoke    erb
create     app/helpers/blorgh/articles_helper.rb
 
invoke test_unit
create      app/views/blorgh/articles
create   test/application_system_test_case.rb
 
create   test/system/articles_test.rb
create      app/views/blorgh/articles/index.html.erb
invoke assets
 
invoke   js
create      app/views/blorgh/articles/edit.html.erb
create     app/assets/javascripts/blorgh/articles.js
 
invoke   css
create      app/views/blorgh/articles/show.html.erb
create     app/assets/stylesheets/blorgh/articles.css
 
invoke css
create      app/views/blorgh/articles/new.html.erb
create   app/assets/stylesheets/scaffold.css
 
create      app/views/blorgh/articles/_form.html.erb
 
invoke    test_unit
 
create      test/controllers/blorgh/articles_controller_test.rb
 
invoke    helper
 
create      app/helpers/blorgh/articles_helper.rb
 
invoke  test_unit
 
create    test/application_system_test_case.rb
 
create    test/system/articles_test.rb
 
invoke  assets
 
invoke    js
 
create      app/assets/javascripts/blorgh/articles.js
 
invoke    css
 
create      app/assets/stylesheets/blorgh/articles.css
 
invoke  css
 
create    app/assets/stylesheets/scaffold.css
</syntaxhighlight>
</syntaxhighlight>


أولًا يستدعي مولد السقالة مولد active_record، الذي ينشئ الترحيل ونموذج للمصدر. ومع ذلك، لاحظ أن الترحيل يدعى Create_blorgh_articles بدلاً من create_articles المعتاد. هذا بسبب التابع isolate_namespace الذي يسمى في تعريف صنف Blorgh :: Engine. ويعتبر النموذج هنا ايضًا مساحة اسم، حيث يُوضع في app / models / blorgh / article.rb بدلاً من app / models / article.rb بسبب استدعاء isolate_namespace داخل صنف Engine.
أول شيء يستدعيه المولد <code>scaffold</code> هو المولد <code>active_record</code>، الذي يولد تهجيرًا (migration) ونموذجًا للمصدر. ومع ذلك، لاحظ أنَّ التهجير يدعى <code>create_blorgh_articles</code> بدلًا من <code>create_articles</code> المتوقع. هذا بسبب التابع <code>isolate_namespace</code> الذي استدعي في تعريف الصنف <code>Blorgh::Engine</code>. وأضيف اسم النموذج هنا ايضًا إلى مجال اسم، حيث يُوضع في app/models/blorgh/article.rb بدلًا من app/models/article.rb بسبب استدعاء <code>isolate_namespace</code> داخل الصنف <code>Engine</code>.


بعد ذلك، يُستدعى مولد test_unit لهذا النموذج، مما يؤدي إلى اختبار النموذج في test / models / blorgh / article_test.rb (بدلاً من test/models/article_test.rb) و تثبيته في test/fixtures/blorgh/articles.yml ( بدلًا من test/fixtures/articles.yml).
بعد ذلك، يُستدعَى المولد <code>test_unit</code> لهذا النموذج، مما يؤدي إلى توليد اختبار لنموذج في test/models /blorgh/article_test.rb (بدلًا من test/models/article_test.rb) و<nowiki/>[[Rails/testing#.D8.AD.D9.82.D9.8A.D9.82.D8.A9 .D8.AA.D8.AD.D8.B6.D9.8A.D8.B1.D8.A7.D8.AA .D8.A7.D9.84.D8.A7.D8.AE.D8.AA.D8.A8.D8.A7.D8.B1 .28Fixtures.29|تحضير اختبار]] في test/fixtures/blorgh/articles.yml (بدلًا من test/fixtures/articles.yml).


بعد ذلك، يُدرج سطر للمصدر في ملف config / routes.rb للمحرك. هذا السطر هو ببساطة resources :articles، وتحويل ملف config / routes.rb للمحرك إلى هذا:
بعد ذلك، يُدرج سطر للمصدر في الملف config/routes.rb للمحرك. هذا السطر هو ببساطة <code>resources :articles</code>، وتحويل الملف config/routes.rb للمحرك إلى ما يلي:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
Blorgh::Engine.routes.draw do
Blorgh::Engine.routes.draw do
 
  resources :articles
 resources :articles
 
end
end
</syntaxhighlight>
</syntaxhighlight>


لاحظ هنا أن المسارات تُرسم على كائن Blorgh :: Engine بدلاً من صنف YourApp :: Application. هذا هو أن مسارات المحرك تقتصر على المحرك نفسه ويمكن تثبيتها في نقطة محددة كما هو موضح في قسم دليل الاختبار. كما يتسبب في عزل مسارات المحرك عن تلك المسارات الموجودة داخل التطبيق. يصف قسم المسارات من هذا الدليل بالتفصيل.
لاحظ هنا أن المسارات الموجهة تعتمد على الكائن <code>Blorgh::Engine</code> بدلًا من الصنف <code>YourApp::Application</code>. هذا هو سبب أن مسارات التوجيه للمحرك تقتصر على المحرك نفسه ويمكن وصلها في نقطة محددة كما هو موضح في قسم «المجلد test». كما يتسبب في عزل مسارات التوجيه للمحرك عن تلك المسارات الموجودة داخل التطبيق. يصف قسم مسارات التوجيه من هذا الدليل ذلك بالتفصيل.


بعد ذلك، يستدعى مولد scaffold_controller، وتُولد وحدة تحكم تسمى Blorgh :: ArticlesController (في app/controllers/blorgh/articles_controller.rb) والواجهات ذات الصلة في app/views/blorgh/articles.
بعد ذلك، يُستدعَى المولد <code>scaffold_controller</code>، وتُولد وحدة تحكم تسمى <code>Blorgh::ArticlesController</code> (في app/controllers/blorgh/articles_controller.rb) والواجهات ذات الصلة في app/views/blorgh/articles.


هذا المولد يولد أيضا اختبار لوحدة التحكم (test/controllers/blorgh/articles_controller_test.rb) والمساعد (app/helpers/blorgh/articles_helper.rb).
هذا المولد يولد أيضا اختبارًا لوحدة التحكم (test/controllers/blorgh/articles_controller_test.rb) ومساعدًا (app/helpers/blorgh/articles_helper.rb).


أُنشئ كل شيء أنشأه هذا المولد بدقة. وعُرفت صنف وحدة التحكم في وحدة Blorgh:
كل شيء أنشأه هذا المولد يكون ضمن مجال اسم. وعُرِّف صنف وحدة التحكم في الوحدة <code>Blorgh</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
 
  class ArticlesController < ApplicationController
 class ArticlesController < ApplicationController
    ...
 
  end
   ...
end
 
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


ملاحظة: ترث الصنف ArticlesController من Blorgh :: ApplicationController ، وليس التطبيق ApplicationController.
'''ملاحظة''': يرث الصنف <code>ArticlesController</code> من <code>Blorgh::ApplicationController</code>، وليس من <code>ApplicationController</code> الخاص بالتطبيق.


المساعد أيضا داخل app/helpers/blorgh/articles_helper.rb هو أيضا namespaced:
يكون اسم المساعد داخل app/helpers/blorgh/articles_helper.rb ضمن مجال أسماء أيضًا:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
  module ArticlesHelper
    ...
  end
end
</syntaxhighlight>


 module ArticlesHelper
يساعد ذلك على منع التعارضات مع أي محرك أو تطبيق آخر قد يكون له مصدر مقالة (article resource) أيضًا.
 
   ...
 
 end
 
End
</syntaxhighlight>


يساعد ذلك على منع التعارضات مع أي محرك أو تطبيق آخر قد يكون له مصدر مقالة أيضًا.
وأخيرًا، يُنشأ أصول لهذا المصدر في ملفين: app/assets/javascripts/blorgh/articles.js و app/assets/stylesheets/blorgh/articles.css. سترى كيفية استخدام هذه بعد قليل.


وأخيرًا، يُنشأ أصول لهذا المصدر في ملفين: app / assets / javascripts / blorgh / articles.js و app / assets / stylesheets / blorgh / articles.css. سترى كيفية استخدام هذه في وقت لاحق قليلا.
يمكنك أن ترى ما الذي يملكه المحرك حتى الآن عبر تنفيذ الأمر <code>bin/rails db:migrate</code> في جذر المحرك الخاص بنا لتنفيذ التهجير المُولَّد عبر المولِّد <code>scaffold</code>، ثم تنفيذ الأمر <code>rails server</code> في test/dummy. عند فتح الرابط http://localhost:3000/blorgh/articles في المتصفح، سترى ما الذي وُلِّد حتى الآن. لقد ولَّدت إلى الآن الدفعة الأولى من الدوال لأول محرك خاص بك.


إذا كنت تفضل اللعب في وحدة التحكم، فستعمل لوحة المفاتيح أيضًا مثل تطبيق Rails. تذكر: نموذج Article هو ذو أسماء، لذلك للإشارة إليه يجب أن تسميته كـ Blorgh :: Article.
إذا كنت تفضل اللعب في وحدة التحكم عوضًا عن ذلك، فسيعمل الأمر <code>rails console</code> أيضًا مثل عمله في أي تطبيق ريلز. تذكر أن النموذج <code>Article</code> قابع ضمن مجال اسم، لذلك أخذ ذلك بالحسبان للإشارة إليه مثل <code>Blorgh::Article</code>.
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
>> Blorgh::Article.find(1)
>> Blorgh::Article.find(1)
=> #<Blorgh::Article id: 1 ...>


=> #<Blorgh::Article id: 1 ...>
</syntaxhighlight>
</syntaxhighlight>


شيء واحد نهائي هو أن مصدر articles  لهذا المحرك يجب أن يكون هو جذر المحرك. عندما يذهب شخص ما إلى مسار الجذر حيث يُركب المحرك، يجب أن  يُعرض قائمة بالمقالات. يمكن إجراء ذلك إذا أُدرج هذا السطر في ملف config / routes.rb داخل المحرك:
شيء واحد نهائي هو أن المصدر <code>articles</code> لهذا المحرك يجب أن يكون هو جذر المحرك. عندما يذهب شخص ما إلى مسار الجذر حيث يوصل المحرك، يجب أن يُعرَّض عليه قائمة بالمقالات. يمكن إجراء ذلك إذا أُدرج السطر التالي في الملف config/routes.rb داخل المحرك:<syntaxhighlight lang="rails">
 
root to: "articles#index"
root to: "articles#index"
</syntaxhighlight>الآن سيحتاج الأشخاص فقط إلى جذر المحرك لرؤية جميع المقالات، بدلًا من زيارة ‎/articles. هذا يعني أنه بدلًا من http://localhost:&#x20;3000/blorgh/articles<nowiki/>، ما عليك سوى الانتقال إلى http://localhost:3000/blorgh الآن.


الآن سيحتاج الناس فقط إلى جذر المحرك لرؤية جميع المقالات ، بدلاً من زيارة /articles. هذا يعني أنه بدلاً من http: // localhost: 3000 / blorgh / articles، ما عليك سوى الانتقال إلى http: // localhost: 3000 / blorgh الآن.
=== توليد مصدر للتعليقات ===
 
والآن بعد أن استطاع المحرك إنشاء مقالات جديدة، فمن المنطقي إضافة وظيفة التعليق أيضًا. للقيام بذلك، ستحتاج إلى إنشاء نموذج تعليق، ووحدة تحكم في التعليق، ثم تعديل قوائم المقالات لعرض التعليقات والسماح للأشخاص بإنشاء نماذج جديدة.
=== توليد مصدر التعليقات ===
والآن بعد أن استطاع المحرك إنشاء مقالات جديدة ، فمن المنطقي إضافة وظيفة التعليق أيضًا. للقيام بذلك، ستحتاج إلى إنشاء نموذج تعليق، ووحدة تحكم في التعليق، ثم تعديل قوائم المقالات لعرض التعليقات والسماح للأشخاص بإنشاء نماذج جديدة.
 
من جذر التطبيق، شغل مولد النموذج. أخبره بإنشاء نموذج تعليق، مع الجدول ذي الصلة الذي به عمودين: عدد صحيح من article_id و text عمود نصي.


من جذر التطبيق، شغل مولد النموذج. أخبره بإنشاء نموذج تعليق، مع الجدول ذي الصلة الذي به عمودين: العمود <code>article_id</code> الذي يمثِّل عددًا صحيحًا والعمود <code>text</code> الذي يمثِّل نصًّا.<syntaxhighlight lang="shell">
$ bin/rails generate model Comment article_id:integer text:text
$ bin/rails generate model Comment article_id:integer text:text
 
</syntaxhighlight>مخرجات تنفيذ هذا الأمر هي:
هذا سوف يخرج ما يلي:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
invoke  active_record
invoke active_record
 
create   db/migrate/[timestamp]_create_blorgh_comments.rb
create    db/migrate/[timestamp]_create_blorgh_comments.rb
create   app/models/blorgh/comment.rb
 
invoke   test_unit
create    app/models/blorgh/comment.rb
create     test/models/blorgh/comment_test.rb
 
create     test/fixtures/blorgh/comments.yml
invoke    test_unit
 
create      test/models/blorgh/comment_test.rb
 
create      test/fixtures/blorgh/comments.yml
</syntaxhighlight>
</syntaxhighlight>


سيولد هذا الاستدعاء فقط ملفات النماذج الضرورية التي تحتاجها، حيث تسمى الملفات تحت دليل blorgh وتُنشئ صنف نموذجية تسمى Blorgh :: Comment. الآن شغل الترحيل لإنشاء جدول blorgh_comments الخاص بنا:
سيولد هذا الاستدعاء فقط ملفات النماذج الضرورية التي تحتاجها، حيث تسمى الملفات تحت المجلد blorgh ويُنشئ صنف نموذج (model class) يسمى <code>Blorgh::Comment</code>. الآن شغل التهجير لإنشاء الجدول <code>blorgh_comments</code> الخاص بنا:<syntaxhighlight lang="shell">
 
$ bin/rails db:migrate
$ bin/rails db:migrate
</syntaxhighlight>لإظهار التعليقات على إحدى المقالات، عدّل app/views/blorgh/articles/show.html.erb وأضف هذا السطر قبل الرابط "Edit" (تعديل):
<syntaxhighlight lang="html">
<h3>Comments</h3>
<%= render @article.comments %>


لإظهار التعليقات على إحدى المقالات، عدّل app/views/blorgh/articles/show.html.erb وأضف هذا السطر قبل رابط "التعديل":
<syntaxhighlight lang="rails">
<nowiki><h3>Comments</h3></nowiki>
<%= render @article.comments %>
</syntaxhighlight>
</syntaxhighlight>


سوف يتطلب هذا السطر أن يكون هناك رابطة has_many للتعليقات المحددة في النموذج Blorgh :: Article، والتي لا توجد الآن. لتحديد واحد، افتح app / models / blorgh / article.rb وأضف هذا السطر إلى النموذج:
سوف يتطلب هذا السطر أن يكون هناك الارتباط [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.AA.D8.B9.D8.AF.D8.AF.D9.8A.D8.A9 .28has many.29|has_many]] للتعليقات المحددة في النموذج <code>Blorgh::Article</code>، والتي لا توجد الآن. لتحديد واحد، افتح app/models/blorgh/article.rb وأضف السطر التالي إلى النموذج:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
has_many :comments
has_many :comments
</syntaxhighlight>
</syntaxhighlight>


تحويل النموذج إلى هذا:
تحويل النموذج إلى:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
 
  class Article < ApplicationRecord
 class Article < ApplicationRecord
    has_many :comments
 
  end
   has_many :comments
end
 
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


نظرًا لتمييز has_many داخل صنف موجودة داخل وحدة Blorgh، سيعرف Rails أنك تريد استخدام نموذج Blorgh :: Comment لهذه الكائنات، لذا لا داعي لتحديد ذلك باستخدام خيار: class_name هنا.
'''ملاحظة''': نظرًا لتعريف الارتباط [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.AA.D8.B9.D8.AF.D8.AF.D9.8A.D8.A9 .28has many.29|has_many]] داخل صنف موجودة داخل الوحدة <code>Blorgh</code>، سيعرف ريلز أنك تريد استخدام نموذج <code>Blorgh::Comment</code> لهذه الكائنات، لذا لا داعي لتحديد ذلك باستخدام الخيار ‎<code>:class_name</code> هنا.


بعد ذلك، يجب أن يكون هناك نموذج حتى يمكن إنشاء التعليقات على المقالة. لإضافة هذا، ضع هذا السطر أسفل الدعوة لتقديم article.comments@ في app/views/blorgh/articles/show.html.erb:
بعد ذلك، يجب أن يكون هناك نموذج حتى يمكن إنشاء التعليقات على المقالة. لتوفير هذا، ضع السطر التالي أسفل الاستدعاء <code>render @article.comments</code> في app/views/blorgh/articles/show.html.erb:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<%= render "blorgh/comments/form" %>
<%= render "blorgh/comments/form" %>
</syntaxhighlight>
</syntaxhighlight>


التالى، جزئيًا أن هذا السطر سيجعل الاحتياجات موجودة. لإنشاء دليل جديد في app/views/blorgh/comments وفيه ملف جديد يسمى form.html.erb_ الذي يحتوي على هذا المحتوى لإنشاء الجزء المطلوب:
بعد ذلك، تحتاج الجزئية (partial) التي سيصيرها هذا السطر إلى أن تكون موجودة. أنشئ مجلدًا جديدًا في app/views/blorgh/comments وأنشئ ملفًا فيه باسم form.html.erb_ الذي يحتوي على المحتوى التالي لإنشاء الجزئية المطلوبة:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="html">
<nowiki><h3>New comment</h3></nowiki>
<h3>New comment</h3>
 
<%= form_with(model: [@article, @article.comments.build], local: true) do |form| %>
<%= form_with(model: [@article, @article.comments.build], local: true) do |form| %>
 
  <p>
 <nowiki><p></nowiki>
    <%= form.label :text %><br>
 
    <%= form.text_area :text %>
   <%= form.label :text %><nowiki><br></nowiki>
  </p>
 
  <%= form.submit %>
   <%= form.text_area :text %>
 
 <nowiki></p></nowiki>
 
 <%= form.submit %>
 
<% end %>
<% end %>
</syntaxhighlight>
</syntaxhighlight>


عندما يرسل هذا النموذج، فإنه سيحاول تنفيذ طلب POST إلى المسار/articles/:article_id/comments داخل المحرك. هذا المسار غير موجود في الوقت الحالي، ولكن يُنشأ عن طريق تغيير سطر resources:articles داخل config / routes.rb إلى هذه السطور:
عندما يرسل هذا النموذج، فإنه سيحاول تنفيذ طلب POST إلى المسار/articles/:article_id/comments داخل المحرك. هذا المسار غير موجود في الوقت الحالي، ولكن يُنشأ عن طريق تغيير السطر <code>resources :articles</code> داخل config/routes.rb إلى هذه السطور:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
resources :articles do
resources :articles do
 
  resources :comments
 resources :comments
end
 
End
</syntaxhighlight>
</syntaxhighlight>


يؤدي هذا إلى إنشاء مسار متداخل للتعليقات، وهو ما يتطلبه النموذج.
يؤدي هذا إلى إنشاء مسار متداخل للتعليقات، وهو ما يتطلبه النموذج.


المسار الآن موجود، ولكن وحدة التحكم التي تذهب إلى هذا المسار لا يوجد. لإنشائه، شغل هذا الأمر من جذر التطبيق:
أصبح المسار الآن موجودًا، ولكن وحدة التحكم التي تذهب إلى هذا المسار غير موجودة. لإنشائها، نفذ الأمر التالي من جذر التطبيق:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails g controller comments
$ bin/rails g controller comments
سطر 402: سطر 333:


هذا سوف يولد الأشياء التالية:
هذا سوف يولد الأشياء التالية:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="text">
create  app/controllers/blorgh/comments_controller.rb
create app/controllers/blorgh/comments_controller.rb
 
invoke erb
invoke  erb
exist   app/views/blorgh/comments
 
invoke test_unit
exist    app/views/blorgh/comments
create   test/controllers/blorgh/comments_controller_test.rb
 
invoke helper
invoke  test_unit
create   app/helpers/blorgh/comments_helper.rb
 
invoke assets
create    test/controllers/blorgh/comments_controller_test.rb
invoke   js
 
create     app/assets/javascripts/blorgh/comments.js
invoke  helper
invoke   css
 
create     app/assets/stylesheets/blorgh/comments.css
create    app/helpers/blorgh/comments_helper.rb
 
invoke  assets
 
invoke    js
 
create      app/assets/javascripts/blorgh/comments.js
 
invoke    css
 
create      app/assets/stylesheets/blorgh/comments.css
</syntaxhighlight>
</syntaxhighlight>


سيقدم النموذج طلب POST إلى / articles /: article_id / comments، والتي تتوافق مع إجراء create في Blorgh :: CommentsController. يجب إنشاء هذا الإجراء، والذي يُنفذ عن طريق وضع السطور التالية داخل تعريف الصنف في app/controllers/blorgh/comments_controller.rb:
سيقدم النموذج طلب POST إلى /articles/:article_id/comments، والتي تتوافق مع الإجراء <code>create</code> في <code>Blorgh::CommentsController</code>. يجب إنشاء هذا الإجراء، والذي يُنفذ عن طريق وضع السطور التالية داخل تعريف الصنف في app/controllers/blorgh/comments_controller.rb:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
def create
def create
 
  @article = Article.find(params[:article_id])
 @article = Article.find(params[:article_id])
  @comment = @article.comments.create(comment_params)
 
  flash[:notice] = "Comment has been created!"
 @comment = @article.comments.create(comment_params)
  redirect_to articles_path
 
 flash[:notice] = "Comment has been created!"
 
 redirect_to articles_path
 
end
end
 
private
private
 
  def comment_params
 def comment_params
    params.require(:comment).permit(:text)
 
  end
   params.require(:comment).permit(:text)
 
 End
</syntaxhighlight>
</syntaxhighlight>


هذه هي الخطوة النهائية المطلوبة للحصول على نموذج التعليق الجديد. ومع ذلك، فإن عرض التعليقات ليس صحيحًا بعد. إذا كنت تريد إنشاء تعليق في الوقت الحالي، فسترى هذا الخطأ:
هذه هي الخطوة النهائية المطلوبة للحصول على نموذج التعليق الجديد. ومع ذلك، فإن عرض التعليقات ليس صحيحًا بعد. إذا كنت تريد إنشاء تعليق في الوقت الحالي، فسترى هذا الخطأ:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="text">
<nowiki>Missing partial blorgh/comments/_comment with {:handlers=>[:erb, :builder],</nowiki>
Missing partial blorgh/comments/_comment with {:handlers=>[:erb, :builder],
 
:formats=>[:html], :locale=>[:en, :en]}. Searched in:   *
<nowiki>:</nowiki>formats=>[:html], :locale=>[:en, :en]}. Searched in:   *
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views"   *
 
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views"   *
 
"/Users/ryan/Sites/side_projects/blorgh/app/views"
"/Users/ryan/Sites/side_projects/blorgh/app/views"
</syntaxhighlight>
</syntaxhighlight>


يتعذر على المحرك العثور على الجزء المطلوب لتقديم التعليقات. يبدو Rails أولاً في دليل التطبيق test/dummy) app/views) ثم في دليل app/views للمحرك. عندما لا يمكن العثور عليه، سوف يرمي هذا الخطأ. يعرف المحرك أن يبحث عن blorgh / comments / _comment لأن كائن النموذج الذي يستقبله هو من صنف Blorgh :: Comment.
يتعذر على المحرك العثور على الجزء المطلوب لتقديم التعليقات. يبحث ريلز أولًا في مجلد التطبيق test/dummy) app/views) ثم في المجلد app/views للمحرك. عندما لا يعثر عليه، سوف يرمي هذا الخطأ. يعرف المحرك أين يبحث عن blorgh/comments/_comment لأن كائن النموذج الذي يستقبله هو من الصنف <code>Blorgh::Comment</code>.


ستكون هذه الجزئية مسؤولة عن تقديم نص التعليق فقط، في الوقت الحالي. أنشئ ملف جديد على app/views/blorgh/comments/_comment.html.erb وضع هذا السطر داخله:
ستكون هذه الجزئية مسؤولة عن تقديم نص التعليق فقط، في الوقت الحالي. أنشئ ملفًا جديدًا في app/views/blorgh/comments/_comment.html.erb وضع هذا السطر داخله:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="html">
<%= comment_counter + 1 %>. <%= comment.text %>
<%= comment_counter + 1 %>. <%= comment.text %>
</syntaxhighlight>
</syntaxhighlight>


يعطي المتغير المحلي comment_counter بواسطة استدعاء <٪ = render
يعطى المتغير المحلي <code>comment_counter</code> بواسطة استدعاء <code>‎<%= render@article.comments %>‎</code>، والذي سيحدده تلقائيًا ويزيد العداد أثناء تكرار كل تعليق. يُستخدم في هذا المثال لعرض رقم صغير بجوار كل تعليق عند إنشائه.


@ article.comments٪>، والتي ستحدده تلقائيًا وتزيد العداد أثناء تكرار كل تعليق. يُستخدم في هذا المثال لعرض رقم صغير بجوار كل تعليق عند إنشائه.
هذا يكمل وظيفة التعليق لمحرك التدوين. الآن حان الوقت لاستخدامه داخل التطبيق.


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


== تركيبه في التطبيق ==
=== وصل المحرك ===
استخدام المحرك داخل التطبيق سهل للغاية. يغطي هذا القسم كيفية تركيب المحرك في التطبيق والإعداد الأولي المطلوب، بالإضافة إلى ربط المحرك بصنف المستخدم التي يوفرها التطبيق لتوفير ملكية المقالات والتعليقات داخل المحرك.
أولًا، يجب تحديد المحرك داخل Gemfile الخاص بالتطبيق. إذا لم يكن هناك تطبيق مفيد لاختبار ذلك، فأنشئ تطبيقًا باستخدام الأمر <code>rails new</code> خارج مجلد المحرك كما يلي:
 
=== تركيب المحرك ===
أولاً، يجب تحديد المحرك داخل Gemfile الخاص بالتطبيق. إذا لم يكن هناك تطبيق مفيد لاختبار ذلك، فأنشئ تطبيقًا باستخدام الأمر rails new خارج دليل المشغل كما يلي:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ rails new unicorn
$ rails new unicorn
</syntaxhighlight>
</syntaxhighlight>


عادة، يحدد المحرك داخل Gemfile من خلال تحديده كالمعتاد، جوهرة كل يوم.
عادةً، يحدد المحرك داخل Gemfile من خلال تحديده كالمعتاد كأي جوهرة.
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
gem 'devise'
gem 'devise'
</syntaxhighlight>
</syntaxhighlight>


ومع ذلك، نظرًا لأنك تقوم بتطوير محرك blorgh على جهازك المحلي، فستحتاج إلى تحديد خيار path: في Gemfile الخاص بك:
ومع ذلك، نظرًا لأنك تقوم بتطوير المحرك blorgh على جهازك المحلي، فستحتاج إلى تحديد الخيار <code>path:‎</code> في Gemfile الخاص بك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
gem 'blorgh', path: 'engines/blorgh'
gem 'blorgh', path: 'engines/blorgh'
</syntaxhighlight>
</syntaxhighlight>


ثم شغل الحزمة لتثبيت الجوهرة.
ثم شغل <code>bundle</code> لتثبيت الجوهرة.


كما هو موضح سابقًا، بوضع الجوهرة في Gemfile، سيحمل عند تحميل Rails. سيتطلب الأمر أولاً lib / blorgh.rb من المحرك، ثم lib / blorgh / engine.rb، وهو الملف الذي يحدد الأجزاء الرئيسية من الوظائف للمحرك.
كما هو موضح سابقًا، بوضع الجوهرة في Gemfile، سيحمل المحرك عند تحميل ريلز. سيتطلب الأمر أولًا lib/blorgh.rb من المحرك، ثم lib/blorgh/engine.rb، وهو الملف الذي يحدد الأجزاء الرئيسية من الوظائف للمحرك.


لجعل وظيفة المحرك قابلة للوصول من داخل التطبيق، يجب تثبيته في ملف config / path.rb لهذا التطبيق:
لجعل وظيفة المحرك قابلة للوصول من داخل التطبيق، يجب تثبيته في الملف config/path.rb لهذا التطبيق:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
mount Blorgh::Engine, at: "/blog"
mount Blorgh::Engine, at: "/blog"
</syntaxhighlight>
</syntaxhighlight>


سيقوم هذا السطر بتركيب المحرك على /blog  في التطبيق. مما يجعل الوصول إليها في http: // localhost: 3000 / blog عند تشغيل التطبيق مع rails server.
سيوصل هذا السطر المحرك في ‎/blog  في التطبيق. مما يجعل الوصول إليها في http://localhost:3000/blog عند تشغيل التطبيق مع <code>rails server</code>.


ملاحظة: المحركات الأخرى، مثل Devise، تتعامل مع هذا بطريقة مختلفة قليلاً عن طريق تعيينك لمساعدات مخصصة (مثل devise_for) في المسارات. يعملون هؤلاء المساعدون نفس الشيء بالضبط، حيث تُركب أجزاء من وظائف المحرك على مسار محدد مسبقًا والذي يمكن تخصيصه.
'''ملاحظة''': المحركات الأخرى، مثل Devise، تتعامل مع هذا بطريقة مختلفة قليلًا عن طريق تعيينك لمساعدات مخصصة (مثل <code>devise_for</code>) في المسارات. ينفذ هؤلاء المساعدون نفس الشيء بالضبط، حيث توصل أجزاءً من وظائف المحرك على مسار محدد مسبقًا والذي يمكن تخصيصه.


=== إعداد المحرك ===
=== إعداد المحرك ===
يحتوي المحرك على عمليات ترحيل لجدول blorgh_articles و blorgh_comments والتي يلزم إنشاؤها في قاعدة بيانات التطبيق بحيث يمكن لنماذج المحرك الاستعلام عنها بشكل صحيح. لنسخ هذه الترحيلات إلى التطبيق،شغل الأمر التالي من دليل test/dummy الخاص بمحرك Rails:
يحتوي المحرك على عمليات تهجير لجدول <code>blorgh_articles</code> والجدول <code>blorgh_comments</code> والتي يلزم إنشاؤها في قاعدة بيانات التطبيق بحيث يمكن لنماذج المحرك الاستعلام عنها بشكل صحيح. لنسخ هذه التهجيرات إلى التطبيق، شغل الأمر التالي من المجلد test/dummy الخاص بمحرك ريلز:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails blorgh:install:migrations
$ bin/rails blorgh:install:migrations
</syntaxhighlight>
</syntaxhighlight>


إذا كان لديك العديد من المحركات التي تحتاج إلى نسخ عمليات الترحيل، استخدام railties:install:migrations  بدلاً من ذلك:
إذا كان لديك العديد من المحركات التي تحتاج إلى نسخ عمليات التهجير، استخدام <code>railties:install:migrations</code> بدلًا من ذلك:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails railties:install:migrations
$ bin/rails railties:install:migrations
</syntaxhighlight>
</syntaxhighlight>


سيعمل هذا الأمر عند تشغيله لأول مرة على نسخ جميع عمليات الترحيل من المحرك. عند تشغيلها في المرة القادمة، لن تُنسخ إلا من خلال عمليات الترحيل التي لم تنسخ بالفعل. التشغيل الأول لهذا الأمر سينتج شيئًا كهذا:
سيعمل هذا الأمر عند تشغيله لأول مرة على نسخ جميع عمليات التهجير من المحرك. عند تشغيلها في المرة القادمة، لن تُنسَخ إلا من خلال عمليات التهجير التي لم تنسخ بالفعل. التشغيل الأول لهذا الأمر سينتج شيئًا كهذا:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="text">
Copied migration [timestamp_1]_create_blorgh_articles.blorgh.rb from blorgh
Copied migration [timestamp_1]_create_blorgh_articles.blorgh.rb from blorgh
Copied migration [timestamp_2]_create_blorgh_comments.blorgh.rb from blorgh
Copied migration [timestamp_2]_create_blorgh_comments.blorgh.rb from blorgh
</syntaxhighlight>
</syntaxhighlight>


سيكون الطابع الزمني الأول ([timestamp_1]) هو الوقت الحالي، وسيكون الطابع الزمني الثاني ([timestamp_2]) هو الوقت الحالي بالإضافة إلى ثانية. السبب في ذلك هو أن تُشغل عمليات ترحيل المحرك بعد أي عمليات ترحيل حالية في التطبيق.
سيكون الطابع الزمني الأول (<code>[timestamp_1]</code>) هو الوقت الحالي، وسيكون الطابع الزمني الثاني (<code>[timestamp_2]</code>) هو الوقت الحالي بالإضافة إلى ثانية. السبب في ذلك هو أن تُشغل عمليات ji[dv المحرك بعد أي عمليات تهجير حالية في التطبيق.


لتشغيل عمليات الترحيل هذه في سياق التطبيق، ما عليك سوى تشغيل bin/rails db:migrate. عند الوصول إلى المحرك من خلال http: // localhost: 3000 / blog، ستكون المقالات فارغة. وذلك لأن الجدول الذي أُنشأ داخل التطبيق يختلف عن الجدول الذي أُنشأ داخل المحرك. امضي قدما، للعلب مع المحرك الذي رُكب حديثا. ستجد أنه هو نفسه عندما كان مجرد محرك.
لتشغيل عمليات التهجير هذه في سياق التطبيق، ما عليك سوى تشغيل <code>bin/rails db:migrate</code>. عند الوصول إلى المحرك من خلال <code><nowiki>http://localhost</nowiki>: 3000/blog</code>، ستكون المقالات فارغة. وذلك لأن الجدول الذي أُنشأ داخل التطبيق يختلف عن الجدول الذي أُنشأ داخل المحرك. امضي قدمًا واستمر بالتعديل على المحرك الذي وصل حديثًا. ستجد أنه هو نفسه عندما كان مجرد محرك.


إذا كنت ترغب في تشغيل عمليات الترحيل فقط من محرك واحد، فيمكنك القيام بذلك عن طريق تحديد SCOPE:
إذا كنت ترغب في تشغيل عمليات التهجير فقط من محرك واحد، فيمكنك القيام بذلك عن طريق تحديد <code>SCOPE</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
bin/rails db:migrate SCOPE=blorgh
bin/rails db:migrate SCOPE=blorgh
</syntaxhighlight>
</syntaxhighlight>


قد يكون هذا مفيدًا إذا كنت تريد التراجع عن عمليات ترحيل المحرك قبل إزالتها. لإعادة جميع عمليات الترحيل من محرك blorgh، يمكنك تشغيل كود مثل:
قد يكون هذا مفيدًا إذا كنت تريد التراجع عن عمليات ترحيل المحرك قبل إزالتها. لإعادة جميع عمليات الترحيل من المحرك blorgh، يمكنك تشغيل شيفرة مثل:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
bin/rails db:migrate SCOPE=blorgh VERSION=0
bin/rails db:migrate SCOPE=blorgh VERSION=0
</syntaxhighlight>
</syntaxhighlight>


=== باستخدام الصنف المقدمة من التطبيق ===
=== استخدام صنف وفره التطبيق ===


==== باستخدام نموذج مقدم من التطبيق ====
==== استخدام نموذج وفره التطبيق ====
عندما يُنشأ المحرك، قد ترغب في استخدام فئات معينة من التطبيق لتوفير روابط بين أجزاء المحرك وأجزاء التطبيق. في حالة محرك blorgh، فإن كتابة المقالات والتعليقات تجعل المؤلفين منطقيين.
عندما يُنشأ المحرك، قد ترغب في استخدام أصناف معينة من التطبيق لتوفير روابط بين أجزاء المحرك وأجزاء التطبيق. في حالة المحرك blorgh، فإن إسناد مؤلفين إلى المقالات والتعليقات تجعل الأمر أكثر واقعية.


قد يحتوي التطبيق النموذجي على صنف User يمكن استخدامها لتمثيل المؤلفين لمقال أو تعليق. ولكن قد تكون هناك حالة يستدعي فيها التطبيق هذا الصنف شيئًا مختلفًا، مثل Person. لهذا السبب، يجب ألا يرتب المحرك الارتباطات الخاصة بصنف User على وجه التحديد.
قد يحتوي التطبيق النموذجي على الصنف <code>User</code> الذي يمكن استخدامه لتمثيل المؤلفين لمقال أو تعليق. ولكن قد تكون هناك حالة يستدعي فيها التطبيق هذا الصنف شيئًا مختلفًا، مثل <code>Person</code>. لهذا السبب، يجب ألا يرتب المحرك الارتباطات الخاصة بالصنف <code>User</code> على وجه التحديد.


لتبسيط الأمر في هذه الحالة، سيحتوي التطبيق على صنف تسمى User الذي يمثل مستخدمو التطبيق (سوف ندخل في جعل هذا التكوين ممكنًا). يمكن إنشاؤه باستخدام هذا الأمر داخل التطبيق:
لتبسيط الأمر في هذه الحالة، سيحتوي التطبيق على صنف يسمى <code>User</code> الذي يمثل مستخدمو التطبيق (سوف نحاول في جعل هذا الضبط ممكنًا). يمكن إنشاؤه باستخدام هذا الأمر داخل التطبيق:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
rails g model user name:string
rails g model user name:string
</syntaxhighlight>
</syntaxhighlight>


يجب تشغيل الأمر bin / rails db: migrate هنا لضمان أن تطبيقنا يحتوي على جدول users للاستخدام في المستقبل.
يجب تشغيل الأمر <code>bin/rails db: migrate</code> هنا لضمان أن تطبيقنا يحتوي على الجدول <code>users</code> للاستخدام المستقبلي.
 
أيضًا، للإبقاء على الأمر بسيطًا، سيحتوي نموذج المقالات على حقل نص جديد يسمى author_name، حيث يمكن للمستخدمين اختيار وضع اسمه. سيأخذ المحرك هذا الاسم ثم ينشئ كائن User جديد منه، أو يعثر على اسم له هذا الاسم بالفعل. سيربط المحرك المقالة بالبحث عنها أو ينشىء كائن User.
 
أولاً، يجب إضافة حقل النص author_name إلىapp/views/blorgh/articles/_form.html.erb جزئيًا داخل المحرك. يمكن إضافة هذا فوق حقل title بهذا الكود:
<syntaxhighlight lang="rails">
<nowiki><div class="field"></nowiki>
 
 <%= form.label :author_name %><nowiki><br></nowiki>


 <%= form.text_field :author_name %>
أيضًا، للإبقاء على الأمر بسيطًا، سيحتوي نموذج المقالات على حقل نص جديد يسمى <code>author_name</code>، حيث يمكن للمستخدمين اختيار وضع اسمه. سيأخذ المحرك هذا الاسم ثم ينشئ كائن <code>User</code> جديد منه، أو يعثر على اسم له هذا الاسم بالفعل. سيربط المحرك المقالة بالبحث عنها أو ينشىء الكائن <code>User</code>.


<nowiki></div></nowiki>
أولًا، يجب إضافة حقل النص <code>author_name</code> إلى الجزئية app/views/blorgh/articles/_form.html.erb داخل المحرك. يمكن إضافة هذا فوق الحقل <code>title</code> بالشكل التالي:
<syntaxhighlight lang="html">
<div class="field">
  <%= form.label :author_name %><br>
  <%= form.text_field :author_name %>
</div>
</syntaxhighlight>
</syntaxhighlight>


بعد ذلك، نحتاج إلى تحديث التابع Blorgh :: ArticleController # article_params للسماح بمعامل النموذج الجديد:
بعد ذلك، نحتاج إلى تحديث التابع <code>Blorgh::ArticleController.article_params</code> للسماح بمعامل النموذج الجديد:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
def article_params
def article_params
 
  params.require(:article).permit(:title, :text, :author_name)
 params.require(:article).permit(:title, :text, :author_name)
 
end
end
</syntaxhighlight>
</syntaxhighlight>


يجب أن يحتوي نموذج Blorgh :: Article على رمز لتحويل حقل author_name إلى كائن User فعلي و إقرانه كـ author المقالة قبل حفظ المقالة. كما ستحتاج أيضًا إلى إعداد attr_accessor لهذا الحقل، بحيث تُحدد توابع الوضع والجلب الخاصة به.
يجب أن يحتوي النموذج <code>Blorgh::Article</code> على نفس الشيفرة لتحويل الحقل <code>author_name</code> إلى كائن <code>User</code> فعلي و إقرانه على أنه مؤلف <code>author</code> تلك المقالة قبل حفظ حفظها. كما ستحتاج أيضًا إلى إعداد <code>attr_accessor</code> لهذا الحقل، بحيث تُحدد توابع الضبط (setter) والجلب (getter) الخاصة به.


للقيام بكل هذا، ستحتاج إلى إضافة attr_accessor لـ author_name، وربط المؤلف واستدعاء before_validation إلى app / models / blorgh / article.rb. سيرمز اقتران author لصنف User في الوقت الحاضر.
للقيام بكل هذا، ستحتاج إلى إضافة <code>attr_accessor</code> لـ <code>author_name</code>، إذ يستدعى الارتباط للمؤلف والاستدعاء <code>before_validation</code> إلى app/models/blorgh/article.rb. سيكون الارتباط <code>author</code> ثابتًا (hard-coded) إلى الصنف <code>User</code> في الوقت الحاضر.
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
attr_accessor :author_name
attr_accessor :author_name
belongs_to :author, class_name: "User"
belongs_to :author, class_name: "User"
 
before_validation :set_author
before_validation :set_author
 
private
private
 
  def set_author
 def set_author
    self.author = User.find_or_create_by(name: author_name)
 
  end
   self.author = User.find_or_create_by(name: author_name)
 
 end
</syntaxhighlight>
</syntaxhighlight>


من خلال تمثيل كائن رابطة author مع صنف المستخدم، ينشأ ارتباط بين المحرك والتطبيق. يجب أن تكون هناك طريقة لربط السجلات في جدول blorgh_articles بالسجلات في جدول المستخدمين. لأن الرابطة تستدعي author، يجب أن يكون هناك عمود author_id مضاف إلى جدول blorgh_articles.
من خلال تمثيل الارتباط <code>author</code> للكائن مع الصنف <code>User</code>، تنشأ وصلةٌ بين المحرك والتطبيق. يجب أن تكون هناك طريقة لربط السجلات في الجدول <code>blorgh_articles</code> بالسجلات في الجدول <code>users</code>. لأن الارتباط يدعى <code>author</code>، يجب أن يكون هناك حقلًا يدعى <code>author_id</code> مضافًا إلى الجدول <code>blorgh_articles</code>.


لإنشاء هذا العمود الجديد، شغل هذا الأمر داخل المحرك:
لإنشاء هذا العمود الجديد، شغل هذا الأمر داخل المحرك:
سطر 601: سطر 498:
</syntaxhighlight>
</syntaxhighlight>


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


يجب تشغيل هذا الترحيل على التطبيق. للقيام بذلك، يجب أولاً نسخها باستخدام هذا الأمر:
يجب تشغيل هذا التهجير على التطبيق. للقيام بذلك، يجب أولًا نسخه باستخدام هذا الأمر:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails blorgh:install:migrations
$ bin/rails blorgh:install:migrations
</syntaxhighlight>
</syntaxhighlight>


لاحظ أنه نُسخ ترحيل واحد فقط هنا. هذا لأن نُسخ أول ترحلين خلال المرة الأولى من تشغيل هذا الأمر.
لاحظ أنه نُسخ تهجير واحد فقط هنا. هذا لأن نُسخ أول تهجيرين خلال المرة الأولى من تشغيل هذا الأمر.
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
NOTE Migration [timestamp]_create_blorgh_articles.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
NOTE Migration [timestamp]_create_blorgh_articles.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
NOTE Migration [timestamp]_create_blorgh_comments.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
NOTE Migration [timestamp]_create_blorgh_comments.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blorgh
Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blorgh
</syntaxhighlight>
</syntaxhighlight>


شغّل الترحيل باستخدام:
شغّل التهجير باستخدام:
<syntaxhighlight lang="shell">
<syntaxhighlight lang="shell">
$ bin/rails db:migrate
$ bin/rails db:migrate
</syntaxhighlight>
</syntaxhighlight>


الآن مع كل القطع في مكانها، سيحدث إجراء يربط المؤلف - الذي يمثله سجل في جدول users- بمقال، ممثلة بجدول blorgh_articles من المحرك.
الآن مع وضع كل القطع في مكانها، سيجري ربط مؤلفٍ - مأخوذ من حقلٍ من الجدول <code>users</code> - مع كل مقال ممثل من الجدول <code>blorgh_articles</code> من المحرك.


وأخيرًا، يجب أن يعرض اسم المؤلف على صفحة المقالة. أضف هذا الرمز فوق مخرج "العنوان" داخل app/views/blorgh/articles/show.html.erb:
وأخيرًا، يجب أن يعرض اسم المؤلف على صفحة المقالات. أضف الشيفرة التالية فوق "Title" داخل الملف app/views/blorgh/articles/show.html.erb:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="html">
<nowiki><p></nowiki>
<p>
 
  <b>Author:</b>
 <nowiki><b>Author:</b></nowiki>
  <%= @article.author.name %>
 
</p>
 <%= @article.author.name %>
 
<nowiki></p></nowiki>
</syntaxhighlight>
</syntaxhighlight>


==== استخدام وحدة التحكم المقدمة من التطبيق ====
==== استخدام وحدة تحكم وفرها التطبيق ====
نظرًا لأن وحدات تحكم Rails تشترك بشكل عام في التعليمات البرمجية لأشياء مثل الاستيثاق والوصول إلى متغيرات جلسة العمل، فإنها ترث من ApplicationController افتراضيًا.
نظرًا لأن وحدات تحكم ريلز تشترك بشكل عام في التعليمات البرمجية لأشياء مثل الاستيثاق والوصول إلى متغيرات جلسة العمل، فإنها ترث من <code>ApplicationController</code> افتراضيًا.
 
محركات Rails، ومع ذلك يحدد نطاق للتشغيل بشكل مستقل عن التطبيق الرئيسي، بحيث يحصل كل محرك على نطاق ApplicationController.


تمنع مجال الاسم هذه تعارضات التعليمات البرمجية، ولكن غالباً ما تحتاج وحدات تحكم المحرك إلى الوصول إلى التوابع في ApplicationController التطبيق الرئيسي. إحدى الطرق السهلة لتوفير هذا الوصول هي تغيير ApplicationController الخاص بالمحرك إلى الوراثة من ApplicationController التطبيق الرئيسي. بالنسبة لمحرك Blorgh، يمكن القيام بذلك عن طريق تغيير app/controllers/blorgh/application_controller.rb لتبدو كما يلي:
تملك محركات ريلز نطاقًا للتشغيل بشكل مستقل عن التطبيق الرئيسي، بحيث يحصل كل محرك على نطاق <code>ApplicationController</code>. يمنع مجال الاسم هذا تعارضات الشيفرة، ولكن غالبًا ما تحتاج وحدات تحكم المحرك إلى الوصول إلى التوابع في <code>ApplicationController</code> للتطبيق الرئيسي. إحدى الطرق السهلة لتوفير هذا الوصول هي تغيير <code>ApplicationController</code> الخاص بالمحرك إلى الوراثة من <code>ApplicationController</code> للتطبيق الرئيسي. بالنسبة لمحرك Blorgh، يمكن القيام بذلك عن طريق تغيير app/controllers/blorgh/application_controller.rb لتبدو كما يلي:<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
 
  class ApplicationController < ::ApplicationController
 class ApplicationController < ::ApplicationController
  end
 
end
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


بشكل افتراضي، ترث وحدات تحكم المحرك من Blorgh :: ApplicationController. لذا، بعد إجراء هذا التغيير، سيكون بإمكانهم الوصول إلى ApplicationController التطبيق الرئيسي، كما لو كانوا جزءًا من التطبيق الرئيسي.
بشكل افتراضي، ترث وحدات تحكم المحرك من <code>Blorgh::ApplicationController</code>. لذا، بعد إجراء هذا التغيير، سيكون بإمكانها الوصول إلى <code>ApplicationController</code> للتطبيق الرئيسي، كما لو كانت جزءًا من التطبيق الرئيسي.


يتطلب هذا التغيير تشغيل المحرك من تطبيق Rails يحتوي على ApplicationController.
يتطلب هذا التغيير تشغيل المحرك من تطبيق ريلز يحتوي على <code>ApplicationController</code>.


=== إعداد تكوين المحرك ===
=== ضبط المحرك ===
يغطي هذا القسم كيفية جعل صنف User قابلة للتكوين، متبوعة بنصائح تهيئة عامة للمحرك.
يغطي هذا القسم كيفية جعل الصنف <code>User</code> قابلة للضبط، ويحتوي على نصائح عامة لتهيئة المحرك.


==== ضبط إعدادات التكوين في التطبيق ====
==== ضبط إعدادات التطبيق ====
الخطوة التالية هي جعل الصنف التي تمثل User في التطبيق للتخصيص للمحرك. ويرجع ذلك إلى أن هذه الصنف قد لا تكون دائمًا من المستخدمين، كما هو موضح سابقًا. لجعل هذا الإعداد قابلاً للتخصيص، سيكون لدى المحرك إعداد تكوين يسمى author_class والذي يستخدم لتحديد الصنف التي تمثل المستخدمين داخل التطبيق.
الخطوة التالية هي جعل الصنف الذي يمثل <code>User</code> في التطبيق قابلًا للتخصيص للمحرك. ويرجع ذلك إلى أن هذا الصنف قد لا يكون <code>User</code> دائمًا، كما أشير إلى ذلك سابقًا. لجعل هذا الإعداد قابلًا للتخصيص، سيكون لدى المحرك خيار ضبط يسمى <code>author_class</code> والذي يستخدم لتحديد الصنف الذي يمثل المستخدمين داخل التطبيق.


لتعريف إعداد التكوين هذا، يجب عليك استخدام mattr_accessor داخل وحدة Blorgh للمحرك. أضف هذا السطر إلى lib / blorgh.rb داخل المحرك:
لتعريف خيار الضبط هذا، يجب عليك استخدام <code>mattr_accessor</code> داخل الوحدة <code>Blorgh</code> للمحرك. أضف السطر التالي إلى lib/blorgh.rb داخل المحرك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
mattr_accessor :author_class
mattr_accessor :author_class
</syntaxhighlight>
</syntaxhighlight>


يعمل هذا التابع مثل أشقائه، attr_accessor و cattr_accessor، ولكنه يوفر تابع setter و getter على النموذج بالاسم المحدد. لاستخدامها، يجب الإشارة إليها باستخدام Blorgh.author_class.
يعمل هذا التابع مثل أمثاله - <code>attr_accessor</code> و <code>cattr_accessor</code> - ولكنه يوفر تابع ضبط (setter) وجلب (getter) على النموذج بالاسم المحدد. لاستخدامها، يجب الإشارة إليها باستخدام <code>Blorgh.author_class</code>.


الخطوة التالية هي تبديل نموذج Blorgh :: Article إلى هذا الإعداد الجديد. غير الارتباط belongs_to داخل هذا النموذج (app/models/blorgh/article.rb) لهذا:
الخطوة التالية هي تبديل النموذج <code>Blorgh::Article</code> إلى هذا الضبط الجديد. غيِّر الارتباط [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.A7.D9.86.D8.AA.D9.85.D8.A7.D8.A1 .28belongs to.29|belongs_to]] داخل هذا النموذج (app/models/blorgh/article.rb) إلى ما يلي:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
belongs_to :author, class_name: Blorgh.author_class
belongs_to :author, class_name: Blorgh.author_class
</syntaxhighlight>
</syntaxhighlight>


يجب أيضًا أن يستخدم التابع set_author في نموذج Blorgh :: Article هذه الصنف:
يجب أيضًا أن يستخدم التابع <code>set_author</code> في النموذج <code>Blorgh::Article</code> هذا الصنف:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name)
self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name)
</syntaxhighlight>
</syntaxhighlight>


لحفظ الحاجة إلى استدعاء ثابت على نتيجة author_class طوال الوقت، يمكنك بدلاً من ذلك فقط تجاوز التابع getter_class author_class داخل وحدة Blorgh في ملف lib / blorgh.rb لتستدعي دائمًا constantize على القيمة المحفوظة قبل إرجاع النتيجة:
لحفظ الحاجة إلى استدعاء ثابت على نتيجة <code>author_class</code> طوال الوقت، يمكنك بدلًا من ذلك فقط استبدال التابع <code>author_class</code> الضابط داخل الوحدة <code>Blorgh</code> في الملف lib/blorgh.rb لتستدعي دائمًا <code>constantize</code> على القيمة المحفوظة قبل إرجاع النتيجة:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
def self.author_class
def self.author_class
 
  @@author_class.constantize
 @@author_class.constantize
end
 
End
</syntaxhighlight>
</syntaxhighlight>


سيؤدي هذا بعد ذلك إلى تحويل الرمز أعلاه إلى set_author في هذا:
سيؤدي هذا بعد ذلك إلى تحويل الشيفرة أعلاه من أجل <code>set_author</code> إلى:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
self.author = Blorgh.author_class.find_or_create_by(name: author_name)
self.author = Blorgh.author_class.find_or_create_by(name: author_name)
</syntaxhighlight>
</syntaxhighlight>


مما يؤدي إلى شيء أقصر قليلا، و مضمنه اكثر في سلوكها. يجب دائمًا إرجاع التابع author_class الكائن Class.
مما يؤدي إلى شيء أقصر قليلًا، وذي سلوك مضمَّن. يجب أن يعيد التابع <code>author_class</code> الكائن <code>Class</code> دومًا.


بما أننا غيرنا التابع author_class لإرجاع صنف بدلاً من سلسلة، فيجب علينا أيضًا تعديل تعريف belongs_to الخاص بنا في نموذج Blorgh :: Article:
بما أننا غيرنا التابع <code>author_class</code> لإعادة صنف بدلًا من سلسلة نصية، فيجب علينا أيضًا تعديل تعريف الارتباط [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.A7.D9.86.D8.AA.D9.85.D8.A7.D8.A1 .28belongs to.29|belongs_to]] الخاص بنا في النموذج <code>Blorgh::Article</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
belongs_to :author, class_name: Blorgh.author_class.to_s
belongs_to :author, class_name: Blorgh.author_class.to_s
</syntaxhighlight>
</syntaxhighlight>


لتعيين إعداد التكوين هذا داخل التطبيق، يجب استخدام المُهيئ. باستخدام المُهيئ،  سيُعد التكوين قبل بدء تشغيل التطبيق ويستدعي نماذج المحرك، والتي قد تعتمد على إعداد التكوين الحالي.
لتعيين خيار الضبط هذا داخل التطبيق، يجب استخدام مُهيئٍ. باستخدام المُهيئ، سيُضبَط الضبط قبل بدء تشغيل التطبيق ويستدعي نماذج المحرك، والتي قد تعتمد على هذا الضبط الموجود حاليًّا.


إنشاء مُهيئ جديد في config / initializers / blorgh.rb داخل التطبيق حيث رُكب المحرك blorgh ووضع هذا المحتوى فيه:
أنشئ مُهيئًا جديدًا في config/initializers/blorgh.rb داخل التطبيق حيث وُصِلَ المحرك <code>blorgh</code> وضع هذا المحتوى فيه:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
Blorgh.author_class = "User"
Blorgh.author_class = "User"
</syntaxhighlight>
</syntaxhighlight>


ملاحظة: من المهم جدًا هنا استخدام نسخة String من الصنف، بدلاً من الصنف نفسها. إذا كنت تريد استخدام الصنف، تحاول Rails تحميل هذه الصنف ثم جعلها مرجع إلى الجدول المرتبط. هذا يمكن أن يؤدي إلى مشاكل إذا كان الجدول غير موجود بالفعل. لذلك، يجب استخدام String ثم تحويلها إلى صنف باستخدام constantize في المحرك في وقت لاحق.
'''تحذير''': من المهم جدًا هنا استخدام النسخة <code>[[Ruby/String|String]]</code> من الصنف، بدلًا من الصنف نفسه. إذا كنت تريد استخدام الصنف، تحاول ريلز تحميل هذه الصنف ثم جعله مرجعًا إلى الجدول المرتبط. هذا يمكن أن يؤدي إلى مشاكل إذا كان الجدول غير موجود بالفعل. لذلك، يجب استخدام <code>[[Ruby/String|String]]</code> ثم تحويله إلى صنف باستخدام <code>constantize</code> في المحرك في وقت لاحق.


امضي قدما وحاول إنشاء مقال جديد. سترى أنه يعمل بالضبط بنفس الطريقة كما كان من قبل، ما عدا هذا الوقت يستخدم المحرك إعداد التكوين في config / initializers / blorgh.rb لمعرفة ما هي الصنف.
امضي قدمًا وحاول إنشاء مقال جديد. سترى أنه يعمل بالضبط بنفس الطريقة كما كان من قبل، ما عدا أن المحرك الآن يستخدم إعداد الضبط في config/initializers/blorgh.rb لمعرفة ما هو الصنف.


لا توجد الآن اعتمادات صارمة على ما هي الصنف، فقط ما يجب أن تكون عليه واجهة برمجة التطبيقات للصنف. يتطلب المحرك ببساطة هذه الصنف لتعريف التابع find_or_create_by التي يرجع عنصر من تلك الصنف، لربطها بمقال عند تكوينه. هذا الكائن، بالطبع، يجب أن يحتوي على نوع من المعرّف يمكن من خلاله الإشارة إليه.
لا توجد الآن اعتمادات صارمة (strict dependencies) على ما هية الصنف، فقط ما يجب أن تكون عليه واجهة برمجة التطبيقات للصنف. يتطلب المحرك ببساطة هذه الصنف لتعريف التابع <code>find_or_create_by</code> الذي يعيد عنصرًا من ذلك الصنف، لربطه بمقال عند ضبطه. هذا الكائن، بالطبع، يجب أن يحتوي على نوع من المعرّف يمكن من خلاله الإشارة إليه.


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


إذا كنت ترغب في استخدام مُهيئ -  يجب أن يشغل الكود قبل تحميل المحرك - فإن مكانه هو مجلد config / initializers. وضح وظيفة هذا الدليل في قسم Initializers من دليل التوصيف، وتعمل بنفس الطريقة بالضبط مثل دليل config / initializers داخل التطبيق. نفس الشيء إذا كنت تريد استخدام مُهيئ قياسي.
إذا كنت ترغب في استخدام مُهيئ -  يجب أن يشغل الشيفرة قبل تحميل المحرك -، فإن مكانه هو المجلد config/initializers. وضح وظيفة هذا المجلد في قسم [[Rails/configuring#.D8.A7.D9.84.D9.85.D9.87.D9.8A.D8.A6.D8.A7.D8.AA|المهيئات]] من دليل [[Rails/configuring|ضبط تطبيقات ريلز]]، وتعمل بنفس الطريقة بالضبط مثل المجلد config/initializers داخل التطبيق. نفس الشيء إذا كنت تريد استخدام مُهيئ قياسي.


محليًا، ضع ببساطة ملفات الإعدادات المحلية في دليل config / locales، تمامًا كما تفعل في التطبيق.
محليًا، ضع ببساطة ملفات الإعدادات المحلية في المجلد config/locales، تمامًا كما تفعل في أي تطبيق.


== اختبار المحرك ==
== اختبار المحرك ==
عند إنشاء المحرك، هناك تطبيق وهمي أصغر ينشئ داخله في test/dummy. يستخدم هذا التطبيق كنقطة تثبيت للمحرك، لجعل اختبار المحرك بسيط للغاية. يمكنك تمديد هذا التطبيق عن طريق توليد وحدات تحكم أو نماذج أو واجهات من داخل الدليل، ثم استخدمها لاختبار محركك.
عند إنشاء محرك، هناك تطبيق وهمي أصغر يُنشَأ داخله في test/dummy. يُستخدَم هذا التطبيق كنقطة وصل للمحرك، لجعل اختبار المحرك بسيط للغاية. يمكنك توسيع هذا التطبيق عن طريق توليد وحدات تحكم أو نماذج أو واجهات من داخل المجلد، ثم استخدمها لاختبار محركك.


يجب معاملة دليل test مثل بيئة اختبار Rails النموذجية، مما يسمح باختبارات الوحدة والوظيفة والتكامل.
يجب معاملة المجلد test مثل بيئة اختبار ريلز النموذجية، مما يسمح باختبارات الوحدة والوظيفة والتكامل.


=== الاختبارات الوظيفية ===
=== الاختبارات الوظيفية ===
هناك مسألة تستحق الأخذ في الاعتبار عند كتابة الاختبارات الوظيفية وهي أن الاختبارات ستعمل على تطبيق - تطبيق test/dummy- بدلاً من المحرك الخاص بك. هذا بسبب إعداد بيئة الاختبار؛ يحتاج المحرك إلى تطبيق كمضيف لاختبار وظائفه الرئيسية، وخاصة أجهزة التحكم. هذا يعني أنه إذا كنت ستجعل GET نموذجية إلى وحدة التحكم في اختبار وظيفية وحدة تحكم مثل هذا:
هناك مسألة تستحق الأخذ في الحسبان عند كتابة الاختبارات الوظيفية وهي أن الاختبارات ستعمل على تطبيق - تطبيق test/dummy - بدلًا من المحرك الخاص بك. هذا بسبب إعداد بيئة الاختبار؛ يحتاج المحرك إلى تطبيق كمضيف لاختبار وظائفه الرئيسية، وخاصة أجهزة التحكم. هذا يعني أنه إذا كنت ستجعل GET نموذجية إلى وحدة التحكم في اختبار وظيفية وحدة تحكم مثل هذا:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
 
  class FooControllerTest < ActionDispatch::IntegrationTest
 class FooControllerTest < ActionDispatch::IntegrationTest
    include Engine.routes.url_helpers
 
   include Engine.routes.url_helpers
    def test_index
 
      get foos_url
   def test_index
      ...
 
    end
     get foos_url
  end
 
end
     ...
 
   end
 
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


قد لا يعمل بشكل صحيح. وذلك لأن التطبيق لا يعرف كيفية توجيه هذه الطلبات إلى المحرك ما لم تخبره بوضوح كيف. للقيام بذلك، يجب عليك تعيين نسخة متغير routes@ على مسار المحرك الذي عُين في رمز الإعداد الخاص بك:
قد لا يعمل بشكل صحيح وذلك لأن التطبيق لا يعرف كيفية توجيه هذه الطلبات إلى المحرك ما لم تخبره بوضوح كيف. للقيام بذلك، يجب عليك تعيين متغير النسخة <code>routes@</code> إلى مسار توجيه المحرك الذي عُيِّن في شيفرة الإعداد الخاصة بك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
module Blorgh
module Blorgh
 
  class FooControllerTest < ActionDispatch::IntegrationTest
 class FooControllerTest < ActionDispatch::IntegrationTest
    include Engine.routes.url_helpers
 
   include Engine.routes.url_helpers
    setup do
 
      @routes = Engine.routes
   setup do
    end
 
     @routes = Engine.routes
    def test_index
 
      get foos_url
   end
      ...
 
    end
   def test_index
  end
 
     get foos_url
 
     ...
 
   end
 
 end
 
end
end
</syntaxhighlight>
</syntaxhighlight>


هذا يخبر التطبيق أنك لا تزال ترغب في تنفيذ طلب GET إلى إجراء index لوحدة التحكم هذه، ولكنك تريد استخدام مسار المحرك للوصول إلى هناك، بدلاً من مسار التطبيق.
هذا يخبر التطبيق أنك لا تزال ترغب في تنفيذ طلب GET إلى الإجراء index لوحدة التحكم هذه، ولكنك تريد استخدام مسار المحرك للوصول إلى هناك، بدلًا من مسار التطبيق.


يضمن هذا أيضًا أن تعمل أدوات مساعدة عنوان URL الخاصة بالمحرك كما هو متوقع في اختباراتك.
يضمن هذا أيضًا أن يعمل مساعدي عنوان URL الخاصة بالمحرك كما هو متوقع في اختباراتك.


== تحسين وظائف المحرك ==
== تحسين وظائف المحرك ==
يشرح هذا القسم كيفية إضافة و / أو تجاوز وظائف نموذج MVC للمحرك في تطبيق Rails الرئيسي.
يشرح هذا القسم كيفية إضافة و/أو تجاوز وظائف نموذج MVC للمحرك في تطبيق ريلز الرئيسي.


=== تجاوز النماذج ووحدات التحكم ===
=== استبدال النماذج ووحدات التحكم ===
توسع فئات المحرك ونموذج وحدة التحكم عن طريق فتحها في تطبيق Rails الرئيسي (نظرًا لأن فئات النموذج ووحدة التحكم ليست سوى فئات Ruby التي ترث وظيفة معينة في Rails). فتح تصنيف صنف المحرك يعيد تعريفه للاستخدام في التطبيق الرئيسي. تنفذ ذلك عادةً باستخدام نمط الديكور.
يمكن توسيع نموذج المحرك وأصناف وحدة التحكم عن طريق فتحها في تطبيق ريلز الرئيسي (نظرًا لأن أصناف النموذج ووحدة التحكم ليست سوى أصناف [[Ruby|روبي]] ترث وظيفة معينة في ريلز). فتح تصنيف (open classing) صنف المحرك يعيد تعريفه للاستخدام في التطبيق الرئيسي. يُنفَّذ ذلك عادةً باستخدام نمط المزخرف (decorator pattern).


بالنسبة لتعديلات صنف بسيطة، استخدم Class # class_eval. لتعديلات صنف معقدة، ضع في اعتبارك استخدام ActiveSupport :: Concern.
بالنسبة لتعديلات صنف بسيطة، استخدم التابع <code>Class.class_eval</code>. لتعديلات صنف معقدة، ضع في اعتبارك استخدام <code>ActiveSupport::Concern</code>.


==== ملاحظة حول Decorators و Code Loading ====
==== ملاحظة حول شيفرة المزخرفات والتحميل ====
نظرًا لأن هذه الأجهزة لا تشار إليها من خلال تطبيق Rails نفسه، فلن يحدث نظام التحميل التلقائي الخاص بـ Rails بتحميل واستخدام أدوات الديكور الخاصة بك. هذا يعني أنك تحتاج إلى طلبها بنفسك.
نظرًا لأن هذه المزخرفات (decorators) لا يُشَار إليها من خلال تطبيق ريلز نفسه، فلن يُطبَّق نظام التحميل التلقائي الخاص بريلز لتحميل المزخرفات الخاصة بك. هذا يعني أنك تحتاج إلى طلبها بنفسك.


إليك بعض نماذج الكود للقيام بذلك:
إليك شيفرة نموذجية توضح كيفية القيام بذلك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<nowiki>#</nowiki> lib/blorgh/engine.rb
# lib/blorgh/engine.rb
 
module Blorgh
module Blorgh
 
  class Engine < ::Rails::Engine
 class Engine < ::Rails::Engine
    isolate_namespace Blorgh
 
   isolate_namespace Blorgh
    config.to_prepare do
 
      Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
   config.to_prepare do
        require_dependency(c)
 
      end
     Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
    end
 
  end
       require_dependency(c)
end
 
     end
 
   end
 
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


هذا لا ينطبق فقط على Decorators، ولكن أي شيء تضيفه في محرك لا يشار إليه بواسطة التطبيق الرئيسي.
هذا لا ينطبق فقط على المزخرفات (Decorators)، ولكن أي شيء تضيفه في محرك لا يشار إليه بواسطة التطبيق الرئيسي.


==== تنفيذ نمط Decorator باستخدام  Class # class_eval ====
==== تنفيذ نمط المزخرف باستخدام التابع <code>Class.class_eval</code> ====
إضافة Article#time_since_created:
'''إضافة''' <code>Article.time_since_created</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<nowiki>#</nowiki> MyApp/app/decorators/models/blorgh/article_decorator.rb
# MyApp/app/decorators/models/blorgh/article_decorator.rb
 
Blorgh::Article.class_eval do
Blorgh::Article.class_eval do
 
  def time_since_created
 def time_since_created
    Time.current - created_at
 
  end
   Time.current - created_at
end
 
</syntaxhighlight><syntaxhighlight lang="rails">
 end
# Blorgh/app/models/article.rb
 
End
 
<nowiki>#</nowiki> Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
class Article < ApplicationRecord
 
  has_many :comments
 has_many :comments
end
 
</syntaxhighlight>'''استبدال''' <code>Article.summary</code>:
End
</syntaxhighlight>
 
تجاوز Article#summary:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<nowiki>#</nowiki> MyApp/app/decorators/models/blorgh/article_decorator.rb
# MyApp/app/decorators/models/blorgh/article_decorator.rb
 
Blorgh::Article.class_eval do
Blorgh::Article.class_eval do
 
  def summary
 def summary
    "#{title} - #{truncate(text)}"
 
  end
   "#{title} - #{truncate(text)}"
end
 
</syntaxhighlight><syntaxhighlight lang="rails">
 end
# Blorgh/app/models/article.rb
 
End
 
<nowiki>#</nowiki> Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
class Article < ApplicationRecord
 
  has_many :comments
 has_many :comments
  def summary
 
    "#{title}"
 def summary
  end
 
   "#{title}"
 
 end
 
end
end
</syntaxhighlight>
</syntaxhighlight>


==== تنفيذ نمط المصمم باستخدام ActiveSupport::Concern ====
==== تنفيذ نمط المزخرف باستخدام <code>ActiveSupport::Concern</code> ====
يعد استخدام Class #evalal # مثاليًا لإجراء عمليات ضبط بسيطة، ولكن بالنسبة لتعديلات صنف أكثر تعقيدًا، قد ترغب في استخدام ActiveSupport :: Concern. يدير ActiveSupport :: Concern ترتيب التحميل للوحدات والفئات التابعة المترابطة في وقت التشغيل مما يسمح لك بتهيئة كودك بشكل كبير.
يعد استخدام <code>Class.class_eval</code> مثاليًا لإجراء عمليات ضبط بسيطة، ولكن بالنسبة لتعديلات صنف أكثر تعقيدًا، قد ترغب في استخدام <code>[http://api.rubyonrails.org/v5.2.2.1/classes/ActiveSupport/Concern.html ActiveSupport::Concern]</code>. يدير [http://api.rubyonrails.org/v5.2.2.1/classes/ActiveSupport/Concern.html <code>ActiveSupport::Concern</code>] ترتيب التحميل للوحدات والأصناف التابعة المترابطة في وقت التشغيل مما يسمح لك بتهيئة شيفرتك بشكل كبير.


إضافة Article#time_since_created وتجاوز Article#summary:
إضافة <code>Article.time_since_created</code> واستبدال <code>Article.summary</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<nowiki>#</nowiki> MyApp/app/models/blorgh/article.rb
# MyApp/app/models/blorgh/article.rb
 
class Blorgh::Article < ApplicationRecord
class Blorgh::Article < ApplicationRecord
 
  include Blorgh::Concerns::Models::Article
 include Blorgh::Concerns::Models::Article
 
  def time_since_created
 def time_since_created
    Time.current - created_at
 
  end
   Time.current - created_at
 
  def summary
 end
    "#{title} - #{truncate(text)}"
 
  end
 def summary
end
 
</syntaxhighlight><syntaxhighlight lang="rails">
   "#{title} - #{truncate(text)}"
# Blorgh/app/models/article.rb
 
 end
 
End
 
<nowiki>#</nowiki> Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
class Article < ApplicationRecord
 
  include Blorgh::Concerns::Models::Article
 include Blorgh::Concerns::Models::Article
end
 
</syntaxhighlight><syntaxhighlight lang="rails">
End
# Blorgh/lib/concerns/models/article.rb
 
<nowiki>#</nowiki> Blorgh/lib/concerns/models/article.rb
 
module Blorgh::Concerns::Models::Article
module Blorgh::Concerns::Models::Article
 
  extend ActiveSupport::Concern
 extend ActiveSupport::Concern
 
  # بتقييم الشيفرة المضمنة في السياق 'included do' يتسبب
 # 'included do' causes the included code to be evaluated in the
  # بدلًا من تنفيذها في سياق  (article.rb) حيث ضُمِّنَت
 
  # (blorgh/concerns/models/article) الوحدة
 # context where it is included (article.rb), rather than being
  included do
 
    attr_accessor :author_name
 # executed in the module's context (blorgh/concerns/models/article).
    belongs_to :author, class_name: "User"
 
 included do
    before_validation :set_author
 
   attr_accessor :author_name
    private
 
      def set_author
   belongs_to :author, class_name: "User"
        self.author = User.find_or_create_by(name: author_name)
 
      end
    before_validation :set_author
  end
 
   private
  def summary
 
    "#{title}"
     def set_author
  end
 
       self.author = User.find_or_create_by(name: author_name)
  module ClassMethods
 
    def some_class_method
     end
      'some class method string'
 
    end
 end
  end
 
 def summary
 
   "#{title}"
 
 end
 
  module ClassMethods
 
   def some_class_method
 
     'some class method string'
 
   end
 
 end
 
end
end
</syntaxhighlight>
</syntaxhighlight>


=== تجاوز الواجهات ===
=== استبدال الواجهات ===
عندما يبحث Rails عن الواجهة التي ستعرض، فسيظهر أولاً في دليل app/views للتطبيق. إذا لم تتمكن من العثور على الواجهة هناك، فستتحقق من مجلدات app/views لجميع المحركات التي تحتوي على هذا الدليل.
عندما يبحث ريلز عن الواجهة التي ستعرض، سيبحث أولًا في المجلد app/views للتطبيق. إذا لم يتمكن من العثور على الواجهة هناك، فسيتحقق من المجلدات الموجودة في app/views لجميع المحركات التي تحتوي على هذا المجلد.


عندما يُطلب من التطبيق تقديم واجهة لإجراء مؤشر Blorgh :: ArticlesController، سيبحث أولاً عن المسار app/views/blorgh/articles/index.html.erb ضمن التطبيق. إذا لم تتمكن من العثور عليه، فسيظهر داخل المحرك.
عندما يُطلب من التطبيق تصيير واجهة <code>Blorgh::ArticlesController</code> للإجراء <code>index</code>، سيبحث أولًا عن المسار app/views/blorgh/articles/index.html.erb ضمن التطبيق. إذا لم تتمكن من العثور عليه، فسيظهر داخل المحرك.


يمكنك تجاوز هذه الواجهة في التطبيق ببساطة عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb. بعد ذلك، يمكنك تغيير طريقة عرض هذه الواجهة تمامًا.
يمكنك تجاوز هذه الواجهة واستبدالها في التطبيق ببساطة عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb. بعد ذلك، يمكنك تغيير طريقة عرض هذه الواجهة تمامًا.


جرب هذا الآن عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb ووضع هذا المحتوى فيه:
جرب هذا الآن عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb ووضع هذا المحتوى فيه:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="html">
<nowiki><h1>Articles</h1></nowiki>
<h1>Articles</h1>
 
<%= link_to "New Article", new_article_path %>
<%= link_to "New Article", new_article_path %>
<% @articles.each do |article| %>
<% @articles.each do |article| %>
 
  <h2><%= article.title %></h2>
 <nowiki><h2><%= article.title %></h2></nowiki>
  <small>By <%= article.author %></small>
 
  <%= simple_format(article.text) %>
 <nowiki><small>By <%= article.author %></small></nowiki>
  <hr>
 
 <%= simple_format(article.text) %>
 
 <nowiki><hr></nowiki>
 
<% end %>
<% end %>
</syntaxhighlight>
</syntaxhighlight>


=== المسارات ===
=== المسارات الموجهة ===
تعزل المسارات داخل المحرك من التطبيق بشكل افتراضي. وذلك عن طريق استدعاء isolate_namespace داخل صنف Engine. وهذا يعني بالضرورة أن التطبيق ومحركاته يمكن أن يكون لهما مسارات مسماة بشكل مماثل ولن يتعارضان.
تُعزَل المسارات داخل المحرك من التطبيق بشكل افتراضي وذلك عن طريق استدعاء <code>isolate_namespace</code> داخل الصنف <code>Engine</code>. وهذا يعني بالضرورة أن التطبيق ومحركاته يمكن أن يكون لهما مسارات لها نفس الاسم دون أن يحدث أي تعارض.


ترسم المسارات داخل المحرك في صنف المحرك داخل config / routes.rb، على النحو التالي:
تُحدَّد المسارات الموجهة داخل المحرك في الصنف <code>Engine</code> داخل config/routes.rb، على النحو التالي:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
Blorgh::Engine.routes.draw do
Blorgh::Engine.routes.draw do
 
  resources :articles
 resources :articles
end
 
End
</syntaxhighlight>
</syntaxhighlight>


من خلال وجود طرق منعزلة مثل هذا، إذا كنت ترغب في الارتباط بمنطقة من المحرك من داخل التطبيق، فستحتاج إلى استخدام تابع بروكسي لتوجيه المحرك. قد تنتهي الاستدعاءات إلى توابع التوجيه العادية، مثل articles_path، إلى الذهاب إلى مواقع غير مرغوبة إذا كان لكل من التطبيق والمحرك تعريف مثل هذا المساعد.
من خلال وجود مسارات موجهة منعزلة مثل هذا، إذا كنت ترغب في ربط (link) منطقة من المحرك من داخل التطبيق، فستحتاج إلى استخدام تابع وسيط لتوجيه المحرك. قد تنتهي الاستدعاءات إلى توابع التوجيه العادية، مثل <code>articles_path</code>، إلى الذهاب إلى مواقع غير مرغوبة إذا كان لكل من التطبيق والمحرك تعريف مثل هذا المساعد.


على سبيل المثال، سيذهب المثال التالي إلى article_path للتطبيق إذا عُرض هذا القالب من التطبيق، أو article_path الخاص بالمحرك إذا عُرض من المحرك:
على سبيل المثال، سيذهب المثال التالي إلى <code>article_path</code> للتطبيق إذا عُرض هذا القالب من التطبيق، أو <code>article_path</code> الخاص بالمحرك إذا عُرض من المحرك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<%= link_to "Blog articles", articles_path %>
<%= link_to "Blog articles", articles_path %>
</syntaxhighlight>
</syntaxhighlight>


ولجعل هذا المسار دائمًا يستخدم تابع مساعد التوجيه article_path للمحرك، يجب علينا استدعاء التابع على تابع بروكسي التوجيه الذي يشترك في نفس اسم المحرك.
ولجعل هذا المسار دائمًا يستخدم تابع مساعد التوجيه <code>article_path</code> للمحرك، يجب علينا استدعاء التابع على تابع التوجيه الوسيط الذي يشترك في نفس اسم المحرك.
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<%= link_to "Blog articles", blorgh.articles_path %>
<%= link_to "Blog articles", blorgh.articles_path %>
</syntaxhighlight>
</syntaxhighlight>


إذا كنت تريد الرجوع إلى التطبيق داخل المحرك بطريقة مماثلة ، فاستخدم المساعد main_app:
إذا كنت تريد الرجوع إلى التطبيق داخل المحرك بطريقة مماثلة، فاستخدم المساعد <code>main_app</code>:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<%= link_to "Home", main_app.root_path %>
<%= link_to "Home", main_app.root_path %>
</syntaxhighlight>
</syntaxhighlight>


إذا كنت تستخدم هذا داخل المحرك، فإنه سوف يذهب دائما إلى جذر التطبيق. إذا كنت ستترك استدعاء تابع "بروكسي التوجيه" main_app، فمن المحتمل أن تذهب إلى جذر المحرك أو التطبيق، حسب المكان الذي اتصل به منه.
إذا كنت تستخدم هذا داخل المحرك، فإنه سوف يذهب دائما إلى جذر التطبيق. إذا كنت ستترك استدعاء تابع "وسيط التوجيه" <code>main_app</code>، فمن المحتمل أن تذهب إلى جذر المحرك أو التطبيق، حسب المكان الذي اتصل به منه.


إذا حاول قالب قُدم من داخل محرك ما استخدام أحد توابع مساعد التوجيه للتطبيق، فقد ينتج عنه استدعاء تابع غير محدد. إذا واجهتك مشكلة من هذا القبيل، فتأكد من أنك لا تحاول استدعاء تابع توجيه التطبيق بدون بادئة main_app من داخل المحرك.
إذا حاول قالب صُيِّر من داخل محرك ما استخدام أحد توابع مساعد التوجيه للتطبيق، فقد ينتج عنه استدعاء تابع غير محدد. إذا واجهتك مشكلة من هذا القبيل، فتأكد من أنك لا تحاول استدعاء تابع توجيه التطبيق بدون البادئة <code>main_app</code> من داخل المحرك.


=== الأصول ===
=== الأصول ===
تعمل الأصول داخل المحرك بطريقة مماثلة لتطبيق كامل. نظرًا لأن صنف المحرك ترث من Rails :: Engine، سيعرف التطبيق البحث عن الأصول في المحرك app/assets والمجلدات  lib/assets.
تعمل الأصول داخل المحرك بطريقة مماثلة لتطبيق كامل. نظرًا لأن الصنف <code>Engine</code> يرث من <code>Rails::Engine</code>، سيعرف التطبيق البحث عن الأصول في المحرك app/assets والمجلدات  lib/assets.


مثل جميع المكونات الأخرى للمحرك، يجب أن تكون الأصول ذات تسميات. وهذا يعني أنه إذا كان لديك اصل يسمى style.css، فيجب وضعه في app/assets/stylesheets/[engine name]/style.css، بدلاً من app/assets/stylesheets/style.css. إذا لم يكن الأصل هذا يحمل أسماء، فمن المحتمل أن يكون لدى التطبيق المضيف أصل مسمى بشكل مطابق، وفي هذه الحالة يكون لأصل التطبيق الأولوية و سيتجاهل المحرك.
مثل جميع المكونات الأخرى للمحرك، يجب أن تكون الأصول ضمن مجال اسم. وهذا يعني أنه إذا كان لديك أصل باسم style.css، فيجب وضعه في app/assets/stylesheets/[engine name]/style.css، بدلًا من app/assets/stylesheets/style.css. إذا لم يكن لدى هذا الأصل مجال اسم، فمن المحتمل أن يكون لدى التطبيق المضيف أصلًا يملك نفس الاسم تمامًا، وفي هذه الحالة يكون لأصل التطبيق الأولوية وسيتجاهل المحرك.


تخيل أن لديك أصل موجود في app/assets/stylesheets/blorgh/style.css لتضمين الأصل هذا داخل أحد التطبيقات، ما عليك سوى استخدام stylesheet_link_tag والإشارة إلى الأصل كما لو كان داخل المحرك:
تخيل أن لديك أصلًا موجودًا في app/assets/stylesheets/blorgh/style.css لتضمين الأصل هذا داخل أحد التطبيقات، ما عليك سوى استخدام <code>stylesheet_link_tag</code> والإشارة إلى الأصل كما لو كان داخل المحرك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
<%= stylesheet_link_tag "blorgh/style.css" %>
<%= stylesheet_link_tag "blorgh/style.css" %>
</syntaxhighlight>
</syntaxhighlight>


يمكنك أيضًا تحديد هذه الأصول باعتبارها تبعيات للإصول الأخرى باستخدام Asset Pipeline تتطلب جمل في الملفات التي تمت معالجتها:
يمكنك أيضًا تحديد هذه الأصول باعتبارها تبعيات للأصول الأخرى باستخدام [[Rails/asset pipeline|Asset Pipeline]] تتطلب تعابير في الملفات التي تمت معالجتها:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
/*
/*
 
*= require blorgh/style
<nowiki>*</nowiki>= require blorgh/style
*/
 
<nowiki>*</nowiki>/
</syntaxhighlight>
</syntaxhighlight>


ملاحظة: تذكر أنه من أجل استخدام لغات مثل Sass أو CoffeeScript، يجب عليك إضافة المكتبة ذات الصلة إلى gemspec. الخاص بالمحرك.
'''ملاحظة''': تذكر أنه من أجل استخدام لغات مثل [[Sass]] أو [[CoffeeScript]]، يجب عليك إضافة المكتبة ذات الصلة إلى <code>gemspec.</code> الخاص بالمحرك.


=== الأصول المنفصلة و Precompiling ===
=== الأصول المنفصلة والتصريف المسبق ===
هناك بعض الحالات التي لا يتطلب فيها التطبيق المضيف أصول محركك. على سبيل المثال، لنفترض أنك أنشأت وظيفة مسؤول لا تتوفر إلا لمحركك. في هذه الحالة، لا يحتاج التطبيق المضيف إلى طلب admin.css أو admin.js. يحتاج تخطيط المشرف فقط إلى هذه الأصول. من غير المعقول أن يتضمن التطبيق المضيف "blorgh / admin.css" في صفحات الأنماط الخاصة به. في هذه الحالة، يجب أن تُعرف هذه الأصول بشكل واضح من أجل التحويل المسبق. هذا يخبر Sprockets لإضافة أصول المحرك الخاص بك عند تشغيل bin/rails assets:precompile.
هناك بعض الحالات التي لا يتطلب فيها التطبيق المضيف أصول محركك. على سبيل المثال، لنفترض أنك أنشأت وظيفة مسؤول لا تتوفر إلا لمحركك. في هذه الحالة، لا يحتاج التطبيق المضيف إلى طلب admin.css أو admin.js. يحتاج تخطيط المشرف فقط إلى هذه الأصول. من غير المعقول أن يتضمن التطبيق المضيف "blorgh/admin.css" في صفحات الأنماط الخاصة به. في هذه الحالة، يجب أن تُعرَّف هذه الأصول بشكل واضح من أجل التصريف المسبق (precompilation). هذا يخبر Sprockets لإضافة أصول المحرك الخاص بك عند تشغيل <code>bin/rails assets:precompile</code>.


يمكنك تعريف الأصول للتحليل المسبق في engine.rb:
يمكنك تعريف الأصول للتصريف المسبق في engine.rb:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
initializer "blorgh.assets.precompile" do |app|
initializer "blorgh.assets.precompile" do |app|
 
  app.config.assets.precompile += %w( admin.js admin.css )
 app.config.assets.precompile += %w( admin.js admin.css )
end
 
End
</syntaxhighlight>
</syntaxhighlight>


لمزيد من المعلومات، اقرأ دليل الأصول pipeline.
لمزيد من المعلومات، اقرأ دليل <nowiki/>[[Rails/asset pipeline|Asset Pipeline]].


=== تبعيات الجوهرة الاخرى ===
=== تبعيات الجوهرة الأخرى ===
يجب تحديد تبعيات Gem داخل المحرك داخل ملف gemspec. في جذر المحرك. والسبب هو أن المحرك قد يركب كجوهرة. إذا كان من المقرر تحديد التبعيات داخل Gemfile، فلن يتعرف عليها من خلال تثبيت جوهرة تقليدية وبالتالي لن تُثبت، مما يؤدي إلى خلل في المحرك.
يجب تحديد تبعيات الجوهرة داخل المحرك داخل الملف <code>gemspec.</code> في جذر المحرك. والسبب هو أن المحرك قد يُثبَّت كجوهرة. إذا كان من المقرر تحديد التبعيات داخل الملف Gemfile، فلن يُتعرَّف عليها من خلال تثبيت جوهرة تقليدية وبالتالي لن تُثبَّت، مما يؤدي إلى خلل في المحرك.


لتحديد تبعية يجب تثبيتها مع المحرك أثناء gem install التقليدية، عينها داخل كتلة Gem :: Specification الموجودة داخل الملف gemspec. في المحرك:
لتحديد تبعية يجب تثبيتها مع المحرك أثناء تنفيذ الأمر <code>gem install</code> الاعتيادي، حددها داخل الكتلة <code>Gem::Specification</code> الموجودة داخل الملف <code>gemspec.</code> في المحرك:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
s.add_dependency "moo"
s.add_dependency "moo"
</syntaxhighlight>
</syntaxhighlight>


لتحديد تبعية لا يجب تثبيتها إلا كإعتماد على تطوير التطبيق، تُعين على النحو التالي:
لتحديد تبعية لا يجب تثبيتها إلا كاعتماد في بيئة التطوير للتطبيق، حددها على النحو التالي:<syntaxhighlight lang="rails">
 
s.add_development_dependency "moo"
s.add_development_dependency "moo"
</syntaxhighlight>تُثبت كلا النوعين من التبعيات عند تنفيذ الأمر <code>bundle install</code> داخل التطبيق. لن يستخدم تبعيات التطوير للجوهرة إلا عند تشغيل اختبارات المحرك.


تُثبت كلا النوعين من التبعيات عند تشغيل bundle install داخل التطبيق. لن يستخدم تبعيات التطوير للجوهرة إلا عند تشغيل اختبارات المحرك.
لاحظ أنه إذا كنت ترغب في طلب تبعيات على الفور عندما يكون المحرك مطلوبًا، فيجب عليك طلب ذلك قبل تهيئة المحرك. فمثلًا:
 
لاحظ أنه إذا كنت ترغب في طلب تبعيات على الفور عندما يكون المحرك مطلوبًا، فيجب عليك طلب ذلك قبل تهيئة المحرك. فمثلا:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
require 'other_engine/engine'
require 'other_engine/engine'
require 'yet_another_engine/engine'
require 'yet_another_engine/engine'
 
module MyEngine
module MyEngine
 
  class Engine < ::Rails::Engine
 class Engine < ::Rails::Engine
  end
 
end
 end
 
End
</syntaxhighlight>
</syntaxhighlight>


== الدعم النشط لتحميل التركيب ==
== Active Support وخطافات On Load ==
الدعم النشط هو مكون Ruby on Rails المسؤول عن توفير ملحقات اللغة Ruby والأدوات المساعدة والأدوات المستعرضة الأخرى.
[[Rails/active support|Active Support]] هو مكون ريلز المسؤول عن توفير ملحقات لغة روبي والأدوات المساعدة والأدوات الأخرى.


كود Rails غالبًا ما يُشار إليه عند تحميل التطبيق. Rails هي المسؤولة عن ترتيب تحميل هذه الأطر، لذلك عند تحميل الأطر، مثل ActiveRecord :: Base، قبل الأوان أنت تنتهك عقدًا ضمنيًا يتضمنه تطبيقك مع Rails. علاوة على ذلك، من خلال تحميل التعليمات البرمجية مثل ActiveRecord :: Base على التمهيد من التطبيق الخاص بك، فإنك تقوم بتحميل إطارات كاملة قد تبطئ وقت الإقلاع وقد تتسبب في حدوث تعارض مع ترتيب التحميل والتمهيد من التطبيق الخاص بك.
شيفرة ريلز يمكن غالبًا الإشارة إليها كمرجع عند تحميل التطبيق. ريلز هي المسؤولة عن ترتيب تحميل هذه الأطر، لذلك عند تحميل الأطر، مثل <code>ActiveRecord::Base</code>، قبل الأوان، أنت تنتهك عقدًا ضمنيًا يتضمنه تطبيقك مع ريلز. علاوة على ذلك، من خلال تحميل الشيفرة مثل <code>ActiveRecord::Base</code> عند اقلاع التطبيق الخاص بك، فإنك تقوم بتحميل إطارات كاملة قد تبطئ وقت الإقلاع وقد تتسبب في حدوث تعارض مع ترتيب التحميل والتمهيد من التطبيق الخاص بك.


تحميل التركيب هي واجهة برمجة التطبيقات التي تسمح لك للتحميل في عملية التهيئة دون انتهاك عقد التحميل مع Rails. سيؤدي ذلك أيضًا إلى تخفيف تدهور أداء التمهيد وتجنب التعارضات.
الخطافات On Load هي واجهة برمجة التطبيقات التي تسمح لك بالتحميل في عملية الإقلاع دون انتهاك عقد التحميل مع ريلز. سيؤدي ذلك أيضًا إلى تخفيف تدهور أداء الإقلاع وتجنب التعارضات.
 
== ما هي خطافات on_load ؟ ==
بما أن روبي هي لغة ديناميكية، فإن بعض الأكواد سيؤدي إلى تحميل أطر Rails المختلفة. خذ هذا المقتطف على سبيل المثال:


== ما هي خطافات on_load؟ ==
بما أن روبي هي لغة ديناميكية، فإن بعض الشيفرات سيؤدي إلى تحميل أطر ريلز المختلفة. خذ هذا المقتطف على سبيل المثال:<syntaxhighlight lang="rails">
ActiveRecord::Base.include(MyActiveRecordHelper)
ActiveRecord::Base.include(MyActiveRecordHelper)
</syntaxhighlight>يعني هذا المقتطف أنه عند تحميل هذا الملف، سيُنفَّذ <code>ActiveRecord::Base</code>. تؤدي هذا التنفيذ إلى قيام روبي بالبحث عن تعريف هذا الثابت وسيتطلبه عبر <code>require</code>. يؤدي هذا لتحميل إطار [[Rails/active record|Active Record]]  بالكامل في الإقلاع.


يعني هذا المقتطف أنه عند تحميل هذا الملف، سيواجه ActiveRecord :: Base. تؤدي هذه المواجهة إلى قيام روبي بالبحث عن تعريف هذا الثابت وسيتطلب فعل ذلك. يؤدي هذا لتحميل إطار Active Record  بالكامل  في التمهيد.
<code>ActiveSupport.on_load</code> هو آلية يمكن استخدامها لتأجيل تحميل الشيفرة إلى أن تكون ضرورية بالفعل. يمكن تغيير المقتطف أعلاه إلى:
 
ActiveSupport.on_load هي آلية يمكن استخدامها لتأجيل تحميل الشفرة إلى أن تكون ضرورية بالفعل. يمكن تغيير المقتطف أعلاه إلى:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper }
ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper }
</syntaxhighlight>
</syntaxhighlight>


يتضمن هذا المقتطف الجديد MyActiveRecordHelper فقط عند تحميل ActiveRecord :: Base.
يتضمن هذا المقتطف الجديد <code>MyActiveRecordHelper</code> فقط عند تحميل <code>ActiveRecord::Base</code>.


== كيف يعمل؟ ==
== كيف يعمل؟ ==
في إطار Rails تُستدعى هذه الخطافات عند تحميل مكتبة معينة. على سبيل المثال، عند تحميل ActionController :: Base، تُستدعى hook: action_controller_base. هذا يعني أن جميع الاستدعاءات ActiveSupport.on_load مع خطافات  action_controller_base: تستدعى في سياق ActionController :: Base (وهذا يعني أن self سوف يكون ActionController :: Base).
في إطار ريلز، تُستدعى هذه الخطافات عند تحميل مكتبة معينة. على سبيل المثال، عند تحميل <code>ActionController::Base</code>، يُستدعَى الخطاف ‎<code>:action_controller_base</code>. هذا يعني أن جميع الاستدعاءات <code>ActiveSupport.on_load</code> مع خطافات  <code>action_controller_base:</code> تستدعى في سياق <code>ActionController::Base</code> (وهذا يعني أن <code>self</code> سوف يكون <code>ActionController::Base</code>).


== تعديل التعليمات البرمجية لاستخدام on_load hooks ==
== تعديل الشيفرة لاستخدام خطافات on_load ==
تعديل التعليمات بشكل عام بسيط. إذا كان لديك سطر من التعليمات البرمجية يشير إلى إطار Rails مثل ActiveRecord :: Base يمكنك التفاف ذلك الرمز في خطاف on_load.
تعديل الشيفرة بشكل عام بسيط. إذا كان لديك سطر من التعليمات البرمجية يشير إلى إطار ريلز مثل <code>ActiveRecord::Base</code> يمكنك تغليف تلك الشيفرة في خطاف <code>on_load</code>.


=== مثال 1 ===
=== مثال 1 ===
سطر 1٬105: سطر 901:
</syntaxhighlight>
</syntaxhighlight>


يصبح
يصبح:
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper }
ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper } # self refers to ActiveRecord::Base here, so we can simply #include


<nowiki>#</nowiki>self تشير إلى ActiveRecord :: Base هنا، لذلك يمكننا ببساطة # تضمينها.
</syntaxhighlight>


=== مثال 2 ===
=== مثال 2 ===
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
ActionController::Base.prepend(MyActionControllerHelper)
ActionController::Base.prepend(MyActionControllerHelper)
</syntaxhighlight>يصبح:<syntaxhighlight lang="rails">
ActiveSupport.on_load(:action_controller_base) { prepend MyActionControllerHelper }
# #prepend لذا يمكننا ببساطة ،ActionController::Base هنا إلى self تشير
</syntaxhighlight>
</syntaxhighlight>
يصبح
<syntaxhighlight lang="rails">
ActiveSupport.on_load(:action_controller_base) { prepend MyActionControllerHelper }
Self تُشير إلى ActionController :: Base هنا، لذلك يمكننا ببساطة #prepend.


=== مثال 3 ===
=== مثال 3 ===
<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
ActiveRecord::Base.include_root_in_json = true
ActiveRecord::Base.include_root_in_json = true
</syntaxhighlight>يصبح:<syntaxhighlight lang="rails">
ActiveSupport.on_load(:active_record) { self.include_root_in_json = true }
# ActiveRecord::Base هنا إلى self يشير
</syntaxhighlight>
</syntaxhighlight>
يصبح
<syntaxhighlight lang="rails">
ActiveSupport.on_load(:active_record) { self.include_root_in_json = true }
</syntaxhighlight>
يشير self إلى ActiveRecord :: Base هنا.


== الخطافات المتاحة ==
== الخطافات المتاحة ==
هذه هي الخطافات التي يمكنك استخدامها في التعليمات البرمجية الخاصة بك.
ستجد في هذا القسم الخطافات التي يمكنك استخدامها في الشيفرة الخاصة بك.


للربط في عملية التهيئة لإحدى الفئات التالية استخدم الخطاف المتوفر.
للربط في عملية التهيئة لإحدى الأصناف التالية، استخدم الخطاف المتوفر.
{| class="wikitable"
{| class="wikitable"
|الصنف
!الصنف
|الخطاف المتاح
!الخطاف المتاح
|-
|-
|ActionCable
|<code>ActionCable</code>
|action_cable
|<code>action_cable</code>
|-
|-
|ActionController::API
|<code>ActionController::API</code>
|action_controller_api
|<code>action_controller_api</code>
|-
|-
|ActionController::API
|<code>ActionController::API</code>
|action_controller
|<code>action_controller</code>
|-
|-
|     ActionController::Base
|<code>ActionController::Base</code>
|action_controller_base
|<code>action_controller_base</code>
|-
|-
|     ActionController::Base
|<code>ActionController::Base</code>
|action_controller
|<code>action_controller</code>
|-
|-
|  ActionController::TestCase
|<code>ActionController::TestCase</code>
|action_controller_test_case
|<code>action_controller_test_case</code>
|-
|-
|      ActionDispatch::IntegrationTest
|<code>ActionDispatch::IntegrationTest</code>
|action_dispatch_integration_test
|<code>action_dispatch_integration_test</code>
|-
|-
|    ActionDispatch::SystemTestCase
|<code>ActionDispatch::SystemTestCase</code>
|action_dispatch_system_test_case
|<code>action_dispatch_system_test_case</code>
|-
|-
|ActionMailer::Base
|<code>ActionMailer::Base</code>
|action_mailer
|<code>action_mailer</code>
|-
|-
|   ActionMailer::TestCase
|<code>ActionMailer::TestCase</code>
|action_mailer_test_case
|<code>action_mailer_test_case</code>
|-
|-
|ActionView::Base
|<code>ActionView::Base</code>
|action_view
|<code>action_view</code>
|-
|-
|ActionView::TestCase
|<code>ActionView::TestCase</code>
|action_view_test_case
|<code>action_view_test_case</code>
|-
|-
|ActiveJob::Base
|<code>ActiveJob::Base</code>
|active_job
|<code>active_job</code>
|-
|-
|ActiveJob::TestCase
|<code>ActiveJob::TestCase</code>
|active_job_test_case
|<code>active_job_test_case</code>
|-
|-
|ActiveRecord::Base
|<code>ActiveRecord::Base</code>
|active_record
|<code>active_record</code>
|-
|-
|ActiveSupport::TestCase
|<code>ActiveSupport::TestCase</code>
|active_support_test_case
|<code>active_support_test_case</code>
|-
|-
|i18n
|<code>i18n</code>
|i18n
|<code>i18n</code>
|}
|}


== إعداد تكوين الخطافات ==
== ضبط الخطافات ==
هذه هي خطافات التكوين المتاحة. فهي لا تعلق في أي إطار معين، بل تعمل في سياق التطبيق بأكمله.
إليك خطافات الضبط المتاحة، فهي لا تُعلِّق (hook) في أي إطار معين، بل تعمل في سياق التطبيق بأكمله.
{| class="wikitable"
{| class="wikitable"
|الخطاف
!الخطاف
|حالة الاستخدام
!حالة الاستخدام
|-
|-
|before_configuration
|<code>before_configuration</code>
|تشغيل أول كتلة قابلة للتكوين. تُستدعى قبل أي تمهيد.
|أول كتلة قابلة للضبط يراد تنفيذها. تُستدعى قبل أي تشغيل أية مهيئات.
|-
|-
|    before_initialize
|<code>before_initialize</code>
|تشغيل ثاني كتلة قابلة للتكوين. تُستدعى قبل تهيئة الأطر.
|ثاني كتلة قابلة للضبط يراد تنفيذها. تُستدعى قبل تهيئة الأطر.
|-
|-
|   before_eager_load
|<code>before_eager_load</code>
|تشغيل ثالث كتلة قابلة للتكوين. لا يعمل إذا كان config.eager_load مضبوطًا على false.
|ثالث كتلة ثابلة للضبط يراد تنفيذها. لا يعمل إذا كان <code>config.eager_load</code> مضبوطًا إلى القيمة <code>false</code>.
|-
|-
|after_initialize
|<code>after_initialize</code>
|تشغيل آخر كتلة قابلة لتكوين. تُستدعى بعد تهيئة الأطر.
|آخر كتلة قابلة للضبط يراد تشغيلها. تُستدعى بعد تهيئة الأطر.
|}
|}
 
إليك المثال التالي:<syntaxhighlight lang="rails">
=== مثال ===
<syntaxhighlight lang="rails">
Config.before_configuration { puts 'I am called before any initializers' }
Config.before_configuration { puts 'I am called before any initializers' }
</syntaxhighlight>
</syntaxhighlight>

المراجعة الحالية بتاريخ 16:24، 22 مارس 2019

ستتعرف في هذا الدليل على المحركات (engines) وكيف يمكن استخدامها لتوفير وظائف إضافية لتطبيقاتها المضيفة من خلال واجهة واضحة وسهلة الاستخدام للغاية.

بعد قراءة هذا الدليل، ستتعلم:

  • مما يتكون المحرك.
  • كيف وُلِّد المحرك.
  • كيفية بناء ميزات للمحرك.
  • كيفية ربط المحرك في التطبيق.
  • كيفية استبدال وظيفة المحرك في التطبيق.
  • تجنب تحميل أطر ريلز مع خطافات التحميل (Load Hooks) وخطافات الضبط (Configuration Hooks).

ما هي المحركات؟

تُعدُّ المحركات تطبيقات مصغرة توفر وظائف لتطبيقاتها المضيفة. تطبيق ريلز هو بالفعل محرك "supercharged"، مع الصنف Rails::Application الذي يرث الكثير من سلوكه من Rails::Engine.

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

ترتبط المحركات أيضًا ارتباطًا وثيقًا بالمكونات الإضافية. يشترك الاثنان في بنية المجلد lib، وكلاهما يُنشَأ باستخدام المولِّد rails plugin new. الفرق هو أن المحرك يعتبر "إضافة كاملة" (full plugin) بواسطة ريلز (كما هو مشار إليه في الخيار ‎--full الذي يُمررإلى أمر المولد).

سنستخدم في الواقع الخيار ‎--mountable هنا، والذي يتضمن جميع ميزات ‎--full. سيشير هذا الدليل إلى هذه "الإضافات الكاملة" ببساطة على أنَّها "المحركات" طوال الوقت. يمكن أن يكون المحرك إضافةً ويمكن أن تكون الإضافة محركًا.

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

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

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

لمشاهدة عروض محركات أخرى، تحقق من Devise، وهو محرك يوفر الاستيثاق لتطبيقاته الرئيسية، أو Thredded، وهو محرك يوفر وظائف منتدى (forum). هناك أيضا Spree الذي يوفر منصة تجارة إلكترونية، و Refinery CMS، الذي يعد محرك نظام إدارة محتوى (CMS).

وأخيرًا، لم تكن المحركات ممكنة بدون أعمال James Adam و Piotr Sarnacki وفريق ريلز الأساسي وعدد آخر من الأشخاص. إذا قابلتهم في أي وقت، لا تنسَ أن تقول لهم شكرًا!

توليد محرك

لتوليد محركٍ، سوف تحتاج لتشغيل المولد plugin وتمرير الخيارات بما يتناسب مع الحاجة. لتوليد المثال "blorgh"، ستحتاج إلى إنشاء محرك "قابل للوصل" (mountable)، وتشغيل هذا الأمر في طرفية:

$ rails plugin new blorgh --mountable

يمكن الاطلاع على القائمة الكاملة لخيارات المولد plugin عن طريق الأمر التالي:

$ rails plugin --help

يخبر الخيار mountable-- المولد بأتك تريد إنشاء محرك "قابل للوصل" (mountable) وذي مجال اسم معزول (namespace-isolated). هذا المولد سيوفر نفس الهيكلية كما هو الحال مع الخيار ‎--full. يخبر الخيار ‎--full المولد بأنك تريد إنشاء محركٍ يتضمن البنية الهيكلية التالية:

  • شجرة المجلد app.
  • الملف config/routes.rb:
‎‎Rails.application.routes.draw do
end
  • ملفٌ في lib/blorgh/engine.rb، وهو يماثل وظيفة الملف config/application.rb في تطبيق ريلز القياسي:
module Blorgh
  class Engine < ::Rails::Engine
  end
end

سيضيف الخيار ‎--mountable إلى الخيار ‎--full ما يلي:

  • ملفات بيان الأصول (application.js و application.css).
  • أصل ذو مجال الاسم ApplicationController.
  • أصل ذو مجال الاسم ApplicationHelper.
  • مخطط قالب العرض للمحرك.
  • مجال اسم معزول إلى config/routes.rb:
Blorgh::Engine.routes.draw do
End
  • مجال اسم معزول إلى lib/blorgh/engine.rb:
module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end

بالإضافة إلى ذلك، يخبر الخيار ‎--mountable المولد أن يوصل المحرك داخل تطبيق الاختبار الوهمي الموجود في test/dummy بإضافة ما يلي إلى ملف مسارات التطبيق الوهمي في test/dummy/config/routes.rb:

mount Blorgh::Engine => "/blorgh"

داخل المحرك

الملفات الحرجة

في المجلد الجذر، يولد الملف blorgh.gemspec ضمن المجلد engines الجديد. عندما تضمِّن المحرك في التطبيق لاحقًا، ستستخدم السطر التالي في الملف Gemfile الخاص بتطبيق ريلز:

gem 'blorgh', path: 'engines/blorgh'

لا تنسَ تشغيل الأمر bundle install كالمعتاد. من خلال تحديده كجوهرة داخل Gemfile، سيحمله المُحزِّم (Bundler) على هذا النحو، مع تحليل الملف blorgh.gemspec هذا وطلب ملف يدعى lib/blorgh.rb داخل المجلد lib. هذا الملف يتطلب الملف blorgh/engine.rb (الموجود في lib/blorgh/engine.rb) ويُعرِّف وحدة أساسية تسمى Blorgh.

require "blorgh/engine"
 
module Blorgh
end

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

يقبع ضمن lib/blorgh/engine.rb الصنف الأساسي للمحرك:

module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
  end
end

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

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

بدون هذا، هناك احتمال أن مكونات المحرك يمكن أن "تسرب" في التطبيق، مما تسبب في اضطراب غير مرغوب فيه، أو أن مكونات المحرك المهمة يمكن استبدالها من قبل أشياء مسماة بالاسم نفسه داخل التطبيق. أحد الأمثلة على مثل هذه التضاربات هو المساعدون. دون استدعاء isolate_namespace، سيُضمَّن مساعدو المحرك في وحدات تحكم التطبيق.

ملاحظة: يوصى بشدة أن يترك سطر isolate_namespace داخل تعريف الصنف Engine. بدونه، قد تتضارب الأصناف التي أُنشئت في المحرك مع أحد التطبيقات.

ماذا تعني هذه العزلة في مجال الاسم هي أنَّ النموذج الذي أُنشِئ بواسطة استدعاء bin/rails g model، مثل bin/rails g model article، لن يُسمى Article، ولكن سيكون بدلًا من ذلك مجال اسم يدعى Blorgh::Article. بالإضافة إلى ذلك، يُوضع جدول للنموذج في مجال اسم بدلًا من ذلك، ليصبح blorgh_articles، بدلًا من مجرد articles. وبشكل مماثل لنموذج مجال الأسماء، وحدة تحكم التي تسمى ArticlesController تُصبح Blorgh::ArticlesController، والواجهات لوحدة التحكم لن تكون app/views/articles ولكن ستكون بدلًا من ذلك app/views/blorgh/articles. مرسلي البريد (Mailers) توضع ضمن مجال اسمٍ أيضًا بالأسلوب نفسه أيضًا.

وأخيرًا، ستُعزَل المسارات الموجه داخل المحرك. هذا هو أحد الأجزاء الأكثر أهمية حول تطبيق مجال الأسماء، وسنناقشه لاحقًا في قسم التوجيه في هذا الدليل.

المجلد app

يوجد داخل المجلد app المجلدات assets و controllers و helpers و mailers و models و views القياسية التي يجب أن تكون على دراية بها من أحد التطبيقات. تكون المجلدات helpers و mailers و models فارغةً، لذا لن تُشرَح في هذا القسم. سننظر أكثر في النماذج (models) في قسم لاحق، عندما نكتب المحرك.

ضمن المجلد app/assets، توجد المجلدات images و javascripts و stylesheets التي يجب أن تكون، مرة أخرى، على دراية بها بسبب تشابهها مع أي تطبيق. لكن أحد الاختلافات هنا هو أن كل مجلد يحتوي على مجلد فرعي باسم المحرك. نظرًا لأن هذا المحرك سيوضع ضمن مجال اسم، يجب أن توضع أصوله أيضًا.

يوجد ضمن المجلد app/controllers المجلد blorgh الذي يحتوي على ملف يسمى application_controller.rb. سيوفر هذا الملف أي وظائف مشتركة لوحدات التحكم في المحرك. المجلد blorgh هو المكان الذي سوف تذهب إليه وحدات التحكم الأخرى للمحرك. بوضعها ضمن هذا المجلد الذي يملك مجال اسم، فإنك تمنعهم من التضارب مع وحدات التحكم المسماة باسم مماثل داخل محركات أخرى أو حتى داخل التطبيق.

ملاحظة: يُسمى الصنف ApplicationController داخل المحرك مثل أي تطبيق ريلز لتسهيل عملية تحويل التطبيقات إلى محركات.

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

# app/controllers/blorgh/articles_controller.rb:
require_dependency "blorgh/application_controller"
 
module Blorgh
  class ArticlesController < ApplicationController
    ...
  end
end

تحذير: لا تستعمل require لأنها ستفشل إعادة التحميل التلقائي للأصناف في بيئة التطوير. استعمال require_dependency يضمن أن تُحمَّل الأصناف وتفرَّغ بالطريقة الصحيحة.

وأخيرا، يحتوي المجلد app/views على المجلد layouts، والذي يحتوي على ملف في blorgh/application.html.erb. يتيح لك هذا الملف تحديد تخطيط للمحرك. إذا كان سيُستخدَم هذا المحرك كمحرك مستقل (stand-alone engine)، فستضيف أي تخصيص لتخطيطه في هذا الملف بدلًا من ملف التطبيق app/views/layouts/application.html.erb.

إذا كنت لا ترغب في فرض تخطيط على مستخدمي المحرك، يمكنك حذف هذا الملف والإشارة إلى تخطيط مختلف في وحدات التحكم في المحرك الخاص بك.

المجلد bin

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

$ bin/rails g model

ضع في اعتبارك، بالطبع، أنّ أي شيء يُولَّد باستخدام هذه الأوامر داخل المحرك الذي يحتوي على isolate_namespace في الصنف Engine سيوضع اسمه ضمن مجال اسم.

المجلد test

المجلد test هو المكان الذي ستذهب إليه اختبارات المحرك. لاختبار المحرك، هناك نسخة مختصرة من تطبيق ريلز مضمَّن فيه في test/dummy. سيوصل (mount) هذا التطبيق المحرك في الملف test/dummy/config/routes.rb:

Rails.application.routes.draw do
  mount Blorgh::Engine => "/blorgh"
end

يقوم هذا السطر بوصل المحرك على المسار ‎/blorgh، مما يجعل الوصول إليه ممكنًا من خلال التطبيق على هذا المسار فقط.

يوجد داخل المجلد test المجلد test/integration، حيث يجب وضع اختبارات التكامل للمحرك. يمكن إنشاء مجلدات أخرى في المجلد test أيضًا. على سبيل المثال، قد ترغب في إنشاء المجلد test/models لاختبارات نموذجك.

توفير وظائف المحرك

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

توليد مصادر للمقال

أول شيء يراد توليده لمحرك مدونة هو النموذج Article ووحدة التحكم ذات الصلة. لتوليد ذلك بسرعة، يمكنك استخدام المولد scaffold في ريلز بالشكل التالي:

$ bin/rails generate scaffold article title:string text:text

سينتج هذا الأمر هذه المعلومات:

invoke  active_record
create    db/migrate/[timestamp]_create_blorgh_articles.rb
create    app/models/blorgh/article.rb
invoke    test_unit
create      test/models/blorgh/article_test.rb
create      test/fixtures/blorgh/articles.yml
invoke  resource_route
 route    resources :articles
invoke  scaffold_controller
create    app/controllers/blorgh/articles_controller.rb
invoke    erb
create      app/views/blorgh/articles
create      app/views/blorgh/articles/index.html.erb
create      app/views/blorgh/articles/edit.html.erb
create      app/views/blorgh/articles/show.html.erb
create      app/views/blorgh/articles/new.html.erb
create      app/views/blorgh/articles/_form.html.erb
invoke    test_unit
create      test/controllers/blorgh/articles_controller_test.rb
invoke    helper
create      app/helpers/blorgh/articles_helper.rb
invoke  test_unit
create    test/application_system_test_case.rb
create    test/system/articles_test.rb
invoke  assets
invoke    js
create      app/assets/javascripts/blorgh/articles.js
invoke    css
create      app/assets/stylesheets/blorgh/articles.css
invoke  css
create    app/assets/stylesheets/scaffold.css

أول شيء يستدعيه المولد scaffold هو المولد active_record، الذي يولد تهجيرًا (migration) ونموذجًا للمصدر. ومع ذلك، لاحظ أنَّ التهجير يدعى create_blorgh_articles بدلًا من create_articles المتوقع. هذا بسبب التابع isolate_namespace الذي استدعي في تعريف الصنف Blorgh::Engine. وأضيف اسم النموذج هنا ايضًا إلى مجال اسم، حيث يُوضع في app/models/blorgh/article.rb بدلًا من app/models/article.rb بسبب استدعاء isolate_namespace داخل الصنف Engine.

بعد ذلك، يُستدعَى المولد test_unit لهذا النموذج، مما يؤدي إلى توليد اختبار لنموذج في test/models /blorgh/article_test.rb (بدلًا من test/models/article_test.rb) وتحضير اختبار في test/fixtures/blorgh/articles.yml (بدلًا من test/fixtures/articles.yml).

بعد ذلك، يُدرج سطر للمصدر في الملف config/routes.rb للمحرك. هذا السطر هو ببساطة resources :articles، وتحويل الملف config/routes.rb للمحرك إلى ما يلي:

Blorgh::Engine.routes.draw do
  resources :articles
end

لاحظ هنا أن المسارات الموجهة تعتمد على الكائن Blorgh::Engine بدلًا من الصنف YourApp::Application. هذا هو سبب أن مسارات التوجيه للمحرك تقتصر على المحرك نفسه ويمكن وصلها في نقطة محددة كما هو موضح في قسم «المجلد test». كما يتسبب في عزل مسارات التوجيه للمحرك عن تلك المسارات الموجودة داخل التطبيق. يصف قسم مسارات التوجيه من هذا الدليل ذلك بالتفصيل.

بعد ذلك، يُستدعَى المولد scaffold_controller، وتُولد وحدة تحكم تسمى Blorgh::ArticlesController (في app/controllers/blorgh/articles_controller.rb) والواجهات ذات الصلة في app/views/blorgh/articles.

هذا المولد يولد أيضا اختبارًا لوحدة التحكم (test/controllers/blorgh/articles_controller_test.rb) ومساعدًا (app/helpers/blorgh/articles_helper.rb).

كل شيء أنشأه هذا المولد يكون ضمن مجال اسم. وعُرِّف صنف وحدة التحكم في الوحدة Blorgh:

module Blorgh
  class ArticlesController < ApplicationController
    ...
  end
end

ملاحظة: يرث الصنف ArticlesController من Blorgh::ApplicationController، وليس من ApplicationController الخاص بالتطبيق.

يكون اسم المساعد داخل app/helpers/blorgh/articles_helper.rb ضمن مجال أسماء أيضًا:

module Blorgh
  module ArticlesHelper
    ...
  end
end

يساعد ذلك على منع التعارضات مع أي محرك أو تطبيق آخر قد يكون له مصدر مقالة (article resource) أيضًا.

وأخيرًا، يُنشأ أصول لهذا المصدر في ملفين: app/assets/javascripts/blorgh/articles.js و app/assets/stylesheets/blorgh/articles.css. سترى كيفية استخدام هذه بعد قليل.

يمكنك أن ترى ما الذي يملكه المحرك حتى الآن عبر تنفيذ الأمر bin/rails db:migrate في جذر المحرك الخاص بنا لتنفيذ التهجير المُولَّد عبر المولِّد scaffold، ثم تنفيذ الأمر rails server في test/dummy. عند فتح الرابط http://localhost:3000/blorgh/articles في المتصفح، سترى ما الذي وُلِّد حتى الآن. لقد ولَّدت إلى الآن الدفعة الأولى من الدوال لأول محرك خاص بك.

إذا كنت تفضل اللعب في وحدة التحكم عوضًا عن ذلك، فسيعمل الأمر rails console أيضًا مثل عمله في أي تطبيق ريلز. تذكر أن النموذج Article قابع ضمن مجال اسم، لذلك أخذ ذلك بالحسبان للإشارة إليه مثل Blorgh::Article.

>> Blorgh::Article.find(1)
=> #<Blorgh::Article id: 1 ...>

شيء واحد نهائي هو أن المصدر articles لهذا المحرك يجب أن يكون هو جذر المحرك. عندما يذهب شخص ما إلى مسار الجذر حيث يوصل المحرك، يجب أن يُعرَّض عليه قائمة بالمقالات. يمكن إجراء ذلك إذا أُدرج السطر التالي في الملف config/routes.rb داخل المحرك:

root to: "articles#index"

الآن سيحتاج الأشخاص فقط إلى جذر المحرك لرؤية جميع المقالات، بدلًا من زيارة ‎/articles. هذا يعني أنه بدلًا من http://localhost:+3000/blorgh/articles، ما عليك سوى الانتقال إلى http://localhost:3000/blorgh الآن.

توليد مصدر للتعليقات

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

من جذر التطبيق، شغل مولد النموذج. أخبره بإنشاء نموذج تعليق، مع الجدول ذي الصلة الذي به عمودين: العمود article_id الذي يمثِّل عددًا صحيحًا والعمود text الذي يمثِّل نصًّا.

$ bin/rails generate model Comment article_id:integer text:text

مخرجات تنفيذ هذا الأمر هي:

invoke  active_record
create    db/migrate/[timestamp]_create_blorgh_comments.rb
create    app/models/blorgh/comment.rb
invoke    test_unit
create      test/models/blorgh/comment_test.rb
create      test/fixtures/blorgh/comments.yml

سيولد هذا الاستدعاء فقط ملفات النماذج الضرورية التي تحتاجها، حيث تسمى الملفات تحت المجلد blorgh ويُنشئ صنف نموذج (model class) يسمى Blorgh::Comment. الآن شغل التهجير لإنشاء الجدول blorgh_comments الخاص بنا:

$ bin/rails db:migrate

لإظهار التعليقات على إحدى المقالات، عدّل app/views/blorgh/articles/show.html.erb وأضف هذا السطر قبل الرابط "Edit" (تعديل):

<h3>Comments</h3>
<%= render @article.comments %>

سوف يتطلب هذا السطر أن يكون هناك الارتباط has_many للتعليقات المحددة في النموذج Blorgh::Article، والتي لا توجد الآن. لتحديد واحد، افتح app/models/blorgh/article.rb وأضف السطر التالي إلى النموذج:

has_many :comments

تحويل النموذج إلى:

module Blorgh
  class Article < ApplicationRecord
    has_many :comments
  end
end

ملاحظة: نظرًا لتعريف الارتباط has_many داخل صنف موجودة داخل الوحدة Blorgh، سيعرف ريلز أنك تريد استخدام نموذج Blorgh::Comment لهذه الكائنات، لذا لا داعي لتحديد ذلك باستخدام الخيار ‎:class_name هنا.

بعد ذلك، يجب أن يكون هناك نموذج حتى يمكن إنشاء التعليقات على المقالة. لتوفير هذا، ضع السطر التالي أسفل الاستدعاء render @article.comments في app/views/blorgh/articles/show.html.erb:

<%= render "blorgh/comments/form" %>

بعد ذلك، تحتاج الجزئية (partial) التي سيصيرها هذا السطر إلى أن تكون موجودة. أنشئ مجلدًا جديدًا في app/views/blorgh/comments وأنشئ ملفًا فيه باسم form.html.erb_ الذي يحتوي على المحتوى التالي لإنشاء الجزئية المطلوبة:

<h3>New comment</h3>
<%= form_with(model: [@article, @article.comments.build], local: true) do |form| %>
  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>
  <%= form.submit %>
<% end %>

عندما يرسل هذا النموذج، فإنه سيحاول تنفيذ طلب POST إلى المسار/articles/:article_id/comments داخل المحرك. هذا المسار غير موجود في الوقت الحالي، ولكن يُنشأ عن طريق تغيير السطر resources :articles داخل config/routes.rb إلى هذه السطور:

resources :articles do
  resources :comments
end

يؤدي هذا إلى إنشاء مسار متداخل للتعليقات، وهو ما يتطلبه النموذج.

أصبح المسار الآن موجودًا، ولكن وحدة التحكم التي تذهب إلى هذا المسار غير موجودة. لإنشائها، نفذ الأمر التالي من جذر التطبيق:

$ bin/rails g controller comments

هذا سوف يولد الأشياء التالية:

create  app/controllers/blorgh/comments_controller.rb
invoke  erb
 exist    app/views/blorgh/comments
invoke  test_unit
create    test/controllers/blorgh/comments_controller_test.rb
invoke  helper
create    app/helpers/blorgh/comments_helper.rb
invoke  assets
invoke    js
create      app/assets/javascripts/blorgh/comments.js
invoke    css
create      app/assets/stylesheets/blorgh/comments.css

سيقدم النموذج طلب POST إلى ‎/articles/:article_id/comments، والتي تتوافق مع الإجراء create في Blorgh::CommentsController. يجب إنشاء هذا الإجراء، والذي يُنفذ عن طريق وضع السطور التالية داخل تعريف الصنف في app/controllers/blorgh/comments_controller.rb:

def create
  @article = Article.find(params[:article_id])
  @comment = @article.comments.create(comment_params)
  flash[:notice] = "Comment has been created!"
  redirect_to articles_path
end
 
private
  def comment_params
    params.require(:comment).permit(:text)
  end

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

Missing partial blorgh/comments/_comment with {:handlers=>[:erb, :builder],
:formats=>[:html], :locale=>[:en, :en]}. Searched in:   *
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views"   *
"/Users/ryan/Sites/side_projects/blorgh/app/views"

يتعذر على المحرك العثور على الجزء المطلوب لتقديم التعليقات. يبحث ريلز أولًا في مجلد التطبيق test/dummy) app/views) ثم في المجلد app/views للمحرك. عندما لا يعثر عليه، سوف يرمي هذا الخطأ. يعرف المحرك أين يبحث عن blorgh/comments/_comment لأن كائن النموذج الذي يستقبله هو من الصنف Blorgh::Comment.

ستكون هذه الجزئية مسؤولة عن تقديم نص التعليق فقط، في الوقت الحالي. أنشئ ملفًا جديدًا في app/views/blorgh/comments/_comment.html.erb وضع هذا السطر داخله:

<%= comment_counter + 1 %>. <%= comment.text %>

يعطى المتغير المحلي comment_counter بواسطة استدعاء ‎<%= render@article.comments %>‎، والذي سيحدده تلقائيًا ويزيد العداد أثناء تكرار كل تعليق. يُستخدم في هذا المثال لعرض رقم صغير بجوار كل تعليق عند إنشائه.

هذا يكمل وظيفة التعليق لمحرك التدوين. الآن حان الوقت لاستخدامه داخل التطبيق.

استعمال المحرك في التطبيق

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

وصل المحرك

أولًا، يجب تحديد المحرك داخل Gemfile الخاص بالتطبيق. إذا لم يكن هناك تطبيق مفيد لاختبار ذلك، فأنشئ تطبيقًا باستخدام الأمر rails new خارج مجلد المحرك كما يلي:

$ rails new unicorn

عادةً، يحدد المحرك داخل Gemfile من خلال تحديده كالمعتاد كأي جوهرة.

gem 'devise'

ومع ذلك، نظرًا لأنك تقوم بتطوير المحرك blorgh على جهازك المحلي، فستحتاج إلى تحديد الخيار path:‎ في Gemfile الخاص بك:

gem 'blorgh', path: 'engines/blorgh'

ثم شغل bundle لتثبيت الجوهرة.

كما هو موضح سابقًا، بوضع الجوهرة في Gemfile، سيحمل المحرك عند تحميل ريلز. سيتطلب الأمر أولًا lib/blorgh.rb من المحرك، ثم lib/blorgh/engine.rb، وهو الملف الذي يحدد الأجزاء الرئيسية من الوظائف للمحرك.

لجعل وظيفة المحرك قابلة للوصول من داخل التطبيق، يجب تثبيته في الملف config/path.rb لهذا التطبيق:

mount Blorgh::Engine, at: "/blog"

سيوصل هذا السطر المحرك في ‎/blog  في التطبيق. مما يجعل الوصول إليها في http://localhost:3000/blog عند تشغيل التطبيق مع rails server.

ملاحظة: المحركات الأخرى، مثل Devise، تتعامل مع هذا بطريقة مختلفة قليلًا عن طريق تعيينك لمساعدات مخصصة (مثل devise_for) في المسارات. ينفذ هؤلاء المساعدون نفس الشيء بالضبط، حيث توصل أجزاءً من وظائف المحرك على مسار محدد مسبقًا والذي يمكن تخصيصه.

إعداد المحرك

يحتوي المحرك على عمليات تهجير لجدول blorgh_articles والجدول blorgh_comments والتي يلزم إنشاؤها في قاعدة بيانات التطبيق بحيث يمكن لنماذج المحرك الاستعلام عنها بشكل صحيح. لنسخ هذه التهجيرات إلى التطبيق، شغل الأمر التالي من المجلد test/dummy الخاص بمحرك ريلز:

$ bin/rails blorgh:install:migrations

إذا كان لديك العديد من المحركات التي تحتاج إلى نسخ عمليات التهجير، استخدام railties:install:migrations بدلًا من ذلك:

$ bin/rails railties:install:migrations

سيعمل هذا الأمر عند تشغيله لأول مرة على نسخ جميع عمليات التهجير من المحرك. عند تشغيلها في المرة القادمة، لن تُنسَخ إلا من خلال عمليات التهجير التي لم تنسخ بالفعل. التشغيل الأول لهذا الأمر سينتج شيئًا كهذا:

Copied migration [timestamp_1]_create_blorgh_articles.blorgh.rb from blorgh
Copied migration [timestamp_2]_create_blorgh_comments.blorgh.rb from blorgh

سيكون الطابع الزمني الأول ([timestamp_1]) هو الوقت الحالي، وسيكون الطابع الزمني الثاني ([timestamp_2]) هو الوقت الحالي بالإضافة إلى ثانية. السبب في ذلك هو أن تُشغل عمليات ji[dv المحرك بعد أي عمليات تهجير حالية في التطبيق.

لتشغيل عمليات التهجير هذه في سياق التطبيق، ما عليك سوى تشغيل bin/rails db:migrate. عند الوصول إلى المحرك من خلال http://localhost: 3000/blog، ستكون المقالات فارغة. وذلك لأن الجدول الذي أُنشأ داخل التطبيق يختلف عن الجدول الذي أُنشأ داخل المحرك. امضي قدمًا واستمر بالتعديل على المحرك الذي وصل حديثًا. ستجد أنه هو نفسه عندما كان مجرد محرك.

إذا كنت ترغب في تشغيل عمليات التهجير فقط من محرك واحد، فيمكنك القيام بذلك عن طريق تحديد SCOPE:

bin/rails db:migrate SCOPE=blorgh

قد يكون هذا مفيدًا إذا كنت تريد التراجع عن عمليات ترحيل المحرك قبل إزالتها. لإعادة جميع عمليات الترحيل من المحرك blorgh، يمكنك تشغيل شيفرة مثل:

bin/rails db:migrate SCOPE=blorgh VERSION=0

استخدام صنف وفره التطبيق

استخدام نموذج وفره التطبيق

عندما يُنشأ المحرك، قد ترغب في استخدام أصناف معينة من التطبيق لتوفير روابط بين أجزاء المحرك وأجزاء التطبيق. في حالة المحرك blorgh، فإن إسناد مؤلفين إلى المقالات والتعليقات تجعل الأمر أكثر واقعية.

قد يحتوي التطبيق النموذجي على الصنف User الذي يمكن استخدامه لتمثيل المؤلفين لمقال أو تعليق. ولكن قد تكون هناك حالة يستدعي فيها التطبيق هذا الصنف شيئًا مختلفًا، مثل Person. لهذا السبب، يجب ألا يرتب المحرك الارتباطات الخاصة بالصنف User على وجه التحديد.

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

rails g model user name:string

يجب تشغيل الأمر bin/rails db: migrate هنا لضمان أن تطبيقنا يحتوي على الجدول users للاستخدام المستقبلي.

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

أولًا، يجب إضافة حقل النص author_name إلى الجزئية app/views/blorgh/articles/_form.html.erb داخل المحرك. يمكن إضافة هذا فوق الحقل title بالشكل التالي:

<div class="field">
  <%= form.label :author_name %><br>
  <%= form.text_field :author_name %>
</div>

بعد ذلك، نحتاج إلى تحديث التابع Blorgh::ArticleController.article_params للسماح بمعامل النموذج الجديد:

def article_params
  params.require(:article).permit(:title, :text, :author_name)
end

يجب أن يحتوي النموذج Blorgh::Article على نفس الشيفرة لتحويل الحقل author_name إلى كائن User فعلي و إقرانه على أنه مؤلف author تلك المقالة قبل حفظ حفظها. كما ستحتاج أيضًا إلى إعداد attr_accessor لهذا الحقل، بحيث تُحدد توابع الضبط (setter) والجلب (getter) الخاصة به.

للقيام بكل هذا، ستحتاج إلى إضافة attr_accessor لـ author_name، إذ يستدعى الارتباط للمؤلف والاستدعاء before_validation إلى app/models/blorgh/article.rb. سيكون الارتباط author ثابتًا (hard-coded) إلى الصنف User في الوقت الحاضر.

attr_accessor :author_name
belongs_to :author, class_name: "User"
 
before_validation :set_author
 
private
  def set_author
    self.author = User.find_or_create_by(name: author_name)
  end

من خلال تمثيل الارتباط author للكائن مع الصنف User، تنشأ وصلةٌ بين المحرك والتطبيق. يجب أن تكون هناك طريقة لربط السجلات في الجدول blorgh_articles بالسجلات في الجدول users. لأن الارتباط يدعى author، يجب أن يكون هناك حقلًا يدعى author_id مضافًا إلى الجدول blorgh_articles.

لإنشاء هذا العمود الجديد، شغل هذا الأمر داخل المحرك:

$ bin/rails g migration add_author_id_to_blorgh_articles author_id:integer

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

يجب تشغيل هذا التهجير على التطبيق. للقيام بذلك، يجب أولًا نسخه باستخدام هذا الأمر:

$ bin/rails blorgh:install:migrations

لاحظ أنه نُسخ تهجير واحد فقط هنا. هذا لأن نُسخ أول تهجيرين خلال المرة الأولى من تشغيل هذا الأمر.

NOTE Migration [timestamp]_create_blorgh_articles.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
NOTE Migration [timestamp]_create_blorgh_comments.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blorgh

شغّل التهجير باستخدام:

$ bin/rails db:migrate

الآن مع وضع كل القطع في مكانها، سيجري ربط مؤلفٍ - مأخوذ من حقلٍ من الجدول users - مع كل مقال ممثل من الجدول blorgh_articles من المحرك.

وأخيرًا، يجب أن يعرض اسم المؤلف على صفحة المقالات. أضف الشيفرة التالية فوق "Title" داخل الملف app/views/blorgh/articles/show.html.erb:

<p>
  <b>Author:</b>
  <%= @article.author.name %>
</p>

استخدام وحدة تحكم وفرها التطبيق

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

تملك محركات ريلز نطاقًا للتشغيل بشكل مستقل عن التطبيق الرئيسي، بحيث يحصل كل محرك على نطاق ApplicationController. يمنع مجال الاسم هذا تعارضات الشيفرة، ولكن غالبًا ما تحتاج وحدات تحكم المحرك إلى الوصول إلى التوابع في ApplicationController للتطبيق الرئيسي. إحدى الطرق السهلة لتوفير هذا الوصول هي تغيير ApplicationController الخاص بالمحرك إلى الوراثة من ApplicationController للتطبيق الرئيسي. بالنسبة لمحرك Blorgh، يمكن القيام بذلك عن طريق تغيير app/controllers/blorgh/application_controller.rb لتبدو كما يلي:

module Blorgh
  class ApplicationController < ::ApplicationController
  end
end

بشكل افتراضي، ترث وحدات تحكم المحرك من Blorgh::ApplicationController. لذا، بعد إجراء هذا التغيير، سيكون بإمكانها الوصول إلى ApplicationController للتطبيق الرئيسي، كما لو كانت جزءًا من التطبيق الرئيسي.

يتطلب هذا التغيير تشغيل المحرك من تطبيق ريلز يحتوي على ApplicationController.

ضبط المحرك

يغطي هذا القسم كيفية جعل الصنف User قابلة للضبط، ويحتوي على نصائح عامة لتهيئة المحرك.

ضبط إعدادات التطبيق

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

لتعريف خيار الضبط هذا، يجب عليك استخدام mattr_accessor داخل الوحدة Blorgh للمحرك. أضف السطر التالي إلى lib/blorgh.rb داخل المحرك:

mattr_accessor :author_class

يعمل هذا التابع مثل أمثاله - attr_accessor و cattr_accessor - ولكنه يوفر تابع ضبط (setter) وجلب (getter) على النموذج بالاسم المحدد. لاستخدامها، يجب الإشارة إليها باستخدام Blorgh.author_class.

الخطوة التالية هي تبديل النموذج Blorgh::Article إلى هذا الضبط الجديد. غيِّر الارتباط belongs_to داخل هذا النموذج (app/models/blorgh/article.rb) إلى ما يلي:

belongs_to :author, class_name: Blorgh.author_class

يجب أيضًا أن يستخدم التابع set_author في النموذج Blorgh::Article هذا الصنف:

self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name)

لحفظ الحاجة إلى استدعاء ثابت على نتيجة author_class طوال الوقت، يمكنك بدلًا من ذلك فقط استبدال التابع author_class الضابط داخل الوحدة Blorgh في الملف lib/blorgh.rb لتستدعي دائمًا constantize على القيمة المحفوظة قبل إرجاع النتيجة:

def self.author_class
  @@author_class.constantize
end

سيؤدي هذا بعد ذلك إلى تحويل الشيفرة أعلاه من أجل set_author إلى:

self.author = Blorgh.author_class.find_or_create_by(name: author_name)

مما يؤدي إلى شيء أقصر قليلًا، وذي سلوك مضمَّن. يجب أن يعيد التابع author_class الكائن Class دومًا.

بما أننا غيرنا التابع author_class لإعادة صنف بدلًا من سلسلة نصية، فيجب علينا أيضًا تعديل تعريف الارتباط belongs_to الخاص بنا في النموذج Blorgh::Article:

belongs_to :author, class_name: Blorgh.author_class.to_s

لتعيين خيار الضبط هذا داخل التطبيق، يجب استخدام مُهيئٍ. باستخدام المُهيئ، سيُضبَط الضبط قبل بدء تشغيل التطبيق ويستدعي نماذج المحرك، والتي قد تعتمد على هذا الضبط الموجود حاليًّا.

أنشئ مُهيئًا جديدًا في config/initializers/blorgh.rb داخل التطبيق حيث وُصِلَ المحرك blorgh وضع هذا المحتوى فيه:

Blorgh.author_class = "User"

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

امضي قدمًا وحاول إنشاء مقال جديد. سترى أنه يعمل بالضبط بنفس الطريقة كما كان من قبل، ما عدا أن المحرك الآن يستخدم إعداد الضبط في config/initializers/blorgh.rb لمعرفة ما هو الصنف.

لا توجد الآن اعتمادات صارمة (strict dependencies) على ما هية الصنف، فقط ما يجب أن تكون عليه واجهة برمجة التطبيقات للصنف. يتطلب المحرك ببساطة هذه الصنف لتعريف التابع find_or_create_by الذي يعيد عنصرًا من ذلك الصنف، لربطه بمقال عند ضبطه. هذا الكائن، بالطبع، يجب أن يحتوي على نوع من المعرّف يمكن من خلاله الإشارة إليه.

الضبط العام للمحرك

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

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

محليًا، ضع ببساطة ملفات الإعدادات المحلية في المجلد config/locales، تمامًا كما تفعل في أي تطبيق.

اختبار المحرك

عند إنشاء محرك، هناك تطبيق وهمي أصغر يُنشَأ داخله في test/dummy. يُستخدَم هذا التطبيق كنقطة وصل للمحرك، لجعل اختبار المحرك بسيط للغاية. يمكنك توسيع هذا التطبيق عن طريق توليد وحدات تحكم أو نماذج أو واجهات من داخل المجلد، ثم استخدمها لاختبار محركك.

يجب معاملة المجلد test مثل بيئة اختبار ريلز النموذجية، مما يسمح باختبارات الوحدة والوظيفة والتكامل.

الاختبارات الوظيفية

هناك مسألة تستحق الأخذ في الحسبان عند كتابة الاختبارات الوظيفية وهي أن الاختبارات ستعمل على تطبيق - تطبيق test/dummy - بدلًا من المحرك الخاص بك. هذا بسبب إعداد بيئة الاختبار؛ يحتاج المحرك إلى تطبيق كمضيف لاختبار وظائفه الرئيسية، وخاصة أجهزة التحكم. هذا يعني أنه إذا كنت ستجعل GET نموذجية إلى وحدة التحكم في اختبار وظيفية وحدة تحكم مثل هذا:

module Blorgh
  class FooControllerTest < ActionDispatch::IntegrationTest
    include Engine.routes.url_helpers
 
    def test_index
      get foos_url
      ...
    end
  end
end

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

module Blorgh
  class FooControllerTest < ActionDispatch::IntegrationTest
    include Engine.routes.url_helpers
 
    setup do
      @routes = Engine.routes
    end
 
    def test_index
      get foos_url
      ...
    end
  end
end

هذا يخبر التطبيق أنك لا تزال ترغب في تنفيذ طلب GET إلى الإجراء index لوحدة التحكم هذه، ولكنك تريد استخدام مسار المحرك للوصول إلى هناك، بدلًا من مسار التطبيق.

يضمن هذا أيضًا أن يعمل مساعدي عنوان URL الخاصة بالمحرك كما هو متوقع في اختباراتك.

تحسين وظائف المحرك

يشرح هذا القسم كيفية إضافة و/أو تجاوز وظائف نموذج MVC للمحرك في تطبيق ريلز الرئيسي.

استبدال النماذج ووحدات التحكم

يمكن توسيع نموذج المحرك وأصناف وحدة التحكم عن طريق فتحها في تطبيق ريلز الرئيسي (نظرًا لأن أصناف النموذج ووحدة التحكم ليست سوى أصناف روبي ترث وظيفة معينة في ريلز). فتح تصنيف (open classing) صنف المحرك يعيد تعريفه للاستخدام في التطبيق الرئيسي. يُنفَّذ ذلك عادةً باستخدام نمط المزخرف (decorator pattern).

بالنسبة لتعديلات صنف بسيطة، استخدم التابع Class.class_eval. لتعديلات صنف معقدة، ضع في اعتبارك استخدام ActiveSupport::Concern.

ملاحظة حول شيفرة المزخرفات والتحميل

نظرًا لأن هذه المزخرفات (decorators) لا يُشَار إليها من خلال تطبيق ريلز نفسه، فلن يُطبَّق نظام التحميل التلقائي الخاص بريلز لتحميل المزخرفات الخاصة بك. هذا يعني أنك تحتاج إلى طلبها بنفسك.

إليك شيفرة نموذجية توضح كيفية القيام بذلك:

# lib/blorgh/engine.rb
module Blorgh
  class Engine < ::Rails::Engine
    isolate_namespace Blorgh
 
    config.to_prepare do
      Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
        require_dependency(c)
      end
    end
  end
end

هذا لا ينطبق فقط على المزخرفات (Decorators)، ولكن أي شيء تضيفه في محرك لا يشار إليه بواسطة التطبيق الرئيسي.

تنفيذ نمط المزخرف باستخدام التابع Class.class_eval

إضافة Article.time_since_created:

# MyApp/app/decorators/models/blorgh/article_decorator.rb
 
Blorgh::Article.class_eval do
  def time_since_created
    Time.current - created_at
  end
end
# Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
  has_many :comments
end

استبدال Article.summary:

# MyApp/app/decorators/models/blorgh/article_decorator.rb
 
Blorgh::Article.class_eval do
  def summary
    "#{title} - #{truncate(text)}"
  end
end
# Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
  has_many :comments
  def summary
    "#{title}"
  end
end

تنفيذ نمط المزخرف باستخدام ActiveSupport::Concern

يعد استخدام Class.class_eval مثاليًا لإجراء عمليات ضبط بسيطة، ولكن بالنسبة لتعديلات صنف أكثر تعقيدًا، قد ترغب في استخدام ActiveSupport::Concern. يدير ActiveSupport::Concern ترتيب التحميل للوحدات والأصناف التابعة المترابطة في وقت التشغيل مما يسمح لك بتهيئة شيفرتك بشكل كبير.

إضافة Article.time_since_created واستبدال Article.summary:

# MyApp/app/models/blorgh/article.rb
 
class Blorgh::Article < ApplicationRecord
  include Blorgh::Concerns::Models::Article
 
  def time_since_created
    Time.current - created_at
  end
 
  def summary
    "#{title} - #{truncate(text)}"
  end
end
# Blorgh/app/models/article.rb
 
class Article < ApplicationRecord
  include Blorgh::Concerns::Models::Article
end
# Blorgh/lib/concerns/models/article.rb
 
module Blorgh::Concerns::Models::Article
  extend ActiveSupport::Concern
 
  # بتقييم الشيفرة المضمنة في السياق 'included do' يتسبب
  # بدلًا من تنفيذها في سياق  (article.rb) حيث ضُمِّنَت
  # (blorgh/concerns/models/article) الوحدة
  included do
    attr_accessor :author_name
    belongs_to :author, class_name: "User"
 
    before_validation :set_author
 
    private
      def set_author
        self.author = User.find_or_create_by(name: author_name)
      end
  end
 
  def summary
    "#{title}"
  end
 
  module ClassMethods
    def some_class_method
      'some class method string'
    end
  end
end

استبدال الواجهات

عندما يبحث ريلز عن الواجهة التي ستعرض، سيبحث أولًا في المجلد app/views للتطبيق. إذا لم يتمكن من العثور على الواجهة هناك، فسيتحقق من المجلدات الموجودة في app/views لجميع المحركات التي تحتوي على هذا المجلد.

عندما يُطلب من التطبيق تصيير واجهة Blorgh::ArticlesController للإجراء index، سيبحث أولًا عن المسار app/views/blorgh/articles/index.html.erb ضمن التطبيق. إذا لم تتمكن من العثور عليه، فسيظهر داخل المحرك.

يمكنك تجاوز هذه الواجهة واستبدالها في التطبيق ببساطة عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb. بعد ذلك، يمكنك تغيير طريقة عرض هذه الواجهة تمامًا.

جرب هذا الآن عن طريق إنشاء ملف جديد في app/views/blorgh/articles/index.html.erb ووضع هذا المحتوى فيه:

<h1>Articles</h1>
<%= link_to "New Article", new_article_path %>
<% @articles.each do |article| %>
  <h2><%= article.title %></h2>
  <small>By <%= article.author %></small>
  <%= simple_format(article.text) %>
  <hr>
<% end %>

المسارات الموجهة

تُعزَل المسارات داخل المحرك من التطبيق بشكل افتراضي وذلك عن طريق استدعاء isolate_namespace داخل الصنف Engine. وهذا يعني بالضرورة أن التطبيق ومحركاته يمكن أن يكون لهما مسارات لها نفس الاسم دون أن يحدث أي تعارض.

تُحدَّد المسارات الموجهة داخل المحرك في الصنف Engine داخل config/routes.rb، على النحو التالي:

Blorgh::Engine.routes.draw do
  resources :articles
end

من خلال وجود مسارات موجهة منعزلة مثل هذا، إذا كنت ترغب في ربط (link) منطقة من المحرك من داخل التطبيق، فستحتاج إلى استخدام تابع وسيط لتوجيه المحرك. قد تنتهي الاستدعاءات إلى توابع التوجيه العادية، مثل articles_path، إلى الذهاب إلى مواقع غير مرغوبة إذا كان لكل من التطبيق والمحرك تعريف مثل هذا المساعد.

على سبيل المثال، سيذهب المثال التالي إلى article_path للتطبيق إذا عُرض هذا القالب من التطبيق، أو article_path الخاص بالمحرك إذا عُرض من المحرك:

<%= link_to "Blog articles", articles_path %>

ولجعل هذا المسار دائمًا يستخدم تابع مساعد التوجيه article_path للمحرك، يجب علينا استدعاء التابع على تابع التوجيه الوسيط الذي يشترك في نفس اسم المحرك.

<%= link_to "Blog articles", blorgh.articles_path %>

إذا كنت تريد الرجوع إلى التطبيق داخل المحرك بطريقة مماثلة، فاستخدم المساعد main_app:

<%= link_to "Home", main_app.root_path %>

إذا كنت تستخدم هذا داخل المحرك، فإنه سوف يذهب دائما إلى جذر التطبيق. إذا كنت ستترك استدعاء تابع "وسيط التوجيه" main_app، فمن المحتمل أن تذهب إلى جذر المحرك أو التطبيق، حسب المكان الذي اتصل به منه.

إذا حاول قالب صُيِّر من داخل محرك ما استخدام أحد توابع مساعد التوجيه للتطبيق، فقد ينتج عنه استدعاء تابع غير محدد. إذا واجهتك مشكلة من هذا القبيل، فتأكد من أنك لا تحاول استدعاء تابع توجيه التطبيق بدون البادئة main_app من داخل المحرك.

الأصول

تعمل الأصول داخل المحرك بطريقة مماثلة لتطبيق كامل. نظرًا لأن الصنف Engine يرث من Rails::Engine، سيعرف التطبيق البحث عن الأصول في المحرك app/assets والمجلدات  lib/assets.

مثل جميع المكونات الأخرى للمحرك، يجب أن تكون الأصول ضمن مجال اسم. وهذا يعني أنه إذا كان لديك أصل باسم style.css، فيجب وضعه في app/assets/stylesheets/[engine name]/style.css، بدلًا من app/assets/stylesheets/style.css. إذا لم يكن لدى هذا الأصل مجال اسم، فمن المحتمل أن يكون لدى التطبيق المضيف أصلًا يملك نفس الاسم تمامًا، وفي هذه الحالة يكون لأصل التطبيق الأولوية وسيتجاهل المحرك.

تخيل أن لديك أصلًا موجودًا في app/assets/stylesheets/blorgh/style.css لتضمين الأصل هذا داخل أحد التطبيقات، ما عليك سوى استخدام stylesheet_link_tag والإشارة إلى الأصل كما لو كان داخل المحرك:

<%= stylesheet_link_tag "blorgh/style.css" %>

يمكنك أيضًا تحديد هذه الأصول باعتبارها تبعيات للأصول الأخرى باستخدام Asset Pipeline تتطلب تعابير في الملفات التي تمت معالجتها:

/*
 *= require blorgh/style
*/

ملاحظة: تذكر أنه من أجل استخدام لغات مثل Sass أو CoffeeScript، يجب عليك إضافة المكتبة ذات الصلة إلى gemspec. الخاص بالمحرك.

الأصول المنفصلة والتصريف المسبق

هناك بعض الحالات التي لا يتطلب فيها التطبيق المضيف أصول محركك. على سبيل المثال، لنفترض أنك أنشأت وظيفة مسؤول لا تتوفر إلا لمحركك. في هذه الحالة، لا يحتاج التطبيق المضيف إلى طلب admin.css أو admin.js. يحتاج تخطيط المشرف فقط إلى هذه الأصول. من غير المعقول أن يتضمن التطبيق المضيف "blorgh/admin.css" في صفحات الأنماط الخاصة به. في هذه الحالة، يجب أن تُعرَّف هذه الأصول بشكل واضح من أجل التصريف المسبق (precompilation). هذا يخبر Sprockets لإضافة أصول المحرك الخاص بك عند تشغيل bin/rails assets:precompile.

يمكنك تعريف الأصول للتصريف المسبق في engine.rb:

initializer "blorgh.assets.precompile" do |app|
  app.config.assets.precompile += %w( admin.js admin.css )
end

لمزيد من المعلومات، اقرأ دليل Asset Pipeline.

تبعيات الجوهرة الأخرى

يجب تحديد تبعيات الجوهرة داخل المحرك داخل الملف gemspec. في جذر المحرك. والسبب هو أن المحرك قد يُثبَّت كجوهرة. إذا كان من المقرر تحديد التبعيات داخل الملف Gemfile، فلن يُتعرَّف عليها من خلال تثبيت جوهرة تقليدية وبالتالي لن تُثبَّت، مما يؤدي إلى خلل في المحرك.

لتحديد تبعية يجب تثبيتها مع المحرك أثناء تنفيذ الأمر gem install الاعتيادي، حددها داخل الكتلة Gem::Specification الموجودة داخل الملف gemspec. في المحرك:

s.add_dependency "moo"

لتحديد تبعية لا يجب تثبيتها إلا كاعتماد في بيئة التطوير للتطبيق، حددها على النحو التالي:

s.add_development_dependency "moo"

تُثبت كلا النوعين من التبعيات عند تنفيذ الأمر bundle install داخل التطبيق. لن يستخدم تبعيات التطوير للجوهرة إلا عند تشغيل اختبارات المحرك.

لاحظ أنه إذا كنت ترغب في طلب تبعيات على الفور عندما يكون المحرك مطلوبًا، فيجب عليك طلب ذلك قبل تهيئة المحرك. فمثلًا:

require 'other_engine/engine'
require 'yet_another_engine/engine'
 
module MyEngine
  class Engine < ::Rails::Engine
  end
end

Active Support وخطافات On Load

Active Support هو مكون ريلز المسؤول عن توفير ملحقات لغة روبي والأدوات المساعدة والأدوات الأخرى.

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

الخطافات On Load هي واجهة برمجة التطبيقات التي تسمح لك بالتحميل في عملية الإقلاع دون انتهاك عقد التحميل مع ريلز. سيؤدي ذلك أيضًا إلى تخفيف تدهور أداء الإقلاع وتجنب التعارضات.

ما هي خطافات on_load؟

بما أن روبي هي لغة ديناميكية، فإن بعض الشيفرات سيؤدي إلى تحميل أطر ريلز المختلفة. خذ هذا المقتطف على سبيل المثال:

ActiveRecord::Base.include(MyActiveRecordHelper)

يعني هذا المقتطف أنه عند تحميل هذا الملف، سيُنفَّذ ActiveRecord::Base. تؤدي هذا التنفيذ إلى قيام روبي بالبحث عن تعريف هذا الثابت وسيتطلبه عبر require. يؤدي هذا لتحميل إطار Active Record  بالكامل في الإقلاع.

ActiveSupport.on_load هو آلية يمكن استخدامها لتأجيل تحميل الشيفرة إلى أن تكون ضرورية بالفعل. يمكن تغيير المقتطف أعلاه إلى:

ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper }

يتضمن هذا المقتطف الجديد MyActiveRecordHelper فقط عند تحميل ActiveRecord::Base.

كيف يعمل؟

في إطار ريلز، تُستدعى هذه الخطافات عند تحميل مكتبة معينة. على سبيل المثال، عند تحميل ActionController::Base، يُستدعَى الخطاف ‎:action_controller_base. هذا يعني أن جميع الاستدعاءات ActiveSupport.on_load مع خطافات  action_controller_base: تستدعى في سياق ActionController::Base (وهذا يعني أن self سوف يكون ActionController::Base).

تعديل الشيفرة لاستخدام خطافات on_load

تعديل الشيفرة بشكل عام بسيط. إذا كان لديك سطر من التعليمات البرمجية يشير إلى إطار ريلز مثل ActiveRecord::Base يمكنك تغليف تلك الشيفرة في خطاف on_load.

مثال 1

ActiveRecord::Base.include(MyActiveRecordHelper)

يصبح:

ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper } # self refers to ActiveRecord::Base here, so we can simply #include

مثال 2

ActionController::Base.prepend(MyActionControllerHelper)

يصبح:

ActiveSupport.on_load(:action_controller_base) { prepend MyActionControllerHelper } 
# #prepend لذا يمكننا ببساطة ،ActionController::Base هنا إلى self تشير

مثال 3

ActiveRecord::Base.include_root_in_json = true

يصبح:

ActiveSupport.on_load(:active_record) { self.include_root_in_json = true } 
# ActiveRecord::Base هنا إلى self يشير

الخطافات المتاحة

ستجد في هذا القسم الخطافات التي يمكنك استخدامها في الشيفرة الخاصة بك.

للربط في عملية التهيئة لإحدى الأصناف التالية، استخدم الخطاف المتوفر.

الصنف الخطاف المتاح
ActionCable action_cable
ActionController::API action_controller_api
ActionController::API action_controller
ActionController::Base action_controller_base
ActionController::Base action_controller
ActionController::TestCase action_controller_test_case
ActionDispatch::IntegrationTest action_dispatch_integration_test
ActionDispatch::SystemTestCase action_dispatch_system_test_case
ActionMailer::Base action_mailer
ActionMailer::TestCase action_mailer_test_case
ActionView::Base action_view
ActionView::TestCase action_view_test_case
ActiveJob::Base active_job
ActiveJob::TestCase active_job_test_case
ActiveRecord::Base active_record
ActiveSupport::TestCase active_support_test_case
i18n i18n

ضبط الخطافات

إليك خطافات الضبط المتاحة، فهي لا تُعلِّق (hook) في أي إطار معين، بل تعمل في سياق التطبيق بأكمله.

الخطاف حالة الاستخدام
before_configuration أول كتلة قابلة للضبط يراد تنفيذها. تُستدعى قبل أي تشغيل أية مهيئات.
before_initialize ثاني كتلة قابلة للضبط يراد تنفيذها. تُستدعى قبل تهيئة الأطر.
before_eager_load ثالث كتلة ثابلة للضبط يراد تنفيذها. لا يعمل إذا كان config.eager_load مضبوطًا إلى القيمة false.
after_initialize آخر كتلة قابلة للضبط يراد تشغيلها. تُستدعى بعد تهيئة الأطر.

إليك المثال التالي:

Config.before_configuration { puts 'I am called before any initializers' }

مصادر