This file includes code style references which can help when contributing to project.
This project will show patterns and tips to contribute in this project. Feel free to contribute there!
The project tree consists of several directories. Some of them are described at RubyGems documentation. See it for clearance.
Folder structure in the lib
directory deserves special attention. There are two other folders there — lib/extensions
and lib/genius
, the last of which is divided into two more layers - outer and inner. The api.rb
file (referred as
the main file) acts as the outer layer, into which the entire interface is imported which is defined in inner layer
referred as lib/genius/api
folder.
As mentioned above, the outer layer consists of only one main file where other files are included. The file consists of
a main module and its submodule to preserve the structure of the project tree, as indicated in the
RubyGems documentation. The other files act as a category for
the main module which is called Genius
. Outer layer is stored in lib/genius
The inner layer is a kind of bunch of interfaces and extensions (categories) for the main module. Each of the interface
is named after the resource in Genius API documentation. Inner layer is stored
in lib/genius/api
.
Extensions are the files which represents the methods which are used in a bunch of other methods in the inner layer.
This kind of interfaces were bundled in a separate folder for multiple reuse in other methods. They are not a part of
the Genius
module interface, so it means that they are a category to other structures (Hash
, Object
or String
classes). Extensions are stored in lib/extensions
.
Rakelib is another sort of extensions but it is used for generating the documentation. Note that the files in this directory are distributed under the BSD license!
This paragraph will show the main conventions in naming of some structures or objects, such as files, classes, variables to represent their semantics more clear.
The files are named after the resource from the Genius API documentation. File names should not contain uppercase letters or contain non-letter characters except for the underscore. All files should be stored in the inner layer.
Modules should be defined inside the Genius
module — a top-level project module — and they should be named after the
filename where they defined.
For the most part, classes are used to represent errors and they are stored in the inner layers file — api/errors.rb
.
Each of this classes must end with the word Error
.
Parameters should be named as a keyword arguments. This rule does not apply to private methods. See: Style/OptionHash
Variables should be named as in a style guide of the RuboCop. See: Naming/VariableName
This paragraph will show some of the style guides for defining some sorts of the structures.
All of the modules defined as a category for the main module Genius
. For e.g., Genius::Account
# lib/genius/api/account.rb
module Genius # :nodoc:
module Account # :nodoc:
# some interface
end
end
Among other things, modules must have documentation on the top of them. Documentation should consist of a brief description of the module, its semantics and the semantics of its interface - objects that the module uses, extensions, etc. For e.g.,
# This module reflects the structure of our world, consisting of a class of people and a class of animals.
module World
class Animal # :nodoc:
# some interface
end
class Human # :nodoc:
# some interface
end
end
If no documentation is attached to the module, then a special tag in the form of a comment should be placed next to its
name, indicating this — # :nodoc:
. In instance,
module A # :nodoc:
# some interface
end
As mentioned above, classes are usually represents error objects. This classes should be stored in a separate file
— lib/genius/errors.rb
, they must be inherited from the Genius::Errors::GeniusExceptionSuperClass
which in turn is
inherited from StandardError
and they must have at least two readable attributes — :msg
and :exception_type
.
Moreover, this constructor must invoke method super(message)
. Declaration of more arguments for debugging or
additional info to represent while error raised is optional. An example interface logic for such a class would be as
follows:
class SomeError < GeniusExceptionSuperClass # :nodoc:
attr_reader :msg, :exception_type, :some_useful_argument
# @param [String (frozen)] msg Exception message.
# @param [String (frozen)] exception_type Exception type.
# @param [String (frozen)] some_useful_argument Some useful stuff for debugging.
# @return [String (frozen)]
def initialize(msg: "Some error message.", exception_type: "some_exception_error", some_useful_argument: nil)
super(message)
@msg = msg + some_useful_argument
@exception_type = exception_type
end
end
As modules, classes must have documentation on top of them and if there is no documentation was attached (in instance,
if there is a category for built-in objects as Hash
or String
classes) there should be a special tag inside a
comment which should be placed next to the class name — # :nodoc:
.
Methods, among other structures, must have at least some documentation, consisting of at least its signature, as well as the arguments that the method takes. The documentation must be written in a specific syntax, according to the YARD documentation. This rule is specific for methods inside classes interface if they are object constructors, because they always point to the instance declaration, so they do not require explicit specification of their signature at the top of the comments section. For this sort of methods the return type can be omitted.
Required YARD tags to indicate and theirs abstract syntax:
@param [Class] param_name documentation.
@raise [ExceptionClass] if something went wrong.
@return [Class]
Required YARD tags to indicate and theirs abstract syntax:
@private
@example Example usage
@option variable_name [Class] :option documentation.
@see #method_name
Example for regular singleton methods (inside modules)
module M # :nodoc:
module A # :nodoc:
class << self
# +M::A.foo+ -> Integer
#
# Method documenation.
#
# @param [Integer] arg1 First integer argument.
# @param [Integer] arg2 Second integer argument.
# @raise [RuntimeError] if one of the argument is not an integer.
# @return [Integer]
def foo(arg1: nil, arg2: nil)
unless arg1.is_a?(Integer) || arg2.is_a?(Integer)
raise RuntimeError, 'Some error was raised'
end
arg1 + arg2
rescue RuntimeError => e
e.message
end
end
end
end
Example for regular constructors.
class SomeError < GeniusExceptionSuperClass # :nodoc:
attr_reader :msg, :exception_type, :some_useful_argument
# @param [String (frozen)] msg Exception message.
# @param [String (frozen)] exception_type Exception type.
# @param [String (frozen)] some_useful_argument Some useful stuff for debugging.
# @return [String (frozen)]
def initialize(msg: "Some error message.", exception_type: "some_exception_error", some_useful_argument: nil)
super(message)
@msg = msg + some_useful_argument
@exception_type = exception_type
end
end
This section will demonstrate the rules for contributing in project
Git commits should clearly describe the purpose of your commit. For example, commits as "Do smth" or "Refactored some
files" do not actually represent theirs content. Commits as "Refactored README.md
" reflects theirs content, so it
will be unused to delve into the purpose of the commit most of the time.
Before pushing, you must be sure that the code you write is correct, so it should always be formatted through a static
analyzer. Look towards the rubocop -D
and rubocop -A
commands.
- Look at the documentation with fresh eyes
- Check the file for all sorts of errors
The documentation is available as open source under the terms of the CC BY-SA 4.0 License