diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 254b467..ffc670f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/Appraisals b/Appraisals index 6464102..d0607a0 100644 --- a/Appraisals +++ b/Appraisals @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b9cdb..85c26e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index e2ac5de..464daed 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/gemfiles/rails.5.2.gemfile b/gemfiles/rails.5.2.gemfile index f709f59..644c6b6 100644 --- a/gemfiles/rails.5.2.gemfile +++ b/gemfiles/rails.5.2.gemfile @@ -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: "../" diff --git a/gemfiles/rails.6.0.gemfile b/gemfiles/rails.6.0.gemfile index 1b85233..be371f5 100644 --- a/gemfiles/rails.6.0.gemfile +++ b/gemfiles/rails.6.0.gemfile @@ -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: "../" diff --git a/gemfiles/rails.6.1.gemfile b/gemfiles/rails.6.1.gemfile index c4b03f6..70dfe7b 100644 --- a/gemfiles/rails.6.1.gemfile +++ b/gemfiles/rails.6.1.gemfile @@ -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: "../" diff --git a/gemfiles/rails.7.0.gemfile b/gemfiles/rails.7.0.gemfile index ba8d46b..299c385 100644 --- a/gemfiles/rails.7.0.gemfile +++ b/gemfiles/rails.7.0.gemfile @@ -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: "../" diff --git a/gemfiles/rails.7.1.gemfile b/gemfiles/rails.7.1.gemfile index b181c1e..7068233 100644 --- a/gemfiles/rails.7.1.gemfile +++ b/gemfiles/rails.7.1.gemfile @@ -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: "../" diff --git a/gemfiles/rails.7.2.gemfile b/gemfiles/rails.7.2.gemfile new file mode 100644 index 0000000..3cf13b3 --- /dev/null +++ b/gemfiles/rails.7.2.gemfile @@ -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: "../" diff --git a/gemfiles/rails.8.0.gemfile b/gemfiles/rails.8.0.gemfile new file mode 100644 index 0000000..4bc1eac --- /dev/null +++ b/gemfiles/rails.8.0.gemfile @@ -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: "../" diff --git a/lib/operations/command.rb b/lib/operations/command.rb index f548b49..b2abfca 100644 --- a/lib/operations/command.rb +++ b/lib/operations/command.rb @@ -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 diff --git a/lib/operations/components/preconditions.rb b/lib/operations/components/preconditions.rb index 7c20d16..9b79c27 100644 --- a/lib/operations/components/preconditions.rb +++ b/lib/operations/components/preconditions.rb @@ -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 diff --git a/operations.gemspec b/operations.gemspec index a3c3336..8ec16c9 100644 --- a/operations.gemspec +++ b/operations.gemspec @@ -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" diff --git a/spec/operations/command_spec.rb b/spec/operations/command_spec.rb index 52b9357..d12c183 100644 --- a/spec/operations/command_spec.rb +++ b/spec/operations/command_spec.rb @@ -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) #, - contract=#<# schema=#"key?(:name) \ + contract=#<# schema=# rules=[#]>, policies=[#], idempotency=[], @@ -1021,7 +1021,7 @@ def call; end form_model_map={}, form_base=#, form_class=# + attributes={name: #(**) { { 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( @@ -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 }] }] } @@ -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" }] }] ) @@ -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" }] }] ) diff --git a/spec/operations/form/base_spec.rb b/spec/operations/form/base_spec.rb index 961ff56..63e4b99 100644 --- a/spec/operations/form/base_spec.rb +++ b/spec/operations/form/base_spec.rb @@ -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) # + attributes={name: #, - :tags=> + tags: #, - :author=> + author: # + attributes={title: #}>>, - :posts=> + posts: # + attributes={title: #, - :id=> + id: #, - :text=> + text: #") @@ -507,11 +507,11 @@ specify do expect(pretty_inspect).to eq(<<~INSPECT) #nil, - :tags=>[], - :author=> - #nil}, errors=#>, - :posts=>[]}, + attributes={name: nil, + tags: [], + author: + #>, + posts: []}, errors=#> INSPECT end diff --git a/spec/operations/form_spec.rb b/spec/operations/form_spec.rb index 905e94b..c3988d6 100644 --- a/spec/operations/form_spec.rb +++ b/spec/operations/form_spec.rb @@ -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) #, @@ -175,21 +175,21 @@ def call(_, **) hydrators=[#], hydration_merge_params=true, form_class=# + attributes={entities: # + attributes={id: #}>>, - :name=> + name: #]+}, "Proc:0x") + .gsub(%r{Class:0x[^>]+}, "Class:0x") + .gsub(%r{:(\w+)=>\n}, "\\1:\n") + .gsub(%r{:(\w+)=>}, '\1: ') + end +end