Skip to content

Commit

Permalink
Allow params checks in preconditions
Browse files Browse the repository at this point in the history
  • Loading branch information
pyromaniac committed Dec 10, 2024
1 parent cdfc1e5 commit 89ff320
Show file tree
Hide file tree
Showing 20 changed files with 89 additions and 37 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ jobs:
- { ruby: '3.0', rails: '6.1' }
- { ruby: '3.1', rails: '7.0' }
- { ruby: '3.2', rails: '7.1' }
- { ruby: '3.3', rails: '7.2' }
- { ruby: '3.4', rails: '8.0' }
runs-on: ubuntu-latest
env:
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails.${{ matrix.rails }}.gemfile
Expand Down
3 changes: 2 additions & 1 deletion Appraisals
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# frozen_string_literal: true

%w[5.2 6.0 6.1 7.0 7.1].each do |version|
%w[5.2 6.0 6.1 7.0 7.1 7.2 8.0].each do |version|
appraise "rails.#{version}" do
gem "activerecord", "~> #{version}.0"
gem "activesupport", "~> #{version}.0"
gem "sqlite3", version > "7.0" ? "~> 2.1" : "~> 1.4"
end
end
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

## [main](https://github.com/BookingSync/operations/tree/main)

### Changes
- Changed `Operations::Command::OperationFailed#message` to include detailed error messages
### Added

