shamu 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/shamu/attributes.rb +1 -0
- data/lib/shamu/attributes/assignment.rb +6 -0
- data/lib/shamu/auditing/support.rb +27 -25
- data/lib/shamu/entities/entity_path.rb +2 -1
- data/lib/shamu/events/support.rb +43 -4
- data/lib/shamu/services/active_record_crud.rb +59 -70
- data/lib/shamu/services/request.rb +31 -1
- data/lib/shamu/services/request_support.rb +5 -10
- data/lib/shamu/services/result.rb +15 -2
- data/lib/shamu/services/service.rb +0 -9
- data/lib/shamu/version.rb +1 -1
- data/spec/lib/shamu/auditing/support_spec.rb +17 -4
- data/spec/lib/shamu/events/support_spec.rb +21 -9
- data/spec/lib/shamu/services/active_record_crud_spec.rb +48 -55
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: dd0f794321cd3d306d36b16935e133e3ded9270b
         | 
| 4 | 
            +
              data.tar.gz: 244f8025c48879a691c7faa6f6e76b3b45efe2e0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 57d00e95ce2bf7a4d1ddbc224aff0a2790255a6bd1c1ac907f7450b95a30b63c7ca2cc404d0754c7ee363761386fc5c1567f1f50114957488d538be85d2cbda8
         | 
| 7 | 
            +
              data.tar.gz: e2ab805eb2ba0db7969601cb825f827c8d5346c8ebd34db8e836543a6726b7327c20b68f092681555b56f1832d8dd8512d08d8bc1ddaf3170e6f221d5b3ab1a0
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/lib/shamu/attributes.rb
    CHANGED
    
    | @@ -46,6 +46,7 @@ module Shamu | |
| 46 46 | 
             
                  self.class.attributes.each_with_object({}) do |(name, options), attrs|
         | 
| 47 47 | 
             
                    next if ( only && !match_attribute?( only, name ) ) || ( except && match_attribute?( except, name ) )
         | 
| 48 48 | 
             
                    next unless serialize_attribute?( name, options )
         | 
| 49 | 
            +
             | 
| 49 50 | 
             
                    attrs[name] = send( name )
         | 
| 50 51 | 
             
                  end
         | 
| 51 52 | 
             
                end
         | 
| @@ -12,6 +12,12 @@ module Shamu | |
| 12 12 | 
             
                    public :assign_attributes
         | 
| 13 13 | 
             
                  end
         | 
| 14 14 |  | 
| 15 | 
            +
                  # @param [Symbol] name of the attribute to assign.
         | 
| 16 | 
            +
                  # @param [Object] value to assign.
         | 
| 17 | 
            +
                  def []=( name, value )
         | 
| 18 | 
            +
                    send :"assign_#{ name }", value if attribute?( name )
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 15 21 | 
             
                  # @return [Array<Symbol>] the attributes that have been assigned.
         | 
| 16 22 | 
             
                  def assigned_attributes
         | 
| 17 23 | 
             
                    @assigned_attributes.to_a || []
         | 
| @@ -20,50 +20,52 @@ module Shamu | |
| 20 20 | 
             
                    #
         | 
| 21 21 | 
             
                    # @!endgroup Dependencies
         | 
| 22 22 |  | 
| 23 | 
            +
                    private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      # Override {Shamu::Services::RequestSupport#with_partial_request} and to yield
         | 
| 26 | 
            +
                      # a {Transaction} as an additional argument to automatically
         | 
| 27 | 
            +
                      # {#audit_request audit the request}.
         | 
| 28 | 
            +
                      def with_partial_request( *args, &block )
         | 
| 29 | 
            +
                        super( *args ) do |request|
         | 
| 30 | 
            +
                          audit_request request do |transaction|
         | 
| 31 | 
            +
                            yield request, transaction
         | 
| 32 | 
            +
                          end
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
             | 
| 23 36 | 
             
                  end
         | 
| 24 37 |  | 
| 25 38 | 
             
                  private
         | 
| 26 39 |  | 
| 27 | 
            -
                    # @!visibility public
         | 
| 28 | 
            -
                    #
         | 
| 29 | 
            -
                    # Same as {#audit_request} but does not validate the request before
         | 
| 30 | 
            -
                    # yielding to the block.
         | 
| 31 | 
            -
                    def audit_partial_request( params, request_class, action: :smart, &block )
         | 
| 32 | 
            -
                      perform_audit_request( :with_partial_request, params, request_class, action: action, &block )
         | 
| 33 | 
            -
                    end
         | 
| 34 40 |  | 
| 35 41 | 
             
                    # @!visibility public
         | 
| 36 42 | 
             
                    #
         | 
| 37 43 | 
             
                    # Audit the requested changes and report the request to the
         | 
| 38 44 | 
             
                    # {#auditing_service}.
         | 
| 39 45 | 
             
                    #
         | 
| 40 | 
            -
                    #  | 
| 41 | 
            -
                    #
         | 
| 42 | 
            -
                    # @param (see Shamu::Services::RequestSupport#with_request)
         | 
| 46 | 
            +
                    # @param [Services::Request] request the coerced request params.
         | 
| 43 47 | 
             
                    # @return (see Shamu::Services::RequestSupport#with_request)
         | 
| 44 | 
            -
                    # @yield ( | 
| 45 | 
            -
                    # @yieldparam [Services::Request] request coerced from `params`.
         | 
| 48 | 
            +
                    # @yield (transaction)
         | 
| 46 49 | 
             
                    # @yieldparam [Transaction] transaction the audit transaction. Most fields
         | 
| 47 50 | 
             
                    #     will be populated automatically from the request but the block
         | 
| 48 51 | 
             
                    #     should call {Transaction#append_entity} to include any parent
         | 
| 49 52 | 
             
                    #     entities in the entity path.
         | 
| 50 | 
            -
                     | 
| 51 | 
            -
             | 
| 52 | 
            -
                    end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
                    def perform_audit_request( method, params, request_class, action: :smart, &block )
         | 
| 53 | 
            +
                    # @yieldreturn [Services::Result]
         | 
| 54 | 
            +
                    def audit_request( request, action: :smart, &block )
         | 
| 55 55 | 
             
                      transaction = Transaction.new \
         | 
| 56 | 
            -
                        user_id_chain: auditing_security_principal.user_id_chain
         | 
| 56 | 
            +
                        user_id_chain: auditing_security_principal.user_id_chain,
         | 
| 57 | 
            +
                        changes: request.to_attributes( only: request.assigned_attributes ),
         | 
| 58 | 
            +
                        action: audit_request_action( request, action )
         | 
| 57 59 |  | 
| 58 | 
            -
                      result =  | 
| 59 | 
            -
             | 
| 60 | 
            -
                        transaction.changes = request.to_attributes
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                        yield request, transaction
         | 
| 63 | 
            -
                      end
         | 
| 60 | 
            +
                      result = yield transaction if block_given?
         | 
