acfs 1.4.0 → 1.7.0
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/CHANGELOG.md +26 -0
- data/README.md +22 -39
- data/acfs.gemspec +8 -14
- data/lib/acfs/adapter/base.rb +2 -0
- data/lib/acfs/adapter/typhoeus.rb +16 -11
- data/lib/acfs/collections/paginatable.rb +1 -1
- data/lib/acfs/configuration.rb +13 -3
- data/lib/acfs/errors.rb +41 -21
- data/lib/acfs/global.rb +2 -2
- data/lib/acfs/location.rb +26 -32
- data/lib/acfs/middleware/base.rb +2 -2
- data/lib/acfs/middleware/json.rb +4 -2
- data/lib/acfs/middleware/logger.rb +4 -6
- data/lib/acfs/middleware/serializer.rb +1 -1
- data/lib/acfs/operation.rb +21 -8
- data/lib/acfs/request/callbacks.rb +4 -4
- data/lib/acfs/request.rb +4 -11
- data/lib/acfs/resource/attributes/date_time.rb +1 -1
- data/lib/acfs/resource/attributes/uuid.rb +1 -1
- data/lib/acfs/resource/attributes.rb +16 -15
- data/lib/acfs/resource/dirty.rb +2 -2
- data/lib/acfs/resource/initialization.rb +5 -5
- data/lib/acfs/resource/locatable.rb +11 -8
- data/lib/acfs/resource/operational.rb +6 -3
- data/lib/acfs/resource/persistence.rb +13 -15
- data/lib/acfs/resource/query_methods.rb +10 -10
- data/lib/acfs/resource/service.rb +2 -2
- data/lib/acfs/resource/validation.rb +17 -7
- data/lib/acfs/response.rb +5 -5
- data/lib/acfs/runner.rb +15 -15
- data/lib/acfs/service.rb +16 -19
- data/lib/acfs/singleton_resource.rb +2 -2
- data/lib/acfs/stub.rb +41 -31
- data/lib/acfs/version.rb +2 -2
- data/spec/acfs/adapter/typhoeus_spec.rb +2 -2
- data/spec/acfs/collection_spec.rb +66 -41
- data/spec/acfs/configuration_spec.rb +22 -12
- data/spec/acfs/global_spec.rb +11 -9
- data/spec/acfs/location_spec.rb +2 -2
- data/spec/acfs/middleware/json_spec.rb +22 -8
- data/spec/acfs/middleware/{msgpack_spec.rb → message_pack_spec.rb} +6 -6
- data/spec/acfs/operation_spec.rb +3 -2
- data/spec/acfs/request/callbacks_spec.rb +19 -10
- data/spec/acfs/request_spec.rb +16 -20
- data/spec/acfs/resource/attributes/boolean_spec.rb +32 -32
- data/spec/acfs/resource/attributes/date_time_spec.rb +16 -8
- data/spec/acfs/resource/attributes/dict_spec.rb +15 -9
- data/spec/acfs/resource/attributes/float_spec.rb +20 -10
- data/spec/acfs/resource/attributes/integer_spec.rb +10 -5
- data/spec/acfs/resource/attributes/list_spec.rb +13 -8
- data/spec/acfs/resource/attributes/uuid_spec.rb +12 -6
- data/spec/acfs/resource/attributes_spec.rb +37 -38
- data/spec/acfs/resource/dirty_spec.rb +6 -3
- data/spec/acfs/resource/initialization_spec.rb +4 -5
- data/spec/acfs/resource/loadable_spec.rb +3 -1
- data/spec/acfs/resource/locatable_spec.rb +24 -18
- data/spec/acfs/resource/{persistance_spec.rb → persistence_spec.rb} +122 -90
- data/spec/acfs/resource/query_methods_spec.rb +143 -110
- data/spec/acfs/resource/validation_spec.rb +34 -27
- data/spec/acfs/response/formats_spec.rb +8 -8
- data/spec/acfs/response/status_spec.rb +16 -9
- data/spec/acfs/runner_spec.rb +10 -8
- data/spec/acfs/service/middleware_spec.rb +3 -3
- data/spec/acfs/service_spec.rb +6 -5
- data/spec/acfs/singleton_resource_spec.rb +2 -1
- data/spec/acfs/stub_spec.rb +57 -53
- data/spec/acfs_spec.rb +111 -93
- data/spec/spec_helper.rb +1 -2
- data/spec/support/response.rb +2 -2
- data/spec/support/service.rb +1 -1
- data/spec/support/shared/find_callbacks.rb +14 -10
- metadata +30 -29
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3cbc57dd595fa4242fa029498d90ca5a497216d2003674296209b679b84a6df2
         | 
| 4 | 
            +
              data.tar.gz: 31a009d88f2a028e2fc11513b68dd011f5cfa0b7f11a83238f167e807c365b35
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ba37a7c96e65b8774879835d93ccf43a5c91bcd77ce9cf604937544f0897ebf307ffbbf914d5e7304eb90e6f62139e37eb52b2e6406db4e86d454ef45bb4423b
         | 
