diff --git a/.github/workflows/ruby_test.yml b/.github/workflows/ruby_test.yml index 6471a3f..fac765f 100644 --- a/.github/workflows/ruby_test.yml +++ b/.github/workflows/ruby_test.yml @@ -12,12 +12,20 @@ jobs: name: Rspec runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0' + ruby: [ "3.0", "3.1", "3.2", "3.3", "3.4", "4.0" ] steps: - name: Checkout Repository uses: sanger/.github/.github/actions/setup/checkout@master - - name: Setup Ruby - uses: sanger/.github/.github/actions/setup/ruby@master + - name: Setup Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run rspec run: bundle exec rspec @@ -25,7 +33,7 @@ jobs: - name: Upload coverage reports to Codecov uses: sanger/.github/.github/actions/tests/codecov@master with: - name: ${{ github.run_id }}_${{ github.job }}_${{ github.event_name }} + name: ${{ github.run_id }}_${{ github.job }}_${{ github.event_name }}_${{ matrix.ruby }} token: ${{ secrets.CODECOV_TOKEN }} flags: ${{ github.event_name }} disable-search: true diff --git a/.rubocop.yml b/.rubocop.yml index c29e870..bde0f27 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,5 @@ --- -inherit_from: ".rubocop_todo.yml" - # The behaviour of RuboCop can be controlled via the .rubocop.yml # configuration file. It makes it possible to enable/disable # certain cops (checks) and to alter their behaviour if they accept @@ -13,12 +11,15 @@ inherit_from: ".rubocop_todo.yml" # # See https://docs.rubocop.org/rubocop/latest/configuration.html +inherit_from: ".rubocop_todo.yml" + plugins: - rubocop-rake - rubocop-rspec AllCops: NewCops: enable + TargetRubyVersion: 3.0 Gemspec/DevelopmentDependencies: EnforcedStyle: gemspec diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 601fba3..7ff2a52 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2026-04-29 17:06:59 UTC using RuboCop version 1.86.1. +# on 2026-04-29 18:48:04 UTC using RuboCop version 1.86.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -12,13 +12,6 @@ Gemspec/RequireMFA: Exclude: - 'record_loader.gemspec' -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. -Style/GuardClause: - Exclude: - - 'record_loader.gemspec' - # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ReduceToHash: diff --git a/CHANGELOG.md b/CHANGELOG.md index ba89a46..456086b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,29 @@ # Changelog Keeps track of notable changes. Please remember to add new behaviours to the -Unreleased section to make new releases easy. +Unreleased section to make new releases easy. +Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] + +- [Changed] Version numbering to follow [Ruby Gem versioning guidelines](https://guides.rubygems.org/patterns/) +- [Changed] Explicitly require and use Psych gem for YAML parsing +- [Added] Add support for Ruby 3.1 - 4.0 +- [Documentation] Update documentation to reflect changes in versioning and supported Ruby versions + +## [0.3.0] + +- [Breaking] Remove support for Ruby 2.5 +- [Added] Add support for Ruby 3.0 ## [0.2.0] - [Added] `RecordLoader.export_attributes` for easy generation of yaml from - existing data + existing data - [Added] Improved feedback if exceptions raised during record creation - [Added] Improved templated yml files to use attributes from table - [Changed] Update name of yaml files generated as part of tests. - No changes are required to existing loaders. + No changes are required to existing loaders. - [Fixed] Default yaml files correctly templated ## [0.1.1] diff --git a/Gemfile.lock b/Gemfile.lock index 3196b27..6b85389 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,8 @@ PATH remote: . specs: - record_loader (0.3.0) + record_loader (1.0.0) + psych (~> 3.0) GEM remote: https://rubygems.org/ @@ -13,7 +14,11 @@ GEM json (2.19.4) language_server-protocol (3.17.0.5) lint_roller (1.1.0) + logger (1.7.0) + mdtoc (0.3.1) + sorbet-runtime method_source (1.0.0) + ostruct (0.6.3) parallel (1.28.0) parser (3.3.11.1) ast (~> 2.4.1) @@ -22,6 +27,7 @@ GEM pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) + psych (3.3.4) racc (1.8.1) rainbow (3.1.1) rake (13.0.6) @@ -67,6 +73,7 @@ GEM simplecov-html (0.13.2) simplecov-lcov (0.9.0) simplecov_json_formatter (0.1.4) + sorbet-runtime (0.6.13169) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.2.0) @@ -76,7 +83,10 @@ PLATFORMS ruby DEPENDENCIES - bundler (~> 2.3) + bundler (~> 2.5) + logger (~> 1.7) + mdtoc (~> 0.3.1) + ostruct (~> 0.6.3) pry (~> 0.14) rake (~> 13.0) record_loader! @@ -89,4 +99,4 @@ DEPENDENCIES yard (~> 0.9) BUNDLED WITH - 2.3.21 + 2.5.9 diff --git a/README.md b/README.md index 93b1f75..c74d5f7 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,28 @@ your production and development environments. While written with ActiveRecord/Rails in mind, it is possible to use RecordLoader in different environments. + +* [RecordLoader](README.md#recordloader) + * [Key features](README.md#key-features) + * [Version compatibility](README.md#version-compatibility) + * [Installation](README.md#installation) + * [How to run](README.md#how-to-run) + * [How to generate new records loaders for your project (Rails)](README.md#how-to-generate-new-records-loaders-for-your-project-rails) + * [An example loader](README.md#an-example-loader) + * [Dev and Wip files](README.md#dev-and-wip-files) + * [RecordLoader Dependencies](README.md#recordloader-dependencies) + * [Triggering on deployment](README.md#triggering-on-deployment) + * [Within Sanger PSD](README.md#within-sanger-psd) + * [Non Rails Environments](README.md#non-rails-environments) + * [Development](README.md#development) + * [Setup](README.md#setup) + * [Testing](README.md#testing) + * [Releasing](README.md#releasing) + * [Contributing](README.md#contributing) + * [License](README.md#license) + * [Updating the table of contents](README.md#updating-the-table-of-contents) + + ## Key features - Produce testable, reproducible data migrations across multiple environments @@ -18,6 +40,24 @@ While written with ActiveRecord/Rails in mind, it is possible to use RecordLoade - Keep work-in-progress isolated with .wip.yml files - Rails generators to quickly create new record loaders +## Version compatibility + +If using with Rails: + +| Rails Version | RecordLoader Version | +| ------------- | -------------------- | +| 6.x and below | 0.3.0\*, 1.0.0 | + +_\* Pin psych < 4_ + +If using with Ruby directly: + +| Ruby Version | RecordLoader Version | +| ------------ | -------------------- | +| 4.x | 1.x | +| 3.x | 0.3.0, 1.x | +| 2.5 - 2.7 | 0.2.0 | + ## Installation Add this line to your application's Gemfile: @@ -82,62 +122,62 @@ Suppose you want to create a loader to maintain a selection of product types. Yo This will create several files: -#### `lib/tasks/record_loader.rake` +- `lib/tasks/record_loader.rake` -Adds the record_loader:all rake task which can be used to trigger all record loaders. + Adds the record_loader:all rake task which can be used to trigger all record loaders. -#### `lib/record_loader/application_record_loader.rb` +- `lib/record_loader/application_record_loader.rb` -Application specific base class for customization. + Application specific base class for customization. -#### `config/record_loader/product_types/default_records.yml` +- `config/record_loader/product_types/default_records.yml` -Example yaml file to begin populating with your record information. Record Loaders will load all yaml files from within -this directory, so it is possible to separate your records into multiple different files for better organization. -In addition yaml files ending in `.dev.yml` and `.wip.yml` exhibit special behaviour. -See [dev and wip files](#dev-and-wip). + Example yaml file to begin populating with your record information. Record Loaders will load all yaml files from within + this directory, so it is possible to separate your records into multiple different files for better organization. + In addition yaml files ending in `.dev.yml` and `.wip.yml` exhibit special behaviour. + See [dev and wip files](#dev-and-wip). -#### `lib/record_loader/product_type_loader.rb` +- `lib/record_loader/product_type_loader.rb` -The actual loader. It will look something like this: + The actual loader. It will look something like this: -```ruby -# frozen_string_literal: true -# This file was automatically generated via `rails g record_loader` - -# RecordLoader handles automatic population and updating of database records -# across different environments -# @see https://rubydoc.info/github/sanger/record_loader/ -module RecordLoader - # Creates the specified plate types if they are not present - class ProductTypeLoader < ApplicationRecordLoader - config_folder 'product_types' - - def create_or_update!(name, options) - ProductType.create_with(options).find_or_create_by!(name: name) + ```ruby + # frozen_string_literal: true + # This file was automatically generated via `rails g record_loader` + + # RecordLoader handles automatic population and updating of database records + # across different environments + # @see https://rubydoc.info/github/sanger/record_loader/ + module RecordLoader + # Creates the specified plate types if they are not present + class ProductTypeLoader < ApplicationRecordLoader + config_folder 'product_types' + + def create_or_update!(name, options) + ProductType.create_with(options).find_or_create_by!(name: name) + end end end -end -``` + ``` -The `config_folder` specifies which directory under `config/record_loader` will be used to source the yaml files. -The method `create_or_update!` will create the actual records, and should be idempotent (ie. calling it multiple times will -have the same effect as calling it once). `create_or_update!` will be called once for each entry in the yaml files, -with the first argument being the key, and the second argument being the value, usually a hash of options. + The `config_folder` specifies which directory under `config/record_loader` will be used to source the yaml files. + The method `create_or_update!` will create the actual records, and should be idempotent (ie. calling it multiple times will + have the same effect as calling it once). `create_or_update!` will be called once for each entry in the yaml files, + with the first argument being the key, and the second argument being the value, usually a hash of options. -#### `lib/record_loader/tasks/record_loader/product_type.rake` +- `lib/record_loader/tasks/record_loader/product_type.rake` -This contains the `record_loader:product_type` which will trigger the record loader, and also ensures that -`record_loader:product_type` will get invoked on calling `record_loader:all`. + This contains the `record_loader:product_type` which will trigger the record loader, and also ensures that + `record_loader:product_type` will get invoked on calling `record_loader:all`. -#### `spec/data/record_loader/product_types/product_types_basic.yml` +- `spec/data/record_loader/product_types/product_types_basic.yml` -A basic configuration for testing the loader. Tests use a separate directory to avoid coupling your specs to the data. + A basic configuration for testing the loader. Tests use a separate directory to avoid coupling your specs to the data. -#### `spec/lib/record_loader/product_type_loader_spec.rb` +- `spec/lib/record_loader/product_type_loader_spec.rb` -A basic rspec spec file for testing your loader. By default this just confirms that your loader creates the -expected number of records, and that it is idempotent. + A basic rspec spec file for testing your loader. By default this just confirms that your loader creates the + expected number of records, and that it is idempotent. ## Dev and Wip files @@ -206,11 +246,20 @@ See {RecordLoader::Adapter} for information about custom adapters. ## Development -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can -also run `bin/console` for an interactive prompt that will allow you to experiment. +### Setup + +After checking out the repo, run `bin/setup` to install dependencies. + +### Testing + +Then, run `bundle rspec` to run the unit tests. +You can also run `bin/console` for an interactive prompt that will allow you to experiment. +To install this gem onto your local machine, run `bundle exec rake install`. -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the -version number in `version.rb`, ensure the CHANGELOG.md is updated and that everything is committed. +### Releasing + +To release a new version, update the version number in `version.rb`, ensure the CHANGELOG.md is updated +and that everything is committed. Then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). @@ -221,3 +270,12 @@ Bug reports and pull requests are welcome on GitHub at 3.0' + # Development dependencies - spec.add_development_dependency 'bundler', '~> 2.3' + spec.add_development_dependency 'bundler', '~> 2.5' + spec.add_development_dependency 'logger', '~> 1.7' + spec.add_development_dependency 'mdtoc', '~> 0.3.1' + spec.add_development_dependency 'ostruct', '~> 0.6.3' spec.add_development_dependency 'pry', '~> 0.14' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rspec', '~> 3.12' @@ -50,4 +51,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'simplecov', '~> 0.22' spec.add_development_dependency 'simplecov-lcov', '~> 0.9' spec.add_development_dependency 'yard', '~> 0.9' + + # For more information and examples about making a new gem, check out our + # guide at: https://bundler.io/guides/creating_gem.html end