| 61 | 
            +
                      result = Services::Result.coerce( result, request: request )
         | 
| 64 62 |  | 
| 65 63 | 
             
                      if result.valid?
         | 
| 66 | 
            -
                         | 
| 64 | 
            +
                        if result.entity
         | 
| 65 | 
            +
                          transaction.append_entity result.entity
         | 
| 66 | 
            +
                        elsif request.respond_to?( :id ) && defined? entity_class
         | 
| 67 | 
            +
                          transaction.append_entity [ entity_class, request.id ]
         | 
| 68 | 
            +
                        end
         | 
| 67 69 | 
             
                        auditing_service.commit( transaction )
         | 
| 68 70 | 
             
                      end
         | 
| 69 71 |  | 
| @@ -70,12 +70,13 @@ module Shamu | |
| 70 70 | 
             
                    def entity_path_name( entity )
         | 
| 71 71 | 
             
                      case entity
         | 
| 72 72 | 
             
                      when String then entity.sub( /Entity$/, "" )
         | 
| 73 | 
            -
                      when Class  then  | 
| 73 | 
            +
                      when Class  then entity.model_name.name
         | 
| 74 74 | 
             
                      else             fail "Don't know how to compose #{ entity }"
         | 
| 75 75 | 
             
                      end
         | 
| 76 76 | 
             
                    end
         | 
| 77 77 |  | 
| 78 78 | 
             
                    def build_composed_entity_path( name, id )
         | 
| 79 | 
            +
                      id = id.to_model_id if id.respond_to?( :to_model_id )
         | 
| 79 80 | 
             
                      "#{ name }[#{ id }]"
         | 
| 80 81 | 
             
                    end
         | 
| 81 82 |  | 
    
        data/lib/shamu/events/support.rb
    CHANGED
    
    | @@ -21,20 +21,37 @@ module Shamu | |
| 21 21 |  | 
| 22 22 | 
             
                  end
         | 
| 23 23 |  | 
| 24 | 
            +
                  # (see Support.event_channel)
         | 
| 25 | 
            +
                  def event_channel
         | 
| 26 | 
            +
                    self.class.event_channel
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 24 29 | 
             
                  private
         | 
| 25 30 |  | 
| 26 31 | 
             
                    # @!visibility public
         | 
| 27 32 | 
             
                    #
         | 
| 28 33 | 
             
                    # Publish the given `message` to the {#events_service}.
         | 
| 29 34 | 
             
                    #
         | 
| 30 | 
            -
                    # @param [Events::Message] message the custom event specific message to
         | 
| 35 | 
            +
                    # @param [Events::Message, Symbol] message the custom event specific message to
         | 
| 31 36 | 
             
                    #     publish.
         | 
| 32 37 | 
             
                    # @param [String] channel to publish to.
         | 
| 38 | 
            +
                    # @param [Hash] message_attrs arguments to use when creating an
         | 
| 39 | 
            +
                    # instance of `message`.
         | 
| 40 | 
            +
                    #
         | 
| 41 | 
            +
                    # If `message` is a symbol, looks for a {Message} class in
         | 
| 42 | 
            +
                    # {ServiceNamespace}::{OptionalServiceDomain}Events::{name.caemlize}.
         | 
| 33 43 | 
             
                    # @return [void]
         | 
| 34 | 
            -
                    def event!( message, channel: event_channel )
         | 
| 44 | 
            +
                    def event!( message, channel: event_channel, **message_attrs )
         | 
| 45 | 
            +
                      if message.is_a?( Symbol )
         | 
| 46 | 
            +
                        message = self.class
         | 
| 47 | 
            +
                                      .event_message_namespace
         | 
| 48 | 
            +
                                      .const_get( message.to_s.camelize )
         | 
| 49 | 
            +
                                      .new( message_attrs )
         | 
| 50 | 
            +
                      end
         | 
| 35 51 | 
             
                      events_service.publish channel, message
         | 
| 36 52 | 
             
                    end
         | 
| 37 53 |  | 
| 54 | 
            +
                  class_methods do
         | 
| 38 55 | 
             
                    # @!visibility public
         | 
| 39 56 | 
             
                    #
         | 
| 40 57 | 
             
                    # The channel to {#publish_event publish events} to. Defaults to the
         | 
| @@ -47,7 +64,7 @@ module Shamu | |
| 47 64 | 
             
                    # @return [String] the name of the channel.
         | 
| 48 65 | 
             
                    def event_channel
         | 
| 49 66 | 
             
                      @event_channel ||= begin
         | 
| 50 | 
            -
                        base_name =  | 
| 67 | 
            +
                        base_name = name || "Events"
         | 
| 51 68 | 
             
                        parts     = base_name.split( "::" )
         | 
| 52 69 | 
             
                        parts[-1].sub!( /Service$/, "" )
         | 
| 53 70 | 
             
                        parts.pop if parts[-1] == parts[-2] || ( parts.length > 1 && parts[-1].blank? )
         | 
| @@ -55,6 +72,28 @@ module Shamu | |
| 55 72 | 
             
                      end
         | 
| 56 73 | 
             
                    end
         | 
| 57 74 |  | 
| 75 | 
            +
                    # The module that holds the per-message event classes for the service.
         | 
| 76 | 
            +
                    # @return [Module]
         | 
| 77 | 
            +
                    def event_message_namespace
         | 
| 78 | 
            +
                      @event_message_namespace ||=
         | 
| 79 | 
            +
                        begin
         | 
| 80 | 
            +
                          namespace = name.deconstantize
         | 
| 81 | 
            +
                          return unless namespace.present?
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                          namespace = namespace.constantize
         | 
| 84 | 
            +
                          domain    = name.demodulize.sub( /Service/, "" ).singularize
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                          # Must use exceptions instead of const_defined? so that rails has
         | 
| 87 | 
            +
                          # a change to autoload the constant.
         | 
| 88 | 
            +
                          begin
         | 
| 89 | 
            +
                            namespace.const_get( "#{ domain }Events" )
         | 
| 90 | 
            +
                          rescue NameError
         | 
| 91 | 
            +
                            namespace.const_get( "Events" )
         | 
| 92 | 
            +
                          end
         | 
| 93 | 
            +
                        end
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 58 97 | 
             
                end
         | 
| 59 98 | 
             
              end
         | 
| 60 | 
            -
            end
         | 
| 99 | 
            +
            end
         | 
| @@ -14,22 +14,22 @@ module Shamu | |
| 14 14 | 
             
                #
         | 
| 15 15 | 
             
                #     # Define finder methods #find, #list and #lookup using the given
         | 
| 16 16 | 
             
                #     # default scope.
         | 
| 17 | 
            -
                #      | 
| 17 | 
            +
                #     define_finders Models::User.active
         | 
| 18 18 | 
             
                #
         | 
| 19 19 | 
             
                #     # Define change methods
         | 
| 20 | 
            -
                #      | 