| 7 | 
            +
              data.tar.gz: 976193afda7d9e6d5bbdc3eef3ec7235a90abcaf23e79f055735cb663400c02448a683e734b9f3212b2a27c101fabad4055f0a0c5dff23a517885a7bda4cea43
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -6,6 +6,7 @@ | |
| 6 6 | 
             
            ---
         | 
| 7 7 |  | 
| 8 8 | 
             
            ### New
         | 
| 9 | 
            +
            * Support for Ruby 3.1 and Rails 7.0
         | 
| 9 10 |  | 
| 10 11 | 
             
            ### Changes
         | 
| 11 12 |  | 
| @@ -14,6 +15,31 @@ | |
| 14 15 | 
             
            ### Breaks
         | 
| 15 16 |  | 
| 16 17 |  | 
| 18 | 
            +
            ## 1.6.0 - (2021-01-07)
         | 
| 19 | 
            +
            ---
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### New
         | 
| 22 | 
            +
            * Support Ruby 3.0
         | 
| 23 | 
            +
            * Use keyword arguments in parameters and when calling methods
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## 1.5.1 - (2020-12-30)
         | 
| 27 | 
            +
            ---
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            ### Changes
         | 
| 30 | 
            +
            * Revert back to using `::MultiJson`
         | 
| 31 | 
            +
             | 
| 32 | 
            +
             | 
| 33 | 
            +
            ## 1.5.0 - (2020-06-19)
         | 
| 34 | 
            +
            ---
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            ### New
         | 
| 37 | 
            +
            * Error classes for more HTTP error responses: `400`, `401`, `403`, `500`, `502`, `503`, `504`.
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            ### Changes
         | 
| 40 | 
            +
            * Replace deprecated MultiJson with core JSON module
         | 
| 41 | 
            +
             | 
| 42 | 
            +
             | 
| 17 43 | 
             
            ## 1.4.0 - (2020-06-12)
         | 
| 18 44 | 
             
            ---
         | 
| 19 45 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -8,13 +8,16 @@ | |
| 8 8 |  | 
| 9 9 | 
             
            Acfs is a library to develop API client libraries for single services within a larger service oriented application.
         | 
| 10 10 |  | 
| 11 | 
            -
            Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses  | 
| 11 | 
            +
            Acfs covers model and service abstraction, convenient query and filter methods, full middleware stack for pre-processing requests and responses, as well as automatic request queuing and parallel processing.
         | 
| 12 | 
            +
             | 
| 12 13 |  | 
| 13 14 | 
             
            ## Installation
         | 
| 14 15 |  | 
| 15 16 | 
             
            Add this line to your application's Gemfile:
         | 
| 16 17 |  | 
| 17 | 
            -
             | 
| 18 | 
            +
            ```ruby
         | 
| 19 | 
            +
            gem 'acfs', '~> 1.7'
         | 
| 20 | 
            +
            ```
         | 
| 18 21 |  | 
| 19 22 | 
             
            And then execute:
         | 
| 20 23 |  | 
| @@ -24,6 +27,7 @@ Or install it yourself as: | |
| 24 27 |  | 
| 25 28 | 
             
                > gem install acfs
         | 
| 26 29 |  | 
| 30 | 
            +
             | 
| 27 31 | 
             
            ## Usage
         | 
| 28 32 |  | 
| 29 33 | 
             
            First you need to define your service(s):
         | 
| @@ -119,7 +123,7 @@ Acfs.run # This call will fire all request as parallel as possible. | |
| 119 123 | 
             
            @friends[0].name # => "Miraculix"
         | 
| 120 124 | 
             
            ```
         | 
| 121 125 |  | 
| 122 | 
            -
            Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed  | 
| 126 | 
            +
            Use `.find_by` to get first element only. `.find_by` will call the `index`-Action and return the first resource. Optionally passed parameters will be sent as `GET` parameters and can be used for filtering in the service's controller.
         | 
| 123 127 | 
             
            ```ruby
         | 
| 124 128 | 
             
            @user = User.find_by age: 24
         | 
| 125 129 |  | 
| @@ -145,6 +149,7 @@ Acfs has basic update support using `PUT` requests: | |
| 145 149 | 
             
            @user.persisted? # => true
         | 
| 146 150 | 
             
            ```
         | 
| 147 151 |  | 
| 152 | 
            +
             | 
| 148 153 | 
             
            ## Singleton resources
         | 
| 149 154 |  | 
| 150 155 | 
             
            Singletons can be used in Acfs by creating a new resource which inherits from `SingletonResource`:
         | 
| @@ -176,18 +181,17 @@ my_single.save # sends PUT request to /single | |
| 176 181 | 
             
            my_single.delete # sends DELETE request to /single
         | 
| 177 182 | 
             
            ```
         | 
| 178 183 |  | 
| 179 | 
            -
            You also can pass parameters to the find call | 
| 184 | 
            +
            You also can pass parameters to the find call. They will be sent as query parameters to the index action:
         | 
