دليل Airbnb لتنسيق الشيفرة البرمجية للغة روبي Ruby


Mohamed Lahlah

إن هذا الدليل مستوحى من دليل GitHub ودليل Bozhidar Batsov.

ولدينا أيضًا دليل جافاسكربت لتنسيق الشيفرة البرمجية

جدول المحتويات

المسافات البيضاء

المسافة البادئة في الشيفرة

  • استخدم الزر (tabs) بمقدار مسافتين (وتسمى أيضًا Soft-Tab نظرًا لأن زر Tab الإفتراضي يكون ثمان مسافات).

  • سيكون عمق الكلمة المفتاحية when بقدر عمق case.

    case
    when song.name == 'Misty'
      puts 'Not again!'
    when song.duration > 120
      puts 'Too long!'
    when Time.now.hour > 21
      puts "It's too late"
    else
      song.play
    end
    
    kind = case year
           when 1850..1889 then 'Blues'
           when 1890..1909 then 'Ragtime'
           when 1910..1929 then 'New Orleans Jazz'
           when 1930..1939 then 'Swing'
           when 1940..1950 then 'Bebop'
           else 'Jazz'
           end

     

  • حاذِ وسطاء الّدالة فإما أن يكونوا على نفس السطر أو سطر لكلّ واحدٍ منهم.

    # bad
    def self.create_translation(phrase_id, phrase_key, target_locale,
                                value, user_id, do_xss_check, allow_verification)
      ...
    end
    
    # good
    def self.create_translation(phrase_id,
                                phrase_key,
                                target_locale,
                                value,
                                user_id,
                                do_xss_check,
                                allow_verification)
      ...
    end
    
    # good
    def self.create_translation(
      phrase_id,
      phrase_key,
      target_locale,
      value,
      user_id,
      do_xss_check,
      allow_verification
    )
      ...
    end

     

  • حاذِ الأسطر المتتابعة بمقدار مسافتين في جملة التنفيذ للتعابير المنطقية متعددة الأسطر.

    # bad
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
      is_in_program?(user) &&
      program_not_expired
    end
    
    # good
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
        is_in_program?(user) &&
        program_not_expired
    end

     

المسافات السطرية

  • لا تترك مسافة زائدة.

  • عند تضمين التعليقات في السطر، اترك مسافة واحدة بين نهاية الشيفرة البرمجية وبداية تعليقك.

    # bad
    result = func(a, b)# we might want to change b to c
    
    # good
    result = func(a, b) # we might want to change b to c

     

  • استخدم المسافات حول المعاملات (operators)، وبعد الفواصل، وبعد النقطتين، وبعد الفاصلة المنقوطة، وبعد فتح قوس ما { وقبل إغلاقه أيضًا }.

    sum = 1 + 2
    a, b = 1, 2
    1 > 2 ? true : false; puts 'Hi'
    [1, 2, 3].each { |e| puts e }

     

  • لا تضع أبدًا مسافة فارغة قبل الفاصلة.

    result = func(a, b)

     

  • لا تضع مسافة داخل كتلة وسطاء الأنبوب (pipe)، بل ضع واحدًا بين الوسطاء في الكتلة، وواحدًا آخر خارج كتلة وسطاء الأنبوب.

    # bad
    {}.each { | x,  y |puts x }
    
    # good
    {}.each { |x, y| puts x }

     

  • لا تضع مسافات بين إشارة التعجب ! ومعاملاتها.

    !something

     

  • لا تترك فارغات بعد الأقواس من نوع ( و[ أو قبلها ) و].

    some(arg).other
    [1, 2, 3].length

     

  • تجنب وضع المسافات عند إنشاء السلاسل النصية.

    # bad
    var = "This #{ foobar } is interpolated."
    
    # good
    var = "This #{foobar} is interpolated."

     

  • لا تستخدم مسافة إضافية في النطاق الحرفي (range literals).

    # bad
    (0 ... coll).each do |item|
    
    # good
    (0...coll).each do |item|

     

الأسطر الجديدة

  • أضف سطرًا جديدًا بعد الجملة الشرطية if ذات الشروط المتعددة لتصبح في عدة أسطر، ولتساعد في التفريق بين الشروط وجسم الشرط.

    if @reservation_alteration.checkin == @reservation.start_date &&
       @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights)
    
      redirect_to_alteration @reservation_alteration
    end

     

  • أضف سطرًا جديدًا بعد نهاية الجمل الشرطية، والكتل، والبيانات …إلخ.

    if robot.is_awesome?
      send_robot_present
    end
    
    robot.add_trait(:human_like_intelligence)

     

  • لا تضف أسطرًا جديدةً فارغةً بين المناطق ذات مسافات بادئة مختلفة (مثلًا حول أجزاء الصنف أو أجسام الوحدة).

    # bad
    class Foo
    
      def bar
        # body omitted
      end
    
    end
    
    # good
    class Foo
      def bar
        # body omitted
      end
    end

     

  • أضف سطرًا جديدًا فارغًا بين الدوالّ.

    def a
    end
    
    def b
    end

     

  • أضف سطرًا فارغًا لفصل البيانات المترابطة في الدوالّ لتشكيلّ فقرات منطقية داخلها.

    def transformorize_car
      car = manufacture(options)
      t = transformer(robot, disguise)
    
      car.after_market_mod!
      t.transform(car)
      car.assign_cool_name!
    
      fleet.add(car)
      car
    end

     

  • أضف سطرًا فارغًا في نهاية كل ملف، ولا تضف أبدًا عدة أسطر.