| 21 | 
            -
                #      | 
| 20 | 
            +
                #     define_create
         | 
| 21 | 
            +
                #     define_update
         | 
| 22 22 | 
             
                #
         | 
| 23 23 | 
             
                #     # Common update/change behavior for #create and #update
         | 
| 24 | 
            -
                #      | 
| 24 | 
            +
                #     define_change do |request, model|
         | 
| 25 25 | 
             
                #       model.last_updated_at = Time.now
         | 
| 26 26 | 
             
                #     end
         | 
| 27 27 | 
             
                #
         | 
| 28 28 | 
             
                #     # Standard destroy method
         | 
| 29 | 
            -
                #      | 
| 29 | 
            +
                #     define_destroy
         | 
| 30 30 | 
             
                #
         | 
| 31 31 | 
             
                #     # Build the entity class from the given record.
         | 
| 32 | 
            -
                #      | 
| 32 | 
            +
                #     define_build_entities do |records|
         | 
| 33 33 | 
             
                #       records.map do |record|
         | 
| 34 34 | 
             
                #         parent = lookup_association( record.parent_id, self ) do
         | 
| 35 35 | 
             
                #                    records.pluck( :parent_id )
         | 
| @@ -123,7 +123,7 @@ module Shamu | |
| 123 123 | 
             
                        send method
         | 
| 124 124 | 
             
                      end
         | 
| 125 125 |  | 
| 126 | 
            -
                       | 
| 126 | 
            +
                      define_build_entities( &block )
         | 
| 127 127 | 
             
                    end
         | 
| 128 128 |  | 
| 129 129 | 
             
                    # @return [Class] the {Entities::Entity} class that the service will
         | 
| @@ -138,49 +138,60 @@ module Shamu | |
| 138 138 | 
             
                      resource_not_configured
         | 
| 139 139 | 
             
                    end
         | 
| 140 140 |  | 
| 141 | 
            +
                    # Define all basic CRUD methods without any customization.
         | 
| 142 | 
            +
                    def define_crud
         | 
| 143 | 
            +
                      define_create
         | 
| 144 | 
            +
                      define_update
         | 
| 145 | 
            +
                      define_destroy
         | 
| 146 | 
            +
                      define_finders
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 141 149 | 
             
                    # Define a `#create` method on the service that takes a single {Request}
         | 
| 142 150 | 
             
                    # parameter.
         | 
| 143 151 | 
             
                    #
         | 
| 144 | 
            -
                    #  | 
| 145 | 
            -
                    # @ | 
| 146 | 
            -
                    # @yieldparam  | 
| 152 | 
            +
                    # @yield ( request, record, *args )
         | 
| 153 | 
            +
                    # @yieldparam [Services::Request] request object.
         | 
| 154 | 
            +
                    # @yieldparam [ActiveRecord::Base] record.
         | 
| 155 | 
            +
                    # @yieldparam [Array] args any additional arguments injected by an
         | 
| 156 | 
            +
                    # overridden {#with_request} method.
         | 
| 147 157 | 
             
                    # @return [void]
         | 
| 148 | 
            -
                    def  | 
| 158 | 
            +
                    def define_create( &block )
         | 
| 149 159 | 
             
                      define_method :create do |params = nil|
         | 
| 150 | 
            -
                        with_request params, request_class( :create ) do |request|
         | 
| 151 | 
            -
                          authorize! :create, entity_class, request
         | 
| 152 | 
            -
             | 
| 160 | 
            +
                        with_request params, request_class( :create ) do |request, *args|
         | 
| 153 161 | 
             
                          record = request.apply_to( model_class.new )
         | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
                             | 
| 162 | 
            +
             | 
| 163 | 
            +
                          if block_given?
         | 
| 164 | 
            +
                            result = instance_exec record, request, *args, &block
         | 
| 165 | 
            +
                            next result if result.is_a?( Services::Result )
         | 
| 158 166 | 
             
                          end
         | 
| 159 167 |  | 
| 168 | 
            +
                          authorize! :create, build_entity( record ), request
         | 
| 169 | 
            +
             | 
| 160 170 | 
             
                          next record unless record.save
         | 
| 161 171 | 
             
                          build_entity record
         | 
| 162 172 | 
             
                        end
         | 
| 163 173 | 
             
                      end
         | 
| 164 174 | 
             
                    end
         | 
| 165 175 |  | 
| 166 | 
            -
                    # rubocop:disable Metrics/MethodLength
         | 
| 167 | 
            -
             | 
| 168 176 | 
             
                    # Define an change `method` on the service that takes the id of the
         | 
| 169 177 | 
             
                    # resource to modify and a corresponding {Request} parameter.
         | 
| 170 178 | 
             
                    #
         | 
| 171 | 
            -
                    #  | 
| 172 | 
            -
                    # @ | 
| 173 | 
            -
                    # @yieldparam  | 
| 179 | 
            +
                    # @yield ( request, record, *args )
         | 
| 180 | 
            +
                    # @yieldparam [Services::Request] request object.
         | 
| 181 | 
            +
                    # @yieldparam [ActiveRecord::Base] record.
         | 
| 182 | 
            +
                    # @yieldparam [Array] args any additional arguments injected by an
         | 
| 183 | 
            +
                    # overridden {#with_request} method.
         | 
| 174 184 | 
             
                    # @return [Result] the result of the request.
         | 
| 175 185 | 
             
                    # @return [void]
         | 
| 176 | 
            -
                    def  | 
| 186 | 
            +
                    def define_change( method, default_scope = model_class, &block ) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/LineLength
         | 
| 177 187 | 
             
                      define_method method do |id, params = nil|
         | 
| 178 188 | 
             
                        klass = request_class( method )
         | 
| 179 189 |  | 
| 180 190 | 
             
                        params, id = id, id[ :id ] if !params && !id.respond_to?( :to_model_id )
         | 
| 191 | 
            +
                        params[ :id ] ||= id       if params
         | 
| 181 192 |  | 
| 182 | 
            -
                        with_partial_request params, klass do |request|
         | 
| 183 | 
            -
                          record =  | 
| 193 | 
            +
                        with_partial_request params, klass do |request, *args|
         | 
| 194 | 
            +
                          record = default_scope.find( id.to_model_id || request.id )
         | 
| 184 195 | 
             
                          entity = build_entity( record )
         | 
| 185 196 |  | 
| 186 197 | 
             
                          backfill_attributes = entity.to_attributes( only: request.unassigned_attributes )
         | 
| @@ -190,11 +201,9 @@ module Shamu | |
| 190 201 | 
             
                          authorize! method, entity, request
         | 
| 191 202 |  | 
| 192 203 | 
             
                          request.apply_to( record )
         | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
                             | 
| 196 | 
            -
                          elsif respond_to? :apply_changes
         | 
| 197 | 
            -
                            apply_changes( request, record )
         | 
| 204 | 
            +
                          if block_given?
         | 
