5 أمثلة على أفضل استخدامات روبي
هل تساءلت يومًا ما الذي يمكننا فعله مع روبي؟ حسناً، ربما تكون السماء هي الحد الأقصى، ولكن يسعدنا أن نتحدث عن بعض الحالات المعروفة بشكل أو بآخر...
سنقوم ببناء تطبيق رف كتب لسرد الكتب مع (أو بدون) بيانات المؤلفين.
سنقوم ببناء تطبيق رف كتب لسرد الكتب مع (أو بدون) بيانات المؤلفين. سيكون هناك #index
وبعض البذور. سيكون هذا مثالاً على تطبيق لتوضيح كيف يمكنك منح المستخدم التحكم في الموارد الفرعية في واجهة برمجة تطبيقات REST-ish API.
تشمل
معلمة الاستعلام لتحميل الموارد المرتبطة (المؤلف
).تشمل
تحتوي معلمة الاستعلام على تنسيق سلسلة: كلمات مفصولة بفاصلة، تمثل الموارد المتداخلة.سنستخدم بلوبرنتر
كمتسلسل، لأنه لا يعتمد على التنسيق ومرن للغاية. هذه هي الجوهرة الوحيدة التي سنضيفها إلى مجموعة الأدوات القياسية للقضبان.
لننشئ مثالاً على التطبيق. لن نضيف إطار اختبار لأنه خارج نطاقنا.
قضبان رف كتب جديدة -T
أنشئ الآن المؤلف
الطراز:
القضبان g اسم مؤلف النموذج: سلسلة
#=> استدعاء السجل النشط
#=> إنشاء db/migrate/2021122224084524_create_authors.rb
#=> إنشاء التطبيق/النماذج/المؤلفين.rb
و كتاب
:
القضبان ز نموذج كتاب نموذج القضبان المؤلف: مراجع العنوان: سلسلة
# => استدعاء السجل النشط
# => إنشاء تطبيق db/migrate/202112224084614_create_books.rb
# => إنشاء التطبيق/النماذج/كتاب.rb
سنحتاج إلى بعض البذور:
# db/seeds.rb
dumas = المؤلف.create(الاسم: 'Alexandre Dumas')
lewis = Author.create(name: 'C.S. Lewis')
مارتين = المؤلف.create(الاسم: 'Robert C. Martin')
كتاب.create(المؤلف: دوماس، العنوان: 'الفرسان الثلاثة')
كتاب.create(المؤلف: لويس، العنوان: 'الأسد والساحرة وخزانة الملابس')
كتاب.create(المؤلف: مارتن، العنوان: "رمز نظيف")
والآن نحن جاهزون لتشغيل عمليات الترحيل وبذر قاعدة البيانات:
قضبان قاعدة بيانات القضبان: ترحيل & وقضبان قاعدة بيانات القضبان: البذور
دعنا نضيف لديه_كثير
للكتب في المؤلف
الطراز:
# التطبيق/النماذج/المؤلف.rb
صنف المؤلف <سجل التطبيق
لديه_كثير :كتب
نهاية
حان الوقت لكتابة وحدة تحكم ستعيد بياناتنا. سنستخدم واجهة برمجة التطبيقات
مساحة الاسم، لذا دعونا أولاً نضيف اختصارًا إلى التصريفات:
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
النهاية
حسنًا، لنضف المتسلسل الخاص بنا إلى ملف الجوهرة
:
# أضف إلى ملف الأحجار الكريمة
جوهرة 'blueprinter'
وبالطبع قم بتثبيته:
تثبيت الحزمة
ثم يمكننا بناء مخططاتنا:
# app/blueprints/author_blueprint.rb
صنف AuthorBlueprint < Blueprinter::Base
المعرف :id
الحقول :اسم
نهاية
# app/blueprints/book_blueprint.rb
صنف BookBlueprint < Blueprinter::Base
المعرف :معرف
الحقول :العنوان
الارتباط :المؤلف، المخطط: مخطط المؤلف
النهاية
إضافة وحدة تحكم أساسية لـ واجهة برمجة التطبيقات
:
# التطبيق/المتحكمون/API/v1/base_controller.rb
وحدة API
الوحدة النمطية V1
صنف BaseController < ActionController::API
نهاية
نهاية
النهاية
ومسودة نسخة مراقب الكتب
:
# التطبيق/عناصر التحكم/API/v1/books_controller.rb
الوحدة النمطية API
الوحدة النمطية V1
صنف BooksController <المتحكم الأساسي
تعريف الفهرس
الكتب = جميع الكتب
تصيير json: BookBlueprint.render(الكتب)
النهاية
النهاية
النهاية
النهاية
يجب علينا أيضًا تحديد التوجيه بالطبع:
# config/routes.rb
Rails.application.routes.routes.draw do
مساحة الاسم :api do
مساحة الاسم :v1 do
الموارد :كتب، فقط: :فهرس
نهاية
النهاية
النهاية
لنختبر ما قمنا به حتى الآن:
القضبان ق
الضفيرة http://localhost:3000/api/v1/books
# => [{"id":1,"author":{"id":1,"id":1,"name":"Alexandre Dumas"},"title":"The Three Musketeers"},{"id":2,"author":{"id":2,"id":2,"name":"C.س. لويس"},"العنوان":"الأسد والساحرة وخزانة الملابس"},{"المعرف":3,"المؤلف":{"المعرف":3,"المؤلف":{"المعرف":3,"الاسم":"روبرت سي مارتن"},"العنوان":"نظيف الكود"}]
تبدو البيانات على ما يرام، ماذا عن السجلات؟
سجلات الطلبات # (n+1)
تم بدء GET "/API/v1/books" ل 127.0.0.1 في 2021-12-24 10:19:40 +0100
تتم المعالجة بواسطة API::V1::BooksController#index ك*/*
تحميل الكتب (0.1 مللي ثانية) حدد "الكتب".* من "الكتب"
↳ ↳ التطبيق/المتحكمون/مراقبو/موقع API/v1/books_controller.rb:7:في 'الفهرس'
تحميل المؤلفين (0.1 مللي ثانية) حدد تحديد "المؤلفين".* من "المؤلفين" حيث "المؤلفون"."id" = ؟ LIMIT ? [[["المعرف"، 1]، ["LIMIT"، 1]]
تحميل المؤلفين (0.1 مللي ثانية) حدد تحديد "المؤلفين".* من "المؤلفين" حيث "المؤلفون"."id" = ؟ LIMIT ? [[["المعرف"، 2]، ["LIMIT"، 1]]
تحميل المؤلفين (0.1 مللي ثانية) حدد تحديد "المؤلفين".* من "المؤلفين" حيث "المؤلفون"."id" = ؟ LIMIT ? [[["المعرف"، 3]، ["LIMIT"، 1]]
تم إكمال 200 موافق في 6 مللي ثانية (المشاهدات: 0.1 مللي ثانية | ActiveRecord: 0.4 مللي ثانية | التخصيصات: 3134)
باستخدام الارتباط في المتسلسلات لدينا، قدمنا n+1
مشكلة. نريد التخلص منها بإضافة تحكم المستخدم فيما يطلبه في نقطة النهاية هذه. لذا يجب أن يكون قادرًا إما على تحميل الكتب فقط، أو تمرير معلمة التضمين والحصول على المؤلفين أيضًا، ولكن يفضل أن يكون ذلك بدون n+1
.
دعونا نعرّف ثابتًا يحتفظ بمعلومات حول ما يمكن للمستخدم تضمينه في كتب#index
الإجراء:
# lib/constants/books/includes.rb
وحدة الثوابت
وحدة الكتب
وحدة التضمينات
مسموح = {
فهرس %i[
مؤلف
].تجميد
}.freeze
النهاية
النهاية
النهاية
بعد ذلك، نحدد مساحة اسم لثوابت الكائنات الفارغة:
# lib/constants/empty.rb
وحدة الثوابت
وحدة فارغة
HASH = {}.freeze
النهاية
النهاية
وهذه هي خدمتنا الرئيسية للسماح بالتضمين. أعتقد أن الرمز لا يحتاج إلى شرح، بعض الأجزاء من السحر
يتم تخصيصها فقط في #default_resources_resources_key
و #Default_purpose
. تم تعريف هذه الأساليب للسماح لنا باستدعاء تصريح يتضمن تمرير البارامترات فقط في وحدات تحكم القضبان. سيكون الناتج هو التجزئة التي تخزن صحيح
لكل إدراج مسموح به.
# app/services/permit_includes.rb
تتطلب 'ثوابت/فارغة'
تتطلب 'ثوابت/ثوابت/كتب/مشتملات'
فئة PermitIncludes
فارغة = الثوابت::فارغة
كوما = '،'
SLASH = '/'
INCLUDES_FORMAT = / A[a-z]+(,[a-z]+)*z/.freeze
المسموح_بإدراجها = {
الكتب الثوابت::كتب::يشمل::ALLOWED
}.freeze
def call(params, resources: default_resources_key(params), purpose: default_purpose(params))
إرجاع فارغ::HASH ما لم يتضمن_مرسل؟ (بارامز)
إرجاع فارغ::HASH ما لم يتضمن_صحيح؟(params)
التضمينات_المطلوبة = تحليل_التضمينات(params)
المسموح_بالتضمينات المسموح بها = تصفية_التضمينات(تضمينات_مطلوبة، الموارد، الغرض)
المسموح_بإدراجها.index_with(صحيح)
النهاية
خاص
def default_resources_key(params)
رفع (ArgumentError, 'params :مفتاح وحدة التحكم يجب أن يكون سلسلة') إلا إذا كان params[:وحدة تحكم].is_a?(String)
params[:وحدة تحكم].split(SLASH).last&.to_sym
النهاية
def default_purpose(params)
رفع(ArgumentError, 'البارامز :مفتاح الإجراء يجب أن يكون سلسلة') إلا إذا كان البارامز[:إجراء].is_a?(سلسلة)
params[:إجراء].to_sym
إنهاء
ديف يتضمن_الإرسال؟(params)
params.key?(:includes)
نهاية
ديف includes_valid?(params)
إرجاع خطأ ما لم يكن params[:يتضمن].is_a?(String)
params[:includes].match؟(INCLUDES_FORMAT)
النهاية
تعريف parse_includes(params)
params[:includes].split(COMMA).map(&:to_sym)
النهاية
def filter_includes(requested_includes, resources_key, purpose)
المطلوب_يتضمن & ALLOWED_INCLUDES [الموارد_المفتاح] [الغرض]
نهاية
النهاية
نحتاج الآن إلى استخدام المفاتيح لتحميل التضمينات وتمرير تجزئة التضمينات نفسها إلى أداة التسلسل:
# التطبيق/عناصر التحكم/API/v1/books_controller.rb
الوحدة النمطية API
الوحدة النمطية V1
صنف BooksController <المتحكم الأساسي
تعريف الفهرس
يتضمّن = PermitIncludes.new.call(params)
الكتب = Book.includes.includes(includes.keys).all
تصيير json: تصيير BookBlueprint.render(كتب، يتضمن: يتضمن)
النهاية
النهاية
النهاية
النهاية
وهذه هي الطريقة التي يجب أن نعدّل بها أداة التسلسل لدينا - نقوم بتحميل الارتباط فقط إذا كان متضمنًا:
# app/blueprints/book_blueprint.rb
صنف BookBlueprint (_حقل_الاسم، _كتاب، خيارات) {
خيارات[:يتضمن] && خيارات[:يتضمن][:المؤلف]
}
النهاية
لنختبرها مرة أخرى:
القضبان ق
تجعيد http://localhost:3000/api/v1/books
# => [{"المعرف":1، العنوان: "الفرسان الثلاثة"},{"المعرف":2، العنوان: "الأسد والساحرة وخزانة الملابس"},{"المعرف":3، العنوان: "الرمز النظيف"}]
سجلات طلب # (نقوم بتحميل الكتب فقط)
بدأ GET "/API/v1/books" لـ ::1 في 2021-12-24 10:33:41 +0100
تتم المعالجة بواسطة API::V1::V1::BooksController#index ك*/*
(0.1 مللي ثانية) حدد sqlite_version (*)
↳ التطبيق/المتحكمون/API/v1/books_controller.rb:8:في 'الفهرس'
تحميل الكتب (0.1 مللي ثانية) حدد "الكتب".* من "الكتب"
↳ التطبيق/المتحكمون/واجهة المستخدم/واجهة المستخدم/v1/books_controller.rb:8:8:في 'الفهرس'
تم الإكمال 200 موافق في 9 مللي ثانية (المشاهدات: 0.1 مللي ثانية | ActiveRecord: 0.9 مللي ثانية | التخصيصات: 4548)
جيد، لم نجتاز التضمينات، لذا حصلنا على الكتب فقط، دون المؤلفين. لنطلبهم الآن:
تجعيد 'http://localhost:3000/api/v1/books?includes=author'
# => [{"id":1,"author":{"id":1,"name":"Alexandre Dumas"},"title":"The Three Musketeers"},{"id":2,"author":{"id":2,"id":2,"name":"C.S. لويس"},"العنوان":"الأسد والساحرة وخزانة الملابس"},{"المعرف":3,"المؤلف":{"المعرف":3,"المؤلف":{"المعرف":3,"الاسم":"روبرت سي مارتن"},"العنوان":"الرمز النظيف"}]%
سجلات الطلبات # (تم التخلص من n+1)
تم بدء GET "/API/v1/books?includes=author" لـ ::1 في 2021-12-24 10:38:23 +0100
تتم المعالجة بواسطة API::V1::BooksController#index كـ */*
المعلمات: {"includes"=>"author"}
تحميل الكتب (0.1 مللي ثانية) حدد "الكتب".* من "الكتب"
تحميل المؤلفين (0.2 مللي ثانية) حدد "المؤلفين".* من "المؤلفين" حيث "المؤلفون"."id" في (?, ?, ?, ?) [[["id", 1]، ["id", 2]، ["id", 3]]
تم الإكمال 200 موافق في 17 مللي ثانية (المشاهدات: 0.1 مللي ثانية | ActiveRecord: 0.7 مللي ثانية | التخصيصات: 7373)
رائع! لقد قمنا بتحميل الجمعية وإزالتها n+1
مشكلة. يمكن استخدام الخدمة لأي مورد، كل ما نريده هو إضافة الثوابت المسموح بها بالصيغة المناسبة وإضافتها إلى تصاريح التضمينات::ALLOWED_INCLUDES
.
علينا أن نتذكر أنه ربما يجب استخدام هذا الأمر مع ترقيم الصفحات (والحذر) لأن تضمين الارتباطات يمكن أن "يأكل" الكثير من الذاكرة.