First off - Thank you for your interest in contributing to the Open OnDemand project!
There is no pull request too small! Everything from simple misspellings to very large feature requests are welcome. If you're not quite sure where to get started you can search our list of good first issues.
Please note we have a code of conduct, please follow it in all your interactions with the project.
Issues, bug reports, questions and feature requests are always welcome. Feel free to open an issue and use any issue labels as appropriate.
We mostly use Discourse for general questions or help. If you're unsure of where to route your question, Discourse may be the best forum for it.
There are other repositories to Open OnDemand that are important as well. You may want to check these out too.
- repository for the Open OnDemand website
- repository for the Open OnDemand documentation
- repository for the Open OnDemand core library
If you have a large feature it may be preferential to open an issue and discuss it first before putting a lot of work into coding something that may not be accepted. Don't let this discourage you though! Feel free to open tickets and engage with the development team on proposed changes.
- Fork this repo.
- Branch off of the master branch.
- Now create a pull request to this repository! At this point a maintainer will be notified and will start reviwing it.
- If changes are being requested, don't let this discourage you! This is a natural part of getting changes right and ensuring quality in what we're building.
git checkout -b feature/<short-name>-<issue#>
git checkout -b bug-fix/<short-name>-<issue#>
A best guess is encouraged.
All new issues are reviewed weekly by the OOD team to categorize correctly regardless.
Ensure to include what issue this fixes in the PR comment to help with automated issue closing:
- "fixes #1234"
- Any new code must also include testing.
- If you need help with writing tests please include the
test help
tag.
- Prefer read-only objects. You'll find mostly read only objects spread throughout this code base.
This means that
attr_writer
orattr_accessor
are to be avoided and onlyattr_reader
should be used. It follows then, that most objects should accept a large number of parameters in their initializers and set the attributes accordingly.
For any Ruby librares/apps there is a .rubocop.yml
at the top of this project. If you
work in IDEs where you want to only open a part of this project, executing rake lint:setup
and it will copy copy lint config files to various locations. Watch out though, rake clean
will remove them!
In addition to the RuboCop styles configured described above, we follow common Ruby style
and idioms. For example snake_case
methods and variable names and favoring functional style
methods.
-
Avoid more than 3 levels of nesting.
-
!
instead ofnot
-
use
&&
or||
-
use
unless
over!if
-
{...}
for single line block -
do..end
for multiline block -
omit
return
for values at end of method -
Use
||=
to init variables only if they're not already initialized -
Use
_
for unused block variables -
Prefer
map
overcollect
find
overdetect
select
overfind_all
size
overlength
-
utf-8
encoding of source file -
2 spaces indent
-
No tabs
-
avoid using semi-colons
;
-
Spaces:
-
around operators
=
,+
,-
,*
,%
-
around curly bracies
{
}
-
after commas , colons, and semicolons
my_hash = { one: 'el1', two: 'el2' }
-
-
No spaces:
-
After shebang
!
-
Around square brackets
[ ]
or parentheses( )
my_arr = [a, b, c] def my_func(arg: nil) if arg != something ... end end
-
-
Empty line between method defintions.
def method1(arg1) ... end def method2(arg1) ... end
-
Align successive method chains with
.method
on subsequent lines:obj .compact .map { |elmt| ... }
-
End each file with a newline.
-
No trailing commas (
,
) for last element inhash
orarray
: -
Place closing brackets for multiline statements on their own line:
# fail arr = [ this = 1, that = 2, ] # pass arr = [ this = 1, that = 2 ]
-
Empty line around
attr_
block:class MyClass attr_reader :name, :email attr_accessor :phone def initialize(name, email) @name = name @email = email @phone = phone end end
-
Set
attrs
first -
Set
class << self
afterattrs
-
Set
initialize
afterclass << self
class MyClass attr_reader :something, :another class << self def singleton_method ... end end def initialize ... end end
-
Indent
private
and keepdefs
aligned with the blockclass MyClass # public methods def public_method ... end private def my_private_method ... end end
-
Prefer literal array syntax over
%w
or%i
# fail arr_1 = %w(one two three) # pass arr_1 = ["one", "two", "three"]
-
Instantiate with literals for all collections if possible.
# fail arr = Array.new() hsh = Hash.new() # pass arr = [] hsh = {}
- We require comments in the code.
- Use proper grammar and punctuation.
- Focus on why the code is the way it is if it is not obvious.
- Focus less on how the code works, that should be evident from the code.
- At the top of each class and method please add a description of the intent of the Class or Method
/**
* Format passed string to snake_case. All characters become lowercase. Existing
* underscores are unchanged and dashes become underscores. Underscores are added
* before locations where an uppercase character is followed by a lowercase character.
*/
function snakeCaseWords(str) {
-
Use implicit
begin
in method# fail def some_method begin ... rescue Psych::SyntaxError => e ... raise e end # pass def some_method ... rescue Psych::SyntaxError => e ... raise e end
- Use meaningful names:
-
Ruby is not a statically typed language so we need good naming for maintainability.
-
Consider using an objects type for the dummy variable if possible.
-
Otherwise, try to convey what the object being passed to the block is through the name:
arr_1 = ['one', 'two', 'three'] arr_2 = [1, 2, 3] # fail arr_1.each { |e| puts e } arr_2.each { |e| puts e } # pass arr_1.each { |str| puts str } arr_2.each { |int| puts int }
-
-
Avoid
is_
in method names. -
Use
?
suffix for methods that return abool
. -
Use
save
for boolean return andsave!
with exception returns. -
Favor functional methods
- break up long logic or data transformations into their own methods
-
DRY out code as best you can
# fail def some_method if var1 && bool2 && x > y || big % small > 1 ... end end def some_other_method if var1 && bool2 && x > y || big % small > 1 ... end end # pass def some_method if conditions_true? ... end end def some_other_method if conditions_true? ... end end def conditions_true? if var1 && bool2 && x > y || big % small > 1 end
-
Mountain/Pascal case for Class and Module names.
class SomeCustomClass # code ... end
Beyond the ESLint configuration files, we follow these styles:
- File names use underscores,
_
, for word seperators. - Variables are
camelCase
named and areconst
orlet
. Usingvar
is discouraged. - Function names are
camelCase
.
In general, when making html elements (or Ruby/Js/Etc models of) we'll follow this style.
- IDs use underscores
_
for word seperation. This follows the Railsform_for
convention.
- Applicable SCSS conventions like hyphenated variables and those variables are in the
_variables.scss
file. - class names use hyphens,
-
for word seperators. If for no other reason than to follow bootstrap which we use quite extensively. - classes should mostly use relative sizes (
em
andrem
), rarely pixel values (px
).