| 205 | 
            +
                            result = instance_exec record, request, *args, &block
         | 
| 206 | 
            +
                            next result if result.is_a?( Services::Result )
         | 
| 198 207 | 
             
                          end
         | 
| 199 208 |  | 
| 200 209 | 
             
                          next record unless record.save
         | 
| @@ -203,69 +212,49 @@ module Shamu | |
| 203 212 | 
             
                      end
         | 
| 204 213 | 
             
                    end
         | 
| 205 214 |  | 
| 206 | 
            -
                    #  | 
| 207 | 
            -
             | 
| 208 | 
            -
             | 
| 209 | 
            -
                    # Define an `update` method on the service that takes the id of the
         | 
| 210 | 
            -
                    # resource to update and a {Request} parameter. After applying the
         | 
| 211 | 
            -
                    # changes the record is persisted and the updated entity result is
         | 
| 212 | 
            -
                    # returned.
         | 
| 213 | 
            -
                    #
         | 
| 214 | 
            -
                    # See {.apply_changes} for details.
         | 
| 215 | 
            -
                    # @yield (see .apply_changes)
         | 
| 216 | 
            -
                    # @yieldparam (see .apply_changes)
         | 
| 217 | 
            -
                    # @return [void]
         | 
| 218 | 
            -
                    def update( &block )
         | 
| 219 | 
            -
                      change :update, &block
         | 
| 215 | 
            +
                    # Defines an #update method. See {#define_change} for details.
         | 
| 216 | 
            +
                    def define_update( default_scope = model_class, &block )
         | 
| 217 | 
            +
                      define_change :update, default_scope, &block
         | 
| 220 218 | 
             
                    end
         | 
| 221 219 |  | 
| 222 220 | 
             
                    # Define a `destroy( id )` method that takes an {Entities::Entity} {Entities::Entity#id}
         | 
| 223 221 | 
             
                    # and destroys the resource.
         | 
| 224 222 | 
             
                    #
         | 
| 223 | 
            +
                    # @yield ( request, record, *args )
         | 
| 224 | 
            +
                    # @yieldparam [Services::Request] request object.
         | 
| 225 | 
            +
                    # @yieldparam [ActiveRecord::Base] record.
         | 
| 226 | 
            +
                    # @yieldparam [Array] args any additional arguments injected by an
         | 
| 227 | 
            +
                    # overridden {#with_request} method.
         | 
| 225 228 | 
             
                    # @param [ActiveRecord::Relation] default_scope to use when finding
         | 
| 226 229 | 
             
                    #     records.
         | 
| 227 230 | 
             
                    # @return [void]
         | 
| 228 | 
            -
                    def  | 
| 231 | 
            +
                    def define_destroy( default_scope = model_class, &block )
         | 
| 229 232 | 
             
                      define_method :destroy do |params|
         | 
| 230 233 | 
             
                        klass = request_class( :destroy )
         | 
| 231 234 |  | 
| 232 235 | 
             
                        params = { id: params } if params.respond_to?( :to_model_id )
         | 
| 233 236 |  | 
| 234 | 
            -
                        with_request params, klass do |request|
         | 
| 237 | 
            +
                        with_request params, klass do |request, *args|
         | 
| 235 238 | 
             
                          record = default_scope.find( request.id )
         | 
| 236 239 | 
             
                          authorize! :destroy, build_entity( record )
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                          instance_exec record, request, *args, &block if block_given?
         | 
| 237 242 | 
             
                          next record unless record.destroy
         | 
| 238 243 | 
             
                        end
         | 
| 239 244 | 
             
                      end
         | 
| 240 245 | 
             
                    end
         | 
| 241 246 |  | 
| 242 | 
            -
                    # Define a private method `apply_changes` on the service used by the
         | 
| 243 | 
            -
                    # {.create} and {.change} defined methods to apply changes in a
         | 
| 244 | 
            -
                    # {Request} to the model.
         | 
| 245 | 
            -
                    #
         | 
| 246 | 
            -
                    # @yield ( request, record ) a block that applies changes in the
         | 
| 247 | 
            -
                    #     `request` to the `record`.
         | 
| 248 | 
            -
                    # @yieldparam [Request] request the {Request} containing all the changes
         | 
| 249 | 
            -
                    #     that should be applied to the `record`.
         | 
| 250 | 
            -
                    # @yieldparam [ActiveRecord::Base] record the record to be updated.
         | 
| 251 | 
            -
                    # @yieldreturn [void]
         | 
| 252 | 
            -
                    # @return [void]
         | 
| 253 | 
            -
                    def apply_changes( &block )
         | 
| 254 | 
            -
                      define_method :apply_changes, &block
         | 
| 255 | 
            -
                      private :apply_changes
         | 
| 256 | 
            -
                    end
         | 
| 257 | 
            -
             | 
| 258 247 | 
             
                    # Define the standard finder methods {.find}, {.lookup} and {.list}.
         | 
| 259 248 | 
             
                    #
         | 
| 260 249 | 
             
                    # @param [ActiveRecord::Relation] default_scope to use when finding
         | 
| 261 250 | 
             
                    #     records.
         | 
| 262 251 | 
             
                    # @return [void]
         | 
| 263 | 
            -
                    def  | 
| 252 | 
            +
                    def define_finders( default_scope = model_class.all, only: nil, except: nil )
         | 
| 264 253 | 
             
                      methods = Array( only || [ :find, :lookup, :list ] )
         | 
| 265 254 | 
             
                      methods -= Array( except ) if except
         | 
| 266 255 |  | 
| 267 256 | 
             
                      methods.each do |method|
         | 
| 268 | 
            -
                        send method, default_scope
         | 
| 257 | 
            +
                        send :"define_#{ method }", default_scope
         | 
| 269 258 | 
             
                      end
         | 
| 270 259 | 
             
                    end
         | 
| 271 260 |  | 
| @@ -278,7 +267,7 @@ module Shamu | |
| 278 267 | 
             
                    # @yield (id)
         | 
| 279 268 | 
             
                    # @yieldreturn (ActiveRecord::Base) the found record.
         | 
| 280 269 | 
             
                    # @return [void]
         | 
| 281 | 
            -
                    def  | 
| 270 | 
            +
                    def define_find( default_scope = model_class.all, &block )
         | 
| 282 271 | 
             
                      if block_given?
         | 
| 283 272 | 
             
                        define_method :find do |id|
         | 
| 284 273 | 
             
                          wrap_not_found do
         | 
| @@ -305,7 +294,7 @@ module Shamu | |
| 305 294 | 
             
                    # @yieldreturn [ActiveRecord::Relation] records for ids found in the
         | 
| 306 295 | 
             
                    #     underlying resource.
         | 
| 307 296 | 
             
                    # @return [void]
         | 
| 308 | 
            -
                    def  | 
| 297 | 
            +
                    def define_lookup( default_scope = model_class.all, &block )
         | 
| 309 298 | 
             
                      define_method :lookup do |*ids|
         | 
