Gemwork

Gem Version

Gemwork is an abstract framework used by pdobb's Ruby Gems.

Support for Rails applications is also included, but not as the automatic default. See the "Rails" subheadings in the below sections.

Installation

Add this line to your my_gem.gemspec file:

spec.add_development_dependency "gemwork"

And then execute:

bundle

# OR

bin/setup

Rake Integration

Gemwork provides a number of additional Rake tasks that can be integrated into your project.

Create file: ./rakelib/gemwork.rake (If the rakelib directory doesn't exist, create it at the project root.)

# frozen_string_literal: true

spec = Gem::Specification.find_by_name("gemwork")

# Load additional tasks defined by Gemwork.
Dir.glob(
  Pathname.new(spec.gem_dir).
    join("lib", "tasks", "{util,rubocop,reek,test}.rake")) do |task|
  load(task)
end

# Redefine the default `rake` task.
Rake::Task["default"].clear
task :default do
  run_tasks(%i[
    test
    rubocop
    reek
  ])
end

Running rake -T after this will reveal the additional tasks defined by Gemwork just as if they were defined within your project.

Rails

For a Rails project, you may need to conditionally run the above by returning early unless the current environment is development. Further, you may want to include other tasks, such as eslint, prettier, and test:system.

# frozen_string_literal: true

return unless Rails.env.development?

spec = Gem::Specification.find_by_name("gemwork")

# Load additional tasks defined by Gemwork.
Dir.glob(
  Pathname.new(spec.gem_dir).
    join("lib", "tasks", "{util,rubocop,reek,eslint,prettier}.rake")) do |task|
  load(task)
end

# Redefine the default `rake` task.
Rake::Task["default"].clear
task :default do
  run_tasks(%i[
    test
    rubocop
    reek
    eslint
    prettier
    test:system
  ])
end

RuboCop Integration

Simple Usage

Add the following to the .rubocop.yml file in your gem:

# .rubocop.yml

inherit_gem:
  gemwork: lib/rubocop/.rubocop-gems.yml

Rails

Or, for a Rails project, add the following to the .rubocop.yml file in your project:

# .rubocop.yml
require:
  - rubocop-rake
  - rubocop-minitest
  - rubocop-performance

inherit_gem:
  gemwork: lib/rubocop/.rubocop-rails.yml

Advanced Usage

The above (simple usage) will automatically includes all RuboCop configuration settings defined in Gemwork. You can, however, require individual cop definitions piecemeal:

# .rubocop.yml

# Load RuboCop plugins.
require:
  - rubocop-rake
  - rubocop-minitest
  - rubocop-performance

# Load Cops configuration by Department.
inherit_gem:
  gemwork:
    - lib/rubocop/all_cops.yml
    - lib/rubocop/gemspec.yml
    - lib/rubocop/layout.yml
    - lib/rubocop/lint.yml
    - lib/rubocop/metrics.yml
    - lib/rubocop/minitest.yml
    - lib/rubocop/naming.yml
    - lib/rubocop/performance.yml
    - lib/rubocop/style.yml

# Override values from gemwork's lib/rubocop/all_cops.yml config.
AllCops:
  TargetRubyVersion: 2.7

See also: RuboCop's Configuration Guide on Inheritance.

Rails

# .rubocop.yml

# Load RuboCop plugins.
require:
  - rubocop-capybara
  - rubocop-minitest
  - rubocop-performance
  - rubocop-rake
  - rubocop-rails

# Load Cops configuration by Department.
inherit_gem:
  gemwork:
    - lib/rubocop/all_cops.yml
    - lib/rubocop/layout.yml
    - lib/rubocop/lint.yml
    - lib/rubocop/metrics.yml
    - lib/rubocop/minitest.yml
    - lib/rubocop/naming.yml
    - lib/rubocop/performance.yml
    - lib/rubocop/rails.yml
    - lib/rubocop/style.yml

# Override values from gemwork's lib/rubocop/all_cops.yml config.
AllCops:
  TargetRubyVersion: 3.3
  Exclude:
    - bin/bundle

Reek

The reek gem doesn't support config files integration. But recommended starting configs have been placed in lib/reek/. One for gems and one for Rails projects.

Testing Support

The following requires may be added to ./test/test_helper.rb to simplify test configuration. These requires support the gem dependencies mentioned in the following section.

# frozen_string_literal: true

require "gemwork/test/support/simplecov"

$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "say"

require "minitest/autorun"

require "gemwork/test/support/reporters"
require "gemwork/test/support/much_stub"
require "gemwork/test/support/spec_dsl"

# ...

Other Setup

Rails

For a Rails app, additional configuration may be desired to improve linter support.

eslint

The below fixes eslint linting errors in Rails (7+, ...) projects.

// .eslintrc.json

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": ["eslint:recommended", "plugin:prettier/recommended"],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "overrides": [
    {
      "files": ["config/tailwind.config.js"],
      "env": {
        "node": true
      },
      "rules": {
        "no-unused-vars": ["error", { "varsIgnorePattern": "defaultTheme" }]
      }
    }
  ]
}

prettier

General config recommendations for prettier:

# .prettierignore

vendor
// .prettierrc.json

{
  "semi": false
}

Gem Dependencies

Gemwork depends on the following gems. None of these are actually used by Gemwork, itself, but are included for use by child gems. This is meant to ease dependency management for child gems as these things evolve over time.

Unit Testing

Linters

  • reek -- Code smell detector for Ruby.
  • rubocop -- A Ruby static code analyzer and formatter, based on the community Ruby style guide.
  • rubocop-minitest -- Code style checking for Minitest files.
  • rubocop-performance -- An extension of RuboCop focused on code performance checks.
  • rubocop-rake -- A RuboCop plugin for Rake.

Documentation

  • yard -- YARD is a Ruby Documentation tool. The Y stands for "Yay!".

Rails

For Rails projects, you may want to manually install additional gems as well:

Linters

  • rubocop-rails -- A RuboCop extension focused on enforcing Rails best practices and coding conventions.
  • rubocop-capybara -- Code style checking for Capybara files.

Development

Development of Gemwork often requires making updates to its code and then testing them in another child gem that uses Gemwork.

Even if the child gem already has the gemwork gem installed from RubyGems, local changes to Gemwork can be compiled and installed as a local gem, which the child gem will then immediately utilize. To facilitate this, it is recommended to add this compile/local-install step to the child gem's bin/setup executable:

# Example ./bin/setup for your child gem:

#!/usr/bin/env bash

# Recompile and install Gemwork locally.
if [ -n "$REBUILD_GEMWORK" ]; then
  ( cd ~/dev/gemwork && bin/setup && rake install:local )
fi

set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here

With the above, you can opt in to using a locally built and installed Gemwork gem from your child gem:

REBUILD_GEMWORK=1 bin/setup

Testing

To test this gem (gemwork):

rake

Linters

rubocop

reek

npx prettier . --check
npx prettier . --write

Releases

To release a new version of Gemwork to RubyGems:

  1. Update the version number in version.rb
  2. Update CHANGELOG.md
  3. Run bundle to update Gemfile.lock with the latest version info
  4. Commit the changes. e.g. Bump to vX.Y.Z
  5. Run rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/pdobb/gemwork. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Gemwork project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.