diff --git a/CHANGELOG.md b/CHANGELOG.md index a76309c..a2695bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Started raising a `NoResponseError` if an operation doesn't return a `result.response` skill + ## [2.2.0] ## Added diff --git a/lib/pragma/rails.rb b/lib/pragma/rails.rb index 167832d..3103657 100644 --- a/lib/pragma/rails.rb +++ b/lib/pragma/rails.rb @@ -6,13 +6,12 @@ require 'pragma/rails/version' require 'pragma/rails/controller' require 'pragma/rails/resource_controller' +require 'pragma/rails/errors' require 'generators/pragma/resource_generator' if defined?(::Rails::Generators) module Pragma # Ruby on Rails integration for the Pragma architecture. - # - # @author Alessandro Desantis module Rails end end diff --git a/lib/pragma/rails/controller.rb b/lib/pragma/rails/controller.rb index bceb815..b081bc5 100644 --- a/lib/pragma/rails/controller.rb +++ b/lib/pragma/rails/controller.rb @@ -4,8 +4,6 @@ module Pragma module Rails # This mixin should be included in a Rails controller to provide integration with Pragma # operations. - # - # @author Alessandro Desantis module Controller def self.included(klass) klass.include InstanceMethods @@ -28,6 +26,8 @@ def run(operation_klass) 'policy.context' => policy_context ) + fail NoResponseError unless result['result.response'] + result['result.response'].headers.each_pair do |key, value| response.headers[key] = value end diff --git a/lib/pragma/rails/errors.rb b/lib/pragma/rails/errors.rb new file mode 100644 index 0000000..5743a46 --- /dev/null +++ b/lib/pragma/rails/errors.rb @@ -0,0 +1,14 @@ +module Pragma + module Rails + class NoResponseError < StandardError + def initialize + super <<~MESSAGE + Your operation did not return a `result.response` skill, which might mean one of the following: + + * The execution of the operation halted early (maybe one of your steps returned a falsey value?). + * You forgot to set a `result.response` skill in a custom operation. + MESSAGE + end + end + end +end diff --git a/lib/pragma/rails/resource_controller.rb b/lib/pragma/rails/resource_controller.rb index 803876c..3adc626 100644 --- a/lib/pragma/rails/resource_controller.rb +++ b/lib/pragma/rails/resource_controller.rb @@ -3,8 +3,6 @@ module Pragma module Rails # Exposes CRUD operations on a resource through a Rails controller. - # - # @author Alessandro Desantis module ResourceController def self.included(klass) klass.include Controller diff --git a/spec/pragma/rails/controller_spec.rb b/spec/pragma/rails/controller_spec.rb index 2d6a076..e9300a8 100644 --- a/spec/pragma/rails/controller_spec.rb +++ b/spec/pragma/rails/controller_spec.rb @@ -5,56 +5,83 @@ RSpec.describe Pragma::Rails::Controller do subject { controller_klass.new.tap(&:run_operation) } - let(:controller_klass) do - Class.new do - attr_reader :render_args - - def run_operation - operation = Class.new(Pragma::Operation::Base) do - step :respond! - - def respond!(options, params:, **) - options['result.response'] = Pragma::Operation::Response::Created.new( - headers: { 'X-Foo' => params[:foo] }, - entity: { foo: params[:foo] } - ) + context 'when the operation executes successfully' do + let(:controller_klass) do + Class.new do + attr_reader :render_args + + def run_operation + operation = Class.new(Pragma::Operation::Base) do + step :respond! + + def respond!(options, params:, **) + options['result.response'] = Pragma::Operation::Response::Created.new( + headers: { 'X-Foo' => params[:foo] }, + entity: { foo: params[:foo] } + ) + end end + + run operation end - run operation - end + def response + @response ||= OpenStruct.new(headers: {}) + end - def response - @response ||= OpenStruct.new(headers: {}) - end + def render(*args) + @render_args = args + end - def render(*args) - @render_args = args - end + def params + {} + end + + protected - def params - {} + def operation_params + ActionController::Parameters.new(foo: 'bar') + end + end.tap do |klass| + klass.include described_class end + end - protected + it 'responds with the headers from the operation' do + expect(subject.response.headers).to eq('X-Foo' => 'bar') + end - def operation_params - ActionController::Parameters.new(foo: 'bar') - end - end.tap do |klass| - klass.include described_class + it 'responds with the status code from the operation' do + expect(subject.render_args.first[:status]).to eq(201) end - end - it 'responds with the headers from the operation' do - expect(subject.response.headers).to eq('X-Foo' => 'bar') + it 'responds with the resource from the operation' do + expect(subject.render_args.first[:json]).to eq('foo' => 'bar') + end end - it 'responds with the status code from the operation' do - expect(subject.render_args.first[:status]).to eq(201) - end + context 'when the operation does not set a result.response skill' do + let(:controller_klass) do + Class.new do + attr_reader :render_args + + def run_operation + operation = Class.new(Pragma::Operation::Base) + run operation + end - it 'responds with the resource from the operation' do - expect(subject.render_args.first[:json]).to eq('foo' => 'bar') + protected + + def operation_params + ActionController::Parameters.new(foo: 'bar') + end + end.tap do |klass| + klass.include described_class + end + end + + it 'raises a NoResponseError' do + expect { subject }.to raise_error(Pragma::Rails::NoResponseError) + end end end