| 310 299 | 
             
                        cached_lookup( ids ) do |uncached_ids|
         | 
| 311 300 | 
             
                          records = block_given? ? yield( uncached_ids ) : default_scope.where( id: uncached_ids )
         | 
| @@ -325,7 +314,7 @@ module Shamu | |
| 325 314 | 
             
                    # @yieldparam [ListScope] scope to apply.
         | 
| 326 315 | 
             
                    # @yieldreturn [ActiveRecord::Relation] records matching the given scope.
         | 
| 327 316 | 
             
                    # @return [void]
         | 
| 328 | 
            -
                    def  | 
| 317 | 
            +
                    def define_list( default_scope = model_class.all, &block )
         | 
| 329 318 | 
             
                      define_method :list do |params = nil|
         | 
| 330 319 | 
             
                        list_scope = Entities::ListScope.for( entity_class ).coerce( params )
         | 
| 331 320 | 
             
                        authorize! :list, entity_class, list_scope
         | 
| @@ -351,7 +340,7 @@ module Shamu | |
| 351 340 | 
             
                    #   entities.
         | 
| 352 341 | 
             
                    # @yieldreturn [Array<Entities::Entity>] the projected entities.
         | 
| 353 342 | 
             
                    # @return [void]
         | 
| 354 | 
            -
                    def  | 
| 343 | 
            +
                    def define_build_entities( &block )
         | 
| 355 344 | 
             
                      if block_given?
         | 
| 356 345 | 
             
                        define_method :build_entities, &block
         | 
| 357 346 | 
             
                      else
         | 
| @@ -374,7 +363,7 @@ module Shamu | |
| 374 363 |  | 
| 375 364 | 
             
                      def inferred_resource_name
         | 
| 376 365 | 
             
                        inferred = name || "Resource"
         | 
| 377 | 
            -
                        inferred.split( "::" ).last.sub( /Service/, "" )
         | 
| 366 | 
            +
                        inferred.split( "::" ).last.sub( /Service/, "" ).singularize
         | 
| 378 367 | 
             
                      end
         | 
| 379 368 |  | 
| 380 369 | 
             
                      def inferred_namespace
         | 
| @@ -25,7 +25,6 @@ module Shamu | |
| 25 25 | 
             
                class Request
         | 
| 26 26 | 
             
                  include Shamu::Attributes
         | 
| 27 27 | 
             
                  include Shamu::Attributes::Assignment
         | 
| 28 | 
            -
                  include Shamu::Attributes::FluidAssignment
         | 
| 29 28 | 
             
                  include Shamu::Attributes::Validation
         | 
| 30 29 |  | 
| 31 30 | 
             
                  # Applies the attributes of the request to the given model. Only handles
         | 
| @@ -53,6 +52,37 @@ module Shamu | |
| 53 52 | 
             
                    end
         | 
| 54 53 | 
             
                  end
         | 
| 55 54 |  | 
| 55 | 
            +
                  # Execute block if the request is satisfied by the service successfully.
         | 
| 56 | 
            +
                  def on_success( &block )
         | 
| 57 | 
            +
                    @on_success_callbacks ||= []
         | 
| 58 | 
            +
                    @on_success_callbacks << block
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  # Execute block if the request is not satisfied by the service.
         | 
| 62 | 
            +
                  def on_fail( &block )
         | 
| 63 | 
            +
                    @on_fail_callbacks ||= []
         | 
| 64 | 
            +
                    @on_fail_callbacks << block
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  # Execute block when the service is done processing the request.
         | 
| 68 | 
            +
                  def on_complete( &block )
         | 
| 69 | 
            +
                    @on_complete_callbacks ||= []
         | 
| 70 | 
            +
                    @on_complete_callbacks << block
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  # Run any {#on_success} or #{on_fail} callbacks.
         | 
| 74 | 
            +
                  # @param [Boolean] success true if the request was completed
         | 
| 75 | 
            +
                  # successfully.
         | 
| 76 | 
            +
                  def run_callbacks( success )
         | 
| 77 | 
            +
                    if success
         | 
| 78 | 
            +
                      @on_success_callbacks && @on_success_callbacks.each( &:call )
         | 
| 79 | 
            +
                    else
         | 
| 80 | 
            +
                      @on_fail_callbacks && @on_fail_callbacks.each( &:call )
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    @on_fail_callbacks && @on_fail_callbacks.each( &:call )
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 56 86 | 
             
                  class << self
         | 
| 57 87 | 
             
                    # Coerces a hash or params object to a proper {Request} object.
         | 
| 58 88 | 
             
                    # @param [Object] params to be coerced.
         | 
| @@ -70,10 +70,10 @@ module Shamu | |
| 70 70 | 
             
                    #     end
         | 
| 71 71 | 
             
                    #   end
         | 
| 72 72 | 
             
                    def with_request( params, request_class, &block )
         | 
| 73 | 
            -
                      with_partial_request params, request_class do |request|
         | 
| 73 | 
            +
                      with_partial_request params, request_class do |request, *args|
         | 
| 74 74 | 
             
                        next unless request.valid?
         | 
| 75 75 |  | 
| 76 | 
            -
                        yield request
         | 
| 76 | 
            +
                        yield request, *args
         | 
| 77 77 | 
             
                      end
         | 
| 78 78 | 
             
                    end
         | 
| 79 79 |  | 
| @@ -90,15 +90,10 @@ module Shamu | |
| 90 90 | 
             
                      request = request_class.coerce( params )
         | 
| 91 91 | 
             
                      sources = yield( request )
         | 
| 92 92 |  | 
| 93 | 
            -
                       | 
| 94 | 
            -
                       | 
| 95 | 
            -
                      request.valid?
         | 
| 93 | 
            +
                      result = Result.coerce( sources, request: request )
         | 
| 94 | 
            +
                      request.run_callbacks( result.valid? )
         | 
| 96 95 |  | 
| 97 | 
            -
                       | 
| 98 | 
            -
                        sources
         | 
| 99 | 
            -
                      else
         | 
| 100 | 
            -
                        result( *Array.wrap( sources ), request: request )
         | 
| 101 | 
            -
                      end
         | 
| 96 | 
            +
                      result
         | 
| 102 97 | 
             
                    end
         | 
| 103 98 |  | 
| 104 99 | 
             
                  # Static methods added to {RequestSupport}
         | 
| @@ -48,7 +48,7 @@ module Shamu | |
| 48 48 | 
             
                  #   the first {Request} object found in the `values`.
         | 
| 49 49 | 
             
                  # @param [Entities::Entity] entity submitted to the service. If :not_set,
         | 
| 50 50 | 
             
                  #   uses the first {Entity} object found in the `values`.
         | 
| 51 | 
            -
                  def initialize( *values, request: :not_set, entity: :not_set )
         | 
| 51 | 
            +
                  def initialize( *values, request: :not_set, entity: :not_set ) # rubocop:disable Metrics/LineLength, Metrics/MethodLength, Metrics/PerceivedComplexity
         | 