- Allow receiving params in preconditions. [\#56](https://github.com/BookingSync/operations/pull/56) ([pyromaniac](https://github.com/pyromaniac))

### Changes

- Changed `Operations::Command::OperationFailed#message` to include detailed error messages. [\#55](https://github.com/BookingSync/operations/pull/55) ([Azdaroth](https://github.com/Azdaroth))
- Rename Operations::Form#model_name parameter to param_key and make it public preserving backwards compatibility. [\#52](https://github.com/BookingSync/operations/pull/52) ([pyromaniac](https://github.com/pyromaniac))

## [0.7.2](https://github.com/BookingSync/operations/tree/v0.7.2)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ When we need to check against the application state, preconditions are coming to

There are many potential scenarios when it can be handy. For example, we might need to render a button only when the subject entity satisfies preconditions for a particular operation. Or we want to return a list of possible operations from an API we have.

**Important:** a rule of thumb here is that preconditions don't depend on the user input, they only check the existing state of the application and they are supposed to access only the operation context for this purpose, not params.
**Important:** a rule of thumb here is that preconditions always depend on application/entities state. If a check depends only on params, then it is rather a Contract validation.

```ruby
class Post::Publish
Expand Down Expand Up @@ -425,7 +425,7 @@ class Post::Publish::NotPublishedPrecondition
include Dry::Monads[:result]

def call(post:, **)
return Failure(error: :already_published, tokens: { published_at: post.published_at }) if post.published?
return Failure(error: :already_published, path: [:post_id], tokens: { published_at: post.published_at }) if post.published?

Success()
end
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails.5.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 5.2.0"
gem "activesupport", "~> 5.2.0"
gem "sqlite3", "~> 1.4"

gemspec path: "../"
1 change: 1 addition & 0 deletions gemfiles/rails.6.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 6.0.0"
gem "activesupport", "~> 6.0.0"
gem "sqlite3", "~> 1.4"

gemspec path: "../"
1 change: 1 addition & 0 deletions gemfiles/rails.6.1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 6.1.0"
gem "activesupport", "~> 6.1.0"
gem "sqlite3", "~> 1.4"

gemspec path: "../"
1 change: 1 addition & 0 deletions gemfiles/rails.7.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 7.0.0"
gem "activesupport", "~> 7.0.0"
gem "sqlite3", "~> 1.4"

gemspec path: "../"
1 change: 1 addition & 0 deletions gemfiles/rails.7.1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 7.1.0"
gem "activesupport", "~> 7.1.0"
gem "sqlite3", "~> 2.1"

gemspec path: "../"
15 changes: 15 additions & 0 deletions gemfiles/rails.7.2.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bookingsync-rubocop", require: false, github: "BookingSync/bookingsync-rubocop", branch: "main"
gem "rspec"
gem "rubocop", require: false
gem "rubocop-performance", require: false
gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 7.2.0"
gem "activesupport", "~> 7.2.0"
gem "sqlite3", "~> 2.1"

gemspec path: "../"
15 changes: 15 additions & 0 deletions gemfiles/rails.8.0.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bookingsync-rubocop", require: false, github: "BookingSync/bookingsync-rubocop", branch: "main"
gem "rspec"
gem "rubocop", require: false
gem "rubocop-performance", require: false
gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "activerecord", "~> 8.0.0"
gem "activesupport", "~> 8.0.0"
gem "sqlite3", "~> 2.1"

gemspec path: "../"
2 changes: 1 addition & 1 deletion lib/operations/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class OperationFailed < StandardError

def initialize(operation_result)
@operation_result = operation_result
operation_class_name = operation_result.operation&.operation&.class&.name
operation_class_name = operation_result.operation.operation.class.name if operation_result.operation

super("#{operation_class_name} failed on #{operation_result.component}")
end
Expand Down
3 changes: 2 additions & 1 deletion lib/operations/components/preconditions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
class Operations::Components::Preconditions < Operations::Components::Prechecks
def call(params, context)
failures = callable.flat_map do |entry|
results = Array.wrap(entry.call(**context))
arg_names = call_args(entry, types: %i[req opt])
results = Array.wrap(arg_names.one? ? entry.call(params, **context) : entry.call(**context))
results.filter_map { |result| result_failure(result) }
end

Expand Down
2 changes: 1 addition & 1 deletion operations.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |spec|

spec.add_development_dependency "appraisal"
spec.add_development_dependency "database_cleaner-active_record"
spec.add_development_dependency "sqlite3", "~> 1.4"
spec.add_development_dependency "sqlite3", ">= 1.4"

spec.add_dependency "activerecord", ">= 5.2.0"
spec.add_dependency "activesupport", ">= 5.2.0"
Expand Down
8 changes: 4 additions & 4 deletions spec/operations/command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1005,13 +1005,13 @@ def call; end
end

describe "#pretty_inspect" do
subject(:pretty_inspect) { command.pretty_inspect }
subject(:pretty_inspect) { normalize_inspect(command.pretty_inspect) }

specify do
expect(pretty_inspect.gsub(%r{Proc:0x[^>]+}, "Proc:0x").gsub(%r{Class:0x[^>]+}, "Class:0x")).to eq(<<~INSPECT)
expect(pretty_inspect).to eq(<<~INSPECT)
#<Operations::Command
operation=#<Proc:0x>,
contract=#<#<Class:0x> schema=#<Dry::Schema::Processor keys=[:name] rules={:name=>"key?(:name) \
contract=#<#<Class:0x> schema=#<Dry::Schema::Processor keys=[:name] rules={name: "key?(:name) \
AND key[name](str? AND filled?)"}> rules=[#<Dry::Validation::Rule keys=[]>]>,
policies=[#<Proc:0x>],
idempotency=[],
Expand All @@ -1021,7 +1021,7 @@ def call; end
form_model_map={},
form_base=#<Class attributes={}>,
form_class=#<Class
attributes={:name=>
attributes={name:
#<Operations::Form::Attribute
name=:name,
collection=false,
Expand Down
9 changes: 4 additions & 5 deletions spec/operations/components/preconditions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,12 @@
{ text: "Failure 4", path: [:name, 1], code: :foobar }
]
},
->(**) { { error: "Failure", foo: 42, path: [nil] } },
->(params, **) { { error: "Failure", path: [nil], **params } },
->(**) {}
]
end

it "aggregates failures" do
pp call.errors.to_h
expect(call)
.to be_failure
.and have_attributes(
Expand All @@ -67,7 +66,7 @@
{ text: "Failure 1", code: :failure1 },
{ text: "Failure 1", code: :failure1 },
"failure3",
{ text: "Failure", foo: 42 }
{ text: "Failure", name: "Batman" }
],
name: [["failure2"], { 1 => [{ text: "Failure 4", code: :foobar }] }]
}
Expand All @@ -81,7 +80,7 @@
{ text: "Failure 1", code: :failure1 },
{ text: "Failure 1", code: :failure1 },
"failure3",
{ text: "Failure", foo: 42 }
{ text: "Failure", name: "Batman" }
],
name: [["name failure2"], { 1 => [{ code: :foobar, text: "1 Failure 4" }] }]
)
Expand All @@ -90,7 +89,7 @@
{ text: "Échec 1", code: :failure1 },
{ text: "Échec 1", code: :failure1 },
"failure3",
{ text: "Failure", foo: 42 }
{ text: "Failure", name: "Batman" }
],
name: [["failure2"], { 1 => [{ code: :foobar, text: "Failure 4" }] }]
)
Expand Down
30 changes: 15 additions & 15 deletions spec/operations/form/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,61 +77,61 @@
end

describe ".pretty_inspect" do
subject(:pretty_inspect) { form_class.pretty_inspect }
subject(:pretty_inspect) { normalize_inspect(form_class.pretty_inspect) }

specify do
expect(pretty_inspect).to eq(<<~INSPECT)
#<Class
attributes={:name=>
attributes={name:
#<Operations::Form::Attribute
name=:name,
collection=false,
model_class=nil,
model_attribute=nil,
form=nil>,
:tags=>
tags:
#<Operations::Form::Attribute
name=:tags,
collection=true,
model_class=nil,
model_attribute=nil,
form=nil>,
:author=>
author:
#<Operations::Form::Attribute
name=:author,
collection=false,
model_class=nil,
model_attribute=nil,
form=#<Class
attributes={:title=>
attributes={title:
#<Operations::Form::Attribute
name=:title,
collection=false,
model_class=nil,
model_attribute=nil,
form=nil>}>>,
:posts=>
posts:
#<Operations::Form::Attribute
name=:posts,
collection=true,
model_class=nil,
model_attribute=nil,
form=#<Class
attributes={:title=>
attributes={title:
#<Operations::Form::Attribute
name=:title,
collection=false,
model_class=nil,
model_attribute=nil,
form=nil>,
:id=>
id:
#<Operations::Form::Attribute
name=:id,
collection=false,
model_class=nil,
model_attribute=nil,
form=nil>,
:text=>
text:
#<Operations::Form::Attribute
name=:text,
collection=false,
Expand Down Expand Up @@ -497,7 +497,7 @@
end

describe "#pretty_inspect" do
subject(:pretty_inspect) { form.pretty_inspect }
subject(:pretty_inspect) { normalize_inspect(form.pretty_inspect) }

before do
allow(form.errors).to receive(:inspect).and_return("#<ActiveModel::Errors>")
Expand All @@ -507,11 +507,11 @@
specify do
expect(pretty_inspect).to eq(<<~INSPECT)
#<Dummy::Form
attributes={:name=>nil,
:tags=>[],
:author=>
#<Dummy::Author attributes={:title=>nil}, errors=#<ActiveModel::Errors>>,
:posts=>[]},
attributes={name: nil,
tags: [],
author:
#<Dummy::Author attributes={title: nil}, errors=#<ActiveModel::Errors>>,
posts: []},
errors=#<ActiveModel::Errors>>
INSPECT
end
Expand Down
10 changes: 5 additions & 5 deletions spec/operations/form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ def call(_, **)
end

describe "#pretty_print" do
subject(:pretty_inspect) { form.pretty_inspect }
subject(:pretty_inspect) { normalize_inspect(form.pretty_inspect) }

specify do
expect(pretty_inspect.gsub(%r{Proc:0x[^>]+}, "Proc:0x")).to eq(<<~INSPECT)
expect(pretty_inspect).to eq(<<~INSPECT)
#<Operations::Form
param_key="dummy_operation_form",
model_map=#<Proc:0x>,
Expand All @@ -175,21 +175,21 @@ def call(_, **)
hydrators=[#<Proc:0x>],
hydration_merge_params=true,
form_class=#<Class
attributes={:entities=>
attributes={entities:
#<Operations::Form::Attribute
name=:entities,
collection=true,
model_class=DummyModel,
model_attribute="entities",
form=#<Class
attributes={:id=>
attributes={id:
#<Operations::Form::Attribute
name=:id,
collection=false,
model_class=DummyModel,
model_attribute="id",
form=nil>}>>,
:name=>
name:
#<Operations::Form::Attribute
name=:name,
collection=false,
Expand Down
3 changes: 3 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "pp" # rubocop:disable Lint/RedundantRequireStatement
require "active_record"
require "database_cleaner-active_record"
require "./spec/support/test_helpers"

ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(nil)
Expand Down Expand Up @@ -37,6 +38,8 @@ class User < ActiveRecord::Base
config.order = :random
Kernel.srand config.seed

config.include TestHelpers

config.before(:suite) do
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :truncation
Expand Down
9 changes: 9 additions & 0 deletions spec/support/test_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module TestHelpers
def normalize_inspect(string)
string
.gsub(%r{Proc:0x[^>]+}, "Proc:0x")
.gsub(%r{Class:0x[^>]+}, "Class:0x")
.gsub(%r{:(\w+)=>\n}, "\\1:\n")
.gsub(%r{:(\w+)=>}, '\1: ')
end
end

0 comments on commit 89ff320

Please sign in to comment.