طول السطر

  • حافظ على كلّ سطر من الشيفرة البرمجية ليكون بطول مناسب للقراءة، وابقِ طول الأسطر أقل من 100 محرف (إليك السبب) إلا إن كان لديك سبب وجيه لجعله أطول من ذلك.

التعليقات

اقتباس

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

- دليل Google لتنسيق الشيفرة البرمجية للغة C++‎

كُتِبَ هذا الشرح باستعانة كبيرة من دليل Google لتنسيق الشيفرة البرمجية للغة C++‎ ولغة Python.

تعليقات على مستوى الملف/الصنف

عند التعريف عن صنف ما يجب أن يوجد تعليق مصاحب له يصف ماهيّة الصنف وكيفية استخدامه.

يجب أن يملك الملف الذي لا يحتوي على أصناف، أو يحتوي على أكثر من صنف على تعليق في أعلاه يصف محتوياته.

# Automatic conversion of one locale to another where it is possible, like
# American to British English.
module Translation
  # Class for converting between text between similar locales.
  # Right now only conversion between American English -> British, Canadian,
  # Australian, New Zealand variations is provided.
  class PrimAndProper
    def initialize
      @converters = { :en => { :"en-AU" => AmericanToAustralian.new,
                               :"en-CA" => AmericanToCanadian.new,
                               :"en-GB" => AmericanToBritish.new,
                               :"en-NZ" => AmericanToKiwi.new,
                             } }
    end

  ...

  # Applies transforms to American English that are common to
  # variants of all other English colonies.
  class AmericanToColonial
    ...
  end

  # Converts American to British English.
  # In addition to general Colonial English variations, changes "apartment"
  # to "flat".
  class AmericanToBritish < AmericanToColonial
    ...
  end

يجب أن تملك جميع الملفات من بينهم ملفات البيانات والإعداد، على تعليقات على مستوى الملف.

# List of American-to-British spelling variants.
#
# This list is made with
# lib/tasks/list_american_to_british_spelling_variants.rake.
#
# It contains words with general spelling variation patterns:
#   [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus
# and these extras:
#   learned/learnt, practices/practises, airplane/aeroplane, ...

sectarianizes: sectarianises
neutralization: neutralisation
...

تعليقات الدالة

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

يجب أن تذكر كلُّ دالّة ما هي مدخلاتها ومخرجاتها، إلا إن كانت لا تستوف جميع المعايير التالية:

  • غير مرئية من الخارج.
  • قصيرة جدًا.
  • واضحة وبديهية.

يمكنك استخدام أي تنسيق تريده، في لغة Ruby، يوجد مخططين لتوثيق وشرح عمل الدالّة وهما TomDoc وYARD كما يمكنك أيضًا كتابة تعليقات موجزة:

# Returns the fallback locales for the_locale.
# If opts[:exclude_default] is set, the default locale, which is otherwise
# always the last one in the returned list, will be excluded.
#
# For example:
#   fallbacks_for(:"pt-BR")
#     => [:"pt-BR", :pt, :en]
#   fallbacks_for(:"pt-BR", :exclude_default => true)
#     => [:"pt-BR", :pt]
def fallbacks_for(the_locale, opts = {})
  ...
end

التعليقات الكتلية والمضمّنة

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

def fallbacks_for(the_locale, opts = {})
  # dup() to produce an array that we can mutate.
  ret = @fallbacks[the_locale].dup

  # We make two assumptions here:
  # 1) There is only one default locale (that is, it has no less-specific
  #    children).
  # 2) The default locale is just a language. (Like :en, and not :"en-US".)
  if opts[:exclude_default] &&
      ret.last == default_locale &&
      ret.last != language_from_locale(the_locale)
    ret.pop
  end

  ret
end

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

وفي سياق متصل: لا تستخدم التعليقات الكتلية، فلا يمكن أن تسبقها بمسافة وليس من السهل رؤيتها كالتعليقات العادية.

# bad
=begin
comment line
another comment line
=end

# good
# comment line
# another comment line

علامات الترقيم والإملاء والنحو

اهتم بعلامات الترقيم، والإملاء، والنحو. فمن السهل قراءة التعليقات المكتوبة بعناية على عكس نظيرتها غير المكتوبة بعناية.