| 52 52 | 
             
                    @values = values
         | 
| 53 53 | 
             
                    @value  = values.first
         | 
| 54 54 |  | 
| @@ -60,6 +60,10 @@ module Shamu | |
| 60 60 | 
             
                    end
         | 
| 61 61 |  | 
| 62 62 | 
             
                    unless request == :not_set
         | 
| 63 | 
            +
                      # Make sure the request captures errors even if the block doesn't
         | 
| 64 | 
            +
                      # check
         | 
| 65 | 
            +
                      request && request.valid?
         | 
| 66 | 
            +
             | 
| 63 67 | 
             
                      @request = request
         | 
| 64 68 | 
             
                      append_error_source request
         | 
| 65 69 | 
             
                    end
         | 
| @@ -93,8 +97,16 @@ module Shamu | |
| 93 97 | 
             
                    self
         | 
| 94 98 | 
             
                  end
         | 
| 95 99 |  | 
| 96 | 
            -
                   | 
| 100 | 
            +
                  # @return [Result] the value coerced to a {Result}.
         | 
| 101 | 
            +
                  def self.coerce( value, **args )
         | 
| 102 | 
            +
                    if value.is_a?( Result )
         | 
| 103 | 
            +
                      value
         | 
| 104 | 
            +
                    else
         | 
| 105 | 
            +
                      Result.new( *Array.wrap( value ), **args )
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
                  end
         | 
| 97 108 |  | 
| 109 | 
            +
                  private
         | 
| 98 110 |  | 
| 99 111 | 
             
                    def append_error_source( source )
         | 
| 100 112 | 
             
                      return unless source.respond_to?( :errors )
         | 
| @@ -103,6 +115,7 @@ module Shamu | |
| 103 115 | 
             
                        errors.add attr, message unless errors[attr].include? message
         | 
| 104 116 | 
             
                      end
         | 
| 105 117 | 
             
                    end
         | 
| 118 | 
            +
             | 
| 106 119 | 
             
                end
         | 
| 107 120 | 
             
              end
         | 
| 108 121 | 
             
            end
         | 
| @@ -340,15 +340,6 @@ module Shamu | |
| 340 340 | 
             
                        end
         | 
| 341 341 | 
             
                      end
         | 
| 342 342 |  | 
| 343 | 
            -
                    # @!visibility public
         | 
| 344 | 
            -
                    #
         | 
| 345 | 
            -
                    # @overload result( *values, request: nil, entity: nil )
         | 
| 346 | 
            -
                    # @param (see Result#initialize)
         | 
| 347 | 
            -
                    # @return [Result]
         | 
| 348 | 
            -
                    def result( *args )
         | 
| 349 | 
            -
                      Result.new( *args )
         | 
| 350 | 
            -
                    end
         | 
| 351 | 
            -
             | 
| 352 343 | 
             
                    # @!visibility public
         | 
| 353 344 | 
             
                    #
         | 
| 354 345 | 
             
                    # Return an error {#result} from a service request.
         | 
    
        data/lib/shamu/version.rb
    CHANGED
    
    
| @@ -5,6 +5,7 @@ module AuditingSupportSpec | |
| 5 5 | 
             
                include Shamu::Auditing::Support
         | 
| 6 6 |  | 
| 7 7 | 
             
                public :audit_request
         | 
| 8 | 
            +
                public :with_request
         | 
| 8 9 | 
             
              end
         | 
| 9 10 |  | 
| 10 11 | 
             
              class Change < Shamu::Services::Request
         | 
| @@ -14,19 +15,22 @@ end | |
| 14 15 |  | 
| 15 16 | 
             
            describe Shamu::Auditing::Support do
         | 
| 16 17 | 
             
              hunt( :service, Shamu::Auditing::AuditingService )
         | 
| 18 | 
            +
             | 
| 17 19 | 
             
              let( :example_service ) { scorpion.new AuditingSupportSpec::ExampleService }
         | 
| 20 | 
            +
              let( :request )         { AuditingSupportSpec::Change.new name: "Penguin" }
         | 
| 18 21 |  | 
| 19 22 | 
             
              it "audits the request on success" do
         | 
| 20 23 | 
             
                expect( service ).to receive( :commit )
         | 