| 180 185 |  | 
| 181 186 | 
             
            ```ruby
         | 
| 182 187 | 
             
            my_single = Single.find name: 'Max'
         | 
| 183 188 | 
             
            Acfs.run # sends GET request with param to /single?name=Max
         | 
| 184 189 | 
             
            ```
         | 
| 185 190 |  | 
| 191 | 
            +
             | 
| 186 192 | 
             
            ## Resource Inheritance
         | 
| 187 193 |  | 
| 188 | 
            -
            Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a
         | 
| 189 | 
            -
            `type` attribute exists and is a valid subclass of your resource they will be converted
         | 
| 190 | 
            -
            to you subclassed resources:
         | 
| 194 | 
            +
            Acfs provides a resource inheritance similar to ActiveRecord Single Table Inheritance. If a `type` attribute exists and is a valid subclass of your resource they will be converted to you subclassed resources:
         | 
| 191 195 |  | 
| 192 196 | 
             
            ```ruby
         | 
| 193 197 | 
             
            class Computer < Acfs::Resource
         | 
| @@ -198,8 +202,7 @@ class Pc < Computer end | |
| 198 202 | 
             
            class Mac < Computer end
         | 
| 199 203 | 
             
            ```
         | 
| 200 204 |  | 
| 201 | 
            -
            With the following response on `GET /computers` the collection will contain the appropriate
         | 
| 202 | 
            -
            subclass resources:
         | 
| 205 | 
            +
            With the following response on `GET /computers` the collection will contain the appropriate subclass resources:
         | 
| 203 206 |  | 
| 204 207 | 
             
            ```json
         | 
| 205 208 | 
             
            [
         | 
| @@ -219,6 +222,7 @@ Acfs.run | |
| 219 222 | 
             
            @computer[2].class # => Pc
         | 
| 220 223 | 
             
            ```
         | 
| 221 224 |  | 
| 225 | 
            +
             | 
| 222 226 | 
             
            ## Stubbing
         | 
| 223 227 |  | 
| 224 228 | 
             
            You can stub resources in applications using an Acfs service client:
         | 
| @@ -243,9 +247,9 @@ it 'should find user number one' do | |
| 243 247 | 
             
              user = MyUser.find 1
         | 
| 244 248 | 
             
              Acfs.run
         | 
| 245 249 |  | 
| 246 | 
            -
              expect(user.id).to  | 
| 247 | 
            -
              expect(user.name).to  | 
| 248 | 
            -
              expect(user.age).to  | 
| 250 | 
            +
              expect(user.id).to eq 1
         | 
| 251 | 
            +
              expect(user.name).to eq 'John Smith'
         | 
| 252 | 
            +
              expect(user.age).to eq 32
         | 
| 249 253 |  | 
| 250 254 | 
             
              expect(@stub).to be_called
         | 
| 251 255 | 
             
              expect(@stub).to_not be_called 5.times
         | 
| @@ -260,8 +264,8 @@ end | |
| 260 264 | 
             
            it 'should allow stub resource creation' do
         | 
| 261 265 | 
             
              session = Session.create! ident: 'john@exmaple.org', password: 's3cr3t'
         | 
| 262 266 |  | 
| 263 | 
            -
              expect(session.id).to  | 
| 264 | 
            -
              expect(session.user).to  | 
| 267 | 
            +
              expect(session.id).to eq 'longhash'
         | 
| 268 | 
            +
              expect(session.user).to eq 1
         | 
| 265 269 | 
             
            end
         | 
| 266 270 | 
             
            ```
         | 
| 267 271 |  | 
| @@ -279,11 +283,10 @@ it 'should find user number one' do | |
| 279 283 | 
             
            end
         | 
| 280 284 | 
             
            ```
         | 
| 281 285 |  | 
| 282 | 
            -
            ## Instrumentation
         | 
| 283 286 |  | 
| 284 | 
            -
             | 
| 287 | 
            +
            ## Instrumentation
         | 
| 285 288 |  | 
| 286 | 
            -
            Acfs  | 
| 289 | 
            +
            Acfs supports [instrumentation via active support][1] and exposes the following events:
         | 
| 287 290 |  | 
| 288 291 | 
             
            * `acfs.operation.complete(operation, response)`: Acfs operation completed
         | 
| 289 292 | 
             
            * `acfs.runner.sync_run(operation)`: Run operation right now skipping queue.
         | 
| @@ -291,26 +294,11 @@ Acfs expose to following events | |
| 291 294 | 
             
            * `acfs.before_run`: directly before `acfs.run`
         | 
| 292 295 | 
             
            * `acfs.run`: Run all queued operations.
         | 
| 293 296 |  | 
| 294 | 
            -
            Read [official guide][2]  | 
| 297 | 
            +
            Read the [official guide][2] on how to subscribe to these events.
         | 
| 295 298 |  | 
| 296 299 | 
             
            [1]: http://guides.rubyonrails.org/active_support_instrumentation.html
         | 
| 297 300 | 
             
            [2]: http://guides.rubyonrails.org/active_support_instrumentation.html#subscribing-to-an-event
         | 
