محمد بغات

الأعضاء
  • المساهمات

    12
  • تاريخ الانضمام

  • تاريخ آخر زيارة

السُّمعة بالموقع

2 Neutral

آخر الزُوّار

65 زيارة للملف الشّخصي
  1. في هذا الدرس سنتعلّم كيفية استخدام بيئات التطوير ومكتبات أندرويد الشهيرة التي تستخدم التأشير annotation processing في Kotlin. يوجد في عالم الأندرويد العديد من بيئات التطوير الشهيرة التي تبسّط التطوير. يمكنك استخدام نفس البيئات لأجل التطوير في Kotlin كما تفعل في جافا. يقدم هذا الدرس بعض الأمثلة ويسلّط الضوء على الفروقات في الإعدادات. سنلقي نظرة على Dagger, Butterknife, Data Binding, Auto-parcel و DBFlow (بقية بيئات التطوير يمكن إعدادها بشكل مماثل). كل بيئات التطوير هذه تعمل بنظام التأشير annotation processing: حيث تقوم بالتأشير على الشيفرة البرمجيّة وسيتمّ توليد أكواد جاهزة لأجلك. تقلّل التأشيرات من الإسهاب غير الضروري وتبسّط الشيفرة البرمجية، وإن كنت تريد أن تفهم ما الذي يحدث فعلا في وقت التنفيذ runtime، يمكنك إلقاء نظرة على الشيفرة التي تم إنشاؤها. تذكّر أن كل بيئات التطوير هذه تولّد الأكواد في جافا، وليس Kotlin. في Kotlin تقوم بتحديد الارتباطات (dependencies بطريقة مماثلة لجافا باستخدام Kotlin Annotation processing tool (kapt بدلاً من annotationProcessor. Dagger Dagger هو بيئة تطوير تحقن الارتباطات dependency injection. إن لم يكن مألوفًا لديك، يمكنك القراءة عنه في دليل المستخدم. لقد حوّلنا مثال القهوة المذكور في هذا الدليل إلى Kotlin، ويمكنك إيجاد النتيجة هنا. الشيفرة البرمجية لـ Kotlin تبدو مشابهة إلى حد كبير للشيفرة الأصليّة. يمكنك تصفح المثال كله في ملف واحد. كما هو الحال في جافا، يمكنك استخدامInject@ للتأشير على المنشئ constructor المستخدم من قبل Dagger لإنشاء عيّنات instancesمن صنف ما. لدى Kotlin أسلوب مختصر للإعلان عن خاصية ما وعن معامل (parameter) منشئ ما في نفس الوقت. ولكي تؤشّر على المنشئ، استخدم الكلمة constructor بشكل صريح وضَع التأشير Inject@ قبله: class Thermosiphon @Inject constructor( private val heater: Heater ) : Pump { // ... } التأشير على الوظائف methods يبدو تمامًا بنفس الشكل. في المثال التالي Binds@ تحدّد أنّ الكائن Thermosiphon سيستخدم حيثما كان Pump مطلوبًا، تحدّد Provides@ طريقة بناء Heater، كما توضّح Singleton@ أنّ Heater نفسه ينبغي أن يستخدم في كل مكان: @Module abstract class PumpModule { @Binds abstract fun providePump(pump: Thermosiphon): Pump } @Module(includes = arrayOf(PumpModule::class)) class DripCoffeeModule { @Provides @Singleton fun provideHeater(): Heater = ElectricHeater() } الأصناف المؤشّرة بـ Module@ تحدّد كيفية توفير مختلف الكائنات. انتبه إلى أنه عند تمرير معامل تأشير (annotation argument) كمعامل vararg، فسيكون عليك أن تغلّفه بـ arrayOf، كما هو الحال في (Module(includes = arrayOf(PumpModule::class)@ أعلاه. لكي يتمّ توليد تطبيق محقون الارتباط (dependency-injected implementation) لأجل نوع ما, قم بالتأشير عليه بـ Component@. الصّنف المُولّد سيكون له اسم هذا النوع مسبوقًا بـ Dagger، مثل DaggerCoffeeShop أدناه: @Singleton @Component(modules = arrayOf(DripCoffeeModule::class)) interface CoffeeShop { fun maker(): CoffeeMaker } fun main(args: Array<String>) { val coffee = DaggerCoffeeShop.builder().build() coffee.maker().brew() } يقوم Dagger بتوليد تطبيق لـ CoffeeShop والذي يسمح لك بالحصول على صنف CoffeeMaker محقون بالكامل. يمكنك التنقل ومشاهدة تطبيق DaggerCoffeeShop إذا قمت بفتح المشروع في بيئة التطوير. لاحظنا أن التأشير على الأكواد البرمجية لم يتغيّر تقريبًا عند التحوّل إلى Kotlin. الآن دعونا نرى التغييرات التي ينبغي إدخالها على نص البناء البرمجي. في جافا تقوم بربط Dagger بـ annotationProcessor (أو apt): dependencies { ... annotationProcessor "com.google.dagger:dagger-compiler:$dagger-version" } في Kotlin عليك إضافة ملحقة kotlin-kapt لإتاحة kapt, ثم قم باستبدال annotationProcessor بـ kapt: apply plugin: 'kotlin-kapt' dependencies { ... kapt "com.google.dagger:dagger-compiler:$dagger-version" } هذا كل شيء! لاحظ أن kapt يعتني بملفات جافا كذلك، لذلك لا تحتاج الإبقاء على الارتباط بـ annotationProcessor. النص البرمجي الكامل لبناء لهذا المشروع يمكن العثور عليه هنا . يمكنك أيضا الاطّلاع على الكود البرمجي المحوَّل لأندرويد. ButterKnife يسمح ButterKnife بربط العروض views بالحقول مباشرة بدلاً من استدعاء findViewById. لاحظ أن ملحقات أندرويد الإضافية لـ Kotlin (مُدمجة تلقائيًا في ملحقة Kotlin في أندرويد ستوديو) تحل المسألة نفسها: استبدال findViewById بكود برمجي موجز وواضح. ربّما عليك استخدامه إلا إذا كنت تستخدم ButterKnife سلفًا ولا تنوي الهجرة. يمكنك استخدام ButterKnife مع Kotlin بنفس طريقة استخدامه مع جافا. دعونا نرى أوّلا التغيرات التي طرأت على نص البناء البرمجي في Gradle، ومن ثم تسليط الضوء على بعض الاختلافات في الكود البرمجي. في ارتباطات Gradle استخدم الملحقة kotlin-kapt وقم باستبدال annotationProcessor بـ kapt: apply plugin: 'kotlin-kapt' dependencies { ... compile "com.jakewharton:butterknife:$butterknife-version" kapt "com.jakewharton:butterknife-compiler:$butterknife-version" } لقد قمنا بتحويل عيّنة ButterKnife إلى Kotlin. يمكن العثور على الكود الناتج هنا. دعونا نلقي نظرة أكثر عليه لمعرفة ما الذي تغيّر. في جافا تؤشّر على الحقل وتربطه بالعرض view المقابل: @BindView(R2.id.title) TextView title; في Kotlin لا يمكنك العمل مع الحقول مباشرة، ولكن بدل ذلك يمكنك العمل مع الخصائص. حيث تؤشّر على الخاصّية: @BindView(R2.id.title) lateinit var title: TextView يتم تعريف التأشير BindView@ ليتم تطبيقه على الحقول فقط، مُترجم Kotlin يدرك هذا ويقوم بالتأشير على الحقل المقابل تلقائيا عندما تقوم بالتأشير على كامل الخاصيّة. لاحظ كيف أن مُعدِّل lateinit يسمح بالتصريح بأنواع غير معدومةnon-null مُبتدَأً (initialized) بعد إنشاء الكائن (بعد استدعاء المنشئ). بدون lateinit سيكون عليك أن تُصرّح بنوع ذي قيمة معدومةً (nullable type) وتقوم بإضافة تحقيقات إضافية لاختبار العدميّة nullability. يمكنك أيضًا إعداد الوظائف كمُصغيات listeners، باستخدام تأشيرات ButterKnife: @OnClick(R2.id.hello) internal fun sayHello() { Toast.makeText(this, "Hello, views!", LENGTH_SHORT).show() } يحدد هذا الكود إجراءً ليتم تنفيذه عند النقر على الزر “hello”. لاحظ أنه باستخدام صيغة lambdas فإن الكود يبدو موجزًا عند كتابته مباشرةً في Kotlin: hello.setOnClickListener { toast("Hello, views!") } الدالّة toast مُعرّفة في المكتبة Anko . ربط البيانات مكتبة ربط البيانات تسمح لك بربط بيانات تطبيقك بالخُطاطةlayouts بطريقة موجزة. يمكنك إتاحة المكتبة باستخدام نفس الإعدادات في جافا: android { ... dataBinding { enabled = true } } لجعله يعمل مع أصناف Kotlin قم بإضافة الارتباط kapt: apply plugin: 'kotlin-kapt' dependencies { kapt "com.android.databinding:compiler:$android_plugin_version" } عند التبديل إلى Kotlin، لا تتغير ملفات خطاطةxml على الإطلاق. على سبيل المثال، يمكنك استخدام variableفي data لوصف متغير يمكن استخدامه في الخطاطة. يمكنك تعريف متغير من نوع Kotlin: <data> <variable name="data" type="org.example.kotlin.databinding.WeatherData"/> </data> يمكنك استخدام الصيغة {}@ لكتابة العبارات، بحيث يمكن الإحالة إلى خصائص Kotlin : <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@{data.imageUrl}" android:contentDescription="@string/image" /> لاحظ أن لغة التعبير عن ربط البيانات تستخدم نفس القواعد للإحالة إلى الخصائص كما هو الحال في Kotlin، أي: data.imageUrl . في Kotlin يمكنك كتابة v.prop بدلا من ()v.getProp حتى لو كانت ()getProp من وظائف جافا. وبالمثل، بدلًا من استدعاء مُحدِّدٍ setter مباشرة، فيمكنك استخدام الإحالة: class MainActivity : AppCompatActivity() { // ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.data = weather // the same as // binding.setData(weather) } } يمكنك ربط مُصغي لإطلاق إجراء ما عندما يحدث حدث معين: <Button android:text="@string/next" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="startOtherActivity" /> هنا startOtherActivity هي وظيفة محددة في النشاط الرئيسي MainActivity: class MainActivity : AppCompatActivity() { // ... fun startOtherActivity(view: View) = startActivity<OtherActivity>() }   يستخدم هذا المثال الدالة startActivity التي تنشئ نيّة intent بدون بيانات وتبدأ نشاطًا جديدًا، والذي يأتي من المكتبة Anko. لأجل تمرير البيانات يمكنك كتابة (startActivity<OtherActivity>("KEY" to "VALUE". لاحظ أنه بدلًا من الإعلان باستخدام lambdas في xml كما في المثال التالي، يمكنك ربط الإجراءات مباشرةً في الكود البرمجي: <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.onSaveClick(task)}" /> // the same logic written in Kotlin code button.setOnClickListener { presenter.onSaveClick(task) } في السّطر الأخير تم تحديد button بالرّجوع إلى رقم تعريفها id باستخدام ملحقة أندرويد لـ Kotlin. يمكنك استخدام هذه الملحقة كبديل وهو ما سيسمح لك بالحفاظ على منطق الربط في الكود البرمجي والحصول على نص برمجي موجز في نفس الوقت. يمكنك العثور على مشروع تمثيليّ هنا. DBFlow DBFlow هي مكتبةSQLite تسمح بتبسيط التفاعل مع قواعد البيانات. كما تعتمد بشكل كبير على التأشير. لاستخدامها مع Kotlin قم بإعداد ارتباطات التأشير باستخدام kapt: apply plugin: 'kotlin-kapt' dependencies { kapt "com.github.raizlabs.dbflow:dbflow-processor:$dbflow_version" compile "com.github.raizlabs.dbflow:dbflow-core:$dbflow_version" compile "com.github.raizlabs.dbflow:dbflow:$dbflow_version" } إن كان تطبيقك يستخدم بالفعل DBFlow، فيمكنك إقحام Kotlin في المشروع بدون مشاكل. يمكنك التّحويل التّدريجي لأكوادك البرمجية الموجودة إلى Kotlin (لضمان ترجمة كل شيء بشكل متناسق). الكود البرمجي المُحوّل لا يختلف كثيرا عن نظيره في جافا. على سبيل المثال، التصريح بجدول يبدو مشابهًا لجافا مع فارق صغير يتمثّل في أنّ القيم الافتراضية للخصائص يجب تحديدها صراحة: @Table(name="users", database = AppDatabase::class) class User : BaseModel() { @PrimaryKey(autoincrement = true) @Column(name = "id") var id: Long = 0 @Column var name: String? = null } بالإضافة إلى تحويل الوظائف الموجودة إلى Kotlin، يمكنك أيضًا الاستفادة من الدعم الخاص بـ Kotlin. على سبيل المثال، يمكن أن تقوم بتعريف الجداول كـ أصناف بيانات data classes. @Table(database = KotlinDatabase::class) data class User(@PrimaryKey var id: Long = 0, @Column var name: String? = null) يعرّف DBFlow مجموعة من الإضافات لجعل استخدامه في Kotlin أكثر سلاسةً، بحيث يمكنك إدراجه في الارتباطات خاصتك: dependencies { compile "com.github.raizlabs.dbflow:dbflow-kotlinextensions:$dbflow_version" } هذا سيمنحك وسيلة للتعبير عن الاستعلامات بأسلوب مشابه لـ C#/LINQ ، استخدم lambdas لكتابة أكواد أبسط بكثير لأجل العمليّات المتزامنة وغيرها. التّحزيم التلقائي Auto-Parcel يسمح التحزيم التلقائي بتوليد قيم محزومة Parcelable من الأصناف المؤشّرة بـ AutoValue@. عند تحديد الارتباطات dependency استخدم مرةً أخرى kapt كمُؤشّر لمعالجة ملفات Kotlin: apply plugin: 'kotlin-kapt' dependencies { ... kapt "frankiesardo:auto-parcel:$latest-version" } يمكنك العثور على النّموذج المُحوّل من هنا. يمكنك التأشير على أصناف Kotlin بـ AutoValue@. دعونا نلقي نظرة على الصّنف المحوّلAddress الذي سيتمّ توليد صياغته المحزومة Parcelable implementation : @AutoValue abstract class Address : Parcelable { abstract fun coordinates(): DoubleArray abstract fun cityName(): String companion object { fun create(coordinates: DoubleArray, cityName: String): Address { return builder().coordinates(coordinates).cityName(cityName).build() } fun builder(): Builder = `$AutoValue_Address`.Builder() } @AutoValue.Builder interface Builder { fun coordinates(x: DoubleArray): Builder fun cityName(x: String): Builder fun build(): Address } } ليس لـ Kotlin وظائف ثابتة static methods، لذلك ينبغي تضمينها داخل كائن مرافق. إذا كنت مصرًّا على استخدامها من داخل الأكواد البرمجية لجافا، قم بالتأشير عليهم بـ JvmStatic. إن كنت ترغب في الوصول إلى صنف أو وظيفة من جافا باسم غير صالح في Kotlin، يمكنك تمرير الاسم داخل علامتي تنصيص (``)، كما هو الحال مع الصّنف التي تم إنشاؤه AutoValue_Address$. عمومًا الكود البرمجي المُحوّل يبدو مشابها كثيرا لكود جافا الأصلي. ترجمة -وبتصرّف- للمقال Android Frameworks Using Annotation Processing من توثيقيات KOTLIN هذا المقال منشور تحت رخصة Apache 2 license
  2. في هذا الدرس، ستتعلم كيفية استخدام خُطّافات (Git (Git hooks لأتمتة نشر بيئة الإنتاج لتطبيقات Rails على خادم أوبونتو 14.04 عن بُعد. باستخدام خُطّافات Git ستتمكن من نشر التطبيقات عن طريق دفع التغييرات إلى خادم الإنتاج production server، وبدلًا من أن تقوم بكل شيء يدويًّا (مثل ترحيل قاعدة البيانات) فالاستعانة بأحد أشكال النشر الآلي، مثل خُطّافات Git، سيوفر عليك الكثير من الوقت على المدى الطويل. في هذا الدرس سنستخدم خُطّافGit من نوعpost-receive ، بالإضافة إلىPuma كخادم للتطبيق،Nginx كوكيل عكسي لـ Puma و PostgreSQL كقاعدة بيانات. المتطلبات الأساسية سوف تحتاج صلاحيات مستخدم غير جذري non-root والذي يملك امتيازات مستخدم أساسي superuser على خادم أوبونتو. في هذا المثال، سيكون اسم المستخدم deploy. يمكنك تعلم كيفية فعل ذلك في هذا الدرس: الإعداد الابتدائي لخادوم أوبنتو 14.04. إذا كنت ترغب في النشر دون الحاجة لإدخال كلمة المرور، فتأكد من إعداد مفاتيح SSH. سوف تحتاج إلى تثبيت Ruby على خادمك. إذا لم تكن قد فعلت ذلك سلفًا، يمكنك تثبيته جنبًا إلى جنب مع Rails باستخدام rbenv أو RVM. سوف تحتاج أيضًا إلى تطبيق Rails مُدار في مستودع git على جهازك. إذا لم يكن لديك تطبيق في git، فسوف نقدم لك تطبيقًا بسيطًا كمثال لتعمل عليه. لنبدأ على بركة الله. تثبيت PostgreSQL معظم بيئات Rails تستخدم PostgreSQL كقاعدة بيانات، لذلك عليك تثبيته على خادمك الآن. على خادم الإنتاج، قم بتحديث apt-get: sudo apt-get update ثم قم بتثبيت PostgreSQL بهذه التعليمات: sudo apt-get install postgresql postgresql-contrib libpq-dev إنشاء قاعدة بيانات الإنتاج الخاصة بالمستخدم لإبقاء الأمور بسيطةً، سنسمي قاعدة بيانات الإنتاج الخاصة بالمستخدم بنفس اسم التطبيق خاصتك. على سبيل المثال، إذا كان اسم تطبيقك “appname”، فيجب عليك إنشاء مستخدم PostgreSQL بهذه الطريقة: sudo -u postgres createuser -s appname لتعيين كلمة مرور لقاعدة بيانات المستخدم، ادخُل سطر أوامر PostgreSQL هكذا: sudo -u postgres psql بعد ذلك قم بتعيين كلمة المرور لقاعدة بيانات المستخدم “appname” هكذا: \password appname قم بإدخال كلمة المرور التي تريد ثم قم بتأكيدها. اخرج من سطر أوامر PostgreSQL بهذه التعليمة: \q الآن نحن على استعداد لتزويد تطبيقك بمعلومات الاتصال الخاصة بقاعدة البيانات. إعداد تطبيق Rails على جهاز التطوير خاصتك، ستقوم بإعداد تطبيقك لأجل النشر. اختياري: إنشاء تطبيق Rails إن كان لديك تطبيق Rails جاهز للنشر. فيمكنك تخطي هذا القسم والقيام بالتغييرات المناسبة لاحقًا. أمّا إن لم يكن لديك تطبيق جاهز، فإن الخطوة الأولى هي إنشاء تطبيق Rails جديدة. هذه التعليمات ستنشئ تطبيق Rails جديد تحت اسم “appname” في المجلد الرئيسي. لا تتردد في استبدال “appname” بالاسم الذي تريد: cd ~ rails new appname ثم تحوّل إلى مجلد التطبيق: cd appname لأجل تطبيقنا هذا، سوف نقوم بتوليد سقالة scaffold controller لكي يجد تطبيقنا شيءً ليعرضه: rails generate scaffold Task title:string note:text لنتأكدْ الآن من أن تطبيقنا موجود في مستودعgit . تهيئة Git Repo إن لم يكن تطبيقك موجودًا بالفعل في مستودع git لسبب ما، قم بتهيئته وإجراء إلزام أولي initial commit. قم بالتحوّل إلى مجلد التطبيق. في مثالنا، التطبيق يسمى " appname" وهو موضوع في المجلد الرئيسي home directory: cd ~/appname git init git add -A git commit -m 'initial commit' الآن دعونا نُجهّز تطبيقنا لربط الاتصال بقاعدة بيانات الإنتاج لـ PostgreSQL. تحديث إعدادات قاعدة البيانات تحوّل إلى مجلد تطبيقك إن لم تكن بالفعل هناك. في مثالنا، التطبيق يسمى “appname” وهو موضوع في المجلد الرئيسي home directory: cd ~/appname الآن افتح ملف إعداد قاعدة البيانات في محرر النصوص المفضل لديك: vi config/database.yml اعثر على مقطع الإنتاج production section في إعدادات قاعدة بيانات تطبيقك، وقم باستبداله بمعلومات الاتصال بقاعدة بيانات الإنتاج خاصتك. من المفروض أن يبدو كشيء من هذا القبيل (قم باستبدال القيم عند الاقتضاء): config/database.yml excerpt production: <<: *default host: localhost adapter: postgresql encoding: utf8 database: appname_production pool: 5 username: <%= ENV['APPNAME_DATABASE_USER'] %> password: <%= ENV['APPNAME_DATABASE_PASSWORD'] %> احفظ واخرج. هذا الملف يؤكد على أن بيئة الإنتاج الخاصة بالتطبيق ينبغي أن تستخدم قاعدة بيانات PostgreSQL تحت مُسمّى “appname_production” على المضيف المحلي localhost. لاحظ أنه تم إحالة اسم المستخدم وكلمة مرور قاعدة البيانات إلى متغيرات البيئة environment variables. سنقوم بتحديدها على الخادم في وقت لاحق. تحديث Gemfile إذا لم يكن لدى Gemfile خاصتك المكتبة pg (PostgreSQL adapter gem)، ولم تكن المكتبة Puma مُحددة، فيجب عليك إضافتهما الآن. افتح Gemfile الخاص بتطبيقك في المحرّر المفضل لديك: vi Gemfile أضف الأسطر التالية إلىGemfile : Gemfile excerpt group :production do gem 'pg' gem 'puma' end احفظ واخرج. سيحدد هذا النص البرمجي أن بيئة الإنتاج production environment يجب أن تستخدم المكتبات pgوpuma : إعداد Puma قبل إعداد Puma، يجب عليك أن تتحقق من عدد وحدات المعالجة المركزية التي يملكها خادمك. يمكنك بسهولة فعل ذلك على خادمك بهذه التعليمة: grep -c processor /proc/cpuinfo الآن، على جهاز التطوير خاصتك، قم بإضافة إعدادات Puma إلى الإعداد config/puma.rb . افتح الملف في محرر النصوص: vi config/puma.rb انسخ وألصق هذه الإعدادات في الملف: config/puma.rb # Change to match your CPU core count workers 2 # Min and Max threads per worker threads 1, 6 app_dir = File.expand_path("../..", __FILE__) shared_dir = "#{app_dir}/shared" # Default to production rails_env = ENV['RAILS_ENV'] || "production" environment rails_env # Set up socket location bind "unix://#{shared_dir}/sockets/puma.sock" # Logging stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true # Set master PID and state locations pidfile "#{shared_dir}/pids/puma.pid" state_path "#{shared_dir}/pids/puma.state" activate_control_app on_worker_boot do require "active_record" ActiveRecord::Base.connection.disconnect! rescue Ac-tiveRecord::ConnectionNotEstablished Ac-tiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env]) end قم بتغيير العددworkers إلى عدد وحدات المعالجة المركزية لخادمك. يفترض المثال أن لديك اثنان. احفظ واخرج. الآن تم إعداد Puma بموضعlocation تطبيقك وموضع مقبسه socket والمذكرات logs ومعرّفات العمليات PIDS. لا تتردد في تعديل الملف، أو إضافة الخيارات التي تناسبك. ألزمCommit التغييرات الأخيرة: git add -A git commit -m 'added pg and puma' قبل الاستمرار، قم بتوليد المفتاح السري والذي سيتم استخدامه لبيئة الإنتاج الخاصة بتطبيقك: rake secret rake secret sample output: 29cc5419f6b0ee6b03b717392c28f5869eff0d136d8ae388c68424c6e5dbe52c1afea8fbec305b057f4b071db1646473c1f9a62f803ab8386456ad3b29b14b89 سوف تنسخ المُخرجات وتستخدمها لتحديد القيمة SECRET_KEY_BASE الخاصة بتطبيقك في الخطوة التالية. إنشاء النص البرمجي لإطلاق Puma سنقوم بإنشاء نص برمجي للإطلاق (Upstart init script). حتى نتمكن من تشغيل وإيقاف Puma بسهولة، وللتأكد من أنه سيبدأ عند بدء التشغيل. على خادم الإنتاج خاصتك، حمّل أداة Jungle Upstart من مستودع Puma على GitHub وضعها في المجلد الرئيسي: cd ~ wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma-manager.conf wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma.conf الآن افتح الملف puma.conf حتى تتمكن من تحرير إعدادات النشر الخاصة بمستخدمPuma : vi puma.conf ابحث عن السطرين الذين يحددان setuid و setgid، و قم باستبدال “apps” باسم النشر الخاص بالمستخدم أو المجموعة خاصتك. على سبيل المثال، إذا كان اسم مستخدم النشر “deploy”، فينبغي أن تكون الأسطر هكذا: puma.conf excerpt 1 of 2 setuid deploy setgid deploy الآن ابحث عن السطر الذي يحتوي:exec /bin/bash <<'EOT'. أضف الأسطر التالية تحته، وتأكد من استبدال اسم المستخدم وكلمة المرور الخاصة ب PostgreSQL، وأضف كذلك rake secret الذي قمت بإنشائه سابقًا: puma.conf excerpt 2 of 2 export APPNAME_DATABASE_USER='appname' export APPNAME_DATABASE_PASSWORD='appname_password' export SECRET_KEY_BASE='rake_secret_generated_above' احفظ واخرج. الآن انسخ النصوص في مجلد خدمات الإطلاق Upstart services: sudo cp puma.conf puma-manager.conf /etc/init النص البرمجي puma-manager.conf يُحدد /etc/puma.conf كمرجع لمعرفة التطبيقات التي يجب إدارتها. دعونا ننشئ ونحرّر هذا الملف الآن: sudo vi /etc/puma.conf كل أسطر هذا الملف يجب أن تتضمن مسارات التطبيقات التي تريد من Puma أن يُديرها. سنقوم بنشر تطبيقنا في مجلد يُسمى “appname” داخل المجلد الرئيسي. في هذا المثال، سيكون كما يلي (تأكد من تعديل المسار ليتناسب مع المكان الذي يتواجد فيه تطبيقك): /etc/puma.conf /home/deploy/appname احفظ واخرج الآن تمّ إعداد تطبيقك لينطلق عند بدء التشغيل بمساعدة Upstart, وهذا يعني أن تطبيقك سيبدأ حتى بعد إعادة إقلاع خادمك. لا تنسى أننا لم ننشر التطبيق حتى الآن، لذلك لسنا جاهزين لتشغيله بعد. تثبيت وإعداد Nginx لجعل التطبيق متاحًا على شبكة الإنترنت، يجب أن تستخدم Nginx كخادم. قم بتثبيت Nginx باستخدام apt-get: sudo apt-get install nginx الآن افتح كتلة الخادم الافتراضي default server block بمحرر النصوص: sudo vi /etc/nginx/sites-available/default استبدل محتويات الملف بالتعليمات البرمجية التالية. تأكد من استبدال الأجزاء الملوّنة باسم المستخدم واسم التطبيق المناسبين. /etc/nginx/sites-available/default upstream app { # Path to Puma SOCK file, as defined previously server unix:/home/deploy/appname/shared/sockets/puma.sock fail_timeout=0; } server { listen 80; server_name localhost; root /home/deploy/appname/public; try_files $uri/index.html $uri @app; location @app { proxy_pass http://app; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; keepalive_timeout 10; } احفظ واخرج. سيقوم هذا النص البرمجي بإعداد Nginx كوكيل عكسي، لذلك طلبات HTTP ستُرسل إلى الخادم Puma عبر مقبس يونيكس Unix socket. لا تتردد في إجراء التغييرات التي تراها مناسبةً. لن نقوم بإعادة تشغيل Nginx, فالتطبيق غير موجود بعدُ على الخادم. سنقوم بإعداد التطبيق فيما يلي. إعداد مستودع الإنتاج (git (Prepare Production Git Remote على خادم الإنتاج، قم بتثبيت git بواسطة apt-get: sudo apt-get install git ثم قم بإنشاء مجلد للمستودع البعيد remote repository. سنقوم بإنشاء مجلد git أوّلي في المجلد الرئيسي وسنسميه “appname_production”. يمكنك تسمية المستودع البعيد كما تريد (ولكن لا تضعه في ~/appnameلأنه المكان الذي سننشر فيه التطبيق): mkdir ~/appname_production cd ~/appname_production git init –bare بما أن هذا المستودع أوّلي، فلا يوجد مجلّد عمل بعدُ وجميع الملفات الموجودة في .git موجودة في المجلد الرئيسي نفسه. نحن بحاجة إلى إنشاء خُطّاف git من نوعpost-receive ، والذي هو النص البرمجي الذي سيتم تشغيله عندما يتلقى خادم الإنتاج دفعةً من git(git push). افتح الملف hooks/post-receive في محرر النصوص: vi hooks/post-receive انسخ وألصق النص التالي في الملف post-receive: hooks/post-receive #!/bin/bash GIT_DIR=/home/deploy/appname_production WORK_TREE=/home/deploy/appname export APPNAME_DATABASE_USER='appname' export APPNAME_DATABASE_PASSWORD='appname_password' export RAILS_ENV=production . ~/.bash_profile while read oldrev newrev ref do if [[ $ref =~ .*/master$ ]]; then echo "Master ref received. Deploying master branch to produc-tion..." mkdir -p $WORK_TREE git --work-tree=$WORK_TREE --git-dir=$GIT_DIR checkout -f mkdir -p $WORK_TREE/shared/pids $WORK_TREE/shared/sockets $WORK_TREE/shared/log # start deploy tasks cd $WORK_TREE bundle install rake db:create rake db:migrate rake assets:precompile sudo restart puma-manager sudo service nginx restart # end deploy tasks echo "Git hooks deploy complete" else echo "Ref $ref successfully received. Doing nothing: only the mas-ter branch may be deployed on this server." fi done تأكد من تحديث القيم التالية: GIT_DIR :مجلد المستودع الأولي لـ (git (bare git repository الذي قمت بإنشائه في وقت سابق WORK_TREE : المجلد حيث تريد نشر تطبيقك (يجب أن يتطابق مع الموضع الذي قمت بتحديده في إعدادات Puma) APPNAME_DATABASE_USER :اسم مستخدم PostgreSQL (ضروري لمهام rake ) APPNAME_DATABASE_PASSWORD : كلمة مرور PostgreSQL (ضروري لمهام rake ) بعد ذلك، يجب عليك مراجعة التعليمات الموجودة بين التعليقين # start deploy tasks و # end deploy tasks. هذه هي التعليمات التي سيتم تشغيلها في كل مرة يتم دفع push الشعبة الرئيسية master branch إلى مستودع الإنتاج في(git (appname_production. إذا تركتها كما هي، فسيحاول الخادم القيام بما يلي بالنسبة لبيئة الإنتاج الخاصة بتطبيقك: تشغيل المُحزّم bundler إنشاء قاعدة بيانات ترحيل قاعدة البيانات الترجمة الأوليةPrecompile للأصول assets إعادة تشغيل Puma إعادة تشغيل Nginx إذا كنت ترغب في إجراء أية تغييرات أو أي إضافات للتحقق من الأخطاء، لا تتردد في القيام بذلك. بمجرد الانتهاء من مراجعة النص البرمجي احفظه واخرج. بعد ذلك، اجعل البرنامج النصي قابلًا للتنفيذ: chmod +x hooks/post-receive Sudo بلا كلمة مرور Passwordless Sudo لأن الخُطّاف post-receive يحتاج إلى تشغيل تعليماتsudo ، فسنسمح للمستخدم deploy باستخدام sudo بدون كلمة مرور(استبدل اسم المستخدمdeploy في حال اخترت اسمًا مختلفًا): sudo sh -c 'echo "deploy ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-deploy' هذا سيسمح للمستخدم deploy بتشغيل التعليمة sudo دون الحاجة لإعطاء كلمة المرور. ربما تريد أن تُقيّد التعليمات التي يمكن للمستخدمdeploy القيام بها. وكحد أدنى، عليك استخدام مفتاح المصادقة SSH كما عليك تعطيل المصادقة بكلمة المرور password authentication. إضافة Production Git Remote الآن بعد أن أعددنا كل شيء لخادم الإنتاج، دعونا نضيف production git remote لمستودع التطبيق خاصتنا. على جهاز التطوير خاصتك، تأكد من أنك في مجلد التطبيق: cd ~/appname ثم قم بإضافة مستودع git بعيد (git remote) جديد تحت اسم “production” والذي يشير إلى مستودع git الأولي appname_production الذي أنشأته على خادم الإنتاج. استبدل اسم المستخدم (deploy) وعنوان الـ IP الخاص بالخادم واسم المستودع البعيد (appname_production): git remote add production deploy@production_server_public_IP:appname_production لقد صار تطبيقك الآن جاهزًا للنشر بواسطة git push. النشر للإنتاج Deploy to Production بعد كل الإعدادات التي قمنا بها، يمكنك الآن نشر تطبيقك على الخادم خاصتك عن طريق تشغيل تعليمات git التالية: git push production master هذا سيدفع push شعبتك الرئيسية المحلية local master branch إلى مستودع الإنتاج البعيد production remote الذي قمت بإنشائه سابقًا. عندما يتلقى production remote أمر الدفع، فسينفّذ النصَّ البرمجي post-receive الذي أعددناه في وقت سابق. إذا قمت بكل شيء بشكل صحيح، فيجب أن يكون تطبيقك متاحًا الآن على عنوان الـ IP العام لخادم الإنتاج خاصتك. إذا كنت تستخدم التطبيق التعليمي لهذا الدرس، فمن المفروض أن تكون قادرًا على الوصول إلى http://production_server_IP/tasks من أيّ متصفح و من المفروض أن ترى شيئًا من هذا القبيل: الخلاصة في أي وقت تقوم بإجراء تغيير على تطبيقك، يمكنك تشغيل نفس التعليمة git push للنشر على خادم الإنتاج خاصتك. هذا لوحده من المفروض أن يوفر عليك الكثير من الوقت على مدى عمر المشروع. لقد شمل هذا الدرس فقط الخطّافات من نوع “post-receive”، ولكن هناك عدة أنواع أخرى من الخطّافات التي يمكن أن تساعدك على تحسين أتمتة عملية النشر. ترجمة -وبتصرّف- للمقال How To Deploy a Rails App with Git Hooks on Ubuntu 14.04 لصاحبه Mitchell Anicas
  3. تم تصميم خطاطة CSS الشّبكيّة لتعمل هي وباقي أجزاء CSS كجزء من نظام شامل لإنجاز التخطيط layout. في هذا الدّرس سنشرح كيف أنّ الشّبكة grid تتّسق مع التقنيات الأخرى التي ربّما تستخدمها سلفًا. Grid و flexbox الفرق الأساسي بين خطاطة CSS الشّبكيّة وخطاطة CSS flexbox هي أنّ flexbox تمّ تصميمه لأجل الخطاطات أحادية البعد، بمعنى خطاطة يُمكن أن تُسلك في صفّ أو عمود. أمّا الشّبكةGrid فقد تمّ تصميمها لأجل الخطاطات ثنائيّة الأبعاد، بمعنى خطاطة تَسري في الصّفوف والأعمدة في نفس الوقت. كلا المواصفتين القياسيتينspecifications تتشاركان في بعض السّمات، وإن كنت قد تعلّمت من قبل كيفيّة استخدام flexbox فسترى بعض أوجه التشابه التي ستساعدك في تعلّم مفهوم الشّبكة بسرعة. الخطاطة أحاديّة البعدvs الخطاطة ثنائية البعد سنستخدم مثال بسيط يمكن أن يُوضّح الفرق بين الخطاطة أحادية البعد أو ثنائية الأبعاد. في المثال الأول، سنستخدم flexbox لنَظم مجموعة من المربّعات. سيكون لدينا خمسة أبناء في الوعاء container، وسنُعيّن للخاصيّةflex قيمًا بحيث يمكنها أن تنبسط وتنقبض في حدود هامش 200 بكسل. كما سنحدّد للخاصّية flex-wrap القيمةwrap ، بحيث إذا صارت المساحة في الوعاء أضيق من أن تستوعب المرونة الأساسيّة flex basis، فسيتمّ وضع العناصر في صفّ جديد. <div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> <div>Four</div> <div>Five</div> </div> .wrapper { display: flex; flex-wrap: wrap; } .wrapper > div { flex: 1 1 200px; } يمكنك أن ترى في الصورة أن اثنين من العناصر انتظما في سطر جديد. هذان العنصران يتقاسمان الحيّز المتاح ولا يصطفّان تحت العناصر الموجودة أعلاها. يحدث هذا لأنّه عندما تُغلّف العناصر المرنة flex items، فإنّ كل صف جديد (أو عمود عند العمل بالأعمدة) يصبح وعاءً مرنًا جديدًا. حيث أنّ توزيع المساحة يحدث على طول الصف. أحد الأسئلة الشائعة هو كيفية جعل هذه العناصر تصطفّ. هنا نحتاج استخدام الخطاطة ثنائيّة الأبعاد: لأنّك إن كنت تريد ضبط المحاذاة عبر الصفوف والأعمدة، فستحتاج إلى الشّبكة grid . نفس الخطاطة لكن مع شبكات CSS في هذا المثال، سنُنشئ نفس الخطاطة باستخدام الشّبكة. هذه المرة لدينا ثلاثة مدارج للأعمدة column tracks. لسنا بحاجة لوضع أي شيء للعناصر نفسها. حيث سينتظمون تلقائيا واحد في كل خلية من الشّبكة التي تم إنشاؤها. وكما هو واضح فقد بقيت في شبكة صارمة strict grid، حيث يصطفّون في صفوف وأعمدة. ومع وجود خمسة عناصر، فستكون هناك فجوة في نهاية الصف 2. <div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> <div>Four</div> <div>Five</div> </div> .wrapper { display: grid; grid-template-columns: repeat(3، 1fr); } سؤال بسيط يجب أن تسأله عند المفاضلة بين الشّبكة و flexbox وهو: هل أنا بحاجة فقط لضبط الخطاطة على صعيد الصّفوف وحدها أو الأعمدة وحدها – استخدم إذًا flexbox هل أحتاج لضبط الخطاطة على صعيد الصفوف والأعمدة معًا – استخدم إذًا الشّبكة محتوى خارجيّ أم خطاطة داخليّة؟ بالإضافة إلى مسألة التفريق بين البعد الواحد والأبعاد الثّنائية، هناك طريقة أخرى لتقرّر ما إذا كان عليك استخدام flexbox أو الشّبكة لأجل التخطيط. يتكيّف flexbox مع حجم المحتوى. فمن الحالات المثالية لاستخدام flexbox هي عندما يكون لديك مجموعة من العناصر التي تريد توزيعها بالتساوي في الوعاء. بحيث يتمّ تحديد المساحة التي سيشغلها كل عنصر على حسب حجم المحتوى. وإذا انتظمت العناصر في سطر جديد، فستتوزع على أساس أحجامها والمساحة المتاحة في ذلك السّطر. الشّبكة تعمل في الاتجاه الداخلي للخطاطة layout in، فعند استخدام خطاطة CSS الشّبكيّة يتمّ إنشاء خطاطة ثم توضع العناصر فيها، وفي حال استخدام قواعد auto-placement ستوضع العناصر في خلايا الشّبكة وفقا للشّبكة الصارمة strict grid. هناك إمكانيّة لإنشاء مدارج tracks تتكيّف مع حجم المحتوى، ولكنّها ستغيّر أيضا كامل المدرج. إذا كنت تستخدم flexbox وتشعر أنّك تفتقد بعض المرونة، فربّما عليك استخدام خطاطة CSS الشّبكيّة. على سبيل المثال إذا حدّدت عرض width عنصر مرن (flex item) بنسبة مئوية لجعله يصطف مع العناصر الأخرى في الصّف الأعلى. في هذه الحالة، فمن المحتمل أن يكون استخدام الشّبكة خيارًا أفضل لك. محاذاة المربّع Box alignment لعلّ من أكثر سمات flexbox إثارة هي أنه مكّننا لأول مرة من التحكم في المحاذاة بشكل صحيح. فقد جعل من السهل توسيط مربع على الصفحة. ويمكن للعناصر المرنة الامتداد على كامل ارتفاع الوعاء، وهذا يعني أنّه صار من الممكن الحصول على أعمدة متساوية الارتفاع. وهي أشياء كنّا نريد القيام به منذ فترة طويلة جدا، كما أنها جاءت مرفقة بجميع أنواع الحيل لمساعدتنا على خلق التأثير البصري الذي نريد. خصائص المحاذاة المحدّدة في المواصفات القياسيّة specification لـ flexbox قد تمّ إضافتها إلى مواصفات قياسيّة جديدة تسمّى Box Alignment Level 3 . وهذا يعني أنّه يمكن استخدامها في مواصفات قياسيّة أخرى، بما في ذلك الخُطاطة الشّبكيّة. وفي المستقبل، قد تنطبق أيضًا على أساليب التخطيط الأخرى. إليك الآن مثالًا بسيطًا لمقارنة flexbox والشّبكة. في المثال الأول، والذي يستخدم flexbox، لدينا وعاء يحتوي ثلاثة عناصر. تمّ تعيين خاصيّة min-height للغلافwrapper ، لتحديد ارتفاع الوعاء المرن flex container. سنعطي أيضا للخاصيّة align-items الخاصّة بالوعاء المرن القيمة flex-end، وبالتالي فإن العناصر سوف تصطفّ في نهاية الوعاء. كما أنّنا أيضا سنحدد الخاصية align-self لـ box1 حتّى تستبدل السّلوك الافتراضي وتمتد على كامل ارتفاع الوعاء وعلى box2 حتّى يصطفّ في بداية الوعاء المرن. <div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three</div> </div> .wrapper { display: flex; align-items: flex-end; min-height: 200px; } .box1 { align-self: stretch; } .box2 { align-self: flex-start; } المحاذاة في شبكات CSS يستخدم المثال الثاني الشّبكة لإنشاء نفس التّصميم. هذه المرة سنستخدم خصائص مربع المحاذاة التي تنطبق على الخطاطة الشبكيّة. لذلك سنستخدم start وend لأجل المحاذاة بدلًا من flex-start وflex-end. في حالة الخطاطة الشبكيّة، فنحن نحاذي العناصر داخل الحيّز الخاص بهم الشّبكة. وهو في هذه الحالة خليّة واحدة، ولكن لا شيء يمنع أن يكون الحيّز مكوّنًا من عدة خلايا. <div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three</div> </div> .wrapper { display: grid; grid-template-columns: repeat(3،1fr); align-items: end; grid-auto-rows: 200px; } .box1 { align-self: stretch; } .box2 { align-self: start; } الوحدة fr و flex-basis لقد رأينا سابقا كيف نستخدم الوحدةfr لأجل تخصيص نسبة من المساحة المتوفرة في وعاء الشّبكة لأجل مدرج الشّبكة grid tracks. عندما تُستخدم الوحدة fr مع الدالّة ()minmax فيمكن أن تعطينا سلوكًا مشابهًا جدًا للخاصيةflex في flexbox وفي الوقت نفسه تُمكّننا من إنشاء خطاطة في بعدين. لو عدنا إلى المثال الذي أوضحنا فيه الفرق بين الخطاطات أحادية وثنائيّة الأبعاد، فيمكنك أن ترى أن هناك فرقا في الطريقة التي تتكيّف وتتجاوب بها الخطاطتان. فمع الخطاطة المرنة، إذا غيّرنا حجم النافذة توسيعًا وتصغيرًا، سيتكيّف flexbox بشكل جيد بتعديل عدد العناصر في كل صف وفقا للمساحة المتاحة. إن كان لدينا ما يكفي من المساحة فكل العناصر الخمسة ستنتظم في صف واحد، أمّا إن كان الوعاء ضيّقًا فقد لا نجد مساحة لأكثر من واحد. على سبيل المقارنة، النسخة الشبكيّة دائما ما يكون لديها ثلاثة مدارج للأعمدة column tracks. والمدارج نفسها سوف تنبسط وتنقبض، ولكن ستكون هناك دائما ثلاثة أعمدة كما طلبنا عند تعريف الشّبكة. الملء التلقائي لمدارج الشّبكة Auto-filling grid tracks يمكننا خلق تأثير مماثل لـ flexbox، دون التفريط في إمكانيّة الحفاظ على المحتوى مرتبًا في صفوف وأعمدة صارمة، من خلال إنشاء تسلسل المدارج track listing باستخدام العبارةrepeat وخاصّيتي auto-fill و auto-fit. في المثال التالي، سنستخدم auto-fill بدلًا من استخدام الأعداد الصحيحة في العبارة repeat وسنحدّد تسلسل المدارج track listing بـ 200 بكسل. وهذا يعني أن الشّبكة سوف تنشئ أكبر عدد من مدارج الأعمدة ذات 200 بكسل التي يمكن احتواؤها في الوعاء. <div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> </div> .wrapper { display: grid; grid-template-columns: repeat(auto-fill، 200px); } عدد مرن من المدارج الأمر هنا مختلف بعض الشيء عن flexbox. ففي مثال flexbox، كانت العناصر أكبر من 200 بكسل قبل التغليف wrapping. يمكننا تحقيق نفس الشّيء في الشّبكة من خلال الجمع بين auto-fill و ()minmax . في المثال التالي، سنُنشئ مدارج تُملأ تلقائيا بواسطة minmax. هدفنا أن نجعل الحدّ الأدنى لحجم المدارج لا يقلّ عن 200 بكسل، سنعيّن الحد الأقصى عند1fr . وبمجرّد أن يخمّن المتصفح كم “200 بكسل” سوف يتّسع لها الوعاء، مع احتساب فجوات الشبكة، فسوف يعتبر الحدّ الأقصى المحدّد 1fr كتعليمات لتوزيع المساحة المتبقية بين العناصر. <div class="wrapper"> <div>One</div> <div>Two</div> <div>Three</div> </div> .wrapper { display: grid; grid-template-columns: repeat(auto-fill، minmax(200px، 1fr)); } لدينا الآن القدرة على إنشاء شبكة بعدد مرن من المدارج المرنة (flexible number of flexible tracks)، ولكن مع محاذاة العناصر التي وُضعت في الشّبكة في الصفوف والأعمدة. الشّبكة والعناصر مُطلقة التموضِع Grid and absolutely positioned elements تتفاعل الشّبكة مع العناصر مطلقة التّموضع، وهو أمر يمكن أن يكون مفيدًا إذا كنّا نرغب في وضع عنصر داخل شبكة أو حيّز من الشبكة. تحدد المواصفات القياسيّة السلوك الافتراضي عندما يكون وعاء الشّبكة كتلة احتواء containing block وحيث يكون وعاء الشّبكة أبًا للعنصر مطلق التّمَوضُع absolutely positioned. وعاء الشبكة ككتلة احتواء لجعل وعاء الشّبكة كتلة احتواء نحتاج إلى إعطاء القيمة relative للخاصيةposition الخاصّة بالوعاء، بالضّبط كما تفعل عادة لصنع كتلة احتواء لأيّ من العناصر الأخرى مُطلقة التموضع absolutely positioned. بعد فعل ذلك، إن حدّدنا لعنصر من الِشبكة position: absolute فإنه سيأخذ نفس الحجم الذي تأخذه كتلة الاحتواء الخاصّة بوعاء الشّبكة، أمّا في حال كان لهذا العنصر الخاصيّة grid position، فسيأخذ حيّز الشّبكة التي وُضع فيها. في المثال أدناه لدينا غلاف يحتوي أربعة عناصر. العنصر الثالث مطلق التموضع وأيضا موضوع على الشّبكة باستخدامline-based placement. وعاء الشّبكة لديه position: relative وكذلك سياق التموضع positioning context لذلك العنصر. <div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3"> This block is absolutely positioned. In this example the grid container is the containing block and so the absolute positioning offset values are calculated in from the outer edges of the area it has been placed into. </div> <div class="box4">Four</div> </div> .wrapper { display: grid; grid-template-columns: repeat(4،1fr); grid-auto-rows: 200px; grid-gap: 20px; position: relative; } .box3 { grid-column-start: 2; grid-column-end: 4; grid-row-start: 1; grid-row-end: 3; position: absolute; top: 40px; left: 40px; } يمكنك أن ترى أن العنصر قد أخذ حيّزًا من الصّف الثاني وحتى الرابع، ثمّ بدأ بعد السّطر 1. مع تحديد احداثيّته باستخدام الخصائصtop و left. كما تمّ إخراجه من الانسيابflow كما هي عادة العناصر مطلقة التموضع كما تضعهم قواعدautoplacement في نفس المساحة. كما لم يتمّ إنشاء صفّ جديد حتّى يمتد العنصر إلى سطر الصّف 3. إن أزلت position: absolute من قواعد .box3 يمكنك أن ترى كيف سيُعرض بدون تحديد الموضع. وعاء الشّبكة كأبٍ A grid container as parent إذا كان لدى الابن مُطلَق التّموضع وعاء شبكيّ grid container كأبٍ ولكنّ ذلك الوعاء لا يخلق سياق تموضع positioning context جديد، فهذا يعني أنه قد أُخرج من الانسيابflow كما في المثال السابق. سياق التموضع positioning context سيكون كالسياق الذي أنشأه العنصر كما هو شائع في أساليب التخطيط الأخرى. في حالتنا، إذا أزلنا position: relative من الغلاف أعلاه، سياق التموضع سيكون من viewport ، كما هو موضح في هذه الصورة. لم يعد يشارك هذا العنصر مرّة أخرى في الخطاطة الشّبكيّة من حيث التحجيم أو عندما تحدّد مواضع العناصر الأخرى بشكل تلقائي. مع حيّز الشّبكة كأب With a grid area as the parent إذا وٌضع العنصر مطلق التموضع داخل حيّز الشّبكة فيمكنك حينها إنشاء سياق تموضع على الحيّز. في المثال أدناه لدينا شبكة كما قبل ولكن هذه المرة سنُدرج عنصرا داخل .box3 في الشّبكة. سنعيَن القيمةrelative للخاصيّةposition الخاصة بـ .box3 ثم سنحدّد موضع العنصر الفرعي بواسطة خصائص الإزاحة offset properties. في هذه الحالة، سياق التّموضع سيكون هو حيّز الشّبكة. <div class="wrapper"> <div class="box1">One</div> <div class="box2">Two</div> <div class="box3">Three <div class="abspos"> This block is absolutely positioned. In this example the grid area is the containing block and so the absolute positioning offset values are calculated in from the outer edges of the grid area. </div> </div> <div class="box4">Four</div> </div> .wrapper { display: grid; grid-template-columns: repeat(4،1fr); grid-auto-rows: 200px; grid-gap: 20px; } .box3 { grid-column-start: 2; grid-column-end: 4; grid-row-start: 1; grid-row-end: 3; position: relative; } .abspos { position: absolute; top: 40px; left: 40px; background-color: rgba(255،255،255،.5); border: 1px solid rgba(0،0،0،0.5); color: #000; padding: 10px; } الشّبكة و display: contents سنقوم بإطلالة أخيرة على إحدى المواصفات القياسيّة للتّخطيط والتي تستحقّ التنويه وهي التفاعل بين خطاطة CSS الشّبكيّة وdisplay: contents. قيمةcontents للخاصيةdisplay هي قيمة جديدة موضّحة في المواصفات القياسيّة (Display specification ) كما يلي: "إنّ العنصر نفسه لا يولّد أية مربّعات، ولكنّ بإمكان أبنائه وأشباه العناصر pseudo-elements توليد المربّعات كالمعتاد. ولغرض توليد المربعات والتخطيط، يجب أن يعامل العنصر كما لو أنه قد تم استبداله بأبنائه وبأشباه العناصر في شجرة وثيقة" إذا حدّدت لعنصر ما display: contents فإنّ المربع الذي سينشئه سيختفي، أمّا مربّعات الأبناء فستظهر كما لو أنها قد ارتفعت لمستوى أعلى. وهذا يعني أن أبناء عناصر الشّبكة يمكن أن يصبحوا بدورهم عناصر للشّبكة. إن بدا هذا غريبا فإليك مثالًا بسيطًا. في الوسوم التالية، لدينا شبكة بحيث سيشمل أوّل عناصرها جميع مدارج الأعمدة الثلاث. كما تحتوي ثلاثة عناصر متداخلة. وبما أنّ هؤلاء العناصر ليسوا أبناءً مباشرين، فإنهم لن يصبحوا جزءا من الخطاطة الشّبكيّة لذك فالخاصّيةdisplay ستكون خطاطة عادية regular block layout. <div class="wrapper"> <div class="box box1"> <div class="nested">a</div> <div class="nested">b</div> <div class="nested">c</div> </div> <div class="box box2">Two</div> <div class="box box3">Three</div> <div class="box box4">Four</div> <div class="box box5">Five</div> </div> .wrapper { display: grid; grid-template-columns: repeat(3، 1fr); grid-auto-rows: minmax(100px، auto); } .box1 { grid-column-start: 1; grid-column-end: 4; } إذا أضفنا display: contents إلى قواعدbox1، فإنّ المربع الخاصّ بذلك العنصر سيختفي والعناصر الفرعية ستصبح الآن عناصر للشّبكة وستنساب وفق قواعدauto-placement . <div class="wrapper"> <div class="box box1"> <div class="nested">a</div> <div class="nested">b</div> <div class="nested">c</div> </div> <div class="box box2">Two</div> <div class="box box3">Three</div> <div class="box box4">Four</div> <div class="box box5">Five</div> </div> .wrapper { display: grid; grid-template-columns: repeat(3، 1fr); grid-auto-rows: minmax(100px، auto); } .box1 { grid-column-start: 1; grid-column-end: 4; display: contents; } يمكن أن تكون هذه طريقة لجعل العناصر الفرعيّة في الشّبكة تتصرّف كأنها جزء من الشّبكة، وهذا قد يساعدنا على الالتفاف على بعض المشاكل والتي سيتم حلها بواسطة subgrids بمُجرّد تقديمها. يمكنك أيضا استخدام display: contents بطريقة مماثلة مع flexbox لتمكين العناصر الفرعيّة من أن تصبح عناصر مرنة. كما رأينا خلال هذا الدليل، خطاطة CSS الشّبكيّة هي مجرّد أداة واحدة من الأدوات المتاحة لك. لا تتردّد في دمجها مع الطرق الأخرى للتّخطيط لتحصل على التأثيرات التي تصبو إليها. ترجمة -وبتصرّف- للمقال Relationship of grid layout to other layout methods لأصحابه المساهمين (kan199041, VladPavel15, mfluehr, NouranMahmoud, bgates, Pickles-Spill, jarrodn, AleshaOleg, teoli, rachelandrew) حقوق الصورة البارزة محفوظة لـ Freepik
  4. يصف هذا الدرس كيفية استخدام إضافات أندرويد فيKotlin لتحسين دعم تطوير أندرويد. في هذا الدرس سوف نستعرض الخطوات اللازمة لاستخدام ملحقات أندرويد الإضافية في لغة البرمجة Kotlin، لتعزيز تجربة التطوير في أندرويد. ربط العرض View Binding الخلفية كل مطوري أندرويد يعرفون جيدًا الدالة ()findViewById. والتي هي من دون أدنى شك، مصدر لكثير من المتاعب والأخطاء المحتملة والشيفرات السيئة والتي يصعب قراءتها وصيانتها. صحيح أن هناك العديد من المكتبات المتاحة لتوفير حلول لهذه المشكلة، إلّا أن هذه المكتبات تتطلب حقول تأشير annotating fields لكل عنصر معروض من نوع View. توفر لنا ملحقات أندرويد الإضافية لـ Kotlin تجربة مماثلة لما توفره بعض تلك المكتبات، دون أن نكون في حاجة إلى كتابة شيفرات إضافية. في الأساس، هذا يسمح لنا بكتابة الشيفرة التالية: // Using R.layout.activity_main from the 'main' source set import kotlinx.android.synthetic.main.activity_main.* class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instead of findViewById<TextView>(R.id.textView) textView.setText("Hello, world!") } } textView هي خاصية إضافية لـ Activity، ولها نفس النوع المعلن في activity_main.xml (أي TextView). استخدام إضافات أندرويد لـKotlin إعداد الارتباطات Configuring the Dependency سنستخدم في هذا الدرس Gradle، لكن يمكنك تحقيق نفس النتائج باستخدام IntelliJ IDEA project structure أو Maven. إضافات أندرويد هي جزء من ملحقة Kotlin الخاصة بكل من IntelliJ IDEA وAndroid Studio. لذلك لا تحتاج إلى تثبيت ملحقات إضافية. كل ما تحتاجه هو إتاحة ملحقة Gradle لأندرويد في ملف الوحدة build.gradle: apply plugin: 'kotlin-android-extensions' استيراد الخصائص التركيبية synthetic properties من الملائم استيراد جميع خصائص الودجةwidget) ) لخطاطة (layout) معينة دفعة واحدة: import kotlinx.android.synthetic.main.<layout>.* وهكذا إذا كان اسم ملف الخطاطة هو activity_main.xml، فسنقوم باستيراد kotlinx.android.synthetic.main.activity_main.* إن كنّا نريد أن نستدعي الخصائص التركيبية على View، فيجب علينا أيضًا استيراد kotlinx.android.synthetic.main.activity_main.view.* وبمجرد أن نفعل ذلك، يمكننا حينها استدعاء الإضافات المقابلة والتي هي اسماء خصائص سُمّيت على إثر عناصر العرضviews الموجودة في ملف XML. فعلى سبيل المثال، بالنسبة لهذا العرض: <TextView android:id="@+id/hello" android:layout_width="fill_parent" android:layout_height="wrap_content"/> ستكون هناك خاصية اسمها hello: activity.hello.text = "Hello World!" الوضع التجريبي Experimental Mode تشمل الملحقات الإضافية لأندرويد العديد من الميزات التجريبية مثل دعم LayoutContainer ومولّدات تقديم الصنف Parcelable (Parcelable implementation generator). هذه الميزات لا تُعتبر جاهزة بعدُ للإنتاج، لذلك نحتاج إلى التحوّل للوضع التجريبي في build.gradle من أجل استخدامها: androidExtensions { experimental = true } دعم LayoutContainer تدعم الملحقات الإضافية لأندرويد أنواع مختلفة من الحاويات containers. وأبسط تلك الحاويات Activity، Fragment وView. ولكن يمكنك أن تحوّل (افتراضيًا) أي صنف إلى حاوية لإضافات أندرويد من خلال تطبيق الواجهةLayoutContainer ، على سبيل المثال: import kotlinx.android.extensions.LayoutContainer class ViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer { fun setup(title: String) { itemTitle.text = "Hello World!" } } لاحظ أنك تحتاج إلى التحوّل إلى الوضع التجريبي لاستخدام LayoutContainer. دعم النكهات Flavor Support تدعم ملحقات أندرويد الإضافية نكهات أندرويد ((Android flavors. لنفترض أن لديك نكهة اسمها free في ملف build.gradle خاصّتك: android { productFlavors { free { versionName "1.0-free" } } } يمكنك استيراد كافة الخصائص التركيبية للخطاطة free/res/layout/activity_free.xml بإضافة هذا الاستيراد: import kotlinx.android.synthetic.free.activity_free.* في الوضع التجريبي، يمكنك تحديد أي اسم آخر (وليس فقط flavor)، على سبيل المثال freeDebug أو freeRelease يصلحان كذلك. التخزين المؤقت لعناصرView استدعاء ()findViewById يمكن أن يكون بطيئًا، خصوصًا في حالة تشعبات العرض (view hierarchies) الكبيرة، لذلك تحاول إضافات أندرويد التقليل من عدد مرّات استدعاء ()findViewById بواسطة التخزين المؤقت للعروض في الحاويات. افتراضيًا، اضافات أندرويد تضيف دالة تخزين مؤقت مخفية وحقل تخزين إلى كل حاوية (Activity، Fragment، View أو LayoutContainer implementation) مكتوبة بـ Kotlin. التابع method)) صغير جدًا لذلك لا يزيد حجم APK كثيرًا. في المثال التالي، يتم استدعاء ()findViewById مرة واحدة فقط: class MyActivity : Activity() fun MyActivity.a() { textView.text = "Hidden view" textView.visibility = View.INVISIBLE } لكن في الحالة التالية: fun Activity.b() { textView.text = "Hidden view" textView.visibility = View.INVISIBLE } لا يمكننا أن نعرف ما إذا كان سيتم استدعاء هذه الدالة في أنشطة مصادرنا فقط أم أيضا في كل أنشطة جافا. لهذا السبب، لن نستخدم التخزين المؤقت هنا، حتى لو تم تمرير أحد عيّنات instance الصنف MyActivity من المثال السابق كمستقبِل. تغيير استراتيجية التخزين المؤقت للصنف View يمكنك تغيير استراتيجية التخزين المؤقت بشكل شامل أو بالنسبة لكل حاوية على حدة. وهذا أيضًا يتطلب التحول إلى الوضع التجريبي. يتم تحديد استراتيجية التخزين المؤقت الشاملة للمشروع في ملف build.gradle: androidExtensions { defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE } افتراضيًا، الملحقات الإضافية لأندرويد تستخدم HashMap كمرجع احتياطي للتخزين، ولكن يمكنك التبديل لتطبيق SparseArray، أو إيقاف التخزين المؤقت وحسب. هذا الأخير مفيد بشكل خاص إن أردت الاكتفاء باستخدام الجزء المقسّم Parcelable من إضافات Android. يمكنك أيضًا التأشير على حاوية ما بـ ContainerOptions@ لتغيير استراتيجية التخزين المؤقت: import kotlinx.android.extensions.ContainerOptions @ContainerOptions(cache = CacheImplementation.NO_CACHE) class MyActivity : Activity() fun MyActivity.a() { // findViewById() will be called twice textView.text = "Hidden view" textView.visibility = View.INVISIBLE } Parcelable بدءًا من الإصدار Kotlin 1.1.4، وفّرت الملحقات الإضافية لأندرويد مولّدات تطبيق للصنف Parcelable كميزة تجريبية. إتاحة دعم Parcelable قم بتطبيق ملحقة Gradle المسمّاة kotlin-android-extensions كما هو موضح [أعلاه] (#إعداد الارتباطات) وقم بتشغيل الوضع التجريبي. كيفية الاستخدام قم بالتأشير على الصنف بـ Parcelize@، وسيتم إنشاء تطبيق Parcelable تلقائيًا. import kotlinx.android.parcel.Parcelize @Parcelize class User(val firstName: String, val lastName: String, val age: Int): Parcelable يتطلّب Parcelize@ التصريح بجميع الخصائص المتسلسلة في المنشئ constructor الأولي. ستقوم إضافات أندرويد بإطلاق تحذير على كل الخصائص ذات الحقول المصرّح بها في جسم الصّنف، كما أنّه لا يمكن تطبيق Parcelize@ إذا لم تكن كل معاملات المنشئ الأوّلية خصائصًا. إن كان صنفك يتطلب تسلسلاً منطقيًا أكثر تقدمًا، فيمكنك كتابته داخل صنف مرافق: @Parcelize data class Value(val firstName: String, val lastName: String, val age: Int) : Parcelable { private companion object : Parceler<User> { override fun User.write(parcel: Parcel, flags: Int) { // Custom write implementation } override fun create(parcel: Parcel): User { // Custom read implementation } } } الأنواع المدعومة يدعم Parcelize@ طيفًا واسعًا من الأنواع: الأنواع الأولية Primitive types (ونسخها المغلّفة boxed versions). Objects وenums . String، CharSequence. Exception. Size، SizeF، Bundle، IBinder، IInterface، FileDescriptor SparseArray، SparseIntArray، SparseLongArray، SparseBooleanArray. كل الأنواع المتسلسلة Serializable (حتى Date مدعوم) وتطبيقات Parcelable. تجميعات كل الأنواع المدعومة: List (مُحالة على ArrayList)، و Set (مُحالة على LinkedHashSet)، و Map (مُحالة على LinkedHashMap). بالإضافة إلى عدد من التطبيقات الملموسة: ArrayList، LinkedList، SortedSet، NavigableSet، HashSet، LinkedHashSet، TreeSet، SortedMap، NavigableMap، HashMap، LinkedHashMap، TreeMap، ConcurrentHashMap. الجداول التي تحتوي الأنواع المدعومة. النسخ الفارغة Nullable versions من كل الأنواع المدعومة. تخصيص الـ Parcelers حتى إن لم يكن النوع مدعوما مباشرة، يمكنك كتابة كائن Parceler لأجل دعمه. class ExternalClass(val value: Int) object ExternalClassParceler : Parceler<ExternalClass> { override fun create(parcel: Parcel) = ExternalClass(parcel.readInt()) override fun ExternalClass.write(parcel: Parcel, flags: Int) { parcel.writeInt(value) } } أمّا عناصر Parcelers الخارجية يمكن تطبيقها باستخدام التأشيرات TypeParceler@ أو WriteWith@: // Class-local parceler @Parcelable @TypeParceler<ExternalClass, ExternalClassParceler>() class MyClass(val external: ExternalClass) // Property-local parceler @Parcelable class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass) // Type-local parceler @Parcelable class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass) ترجمة -وبتصرّف- للمقال Kotlin Android Extensions لصاحبه Yan Zhulanow
  5. مقدمة Node.js هو بيئة عمل مفتوحة المصدر لجافا سكريبت تعمل وقت التشغيل لبناء تطبيقات الخادم والشبكات بسهولة. تعمل المنصة على لينكس, OS X, FreeBSD وويندوز. يمكن تشغيل تطبيقات Node.js من سطر الأوامر، ولكن سنركز على تشغيلها كخدمة، بحيث سيتم إعادة تشغيلها تلقائيًا في حال حدوث فشل أو في حال إعادة تشغيل الحاسوب، كما يمكن استخدامها بأمان في بيئة الإنتاج. في هذا الدرس، سوف نغطّي موضوع إنشاء بيئة عمل Node.js جاهزة للإنتاج على خادم Debian 8. هذا الخادم سيشغّل تطبيقًا لـ Node.js سيكون مُدارًا بواسطة PM2، ويوفر للمستخدمين وصولًا آمنًا إلى التطبيق عبر Nginx reverse proxy. المتطلبات الأساسية يفترض هذا الدليل أن لديك خادم Debian 8، بصلاحيات مستخدم غير جذري non-root user ومع امتيازات sudo. ونفترض كذلك أن لديك اسم نطاق يشير إلى عنوان IP العام للخادم. دعونا نبدأ بتثبيت بيئة العمل Node.js على خادمك. تثبيت Node.js سوف نقوم بتثبيت أحدث إصدارات Node.js المدعومة على المدى الطويل، وذلك باستخدام أرشيف الحزمة NodeSource. أولًا، تحتاج إلى تثبيت NodeSource PPA لأجل الوصول إلى محتوياته. تأكد من أنك في المجلد الأساسي home directory، وقم باستخدامcurl للحصول على برنامج التثبيت النصي من مستودعات 6.X Node.js: cd ~ curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh يمكنك فحص محتويات هذا النص البرمجي بواسطةnano (أو محرر النصوص المفضل لديك): nano nodesource_setup.sh وقم بتشغيل النص البرمجي عقب sudo: sudo bash nodesource_setup.sh سيتم إضافة PPA إلى إعداداتك وسوف يتم تحديث حزمتك المحلية المُخزنة تلقائيًا. بعد تشغيل برنامج التنصيب من nodesource، يمكنك تثبيت حزمة Node.js بنفس الطريقة التي اتبعتها أعلاه: sudo apt-get install nodejs الحزمة nodejs تحتوي رُقامة nodejs (nodejs binary ) إضافة إلى npm، لذلك لا تحتاج إلى تثبيت npm بشكل منفصل. ولكن لكي تعمل بعض حُزم npm (مثل تلك التي تتطلب ترجمة التعليمات البرمجية من المصدر)، فستحتاج إلى تثبيت الحزمة build-essential: sudo apt-get install build-essential لقد تم تثبيت بيئة العمل Node.js، وصارت جاهزة لتشغيل تطبيقاتنا! لذلك دعونا الآن نكتب تطبيقًا بـ Node.js إنشاء تطبيق Node.js سنكتب تطبيق Hello World والذي سيُرجع ببساطة الجملة “Hello World” لكل طلبات HTTP. هذا تطبيق تعليمي بسيط من شأنه أن يساعدك على التعامل مع Node.js، يمكنك بعد ذلك استبداله بتطبيقاتك الخاصة. فقط تذكر أن تعدّل تطبيقك لكي يُصغي إلى منافذ وعناوين IP المناسبة. الشيفرة البرمجية لـ Hello World أولًا، قم بإنشاء وفتح تطبيق Node.js لأجل التحرير. في هذا الدرس، سوف نستخدم nano لتحرير تطبيق تعليمي يُسمّى hello.js: cd ~ nano hello.js أدرج التعليمات البرمجية التالي في الملف. يمكنك استبدال المنفذ 8080، في كلا الموقعين (تأكد من استخدام منفذ غير أساسي non-admin، أي 1024 أو أكبر): hello.js #!/usr/bin/env nodejs var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8080, 'localhost'); console.log('Server running at http://localhost:8080/'); الآن احفظ واخرج. هذا التطبيق سيُصغي إلى العنوان المحدد(localhost) والمنفذ (8080)، ويُرجع “Hello World” مع كود النجاح (HTTP success) يساوي200 . وبما أننا نُصغي على localhost، فإن العملاء البعيدين remote clients لن يكونوا قادرين على ربط الاتصال بتطبيقنا. اختبار التطبيق لتتمكن من اختبار التطبيق، اجعل hello.js ملفًّا تنفيذيًا: chmod +x ./hello.js وقم بتشغيله هكذا: ./hello.js Output Server running at http://localhost:8080/ لاختبار التطبيق، قم بفتح جلسة على المطراف terminal على الخادم الخاص بك، ثم اربط االاتصال بـlocalhost بواسطة curl: curl http://localhost:8080 إن رأيت المخرجات التالية، فهذا يعني أن التطبيق يعمل بشكل صحيح ويُصغي للعنوان والمنفذ الصحيحين: Output Hello World إن لم ترى المخرجات المناسبة، تأكد من أن تطبيق Node.js قيد التشغيل، وأنه يٌصغي للعنوان والمنفذ الصحيح. بمجرد أن تتيقن بأنه يعمل، أوقف التطبيق (إذا لم تكن قد فعلت من قبل) عن طريق الضغط على Ctrl + C. تثبيت PM2 سنقوم الآن بتثبيتPM2 ، والذي هو مدير العمليات process manager لتطبيقات Node.js. يوفر PM2 وسيلة سهلة لإدارة وإخفاء daemonize التطبيقات (أي تشغيلها كخدمة في الخلفية.( سوف نستخدم npm، وهي حزمة لإدارة وحدات Nodeالتي تُثبّت مع Node.js، لتثبيت PM2 على الخادم استخدم هذا الأمر: sudo npm install -g pm2 الخيار -g يقول لـnpm أنّ عليه تثبيت الوحدة بشكل كلي globally، بحيث تكون متاحة على نطاق النظام كله. إدارة التطبيق عبر PM2 PM2 بسيط وسهل الاستخدام. سوف نغطي فيما يلي بعض استخداماته الأساسية. بدء التطبيق أول شيء سنفعله هو استخدام التعليمة pm2 start لبدء تشغيل التطبيق hello.js في الخلفية: pm2 start hello.js هذا يضيف أيضًا تطبيقك إلى لائحة عمليات PM2، والتي تُحدَّث في كل مرة تبدأ تشغيل التطبيق: Output [PM2] Spawning PM2 daemon [PM2] PM2 Successfully daemonized [PM2] Starting hello.js in fork_mode (1 instance) [PM2] Done. ┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ hello │ 0 │ fork │ 3524 │ online │ 0 │ 0s │ 21.566 MB │ disabled │ └──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────────────┴──────────┘ Use `pm2 show <id|name>` to get more details about an app كما ترى، يُعيّن PM2 تلقائيا اسم التطبيق (بناءً على اسم الملف دون الامتداد .js). ورقم تعريفPM2 . كما يحفظ PM2 معلومات أخرى، من قبيل معرّف العمليةPID ، وحالته الراهنة، واستخدام الذاكرة. سيتم إعادة تشغيل التطبيقات التي تعمل تحت PM2 تلقائيا إذا تعطل التطبيق أو أُوقف، لكي نجعل التطبيق يشتغل تلقائيًا عند بدء أو إعادة التشغيل ينبغي اتخاذ خطوة إضافية. ولحسن الحظ، يوفّرPM2 وسيلةً سهلةً للقيام بذلك، وهي التعليمة الفرعية startup. تقوم التعليمة الفرعية startup بإنشاء وإعداد برنامج نصي لإطلاق PM2 والعمليات المُدارة من قبله عند بدء تشغيل الخادم. يجب عليك أيضًا تحديد المنصة الذي تعمل عليها، والتي هي Ubuntu، في حالتنا: pm2 startup system السطر الأخير من المخرجات سوف يتضمّن تعليمة عليك تشغيلها بامتيازات المستخدم الجذري superuser. Output [PM2] You have to run this command as root. Execute the following command: sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/Sammy قم بتشغيل التعليمة التي تم إنشاؤها (مماثلة للمخرجات الملوّنة أعلاه، ولكن استخدم اسم المستخدم الخاص بك بدلًا من sammy) لجعل PM2 يبدأ مع بداية التشغيل (استخدم التعليمة من المخرجات التي لديك): sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy هذا سوف يُنشئ systemd unit والتي ستُشغّل PM2 للمستخدم خاصتك عند بدء التشغيل. عينة pm2 هذه ستشغّل بدورهاhello.js . يمكنك التحقق من حالة الوحدة systemd بواسطة systemctl: systemctl status pm2 لمزيد من التفاصيل عن systemd، طالع مقال أساسيات Systemd: العمل مع الخدمات، الوحدات Units، واليوميات Journal استخدامات أخرى لـ PM2 (اختياري) يوفرPM2 العديد من التعليمات الفرعية التي تسمح لك بإدارة أو البحث عن معلومات حول تطبيقاتك. لاحظ أن تشغيل PM2 دون أي معاملات arguments سيؤدي إلى عرض صفحة مساعدة تتضمن أمثلة على الاستخدام والتي تغطي استخدامات PM2 بتفاصيل أكثر مما هو موجود في هذا الدرس. يمكنك إيقاف التطبيق بهذه التعليمة (حدّد App name أو id الخاص بـPM2 ): pm2 stop app_name_or_id يمكنك إعادة تشغيل التطبيق بهذه التعليمة (حدّد App name أو id الخاص بـPM2 ): pm2 restart app_name_or_id ويمكن أيضًا مطالعة قائمة من التطبيقات المُدارة حاليًا من قبل PM2 بالتعليمة الفرعيةlist . pm2 list يمكنك الحصول على مزيد من المعلومات حول تطبيق معين باستخدام التعليمة الفرعية info (حدّد App name أو id الخاص بـPM2 ): pm2 info example يمكن الوصول إلى مراقب عمليات PM2 بالتعليمة الفرعية monit. سيتم عرض حالة التطبيق ووحدة المعالجة المركزية CPU واستخدام الذاكرة: pm2 monit الآن وبعد أن قمنا بتشغيل Node.js وإدارته بواسطة PM2، دعونا نبدأ إعداد الوكيل العكسيreverse proxy . إعداد Nginx ليكون الوكيل العكسي للخادم Reverse Proxy Server الآن وبعد أن بدأ تطبيقك يشتغل ويُصغي إلى المضيف المحلي localhost، تحتاج إلى إعداد وسيلة ليتمكن المستخدمون من الوصول إليه. سوف نقوم بإنشاء خادم Nginx كوكيل عكسي لهذا الغرض. في هذا الدرس ستتعلم كيفية إعداد خادم Nginx من الصفر. إن سبق وقمت بإعداد خادم Nginx، فيمكنك الاكتفاء بنسخ location في server block من اختيارك (تأكد من أن المحل location لا يتداخل مع المحتوى الموجود على الخادم). أولًا، قم بتثبيت Nginx باستخدام apt-get: sudo apt-get install nginx الآن افتح ملف الإعدادات الافتراضي لـ server block لأجل تحريره: sudo nano /etc/nginx/sites-available/default احذف كل ما هو موجود في الملف وقم بإدراج الإعدادات التالية. تأكد من استبدال اسم النطاق خاصتك في الموجّه server_name. بالإضافة إلى ذلك، قم بتغيير المنفذ (8080) إن كان تطبيقك مُعدًّا للإصغاء إلى منفذ آخر: /etc/nginx/sites-available/default server { listen 80; server_name example.com; location / { proxy_pass http://localhost:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } } هذا سيقوم بإعداد الخادم بحيث يرد على الطلبات requests على مستوى الجذر root. إن افترضنا مثلًا أن خادمنا مُتوفر فيexample.com ، فإن الدخول إلى http://example.com/ عبر متصفح الإنترنت سيبعث الطلب إلى hello.js الذي يُصغي إلى المنفذ 8080 في المضيف المحلي. يمكنك إضافة كتل محل location blocks إضافية لنفس كتلة الخادم server block لإتاحة إمكانية الوصول إلى التطبيقات الأخرى على نفس الخادم. على سبيل المثال، إن كنت تُشغّل تطبيقًا آخرًا لـ Node.js على المنفذ 8081، يمكنك إضافة كتلة المحل location block التالية للسماح بالوصول إليها عبر http://example.com/app2 : Nginx Configuration — Additional Locations location /app2 { proxy_pass http://localhost:8081; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } بمجرد الانتهاء من إضافة كتل المحل لتطبيقاتك، قم بالحفظ واخرج. تأكد من أنك لم ترتكب أي أخطاء نحوية syntax errors أثناء الكتابة: sudo nginx –t بعد ذلك، قم بإعادة تشغيل Nginx: sudo systemctl restart nginx بعد ذلك، قم بترخيص المرور لـ Nginx عبر جدار حماية، إن كان متاحًا. إذا كنت تستخدم ufw ، يمكنك استخدام التعليمة التالية: sudo ufw allow 'Nginx Full' يمكنك بواسطةufw التحقق من الحالة باستخدام التعليمة التالية: sudo ufw status إذا كنت تستخدم IPTables بدلًا من ذلك، يمكنك ترخيص المرور لـ Nginx باستخدام التعليمة التالية: sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT يمكنك دائمًا التحقق من حالة IPTables باستخدام التعليمة التالية: sudo iptables –S على افتراض أن تطبيق Node.js الخاص بك قيد التشغيل، وأن إعدادات Nginx وتطبيقاتك صحيحة، فسيكون بإمكانك الآن الوصول إلى تطبيقك عبر وكيل عكسي لـ Nginx. جرّب ذلك بنفسك عن طريق الدخول إلى عنوان الخادم الخاص بك (الـ IP أو اسم النطاق) الخلاصة تهانينا! لقد نجحت في جعل تطبيق Node.js الخاص بك يعمل في خلفية وكيل عكسي لـ Nginx على خادم Debian 8. إعدادات الوكيل العكسي هاته مرنة بما فيه الكفاية لتمكين المستخدمين من الوصول إلى التطبيقات الأخرى أو محتويات صفحات الأنترنت الثابتة التي تريد مشاركتها. حظًا سعيدًا في عملك على Node.js. ترجمة -وبتصرّف- للمقال How To Set Up a Node.js Application for Production on Debian 8 لصاحبته Lisa Tagliaferri
  6. جميعنا – نحن مصمّمي المواقع –تعلّمنا أن نبقى بعيدًا عن الخُطاطات layouts المستندة على الجداول. لهذا غالبًا ما ننساها ونهملها إلى أن نجد أنفسنا محتاجين إليها. هذا الدرس سيأخذك خطوة بخطوة لإنشاء جدول بيانات أنيق وسلس يحتوي مقارنة بين مميّزات عدة درّاجات نارية من طرازHarley Davidson . سنقوم ببناء الجدول حصرًا بـ HTML ثمّ سنصقله بـ CSS لإنشاء جدول HTML جميل وواضح. الجدول سيقارن ميزات ثلاثة نماذج من الدراجات النارية من طراز Harley Davidson Sportster. سيتمّ بناء الجدول بـ HTML ثمّ سنُنسّقه بـ CSS لجعل البيانات واضحة. مشاهدة الجدول النّهائي سنحتاج إلى عدّة ملفات لأجل تصميم هذا المشروع التّعليمي. وهي: صورة PNG سنستخدمها كخلفية، وصورة كبيرة بالأبيض والأسود لملء الخلفيّة، وشعار Harley Davidson إضافة إلى صور نماذج الدرّاجات النارية الثلاث التي سنقارنها. الملفّ عبارة عن HTML عاديّ. حيث يبدأ بـ DOCTYPE، عنوان الصفحة ورابط لـ CSS. يبدأ محتوى الصفحة بـ <H1>، والذي سيتم لاحقا تحويله إلى شعار HD، بعد ذلك سنضيف وعاء div لمساعدتنا على توسيط المحتوى. بعد ذلك سندرج <table> متبوعًا بـ <THEAD> لتحديد العناوين والتّرويسات headings في جدولنا. يحتوي <THEAD> على صفّين، أحدهما يتضمّن صور الدرّاجات والآخر يتضمّن عناوين كل نموذج في وسم <H2>. الجدول يحتوي ثلاثة أعمدة، ولكن الخلايا الأولى في الترويسة فارغة لذلك سنضيف الحرف الرّابط &nbsp;. لمساعدتنا على تنسيق الجدول وتحسين مقروئية البيانات وفي نفس الوقت سنضيف في الكود أصنافًا classes إلى الخلايا. بعد إغلاق <THEAD> يبدأ <TBODY>. وبينما تُستخدم <th> داخل <THEAD>، فإنّ <td> تستخدم داخل <TBODY>. كل سلسة من الخلايا مُتضمَّنة داخل صفّ واحد، وصفوفنا تساعدنا في تحديد الأعمدة. كما ستتم إضافة كافّة صفوف البيانات لإنهاء HTML. HTML النّهائي <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Harley Davidson Sportster Motorcycle Model Comparison</title> <link href="style.css" rel="stylesheet" /> </head> <body> <h1>Harley Davidson Motorcycles</h1> <div id="container"> <table> <thead> <tr> <th> </th> <th class="iron"><img src="images/iron.jpg" alt="Harley Davidson Iron 883" /></th> <th class="nightster"><img src="images/nightster.jpg" alt="Harley Davidson Nightster" /></th> <th class="fortyeight"><img src="images/forty-eight.jpg" alt="Harley Davidson Forty-Eight" /></th> </tr> <tr> <th> </th> <th class="iron"><h2>Iron 883</h2></th> <th class="nightster"><h2>Nightster</h2></th> <th class="fortyeight"><h2>Forty-Eight</h2></th> </tr> </thead> <tbody> <tr> <td class="feature">Engine</td> <td class="iron">883cc</td> <td class="nightster">1202cc</td> <td class="fortyeight">1202cc</td> </tr> <tr> <td class="feature">Torque</td> <td class="iron">70Nm</td> <td class="nightster">98Nm</td> <td class="fortyeight">98Nm</td> </tr> <tr> <td class="feature">Exhaust</td> <td class="iron">Chrome, staggered shorty exhaust with dual mufflers</td> <td class="nightster">Chrome, slash-cut ex-haust with dual mufflers</td> <td class="fortyeight">Chrome, staggered shorty exhaust with dual slash-cut mufflers</td> </tr> <tr> <td class="feature">Wheels</td> <td class="iron">Black, 13-Spoke Cast Alumi-num</td> <td class="nightster">Black, Laced Steel</td> <td class="fortyeight">Black, Laced Steel</td> </tr> <tr> <td class="feature">Ground Clearance</td> <td class="iron">120mm</td> <td class="nightster">130mm</td> <td class="fortyeight">100mm</td> </tr> <tr> <td class="feature">Price</td> <td class="iron">£6,699</td> <td class="nightster">£8,099</td> <td class="fortyeight">£8,849</td> </tr> </tbody> </table> </div> </body> </html> يبدأ CSS بإزالة وتعويض تنسيق المتصفّح الافتراضي، ثم يقوم بتعيين التنسيق الكلّي للصّفحة. بعد ذلك نضيف صورة الخلفية إلى جسم الصّفحة. أمّا خصائص الخط العّام فستكون16px Georgia رمادي. بعد ذلك سيتمّ تحويل <H1> إلى شعار Harley Davidson باستخدام تقنية استبدال الصورة، ثم سنضع الوعاء div في وسط الصفحة. تُستخدم صورة الخلفية النّمطية للملء. وبعدها سنحدّد قيم الخاصّية box-shadow لـ CSS3 لمحاكاة تأثير الظّل المُنسدل في فوتوشوب. افتراضيًا سيعرض الجدول فجوات صغيرة بين خلايا الجدول. التصميم الذي نبتغي يتطلّب هامشًا بين الأعمدة ولكن دون ثغرات بين الصّفوف. تسمح لنا الخاصيّة border-spacing بضبط التباعد على المحورين Y و X. ستتمّ إضافة الهامش الداخلي Padding, كما سيتمّ توسيط النص في جميع العناصر <th> و <td>، ثم سنستثني الخلايا ذات الصّنف “feature”، حيث سنحاذي النصّوص فيها إلى اليسار. كما ستُمنح هذه الخلايا عرضًاwidth محدّدًا لتغيير تناسب الجدول لجعل ذلك العمود الأكبر من بين بقيّة الأعمدة. أعطينا لكلّ الأصناف “iron”، “nightster” و “fortyeight” خلفيّة بيضاء شفّافة باستخدام RGBa. كان بإمكاننا أن نستخدم صنفًا واحدًا لجميع هذه الخلايا، ولكنّ الأصناف المعيّنة ستساعدنا على التنقل في بيانات الجدول في الكود البرمجي. لأجل إضافة لمسة أخيرة على الجدول، سنضيف نفس الملء الشفّاف إلى صفوف الجدول، ولكن فقط عندما يطوف عليها مؤشّر الفأرة. هذا التأثير البسيط يعزّز سهولة استخدام الجدول، ممّا يساعد المستخدم على مطالعة ومقارنة البيانات. CSS الكامل body, div, h1, h2, h3, h4, h5, h6, p, ul, ol, li, dl, dt, dd, img, form, fieldset, input, textarea, blockquote, table, tr, td, th { margin: 0; padding: 0; border: 0; } body { background: #000 url(images/bg.jpg) center top fixed no-repeat; font: 16px Georgia, Serif; color: #ccc; } h1 { width: 168px; height: 130px; margin: 30px auto; position: relative; background: url(images/harley-davidson.png); text-indent: -9999px; } #container { width: 940px; margin: -115px auto; padding: 110px 10px 50px 10px; background: url(images/bg-pattern.png); box-shadow: 0px 0px 15px #000; } table { border-spacing: 10px 0px; } th, td { text-align: center; padding: 10px; } .feature { width: 183px; text-align: right; font-size: 24px; font-weight: normal; color: #fff; } .iron, .nightster, .fortyeight { background: rgba(255,255,255,0.05); } h2 { font-size: 24px; font-weight: normal; color: #fff; } tr:hover { background: rgba(255,255,255,0.05); } thead tr:hover { background: none; } جدول HTML / CSS النهائي تحقّق بنفسك من المثال لرؤية الشكل النهائي للجدول بكلّ التّأثيرات التي ترافقه. استخدام الشفافية من ملفّات PNG24 وصيغة التّلوينRGBa ساعد على إنشاء تصميم أنيق عندما دُمج مع صورة الخلفية الكبيرة. عمومًا تقنيات الجدول البسيطة هذه يمكن استخدامها في أيّ مشروع لعرض بياناتك الجدولية بطريقة واضحة وسهلة الفهم. ترجمة -وبتصرّف- للمقال How To Create a Slick Features Table in HTML & CSS لصاحبه iggy
  7. في هذا الدرس ستتعلّم كيفيّة إنشاء تطبيق Kotlin بسيط لأندرويد باستخدام Android Studio. تثبيت ملحقة Kotlin تمّ إدماج ملحقة Kotlin مع Android Studio بدءًا من النسخة 3.0. إن كنت تستخدم إصدارًا سابقًا، فستحتاج تثبيت ملحقة Kotlin. اذهب إلى File | Settings | Plugins | Install JetBrains plugin… بعد ذلك ابحث عنKotlin وقم بتثبيته. إن كنت تبحث في الشاشة " Welcome to Android Studio"، فقم باختيار Configure | Plugins | Install JetBrains plugin… عليك إعادة تشغيل بيئة التطوير بعد الانتهاء. إنشاء مشروع من السهل للغاية البدء في استخدام Kotlin لتطوير أندرويد. في هذا الدرس سنعمل على Android Studio. ولكن إن كنت تستخدم Intellij IDEA مع أندرويد، فالعمليّة هي نفسها تقريبًا. لنقم أوّلاً بإنشاء مشروع جديد. اختر Start a new Android Studio project أو File | New project. سيساعدك صندوق الحوار التالي في عملية إنشاء مشروع جديد. ستحتاج إلى اختيار اسم للمشروع وتحديد أيّ إصدارات Android SDK قمت بتثبيتها. معظم الخيارات يمكن إبقاؤها على قيمها الافتراضية، لذلك يمكنك الضغط على Enter مرارًا. تسميّة المشروع: يوفر Android Studio 3.0 خيارًا لتمكين دعم Kotlin على هذه الشاشة. يمكنك التحقق من هذا الخيار وتجاوز الخطوة " Configuring Kotlin in the project" أدناه. اختيار إصدار الأندرويد: اختر إنشاء النشاط الذي سيتم تجهيزه لك: قم بتسمية النّشاط: في Android Studio 3.0، يمكنك أن تختار إنشاء النشاط في Kotlin على الفور، حتى تتمكن من تجاوز الخطوة “Converting Java code to Kotlin”. الإصدارات السابقة تخلق النشاط في Java، ويمكنك بعدها استخدام أداة التحويل الآلي لتحويله. بشكل عام، أسهل طريقة للبدء في استخدام Kotlin هي بالتحويل التلقائي لنشاط جافا إلى نشاط Kotlin. يرجى ملاحظة أنه بدلاً من البحث في الوثائق عن وسيلة جديدة للتعبير عن نمط برمجي قديم، يمكنك كتابته بالجافا، ثم نسخ-لصق كود الجافا في ملف Kotlin، وسيقترح عليك IntelliJ IDEA (أو Android Studio) تحويله. تحويل كود الجافا إلى Kotlin افتح ملف MainActivity.java. ثم قم باستدعاء الإجراء Convert Java File to Kotlin File. يمكنك القيام بذلك بعدّة طرق أسهلها استدعاء Find Action ثمّ البدء في كتابة اسم الإجراء (كما هو مبيّن في المقتطف أدناه). أو يمكنك بدلا من ذلك استدعاء هذا الخيار Code | Convert Java File to Kotlin File من القائمة أو استخدام الاختصار المقابل (يمكنك العثور عليه في عنصر القائمة). بعد التحويل من المفروض أن تحصل على نشاط مكتوب بـ Kotlin. إعداد Kotlin في المشروع إن بدأت في تحرير هذا الملف، Android Studio سيُلمّح لك في الموجّه prompt بأنّ Kotlin لم يتم إعداده بعد لكي تقوم بإعداده. أو بدلاً من ذلك، يمكنك أن تبدأ الإعداد بأن تختار Tools | Kotlin | Configure Kotlin in Project من القائمة الرئيسيّة. ثم ستتم مطالبتك بتحديد إصدار Kotlin. قم باختيار أحدث الإصدارات المتاحة في قائمة الإصدارات المثبّتة. بعد إعداد Kotlin، ينبغي تحديث ملف التطبيق build.gradle. وحينها يمكنك أن ترى أنه تمّت إضافة ملحقة التطبيق: "kotlin-android" ومكتبتها المتعلّقة kotlin-stdlib. الخطوة الأخيرة هي مزامنة المشروع. يمكنك الضغط على “Sync Now” في الموجّه أو استدعاء الإجراء Sync Project with Gradle Files. بناء ونشر تطبيقات Kotlin لأندرويد أنت الآن جاهز لإنشاء التطبيق وتشغيله على محاكٍ أو جهاز. وهذا يجري تمامًا بنفس الطريقة في جافا. يمكنك إطلاق التطبيق والتوقيع عليه على غرار ما تفعله في تطبيقات أندرويد المكتوبة بلغة جافا. حجم ملف تشغيل Kotlin صغير: فحجم المكتبة في حدود 932 KB (الإصدار 1.2.10). وهذا يعني أنّ Kotlin لا يضيف إلا قدرًا قليلًا إلى حجم الملف apk. يُنتج مترجم Kotlin رُقامات byte-code، وبالتالي فليس هناك حقّا أيّ فرق من حيث الشكل والمظهر بين التطبيقات المكتوبة بـ Kotlin وتلك المكتوبة بلغة جافا. من توثيقيات Kotlin
  8. السلام عليكم, بعد بحث مطول عن أفضل بيئة عمل لتطوير تطبيقات الهاتف استقريت على اختيار ionic. لكن قبل أن أبدأ تعلمه يراودني هاجس يؤرق كل مبرمج عربي, وهو مشكلة دعم اللغة العربية. سؤالي هو: هل يمكن إنشاء تطبيقات على ionic باللغة العربية دون مشاكل,
  9. شكرا أخي أحمد ماذا عن الخيار الثاني html/css/javascript/jquiry هل يمكنني الاكتفاء بها لبرمجة التطبيقات أم ان فيها نفس مشكل بايثون
  10. السلام عليكم, أريد أن أدخل عالم برمجة التطبيقات, وقد احترت كيف أفعل ذلك, بحثت كثيرا على الأنترنت وكانت معظم الإيجابات أني سأحتاج الجافا (والتي أكرهها) لبرمجة تطبيقات الأندرويد و وسويفت لأجل منتجات آبل. أرجوا من الإخوان الذين سبقوني في هذا المجال أن يبينوا لي الطريق وأجرهم على الله. لدي سؤالان: 1 - بايثون هو لغتي المفضلة, مستواي فيه متوسط, لكن بإمكاني أن أواصل التعلم , فهل يمكنني أن أبرمج به تطبيقات تعمل على الهاتف وسطح المكتب بنفس الكود؟ وهل علي أن أستخدم أدوات مساعدة؟ 2 - لدي معرفة لا بأس بها ب html/css/javascript/jquiry , وقد قرأت في بعض المقالات أنه بالإمكان برمجة تطبيقات تعمل على كل المنصات فقط بهذه اللغات. فهل هذا صحيح؟ إن كان بالإمكان البرمجة بهما معا, فأي منهما أفضل في نظرك؟ أخذا بعين الاعتبار المعايير التالية: - العمل على كل المنصات (أندرويد, ios, سطح المكتب) - دعم اللغة العربية - السرعة في إنجاز المشروع أيضا سأكون ممتنا لمن يضع كتبا أو مراجع, تحياتي
  11. شكرا أخي الكريم, شرح رائع وسلس. لدي مشكلة مع مستندات جوجل, وهي أن التحويل أحيانا إلى صيغة DOCX . لا يتم بشكل صحيح, حيث تكون هنالك مشاكل في موضع الأشكال والصور. هل هناك حل لهذا. شكرا.