وجود مثل أعلي مهم
-- أليكس ج. ميرفي / روبوكب
شئ كان دايما بيضايقني ك روبي ديفيلوبر—مطورين البايثون عندهم مرجع عظيم لطريقة كتابة الكود (راجع ستايل البايثون) و احنا مش عندنا واحد رسمي زيه يوثق طريقة الكتابة بالروبي و افضل التطبيقات. و انا مؤمن جدا ان دا حاجة مهمة , و ايضا مؤمن ان مجتمع عظيم زي بتاع الروبي لازم يكون قادر يطلع ستايل زي ده.
المرجع او الدليل دا ابتدا ك مرجع داخلي خاص لشركتنا دليل البرمجة بالروبي (اتكتب عشانك بجد). في لحظة كدا قررت ان اللي انا بعمله دا ممكن ناس كتيرة من مجتمع الروبي تهتم بيه عامة , و ان العالم في احتياج اقل ل توجيه داخلي اخر لشركة . بس بكل تأكيد العالم هيستنفع من اللي هنقدر نسجله و نتوصل ايه من تطبيقات و ممارسات تسهل كتابة لغة الروبي
من ساعة ما بدأت الدليلل دا و انا بيجيلي رد فعل من اعضاء مجتمع الروبي من جميع انحاء العالم. الفضل ل كل الاقتراحات و الدعم! مع بعض هنقدر نعمل مصدر مفيد لكل مطزر بلغة الروبي.
علي فكرة لو انت شغال رايلز برضه ممكن تحتاج تبص بصة علي دليل أسلوب الروبي اون رايلز.
الدليل دا بيقترح عليك افضل التطبيقات عشان مبرمج الروبي الحقيقي يقدر يكتب حاجة مبرمج روبي حقيقي تاني يقدر يعدل عليها دليل بيعكس الاستخدام بتاعه في العالم الحقيقي , في حين ان الدليل المتمسك بالمثالية بيترفض من الناس , و هو المفروض يساعد علي التخلص من المشاكل نهائيا بغض النظر هو كويس اد ايه .
الدليل دا متقسم ل اجزاء ليها علاقة ببعض . و انا حاولت اضيف المنطق علي القواعد (لو حاجة منها اهملت ف انا حاطط افتراض انها واضحة)
انا مجبتش القواعد دي من عندي, هم غالبا مستندين علي حياتي المهنية ك مهندس برمجيات محترف و الاقتراحات و الاراء من اعضاء مجتمع الروبي و مصادر محترمة ل برمجة الروبي مثل :- "Programming Ruby" و "The Ruby Programming Language".
في بعض النواحي مبتلاقيش توافق واضح للاراء في مجتمع الروبي بالنسبة لاسلوب معين (زي طريقة تعريف السترينج, المسافات اللي جوا الهاش, مكان النقطة في تسلسل الدوال, الخ .. ) في الحالات اللي زي دي يتم التعريف بجميع الانماط المشهورة و يسيبك براحتك تطبق اللي تحبه .
الدليل دا بيتطور علي طول مع الوقت ,عشان عرف جديد بيتحط او واحد قديم يتشال عشان تغير بيحصل في لغة الروبي نفسها
كتير من المشاريع عندهم دليل كتابة كود خاص بيهم (غالبا بيكون مشتق من الدليل دا). في حالة وجود اي خلاف فان الاولوية تكون للدليل دا.
ممكن تعمل نسخة من الدليل PDF او HTML باستخدام Pandoc.
و دا يقدر يحلل الكود بتاعك , بناء علي الدليل دا RuboCop .
الترجمات المتاحة للدليل دا موجودة بالغات دي :-
- الانجليزية
- الصينية المبسطة
- الصينية التقليدية
- الفرنسية
- اليابانية
- الكورية
- البرتغالية
- الروسية
- الاسبانية
- الفيتنامية
- نموذج السورس كود
- تركيب الجملة
- التسمية
- التعليقات
- الفئات و الوحدات
- الاستثنائات
- المجموعات
- الارقام
- strings
- الوقت و التاريخ
- التعبيرات العادية
- حرف النسبة المئوية
- ميتابروجرامينج
- متفرقات
- ادوات
تقريبا كل واحد شايف ان كل الستيلات معفنة و متتقريش معادا بتاعهم , شيل كدا "بتاعهم" . تقريبا كدا هم صح
تقريبا كل واحد شايف ان كل الستيلات معفنة و متتقريش معادا بتاعهم , شيل كدا "بتاعهم" تقريبا كدا هم صح...
-- جيري كوفين
-
استخدم
UTF-8
لل انكودين بتاع فايل الكود بتاعك. [link] -
استخدم مسافتين لكل مستوي ف كودك (زي السوفت تاب) مستخدمش هارد تاب [link]
# سئ - اربع مسافات def some_method do_something end # جيد def some_method do_something end
-
استخدم ستايل اليونكس في نهاية السطور. (*BSD/Solaris/Linux/macOS مستخدمين نظم التشغيل دول عنهم بشكل افتراضي , الويندوز يوزر بس هو اللي محتاج ياخد باله.) [link]
-
لو انت بتستخدم ال جيت ف ممكن تكون محتاج تحط تعريف لحماية مشروعك من نهاية السطور في نظام الويندوز:
$ git config --global core.autocrlf true
-
-
متسدخدمش
;
عشان تفرق بين الستاتمينت و للتانية استخدم ستاتمينت واحدة في السطر [link]# سئ puts 'foobar'; # فاصلة منقوطة ملهاش لازمة puts 'foo'; puts 'bar' # اتنين اكسبريشن في سطر واحد # جيد puts 'foobar' puts 'foo' puts 'bar' puts 'foo', 'bar' # دا وضع خاص بس
-
يفضل تعريف الكلاس اللي منغير محتوي في سطر واحد. [link]
# سئ class FooError < StandardError end # شغال class FooError < StandardError; end # جيد FooError = Class.new(StandardError)
-
بلاش تكتب دالة في سطر واحد, مع انها مشهورة عامة في شوية خوصيات في السينتاكس بتاعها بتخلي استخدامها غير مرغوب فيه علي اي حال مينفعش يبقي في اكتر من اكسبرشن ف سطر واحد في الدالة بتاعتك [link]
# سئ def too_much; something; something_else; end # شغال - لاحظ ان اول ; ضرورية def no_braces_method; body end # شغال - لاحظ ان تاني ; اختيارية def no_braces_method; body; end # شغال - بس منغير ; بتبقي صعب تقراه def some_method() body end # جيد def some_method body end
استثناء وحيد للقاعدة و هي الدالة الفاضية
# جيد def no_op; end
-
استخدم المسافات جوالين ال فواصل و النقطتان و الفواصل المنقوطة ممكن يكون المسافات (او غالبا) ملهاش علاقة بمترجم الروبي . بس الهدف الاساسي منه كتابة كود سهل القراية [link]
sum = 1 + 2 a, b = 1, 2 class FooError < StandardError; end
في بعض الاستثنائات منها علامة الأس:
# سئ e = M * c ** 2 # جيد e = M * c**2
استثناء اخر عشان الجذر:
# سئ o_scale = 1 / 48r # جيد o_scale = 1/48r
-
مفيش مسافات بعد
(
,[
او بعد]
,)
. Use spaces around{
and before}
. [link]# سئ some( arg ).other [ 1, 2, 3 ].each{|e| puts e} # جيد some(arg).other [1, 2, 3].each { |e| puts e }
{
و}
محتاجين بعض التوضيح خاصة اننا بنستخدمه للهاش و البلوك و السترينجفي الهاش الطريقتين يعتبروا مقبولين. بس الطريقة الاولي اوضح شوية (و اكتر شعبية و استخداما في مجتمع الروبي عامة). انما الطريقة التانية ميزيتها انها بتعمل فرق مرئي لشكل الهاش و البلوك . ايا يكن الطريقة اللي هتخترها استخدمها باتساق.
# جيد - مسافة بعد { و قبل } { one: 1, two: 2 } # جيد - مفيش مسافة قبل { و بعد } {one: 1, two: 2}
مع استخدامها جوا السترينج, متسيبش مسافة فاضية من جوا .
# سئ "From: #{ user.first_name }, #{ user.last_name }" # جيد "From: #{user.first_name}, #{user.last_name}"
-
متسيبش مسافة بعد
!
. [link]# سئ ! something # جيد !something
-
مفيش مسافات جوا تعريف الراينج. [link]
# سئ 1 .. 3 'a' ... 'z' # جيد 1..3 'a'...'z'
-
خلي
when
وcase
. الاتتنين علي نفس المستوي دا اللي موجود في كتابين "The Ruby Programming Language" و "Programming Ruby". [link]# سئ 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 # جيد 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
-
لما تيجي تحط ناتج الكونداشن بتاعك في متغير , حافظ عليهم بحيث يكونوا في نفس البرانش. [link]
# سئ - معقد شوية 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 result = if some_cond calc_something else calc_something_else 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 result = if some_cond calc_something else calc_something_else 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 result = if some_cond calc_something else calc_something_else end
-
Use empty lines between method definitions and also to break up methods into logical paragraphs internally. [link]
def some_method data = initialize(options) data.manipulate! data.result end def some_method result end
-
متحطش كذا سطر فاضي ورا بعض. [link]
# سئ - فيها سطرين فاضيين. some_method some_method # جيد some_method some_method
-
حط سطر فاضي بعد الاكسيس موديفاير. [link]
# bad class Foo attr_reader :foo def foo # do something... end end # good class Foo attr_reader :foo def foo # do something... end end
-
متحطش سطور فاضية حوالين الدوال و الكلاسيس و الموديول . [link]
# سئ class Foo def foo begin do_something do something end rescue something end end end # جيد class Foo def foo begin do_something do something end rescue something end end end
-
لما تيجي تستخدم دالة متحطش اخر فاصلة ,خاصة لما يكون الباراميتيرز بتوعها مش علي خطوط منفصلة [link]
# سئ - اسهل ل تحريك/اضافة/ازالة باراميتير, بس برضه مش احسن حاجة some_method( size, count, color, ) # سئ some_method(size, count, color, ) # جيد some_method(size, count, color)
-
حط مسافات حوالين علامة
=
لما تيجي تحط قيمة افتراضية للبراميتيرز : [link]# سئ def some_method(arg1=:default, arg2=nil, arg3=[]) # do something... end # جيد def some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something... end
في حين ان بعض الكتب بتقترح الطريقة الاولي, التاني احسن في الاستخدام (تقدر تقول كدا مقري اكتر).
-
متكملش في سطر جديد لو انت مش محتاج كدا
\
عمليا متكملش في سطر جديد الا لو محتاج كدا في سترانج مثلا [link]# سئ result = 1 - \ 2 # جيد (بس شكلها وحش زي الزفت) result = 1 \ - 2 long_string = 'First part of the long string' \ ' and second part of the long string'
-
عشان تنسق تسلسل الدوال متعدد السطور. There are two popular في طريقتين مشهورين في مجتمع الروبي, و الاتنين يعتبروا كويسين—قدام
.
(الاختيار أ) و تابعة.
(الاختيار ب). [link]-
(الاختيار أ) لما تيجي تستخدم التسلسل في سطر جديد سيب ال
.
في السطر التاني.# سئ - محتاج تراجع السطر الاول عشان تفهم السطر التاني. one.two.three. four # جيد - انت فاهم كويس ايه اللي بيحصل في السطر التاني. one.two.three .four
-
(الاختيار ب) لما تيجي تستخدم التسلسل في سطر جديد سيب ال
.
في السطر الاول عشان يبقي فاهم ان في تكملة تحت.# سئ - انت محتاج توصل للسطر التاني عشان تفهم ان لسة التسلسل مخلصش . one.two.three .four # جيد - واضحة اووي من السطر الاول ان لسة في تكملة . one.two.three. four
هتلاقي مناقشة هنا فيها المزايا بتاعت الطريقتين here.
-
-
حاذي علي بارامتر الدالة لو هياخدوا اكتر من سطر. لما تيجي تحاذيهم و ميبقوش مناسبين عشان كول السطر , حط واحد بس ف كل سطر و مقبول برضه لو بعد اول واحد [link]
# نقطة البداية (السطر طويل اوي) def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # سئ (مزدوج المسافة) def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # جيد def send_mail(source) Mailer.deliver(to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text) end # جيد - (مسافة عادية) def send_mail(source) Mailer.deliver( to: '[email protected]', from: '[email protected]', subject: 'Important message', body: source.text ) end
-
حاذي عناصر الاراي اللي بتاخد اكتر من سطر . [link]
# سئ - مسافة البداية menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] # جيد menu_item = [ 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' ] # جيد menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
-
حط اندرسكور ل الارقام الكبيرة عشان نعرف نقراها. [link]
# سئ - في كام صفر موجود ؟ num = 1000000 # جيد - كدا مخك يقدر يفهمها num = 1_000_000
-
بيفضل تخلي الحروف سمول لما تستخدم الحروف الرقمية.
0o
ل الاوكتل ,0x
لل الهيكساديسيمل و0b
لل بيناري. متستخدمش ال0d
في او الدسيمال . [link]# سئ num = 01234 num = 0O1234 num = 0X12AB num = 0B10101 num = 0D1234 num = 0d1234 # جيد - اسهل عشان تفصل الارقام من البداية num = 0o1234 num = 0x12AB num = 0b10101 num = 1234
-
استخدم Rdoc و طريقيتها في شرح او توثيق ال API. متسيبش سطر فاضي بين ال كومنت بلوك و ال
def
. [link] -
اخرك في السطر 80 رمز [link]
-
تجنب المسافات الزيادة اللي ملهاش لازمة [link]
-
في نهاية كل صفحة حط سطر فاضي. [link]
-
متستخدمش البلوك كومنت. مبتتسبقش بمسافة و مش سهل تتفهم زي الكومنتات العادية. [link]
# سئ =begin comment line another comment line =end # جيد # comment line # another comment line
-
استخدم
::
ك مرجع للثوابت (زي الكلاسيس و الموديل) و برضه (زيArray()
اوNokogiri::HTML()
). متستخدمش::
لل الدوال العادية . [link]# سئ SomeClass::some_method some_object::some_method # جيد SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass()
-
استخدم
def
ب اقواس لما تكون بتاخد براميتيرز. و شيلهم لو هي مش بتاخد حاجة. [link]# سئ def some_method() # اللي جوا اتشال end # جيد def some_method # اللي جوا اتشال end # سئ def some_method_with_parameters param1, param2 # اللي جوا اتشال end # جيد def some_method_with_parameters(param1, param2) # اللي جوا اتشال end
-
استخدم الاقواس حوالين الارجيومنت الدوال المستعاه خاصة لو اول ارجيومنت بيبدا ب قوس مفتوح
)
, زي هناf((3 + 2) + 1)
. [link]# سئ x = Math.sin y # جيد x = Math.sin(y) # سئ array.delete e # جيد array.delete(e) # سئ temperance = Person.new 'Temperance', 30 # جيد temperance = Person.new('Temperance', 30)
دايما شيل الاقواس في الحالات دي
-
دالة مش بتاخد ارجيومنت:
# سئ Kernel.exit!() 2.even?() fork() 'test'.upcase() # جيد Kernel.exit! 2.even? fork 'test'.upcase
-
دوال هم جزي من DSL الداخلي (زي ال, Rake, Rails, RSpec):
# سئ validates(:name, presence: true) # جيد validates :name, presence: true
-
الدوال الي ليها صفة "الكلمة" في الروبي:
class Person # سئ attr_reader(:name, :age) # جيد attr_reader :name, :age # شلنا باقي الكلاس end
ممكن نشيل الاقواس ل
-
الدوال الي ليها صفة "الكلمة" في الروبي, بس تكون مش معبرة:
# جيد puts(temperance.age) system('ls') # برضه جيد puts temperance.age system 'ls'
-
-
عرف الارجيومنت الاختيارية في النهاية. الروبي عندها شويه نتايج غير متوقعة لما تيجي تنادي دوال و يكون عندها ارجيومنت اختيارية في النص. [link]
# سئ def some_method(a = 1, b = 2, c, d) puts "#{a}, #{b}, #{c}, #{d}" end some_method('w', 'x') # => '1, 2, w, x' some_method('w', 'x', 'y') # => 'w, 2, x, y' some_method('w', 'x', 'y', 'z') # => 'w, x, y, z' # جيد def some_method(c, d, a = 1, b = 2) puts "#{a}, #{b}, #{c}, #{d}" end some_method('w', 'x') # => '1, 2, w, x' some_method('w', 'x', 'y') # => 'y, 2, w, x' some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
-
تحجنب التعريف المتوازي للمتغيرات. هو بيبقي مسموح في حالة القيمة راجعة من دالة . استخدمه مع علامة النجمة , او تبديل قيم المتغيرات من الاخر هو صعب يتقري مش زي اما تعرف كل حاجة لوحدها [link]
# سئ a, b, c, d = 'foo', 'bar', 'baz', 'foobar' # جيد a = 'foo' b = 'bar' c = 'baz' d = 'foobar' # جيد - استبدال قيم المتغيرات # استبدال قيم المتغيرات حالة خاصة , عشان هتسمحلك # انك تغير القيم بتاعت كل متغير. a = 'foo' b = 'bar' a, b = b, a puts a # => 'bar' puts b # => 'foo' # جيد - اللي الدالة بترجعه def multi_return [1, 2] end first, second = multi_return # جيد - بيستخدم علامة النجمة first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4] hello_array = *'Hello' # => ["Hello"] a = *(1..3) # => [1, 2, 3]
-
تجنب استخدام اسامي الاندرسكور في المتغيرات خلال ال التعريف الموازي . التسمية المتغيرات بالاندرس سكور بيبقي مفضل عشان السياق اللي بتوفره . هي بتبقي مهمة لما بيكون في متغير من سبلات و متعرف علي شمال التعريف. المتغير من نوع سبلات مش بيكون اندر سكور [link]
# سئ foo = 'one,two,three,four,five' # تعريف عديم النفع بيقول معلمات مش مهمة first, second, _ = foo.split(',') first, _, _ = foo.split(',') first, *_ = foo.split(',') # جيد foo = 'one,two,three,four,five' # لاندر سكور مهم عشان يوضحلك انك محتاجهم كلهم # معادا اخر واحد اللي هو اندرسكور *beginning, _ = foo.split(',') *beginning, something, _ = foo.split(',') a, = foo.split(',') a, b, = foo.split(',') # تعريف غير مهم ل متغير غير مستخدم , بس التعريف دا # بيوفر معلومات مهمة. first, _second = foo.split(',') first, _second, = foo.split(',') first, *_ending = foo.split(',')
-
مستخدمش
for
لحد ما تعرف السبب بالظبط . اغلب الوقت حاجات كتير المفروض تستخدمها بدلها .for
اتعملت علي طراز الeach
(كدا انت بتعمل مستوي جديد غير مباشر), الفكرة كلها ان الfor
مبتعملش سكوب جديد, بمعني الفاريبال اللي هتعملها جوه هتتشاف برا عادي. [link]arr = [1, 2, 3] # سئ for elem in arr do puts elem end # لاحظ هنا elem بيستخدموها برا اللوب عادي . elem # => 3 # جيد arr.each { |elem| puts elem } # هنا بقي متقدرش تستخدم elem برا البلوك elem # => NameError: undefined local variable or method `elem'
-
متستخدمش
then
لبلوك متعددة السطورif
/unless
. [link]# سئ if some_condition then # اللي هنا اتشال end # جيد if some_condition # اللي هنا اتشال end
-
حط دايما الشرط بتاع
if
/unless
علي نفس السطر. في حالات تعدد سطور الكوندشن [link]# سئ if some_condition do_something do_something_else end # جيد if some_condition do_something do_something_else end
-
بيفضل استخدام علامات ال (
:?
) عنif/then/else/end
. عشان هي اكتر انتشارا و واضحة اكتر . [link]# سئ result = if some_condition then something else something_else end # جيد result = some_condition ? something : something_else
-
متكتبش اكتر من واحدة في نفس السطر . فيما معناه مينفعش تكتب واحدة و جواها واحدة تانية . في الحالة دي بيفضل تعملها ب
if/else
. [link]# سئ some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else # جيد if some_condition nested_condition ? nested_something : nested_something_else else something_else end
-
متعملش
if x; ...
. استخدم بدالهم علامة الترناري. [link]# سئ result = if some_condition; something else something_else end # جيد result = some_condition ? something : something_else
-
استفيد من ان
if
وcase
بيرجعوا الناتج . [link]# سئ if condition result = x else result = y end # جيد result = if condition x else y end
-
استخدم
when x then ...
في حالات السطر الواحد . عشان الطريقة المختلفة بتاعتwhen x:...
اتشالت من الروبي نسخة 1.9 . [link] -
متستخدمش
when x; ...
. بص علي القاعدة اللي فاتت . [link] -
استخدم
!
بدلnot
. [link]# سئ - عشان اولوية الاقواس. x = (not something) # جيد x = !something
-
تجنب استخدام
!!
. [link]!!
بتحول القيمة ل بوليان, بس انت مش محتاج الكلام دا في الكوندشن. لو عاوز تتاكد من ان قيمتهاnil
استخدمnil?
# سئ x = 'test' # طريقة تاكيد غير واضحة if !!x # اللي هنا اتشال end # جيد x = 'test' if x # اللي هنا اتشال end
-
علامتين ال
and
وor
محظور استخدامهم . هي بتضيف قابلية بسيطة علي قرايه اسهل بس بتبقي سبب لوجود اخطاء برمجية . في البوليان اكسبريشنز دايما يفضل تستخدم&&
و||
بدلهم . استخدامif
وunless
;&&
و||
مقبولين برضه بس مش واضحين اووي . [link]# سئ # boolean expression ok = got_needed_arguments and arguments_are_valid # control flow document.save or fail(RuntimeError, "Failed to save document!") # جيد # boolean expression ok = got_needed_arguments && arguments_are_valid # control flow fail(RuntimeError, "Failed to save document!") unless document.save # تمام # control flow document.save || fail(RuntimeError, "Failed to save document!")
-
تجنب استخدام اكتر من سطر مع
?:
(the ternary operator); استخدمif
/unless
بدالهم . [link] -
بيفضل استخدام
if
/unless
لما تكون هتعملها في سطر واحد . برضه حل تاني مختلف انك تستخدم&&
/||
. [link]# سئ if some_condition do_something end # جيد do_something if some_condition # حل تاني كويس some_condition && do_something
-
ابعد عن استخدام
if
/unless
في اخر البلوك اللي بيتكون من اكتر من سطر . [link]# سئ 10.times do # multi-line body omitted end if some_condition # جيد if some_condition 10.times do # multi-line body omitted end end
-
بلاش تعمل
if
/unless
/while
/until
جوا بعض . يفضل&&
/||
لو هينفع يعني . [link]# سئ do_something if other_condition if some_condition # جيد do_something if some_condition && other_condition
-
استخدم
unless
احسن منif
لو بتتاكد من الحالة غلط . [link]# سئ do_something if !some_condition # سئ do_something if not some_condition # جيد do_something unless some_condition # حالة تانية كويسة some_condition || do_something
-
متستخدمش
unless
معelse
. اكتبها من اول و جديد وحط الحالة الايجابية الاول . [link]# سئ unless success? puts 'failure' else puts 'success' end # جيد if success? puts 'success' else puts 'failure' end
-
متستخدمش الاقواس حوالين الكونديشن [link]
# سئ if (x > 10) # اللي هنا اتشال end # جيد if x > 10 # اللي هنا اتشال end
خد بالك ان في استثناء للقاعدة دي, اللي هي safe assignment in condition.
-
متستخدمش
while/until condition do
لاكتر من سطر استخدمwhile/until
. [link]# سئ while x > 5 do # اللي هنا اتشال end until x > 5 do # اللي هنا اتشال end # جيد while x > 5 # اللي هنا اتشال end until x > 5 # اللي هنا اتشال end
-
يفضل استخدام
while/until
في حالة هتكتبها ف سطر واحد . [link]# سئ while some_condition do_something end # جيد do_something while some_condition
-
يفضل
until
عنwhile
في الحالات السلبية [link]# سئ do_something while !some_condition # جيد do_something until some_condition
-
لما تكون عاوز تعمل لوب لا نهائي استخدم
Kernel#loop
بدلwhile/until
. [link]# سئ while true do_something end until false do_something end # جيد loop do do_something end
-
استخدم
Kernel#loop
مع الbreak
بدل منbegin/end/until
اوbegin/end/while
في حالة ال post-loop [link]# سئ begin puts val val += 1 end while val < 0 # جيد loop do puts val val += 1 break unless val < 0 end
-
شيل الاقواس الخارجيه اللي حوالين الهاش . [link]
# سئ user.set({ name: 'John', age: 45, permissions: { read: true } }) # جيد user.set(name: 'John', age: 45, permissions: { read: true })
-
شيل الاقواس الخارجية للدوال الجزء من DSL . [link]
class Person < ActiveRecord::Base # سئ validates(:name, { presence: true, length: { within: 1..10 } }) # جيد validates :name, presence: true, length: { within: 1..10 } end
-
استخدم اختصار ال Proc في حالة الدوال اللي بتعمل عملية واحدة. [link]
# سئ names.map { |name| name.upcase } # جيد names.map(&:upcase)
-
يفضل استخدام
{...}
عنdo...end
في حالة البلوك اللي بيتكون من سطر. تجنب استخدام{...}
في حالة البلوك بيتكون من اكتر من سطر (تسلسل السطور دايما وحش). دايما استخدمdo...end
لل control flow و تعريف الدوال (زي اللي في Rakefiles و DSL ) ابعد عنdo...end
في التسلسل . [link]names = %w[Bozhidar Steve Sarah] # سئ names.each do |name| puts name end # جيد names.each { |name| puts name } # سئ names.select do |name| name.start_with?('S') end.map { |name| name.upcase } # جيد names.select { |name| name.start_with?('S') }.map(&:upcase)
في ناس هتجادل و تقول ان استخدام الاقواس دي {...} شكلها احسن, بس المفروض يسألوا نفسهم - هل الكود دا فعلا ينفع يتقري , و محتوي الدالة ممكن تخرجه بسهوله ؟
-
حط في اعتبارك استخدام البلوك ك ارجيومنت بدل ما تضطر تبعت بلوك من جوا. خد بالك ان دا بيأثر علي الاداء , عشان البلوك بيتحول ل بروك. [link]
require 'tempfile' # سئ def with_tmp_dir Dir.mktmpdir do |tmp_dir| Dir.chdir(tmp_dir) { |dir| yield dir } # block just passes arguments end end # جيد def with_tmp_dir(&block) Dir.mktmpdir do |tmp_dir| Dir.chdir(tmp_dir, &block) end end with_tmp_dir do |dir| puts "dir is accessible as a parameter and pwd is set: #{dir}" end
-
متحطش
return
لما ميبقاش ليها لازمة. [link]# سئ def some_method(some_arr) return some_arr.size end # جيد def some_method(some_arr) some_arr.size end
-
متستخدمش
self
لما ميبقاش ليها لازمة (هي مهمة بس لما بتستخدم self write accessor, او تعيد كتابة علامة, تسميه دالة باسم متاخد اصلا) [link]# سئ def ready? if self.last_reviewed_at > self.last_updated_at self.worker.update(self.content, self.options) self.status = :in_progress end self.status == :verified end # جيد def ready? if last_reviewed_at > last_updated_at worker.update(content, options) self.status = :in_progress end status == :verified end
-
و نتيجة لكدا, بطل تضلل الدوال بتاعتك بالمتغيرا طالما يعني الاتنين واحد . [link]
class Foo attr_accessor :options # تمام def initialize(options) self.options = options # both options and self.options are equivalent here end # سئ def do_something(options = {}) unless options[:when] == :later output(self.options[:message]) end end # جيد def do_something(params = {}) unless params[:when] == :later output(options[:message]) end end end
-
متستخدمش القيمة اللي راجعة من
=
(علامة اليساوي) في الكونديشنز طالما مش محطوطين بين اقواس. و دي حاجة مشهورة جا عند مبرمجين الروبي و شاعات بيحبوا يسموها safe assignment in condition [link]# سئ if v = array.grep(/foo/) do_something(v) # شوية كود end # جيد (MRI هيقولك انها غلط بس روبوكب هيشتغل عادي قشطة) if (v = array.grep(/foo/)) do_something(v) # شوية كود end # جيد v = array.grep(/foo/) if v do_something(v) # some code end
-
استخدم الاختصارات لما تيجي تخلي المتغير يزود علي نفسه لما يكون ينفع. [link]
# سئ x = x + y x = x * y x = x**y x = x / y x = x || y x = x && y # جيد x += y x *= y x **= y x /= y x ||= y x &&= y
-
استخدم
||=
لما تعرف المتغير بقيمة اذا كان مش متعرف . [link]# سئ name = name ? name : 'Bozhidar' # سئ name = 'Bozhidar' unless name # جيد حطلها القيمة دي 'Bozhidar' لو كانت هي ب nil او false name ||= 'Bozhidar'
-
متستخدمش ال
||=
لما تيجي تعرف boolean . (حط في اعتبارك اللي ممكن يحصل لو القيمة اللي موجودة بfalse
) [link]# سئ - القيمة هتبقي ب true حتي لو متعرفة قبل كدا ب false. enabled ||= true # جيد enabled = true if enabled.nil?
-
استخدم
&&=
مع المتغيرات اللي ممكن او مش ممكن تكون موجودة. استخدام&&=
هيغير قيمة المتغير في حالة انه فعلا موجود بس, بيوفر عليك انك تتاكد من وجوده بif
. [link]# سئ if something something = something.downcase end # سئ something = something ? something.downcase : nil # تمام something = something.downcase if something # جيد something = something && something.downcase # احسن something &&= something.downcase
-
تجنب استخدام
===
(case equality operator). من اسمها كدا باين معناها انها بترجع ل تعبير الcase
و بعيدا عن كدا هي بتخلي كودك يلغبط . [link]# سئ Array === something (1..100) === 7 /something/ === some_string # جيد something.is_a?(Array) (1..100).include?(7) some_string =~ /something/
-
متستخدمش
eql?
لما ينفع تستخدم==
. عملية المقارنة اللي بتحصل بالعلامة ديeql?
الاحتياج لاستخدامها نادر عمليا . [link]# سئ - eql? هتعمل نفس الي == بتعمله مع السترينج 'ruby'.eql? some_str # جيد 'ruby' == some_str 1.0.eql? x # eql? استخدامها هنا اكتر منطقية لو حابب تفرق بين الاعداد الصحيحة و العشرية
-
تجنب استخدام المتغيات اللي علي طراز لغة بيرل (زي
$:
,$;
, و هكذا ). هم مخفيين جدا دا غير ان استخدامهم محدش بيحبذه طالما مش في one-liner scripts . استخدم حاجة الني ادمين يفهموها و تكون موجوده في مكتبة الEnglish
. [link]# سئ $:.unshift File.dirname(__FILE__) # جيد require 'English' $LOAD_PATH.unshift File.dirname(__FILE__)
-
متسيبش مسافة فاضية ما بين اسم الدالة و الاقواس . [link]
# سئ f (3 + 2) + 1 # جيد f(3 + 2) + 1
-
دايما لما تيجي تشغل كود روبي ابعتله
-w
عشان يحذرك لو نسيت حاجة من اللي قلناهم فوق ! [link] -
متحطش دالة جوا دالة, استخدم lambda بدالها . تعريف دالتين في مكان واحد بينتج عنه دوال عندهم نفس السكوب . غير كدا كمان "الدوال اللي جوا بعض" هيتم تعريفهم من جديد في كل مرة الدالة تتعرف فيه . [link]
# سئ def foo(x) def bar(y) # اللي هنا اتشال end bar(x) end # جيد - بتعمل زيها زي اللي فاتت بس مبيحصلش تعريف جديد للدوال كل مرة . def bar(y) # اللي هنا اتشال end def foo(x) bar(x) end # برضه جيد def foo(x) bar = ->(y) { ... } bar.call(x) end
-
لو هتكتب ال lambda في سطر واحد استخدم السنتاكس الجديد بتاعها. و لو اكتر من سطر يبقي استخدم بتاع ال
lambda
اللي بياخد كذا سطر . [link]# سئ l = lambda { |a, b| a + b } l.call(1, 2) # هو صح, بس متعملش الهبل دا . l = ->(a, b) do tmp = a * 7 tmp * b / 50 end # جيد l = ->(a, b) { a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end
-
متشيلش الاقواس بتاعت البراميتر لما تيجي تعمل stabby lambda معاها براميتير. [link]
# سئ l = ->x, y { something(x, y) } # جيد l = ->(x, y) { something(x, y) }
-
شيل الاقواس لما تيجي تعمل stabby lambda منغير براميتير. [link]
# سئ l = ->() { something } # جيد l = -> { something }
-
يفصل
proc
عنProc.new
. [link]# سئ p = Proc.new { |n| puts n } # جيد p = proc { |n| puts n }
-
بيفضل
proc.call()
عنproc[]
اوproc.()
في الحالتين lambdas و procs. [link]# سئ - كانك بتستخدم Enumeration. l = ->(v) { puts v } l[1] # also bad - uncommon syntax l = ->(v) { puts v } l.(1) # جيد l = ->(v) { puts v } l.call(1)
-
حط
_
قبل اي اسم متغير او براميتر مش هتستخدمهم. ممكن برضه تستخدم_
بس (بس برضه هي مش معبرة اوي). الطريقة دي بيفهمها محلل الروبي و ادوات تانية زي روبوكب و مش هيطلعلك تحذر لعدم استخدام المتغير المعرف . [link]# سئ result = hash.map { |k, v| v + 1 } def something(x) unused_var, used_var = something_else(x) # شوية كود end # جيد result = hash.map { |_k, v| v + 1 } def something(x) _unused_var, used_var = something_else(x) # some code end # جيد result = hash.map { |_, v| v + 1 } def something(x) _, used_var = something_else(x) # some code end
-
استخدم
$stdout/$stderr/$stdin
بدل منSTDOUT/STDERR/STDIN
. عشانSTDOUT/STDERR/STDIN
ثوابت تمبتتغيش . في حين ان انت ممكن تغير قيميتهم (ممكن عاوز تغير مسار حاجة) ساعتها هيجيلك نحذير من محول الروبي . [link] -
استخدم
warn
بدلا من$stderr.puts
. بغض النظر انها بتقول المختصر المفيد . بسwarn
بتسمحلك انك تمنع التحذيرات لو عاوز . (عن طريق ان تجط مستوي التحذير ل 0 عن طريق-W0
). [link] -
يفضل استخدام
sprintf
و اللي زيهاformat
عن انك تستخدمString#%
[link]# سئ '%d %d' % [20, 10] # => '20 10' # جيد sprintf('%d %d', 20, 10) # => '20 10' # جيد sprintf('%<first>d %<second>d', first: 20, second: 10) # => '20 10' format('%d %d', 20, 10) # => '20 10' # جيد format('%<first>d %<second>d', first: 20, second: 10) # => '20 10'
-
لما تيجي تستخد الاسامي في ال format string tokens . يفضل
%<name>s
عن%{name}
عشان يحدد معلومات نوع المتغير . [link]# سئ format('Hello, %{name}', name: 'John') # جيد format('Hello, %<name>s', name: 'John')
-
يفضل استخدام
Array#join
عن اللي ممكن تخدعك شويةArray#*
لما تيجي تبعتلها سترنج . [link]# سئ %w[one two three] * ', ' # => 'one, two, three' # جيد %w[one two three].join(', ') # => 'one, two, three'
-
Use
Array()
instead of explicitArray
check or[*var]
, when dealing with a variable you want to treat as an Array, but you're not certain it's an array. [link]# سئ paths = [paths] unless paths.is_a? Array paths.each { |path| do_something(path) } # سئ (دايما هعمل array جديد) [*paths].each { |path| do_something(path) } # جيد (و مقري اكتر شوية) Array(paths).each { |path| do_something(path) }
-
استخدم ranges او
Comparable#between?
بدل ما تعمل علامات مقارنة منطقية تبقي معقدة طالما ممكن تعملها . [link]# سئ do_something if x >= 1000 && x <= 2000 # جيد do_something if (1000..2000).include?(x) # جيد do_something if x.between?(1000, 2000)
-
يفضل استخدام الدوال الجاهزة اللي زي
==
. مقارنات الارقام كويسة برضه. [link]# سئ if x % 2 == 0 end if x % 2 == 1 end if x == nil end # جيد if x.even? end if x.odd? end if x.nil? end if x.zero? end if x == 0 end
-
اتاكد من ان القيمة
nil
بشكل مباشر . طالما مش بتتعامل مع قيمة boolean . [link]# سئ do_something if !something.nil? do_something if something != nil # جيد do_something if something # جيد - لانه بيتعامل مع boolean def value_set? !@some_boolean.nil? end
-
تجنب استخدام
BEGIN
في البلوك . [link] -
متسخدمش
END
. استخدم بدالهاKernel#at_exit
. [link]# سئ END { puts 'Goodbye!' } # جيد at_exit { puts 'Goodbye!' }
-
تجنب استخدام flip-flops. [link]
-
تجنب استخدام كوندشن جوا بعض . [link] يفضل guard clause لما يكون متاح ليك تتاكد من عدم صحة البيانات . دي حاجة بتبقي موجودة ف الاول خالص بس بترجع الناتج في اقرب وقت ممكن .
# سئ def compute_thing(thing) if thing[:foo] update_with_bar(thing[:foo]) if thing[:foo][:bar] partial_compute(thing) else re_compute(thing) end end end # جيد def compute_thing(thing) return unless thing[:foo] update_with_bar(thing[:foo]) return re_compute(thing) unless thing[:foo][:bar] partial_compute(thing) end
بيفضل استخدام
next
بدل من الكوندشن جوا.# سئ [0, 1, 2, 3].each do |item| if item > 1 puts item end end # جيد [0, 1, 2, 3].each do |item| next unless item > 1 puts item end
-
بيفضل
map
عنcollect
,find
عنdetect
,select
عنfind_all
,reduce
عنinject
وsize
عنlength
. دا مش متطلب صعب; لو استخدام الحاجات الشبيهة بتحسب طريقة قراية الكود, في يحالة دي قشطة استخدمها. الدوال دي اتاخدت من smalltalk و مش موجودة في لغات البرمجة التانيه. السبب في استخدامselect
للتشجيع عنها عنfind_all
تمام زيها زيreject
و اسمها كمان معبر عن نفسه اووي. [link] -
متسخدمش
count
كبديل لsize
. في حالةEnumerable
objects تكون غير ال Array عشان في الحالة دي هيلف حوالين المجموعة كلها عشان يقولك الحجم . [link]# سئ some_hash.count # جيد some_hash.size
-
استخدم
flat_map
بدل منmap
+flatten
. الكلام دا مش لل Array اللي عمقة اكتر من 2 زي دا مثلاusers.first.songs == ['a', ['b','c']]
, استخدمmap + flatten
بدل منflat_map
.flat_map
بتسطح الاراي لعمق 1 بسflatten
بتسطحه بكل الطرق . [link]# سئ all_songs = users.map(&:songs).flatten.uniq # جيد all_songs = users.flat_map(&:songs).uniq
-
يفضل
reverse_each
عنreverse.each
عشان بعض ال classes اللي بتضمinclude Enumerable
هتوفر تطبيق فعال ليها. حتي في السواء الحالات لما مبتكنش بتدعم التطبيق دا, التطبيق الهام بتاعها اللي متاخد منEnumerable
هيعمل نفس اللي بيعملهreverse.each
. [link]# سئ array.reverse.each { ... } # جيد array.reverse_each { ... }
الحاجات الوحيدة الصعبة في البرمجة هي cache invalidation و تسمية الاشياء.
-- Phil Karlton
-
الاسامي لازم تكون بالانجليزي . [link]
# سئ - اسامي لا تدعم الاسكي كود заплата = 1_000 # سئ - كلمة بلغارية مكتوبة بحروف لاتينية zaplata = 1_000 # جيد salary = 1_000
-
استخدم
snake_case
للرموز , و المتغيرات و الدوال. [link]# سئ :'some symbol' :SomeSymbol :someSymbol someVar = 5 var_10 = 10 def someMethod # some code end def SomeMethod # some code end # جيد :some_symbol some_var = 5 var10 = 10 def some_method # some code end
-
متفصلش الارقام عن الحروف في الرموز و الممتغيرات و الدوال . [link]
# سئ :some_sym_1 some_var_1 = 1 def some_method_1 # شوية كود هنا end # جيد :some_sym1 some_var1 = 1 def some_method1 # شوية كود هنا end
-
استخدم
CamelCase
في حالات classes و modules. (خلي الاختصارات زي دي HTTP, RFC, XML كبيرة زي ما هي .) [link]# سئ class Someclass # شوية كود هنا end class Some_Class # شوية كود هنا end class SomeXml # شوية كود هنا end class XmlSomething # some code end # جيد class SomeClass # شوية كود هنا end class SomeXML # شوية كود هنا end class XMLSomething # شوية كود هنا end
-
استخدم
snake_case
في يتسمية الملفات, زيhello_world.rb
. [link] -
استخدم
snake_case
لتسميه المجلدات زيlib/hello_world/hello_world.rb
. [link] -
احرص انك يكو عندك class/module وحيد جوا ملف الكود الواحد . سمي الملف زيه زي اسم ال class/module بس الفرق حول الاسم من CamelCase ل snake_case. [link]
-
استخدم
SCREAMING_SNAKE_CASE
لجميع الثوابت التانية . [link]# سئ SomeConst = 5 # جيد SOME_CONST = 5
-
اسامي الدوال التأكيد ( اللي بترجع قيمة boolean) المفروض تنتهي بعلامة استفهام. مثال علي كدا (زي
Array#empty?
) . الدوال بقي اللي مش بترجع القيم دي مش المفروض تخلص بعلامة استفهام. [link] -
تجنب بدء الدوال التأكيديه بالافعال المساعدة اللي زي
is
,does
, اوcan
. الكلمات دي تعتبر زيادة و ملهاش لازمة بالنسبة للاسلوي اللي اساس لغة الروبي كاتب بيه دوال ال boolean, اللي هي مثلا زيempty?
وinclude?
. [link]# سئ class Person def is_tall? true end def can_play_basketball? false end def does_like_candy? true end end # جيد class Person def tall? true end def basketball_player? false end def likes_candy? true end end
-
اسامي الدوال الخطيرة (الدوال اللي بتعدل
self
او ال arguments),exit!
(مبترجعش القيمة النهائية زي ماexit
بتعمل و هكذا ) المفروض تنتهي بعلامة تعجب لو في نسخة امنة من الدالة دي . [link]# شئ مفيش نسخة تانيه 'امنة' منها class Person def update! end end # جيد class Person def update end end # جيد class Person def update! end def update end end
-
عرف الدوال اللي منغي علامة استفهام (الامنة) من نفس الناحية او الطريقة اللي مكتوب بيها اللي بعلامة تعجب (الخطرة) لو ممكن يعني . [link]
class Array def flatten_once! res = [] each do |e| [*e].each { |f| res << f } end replace(res) end def flatten_once dup.flatten_once! end end
-
لما تيجي تعرف علامات ال binary سمي البرامتر
other
(<<
و[]
دول حالات خاصة للحالة دي, خاصة ان دول ليهم دلالات مختلفة). [link]def +(other) # body omitted end
الكود الجيد بيرجع للشرح الكويس بتاعه . لما تيجي تحط تعليق, أسال نفسك , "ازاي اقدر احسن الحتة دي من الكود عشان متكنش محتاجة تعليق يوضحها" حسن كودك و بعدين اشرحه مدا هيكون اوضح كتير.
-- Steve McConnell
-
خلي الكود بتاعك بيشح نفسه و انت مش هتحتاج تقرأ الكلام اللي جاي كله , بجد! [link]
-
Write comments in English. [link]
-
استخدم مسافة واحد فاضية ما بين الرمز دا
#
و الكلام اللي بعده اللي مكتوب في الكومنت. [link] -
التعليقات اللي اكتر من كلمة بتكبر الحروف الاولي منها و بتستخدم علامات الترقيم. استخدم [مسافة واحدة](https://en.wikipedia.org/wiki/Sentence_spacing. [link]
-
بلاش التعليقات اللي ملهاش لازمة. [link]
# سئ counter += 1 # Increments counter by one.
-
حدث التعليقات اول بأول, لان التعليقات القديمة اسواء من عدمه بكتيير. [link]
الكود الحلو زي النكتة الحلوة: مش محتاج تشرحها
— المبرمج القديم maxim, خلال Russ Olsen
- تجنب كتابة تعليقات تشرح الكود الوحش . عدل الكود بتاعك عشان يشرح نفسه . . ("Do or do not—there is no try." Yoda) [link]
-
التعليقات التوضيحيه عادة بتكتب في سطر فوق بالظبط الكود اللي عاوز يتوضح. [link]
-
الكلمات اللي بتميزها لازم يجي بعدها نقطتين فوق بعض و مسافة, و بعدها الملاحظة اللي بتفسر المشكلة. [link]
-
لو المشكلة محتاجة اكتر من سطر عشان تتشرح , اشرحها في السطور اللي بعدها و سيب 3 مسافات بعد
#
(مسافة واحدة كدا كدا بتبقي محطوطه, الاتنين التانين معناهم التكملة). [link]def bar # FIXME: This has crashed occasionally since v3.2.1. It may # be related to the BarBazUtil upgrade. baz(:quux) end
-
في الحالات اللي بتكون الحالة واضحة اووي و اي توضيح هيعتبر اعادة, التعليقات التوضيحية بتتساب اخر السطر منغير تفسير . الموضوع دا استثناء و مش قاعدة. [link]
def bar sleep 100 # OPTIMIZE end
-
استخدم
TODO
عشان تسيب ملاحظة علي خاصية او ميزة متعملوش و المفروض يتعملوا بعدين. [link] -
استخدم
FIXME
ك ملاحظة ل كود بايظ محتاج يتصلح . [link] -
استخدم
OPTIMIZE
ك ملاحظة لل بطء او عدم فاعلية الكود, اللي مكن يعمل مشاكل ف الاداء بعدين. [link] -
استخدم
HACK
ك ملاحظة ل حاجة اتكروتت و المفروض تتظبط . [link] -
استخدم
REVIEW
ك ملاحظة ل حاجة محتاجة تأكيد انها كدا بتعمل المطلوب. علي سبيل المثال:REVIEW: Are we sure this is how the client does X currently?
[link] -
استخدم كلمات مختلفة للتعليقات التوضيحية لو شايف انها كدا هتوضح اكتر, بس اتاكد انك شارحها في مكان وي ال
README
بتاع المشروهع او حاجة زيه . [link]
-
حط ال التعليقات السحرية فوق كل الاكواد و الشرح. هي بس بتتحط لو انت محتاجهم في ملف الكود بتاعك [link]
# جيد # frozen_string_literal: true # Some documentation about Person class Person end # سئ # Some documentation about Person # frozen_string_literal: true class Person end
# جيد #!/usr/bin/env ruby # frozen_string_literal: true App.parse(ARGV) # سئ # frozen_string_literal: true #!/usr/bin/env ruby App.parse(ARGV)
-
استخدم تعليق سحري وحيد ف كل سطر لو محتاج اكتر من واحد. [link]
# جيد # frozen_string_literal: true # encoding: ascii-8bit # سئ # -*- frozen_string_literal: true; encoding: ascii-8bit -*-
-
فرق ما بين التعليقات السحرية و الاخري ب سطر واحد فاضي . [link]
# جيد # frozen_string_literal: true # Some documentation for Person class Person # شوية كود هنا end # سئ # frozen_string_literal: true # Some documentation for Person class Person # Some code end
-
استخدم شكل متناسق لما تيجي تعرف ال class . [link]
class Person # extend و include بيبقوا الاول extend SomeModule include AnotherModule # inner classes CustomError = Class.new(StandardError) # و بعد كدا الثوابت SOME_CONSTANT = 20 # بعد كدا بنحط العناصر attr_reader :name # بعد كدا بتحط اي macro لو موجود validates :name # و بعدهم بقي يجي دور ال public class methods def self.some_method end # الدالة ةالتعريفية بتكون ما بين ال class methods و ال instance methods def initialize end # بيجي بعدها public instance methods def some_method end # الوال protected و private بيتجمعوا قبل كلمة end protected def some_protected_method end private def some_private_method end end
-
قسم اكتر من mixins في اكتر من سطر . [link]
# سئ class Person include Foo, Bar end # جيد class Person # اكتر من mixins انفصلوا في سطور مختفلة include Foo include Bar end
-
تجنب تحط classes بتتكون من اكتر من سطر جوا classes تانية. حطه جوا بعض في كل واحدة في الملف بتاعها و خلي اسم الفايل نفس اسم الكلاس اللي جوا. [link]
# سئ # foo.rb class Foo class Bar # 30 دالة جوا end class Car # 20 دالة جوا end # 30 دالة جوا end # good # foo.rb class Foo # 30 دالة جوا end # foo/bar.rb class Foo class Bar # 30 دالة جوا end end # foo/car.rb class Foo class Car # 20 دالة جوا end end
-
عرف (و اعد فتح) namespaced classes و modules عن طريق ال explicit nesting. عن طريق استخدام علامة ال scope resolution هيؤدي ل استدعاء مفاجي لل ثوابت طبقا ل الروبي lexical scoping, و اللي بتعتمد علي طريقة تعريف جوا عض في لحظة التعريف. [link]
module Utilities class Queue end end # سئ class Utilities::Store Module.nesting # => [Utilities::Store] def initialize # بيرجع ل اعلي مستوي عشان ال ::Queue class # مش في التسلسل الداخلي لل Utilities @queue = Queue.new end end # جيد module Utilities class WaitingList Module.nesting # => [Utilities::WaitingList, Utilities] def initialize @queue = Queue.new # بترجع ل Utilities::Queue end end end
-
بيفضل انه يكون modules عن classes لما بتكون عبارة عن دوال بس. بنستخددم ال classes بس لما بيكون في سبب منطقي اننا نعمل نسخة منها. [link]
# سئ class SomeClass def self.some_method # اللي هنا اتشال end def self.some_other_method # اللي هنا اتشال end end # جيد module SomeModule module_function def some_method # اللي هنا اتشال end def some_other_method # اللي هنا اتشال end end
-
يفضل استخدام
module_function
عنextend self
لما تحب تحول نسخة الدوال بتاعت ال module ب دوال بتاعت class. [link]# سئ module Utilities extend self def parse_something(string) # شوية حاجات هنا end def other_utility_method(number, string) # شوية حاجات هنا end end # جيد module Utilities module_function def parse_something(string) # شوية حاجات هنا end def other_utility_method(number, string) # شوية حاجات هنا end end
-
لما تيجي تصمم التسلسل الهرمي للكلاس اتاكد انها بتتبع Liskov Substitution Principle. [link]
-
حاول تخلي الكلاس بتاعتك زي دي SOLID as علي اد ما تقدر. [link]
-
دايما خالي عندك دالة ال
to_s
متعرفة في الكلاسس بتوعك اللي بعبروا عن ال domain objects. [link]class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end def to_s "#{@first_name} #{@last_name}" end end
-
استخدم
attr
و الحاجات اللي زيه عشان تعرف trivial accessors او mutators [link]# سئ class Person def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end def first_name @first_name end def last_name @last_name end end # جيد class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end
-
في حالة استخدام accessors and mutators, تجنب اسامي الدوال اللي بتبداء ب
get_
وset_
. دا طريقة الروبي لتعريف الاسامي بتعتهم يا بتتب اسمه لو عاوز تاخد القيمة. يا بتحط اسمه و بعده علامة يساوي لو هتديله قيمه كدهوattr_name=
. [link]# سئ class Person def get_name "#{@first_name} #{@last_name}" end def set_name(name) @first_name, @last_name = name.split(' ') end end # جيد class Person def name "#{@first_name} #{@last_name}" end def name=(name) @first_name, @last_name = name.split(' ') end end
-
تجنب استخدام ال
attr
, استخدم بدالهاattr_reader
وattr_accessor
[link]# سئ - بتعرف عنصر واحد (اساسا اهملت من بعد نسخة روبي 1.9) attr :something, true attr :one, :two, :three # behaves as attr_reader # جيد attr_accessor :something attr_reader :one, :two, :three
-
حط في اعتبارك استخدام
Struct.new
, هي هتعرفلك trivial accessors و constructor و comparison operators. [link]# جيد class Person attr_accessor :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end # احسن Person = Struct.new(:first_name, :last_name) do end
-
متمددش حاجة انت عرفتها بال
Struct.new
. لانه حاجة ملهاش لازمة و هتجيبلك شوية اخطاء لو الملف اتطلب في اكتر من وقت. [link]# سئ class Person < Struct.new(:first_name, :last_name) end # جيد Person = Struct.new(:first_name, :last_name)
-
حط في اعتبارك تعريف دوال تعريفيه زيادة . عشان تسمحلك تعرف نسخ تانيه من الكلاس بشكل اكتر منطقية . [link]
class Person def self.create(options_hash) # اللي هنا اتشال end end
-
بيفضل duck-typing عن الوراثة. [link]
# سئ class Animal # abstract method def speak end end # extend superclass class Duck < Animal def speak puts 'Quack! Quack' end end # extend superclass class Dog < Animal def speak puts 'Bau! Bau!' end end # جيد class Duck def speak puts 'Quack! Quack' end end class Dog def speak puts 'Bau! Bau!' end end
-
تجنب استخدام متغيرات الكلاس (
@@
) عشان التداخل اللي بيحصل في الوراثة. [link]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'
زي ما انت شايف كل ال classes في الترتيب بتاعم بيتشاركوا في واحد class variable. دايما بيفضل استخدام ال Class instance variables عن ال class variables.
-
عرف المستوي المناسب للدوال (
private
,protected
) شبه بعض في الاستخدام . متسيبش كل حاجةpublic
(تقدر تقول هو دا اساسا الوضع الافتراضي). علي اي حال احنا بنكتب روبي دلوقتي, مش بايثون. [link] -
المسافة اللي في الاول بتاعت الدوال دي
public
وprotected
وprivate
مهمة زيها زي التعريف . سيب سطر فاضي قبل الدوال دي قبلها و بعدها . عشان توضح ان دا الستوي بتاع كل الدوال اللي تحتها . [link]class SomeClass def public_method # شوية كود هنا end private def private_method # شوية كود هنا end def another_private_method # شوية كود هنا end end
-
استخدم
def self.method
عشان تعرف ال class methods. دا بيخلي الكون احسن لو عاوز تحسنه لان اسم الكلاس مش بيتكرر . [link]class TestClass # سئ def TestClass.some_method # body omitted end # جيد def self.some_other_method # body omitted end # ممكن جدا و بتبقي كويسة # لما تكون مضطر تعرف اكتر من دالة. class << self def first_method # اللي هنا اتشال end def second_method_etc # اللي هنا اتشال end end end
-
يفضل
alias
عند استعارة الدوال في حال ال lexical class scope حيث ان اخذ قرار لself
في حالة زي دي يعتبر برضه lexical, و ف حين ان التواصل بتاعها اوضح للمستخدم و برضه مش هيحصل تغير لل aliase في الوقت اللي الكود شغال فيه او عن طريق اي subclass الا لو اتعمل بشكل مباشر.
[link]
class Westerner
def first_name
@names.first
end
alias given_name first_name
end
و زي ما alias
شبهة, و هي كلمة مستخدمة
بيفضل انك تستخدمها كدا alias foo bar
مش كدا alias :foo :bar
.
برضه خليك عارف ازاي الروبي بتتعامل مع ال aliases و ال inheritance: ال alias بيرجع للدالة اللي اتعرفت وقت ما هو بص عليها . مبتتغير بعد كدا لوحدها.
class Fugitive < Westerner
def first_name
'Nobody'
end
end
في المثال دا, Fugitive#given_name
هيفضل يستخدم الدالة الاصلية من دي Westerner#first_name
,
مش دي Fugitive#first_name
.
عشان تعيد كتابة التصرف بتاع Fugitive#given_name
, انت لازم تعرفها من جديد في ال derived class .
class Fugitive < Westerner
def first_name
'Nobody'
end
alias given_name first_name
end
-
دايما استخدم
alias_method
لما تعمل aliasing من دوال ال modules او classes او singleton classes في وقت تشغيل الكود. حيث ان تصرف الalias
هيعمل حاجة غير متوقعه في الحالات دي. [link]module Mononymous def self.included(other) other.class_eval { alias_method :full_name, :given_name } end end class Sting < Westerner include Mononymous end
-
لما دوال ال class (او module) يستخدموا دوال تانيين, شيل
self
من البداية او اسمها متبوع ب.
لما تيجي تستخدم زيها من مكان تاني. دا حاجة معتادة في ال "service classes" او في الحاجات اللي زيها المبدا دا بيقلل الاعادة في ال classes. [link]class TestClass # سئ - هتتعب لما تيجي تغير اسم او تشيل الدالة def self.call(param1, param2) TestClass.new(param1).call(param2) end # سئ - اطول من الازم. def self.call(param1, param2) self.new(param1).call(param2) end # جيد def self.call(param1, param2) new(param1).call(param2) end # ...دوال تانية... end
-
بيفضل
raise
عنfail
في الاستثنائات [link]# سئ fail SomeException, 'message' # جيد raise SomeException, 'message'
-
متحددش نوع الاستثناء
RuntimeError
و تبعته مع نسخة الraise
. [link]# سئ raise RuntimeError, 'message' # جيد - المفرد بيعتبر RuntimeError منغير ما تكتبه . raise 'message'
-
بيفضل فصل الاستثناء و الرسالة بتاعته منفصلين لما تستخدم
raise
, بدل ما تعمل نسخة منه . [link]# سئ raise SomeException.new('message') # خد بالك مفيش طريقة تعرف تعمل بيها كدا `raise SomeException.new('message'), backtrace`. # جيد raise SomeException, 'message' # شغاله برضه مع `raise SomeException, 'message', backtrace`.
-
متسخدمش return مع بلوك ال
ensure
. لو انت استخدمت return جوا دالة ف بلوك الensure
, الاولويه هتكون ليها عن اي استثناء, و الدالة هترجع قيمة كأن مكنش في اي استثناء . الاستثناء ببساطة كانه محصلش. [link]# سئ def foo raise ensure return 'very bad idea' end
-
Use implicit begin blocks where possible. [link]
# سئ def foo begin # الكود اللي شغال هنا rescue # الكود اللي بتعامل مع الاخطاء end end # جيد def foo # الكود اللي شغال هنا rescue # الكود اللي بتعامل مع الاخطاء end
-
خفف انتشار بلوكات
begin
عن طريق استخدام contingency methodsMitigate the proliferation of
begin
blocks by using contingency methods (مصطلح اللي عمله واحد اسمه Avdi Grimm). [link]# سئ begin something_that_might_fail rescue IOError # handle IOError end begin something_else_that_might_fail rescue IOError # handle IOError end # جيد def with_io_error_handling yield rescue IOError # handle IOError end with_io_error_handling { something_that_might_fail } with_io_error_handling { something_else_that_might_fail }
-
متمنعش استخدام الاستثنائات. [link]
# سئ begin # الاستثنائات بتتحط هنا rescue SomeError # ال rescue هنا بقي ملهوش لازمة end # سئ do_something rescue nil
-
تجنب استخدام
rescue
في شكلة الجديد. [link]# سئ - دي بتمسك الاستثنائات اللي زي StandardError و اللي زيها. read_file rescue handle_error($!) # جيد - دي بتمسك الاستثنائات فقط اللي زي Errno::ENOENT و اللي زيها. def foo read_file rescue Errno::ENOENT => ex handle_error(ex) end
-
متستخدمش الاستثنائات مع ال flow of control. [link]
# سئ begin n / d rescue ZeroDivisionError puts 'Cannot divide by 0!' end # جيد if d.zero? puts 'Cannot divide by 0!' else n / d end
-
تجنب استخدام الكلاس دي
Exception
. عشان دي هتؤدي انها تقفل البرنامج لانها بتستخدمexit
. بيتطلب منك انك تنفهي العمليه بkill -9
. [link]# سئ begin # calls to exit and kill signals will be caught (except kill -9) exit rescue Exception puts "you didn't really want to exit, right?" # التعامل مع الاستثناء end # جيد begin # تعامل اعمي مع الاستثنائات بيندرج تحت StandardError. # و معظم المبرمجين مبتعتبروش استثناء. rescue => e # التعامل مع الاستثناء end # برضه جيد begin # الاستثناء بيبقي هنا rescue StandardError => e # التعامل مع الاستثناء end
-
حط استثناءات محدده أعلى سلسلة ال exceptions ، وإلا مش هتتنفذ. [link]
# سئ begin # شوية كود هنا rescue StandardError => e # التعامل هنا rescue IOError => e # دي عمرها ما هتشتغل end # جيد begin # شوية كود هنا rescue IOError => e # طريقة تعامل rescue StandardError => e # طريقة تعامل end
-
الحاجات اللي الكود بتاعك معتمد عليها و بتيجي من برا حطها جوا
ensure
بلوك. [link]f = File.open('testfile') begin # .. process rescue # .. handle error ensure f.close if f end
-
طالما ممكن تستخدم دوال بتنضف بعد ما بتخلص لوحدها و بتتقفل لوحدها استخدمها. [link]
# سئ - انت محتاج تقفل الكود بنفسك f = File.open('testfile') # some action on the file f.close # جيد - الملف بيتقل لوحده بعد ما تخلص. File.open('testfile') do |f| # some action on the file end
-
بيفضل انك تستخدم استثناء من اللي موجودين ف ال standard library بدل ما تعمل واحد جديد من الاول. [link]
-
بيفضل صنع ال array و hash بالعلامات اللي بترمز ليهم ( دا طالما انت مش عاوز تبعتلهم بارمتر هيساعد في صنعهم, بس كدا). [link]
# سئ arr = Array.new hash = Hash.new # جيد arr = [] arr = Array.new(10) hash = {} hash = Hash.new(0)
-
بيفضل استخدام
%w
لما تيجي تعرف array متكون من كلمات ( الكلمات الي منغير فواصل من جوا مع عدم وجود اي رموز مميزة). طبق القاعدة دي مع ال array اللي بعنصرين او اكتر. [link]# سئ STATES = ['draft', 'open', 'closed'] # جيد STATES = %w[draft open closed]
-
ط بيفضل استخدام
%i
لما تيجي تعرف array بيتكون من symbols (و انت مش محتاج تصلح في نسخة الروبي 1.9). طبق القاعدة دي علي ال arrays اللي بتتكون من عنصر. [link]# سئ STATES = [:draft, :open, :closed] # جيد STATES = %i[draft open closed]
-
تجنب انك تحط اخر فاصلة ف ال
Array
اوHash
, خاصة لما يكون متعرف في اكتر من سطر . [link]# سئ - اسهل في انك تشيل/تحط/تحرك عنصر بس برضه مش مفضلة. VALUES = [ 1001, 2020, 3333, ] # سئ VALUES = [1001, 2020, 3333, ] # جيد VALUES = [1001, 2020, 3333]
-
تجنب تسيب مسافات كبيرة جوا فاضية ف ال array. [link]
arr = [] arr[100] = 1 # دلوقتي بقي عندك array كبير من حاجات فاضية
-
لما تيجي تتعامل مع اول و اخر عنصر في ال array بفضل استخدام
first
اوlast
عن استخدام[0]
او[-1]
. [link] -
استخدم ال
Set
بدلا من الArray
لما تكون بتتعامل مع عناصر فريدة مبتتكررش. الSet
مبنية بحيث انها تشيل مجموعات مش مترتبة بس برضه مش متكررة. دا عبارة عن نوع هجين من الArray
في علاماته و الHash
في سرعته . [link] -
بيفضل استخدام ال symbols بدل ال strings ك hash keys. [link]
# سئ hash = { 'one' => 1, 'two' => 2, 'three' => 3 } # جيد hash = { one: 1, two: 2, three: 3 }
-
تجنب استخدام mutable objects ك hash keys. [link]
-
استخدم طريقة نسخة الروبي 1.9 لما تيجي تعرف hash و يكون ال keys بتاعته symbols. [link]
# سئ hash = { :one => 1, :two => 2, :three => 3 } # جيد hash = { one: 1, two: 2, three: 3 }
-
متنوعش في الروبي 1.9 لما تيجي تعرف ال hash بين الطريقة العادية و ال hash rockets في نفس ال hash لما يكون عندك keys مش symbols و محتاجين hash rockets عشان يتعرفوا . [link]
# سئ { a: 1, 'b' => 2 } # جيد { :a => 1, 'b' => 2 }
-
استخدم
Hash#key?
بدلا منHash#has_key?
و استخدمHash#value?
بدلا منHash#has_value?
.. [link]# سئ hash.has_key?(:test) hash.has_value?(value) # جيد hash.key?(:test) hash.value?(value)
-
استخدم
Hash#each_key
بدلا منHash#keys.each
وHash#each_value
بدلا منHash#values.each
. [link]# سئ hash.keys.each { |k| p k } hash.values.each { |v| p v } hash.each { |k, _v| p k } hash.each { |_k, v| p v } # جيد hash.each_key { |k| p k } hash.each_value { |v| p v }
-
استخدم
Hash#fetch
لما تتعامل مع hash keys المفﻻوض تتعرض. [link]heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' } # سئ - لو في غلط مش هيبان heroes[:batman] # => 'Bruce Wayne' heroes[:supermann] # => nil # جيد - fetch هتعمل KeyError ما متلاقي ال key. heroes.fetch(:supermann)
-
حط قيمة افتراضيه لل hash keys عن طريق
Hash#fetch
بدل ما تستخدم علامة منطقية . [link]batman = { name: 'Bruce Wayne', is_evil: false } # سئ - لو احنا استخدمنا علامة ال || مع ال false مش هيجيلنا الناتج اللي متوقعينه. batman[:is_evil] || true # => true # جيد - هتشتغل صح مع القيمة ال false. batman.fetch(:is_evil, true) # => false
-
يفضل استخدام البلوك بدل من القيمة الافتراضيه في
Hash#fetch
لو الكود بتاعك اللي عدلته ممكن يكون مهم او مؤثر . [link]batman = { name: 'Bruce Wayne' } # سئ - لو استخدمنا القيمة الافتراضية # ممكن جدا تبظء البرنامج لو اتعملت كذا مرة . batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers استخدام مكلف # good - blocks are lazy evaluated, so only triggered in case of KeyError exception batman.fetch(:powers) { obtain_batman_powers }
-
استخدم
Hash#values_at
لما تحب ترجع مجموعة من القيم المتتالية. [link]# سئ email = data['email'] username = data['nickname'] # جيد email, username = data.values_at('email', 'nickname')
-
اعتمد علي الحقيقة اللي عملتها روبي نسخة 1.9 ان ال hash مترتب. [link]
-
متعدلش في مجموعة و انت بتلف عليها. [link]
-
لما تيجي تستعمل عنصر من عناصر المجموعة, تجنب انك تستخدم دي
[n]
. لما بتسخدم حاجة بديلة من دا بيحميك من انك تنادي[]
علnil
. [link]# سئ Regexp.last_match[1] # جيد Regexp.last_match(1)
-
لما تيجي نوفر accessor ل للمجوعة, احمي المستخدم من انه يناديها علي
nil
قبل ما يبدا يتعامل مع العنصر بتاع المجموعة. [link]# سئ def awesome_things @awesome_things end # جيد def awesome_things(index = nil) if index && @awesome_things @awesome_things[index] else @awesome_things end end
-
استخدم
Integer
لو عاوز تتاكد من انه رقم صحيح او لا. عشانFixnum
تعتبر platform-dependent و التاكد من خلالها هيرجع قيم مختلفة في حالات الاجهزة نوعها 32-bit و 64-bit [link]timestamp = Time.now.to_i # سئ timestamp.is_a? Fixnum timestamp.is_a? Bignum # جيد timestamp.is_a? Integer
-
بيفضل استخدام ال ranges لو كنت عاوز تطلع رقم عشوائي بدل ما تستخدم فارق الاعداد الصحيحة, عشان هتبقي اوضح تخيل اللي ال الزهر بيعمله. [link]
# سئ rand(6) + 1 # جيد rand(1..6)
-
-
بيفضل انك تستخدم string interpolation و string formatting بدل من string concatenation: [link]
# سئ email_with_name = user.name + ' <' + user.email + '>' # جيد email_with_name = "#{user.name} <#{user.email}>" # جيد email_with_name = format('%s <%s>', user.name, user.email)
-
اعتمد علي نوع واحد لم تيجي تعرف ال string بالنسبة لل quoting style. هو في اتنين مشهورين في مجتمع الروبي, و الاتنين بنعتبرهم كويسين و هو single quotes (الاختيار أ) و التاني double quotes (الاختيار ب). [link]
-
(الاختيار أ ) بيفضل ال single-quoted لما مبتكنش محتاج تعمل string interpolation او الرموز الخاصة زي
\t
,\n
,'
, وهكذا.# سئ name = "Bozhidar" # جيد name = 'Bozhidar'
-
(الاختيار ب) يفضل ال double-quotes طالما ال string بتاعك مفيهوش
"
او اي رموز مميزة عاوز تظهرها علي اصلها.# سئ name = 'Bozhidar' # جيد name = "Bozhidar"
طريقةالتعامل مع ال string في الدليل دا بتفضل الاختيار الاول.
-
-
متستخدمش طريقة تعريف الحروف دي
?x
. من ساعة روبي 1.9 و هي مستغني عنها بس?x
هتتحول ك'x'
(عبارة عن string متعرف ب حرف واحد جواه). [link]# سئ char = ?c # جيد char = 'c'
-
متسيبش
{}
دي حوالين ال instance و global variables عشان يتعمله interpolated جوا string. [link]class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end # سئ - صح, بس مش واضحة def to_s "#@first_name #@last_name" end # جيد def to_s "#{@first_name} #{@last_name}" end end $global = 0 # سئ puts "$global = #$global" # جيد puts "$global = #{$global}"
-
متستخدمش Object#to_s` علي ال interpolated objects. هي بتتعمل لوحدها كدا. [link]
# سئ message = "This is the #{result.to_s}." # جيد message = "This is the #{result}."
-
تجنب استخدام
String#+
لما تكون محتاج تكون داتا كبيرة. في حين انك ممكن تستخدمString#<<
للوصل بينهم في مكان هي دايما اسرع منString#+
اللي بتخليك كل مرة تصنع string object جداد. [link]# سئ html = '' html += '<h1>Page title</h1>' paragraphs.each do |paragraph| html += "<p>#{paragraph}</p>" end # جيد و اسرع html = '' html << '<h1>Page title</h1>' paragraphs.each do |paragraph| html << "<p>#{paragraph}</p>" end
-
متستخدمش
String#gsub
في الحالات اللي ممكن تستخدم فيهاحاجات تانية اسرغ و متخصصه اكتر. [link]url = 'http://example.com' str = 'lisp-case-rules' # سئ url.gsub('http://', 'https://') str.gsub('-', '_') # جيد url.sub('http://', 'https://') str.tr('-', '_')
-
لما تيجي تستخدم heredocs لل strings اللي بتاخد اكتر من سطر, حط في اعتبارك انها بتحافظ علي المسافة الزيادة. دا تطبيق كويس عشان تبطل تحط مسافات ملهاش لازمة. [link]
code = <<-END.gsub(/^\s+\|/, '') |def test | some_method | other_method |end END # => "def test\n some_method\n other_method\nend\n"
-
استخدم طريقة الروبي 2.3 لل squiggly heredocs بشكل الطف ل strings اللي تاخد كذا سطر [link]
# سئ - بيستخدم Powerpack String#strip_margin code = <<-RUBY.strip_margin('|') |def test | some_method | other_method |end RUBY # برضه سئ code = <<-RUBY def test some_method other_method end RUBY # جيد code = <<~RUBY def test some_method other_method end RUBY
-
استخدم محددات تكون معبرة في ال heredocs . محددتات تكون بتضيف معلومات قيمة عن المحتوي بتاع ال heredocs, و ك حاجة حلوة تانية بعد محررات النصوص تقدر تسيب علامة علي الكود اللي خلال ال heredocs لو هو مستخدم المحددات صح . [link]
# سئ code = <<~END def foo bar end END # جيد code = <<~RUBY def foo bar end RUBY # good code = <<~SUMMARY An imposing black structure provides a connection between the past and the future in this enigmatic adaptation of a short story by revered sci-fi author Arthur C. Clarke. SUMMARY
-
بيفضل
Time.now
عنTime.new
لما تكون عاوز تاخد الوقت الحالي للنظام. [link] -
متسخدمش
DateTime
الا لو كنت عاوز تاخد التقويم التاريخي معاك -- و لو عملت كدا حدد بصراحة الstart
ارجيومنت عشان توضح بالظبط انت عاوز ايه . [link]# سئ - استخدم DateTime للوقت بتاع دلوقتي DateTime.now # جيد - استخدم Time للوقت دلوقتي Time.now # سئ - استخدم DateTime للتوقيتات العصرية DateTime.iso8601('2016-06-29') # جيد - استخدم Date للتوقيتات العصرية Date.iso8601('2016-06-29') # جيد - استخدم DateTime و معاها start argument للتاريخ القديم. DateTime.iso8601('1751-04-23', Date::ENGLAND)
بعض الناس, لما بتواجهم مشكلة بيفكر, "انا عارف, انا هستخدم التعبيرات العادية" دلوقتي بقي عنده مشكلتين.
-- Jamie Zawinski
-
متستخدمش التعبيرات العادية لو بتدور علي string في نص عادي:
string['text']
[link] -
لتكوين بسيط انت ممكن تستخدم regexp مباشرة خلال string index. [link]
match = string[/regexp/] # هات المحتوي اللي بيساوي ال regexp first_group = string[/text(grp)/, 1] # هات امحتي من المجموعة الي ماسكها string[/text (grp)/, 1] = 'replace' # string => 'text replace'
-
استخدم مجموعات ال non-capturing لما متكونش هتستخدم ناتج ال captured. [link]
# سئ /(first|second)/ # جيد /(?:first|second)/
-
متستخدمش متغيرات ال cryptic Perl-legacy للدلالة علي اخر مجموعة بتطابق ال regexp (
$1
,$2
, وهكذا). استخدمRegexp.last_match(n)
بدلها. [link]/(regexp)/ =~ string ... # سئ process $1 # جيد process Regexp.last_match(1)
-
تجنب استخدام المجموعات المرقمة عشان بيبقي صعب متابعة محتواها. ممكن تسمي مجموعات متسمية احسن منها . [link]
# سئ /(regexp)/ =~ string # some code process Regexp.last_match(1) # جيد /(?<meaningful_var>regexp)/ =~ string # some code process meaningful_var
-
ال Character classes عندهم بس قليل من الحروف اللي المفروض تهتم بيهم: هم
^
,-
,\
,]
, يبقي متتجنبش.
او الاقواس في[]
. [link] -
خد بالك من
^
و$
عشان هم بيعبروا عن بداية/نهاية السطر . مش بتاعت ال string. لو عاوز تطتبق ال string كله استخدم\A
و\z
(متخليش\Z
اللي هي بتساوي/\n?\z/
). [link]string = "some injection\nusername" string[/^username$/] # متطابقة string[/\Ausername\z/] # لا تطابق
-
استخدم
x
في علامات ال regexps المعقدة . دا هيخليك تعرف تقري كويس و تقدر تحط شوية تعليقات مفيدة. بس خد بالك المسافات مبتتحسبش.ً [link]regexp = / start # some text \s # white space char (group) # first group (?:alt1|alt2) # some alternation end /x
-
للتبديل المعقد
sub
/gsub
ممكن تستخدم مع بلوك او هاش. [link]words = 'foo bar' words.sub(/f/, 'f' => 'F') # => 'Foo bar' words.gsub(/\w+/) { |word| word.capitalize } # => 'Foo Bar'
-
استخدم
%()
(دي اختصار ل%Q
) اللي بتشتغل علي ال لسطر واحد single-line اليي بتبقي محتاجة حاجتين interpolation و انك تحط double-quotes . في حالة تعدد السطور بيفضل ال heredocs. [link]# سئ (مفيش اي حاجة لل interpolation) %(<div class="text">Some text</div>) # should be '<div class="text">Some text</div>' # سئ (مفي double-quotes) %(This is #{quality} style) # المفروض تبقي "This is #{quality} style" # سئ (متعدد السطور) %(<div>\n<span class="big">#{exclamation}</span>\n</div>) # المفروض تبقي heredoc. # جيد (محتاجة interpolation, عندها quotes,سطر واحد ) %(<tr><td class="name">#{name}</td>)
-
تجنب %() او اللي زيها بالظبط %q() طالما مش عندك الاتنيد دول
'
و"
جوا ال string. علامات ال string العادية اسهل للقراية و دايما مفضلة طالما مش عندك رموز كتيرة عاوز تبطل مفعولها [link]# سئ name = %q(Bruce Wayne) time = %q(8 o'clock) question = %q("What did you say?") # جيد name = 'Bruce Wayne' time = "8 o'clock" question = '"What did you say?"' quote = %q(<p class='quote'>"What did you say?"</p>)
-
استخدم
%r
فقط في التعبيرات العادية اللي بتساوي علي الاقل واحد من دا '/' . [link]# سئ %r{\s+} # جيد %r{^/(.*)$} %r{^/blog/2011/(.*)$}
-
تجنب استخدام
%x
طالما مش هتبعت معاها امر معين (و هي حاجة مستبعدة ل حد ما). [link]# سئ date = %x(date) # جيد date = `date` echo = %x(echo `date`)
-
تجنب استخدام
%s
. يبدو ان المجتمع قرر:"some string"
الطريقة المفضلة عشان تعمل symbol مع وجود مسافات فاضية جواها . [link] -
استخدم الاقواس الانسب للعلامة المئوية. [link]
()
لتعريف ال string (%q
,%Q
).[]
لتعريف ال array (%w
,%i
,%W
,%I
) دا غير انها بتشتغل مع علامة الاراي العادية.{}
ل اعريف ال regexp (%r
) لما بيكون الاقواس موجودة جوا التعبير . و لهاذا السبب الحرف الاقل شيوعا مع ال{
هو دايما افضل delimiter في حالة ال%r
.()
لجميع العلامات التانية (زي%s
,%x
)
# سئ %q{"Test's king!", John said.} # جيد %q("Test's king!", John said.) # سئ %w(one two three) %i(one two three) # جيد %w[one two three] %i[one two three] # سئ %r((\w+)-(\d+)) %r{\w{1,2}\d{2,5}} # جيد %r{(\w+)-(\d+)} %r|\w{1,2}\d{2,5}|
-
تجنب ال metaprogramming اللي ملهاش لازمة. [link]
-
متلعبش في المكتبات الاساسية في اللغة لما تيجي تعمل حاجة جديدة. (متعملش زي ال monkey-patch معاهم.) [link]
-
البلوك اللي بتعمل من
class_eval
بيفضل ل ال string-interpolated . [link]-
لما تيجي تستخدم نموذج ال string-interpolated, دايما وفر
__FILE__
و__LINE__
, عشان اقواسك يبقوا اكتر منطقية:class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__
-
define_method
بيفضل لclass_eval{ def ... }
-
-
لما تيجي تستخدم
class_eval
(او ايeval
تاني ) و معاه string interpolation, حط معاه بلوك من التعليقات يوضح المظهر بتاع ال interpolated (مثال متاخد من كود الرايلز) [link]# من activesupport/lib/active_support/core_ext/string/output_safety.rb UNSAFE_STRING_METHODS.each do |unsafe_method| if 'String'.respond_to?(unsafe_method) class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{unsafe_method}(*params, &block) # def capitalize(*params, &block) to_str.#{unsafe_method}(*params, &block) # to_str.capitalize(*params, &block) end # end def #{unsafe_method}!(*params) # def capitalize!(*params) @dirty = true # @dirty = true super # super end # end EOT end end
-
تجنب استخدام ال
method_missing
في ال ميتابروجرايمنج عشان ال backtraces بيبقي فوضوي, التصرف مبيبقي مرصود ف ال#methods
و الخطاء في كتابة اسم دالة هيشغله بهدوء, زيnukes.launch_state = false
. اعتبر استخدام delegation, proxy, اوdefine_method
كبديل. لو لازمك يعني استخدامmethod_missing
: [link]-
خليك متأكد ان و برضه معرف
respond_to_missing?
-
اسمك الدوال اللي بدايتها محددة جدا, زي
find_by_*
-- خلي كودك مؤكد قدر المستطاع. -
كلم
super
في اخر سطر عندك -
خلي التاكيد علي , الدوال غير سحرية:
# سئ def method_missing?(meth, *params, &block) if /^find_by_(?<prop>.*)/ =~ meth # ... lots of code to do a find_by else super end end # جيد def method_missing?(meth, *params, &block) if /^find_by_(?<prop>.*)/ =~ meth find_by(prop, *params, &block) else super end end # افضل الموجودين, علي الرغم , من انك ه define_method مع كل attribute هتلاقيه متعرف
-
-
بيفضل
public_send
عنsend
عشان متتلغبتش مع الprivate
/protected
المتاحة . [link]# احنا عندنا منظومة من ال ActiveModel و اللي بتضمن concern Activatable module Activatable extend ActiveSupport::Concern included do before_create :create_token end private def reset_token # شوية كود هنا end def create_token # شوية كود هنا end def activate! # شوية كود هنا end end class Organization < ActiveRecord::Base include Activatable end linux_organization = Organization.find(...) # سئ - بيخض علي الخصوصيات linux_organization.send(:reset_token) # جيد - هيرجع استثناء linux_organization.public_send(:reset_token)
-
بيفضل
__send__
عنsend
لانها ممكن تلغبط مع دالة موجودة اصلا. [link]require 'socket' u1 = UDPSocket.new u1.bind('127.0.0.1', 4913) u2 = UDPSocket.new u2.connect('127.0.0.1', 4913) # مش هتبعت حالة للعنصر اللي مفروض يستلم. # في المقابل هتبعتها عن طريق ال UDP socket. u2.send :sleep, 0 #كدا فعلا هتبعت للعنصر اللي مفروض يستلم. u2.__send__ ...
-
اكتب
ruby -w
لكود أمن. [link] -
تجنب ال hashes ك باراميتر اختيارية. الدالة بتاعتك بتعمل حاجات كتير؟ (معرف ال Object استثناء للقاعدة دي). [link]
-
تجنب الدوال اللي حجمها اطول من 10 سطور, لو عاوز تبقي مثالي اوي المفروض تبقي اصغر من 10 سطور. السطور الفاضية مش بتساهم في موضوع السطور دا. [link]
-
تجنب ال parameter lists اطول من تلاتة او اربعة براميتر. [link]
-
لو انت فعلا محتاج دالة "global"و حطهم ف ال kenral و اعملهم private. [link]
-
استخدم متغيرات ال module نوع instance بدال ما تستخدم ال global variables. [link]
# سئ $foo_bar = 1 # جيد module Foo class << self attr_accessor :bar end end Foo.bar = 1
-
استخدم
OptionParser
ل تحليل الاوامر الاختيارية المعقدة وruby -s
لل الاوامر التافهة. [link] -
اكت كود بطريقة وظيفية و تجنب ال mutation لما تكون اكثر منطقية. [link]
-
متعملش mutate parameters طالما دا هدف الدالة . [link]
-
تجنب اكتر من تلت مستوايات من البلوك جوا بعض. [link]
-
خلي ثابت علي مبداك. في عالم مثالي , خليك ثابت علي الدليل دا. [link]
-
استخدم المنطق. [link]
في شوية ادوات تساعد تفحص كود الروبي بتاعك اوتوماتيك كدا بدل الدليل دا.
RuboCop هو بيفحص ل شكل كود الروبي و المرجع بتاعه الدليل دا. روبوكب شامل جزء مش قليل من الدليل دا, بيدعم الاتنين MRI 1.9 و MRI 2.0 و عنده قابليه للدمج كويس مع Emacs.
RubyMine دا محلل الكود بتاعه جزئيا بيعتمد علي الدليل دا.
الدليل دا لسة شغالين عليه; بعض القواعد ناقصها امثلة, و بعض القواعد معندهاش امثلة تفصله بشكل واضح و كافي. لتحسين القواعد دي بطريقة عظيمة (و طريقة سهلة) لمساعدة مجتمع الروبي! في الوقت الحالي المشاكل دي (نتمني) تتصلح انت بس حطهم في بالك دلوقتي. مفيش حاجة في الدليل دا مكتوبة علي حتة حجر, دي رغبتي في الشغل مع كل من الناس المهتمة بوجود طريقة لكتابة الروبي عشان كدا احنا نقدر نعمل دليل هيعود بالنفع علي كل مجتمع الروبي.
خد راحتك خالص انك تبلغ بمشكلة او تضيف تغيير في تحسينات. و شكرا مقدما علي تعبك دا انت بره تقدر تساعد في البروجكت (و روبوكب) بالفلوس عن طريق Gratipay.
بمنتهي السهولة, اتبع الخطوات دي contribution guidelines.
العمل دا بينطبق عليه الرخصة دي Creative Commons Attribution 3.0 Unported License
الدليل اللي موجود بسبب ال مجتمع قيمته قليلة لمجتمع ميعرفش عن وجوده.
اكتب عنه ف تويتر، انشره لزمايلك و صحابك.
كل كومنت و اقتراح و رايي بيخلي الدليل احسن
واحنا عايزين احسن دليل ممكن، مش كدا؟
علي وضعك,
Bozhidar