diff --git a/README.md b/README.md index 5255e97..c072a00 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ Code style used by AlpineLab in all our projects. ## Usage -1. Add `rubocop` gems to your `Gemfile` (or `gems.rb`, or `gems.deps.rb`): +1. Add those gems to your `Gemfile` (or `gems.rb`, or `gems.deps.rb`): ```ruby group :development, :test do + gem "alpinelab-codestyle", "~> x.y", require: false gem "rubocop", "~> x.y", require: false gem "rubocop-md", "~> x.y", require: false end @@ -16,53 +17,33 @@ Code style used by AlpineLab in all our projects. or install them manually: ```shell - gem install rubocop rubocop-md + gem install alpinelab-codestyle rubocop rubocop-md ``` -2. Add `alpinelab-codestyle` gem to your `Gemfile` -(or `gems.rb`, or `gems.deps.rb`): - - ```ruby - group :development, :test do - gem "alpinelab-codestyle", "~> x.y" - end - ``` - - or install it manually: - - ```shell - gem install alpinelab-codestyle - ``` - -3. Create or prepend your Rubocop configuration (usually `.rubocop.yml`) with: +2. Create or prepend your Rubocop configuration (usually `.rubocop.yml`) with: ```yaml inherit_gem: alpinelab-codestyle: - config/default.yml - - config/rails.yml + - config/rails.yml # for Rails projects only ``` ## Best practices -Some conventions are adopted and enforced, in no particular order: - -1. Developer-specific configuration files must be git-ignored globally, _e.g._: +Some conventions are adopted and enforced, but cannot be translated as Rubocop +rules (or are not supposed to). You will find those conventions as Markdown +files in the [`docs`](docs/) directory of this project, organized by subject: - * `.ruby-{version,gemset}` - * `docker-compose.override.yml` - -2. Project-specific configuration files must be git-ignored per project, _e.g._: - - * `config/database.yml` +* [File structure and organisation](docs/files.md) +* [Rails-specific conventions](docs/rails.md) +* [HTTP API development guidelines](docs/http-api.md) ## Releases To release this very gem: -1. Bump its version number in -[`lib/alpine_lab/code_style/version.rb`](lib/alpine_lab/code_style/version.rb) -according to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) +1. Bump its version number in [`version.rb`] respecting [Semantic Versioning 2] 2. Use Bundler Rake task to build, tag and push the gem: @@ -70,13 +51,22 @@ according to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) rake release ``` - _(with Docker, you need to have configured both - [Git authentication](https://github.com/alpinelab/docker-ruby-dev/blob/latest/RECIPES.md#git-authentication) - and [Rubygems authentication](https://github.com/alpinelab/docker-ruby-dev/blob/latest/RECIPES.md#rubygems-authentication) - then you can run the same command with `docker-compose run app rake release`)_ + _(using Docker, both [Git authentication] and [Rubygems authentication] must + be configured to run `docker-compose run app rake release`)_ ## License -This project is developed by [Alpine Lab](https://www.alpine-lab.com) and released under the terms of the [MIT license](LICENSE.md). +This project is developed by [Alpine Lab] and released under the terms of the +[MIT license]. + + + + - + +[Alpine Lab]: https://www.alpine-lab.com +[git authentication]: https://github.com/alpinelab/docker-ruby-dev/blob/latest/RECIPES.md#git-authentication +[MIT license]: LICENSE.md +[RubyGems authentication]: https://github.com/alpinelab/docker-ruby-dev/blob/latest/RECIPES.md#rubygems-authentication +[Semantic Versioning 2]: https://semver.org/spec/v2.0.0.html +[`version.rb`]: lib/alpine_lab/code_style/version.rb diff --git a/docs/files.md b/docs/files.md new file mode 100644 index 0000000..178ae7b --- /dev/null +++ b/docs/files.md @@ -0,0 +1,19 @@ +# File structure and organization + +## Git-ignore'd files + +**Developer-specific** configuration files must be +[git-ignored globally], _e.g._: + +* `.ruby-version` +* `.ruby-gemset` +* `docker-compose.override.yml` + +**Project-specific** configuration files must be +[git-ignored locally] (_i.e._ per project), _e.g._: + +* `config/database.yml` + + +[git-ignored locally]: https://help.github.com/articles/ignoring-files/#create-a-local-gitignore +[git-ignored globally]: https://help.github.com/articles/ignoring-files/#create-a-global-gitignore diff --git a/docs/http-api.md b/docs/http-api.md index a2e2272..17de555 100644 --- a/docs/http-api.md +++ b/docs/http-api.md @@ -2,22 +2,20 @@ ## API description -We use [OpenAPI Specification 2.0](https://swagger.io/specification/v2/) -to describe our API. +We use [OpenAPI Specification 2.0] to describe our API. This OAS 2.0 description is then used by: -* [Dredd](https://github.com/apiaryio/dredd) to check our implementation -* [Committee](https://github.com/interagent/committee) to validate input data -* [SwaggerUI](https://swagger.io/tools/swagger-ui) to generate documentation +* [Dredd] to check our implementation +* [Committee] to validate input data +* [SwaggerUI] to generate documentation ## JSON generation -We use [jBuilder](https://github.com/rails/jbuilder) to write our JSON views. +We use [jBuilder] to write our JSON views. -We may use [Oj](https://github.com/ohler55/oj) to parse and marshall -JSON on performance-demanding projects (`jBuilder` uses -[MultiJSON](https://github.com/intridea/multi_json) that uses `Oj` as soon as -it is required, so we simply have to add it to the project `Gemfile`). +We may use [Oj] to parse and marshall JSON on performance-demanding projects +([jBuilder] uses [MultiJSON] that uses [Oj] as soon as it is required, +so we simply have to add it to the project `Gemfile`). ## Controllers segregation @@ -28,9 +26,19 @@ Instead, we favour an approach where API controllers are namespaced in a module named `API` (_e.g._ `ResourcesController` processes HTML requests while `API::ResourcesController` processes JSON requests). -It avoids unreadable controller actions with many `respond_to` and `format`, -but is a bit less [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) -because we basically need 2 controllers per resource. -This should incentivise us to keep business logic separated in external modules -like service classes, thus enforcing the -[SRP](https://en.wikipedia.org/wiki/Single_responsibility_principle). +It avoids unreadable controller actions with many calls to `respond_to` and +`format`, but is a bit less [Don't Repeat Yourself] because we basically need +two controllers per resource. On the other hand, it incentivises us to keep +business logic separated in external modules like service classes, +thus enforcing the [Single Responsibility Principle]. + + +[Committee]: https://github.com/interagent/committee +[Don't Repeat Yourself]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself +[Dredd]: https://github.com/apiaryio/dredd +[jBuilder]: https://github.com/rails/jbuilder +[MultiJSON][https://github.com/intridea/multi_json] +[Oj]: https://github.com/ohler55/oj +[OpenAPI Specification 2.0]: https://swagger.io/specification/v2/ +[Single Responsibility Principle]: https://en.wikipedia.org/wiki/Single_responsibility_principle +[SwaggeUI]: https://swagger.io/tools/swagger-ui diff --git a/docs/rails.md b/docs/rails.md index c494225..6d8d350 100644 --- a/docs/rails.md +++ b/docs/rails.md @@ -1,19 +1,25 @@ -# Rails conventions +# Rails-specific conventions -## App behavior should be configured via environment variables +## Application configuration -When implementing new features, it should not depend on current -`RAILS_ENV` value, but environment variable(s) specific to the -feature. +Application configuration should **depend on environment variables**, +and absolutely **not rely on `RAILS_ENV`** value (nor `Rails.env`), +respecting the [12-factor app methodology][12-factor config]. -* Avoid modifying `config/environments/*.rb`, and keep default - Rails configuration (this will also help when updating Rails) -* Avoid testing: +Specifically, the following rules should be followed: + +* Avoid modifying `config/environments/*.rb`: keep default Rails configuration + in there untouched (this will also make Rails upgrades a lot smoother) +* Write application configuration in `config/application.rb` +* Avoid testing any of: + * `ENV["RACK_ENV"]` + * `ENV["RAILS_ENV"]` * `Rails.env.development?` * `Rails.env.production?` * `Rails.env.test?` Examples: + ``` ruby # bad: module MyRailsApp @@ -75,3 +81,6 @@ Rails.application.routes.draw do end end ``` + + +[12-factor config]: https://12factor.net/config