ينبغي أن تكون التعليقات قابلة للقراءة كنص سردي، مع وجود الحروف الكبيرة وعلامات الترقيم المناسبة. في حالات كثيرة، تكون الجمل الكاملة قابلة للقراءة أكثر من الجمل القصيرة.

يمكن للتعليقات القصيرة، مثل الّتي توضع في نهاية آخر سطر في شيفرة برمجية أن تكون أقل رسمية، لكن على كلّ الأحوال يجب أن تكون متسقة مع تنسيقك للتعليقات ككلّ.

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

تعليقات TODO

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

  # bad
  # TODO(RS): Use proper namespacing for this constant.

  # bad
  # TODO(drumm3rz4lyfe): Use proper namespacing for this constant.

  # good
  # TODO(Ringo Starr): Use proper namespacing for this constant.

شيفرات برمجية بدون تعليقات

  • لا تترك أبدًا شيفراتك البرمجية بدون تعليقات.

الدوال

تعريف الدوال

  • استخدم الكلمة المفتاحية def مع الأقواس عند وجود وسطاء، واحذف الأقواس عندما لا تقبل الدوالّ أي وسطاء.

    def some_method
      # body omitted
    end
    
    def some_method_with_parameters(arg1, arg2)
      # body omitted
    end

     

  • لا تستخدم الوسطاء الموضوعة افتراضيًا، بل استخدم معاملات الكلمات الرئيسية (keyword) – الموجودة في إصدار لغة روبي Ruby 2.0 أو الأحدث - أو يمكنك استخدام أسماء hash أيضًا.

    # bad
    def obliterate(things, gently = true, except = [], at = Time.now)
      ...
    end
    
    # good
    def obliterate(things, gently: true, except: [], at: Time.now)
      ...
    end
    
    # good
    def obliterate(things, options = {})
      options = {
        :gently => true, # obliterate with soft-delete
        :except => [], # skip obliterating these things
        :at => Time.now, # don't obliterate them until later
      }.merge(options)
    
      ...
    end

     

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

    # bad
    def too_much; something; something_else; end
    
    # good
    def some_method
      # body
    end

     

دوال الاستدعاء

استخدم الأقواس لدالة الاستدعاء في الحالات التالية:

  • إذا كانت الدالة ترجع قيمة.

    # bad
    @current_user = User.find_by_id 1964192
    
    # good
    @current_user = User.find_by_id(1964192)

     

  • إذا كان الوسيط الأول للدالّة يستخدم الأقواس.

    # bad
    put! (x + y) % len, value
    
    # good
    put!((x + y) % len, value)

     

  • لا تضع أبدًا مسافة بين اسم الدالّة والقوس الأول.

    # bad
    f (3 + 2) + 1
    
    # good
    f(3 + 2) + 1

     

  • تجنب الأقواس عند استدعاء الدالّة إذا لم تقبل الدالّة أي وسطاء.

    # bad
    nil?()
    
    # good
    nil?

     

  • ستكون الأقواس اختيارية ما لم تكن الدالة تُعيدّ قيمة (أو لا نهتم بما تُعيده)، أما إذا كان الوسطاء في عدة أسطر، فيمكن للأقواس أن تزيد من قابلية القراءة.

    # okay
    render(:partial => 'foo')
    
    # okay
    render :partial => 'foo'

    في كِلا الحالتين:

    • في حالة قبول الدالة hash كوسيط أخير اختياري، لا تستخدم الأقواس { أو } أثناء الاستدعاء.
    # bad
    get '/v1/reservations', { :id => 54875 }
    
    # good
    get '/v1/reservations', :id => 54875

     

التعابير الشرطيّة

الكلمات المفتاحية للجمل الشرطية

  • لا تستخدم الكلمة المفتاحية then للجملة الشرطية if/unless المتعددة الأسطر.

    # bad
    if some_condition then
      ...
    end
    
    # good
    if some_condition
      ...
    end

     

  • لا تستخدم الكلمة المفتاحية do مع while أو until المتعددات الأسطر.

    # bad
    while x > 5 do
      ...
    end
    
    until x > 5 do
      ...
    end
    
    # good
    while x > 5
      ...
    end
    
    until x > 5
      ...
    end

     

  • إن الكلمات المفتاحية and وor وnot محظورة، فهي لا تستحق العناء، استخدم دائمًا && و|| و! بدلًا منها.

  • يمكنك استخدام المعدِل if/unless عندما يكون الجسم بسيط والشرط بسيط وكلّ شيء في سطر واحد، وخلافًا لذلك، تجنب استخدام if/unless.

    # bad - this doesn't fit on one line
    add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?
    
    # okay
    if request_opts[:trebuchet_experiments_on_page] &&
         !request_opts[:trebuchet_experiments_on_page].empty?
    
      add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])
    end
    
    # bad - this is complex and deserves multiple lines and a comment
    parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i)
    
    # okay
    return if reconciled?

     

  • لا تستخدم أبدًا الكلمة المفتاحية unless مع else، أعد كتابة الشرط لتصبح الحالة الصحيحة الأولى أولًا.

    # bad
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
    
    # good
    if success?
      puts 'success'
    else
      puts 'failure'
    end

     

  • تجنب unless مع الشروط المتعددة.

      # bad
      unless foo? && bar?
        ...
      end
    
      # okay
      if !(foo? && bar?)
        ...
      end

     

  • تجنب استخدام unless مع معاملات الموازنة فإن استطعت استخدام الشرط if مع عكس الشرط الموجود في unless ففعل ذلك.

      # bad
      unless x == 10
        ...
      end
    
      # good
      if x != 10
        ...
      end
    
      # bad
      unless x < 10
        ...
      end
    
      # good
      if x >= 10
        ...
      end
    
      # ok
      unless x === 10
        ...
      end

     

  • لا تستخدم أقواسًا حول الشروط if/unless/while.

    # bad
    if (x > 10)
      ...
    end
    
    # good
    if x > 10
      ...
    end

     

