Code styles are predefined in the RubyMine/IntelliJ project files. Also, the Rails Community Style Guide covers majority of the styles that we use.
Write Yardoc when implementing classes. Yardoc is preferred because RubyMine is able to infer parameter and return types when annotated using it.
Run yard stats --list-undoc
to find which methods need documenting.
Run tests using rake spec
. You need PhantomJS to run the tests. We use
lvh.me to simulate tenants, thanks levicook!
Tests should always use I18n.t()
to indicate translatable strings. In test mode, the strings
are stubbed out so variable interpolations are not used. This is to simplify testing and reducing
the brittleness of the tests against changing translations and arguments.
Try to group tests according to their purpose. Feature tests should be namespaced using colons
for tidiness (e.g. Courses: Users
)
Write your tests to be as compartmentalised from other tests as possible. Compartmentalised tests are those that do not depend on any external state to run. This would allow tests to be run in parallel.
When defining constants, by default all constants would go to the global namespace and
potentially cause your specs to interfere with each other. To overcome this,
prefix all constants with self::
.
In Rails, controller specs will always execute the rescue_from
handlers. In our specs, we
disable that by default; to execute controllers with the handlers enabled, declare run_rescue
within the example group.
In development mode and when running specs, ActiveJob jobs are run with the :background_thread
queue adapter (see lib/autoload/active_job/queue_adapters
). This is to ensure consistency with
production (where we will be using a separate jobs server): running jobs inline might cause
database connections within the job to remain within a transaction.
Therefore, specs have a group helper with_active_job_queue_adapter
, which takes the queue
adapter to use for that group of examples. The only adapters which should be used is:
:test
when counting the number of enqueued jobs.:background_thread
when running job specs.spec/support/active_job.rb
automatically sets the default queue adapter to be:test
when running job specs.
There is no longer a need to explicitly set the :inline
adapter for delayed delivery of mail.
When running specs, all deferred mail deliveries are converted to immediate deliveries to keep
track of the number of pending mail deliveries.
Trackable Jobs can be waited -- use the TrackableJob#wait
method.
The project's Gemfile contains a few developer tools to help keep the project tidy:
- Traceroute checks that the routes are properly defined and reachable.
- i18n-tasks checks that the localisations are all defined and used.
Declare model attributes in the following order:
- includes (e.g.
include UserPasswordConcern
) - declarations (e.g.
acts_as :superclass
,stampable
) - callbacks
- attribute overrides (e.g.
enum
s, workflows) - validations. Exception: when the validation is over a collection association, this validation needs to be placed after the association so that the validation is not overwritten by the association model's validations.
- associations
- calculated fields
- scopes
This allows models to be inherited. See the section on Inherited Callback Queues from
ActiveRecord::Callbacks
.
Take note that workflows do not persist their state, save
needs to be called on the record.
See lib/extensions/deferred_workflow_state_persistence
.
When declaring cancancan abilities, use only hashes. Do not define an ability using a block as cancancan only allows a single can
statement for a model if a block is used. If we use hashes, we can write multiple can
statements and cancancan will combine them.
The same code style for Ruby code applies to all views (e.g. single quotes unless interpolations are used.) There is no linting for views at this point, but that might change in future.
By default, page titles will be generated by reversing breadcrumbs, so the name of current resource will always be clearly present at the start of the title (for users to quickly switch to the tab when many tabs are open). This will also allow users to jump to a page by the location in the module.
Custom page titles can be specified in views with content_for(:page_title, 'title here')
. If
present they will override the default titles.
The Coursemology branding will automatically be appended to all page titles.
Use the page_header
helper in resource index
pages. All toolbar items dealing with a resource
should be placed in the block that page_header
accepts. On smaller screens, the toolbar should
wrap below the title.
Use content_tag_for
or div_for
helper whenever you want to present an active_record object (such
as in lists/tables). This makes your code more readable and enforces the consistency in views.
When specifying the class list of tags in templates or helpers, specify them as an array over a space-delimited list. This allows other parts of the view rendering pipeline to modify the set of classes which should be applied to an element. Use the Slim shorthand for classes, where possible.
When displaying translations for long stretches of text (e.g. a paragraph), use Rails'
simple_format
view helper to present the text. This automatically paragraphs the translations.
When displaying user input, use the formatting helpers in
app/helpers/application_formatters_helper.rb
. This would apply HTML sanitisation, automatic
linking to URL-like strings, etc.
When using Simple Form remember to declare f.error_notification
.
simple_form_for
, simple_fields_for
, and associated methods (f.input
, etc.) should be called
without parentheses.
Controller actions (render
, redirect_to
) should be called without parentheses.
Arrange controller methods for Rails' default routes in the following order:
index
show
new
create
edit
update
destroy
Add all other non REST-ful controller actions after these methods.
Arrange private controller methods in the following order:
- All
params
methods - Callbacks -
before_action
andafter_action
- Any other helper methods for the controller
Remember to specify page breadcrumbs in controllers with the add_breadcrumb
helper.
Controllers which involve resources should have breadcrumbs for all relevant actions. These are specified in individual views.
Actions which involve a resource member (show
, edit
) should have breadcrumb text which describes
the resource, e.g. showing its title or name.
Themes are provided by themes_on_rails. Coursemology uses standard Bootstrap 3 styles, so it is possible to theme Coursemology using any Bootstrap 3 template.
Bootstrap is included in assets/styles/application.scss
. Coursemology's markup is semantically
written, modulo extras that Bootstrap requires to work. Themes should therefore @import 'application';
in their theme stylesheets, and override the base styles there.
It also follows that because the application stylesheet has been deferred to themes, the base
Coursemology application cannot define an application template. Coursemology instead implements a
default
theme which contains the default application template and the default Bootstrap 3 styles.
Application components still implement their views in app/views
, with themes given the option to
override them in the theme directory.
A sample theme (for coursemology.org) can be found in the coursemology-theme project.
Because we use Sass' @import
directive, which does not ensure a file is included exactly once
(see sass/sass#139), we have a custom workaround by using an ERB template. This will properly
pick up changes made to scss files, but adding new files would require clearing the asset cache.
Run rake assets:clobber
(in production) and rm -rf tmp/cache
(in development) to clear the
cache.
The lib
directory is not autoloaded, as described in this blog post. However, the lib/autoload
directory is. Place libraries which will be autoloaded in that directory instead.
Following a paradigm similar to that described by André Arko, layouts can be nested. This is most visible when defining a global layout, and then a sub-layout for course modules (with the sidebar). Our implementation is similar, but instead of an application helper, we extend ActionView directly, so templates look like:
render within: 'parent_template' do
| Extra pre-adornments
= yield
| Extra post-adornments
end