| 298 301 |  | 
| 299 | 
            -
            ## Roadmap
         | 
| 300 | 
            -
             | 
| 301 | 
            -
            * Update
         | 
| 302 | 
            -
                * Better new? detection eg. storing ETag from request resources.
         | 
| 303 | 
            -
                * Use PATCH for with only changed attributes and `If-Unmodifed-Since`
         | 
| 304 | 
            -
                  and `If-Match` header fields if resource was surly loaded from service
         | 
| 305 | 
            -
                  and not created with an id (e.g `User.new id: 5, name: "john"`).
         | 
| 306 | 
            -
                * Conflict detection (ETag / If-Unmodified-Since)
         | 
| 307 | 
            -
            * High level features
         | 
| 308 | 
            -
                * Support for custom mime types on client and server side. (`application/vnd.myservice.user.v2+msgpack`)
         | 
| 309 | 
            -
                * Server side components
         | 
| 310 | 
            -
                    * Reusing model definitions for generating responses?
         | 
| 311 | 
            -
                    * Rails responders providing REST operations with integrated ETag,
         | 
| 312 | 
            -
                      Modified Headers, conflict detection, ...
         | 
| 313 | 
            -
            * Documentation
         | 
| 314 302 |  | 
| 315 303 | 
             
            ## Contributing
         | 
| 316 304 |  | 
| @@ -322,14 +310,9 @@ Read [official guide][2] to see to to subscribe. | |
| 322 310 | 
             
            7. Push to the branch (`git push origin my-new-feature`)
         | 
| 323 311 | 
             
            8. Create new Pull Request
         | 
| 324 312 |  | 
| 325 | 
            -
            ## Contributors
         | 
| 326 | 
            -
             | 