المعامل الثلاثي

  • تجنب استخدام العامل الثلاثي (:?) إلا في الحالات الّتي تكون فيها جميع التعابير بسيطة، إلا أنه يمكنك استخدامه بدلًا من if/then/else/end في الشروط المتكونة من سطر واحد.

    # bad
    result = if some_condition then something else something_else end
    
    # good
    result = some_condition ? something : something_else

     

  • استخدم تعبير واحد لكلّ فرع في المعامل الثلاثي، كذلك يجب ألا يكون المعامل الثلاثي متشعبًا، إذ يُفضّل استخدام بنية if/else في هذه الحالات.

    # bad
    some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
    
    # good
    if some_condition
      nested_condition ? nested_something : nested_something_else
    else
      something_else
    end

     

  • تجنب الشروط المتعدّدة في المعامل الثلاثي إذ يُفضّل استخدام الأخير مع الشروط الفردية.

  • تجنب الأسطر المتعدّدة :? في المعامل الثلاثي، واستخدم if/then/else/end بدلا منه.

    # bad
    some_really_long_condition_that_might_make_you_want_to_split_lines ?
      something : something_else
    
    # good
    if some_really_long_condition_that_might_make_you_want_to_split_lines
      something
    else
      something_else
    end

     

الشروط المتشعّبة

  • تجنب استخدام الشروط المتشعّبة لزيادة التحكم بالشيفرة (يمكنك الاطلاع على هذا المقال لمزيد من المعلومات).

    يُفضّل استخدام تعبير الحراسة (guard clause) عندما تريد التحقق بيانات غير صالحة، فشرط الحراسة هو عبارة شرطية في أعلى الدالة التي تعيد النتيجة بأسرع ما يمكن.

    المبادئ العامة هي:

    • أعدّ القيمة (النتيجة) مباشرة بمجرد معرفتك بأن الدالّة لا طائل منها، أو أنها لا تضف أي زيادة حقيقة للشيفرة.
    • قلل التشعّب والمسافات البادئة في الشيفرة البرمجة من خلال إعادة النتيجة بأسرع وقت ممكن. وهذا يجعل الشيفرات البرمجية أسهل للقراءة، وتتطلّب جهدً عقليًا أقل أثناء قراءة الفرع الثاني من الشرط else.
    • ينبغي أن تكون الشروط الجوهرية أو الأكثر أهمية ذات مسافات بادئة أقل.
    # bad
    def compute
      server = find_server
      if server
        client = server.client
        if client
          request = client.make_request
          if request
             process_request(request)
          end
        end
      end
    end
    
    # good
    def compute
      server = find_server
      return unless server
      client = server.client
      return unless client
      request = client.make_request
      return unless request
      process_request(request)
    end

    يُفضّل استخدام next في الحلقات بدلًا من الكتل الشرطيّة.

    # bad
    [0, 1, 2, 3].each do |item|
      if item > 1
        puts item
      end
    end
    
    # good
    [0, 1, 2, 3].each do |item|
      next unless item > 1
      puts item
    end

