دليل Airbnb لنمط روبي


هشام رزق الله

هذا هو دليل Airbnb لنمط روبي. مستوحى من دليل GitHub ودليل Bozhidar Batsov.

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

المسافة الفارغة

  • استخدم علامات التبويب (tabs) ذات المسافتين.
  • يكون عمق 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."
  • لا تستخدم مسافة إضافية في النطاق الحرفي.
# 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 حرف (أساس منطقي).

التعليقات

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

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

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

# 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
…

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

يجب أن يكون لكل إعلان دالة تعليقات تسبقها مباشرة تصف ما تقوم به الدالة وكيفية استخدامها، ويجب أن تكون هذه الملفات وصفية (‘تفتح الملف’) بدلا من أمرية (‘افتح الملف’).
التعليقات التي تصف الدالة، لا تقول ما تقوم به الدالة، بشكل عام، هذه التعليقات لا تصف كيفية عمل الدالة، وبدلا من ذلك، يجب ترك هذه للتعليقات التي تتخلل الشيفرة البرمجية للدالة.
يجب أن تذكر كل دالة ما هي المدخلات وما هي المخرجات، ما لم تستوف جميع المعايير التالية:

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

يمكنك استخدام أي تنسيق تريده، في الروبي، يوجد مخططين لتوثيق الدالة وهما 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) – إذا كانت موجودة، في الإصدار 2 من روبي أو أحدث - أو 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 مع عامل المقارنة المعاكس.
  # 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) عندما يمكنك تأكيد بيانات غير صالحة، فشرط الحراسة هو عبارة مشروطة في أعلى الدالة التي ترّجع في أسرع ما يمكن.
المبادئ العامة هي:

  • إرجاع الفوري بمجرد معرفة أن الدالة لا يمكنها أن تفعل أي شيء أكثر من الذي فعلته.
  • تقليل التداخل ومسافة البادئة في الشيفرة البرمجة عن طريق الإرجاع في وقت مبكّر. وهذا يجعل الشيفرات برمجية أسهل للقراءة وتطلّب أقل جهد عقلي في الجزء القراءة عند تتبّع فروع أخرى.
  • ينبغي أن تكون التدفقات الجوهرية أو الأكثر أهمية هي الأقل مسافات البادئة.
# 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 دائما للتحكم في التدفّق وتعريف الأساليب (على سبيل المثال في Rakefile وفي بعض DSL) وتجنب استخدام 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)
  • تجنب استخدام متغيرات الخاصة على نمط بيرل (مثل $0-9، KaTeX parse error: Expected 'EOF', got '،' at position 1: ،̲ الخ)، فهي مشفّ…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.
    سبب ذلك أن ثوابت روبي هي أنواع قابلة للتحويل، وستتأكد من عدم قابليتهم للتحويل عند استدعاء 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) - للمزيد من المعلومات -.
  • سمّي متغيرات الرمي (throwaway) _.
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 لتعريف الأساليب المنفردة، وسيجعل هذا الأساليب أكثر مقاومة لتغييرات إعادة الهيكلة.
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 في شكل المُعدّل.
# 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 على أنك تعمل مع مجموعة روبي وليس كائن AR.
  • يُفضّل استخدام reduce على inject.
  • يُفضّل استخدام size على length و count لأسباب تتعلّق بالأداء.
  • يفّضل استخدام تدوين الإنشاء (creation notation) للمصفوفة الحرفيّة وللـ hash إلا إذا كنت بحاجة إلى تمرير معاملات إلى منشئيها.
# 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 متعدّد الأسطر لأنه يجعل التعليمات البرمجية أكثر قابليّة للقراءة واستخدم الفواصل الزائدة للتأكد من أن تغييرات المعامل لا تتسبب بأسطر diff الغريبة عندما لم يتغيّر المنطق خلاف ذلك.
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
html = ''
html << '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html << "<p>#{paragraph}</p>"
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 لأنه من الصعب متابعة ما يحتويه، واستخدم بدلا منه المجموعات المسماة.
# 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 لتعبيرات (regexps) المعقدة، فهذا سيجعلها أكثر قابلية للقراءة ويمكنك إضافة بعض التعليقات المفيدة، فقط كن حذرا لأنه سيتجاهل الفراغات.
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)
  • استخدم %() للسلاسل النصية المتكونة من سطر واحد والتي تتطلّب الاستيفاء (interpolation) وعلامات الاقتباس المزدوجة المضمّنة ، وبالنسبة للسلاسل النصية المتكونة من عدة أسطر، يُفضل استخدام heredocs.
# bad - no interpolation needed
%(<div class="text">Some text</div>)
# should be '<div class="text">Some text</div>'

# bad - no double-quotes
%(This is #{quality} style)
# should be "This is #{quality} style"

# bad - multiple lines
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# should be a heredoc.

# good - requires interpolation, has quotes, single line
%(<tr><td class="name">#{name}</td>)
  • استخدم ٪ 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 حولها، فاجعل تعليقاتك كذلك أيضا.
الهدف من امتلاك مبادئ توجيهية للنمط هي الحصول على مفردات مشتركة من الشيفرات البرمجية حتى يتمكن الناس من فهم الذي تريد قوله بدلا من كيف تقوله، ونحن نعرض هنا قواعد النمط العالمية حتى يعرف الناس المفردات، لكن النمط المحلي مهم أيضا، فإذا كانت الشيفرة البرمجية التي تضيفها تختلف بشكل كبيرا عن الشيفرات البرمجية التي حولها فستجعل قراءة الشيفرة البرمجية صعبة لذلك تجنب ذلك.

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





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


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



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

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

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


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

تسجيل الدخول

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


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