This README outlines the details of collaborating on this Ember application.
You will need the following things properly installed on your computer.
- Git
- Node.js (with NPM) - preferably via NVM. v4.2.2 is recommended. Some NPM dependencies still have issues with v5.*
- Bower
- Ember CLI
- PhantomJS
- Ruby via RubyInstaller or RVM
Please make sure you are familiar with EmberJS framework, Ember CLI tool, Mocha and Chai testing frameworks and Openlayers library. If not, please read documentation first:
- Ember Official Guides
- Ember CLI guide
- Ember tutorial
- Mocha Docs
- Chai BDD assertions
- Openlayers Workshop
Also you may use API documentation for development:
- Ember API
- Openlayers API
- Addons documentation links are listed under Addons section of this Readme
- Development flow is based on GitFlow
- Feature branches must be prefixed by
feature/
and forked fromdevelop
branch only - Hotfix branches must be prefixed by
hotfix/
and forked frommaster
branch only - Bugfix branches must be prefixed by
bugfix/
and forked fromdevelop
branch only - Direct pushing to
master
anddevelop
branches is strictly prohibited! - All branches must pass code review and CI runs before merging.
- Merging to
master
anddevelop
branches without code review is strictly prohibited! Everything merged to these two branches must be reviewed first.
- Following the GitFlow development guidelines, any pull-request needs to be reviewed before merge.
- Code review consists of several parts:
- Review code changes
- Checkout the branch and run it locally
- Manually test changed parts of application, especially the bottlenecks you have noticed reviewing the code
- Make sure there are no deprecation messages, warnings or errors in javascript console
- Run tests locally (
ember test
) and make sure they all pass - Make sure there were no warnings during build process (
scss-lint
does not break tests but flushes warnings in build process output)
- Code requirements to pass code review:
- No logging/debugging logic or helpers left
- Branch has any meaningful tests (the coverage volume is for reviewer judgement)
- The code follows all styleguides, conventions and rules described in this document
- Build must pass all tests and checks
- Build must not have any regressions (exceptions might be discussed)
- Pull request description must provide all meaningful details for reviewer and have Jira issue attached (if exists)
- clone git repository:
git clone <repository-url>
- change into the new directory:
cd <directory-name>
- install NPM dependencies:
npm install
- install bower dependencies:
bower install
- for Windows users it may be useful to go through this giude
- install SASS:
gem install sass
- install scss-lint:
gem install scss-lint
ember server
- Visit your app at http://localhost:4200.
Make use of the many generators for code, try ember help generate
for more details.
Generators also produce basic tests, so it is the preferred way of creating new entities.
First of all please go through Ember testing guide to become familiar with the concept.
We use mocha instead of qUnit as our test framework. Please take a brief view to get a general idea and understanding of BDD approach in testing. Mocha is integrated to Ember app via ember-mocha. It provides Ember test helpers mapping for mocha.
To come along with BDD approach we also use expect as an assertion library.
Basic Rules:
- Write all types of tests:
- Unit tests for entities
- Integration tests for entities' visual representation and intercommunications
- Acceptance tests for flows and behaviors
- Reflect all changes in existing tests
- Use
ember-mocha
helpers for unit tests - Try to stick with thin test cases. Don't make a lot of assertions in a single test. More small tests is much easier to maintain.
- Always put test type to top-level describe, followed by
-
:Unit - Vessel model
,Acceptance - Vessel selection
and so on. - Acceptance tests:
- Use nested describes to keep the lower level have just few assertions (only one ideally)
- Make sure of the logical test case output. Mocha concatenates all nested
describe
titles for each test case. It should be something like: 'Users page has list of all users rendered properly' built from:describe('Users page')
->describe('has list')
->describe('of all users')
->describe('rendered properly')
- Always use mocks and stubs in all types of tests. Test should never interact with remote APIs.
ember test
- simply runs all testsember test --server
- starts a CI server which track files for changes and automatically reruns tests on changesember server --environment=test
- runs development server within test environment. You can open application by visiting http://localhost:4200 and interact with it. Also you can run tests at http://localhost:4200/tests route.
ember build
(development)ember build --environment production
(production)
The major part of the application is built around Openlayers library. The library itself is very feature-rich and therefore is very big in terms of compiled code size. Because of that we use custom build of Openlayers to optimize application size. The build process is completely transparent and built-in to Ember CLI. Openlayers custom builds are managed by ember-cli-openlayers-builder addon. Please take a brief view on how it works.
If you need to add a new Openlayers module to the app:
- add the module to
exports
array of.ol-build
config - restart
ember server
to apply changes - Ember CLI will automatically rebuild Openlayers according to new config and append it to vendor scripts
Please note that openlayers doesn't support ES2015 modules for dependencies management.
So it is extracted to the global scope (window
) and available everywhere via ol.*
namespace.
However, there are some usage conventions:
- all Openlayers functionality should be proxied via
appName/helpers/ol/*
helpers. ol.*
API should not be used anywhere in the app outside ofappName/helpers/ol*
directly. If you need any Openlayers function, find an appropriate helper, extend it if necessary or create one if it does not exist yet.- any ol helper must be extended from
appName/helpers/ol/core/object
which is responsible for proper Openlayers instance handling and contains some useful hooks. - any ol helper must implement
init()
hook which should create a corresponding openlayers object and pass it to thethis._super()
method. - since
appName/helpers/ol/core/object
extendsEmber.Object
and all its APIs are available for any ol helper (includingEmber.Observable
), it should be used the same way like any other Ember object. Don't hesitate to useget
,set
, computed properties and observers on ol helpers.
Specify what it takes to deploy your app.
Ember
Ember Inspector
Other docs
- Ember Addons Repository
- ember-cli-windows-addon - performance improvement on Windows platform
- ember-suave - JSCS linter
- ember-cli-autoprefixer - Autoprefixer for Ember CLI
- ember-cli-mirage - API mocks for tests and development
- ember-cli-mocha - test framework (mocha + chai)
- ember-cli-es5-shim - es5 to es3 shims
- ember-moment - moment.js for Ember. Docs: http://momentjs.com, ember-moment
- ember-i18n - internalization service for ember app. Docs
- ember-cli-openlayers-builder - openlayers building tool for Ember CLI
- ember-cli-sass - SASS compiler for Ember CLI
- ember-cli-scss-lint - build-time linter for SCSS
- Ember official recommended styleguide
- SCSS code style: see .scss-lint.yml
- Private variables must be prepended by
_
:let _privateVar
- Variables that contain links to jQuery objects must be prepended by
$
:let $element
- Commented-out code should never pass to the repository. It greatly reduces readability. Use VCS to restore deleted code.
- Always use single quote marks for strings. This allows to avoid escaping double quote marks inside.
We use ember-i18n for application localization.
Translation files are located at app/locales/**
. Default locale is currently en-gb
.
Rules
- Developer should never put strings, which would be displayed to end user, directly. We must use translation helpers for this.
- Use dates only with
moment
helpers or service since they have to be localized as well. - Direct strings and dates are allowed only for logging and debugging purposes
- Any new string should be defined at least for default translation
- To define new string add it to
translations.js
file as a value and choose a valid logical path as a key. Make sure it is unique. - Sort path keys in alphabetical order for easier navigation
- After the string is defined, it could be used via i18n service or helpers identified by its path.
- i18n service is injected to all types of Ember entities by default. It can be used (
this.get('i18n')
) everywhere out of the box - If you faced incorrect localization (e.g. invalid pluralization form), please refer to library documentation to solve an issue.
We use SCSS to write styles and scss-lint for codestyle validation. Please take a look at .scss-lint.yml to be aware of SCSS codestyle rules.
- All styles are stored under
appName/styles
folder. - Main styles file is
appName/styles/app.scss
, it must include all other styles (using nesting). - All styles are splitted into strictly structured directories tree.
- Each subdirectory must contain
index.scss
file wich includes all files within the folder andindex.scss
files from all subfolders. - All files with style rules must be sass partials and must be prepended by
_
. In this case theindex.scss
file will always be the last file in the folder. - All css animations must be stored under
appName/styles/animations
folder, one animation per file, and the file name must reflect the animation name. - All custom fonts must be defined under
appName/styles/fonts
folder, one font per file, and the file name must reflect the font name. appName/styles/helpers/*
is for helper style rules. Such rules are universal and may be used all over the application._icons.scss
contains symbol icons,_layout.scss
contains layout helpers (alignment, positioning, etc.).appName/styles/images/*
is for base64 encoded images, one image per file. Base64 string must be assigned to a variable and file name should be equal to variable name.appName/styles/mixins/*
is for mixins, one mixin per file, file name should be equal to mixin name.appName/styles/variables/*
is for variables. Subfolders define styles of different categories.appName/styles/templates/*
is for application styles.
- All styles in the application are splitted into small blocks.
- Each block is stored in its own file.
- Each single block represents Ember template (and therefore
appName/styles/templates
hierarchy follows theappName/templates
one). - Block name is always equal to corresponding template name.
- Basicaly, the concept is: each template file has a corresponding styles, scoped into that template.
- How the scoping works read in "Naming" section.
- We follow the simplified BEM convention for styles naming.
- Each template block is the "Block" in BEM notation.
- Each template can have only one Block
- Block class name should correspond template name.
- Block may have as many elements as needed.
- Elements can not have nested elements.
- This is how scoping works: Block class name equals to it's template name (one-to-one), which means it's unique for the application. Therefore all its elements are also unique.
- This allows to completely avoid styles cascading without collisions.
- Theming is made by overwriting default styles for each block by theme specific styles.
- When theme is applied, its name is applied to the application container element as a class name.
- In order to overwrite styles for any element just nest it under
.<theme-name>
class. - Only the styles that differs from default ones have to be overwritten.
- All theme-related styles for each Block should be stored in a separate file, e.g.
appName/styles/tempaltes/<template-path>/<template-name>/_<theme-name>.scss
- Theme-related variables should also be stored in separate files like
appName/styles/variables/<category>/_<theme-name>.scss
- Theme-related variables should have the same name as default ones with
--<theme-name>
postfix, e.g.$text-color
->$text-color--light
. - Theme files should be included in
index.scss
files after_default.scss
in order to properly overwrite default styles. - Styles for mobile devices are implemented as a theme called
mobile
.
appName/templates/application.hbs
appName/templates/map.hbs
appName/templates/components/some-component.hbs
appName/components/some-component.js
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['some-component'] // this will apply Block root class to component root element
});
appName/styles/templates/application/_default.scss
.application {
width: 1000px;
padding: 20px;
background-color: #555555;
position: relative;
}
appName/styles/templates/application/_light.scss
.light {
.application {
background-color: #eeeeee;
}
}
appName/styles/templates/application/index.scss
@import 'default';
@import 'light';
appName/styles/templates/map/_default.scss
.map {
position: absolute;
top: 0;
bottom: 0;
}
appName/styles/templates/map/index.scss
@import 'default';
appName/styles/templates/components/some-component/_default.scss
.some-component {
display: inline-block;
padding: 5px;
background: #999999;
&:hover {
color: #222222;
}
&::after {
content: '!';
}
}
.some-component__title {
text-decoration: underline;
}
.some-component__title--active {
font-weight: bold;
}
.some-component__icon {
text-decoration: none;
display: inline-block;
}
appName/styles/templates/components/some-component/_light.scss
.light {
.some-component {
background: #dddddd;
&:hover {
color: #eeeeee;
}
}
}
appName/styles/templates/components/some-component/index.scss
@import 'default';
@import 'light';
app.scss
and all*/index.scss
files must be used for importing nested files only. They must not contain any style rules.- All style rules must be classes. Identifiers in selectors are prohibited. Identifiers should only be used for javascript needs.
- Tag names should never be used in selectors. If you need to add style for element, always make a class for it.
- Cascading the rules is a considered bad practice. So it is allowed only in few cases: to apply a theme, for pseudo-classes and psuedo-elements
- Styles cascading (and nesting therefore) is allowed only for two levels deep. But it's always better to avoid it when possible.
!important
instruction is completely prohibited since it makes styles not reusable.
- All CSS images should be stored under
appName/styles/images/*
, one image per file. - CSS image is a base64-encoded string that is assigned to a SASS variable:
$my-image: 'data:image/png;base64,...';
- File name should be the same as the variable name, prefixed with
_
:_my-image.scss
->$my-image: '...';
- If the image may be used in markup and/or JS, it should be assigned to a CSS class as a background image. CSS class name should be the same as the variable name, prepended with
img--
:$my-image
->.img--my-image: {}
.width
andheight
have to be declared in this case.