| 327 | 
            -
            * [Nicolas Fricke](https://github.com/nicolas-fricke)
         | 
| 328 | 
            -
            * [Tino Junge](https://github.com/tino-junge)
         | 
| 329 | 
            -
            * [Malte Swart](https://github.com/mswart)
         | 
| 330 313 |  | 
| 331 314 | 
             
            ## License
         | 
| 332 315 |  | 
| 333 316 | 
             
            MIT License
         | 
| 334 317 |  | 
| 335 | 
            -
            Copyright (c) 2013 Jan Graichen. MIT license, see LICENSE for more details.
         | 
| 318 | 
            +
            Copyright (c) 2013-2022 Jan Graichen. MIT license, see LICENSE for more details.
         | 
    
        data/acfs.gemspec
    CHANGED
    
    | @@ -16,6 +16,8 @@ Gem::Specification.new do |spec| | |
| 16 16 | 
             
                An abstract API base client for service oriented application.
         | 
| 17 17 | 
             
              SUMMARY
         | 
| 18 18 |  | 
| 19 | 
            +
              spec.metadata['rubygems_mfa_required'] = 'true'
         | 
| 20 | 
            +
             | 
| 19 21 | 
             
              spec.files = Dir['**/*'].grep(%r{
         | 
| 20 22 | 
             
                ^((bin|lib|test|spec|features)/|
         | 
| 21 23 | 
             
                .*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
         | 
| @@ -25,22 +27,14 @@ Gem::Specification.new do |spec| | |
| 25 27 | 
             
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 26 28 | 
             
              spec.require_paths = %w[lib]
         | 
| 27 29 |  | 
| 28 | 
            -
              spec. | 
| 29 | 
            -
              spec.add_runtime_dependency 'activemodel', '>= 4.2'
         | 
| 30 | 
            -
              spec.add_runtime_dependency 'activesupport', '>= 4.2'
         | 
| 31 | 
            -
              spec.add_runtime_dependency 'multi_json'
         | 
| 32 | 
            -
             | 
| 33 | 
            -
              # Bundle update w/o version resolves to 0.3.3 ...
         | 
| 34 | 
            -
              spec.add_runtime_dependency 'typhoeus', '~> 1.0'
         | 
| 30 | 
            +
              spec.required_ruby_version = '>= 2.5.0'
         | 
| 35 31 |  | 
| 32 | 
            +
              spec.add_runtime_dependency 'actionpack', '>= 5.2'
         | 
| 33 | 
            +
              spec.add_runtime_dependency 'activemodel', '>= 5.2'
         | 
| 34 | 
            +
              spec.add_runtime_dependency 'activesupport', '>= 5.2'
         | 
| 35 | 
            +
              spec.add_runtime_dependency 'multi_json', '~> 1.0'
         | 
| 36 36 | 
             
              spec.add_runtime_dependency 'rack'
         | 
| 37 | 
            +
              spec.add_runtime_dependency 'typhoeus', '~> 1.0'
         | 
| 37 38 |  | 
| 38 39 | 
             
              spec.add_development_dependency 'bundler'
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              if ENV['TRAVIS_BUILD_NUMBER'] && !ENV['TRAVIS_TAG']
         | 
| 41 | 
            -
                # Append travis build number for auto-releases
         | 
| 42 | 
            -
                # rubocop:disable Gemspec/DuplicatedAssignment
         | 
| 43 | 
            -
                spec.version = "#{spec.version}.1.b#{ENV['TRAVIS_BUILD_NUMBER']}"
         | 
| 44 | 
            -
                # rubocop:enable Gemspec/DuplicatedAssignment
         | 
| 45 | 
            -
              end
         | 
| 46 40 | 
             
            end
         | 
    
        data/lib/acfs/adapter/base.rb
    CHANGED
    
    
| @@ -7,14 +7,19 @@ module Acfs | |
| 7 7 | 
             
                DEFAULT_OPTIONS = {
         | 
| 8 8 | 
             
                  tcp_keepalive: true,
         | 
| 9 9 | 
             
                  tcp_keepidle: 5,
         | 
| 10 | 
            -
                  tcp_keepintvl: 5
         | 
| 10 | 
            +
                  tcp_keepintvl: 5,
         | 
| 11 11 | 
             
                }.freeze
         | 
| 12 12 |  | 
| 13 13 | 
             
                # Adapter for Typhoeus.
         | 
| 14 14 | 
             
                #
         | 
| 15 15 | 
             
                class Typhoeus < Base
         | 
| 16 | 
            -
                  def initialize( | 
| 17 | 
            -
                     | 
| 16 | 
            +
                  def initialize(**kwargs)
         | 
| 17 | 
            +
                    super
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    extra_opts = kwargs.delete(:opts)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    @opts = DEFAULT_OPTIONS
         | 
| 22 | 
            +
                    @opts = @opts.merge(extra_opts) if extra_opts
         | 
| 18 23 | 
             
                    @kwargs = kwargs
         | 
| 19 24 | 
             
                  end
         | 
| 20 25 |  | 
| @@ -47,22 +52,22 @@ module Acfs | |
| 47 52 | 
             
                      params: req.params,
         | 
| 48 53 | 
             
                      headers: req.headers.merge(
         | 
| 49 54 | 
             
                        'Expect' => '',
         | 
| 50 | 
            -
                        'Transfer-Encoding' => ''
         | 
| 55 | 
            +
                        'Transfer-Encoding' => '',
         | 
| 51 56 | 
             
                      ),
         | 
| 52 | 
            -
                      body: req.body
         | 
| 57 | 
            +
                      body: req.body,
         | 
| 53 58 | 
             
                    }
         | 
| 54 59 |  | 
| 55 | 
            -
                    request = ::Typhoeus::Request.new(req.url, **@opts | 
| 60 | 
            +
                    request = ::Typhoeus::Request.new(req.url, **@opts, **opts)
         | 
| 56 61 |  | 
| 57 62 | 
             
                    request.on_complete do |response|
         | 
| 58 | 
            -
                      if response.timed_out?
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                       | 
| 63 | 
            +
                      raise ::Acfs::TimeoutError.new(req) if response.timed_out?
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                      if response.code.zero?
         | 
| 61 66 | 
             
                        # Failed to get HTTP response
         | 
| 62 67 | 
             
                        raise ::Acfs::RequestError.new(req, response.return_message)
         | 
| 63 | 
            -
                      else
         | 
| 64 | 
            -
                        req.complete! convert_response(req, response)
         | 
| 65 68 | 
             
                      end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      req.complete! convert_response(req, response)
         | 
| 66 71 | 
             
                    end
         | 
| 67 72 |  | 
| 68 73 | 
             
                    request
         | 
    
        data/lib/acfs/configuration.rb
    CHANGED
    
    | @@ -28,7 +28,7 @@ module Acfs | |
| 28 28 | 
             
                #
         | 
| 29 29 | 
             
                def configure(&block)
         | 
| 30 30 | 
             
                  if block.arity.positive?
         | 
| 31 | 
            -
                     | 
| 31 | 
            +
                    yield self
         | 
| 32 32 | 
             
                  else
         | 
| 33 33 | 
             
                    instance_eval(&block)
         | 
| 34 34 | 
             
                  end
         | 
| @@ -70,8 +70,8 @@ module Acfs | |
| 70 70 | 
             
                # @return [undefined]
         | 
| 71 71 | 
             
                #
         | 
| 72 72 | 
             
                def load(filename)
         | 
| 73 | 
            -
                  config =  | 
| 74 | 
            -
                  env | 
| 73 | 
            +
                  config = load_yaml_file(filename)
         | 
| 74 | 
            +
                  env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
         | 
| 75 75 |  | 
| 76 76 | 
             
                  config = config[env] if config.key? env
         | 
| 77 77 | 
             
                  config.each do |key, value|
         | 
| @@ -116,5 +116,15 @@ module Acfs | |
| 116 116 | 
             
                    @current = configuration if configuration.is_a? Configuration
         | 
| 117 117 | 
             
                  end
         | 
| 118 118 | 
             
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                private
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def load_yaml_file(path)
         | 
| 123 | 
            +
                  if YAML.respond_to?(:safe_load_file)
         | 
| 124 | 
            +
                    YAML.safe_load_file(path, aliases: true)
         | 
| 125 | 
            +
                  else
         | 
| 126 | 
            +
                    YAML.safe_load(File.read(path), [], [], true)
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
                end
         | 
| 119 129 | 
             
              end
         | 
| 120 130 | 
             
            end
         | 
    
        data/lib/acfs/errors.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module Acfs | |
| 5 5 | 
             
              #
         | 
| 6 6 | 
             
              class Error < StandardError
         | 
| 7 7 | 
             
                def initialize(opts = {}, message = nil)
         | 
| 8 | 
            -
                  opts | 
| 8 | 
            +
                  opts[:message] = message if message
         | 
| 9 9 | 
             
                  super opts[:message]
         | 
| 10 10 | 
             
                end
         | 
| 11 11 | 
             
              end
         | 
| @@ -26,7 +26,7 @@ module Acfs | |
| 26 26 |  | 
| 27 27 | 
             
              class TimeoutError < RequestError
         | 
| 28 28 | 
             
                def initialize(request)
         | 
| 29 | 
            -
                  super(request,  | 
| 29 | 
            +
                  super(request, 'Timeout reached')
         | 
| 30 30 | 
             
                end
         | 
| 31 31 | 
             
              end
         | 
| 32 32 |  | 
| @@ -38,13 +38,13 @@ module Acfs | |
| 38 38 | 
             
                def initialize(opts = {})
         | 
| 39 39 | 
             
                  @response = opts[:response]
         | 
| 40 40 |  | 
| 41 | 
            -
                  if response
         | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 41 | 
            +
                  message = if response
         | 
| 42 | 
            +
                              (opts[:message] ? "#{opts[:message]}:" : 'Received') +
         | 
| 43 | 
            +
                                " #{response.code} for #{response.request.method.upcase}" \
         | 
| 44 | 
            +
                                " #{response.request.url} #{response.request.format}"
         | 
| 45 | 
            +
                            else
         | 
| 46 | 
            +
                              opts[:message] || 'Received erroneous response'
         | 
| 47 | 
            +
                            end
         | 
| 48 48 |  | 
| 49 49 | 
             
                  super opts, message
         | 
| 50 50 | 
             
                end
         | 
| @@ -67,11 +67,19 @@ module Acfs | |
| 67 67 | 
             
                end
         | 
| 68 68 | 
             
              end
         | 
| 69 69 |  | 
| 70 | 
            -
              #  | 
| 71 | 
            -
               | 
| 72 | 
            -
             | 
| 73 | 
            -
               | 
| 70 | 
            +
              # 400
         | 
| 71 | 
            +
              class BadRequest < ErroneousResponse; end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              # 401
         | 
| 74 | 
            +
              class Unauthorized < ErroneousResponse; end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              # 403
         | 
| 77 | 
            +
              class Forbidden < ErroneousResponse; end
         | 
| 74 78 |  | 
| 79 | 
            +
              # 404
         | 
| 80 | 
            +
              class ResourceNotFound < ErroneousResponse; end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              # 422
         | 
| 75 83 | 
             
              class InvalidResource < ErroneousResponse
         | 
| 76 84 | 
             
                attr_reader :errors, :resource
         | 
| 77 85 |  | 
| @@ -79,18 +87,31 @@ module Acfs | |
| 79 87 | 
             
                  @errors   = opts.delete :errors
         | 
| 80 88 | 
             
                  @resource = opts.delete :resource
         | 
| 81 89 |  | 
| 82 | 
            -
                   | 
| 83 | 
            -
                     | 
| 84 | 
            -
                      @errors. | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
                     | 
| 90 | 
            +
                  case @errors
         | 
| 91 | 
            +
                    when Hash
         | 
| 92 | 
            +
                      opts[:message] ||= @errors.each_pair.map do |k, v|
         | 
| 93 | 
            +
                        @errors.is_a?(Array) ? "#{k}: #{v.join(', ')}" : "#{k}: #{v}"
         | 
| 94 | 
            +
                      end.join ', '
         | 
| 95 | 
            +
                    when Array
         | 
| 96 | 
            +
                      opts[:message] ||= @errors.join ', '
         | 
| 88 97 | 
             
                  end
         | 
| 89 98 |  | 
| 90 99 | 
             
                  super
         | 
| 91 100 | 
             
                end
         | 
| 92 101 | 
             
              end
         | 
| 93 102 |  | 
| 103 | 
            +
              # 500
         | 
| 104 | 
            +
              class ServerError < ErroneousResponse; end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              # 502
         | 
| 107 | 
            +
              class BadGateway < ErroneousResponse; end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              # 503
         | 
| 110 | 
            +
              class ServiceUnavailable < ErroneousResponse; end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
              # 504
         | 
| 113 | 
            +
              class GatewayTimeout < ErroneousResponse; end
         | 
| 114 | 
            +
             | 
| 94 115 | 
             
              # A ResourceNotLoaded error will be thrown when calling some
         | 
| 95 116 | 
             
              # modifing methods on not loaded resources as it is usally
         | 
| 96 117 | 
             
              # unwanted to call e.g. `update_attributes` on a not loaded
         | 
| @@ -111,8 +132,7 @@ module Acfs | |
| 111 132 | 
             
              # parent resource. Check if the type is set to the correct
         | 
| 112 133 | 
             
              # Acfs::Resource Name
         | 
| 113 134 | 
             
              class ResourceTypeError < Error
         | 
| 114 | 
            -
                attr_reader :base_class
         | 
| 115 | 
            -
                attr_reader :type_name
         | 
| 135 | 
            +
                attr_reader :base_class, :type_name
         | 
| 116 136 |  | 
| 117 137 | 
             
                def initialize(opts = {})
         | 
| 118 138 | 
             
                  @base_class    = opts.delete :base_class
         | 
    
        data/lib/acfs/global.rb
    CHANGED
    
    | @@ -71,12 +71,12 @@ module Acfs | |
| 71 71 | 
             
                def add_callback(resource, &block)
         | 
| 72 72 | 
             
                  unless resource.respond_to?(:__callbacks__)
         | 
| 73 73 | 
             
                    raise ArgumentError.new 'Given resource is not an Acfs resource ' \
         | 
| 74 | 
            -
             | 
| 74 | 
            +
                                            "delegator but a: #{resource.class.name}"
         | 
| 75 75 | 
             
                  end
         | 
| 76 76 | 
             
                  return false if block.nil?
         | 
| 77 77 |  | 
| 78 78 | 
             
                  if resource.nil? || resource.loaded?
         | 
| 79 | 
            -
                     | 
| 79 | 
            +
                    yield resource
         | 
| 80 80 | 
             
                  else
         | 
| 81 81 | 
             
                    resource.__callbacks__ << block
         | 
| 82 82 | 
             
                  end
         | 
    
        data/lib/acfs/location.rb
    CHANGED
    
    | @@ -6,36 +6,31 @@ module Acfs | |
| 6 6 | 
             
              # Describes a URL with placeholders.
         | 
| 7 7 | 
             
              #
         | 
| 8 8 | 
             
              class Location
         | 
| 9 | 
            -
                attr_reader :arguments, :raw, :struct, : | 
| 9 | 
            +
                attr_reader :arguments, :raw, :struct, :vars
         | 
| 10 10 |  | 
| 11 11 | 
             
                REGEXP = /^:([A-z][A-z0-9_]*)$/.freeze
         | 
| 12 12 |  | 
| 13 | 
            -
                def initialize(uri,  | 
| 13 | 
            +
                def initialize(uri, vars = {})
         | 
| 14 14 | 
             
                  @raw       = URI.parse uri
         | 
| 15 | 
            -
                  @ | 
| 15 | 
            +
                  @vars      = vars
         | 
| 16 16 | 
             
                  @struct    = raw.path.split('/').reject(&:empty?).map {|s| s =~ REGEXP ? Regexp.last_match[1].to_sym : s }
         | 
| 17 17 | 
             
                  @arguments = struct.select {|s| s.is_a?(Symbol) }
         | 
| 18 18 | 
             
                end
         | 
| 19 19 |  | 
| 20 | 
            -
                def build( | 
| 21 | 
            -
                   | 
| 22 | 
            -
                    raise ArgumentError.new "URI path arguments must be a hash, `#{args.inspect}' given."
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  self.class.new raw.to_s, args.merge(self.args)
         | 
| 20 | 
            +
                def build(vars)
         | 
| 21 | 
            +
                  self.class.new raw.to_s, vars.stringify_keys.merge(self.vars)
         | 
| 26 22 | 
             
                end
         | 
| 27 23 |  | 
| 28 24 | 
             
                def extract_from(*args)
         | 
| 29 | 
            -
                   | 
| 30 | 
            -
             | 
| 31 | 
            -
                  end
         | 
| 25 | 
            +
                  vars = {}
         | 
| 26 | 
            +
                  arguments.each {|key| vars[key.to_s] = extract_arg(key, args) }
         | 
| 32 27 |  | 
| 33 | 
            -
                  build | 
| 28 | 
            +
                  build(vars)
         | 
| 34 29 | 
             
                end
         | 
| 35 30 |  | 
| 36 31 | 
             
                def str
         | 
| 37 32 | 
             
                  uri = raw.dup
         | 
| 38 | 
            -
                  uri.path =  | 
| 33 | 
            +
                  uri.path = "/#{struct.map {|s| lookup_variable(s) }.join('/')}"
         | 
| 39 34 | 
             
                  uri.to_s
         | 
| 40 35 | 
             
                end
         | 
| 41 36 |  | 
| @@ -49,34 +44,33 @@ module Acfs | |
| 49 44 | 
             
                def extract_arg(key, hashes)
         | 
| 50 45 | 
             
                  hashes.each_with_index do |hash, index|
         | 
| 51 46 | 
             
                    if hash.key?(key)
         | 
| 52 | 
            -
                      return (index  | 
| 47 | 
            +
                      return (index.zero? ? hash.delete(key) : hash.fetch(key))
         | 
| 53 48 | 
             
                    end
         | 
| 54 49 | 
             
                  end
         | 
| 55 50 |  | 
| 56 51 | 
             
                  nil
         | 
| 57 52 | 
             
                end
         | 
| 58 53 |  | 
| 59 | 
            -
                def  | 
| 60 | 
            -
                   | 
| 61 | 
            -
             | 
| 54 | 
            +
                def lookup_variable(name)
         | 
| 55 | 
            +
                  return name unless name.is_a?(Symbol)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  value = vars.fetch(name.to_s) do
         | 
| 58 | 
            +
                    if @raise.nil? || @raise
         | 
| 59 | 
            +
                      raise ArgumentError.new <<~ERROR.strip
         | 
| 60 | 
            +
                        URI path argument `#{name}' missing on `#{self}'. Given: `#{vars}.inspect'
         | 
| 61 | 
            +
                      ERROR
         | 
| 62 | 
            +
                    end
         | 
| 62 63 |  | 
| 63 | 
            -
             | 
| 64 | 
            -
                   | 
| 65 | 
            -
                  return ::URI.encode_www_form_component(value) unless value.empty?
         | 
| 64 | 
            +
                    ":#{name}"
         | 
| 65 | 
            +
                  end
         | 
| 66 66 |  | 
| 67 | 
            -
                   | 
| 68 | 
            -
                end
         | 
| 67 | 
            +
                  value = value.to_s.strip
         | 
| 69 68 |  | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
                    args.fetch(sym) do
         | 
| 73 | 
            -
                      if args[:raise].nil? || args[:raise]
         | 
| 74 | 
            -
                        raise ArgumentError.new "URI path argument `#{sym}' missing on `#{self}'. Given: `#{args}.inspect'"
         | 
| 75 | 
            -
                      else
         | 
| 76 | 
            -
                        ":#{sym}"
         | 
| 77 | 
            -
                      end
         | 
| 78 | 
            -
                    end
         | 
| 69 | 
            +
                  if value.empty?
         | 
| 70 | 
            +
                    raise ArgumentError.new "Cannot replace path argument `#{name}' with empty string."
         | 
| 79 71 | 
             
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  ::URI.encode_www_form_component(value)
         | 
| 80 74 | 
             
                end
         | 
| 81 75 | 
             
              end
         | 
| 82 76 | 
             
            end
         | 
    
        data/lib/acfs/middleware/base.rb
    CHANGED
    
    
    
        data/lib/acfs/middleware/json.rb
    CHANGED
    
    | @@ -12,11 +12,13 @@ module Acfs | |
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 14 | 
             
                  def encode(data)
         | 
| 15 | 
            -
                    ::MultiJson.dump | 
| 15 | 
            +
                    ::MultiJson.dump(data)
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  def decode(body)
         | 
| 19 | 
            -
                    ::MultiJson.load | 
| 19 | 
            +
                    ::MultiJson.load(body)
         | 
| 20 | 
            +
                  rescue ::MultiJson::ParseError => e
         | 
| 21 | 
            +
                    raise ::JSON::ParserError.new(e)
         | 
| 20 22 | 
             
                  end
         | 
| 21 23 | 
             
                end
         | 
| 22 24 |  | 
| @@ -7,19 +7,17 @@ module Acfs | |
| 7 7 | 
             
                # Log requests and responses.
         | 
| 8 8 | 
             
                #
         | 
| 9 9 | 
             
                class Logger < Base
         | 
| 10 | 
            -
                   | 
| 10 | 
            +
                  attr_reader :logger
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def initialize(app, **opts)
         | 
| 11 13 | 
             
                    super
         | 
| 12 | 
            -
                    @logger = options[:logger]  | 
| 14 | 
            +
                    @logger = options[:logger] || ::Logger.new($stdout)
         | 
| 13 15 | 
             
                  end
         | 
| 14 16 |  | 
| 15 17 | 
             
                  def response(res, nxt)
         | 
| 16 18 | 
             
                    logger.info "[ACFS] #{res.request.method.to_s.upcase} #{res.request.url} -> #{res.status}"
         | 
| 17 19 | 
             
                    nxt.call res
         | 
| 18 20 | 
             
                  end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                  def logger
         | 
| 21 | 
            -
                    @logger ||= ::Logger.new STDOUT
         | 
| 22 | 
            -
                  end
         | 
| 23 21 | 
             
                end
         | 
| 24 22 | 
             
              end
         | 
| 25 23 | 
             
            end
         | 
| @@ -29,7 +29,7 @@ module Acfs | |
| 29 29 | 
             
                    request.headers['Accept'] = accept.join(',')
         | 
| 30 30 |  | 
| 31 31 | 
             
                    request.on_complete do |response, nxt|
         | 
| 32 | 
            -
                      response.data = decode | 
| 32 | 
            +
                      response.data = decode(response.body) if mime == response.content_type
         | 
| 33 33 |  | 
| 34 34 | 
             
                      nxt.call response
         | 
| 35 35 | 
             
                    end
         |