Skip to content

Commit

Permalink
Merge pull request #73 from s-andringa/overridable-save
Browse files Browse the repository at this point in the history
Add option to pass block to controller's #save!
  • Loading branch information
pond authored Oct 30, 2023
2 parents 9114583 + 67de7ac commit 773df2d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,35 @@ def record_to_scim(record)
# Save a record, dealing with validation exceptions by raising SCIM
# errors.
#
# +record+:: ActiveRecord subclass to save (via #save!).
# +record+:: ActiveRecord subclass to save.
#
# If you just let this superclass handle things, it'll call the standard
# +#save!+ method on the record. If you pass a block, then this block is
# invoked and passed the ActiveRecord model instance to be saved. You can
# then do things like calling a different method, using a service object of
# some kind, perform audit-related operations and so-on.
#
# The return value is not used internally, making life easier for
# overriding subclasses to "do the right thing" / avoid mistakes (instead
# of e.g. requiring that a to-SCIM representation of 'record' is returned
# and relying upon this to generate correct response payloads - an early
# version of the gem did this and it caused a confusing subclass bug).
#
def save!(record)
record.save!

def save!(record, &block)
if block_given?
yield(record)
else
record.save!
end
rescue ActiveRecord::RecordInvalid => exception
handle_invalid_record(exception.record)
end

# Deal with validation errors by responding with an appropriate SCIM error.
#
# +record+:: The record with validation errors.
#
def handle_invalid_record(record)
joined_errors = record.errors.full_messages.join('; ')

# https://tools.ietf.org/html/rfc7644#page-12
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# For tests only - uses custom 'save!' implementation which passes a block to
# Scimitar::ActiveRecordBackedResourcesController#save!.
#
class CustomSaveMockUsersController < Scimitar::ActiveRecordBackedResourcesController

CUSTOM_SAVE_BLOCK_USERNAME_INDICATOR = 'Custom save-block invoked'

protected

def save!(_record)
super do | record |
record.update!(username: CUSTOM_SAVE_BLOCK_USERNAME_INDICATOR)
end
end

def storage_class
MockUser
end

def storage_scope
MockUser.all
end

end
5 changes: 5 additions & 0 deletions spec/apps/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
#
delete 'CustomDestroyUsers/:id', to: 'custom_destroy_mock_users#destroy'

# For testing blocks passed to ActiveRecordBackedResourcesController#save!
#
post 'CustomSaveUsers', to: 'custom_save_mock_users#create'
get 'CustomSaveUsers/:id', to: 'custom_save_mock_users#show'

# For testing environment inside Scimitar::ApplicationController subclasses.
#
get 'CustomRequestVerifiers', to: 'custom_request_verifiers#index'
Expand Down
16 changes: 16 additions & 0 deletions spec/requests/active_record_backed_resources_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,22 @@
expect(result['scimType']).to eql('invalidValue')
expect(result['detail']).to include('is reserved')
end

it 'invokes a block if given one' do
mock_before = MockUser.all.to_a
attributes = { userName: '5' } # Minimum required by schema

expect_any_instance_of(CustomSaveMockUsersController).to receive(:create).once.and_call_original
expect {
post "/CustomSaveUsers", params: attributes.merge(format: :scim)
}.to change { MockUser.count }.by(1)

mock_after = MockUser.all.to_a
new_mock = (mock_after - mock_before).first

expect(response.status).to eql(201)
expect(new_mock.username).to eql(CustomSaveMockUsersController::CUSTOM_SAVE_BLOCK_USERNAME_INDICATOR)
end
end # "context '#create' do"

# ===========================================================================
Expand Down

0 comments on commit 773df2d

Please sign in to comment.