الصياغة

  • لا تستخدم for إلّا إذا كنت تعرّف تمامًا لماذا ستستخدمها، فغالبًا، يجب استخدام المكرّرات (iterator) بدلًا منها، فتعمل for بنفس شروط each (أي أنك ستضيف مستوى جديد من المراوغة للبرنامج) لكن مع عيب صغير، فلا تُعرّف for نطاق جديد (على عكس each) وستظهر المتغيرات المعرّفة في كتلتها خارجيًا.

    arr = [1, 2, 3]
    
    # bad
    for elem in arr do
      puts elem
    end
    
    # good
    arr.each { |elem| puts elem }

     

  • يفضل استخدام {...} بدلًا من do...end للكتل المكوّنة من سطر واحد، وتجنّب استخدام {...} للكتل المكونة من عدة أسطر (فسَلّسَلةُ عدة أسطر هو شيء قبيح)، واستخدم do...end دائمًا للتحكم في التدفّق ولتعريف الدوالّ، وتجنب استخدام do...end عند السَلّسَلة.

    names = ["Bozhidar", "Steve", "Sarah"]
    
    # good
    names.each { |name| puts name }
    
    # bad
    names.each do |name| puts name end
    
    # good
    names.each do |name|
      puts name
      puts 'yay!'
    end
    
    # bad
    names.each { |name|
      puts name
      puts 'yay!'
    }
    
    # good
    names.select { |name| name.start_with?("S") }.map { |name| name.upcase }
    
    # bad
    names.select do |name|
      name.start_with?("S")
    end.map { |name| name.upcase }

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

  • استخدم معاملات الإسناد المختصرة كلما كان ذلك ممكنًا.

    # bad
    x = x + y
    x = x * y
    x = x**y
    x = x / y
    x = x || y
    x = x && y
    
    # good
    x += y
    x *= y
    x **= y
    x /= y
    x ||= y
    x &&= y

     

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

    # bad
    puts 'foobar'; # superfluous semicolon
    puts 'foo'; puts 'bar' # two expressions on the same line
    
    # good
    puts 'foobar'
    
    puts 'foo'
    puts 'bar'
    
    puts 'foo', 'bar' # this applies to puts in particular

     

  • استخدم :: للثوابت المرجعيّة فقط (ويشمل هذا الأصناف والوحدات) والبواني (مثل: Array()‎ أو Nokogiri::HTML()‎) ولا تستخدم :: لاستدعاء الدالّة العادية.

    # bad
    SomeClass::some_method
    some_object::some_method
    
    # good
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    SomeModule::SomeClass()

     

  • تجنب استخدام return عندما تكون غير مطلوبة.

    # bad
    def some_method(some_arr)
      return some_arr.size
    end
    
    # good
    def some_method(some_arr)
      some_arr.size
    end

     

  • لا تستخدم القيمة المُعادة من المساواة = في الجمل الشرطيّة.

    # bad - shows intended use of assignment
    if (v = array.grep(/foo/))
      ...
    end
    
    # bad
    if v = array.grep(/foo/)
      ...
    end
    
    # good
    v = array.grep(/foo/)
    if v
      ...
    end

     

  • استخدم ‎||=‎ كما تريد لتهيئة المتغيّرات.

    # set name to Bozhidar, only if it's nil or false name ||= 'Bozhidar'

     

  • لا تستخدم ‎||=‎ لتهيئة المتغيرات المنطقيّة، (فكر في ما سيحدث إذا كانت القيمة الحالية تساوي false).

    # bad - would set enabled to true even if it was false
    enabled ||= true
    
    # good
    enabled = true if enabled.nil?

     

  • استخدم ‎.call الصريحة عند استدعاء lambda.

    # bad
    lambda.(x, y)
    
    # good
    lambda.call(x, y)

     

  • تجنب استخدام متغيرات الخاصة على نمط بيرل Perl (مثل: $ أو ‎$0-9 …إلخ)، فهي مبهمة فعلًا. واستخدامها سيُعيقنا بكلّ شيئ عدا في السكربتات ذات السطر الواحد. يُفضّل اصدارات ذات الشكل الطويل مثل: ‎$PROGRAM_NAME.

  • استخدم الاختصار &: عندما تأخذ كتلة الدالّة وسيط واحد فقط، ويعمل الجسم على قراءة سِمة أو استدعاء دالّة بدون وسطاء .

    # bad
    bluths.map { |bluth| bluth.occupation }
    bluths.select { |bluth| bluth.blue_self? }
    
    # good
    bluths.map(&:occupation)
    bluths.select(&:blue_self?)

     

  • يفضلُ استخدام some_method على self.some_method عند استدعاء الدالّة داخليًا.

    # bad
    def end_date
      self.start_date + self.nights
    end
    
    # good
    def end_date
      start_date + nights
    end

    يجب عليك استخدام self.‎ في الحالات الثلاثة التالية:

  1. عند تعريف دالّة صنف: def self.some_method.
  2. عندما تكون self هي نموذج سجل فعالّ ActiveRecord ويكون الجانب الأيسر هو استدعاء دالّة اِسناد بما في ذلك اِسناد سِمة: self.guest = user.
  3. الإشارة للصنف داخليًا: self.class.
  • عند تعريف كائن من أي نوع قابل للتحويل وتريده أن يكون ثابتًا، تأكد من استدعاء freeze عليه، ومن الأمثلة الشائعة على ذلك هي السلاسل النصية، والمصفوفات، ومتغيرات من نوع hash (انظر لهذا المقال لمزيد من المعلومات). والسبب في ذلك أن الثوابت في لفة روبي (Ruby) هي أنواع قابلة للتحويل، وبعضها الآخر ليس كذلك. ستتأكد من عدم قابليتها للتحويل عند استدعاء freeze عليها لأنه سيصدر استثناء عند محاولة تعديلهم إن كانت غير قابلة للتعديل، وبالنسبة للسلاسل النصية، سيُسمح بالتعامل معهم لنسخ روبي ذات الاصدار الأقدم من 2.2.

    # bad
    class Color
      RED = 'red'
      BLUE = 'blue'
      GREEN = 'green'
    
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ]
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }
    end
    
    # good
    class Color
      RED = 'red'.freeze
      BLUE = 'blue'.freeze
      GREEN = 'green'.freeze
    
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ].freeze
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }.freeze
    end

     