| 21 24 |  | 
| 22 | 
            -
                example_service.audit_request(  | 
| 25 | 
            +
                example_service.audit_request( request ) do |transaction|
         | 
| 23 26 | 
             
                end
         | 
| 24 27 | 
             
              end
         | 
| 25 28 |  | 
| 26 29 | 
             
              it "skips the request on failure" do
         | 
| 27 30 | 
             
                expect( service ).not_to receive( :commit )
         | 
| 31 | 
            +
                request.name = nil
         | 
| 28 32 |  | 
| 29 | 
            -
                example_service.audit_request(  | 
| 33 | 
            +
                example_service.audit_request( request ) do |transaction|
         | 
| 30 34 | 
             
                end
         | 
| 31 35 | 
             
              end
         | 
| 32 36 |  | 
| @@ -35,7 +39,16 @@ describe Shamu::Auditing::Support do | |
| 35 39 | 
             
                  expect( transaction.action ).to eq "change"
         | 
| 36 40 | 
             
                end
         | 
| 37 41 |  | 
| 38 | 
            -
                example_service.audit_request(  | 
| 42 | 
            +
                example_service.audit_request( request ) do |request|
         | 
| 39 43 | 
             
                end
         | 
| 40 44 | 
             
              end
         | 
| 41 | 
            -
             | 
| 45 | 
            +
             | 
| 46 | 
            +
              it "wraps with_request" do
         | 
| 47 | 
            +
                expect( service ).to receive( :commit )
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                example_service.with_request( { name: "Example" }, AuditingSupportSpec::Change ) do |request, transaction|
         | 
| 50 | 
            +
                  expect( request ).to be_a AuditingSupportSpec::Change
         | 
| 51 | 
            +
                  expect( transaction ).to be_a Shamu::Auditing::Transaction
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
| @@ -1,5 +1,19 @@ | |
| 1 1 | 
             
            require "spec_helper"
         | 
| 2 2 |  | 
| 3 | 
            +
            module EventsSupportSpec
         | 
| 4 | 
            +
              class Service < Shamu::Services::Service
         | 
| 5 | 
            +
                include Shamu::Events::Support
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                public :event!
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              module Events
         | 
| 11 | 
            +
                class Boom < Shamu::Events::Message
         | 
| 12 | 
            +
                  attribute :name
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 3 17 | 
             
            describe Shamu::Events::Support do
         | 
| 4 18 | 
             
              describe "#event_channel" do
         | 
| 5 19 | 
             
                {
         | 
| @@ -27,18 +41,16 @@ describe Shamu::Events::Support do | |
| 27 41 | 
             
              describe "event!" do
         | 
| 28 42 | 
             
                hunt( :events_service, Shamu::Events::EventsService )
         | 
| 29 43 |  | 
| 30 | 
            -
                let( : | 
| 31 | 
            -
                  Class.new( Shamu::Services::Service ) do
         | 
| 32 | 
            -
                    include Shamu::Events::Support
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                    public :event!
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
                end
         | 
| 37 | 
            -
                let( :service ) { scorpion.new klass }
         | 
| 44 | 
            +
                let( :service ) { scorpion.new EventsSupportSpec::Service }
         | 
| 38 45 |  | 
| 39 46 | 
             
                it "publishes message to events_service" do
         | 
| 40 47 | 
             
                  expect( events_service ).to receive( :publish )
         | 
| 41 48 | 
             
                  service.event! Shamu::Events::Message.new
         | 
| 42 49 | 
             
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                it "creates message from attributes" do
         | 
| 52 | 
            +
                  expect( events_service ).to receive( :publish )
         | 
| 53 | 
            +
                  service.event! :boom, name: "Me"
         | 
| 54 | 
            +
                end
         | 
| 43 55 | 
             
              end
         | 
| 44 | 
            -
            end
         | 
| 56 | 
            +
            end
         | 
| @@ -33,10 +33,8 @@ module ActiveRecordCrudSpec | |
| 33 33 | 
             
                include Shamu::Services::ActiveRecordCrud
         | 
| 34 34 |  | 
| 35 35 | 
             
                resource FavoriteEntity, ActiveRecordSpec::Favorite
         | 
| 36 | 
            -
                 | 
| 37 | 
            -
             | 
| 38 | 
            -
                finders
         | 
| 39 | 
            -
                destroy
         | 
| 36 | 
            +
                define_crud
         | 
| 37 | 
            +
             | 
| 40 38 | 
             
              end
         | 
| 41 39 |  | 
| 42 40 | 
             
              class FavoriteListScope < Shamu::Entities::ListScope
         | 
| @@ -46,6 +44,7 @@ end | |
| 46 44 |  | 
| 47 45 | 
             
            describe Shamu::Services::ActiveRecordCrud do
         | 
| 48 46 | 
             
              use_active_record
         | 
| 47 | 
            +
             | 
| 49 48 | 
             
              before( :all )  { Shamu::ToModelIdExtension.extend! }
         | 
| 50 49 |  | 
| 51 50 | 
             
              let( :klass )   { ActiveRecordCrudSpec::Service }
         | 
| @@ -125,15 +124,6 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 125 124 | 
             
                  service.create request
         | 
| 126 125 | 
             
                end
         | 
| 127 126 |  | 
| 128 | 
            -
                it "calls apply_changes if present" do
         | 
| 129 | 
            -
                  expect( service ).to receive( :apply_changes )
         | 
| 130 | 
            -
                    .with(
         | 
| 131 | 
            -
                      request,
         | 
| 132 | 
            -
                      kind_of( ActiveRecord::Base )
         | 
| 133 | 
            -
                    )
         | 
| 134 | 
            -
                  service.create( request )
         | 
| 135 | 
            -
                end
         | 
| 136 | 
            -
             | 
| 137 127 | 
             
                it "returns a Result" do
         | 
| 138 128 | 
             
                  expect( service.create( request ) ).to be_a Shamu::Services::Result
         | 
| 139 129 | 
             
                end
         | 
| @@ -148,24 +138,27 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 148 138 | 
             
                it "yields if block given" do
         | 
| 149 139 | 
             
                  expect do |b|
         | 
| 150 140 | 
             
                    yield_klass = Class.new( klass ) do
         | 
| 151 | 
            -
                       | 
| 141 | 
            +
                      define_create( &b )
         | 
| 152 142 | 
             
                    end
         | 
| 153 143 |  | 
| 154 144 | 
             
                    scorpion.new( yield_klass ).create( request )
         | 
| 155 145 | 
             
                  end.to yield_with_args(
         | 
| 156 | 
            -
                     | 
| 157 | 
            -
                     | 
| 146 | 
            +
                    kind_of( ActiveRecord::Base ),
         | 
| 147 | 
            +
                    request
         | 
| 158 148 | 
             
                  )
         | 
| 159 149 | 
             
                end
         | 
| 160 150 |  | 
| 161 | 
            -
                it " | 
| 151 | 
            +
                it "short-circuits if block yields a Services::Result" do
         | 
| 162 152 | 
             
                  yield_klass = Class.new( klass ) do
         | 
| 163 | 
            -
                     | 
| 153 | 
            +
                    define_create do
         | 
| 154 | 
            +
                      Shamu::Services::Result.new
         | 
| 155 | 
            +
                    end
         | 
| 164 156 | 
             
                  end
         | 
| 165 157 |  | 
| 166 158 | 
             
                  service = scorpion.new( yield_klass )
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                  service. | 
| 159 | 
            +
             | 
| 160 | 
            +
                  expect( service ).not_to receive( :build_entity )
         | 
| 161 | 
            +
                  service.create request
         | 
| 169 162 | 
             
                end
         | 
| 170 163 |  | 
| 171 164 | 
             
                it "calls #authorize!" do
         | 
| @@ -198,37 +191,34 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 198 191 | 
             
                  service.update entity, request
         | 
| 199 192 | 
             
                end
         | 
| 200 193 |  | 
| 201 | 
            -
                it "calls apply_changes if present" do
         | 
| 202 | 
            -
                  expect( service ).to receive( :apply_changes )
         | 
| 203 | 
            -
                    .with(
         | 
| 204 | 
            -
                      kind_of( Shamu::Services::Request ),
         | 
| 205 | 
            -
                      kind_of( ::ActiveRecord::Base )
         | 
| 206 | 
            -
                    )
         | 
| 207 | 
            -
                  service.create( request )
         | 
| 208 | 
            -
                end
         | 
| 209 | 
            -
             | 
| 210 194 | 
             
                it "yields if block given" do
         | 
| 211 195 | 
             
                  entity = service.create.entity
         | 
| 212 196 |  | 
| 213 197 | 
             
                  expect do |b|
         | 
| 214 198 | 
             
                    yield_klass = Class.new( klass ) do
         | 
| 215 | 
            -
                       | 
| 199 | 
            +
                      define_change( :update, &b )
         | 
| 216 200 | 
             
                    end
         | 
| 217 201 |  | 
| 218 202 | 
             
                    scorpion.new( yield_klass ).update entity.id, request
         | 
| 219 | 
            -
                  end.to yield_with_args(  | 
| 203 | 
            +
                  end.to yield_with_args( kind_of( ::ActiveRecord::Base ), request )
         | 
| 220 204 | 
             
                end
         | 
| 221 205 |  | 
| 222 | 
            -
                it " | 
| 223 | 
            -
                  entity | 
| 206 | 
            +
                it "short-circuits if block yields a Services::Result" do
         | 
| 207 | 
            +
                  entity
         | 
| 208 | 
            +
                  record  = service.class.model_class.all.first
         | 
| 209 | 
            +
                  records = [ record ]
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                  expect( records ).to receive( :find ).and_return record
         | 
| 212 | 
            +
                  expect( record ).not_to receive( :save )
         | 
| 224 213 |  | 
| 225 214 | 
             
                  yield_klass = Class.new( klass ) do
         | 
| 226 | 
            -
                     | 
| 215 | 
            +
                    define_update records do
         | 
| 216 | 
            +
                      Shamu::Services::Result.new
         | 
| 217 | 
            +
                    end
         | 
| 227 218 | 
             
                  end
         | 
| 228 219 |  | 
| 229 220 | 
             
                  service = scorpion.new( yield_klass )
         | 
| 230 | 
            -
                   | 
| 231 | 
            -
                  service.update entity.id
         | 
| 221 | 
            +
                  service.update entity.id, request
         | 
| 232 222 | 
             
                end
         | 
| 233 223 |  | 
| 234 224 | 
             
                it "calls #authorize!" do
         | 
| @@ -244,19 +234,6 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 244 234 | 
             
                end
         | 
| 245 235 | 
             
              end
         | 
| 246 236 |  | 
| 247 | 
            -
              describe ".apply_changes" do
         | 
| 248 | 
            -
                it "defines an apply_changes method from the block" do
         | 
| 249 | 
            -
                  klass = Class.new( Shamu::Services::Service ) do
         | 
| 250 | 
            -
                    include Shamu::Services::ActiveRecordCrud
         | 
| 251 | 
            -
             | 
| 252 | 
            -
                    apply_changes do |record, request|
         | 
| 253 | 
            -
                    end
         | 
| 254 | 
            -
                  end
         | 
| 255 | 
            -
             | 
| 256 | 
            -
                  expect( klass.new.respond_to?( :apply_changes, true ) ).to be_truthy
         | 
| 257 | 
            -
                end
         | 
| 258 | 
            -
              end
         | 
| 259 | 
            -
             | 
| 260 237 | 
             
              describe ".finders" do
         | 
| 261 238 | 
             
                it "builds #lookup" do
         | 
| 262 239 | 
             
                  expect( service ).to respond_to :lookup
         | 
| @@ -279,7 +256,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 279 256 | 
             
                  end
         | 
| 280 257 |  | 
| 281 258 | 
             
                  it "excludes unwanted methods" do
         | 
| 282 | 
            -
                    klass. | 
| 259 | 
            +
                    klass.define_finders except: :list
         | 
| 283 260 |  | 
| 284 261 | 
             
                    expect( klass.new ).to respond_to :find
         | 
| 285 262 | 
             
                    expect( klass.new ).to respond_to :lookup
         | 
| @@ -287,7 +264,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 287 264 | 
             
                  end
         | 
| 288 265 |  | 
| 289 266 | 
             
                  it "includes only specific methods" do
         | 
| 290 | 
            -
                    klass. | 
| 267 | 
            +
                    klass.define_finders only: :list
         | 
| 291 268 |  | 
| 292 269 | 
             
                    expect( klass.new ).not_to respond_to :find
         | 
| 293 270 | 
             
                    expect( klass.new ).not_to respond_to :lookup
         | 
| @@ -307,7 +284,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 307 284 | 
             
                it "yields to block if block given" do
         | 
| 308 285 | 
             
                  find_klass = Class.new( klass )
         | 
| 309 286 | 
             
                  expect do |b|
         | 
| 310 | 
            -
                    find_klass. | 
| 287 | 
            +
                    find_klass.define_find do |_|
         | 
| 311 288 | 
             
                      b.to_proc.call
         | 
| 312 289 | 
             
                      ActiveRecordSpec::Favorite.all.first
         | 
| 313 290 | 
             
                    end
         | 
| @@ -353,7 +330,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 353 330 | 
             
                it "yields to block if given?" do
         | 
| 354 331 | 
             
                  expect do |b|
         | 
| 355 332 | 
             
                    yield_klass = Class.new( klass ) do
         | 
| 356 | 
            -
                       | 
| 333 | 
            +
                      define_lookup( &b )
         | 
| 357 334 | 
             
                    end
         | 
| 358 335 |  | 
| 359 336 | 
             
                    scorpion.new( yield_klass ).lookup entity.id
         | 
| @@ -392,7 +369,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 392 369 | 
             
                it "yields if block given" do
         | 
| 393 370 | 
             
                  expect do |b|
         | 
| 394 371 | 
             
                    yield_klass = Class.new( klass ) do
         | 
| 395 | 
            -
                       | 
| 372 | 
            +
                      define_list( &b )
         | 
| 396 373 | 
             
                    end
         | 
| 397 374 |  | 
| 398 375 | 
             
                    scorpion.new( yield_klass ).list
         | 
| @@ -438,6 +415,22 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 438 415 | 
             
                  end.to change( ActiveRecordSpec::Favorite, :count ).by( -1 )
         | 
| 439 416 | 
             
                end
         | 
| 440 417 |  | 
| 418 | 
            +
                it "short-circuits if block yields a Services::Result" do
         | 
| 419 | 
            +
                  record = klass.model_class.all.first
         | 
| 420 | 
            +
                  records = [ record ]
         | 
| 421 | 
            +
             | 
| 422 | 
            +
                  expect( records ).to receive( :find ).and_return record
         | 
| 423 | 
            +
             | 
| 424 | 
            +
                  yield_klass = Class.new( klass ) do
         | 
| 425 | 
            +
                    define_destroy records do
         | 
| 426 | 
            +
                      Shamu::Services::Result.new
         | 
| 427 | 
            +
                    end
         | 
| 428 | 
            +
                  end
         | 
| 429 | 
            +
             | 
| 430 | 
            +
                  service = scorpion.new( yield_klass )
         | 
| 431 | 
            +
                  service.destroy entity
         | 
| 432 | 
            +
                end
         | 
| 433 | 
            +
             | 
| 441 434 | 
             
                it "calls #authorize!" do
         | 
| 442 435 | 
             
                  expect( service ).to receive( :authorize! ).with(
         | 
| 443 436 | 
             
                    :destroy,
         | 
| @@ -458,7 +451,7 @@ describe Shamu::Services::ActiveRecordCrud do | |
| 458 451 | 
             
                let( :klass ) do
         | 
| 459 452 | 
             
                  ec = entity_class
         | 
| 460 453 | 
             
                  Class.new( super() ) do
         | 
| 461 | 
            -
                     | 
| 454 | 
            +
                    define_build_entities do |records|
         | 
| 462 455 | 
             
                      records.map do |record|
         | 
| 463 456 | 
             
                        scorpion.fetch ec, { record: record }, {}
         | 
| 464 457 | 
             
                      end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: shamu
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.18
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Paul Alexander
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2017-06- | 
| 11 | 
            +
            date: 2017-06-08 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activemodel
         |