Skip to content

Commit

Permalink
Update Ruby & Rails version support (#13)
Browse files Browse the repository at this point in the history
- Add Ruby 3.2 support.
- Drop end of life Ruby and Rails versions support.
- Address rubocop offenses (detailed per commit).

---------

Co-authored-by: Irving Caro <112433591+betterment-coding-agent@users.noreply.github.com>
  • Loading branch information
Irving-Betterment and Irving-Betterment committed Jan 4, 2024
1 parent f646661 commit 3009b8a
Show file tree
Hide file tree
Showing 19 changed files with 87 additions and 84 deletions.
12 changes: 1 addition & 11 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,10 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [2.6, 2.7, 3.0, 3.1]
ruby: [3.0, 3.1, 3.2]
gemfile:
- gemfiles/rails_5_2.gemfile
- gemfiles/rails_6_0.gemfile
- gemfiles/rails_6_1.gemfile
- gemfiles/rails_7_0.gemfile
exclude:
- ruby: 3.1
gemfile: gemfiles/rails_5_2.gemfile
- ruby: 3.0
gemfile: gemfiles/rails_5_2.gemfile
- ruby: 2.6
gemfile: gemfiles/rails_7_0.gemfile

steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
Expand Down
5 changes: 4 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ inherit_gem:
- config/default.yml

AllCops:
TargetRubyVersion: 2.6
TargetRubyVersion: 3.0
NewCops: enable

Gemspec/DevelopmentDependencies:
Enabled: false
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.7.5
3.2.2
8 changes: 1 addition & 7 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
appraise 'rails-5-2' do
gem 'activemodel', '~> 5.2.0'
end

appraise 'rails-6-0' do
gem 'activemodel', '~> 6.0.0'
end
# frozen_string_literal: true

appraise 'rails-6-1' do
gem 'activemodel', '~> 6.1.0'
Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ and this project aims to adhere to [Semantic Versioning](http://semver.org/spec/
### Removed <!-- for now removed features. -->
### Fixed <!-- for any bug fixes. -->

## [1.1.0] - 2023-12-29
### Added
- Added testing support for Ruby 3.2.
### Removed
- Drops support for Ruby < 3.0
- Drops support for Rails < 6.1

## [1.0.0] - 2022-02-10
### Added
- Added testing support for Ruby 2.7, 3.0, and 3.1
Expand All @@ -34,7 +41,7 @@ and this project aims to adhere to [Semantic Versioning](http://semver.org/spec/
### Added
- The initial open source release! See the README for all available features.


[1.1.0]: https://github.com/betterment/uncruft/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/betterment/uncruft/compare/v0.1.0...v1.0.0
[0.1.0]: https://github.com/betterment/uncruft/compare/v0.0.1...v0.1.0
[0.0.1]: https://github.com/betterment/uncruft/releases/tag/v0.0.1
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

begin
require 'bundler/setup'
rescue LoadError
Expand Down
7 changes: 0 additions & 7 deletions gemfiles/rails_5_2.gemfile

This file was deleted.

7 changes: 0 additions & 7 deletions gemfiles/rails_6_0.gemfile

This file was deleted.

2 changes: 2 additions & 0 deletions gemfiles/rails_6_1.gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# This file was generated by Appraisal

source "https://rubygems.org"
Expand Down
2 changes: 2 additions & 0 deletions gemfiles/rails_7_0.gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# This file was generated by Appraisal

source "https://rubygems.org"
Expand Down
2 changes: 2 additions & 0 deletions lib/steady_state.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'active_support'
require 'active_support/core_ext'
require 'active_model'
Expand Down
18 changes: 10 additions & 8 deletions lib/steady_state/attribute.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'steady_state/attribute/state'
require 'steady_state/attribute/state_machine'
require 'steady_state/attribute/transition_validator'
Expand All @@ -17,26 +19,26 @@ def steady_state(attr_name, predicates: true, states_getter: true, scopes: Stead
overrides = Module.new do
define_method :"validate_#{attr_name}_transition_to" do |next_value|
if public_send(attr_name).may_become?(next_value)
remove_instance_variable("@last_valid_#{attr_name}") if instance_variable_defined?("@last_valid_#{attr_name}")
elsif !instance_variable_defined?("@last_valid_#{attr_name}")
instance_variable_set("@last_valid_#{attr_name}", public_send(attr_name))
remove_instance_variable(:"@last_valid_#{attr_name}") if instance_variable_defined?(:"@last_valid_#{attr_name}")
elsif !instance_variable_defined?(:"@last_valid_#{attr_name}")
instance_variable_set(:"@last_valid_#{attr_name}", public_send(attr_name))
end
end

define_method :"#{attr_name}=" do |value|
unless instance_variable_defined?("@#{attr_name}_state_initialized")
instance_variable_set("@#{attr_name}_state_initialized", true)
unless instance_variable_defined?(:"@#{attr_name}_state_initialized")
instance_variable_set(:"@#{attr_name}_state_initialized", true)
end
public_send(:"validate_#{attr_name}_transition_to", value) if public_send(attr_name).present?
super(value)
end

define_method :"#{attr_name}" do |*args, &blk|
unless instance_variable_defined?("@#{attr_name}_state_initialized")
unless instance_variable_defined?(:"@#{attr_name}_state_initialized")
public_send(:"#{attr_name}=", state_machines[attr_name].start) if super(*args, &blk).blank?
instance_variable_set("@#{attr_name}_state_initialized", true)
instance_variable_set(:"@#{attr_name}_state_initialized", true)
end
last_valid_value = instance_variable_get("@last_valid_#{attr_name}") if instance_variable_defined?("@last_valid_#{attr_name}")
last_valid_value = instance_variable_get(:"@last_valid_#{attr_name}") if instance_variable_defined?(:"@last_valid_#{attr_name}")
state_machines[attr_name].new_state super(*args, &blk), last_valid_value
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/steady_state/attribute/state.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module SteadyState
module Attribute
class State < SimpleDelegator
Expand Down
2 changes: 2 additions & 0 deletions lib/steady_state/attribute/state_machine.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module SteadyState
module Attribute
class StateMachine
Expand Down
4 changes: 3 additions & 1 deletion lib/steady_state/attribute/transition_validator.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# frozen_string_literal: true

module SteadyState
module Attribute
class TransitionValidator < ActiveModel::EachValidator
def validate_each(obj, attr_name, _value)
obj.errors.add(attr_name, :invalid) if obj.instance_variable_defined?("@last_valid_#{attr_name}")
obj.errors.add(attr_name, :invalid) if obj.instance_variable_defined?(:"@last_valid_#{attr_name}")
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/steady_state/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module SteadyState
VERSION = '1.0.0'.freeze
VERSION = '1.1.0'
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# frozen_string_literal: true

require 'steady_state'
76 changes: 39 additions & 37 deletions spec/steady_state/attribute_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SteadyState::Attribute do
Expand Down Expand Up @@ -29,52 +31,52 @@ def self.model_name
it 'adds validation errors when initializing to an invalid state' do
object = steady_state_class.new(state: 'banana')
expect(object).not_to be_valid
expect(object.errors[:state]).to match_array(['is not included in the list'])
expect(object.errors[:state]).to contain_exactly('is not included in the list')
end

it 'allows valid transitions' do
expect(subject.state.may_become?('liquid')).to eq true
expect(subject.state.next_values).to match_array(['liquid'])
expect(subject.state.previous_values).to match_array([])
expect(subject.state.next_values).to contain_exactly('liquid')
expect(subject.state.previous_values).to be_empty
expect { subject.state = 'liquid' }.to change { subject.state }.from('solid').to('liquid')
expect(subject).to be_valid

expect(subject.state.may_become?('gas')).to eq true
expect(subject.state.next_values).to match_array(['gas'])
expect(subject.state.previous_values).to match_array(['solid'])
expect(subject.state.next_values).to contain_exactly('gas')
expect(subject.state.previous_values).to contain_exactly('solid')
expect { subject.state = 'gas' }.to change { subject.state }.from('liquid').to('gas')
expect(subject).to be_valid

expect(subject.state.may_become?('plasma')).to eq true
expect(subject.state.next_values).to match_array(['plasma'])
expect(subject.state.previous_values).to match_array(['liquid'])
expect(subject.state.next_values).to contain_exactly('plasma')
expect(subject.state.previous_values).to contain_exactly('liquid')
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
expect(subject).to be_valid
expect(subject.state.next_values).to be_empty
expect(subject.state.previous_values).to match_array(['gas'])
expect(subject.state.previous_values).to contain_exactly('gas')
end

it 'adds validation errors for invalid transitions' do
expect(subject.state.may_become?('gas')).to eq false
expect { subject.state = 'gas' }.to change { subject.state }.from('solid').to('gas')
expect(subject).not_to be_valid
expect(subject.errors[:state]).to match_array(['is invalid'])
expect(subject.state.next_values).to match_array(['liquid'])
expect(subject.state.previous_values).to match_array([])
expect(subject.errors[:state]).to contain_exactly('is invalid')
expect(subject.state.next_values).to contain_exactly('liquid')
expect(subject.state.previous_values).to be_empty

expect(subject.state.may_become?('plasma')).to eq false
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
expect(subject).not_to be_valid
expect(subject.errors[:state]).to match_array(['is invalid'])
expect(subject.state.next_values).to match_array(['liquid'])
expect(subject.state.previous_values).to match_array([])
expect(subject.errors[:state]).to contain_exactly('is invalid')
expect(subject.state.next_values).to contain_exactly('liquid')
expect(subject.state.previous_values).to be_empty

expect(subject.state.may_become?('solid')).to eq false
expect { subject.state = 'solid' }.to change { subject.state }.from('plasma').to('solid')
expect(subject).not_to be_valid
expect(subject.errors[:state]).to match_array(['is invalid'])
expect(subject.state.next_values).to match_array(['liquid'])
expect(subject.state.previous_values).to match_array([])
expect(subject.errors[:state]).to contain_exactly('is invalid')
expect(subject.state.next_values).to contain_exactly('liquid')
expect(subject.state.previous_values).to be_empty
end
end

Expand All @@ -97,7 +99,7 @@ def self.model_name
context 'with inheritance' do
let(:subclass) do
Class.new(steady_state_class) do
def initialize
def initialize # rubocop:disable Lint/MissingSuper
# I do my own thing.
end
end
Expand All @@ -123,7 +125,7 @@ def state
it 'does not allow initialization to an invalid next state' do
object = steady_state_class.new(state: 'solid')
expect(object).not_to be_valid
expect(object.errors[:state]).to match_array(['is invalid'])
expect(object.errors[:state]).to contain_exactly('is invalid')
end

it 'allows initialization to a valid next state' do
Expand All @@ -133,40 +135,40 @@ def state
it 'adds validation errors when initializing to an invalid state' do
object = steady_state_class.new(state: 'banana')
expect(object).not_to be_valid
expect(object.errors[:state]).to match_array(['is invalid', 'is not included in the list'])
expect(object.errors[:state]).to contain_exactly('is invalid', 'is not included in the list')
end

it 'allows valid transitions' do
expect(subject).to be_valid
expect(subject.state.may_become?('gas')).to eq true
expect(subject.state.next_values).to match_array(['gas'])
expect(subject.state.previous_values).to match_array(['solid'])
expect(subject.state.next_values).to contain_exactly('gas')
expect(subject.state.previous_values).to contain_exactly('solid')
expect { subject.state = 'gas' }.to change { subject.state }.from('liquid').to('gas')
expect(subject).to be_valid

expect(subject.state.may_become?('plasma')).to eq true
expect(subject.state.next_values).to match_array(['plasma'])
expect(subject.state.previous_values).to match_array(['liquid'])
expect(subject.state.next_values).to contain_exactly('plasma')
expect(subject.state.previous_values).to contain_exactly('liquid')
expect { subject.state = 'plasma' }.to change { subject.state }.from('gas').to('plasma')
expect(subject).to be_valid
expect(subject.state.next_values).to be_empty
expect(subject.state.previous_values).to match_array(['gas'])
expect(subject.state.previous_values).to contain_exactly('gas')
end

it 'adds validation errors for invalid transitions' do
expect(subject.state.may_become?('plasma')).to eq false
expect { subject.state = 'plasma' }.to change { subject.state }.from('liquid').to('plasma')
expect(subject).not_to be_valid
expect(subject.errors[:state]).to match_array(['is invalid'])
expect(subject.state.next_values).to match_array(['gas'])
expect(subject.state.previous_values).to match_array(['solid'])
expect(subject.errors[:state]).to contain_exactly('is invalid')
expect(subject.state.next_values).to contain_exactly('gas')
expect(subject.state.previous_values).to contain_exactly('solid')

expect(subject.state.may_become?('solid')).to eq false
expect { subject.state = 'solid' }.to change { subject.state }.from('plasma').to('solid')
expect(subject).not_to be_valid
expect(subject.errors[:state]).to match_array(['is invalid'])
expect(subject.state.next_values).to match_array(['gas'])
expect(subject.state.previous_values).to match_array(['solid'])
expect(subject.errors[:state]).to contain_exactly('is invalid')
expect(subject.state.next_values).to contain_exactly('gas')
expect(subject.state.previous_values).to contain_exactly('solid')
end
end
end
Expand All @@ -189,9 +191,9 @@ def state
expect(subject.step.may_become?('step-2')).to eq true
expect(subject.step.may_become?('cancelled')).to eq true
expect(subject.step.next_values).to match_array(%w(cancelled step-2))
expect(subject.step.previous_values).to match_array([])
expect(subject.step.previous_values).to be_empty
expect { subject.step = 'cancelled' }.to change { subject.step }.from('step-1').to('cancelled')
expect(subject.step.next_values).to match_array([])
expect(subject.step.next_values).to be_empty
expect(subject.step.previous_values).to match_array(%w(step-1 step-2))
expect(subject).to be_valid
end
Expand All @@ -201,17 +203,17 @@ def state
expect(subject.step.may_become?('step-2')).to eq true
expect(subject.step.may_become?('cancelled')).to eq true
expect(subject.step.next_values).to match_array(%w(cancelled step-2))
expect(subject.step.previous_values).to match_array([])
expect(subject.step.previous_values).to be_empty
expect { subject.step = 'step-2' }.to change { subject.step }.from('step-1').to('step-2')
expect(subject).to be_valid

expect(subject.step.may_become?('step-1')).to eq false
expect(subject.step.may_become?('step-2')).to eq false
expect(subject.step.may_become?('cancelled')).to eq true
expect(subject.step.next_values).to match_array(['cancelled'])
expect(subject.step.previous_values).to match_array(['step-1'])
expect(subject.step.next_values).to contain_exactly('cancelled')
expect(subject.step.previous_values).to contain_exactly('step-1')
expect { subject.step = 'cancelled' }.to change { subject.step }.from('step-2').to('cancelled')
expect(subject.step.next_values).to match_array([])
expect(subject.step.next_values).to be_empty
expect(subject.step.previous_values).to match_array(%w(step-1 step-2))
expect(subject).to be_valid
end
Expand Down
5 changes: 3 additions & 2 deletions steady_state.gemspec
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

$LOAD_PATH.push File.expand_path('lib', __dir__)

require 'steady_state/version'
Expand All @@ -19,9 +21,8 @@ Gem::Specification.new do |s|
}

s.files = Dir['{app,config,db,lib}/**/*', 'LICENSE', 'Rakefile', 'README.md']
s.test_files = Dir['spec/**/*']

s.required_ruby_version = '>= 2.6.5'
s.required_ruby_version = '>= 3.0'

s.add_dependency 'activemodel', '>= 5.2'
s.add_dependency 'activesupport', '>= 5.2'
Expand Down

0 comments on commit 3009b8a

Please sign in to comment.