قواعد التسمية

  • استخدم أسلوب التسمية snake_case (جميع حروف اسم المتغير صغيرة) للدوالّ والمتغيرات.

  • استخدم أسلوب التسمية camelCase (تكبير أول حرف من كلّ كلمة في اسم المتغير عدا أول كلمة) للأصناف والوحدات. (احتفظ بالاختصارات مثل" HTTP وRFC وXML بحالة حروف كبيرة).

  • استخدم أسلوب التسمية SCREAMING_SNAKE_CASE (جميع حروف اسم المتغير كبيرة) للثوابت الأخرى.

  • يجب أن تنتهي الدوالّ الإسنادية (الّتي ترجع قيمة منطقية) بعلامة استفهام. (هكذا: Array#empty?‎).

  • يجب أن تنتهي أسماء الدوالّ التي يُحتمل أن تكون خطرة (مثل الدوالّ الّتي تُعدلّ self أو الوسطاء، أو exit!‎ …إلخ) بعلامة تعجب، ويجب أن تتواجد الدوالّ الخطرة (bang) إذا تتواجدت الدوالّ غير الخطرة (non-bang) معها (يمكنك الاطلاع على المقال للمزيد من المعلومات).

  • سمِّ متغيّرات رمي الأخطاء _.

    version = '3.2.1'
    major_version, minor_version, _ = version.split('.')

     

الأصناف

  • تجنب استخدام متغيرات (@@) بسبب سلوكهم السيئ في الوراثة.

    class Parent
      @@class_var = 'parent'
    
      def self.print_class_var
        puts @@class_var
      end
    end
    
    class Child < Parent
      @@class_var = 'child'
    end
    
    Parent.print_class_var # => will print "child"

    كما ترى أن جميع الأصناف في التسلسل الهرمي للصنف تتشارك في متغير صنف واحد، ويجب أن تُفضّل متغيّرات مثيل الصنف على متغيّرات الصنف.

  • استخدم def self.method لتعريف الدوالّ المتفرّدة (Singleton Methods)، وسيجعل هذا الدوالّ أكثر مقاومة لتغييرات إعادة الهيكلة.

    class TestClass
      # bad
      def TestClass.some_method
        ...
      end
    
      # good
      def self.some_other_method
        ...
      end

     

  • تجنب استخدام class << self إلا عند الضرورة، فمثلًا، الجالبات المفردة (single accessors) والسمات مستعارة (aliased attributes).

    class TestClass
      # bad
      class << self
        def first_method
          ...
        end
    
        def second_method_etc
          ...
        end
      end
    
      # good
      class << self
        attr_accessor :per_page
        alias_method :nwo, :find_by_name_with_owner
      end
    
      def self.first_method
        ...
      end
    
      def self.second_method_etc
        ...
      end
    end
  • ضع مسافة بادئة لدوالّ public وprotected وprivate بقدر تعريف الدالّة الّتي ينتمون إليها، واترك سطرًا فارغًا أعلاها وأسفلها.

    class SomeClass
      def public_method
        # ...
      end
    
      private
    
      def private_method
        # ...
      end
    end

     

الاستثناءات

  • لا تستخدم الاستثناءات للتحكم بسير البرنامج.

    # bad
    begin
      n / d
    rescue ZeroDivisionError
      puts "Cannot divide by 0!"
    end
    
    # good
    if d.zero?
      puts "Cannot divide by 0!"
    else
      n / d
    end

     

  • تجنب إنقاذ (rescue) صنف الاستثناء Exception.

    # bad
    begin
      # an exception occurs here
    rescue Exception
      # exception handling
    end
    
    # good
    begin
      # an exception occurs here
    rescue StandardError
      # exception handling
    end
    
    # acceptable
    begin
      # an exception occurs here
    rescue
      # exception handling
    end

     

  • لا تحدّد في استثناء RuntimeError وسيطين فقط في الكلمة المفتاحية raise، ومن الأفضل استخدام خطأ الأصناف الفرعية لتوضيح الخطأ وشرحه بطريقة أفضل.

    # bad
    raise RuntimeError, 'message'
    
    # better - RuntimeError is implicit here
    raise 'message'
    
    # best
    class MyExplicitError < RuntimeError; end
    raise MyExplicitError

     

  • يُفضّل توفير صنف استثناء والرسالة كوسيطين في الكلمة المفتاحية raise بدلًا من نسخة استثناء عادي.

    # bad
    raise SomeException.new('message')
    # Note that there is no way to do `raise SomeException.new('message'), backtrace`.
    
    # good
    raise SomeException, 'message'
    # Consistent with `raise SomeException, 'message', backtrace`.

     

  • تجنب استخدام rescue في شكل مُعدّل (Modifier).

    # bad
    read_file rescue handle_error($!)
    
    # good
    begin
      read_file
    rescue Errno:ENOENT => ex
      handle_error(ex)
    end

     

التجميعات (Collections)

  • يفضّل استخدام الخارطة (أو الخريطة) map بدلًا من تجميعة collect.

  • يفضّل استخدام detect على find، فاستخدام find غامض بسبب دالّة find الخاصة بكائن السجل الغعال (ActiveRecord) وسيُظهر detect على أنك تعمل مع تجميعة في لغة روبي وليس كائن سجل فعال.

  • يُفضّل استخدام reduce على inject.

  • يُفضّل استخدام size على length وcount لتحسين أداء الشيفرة.

  • يفّضل استخدام تدوين الإنشاء (creation notation) ومصفوفة مجزئة مصنّفة النوع (Literal)، إلّا إذا كنت بحاجة لتمرير الوسطاء لمنشئيها.

    # bad
    arr = Array.new
    hash = Hash.new
    
    # good
    arr = []
    hash = {}
    
    # good because constructor requires parameters
    x = Hash.new { |h, k| h[k] = {} }

     

  • يُفضّل استخدام Array#join بدلًا من Array#*‎ لوضوح الشيفرة.

    # bad
    %w(one two three) * ', '
    # => 'one, two, three'
    
    # good
    %w(one two three).join(', ')
    # => 'one, two, three'

     

  • استخدم الرموز (symbols) بدلًا من السلاسل النصية كمفاتيح لجدول Hash.

    # bad
    hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
    
    # good
    hash = { :one => 1, :two => 2, :three => 3 }

     

  • على نحو مماثل، استخدم رموز واضحة بدلًا من رموز السلاسل النصية عندما يكون ذلك ممكنًا.

    # bad
    :"symbol"
    
    # good
    :symbol

     

  • استخدم Hash#key?‎ بدلًا من Hash#has_key?‎ وHash#value?‎ بدلًا من Hash#has_value?‎. فوفقًا لماتز (Matz)، تعدّ الأشكال الطويلة مُهملة.

    # bad
    hash.has_key?(:test)
    hash.has_value?(value)
    
    # good
    hash.key?(:test)
    hash.value?(value)

     

  • استخدم جدول hash متعدّد الأسطر لأنه يجعل الشيفرة أكثر قابليّة للقراءة، واستخدم الفواصل التذييلية للتأكد من أن أي تغيّر للوسطاء لن يتسبب بتغيّرات غريبة لشكل هذه الأسطر، وذلك عندما لا يتغيّر منطق الحلّ.

    hash = {
      :protocol => 'https',
      :only_path => false,
      :controller => :users,
      :action => :set_password,
      :redirect => @redirect_url,
      :secret => @secret,
    }

     

  • استخدم الفاصلة التذيليّة في المصفوفة الّتي تمتد لأكثر من سطر واحد.

    # good
    array = [1, 2, 3]
    
    # good
    array = [
      "car",
      "bear",
      "plane",
      "zoo",
    ]

     

السلاسل النصية

  • يُفضّل توليد السلسلة النصية (string interpolation) بدلًا من دمج السِلاسل النصية:

    # bad
    email_with_name = user.name + ' <' + user.email + '>'
    
    # good
    email_with_name = "#{user.name} <#{user.email}>"

    وعلاوة على ذلك، لا تنس توليد نمط روبي 1.9، لنفترض أنك تُنشئ مفاتيح ذاكرة التخزين المؤقت كالتالي:

    CACHE_KEY = '_store'
    
    cache.write(@user.id + CACHE_KEY)

    مرةً أخرى يُفضّل توليد السلسلة النصية (string interpolation) بدلًا من دمج السِلاسل النصية:

    CACHE_KEY = '%d_store'
    
    cache.write(CACHE_KEY % @user.id)

     

  • تجنب استخدام String#+‎ عندما تحتاج لإنشاء قطع بيانات كبيرة، واستخدم بدلًا من ذلك String#<<‎. تحور عملية دمج نسخ السلاسل النصية في مكانها، وهي أسرع دومًا من String#+‎، والّذي ينشئ مجموعة جديدة من كائنات السلسلة النصية.

    # good and also fast
    story = ''
    story << 'The Ugly Duckling'
    
    paragraphs.each do |paragraph|
      story << paragraph
    end

     

  • استخدم \ في نهاية السطر بدلًا من + أو << لدمج السلاسل النصية متعددة الأسطر.

    # bad
    "Some string is really long and " +
      "spans multiple lines."
    
    "Some string is really long and " <<
      "spans multiple lines."
    
    # good
    "Some string is really long and " \
      "spans multiple lines."

     

التعابير النمطية

  • تجنب استخدام ‎$1-9 لأنه من الصعب متابعة ما يحتويه، واستخدم بدلًا منه المجموعات المسماة (Named groups).

    # bad
    /(regexp)/ =~ string
    ...
    process $1
    
    # good
    /(?<meaningful_var>regexp)/ =~ string
    ...
    process meaningful_var

     

  • كن حذرًا عند استخدام ^ و$ لأنها تطابق بداية/نهاية السطر، وليس نهايات السلسلة النصية، فإذا أردت مطابقة كامل السلسلة النصية فاستخدم: ‎\A و‎\z.

    string = "some injection\nusername"
    string[/^username$/]   # matches
    string[/\Ausername\z/] # don't match

     

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

    regexp = %r{
      start         # some text
      \s            # white space char
      (group)       # first group
      (?:alt1|alt2) # some alternation
      end
    }x

     

محارف النسبة المئوية

  • يفضل استخدام الأقواس الهلالية على الأقواس المعقوصة، أو الأقواس المعقوفة، أو حتى الأنابيب (pipes) عند استخدام محددات مجردة % للتناسق ولأن سلوك محارف % أقرب إلى استدعاء الدالّة منه إلى البدائل.

    # bad
    %w[date locale]
    %w{date locale}
    %w|date locale|
    
    # good
    %w(date locale)

     

  • استخدم ‎%w كما يحلو لك.

    STATES = %w(draft open closed)

     

  • استخدم ()‎% للسلاسل النصية المتكونة من سطر واحد، والّتي تتطلّب عملية توليد سلسلة وعلامات الاقتباس المزدوجة المضمّنة، وبالنسبة للسلاسل النصية المتكونة من عدة أسطر، يُفضل استخدام heredocs.

    # bad - no interpolation needed
    %(Welcome, Jane!)
    # should be 'Welcome, Jane!'
    
    # bad - no double-quotes
    %(This is #{quality} style)
    # should be "This is #{quality} style"
    
    # bad - multiple lines
    %(Welcome, Jane!\nPlease enjoy your stay at #{location}\nCheers!)
    # should be a heredoc.
    
    # good - requires interpolation, has quotes, single line
    %(Welcome, #{name}!)

     

  • استخدم ‎٪r للتعابير النمطية فقط والّتي تتطابق مع أكثر من محرف /.

    # bad
    %r(\s+)
    
    # still bad
    %r(^/(.*)$)
    # should be /^\/(.*)$/
    
    # good
    %r(^/blog/2011/(.*)$)

     

  • تجنب استخدام ‎%x إلا إذا أردت استدعاء أمر مع علامة الاقتباس الخلفية (`) - وهو أمر مستبعد إلى حد ما -.

    # bad
    date = %x(date)
    
    # good
    date = `date`
    echo = %x(echo `date`)

     

ريلز

عند العودة فورًا بعد استدعاء render أو redirect_to، ضع الكلمة المفتاحية return في السطر التالي وليس في نفس السطر.

# bad
render :text => 'Howdy' and return

# good
render :text => 'Howdy'
return

# still bad
render :text => 'Howdy' and return if foo.present?

# good
if foo.present?
  render :text => 'Howdy'
  return
end

نطاقات

  • عند تعريف نطاقات نموذج السجل الفعّال (ActiveRecord)، أحِط العلاقة بـ lambda، وخلافًا لذلك سيفرض الاتصال المجردة لقاعدة البيانات تنفيذه في وقت تحميل الصنف (مشابهة لطريقة بدء التشغيل).

    # bad
    scope :foo, where(:bar => 1)
    
    # good
    scope :foo, -> { where(:bar => 1) }

     

انسجم مع الشيفرة

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

الهدف من امتلاك مبادئ توجيهية لتنسيق الشيفرة هي الحصول على مفردات مشتركة من الشيفرات ليتمكن الناس من فهم ما تقوله بدلًا من كيف تقوله، واستعرضنا هنا قواعد التنسيق العالمية حتى يعرف الناس المفردات، لكن التنسيقات شيفراتك المحلية مهمة أيضًا، فإذا كانت الشيفرة البرمجية الّتي ستضيفها تختلف اختلافًا كبيرًا عن الشيفرات البرمجية الّتي حولها فعندها ستجعل قراءة الشيفرة صعبة ولذلك تجنب هذا الأمر - دليل Google لتنسيق الشيفرة البرمجية للغة C++‎

ترجمة لدليل Ruby Style Guide من شركة Airbnb على موقع GitHub





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن