fmrest 0.7.1 → 0.11.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/.github/workflows/ci.yml +33 -0
 - data/CHANGELOG.md +37 -0
 - data/README.md +176 -26
 - data/fmrest.gemspec +2 -2
 - data/lib/fmrest.rb +8 -3
 - data/lib/fmrest/connection_settings.rb +124 -0
 - data/lib/fmrest/errors.rb +2 -0
 - data/lib/fmrest/spyke/base.rb +2 -0
 - data/lib/fmrest/spyke/model.rb +2 -0
 - data/lib/fmrest/spyke/model/auth.rb +8 -0
 - data/lib/fmrest/spyke/model/connection.rb +88 -18
 - data/lib/fmrest/spyke/model/global_fields.rb +40 -0
 - data/lib/fmrest/spyke/model/orm.rb +2 -1
 - data/lib/fmrest/spyke/model/serialization.rb +16 -5
 - data/lib/fmrest/spyke/relation.rb +73 -0
 - data/lib/fmrest/spyke/spyke_formatter.rb +46 -9
 - data/lib/fmrest/string_date.rb +46 -7
 - data/lib/fmrest/token_store.rb +6 -0
 - data/lib/fmrest/token_store/base.rb +3 -3
 - data/lib/fmrest/v1.rb +8 -4
 - data/lib/fmrest/v1/auth.rb +30 -0
 - data/lib/fmrest/v1/connection.rb +54 -28
 - data/lib/fmrest/v1/dates.rb +81 -0
 - data/lib/fmrest/v1/raise_errors.rb +3 -1
 - data/lib/fmrest/v1/token_session.rb +41 -49
 - data/lib/fmrest/v1/type_coercer.rb +111 -36
 - data/lib/fmrest/v1/utils.rb +0 -17
 - data/lib/fmrest/version.rb +1 -1
 - metadata +18 -13
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: e6433cf32d6d0111377f6080967e96fd5ff4cd54c5e223551c777a24c58e8662
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 1a0fcd5951ab9b5bee88d822a429f16b02dcb01d5c95f74de9d529bb1ed00278
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: a0130f8b3598b3723d3c9e2514448a44a10381045176894cfed21244547c7ed41d0bc83fd18ab40981e9a2fc9ac7bd5c7290a5026b301878ea2c1d9ec440bb76
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 0fc6a827bb57dea0b261bb812b58322dad8a01eecf78a4344bef877732f4f0b016636750833c048c5831e63e5015c707264a0697282cc7fba9a6879a0cd7e8b1
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # This workflow uses actions that are not certified by GitHub.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # They are provided by a third-party and are governed by
         
     | 
| 
      
 3 
     | 
    
         
            +
            # separate terms of service, privacy policy, and support
         
     | 
| 
      
 4 
     | 
    
         
            +
            # documentation.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
         
     | 
| 
      
 6 
     | 
    
         
            +
            # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            name: CI
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            on:
         
     | 
| 
      
 11 
     | 
    
         
            +
              push:
         
     | 
| 
      
 12 
     | 
    
         
            +
                branches: [ master ]
         
     | 
| 
      
 13 
     | 
    
         
            +
              pull_request:
         
     | 
| 
      
 14 
     | 
    
         
            +
                branches: [ master ]
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            jobs:
         
     | 
| 
      
 17 
     | 
    
         
            +
              test:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                runs-on: ubuntu-latest
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                steps:
         
     | 
| 
      
 22 
     | 
    
         
            +
                - uses: actions/checkout@v2
         
     | 
| 
      
 23 
     | 
    
         
            +
                - name: Set up Ruby
         
     | 
| 
      
 24 
     | 
    
         
            +
                # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
         
     | 
| 
      
 25 
     | 
    
         
            +
                # change this to (see https://github.com/ruby/setup-ruby#versioning):
         
     | 
| 
      
 26 
     | 
    
         
            +
                # uses: ruby/setup-ruby@v1
         
     | 
| 
      
 27 
     | 
    
         
            +
                  uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
         
     | 
| 
      
 28 
     | 
    
         
            +
                  with:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    ruby-version: 2.6
         
     | 
| 
      
 30 
     | 
    
         
            +
                - name: Install dependencies
         
     | 
| 
      
 31 
     | 
    
         
            +
                  run: bundle install
         
     | 
| 
      
 32 
     | 
    
         
            +
                - name: Run specs
         
     | 
| 
      
 33 
     | 
    
         
            +
                  run: bundle exec rspec spec
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,42 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ## Changelog
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ### 0.11.0
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * Added custom class for connection settings, providing indifferent access
         
     | 
| 
      
 6 
     | 
    
         
            +
              (i.e. keys can be strings or symbols), and centralized default values and
         
     | 
| 
      
 7 
     | 
    
         
            +
              validations
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Added `:autologin`, `:token` and `:token_store` connection settings
         
     | 
| 
      
 9 
     | 
    
         
            +
            * Added `FmRest::Base.fmrest_config_overlay=` and related methods
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Added `FmRest::V1.request_auth_token` and
         
     | 
| 
      
 11 
     | 
    
         
            +
              `FmRest::Spyke::Base.request_auth_token` (as well as `!`-suffixed versions
         
     | 
| 
      
 12 
     | 
    
         
            +
              which raise exceptions on failure)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ### 0.10.1
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            * Fix `URI.escape` obsolete warning messages in Ruby 2.7 by replacing it with
         
     | 
| 
      
 17 
     | 
    
         
            +
              `URI.encode_www_form_component`
         
     | 
| 
      
 18 
     | 
    
         
            +
              ([PR#40](https://github.com/beezwax/fmrest-ruby/pull/40))
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            ### 0.10.0
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            * Added `FmRest::StringDateAwareness` module to correct some issues when using
         
     | 
| 
      
 23 
     | 
    
         
            +
              `FmRest::StringDate`
         
     | 
| 
      
 24 
     | 
    
         
            +
            * Added basic timezones support
         
     | 
| 
      
 25 
     | 
    
         
            +
            * Deprecated `class < FmRest::Spyke::Base(config_hash)` syntax in favor of
         
     | 
| 
      
 26 
     | 
    
         
            +
              using `self.fmrest_config=`
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            ### 0.9.0
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            * Added `FmRest::Spyke::Base.set_globals`
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            ### 0.8.0
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            * Improved metadata when using `FmRest::Spyke::Model`. Metadata now uses
         
     | 
| 
      
 35 
     | 
    
         
            +
              Struct/OpenStruct, so properties are accessible through `.property`, as well
         
     | 
| 
      
 36 
     | 
    
         
            +
              as `[:property]`
         
     | 
| 
      
 37 
     | 
    
         
            +
            * Added batch-finders `.find_in_batches` and `.find_each` for
         
     | 
| 
      
 38 
     | 
    
         
            +
            * `FmRest::Spyke::Base`
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       3 
40 
     | 
    
         
             
            ### 0.7.1
         
     | 
| 
       4 
41 
     | 
    
         | 
| 
       5 
42 
     | 
    
         
             
            * Made sure `Model.find_one` and `Model.find_some` work without needing to call
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,9 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # fmrest-ruby
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            [](https://rubygems.org/gems/fmrest)
         
     | 
| 
      
 4 
     | 
    
         
            +
            
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
6 
     | 
    
         
             
            A Ruby client for
         
     | 
| 
       6 
     | 
    
         
            -
            [FileMaker 18's Data API](https:// 
     | 
| 
      
 7 
     | 
    
         
            +
            [FileMaker 18 and 19's Data API](https://help.claris.com/en/data-api-guide)
         
     | 
| 
       7 
8 
     | 
    
         
             
            using
         
     | 
| 
       8 
9 
     | 
    
         
             
            [Faraday](https://github.com/lostisland/faraday) and with optional
         
     | 
| 
       9 
10 
     | 
    
         
             
            [Spyke](https://github.com/balvig/spyke) support (ActiveRecord-ish models).
         
     | 
| 
         @@ -120,6 +121,9 @@ Option              | Description                                | Format 
     | 
|
| 
       120 
121 
     | 
    
         
             
            `:date_format`      | Date parsing format                        | String (FM date format)     | `"MM/dd/yyyy"`
         
     | 
| 
       121 
122 
     | 
    
         
             
            `:timestamp_format` | Timestmap parsing format                   | String (FM date format)     | `"MM/dd/yyyy HH:mm:ss"`
         
     | 
| 
       122 
123 
     | 
    
         
             
            `:time_format`      | Time parsing format                        | String (FM date format)     | `"HH:mm:ss"`
         
     | 
| 
      
 124 
     | 
    
         
            +
            `:timezone`         | The timezone for the FM server             | `:local` \| `:utc` \| `nil` | `nil`
         
     | 
| 
      
 125 
     | 
    
         
            +
            `:autologin`        | Whether to automatically start Data API sessions | Boolean               | `true`
         
     | 
| 
      
 126 
     | 
    
         
            +
            `:token`            | Used to manually provide a session token (e.g. if `:autologin` is `false`) | String | None
         
     | 
| 
       123 
127 
     | 
    
         | 
| 
       124 
128 
     | 
    
         
             
            ### Default connection settings
         
     | 
| 
       125 
129 
     | 
    
         | 
| 
         @@ -195,7 +199,7 @@ FmRest.token_store = FmRest::TokenStore::Redis.new(redis: Redis.new, prefix: "my 
     | 
|
| 
       195 
199 
     | 
    
         
             
            FmRest.token_store = FmRest::TokenStore::Redis.new(prefix: "my-fancy-prefix:", host: "10.0.1.1", port: 6380, db: 15)
         
     | 
| 
       196 
200 
     | 
    
         
             
            ```
         
     | 
| 
       197 
201 
     | 
    
         | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
      
 202 
     | 
    
         
            +
            NOTE: redis-rb is not included as a gem dependency of fmrest-ruby, so you'll
         
     | 
| 
       199 
203 
     | 
    
         
             
            have to add it to your Gemfile.
         
     | 
| 
       200 
204 
     | 
    
         | 
| 
       201 
205 
     | 
    
         
             
            ### Moneta
         
     | 
| 
         @@ -232,7 +236,7 @@ FmRest.token_store = FmRest::TokenStore::Moneta.new( 
     | 
|
| 
       232 
236 
     | 
    
         
             
            )
         
     | 
| 
       233 
237 
     | 
    
         
             
            ```
         
     | 
| 
       234 
238 
     | 
    
         | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
      
 239 
     | 
    
         
            +
            NOTE: the moneta gem is not included as a dependency of fmrest-ruby, so
         
     | 
| 
       236 
240 
     | 
    
         
             
            you'll have to add it to your Gemfile.
         
     | 
| 
       237 
241 
     | 
    
         | 
| 
       238 
242 
     | 
    
         | 
| 
         @@ -270,9 +274,9 @@ a DSL in model classes). 
     | 
|
| 
       270 
274 
     | 
    
         
             
            ### Hybrid string/date objects
         
     | 
| 
       271 
275 
     | 
    
         | 
| 
       272 
276 
     | 
    
         
             
            `FmRest::StringDate` and `FmRest::StringDateTime` are special classes that
         
     | 
| 
       273 
     | 
    
         
            -
            inherit from `String`, but internally parse and store a `Date 
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
      
 277 
     | 
    
         
            +
            inherit from `String`, but internally parse and store a `Date` or `DateTime`,
         
     | 
| 
      
 278 
     | 
    
         
            +
            and delegate any methods not provided by `String` to those objects. In other
         
     | 
| 
      
 279 
     | 
    
         
            +
            words, they quack like a duck *and* bark like a dog.
         
     | 
| 
       276 
280 
     | 
    
         | 
| 
       277 
281 
     | 
    
         
             
            You can use these when you want fmrest-ruby to provide you with date objects,
         
     | 
| 
       278 
282 
     | 
    
         
             
            but you don't want to worry about date coercion of false positives (i.e. a
         
     | 
| 
         @@ -280,7 +284,29 @@ string field that gets converted to `Date` because it just so matched the given 
     | 
|
| 
       280 
284 
     | 
    
         
             
            date format).
         
     | 
| 
       281 
285 
     | 
    
         | 
| 
       282 
286 
     | 
    
         
             
            Be warned however that these classes come with a fair share of known gotchas
         
     | 
| 
       283 
     | 
    
         
            -
            (see GitHub wiki for more info).
         
     | 
| 
      
 287 
     | 
    
         
            +
            (see GitHub wiki for more info). Some of those gothas can be removed by calling
         
     | 
| 
      
 288 
     | 
    
         
            +
             
     | 
| 
      
 289 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 290 
     | 
    
         
            +
            FmRest::StringDateAwareness.enable
         
     | 
| 
      
 291 
     | 
    
         
            +
            ```
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
            Which will extend the core `Date` and `DateTime` classes to be aware of
         
     | 
| 
      
 294 
     | 
    
         
            +
            `FmRest::StringDate`, especially when calling `Date.===`, `Date.parse` or
         
     | 
| 
      
 295 
     | 
    
         
            +
            `Date._parse`.
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
            If you're working with ActiveRecord models this will also make them accept
         
     | 
| 
      
 298 
     | 
    
         
            +
            `FmRest::StringDate` values for date fields.
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
            ### Timezones
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
            fmrest-ruby has basic timezone support. You can set the `:timezone` option in
         
     | 
| 
      
 303 
     | 
    
         
            +
            your connection settings to one of the following values:
         
     | 
| 
      
 304 
     | 
    
         
            +
             
     | 
| 
      
 305 
     | 
    
         
            +
            * `:local` - dates will be converted to your system local time offset (as
         
     | 
| 
      
 306 
     | 
    
         
            +
              defined by `ENV["TZ"]`), or the timezone set by `Time.zone` if you're using
         
     | 
| 
      
 307 
     | 
    
         
            +
              ActiveSupport
         
     | 
| 
      
 308 
     | 
    
         
            +
            * `:utc` - dates will be converted to UTC offset
         
     | 
| 
      
 309 
     | 
    
         
            +
            * `nil` - (default) ignore timezones altogether
         
     | 
| 
       284 
310 
     | 
    
         | 
| 
       285 
311 
     | 
    
         | 
| 
       286 
312 
     | 
    
         
             
            ## Spyke support (ActiveRecord-like ORM)
         
     | 
| 
         @@ -322,17 +348,6 @@ class Honeybee < FmRest::Spyke::Base 
     | 
|
| 
       322 
348 
     | 
    
         
             
            end
         
     | 
| 
       323 
349 
     | 
    
         
             
            ```
         
     | 
| 
       324 
350 
     | 
    
         | 
| 
       325 
     | 
    
         
            -
            In this case you can pass the [`fmrest_config`](#modelfmrest_config) hash as an
         
     | 
| 
       326 
     | 
    
         
            -
            argument to `Base()`:
         
     | 
| 
       327 
     | 
    
         
            -
             
     | 
| 
       328 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       329 
     | 
    
         
            -
            class Honeybee < FmRest::Spyke::Base(host: "...", database: "...", username: "...", password: "...")
         
     | 
| 
       330 
     | 
    
         
            -
            end
         
     | 
| 
       331 
     | 
    
         
            -
             
     | 
| 
       332 
     | 
    
         
            -
            Honeybee.fmrest_config
         
     | 
| 
       333 
     | 
    
         
            -
            # => { host: "...", database: "...", username: "...", password: "..." }
         
     | 
| 
       334 
     | 
    
         
            -
            ```
         
     | 
| 
       335 
     | 
    
         
            -
             
     | 
| 
       336 
351 
     | 
    
         
             
            All of Spyke's basic ORM operations work:
         
     | 
| 
       337 
352 
     | 
    
         | 
| 
       338 
353 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -406,6 +421,59 @@ class Honeybee < BeeBase 
     | 
|
| 
       406 
421 
     | 
    
         
             
            end
         
     | 
| 
       407 
422 
     | 
    
         
             
            ```
         
     | 
| 
       408 
423 
     | 
    
         | 
| 
      
 424 
     | 
    
         
            +
            ### Model.fmrest_config_overlay=
         
     | 
| 
      
 425 
     | 
    
         
            +
             
     | 
| 
      
 426 
     | 
    
         
            +
            There may be cases where you want to use different connection settings
         
     | 
| 
      
 427 
     | 
    
         
            +
            depending on context, for example if you want to use username and password
         
     | 
| 
      
 428 
     | 
    
         
            +
            provided by the user in a web application. Since `Model.fmrest_config` is
         
     | 
| 
      
 429 
     | 
    
         
            +
            global, changing the username/password for one context would also change it for
         
     | 
| 
      
 430 
     | 
    
         
            +
            all other contexts, leading to security issues.
         
     | 
| 
      
 431 
     | 
    
         
            +
             
     | 
| 
      
 432 
     | 
    
         
            +
            `Model.fmrest_config_overlay=` solves that issue by allowing you to override
         
     | 
| 
      
 433 
     | 
    
         
            +
            some settings in a thread-local and reversible manner. That way, using the same
         
     | 
| 
      
 434 
     | 
    
         
            +
            example as above, you could connect to the Data API with user-provided
         
     | 
| 
      
 435 
     | 
    
         
            +
            credentials without having them leak into other users of your web application.
         
     | 
| 
      
 436 
     | 
    
         
            +
             
     | 
| 
      
 437 
     | 
    
         
            +
            E.g.:
         
     | 
| 
      
 438 
     | 
    
         
            +
             
     | 
| 
      
 439 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 440 
     | 
    
         
            +
            class BeeBase < Spyke::Base
         
     | 
| 
      
 441 
     | 
    
         
            +
              include FmRest::Spyke
         
     | 
| 
      
 442 
     | 
    
         
            +
             
     | 
| 
      
 443 
     | 
    
         
            +
              # Host and database provided as base settings
         
     | 
| 
      
 444 
     | 
    
         
            +
              self.fmrest_config = {
         
     | 
| 
      
 445 
     | 
    
         
            +
                host:     "example.com",
         
     | 
| 
      
 446 
     | 
    
         
            +
                database: "My Database"
         
     | 
| 
      
 447 
     | 
    
         
            +
              }
         
     | 
| 
      
 448 
     | 
    
         
            +
            end
         
     | 
| 
      
 449 
     | 
    
         
            +
             
     | 
| 
      
 450 
     | 
    
         
            +
            # E.g. in a controller-action of a Rails application:
         
     | 
| 
      
 451 
     | 
    
         
            +
             
     | 
| 
      
 452 
     | 
    
         
            +
            # User-provided credentials
         
     | 
| 
      
 453 
     | 
    
         
            +
            BeeBase.fmrest_config_overlay = {
         
     | 
| 
      
 454 
     | 
    
         
            +
              username: params[:username],
         
     | 
| 
      
 455 
     | 
    
         
            +
              password: params[:password]
         
     | 
| 
      
 456 
     | 
    
         
            +
            }
         
     | 
| 
      
 457 
     | 
    
         
            +
             
     | 
| 
      
 458 
     | 
    
         
            +
            # Perform some Data API requests ...
         
     | 
| 
      
 459 
     | 
    
         
            +
            ```
         
     | 
| 
      
 460 
     | 
    
         
            +
             
     | 
| 
      
 461 
     | 
    
         
            +
            ### Model.clear_fmrest_config_overlay
         
     | 
| 
      
 462 
     | 
    
         
            +
             
     | 
| 
      
 463 
     | 
    
         
            +
            Clears the thread-local settings provided to `fmrest_config_overaly=`.
         
     | 
| 
      
 464 
     | 
    
         
            +
             
     | 
| 
      
 465 
     | 
    
         
            +
            ### Model.with_overlay
         
     | 
| 
      
 466 
     | 
    
         
            +
             
     | 
| 
      
 467 
     | 
    
         
            +
            Runs a block with the given settings overlay, resetting them after the block
         
     | 
| 
      
 468 
     | 
    
         
            +
            finishes running. It wraps execution in its own fiber, so it doesn't affect the
         
     | 
| 
      
 469 
     | 
    
         
            +
            overlay of the currently-running thread.
         
     | 
| 
      
 470 
     | 
    
         
            +
             
     | 
| 
      
 471 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 472 
     | 
    
         
            +
            Honeybee.with_overlay(username: "...", password: "...") do
         
     | 
| 
      
 473 
     | 
    
         
            +
              Honeybee.query(...)
         
     | 
| 
      
 474 
     | 
    
         
            +
            end
         
     | 
| 
      
 475 
     | 
    
         
            +
            ```
         
     | 
| 
      
 476 
     | 
    
         
            +
             
     | 
| 
       409 
477 
     | 
    
         
             
            ### Model.layout
         
     | 
| 
       410 
478 
     | 
    
         | 
| 
       411 
479 
     | 
    
         
             
            Use `layout` to set the `:layout` part of API URLs, e.g.:
         
     | 
| 
         @@ -422,6 +490,15 @@ Data API models. 
     | 
|
| 
       422 
490 
     | 
    
         
             
            Note that you only need to set this if the name of the model and the name of
         
     | 
| 
       423 
491 
     | 
    
         
             
            the layout differ, otherwise the default will just work.
         
     | 
| 
       424 
492 
     | 
    
         | 
| 
      
 493 
     | 
    
         
            +
            ### Model.request_auth_token
         
     | 
| 
      
 494 
     | 
    
         
            +
             
     | 
| 
      
 495 
     | 
    
         
            +
            Requests a Data API session token using the connection settings in
         
     | 
| 
      
 496 
     | 
    
         
            +
            `fmrest_config` and returns it if successful, otherwise returns `false`.
         
     | 
| 
      
 497 
     | 
    
         
            +
             
     | 
| 
      
 498 
     | 
    
         
            +
            You normally don't need to use this method as fmrest-ruby will automatically
         
     | 
| 
      
 499 
     | 
    
         
            +
            request and store session tokens for you (provided that `:autologin` is
         
     | 
| 
      
 500 
     | 
    
         
            +
            `true`).
         
     | 
| 
      
 501 
     | 
    
         
            +
             
     | 
| 
       425 
502 
     | 
    
         
             
            ### Model.logout
         
     | 
| 
       426 
503 
     | 
    
         | 
| 
       427 
504 
     | 
    
         
             
            Use `logout` to log out from the database session (you may call it on any model
         
     | 
| 
         @@ -555,7 +632,7 @@ Honeybee.limit(10) 
     | 
|
| 
       555 
632 
     | 
    
         
             
            ```
         
     | 
| 
       556 
633 
     | 
    
         | 
| 
       557 
634 
     | 
    
         
             
            NOTE: You can also set a default limit value for a model class, see
         
     | 
| 
       558 
     | 
    
         
            -
            [ 
     | 
| 
      
 635 
     | 
    
         
            +
            [other notes on querying](#other-notes-on-querying).
         
     | 
| 
       559 
636 
     | 
    
         | 
| 
       560 
637 
     | 
    
         
             
            You can also use `.limit` to set limits on portals:
         
     | 
| 
       561 
638 
     | 
    
         | 
| 
         @@ -727,15 +804,15 @@ the scope object: 
     | 
|
| 
       727 
804 
     | 
    
         
             
            Honeybee.limit(10).sort(:name).find_some # => [<Honeybee...>, ...]
         
     | 
| 
       728 
805 
     | 
    
         
             
            ```
         
     | 
| 
       729 
806 
     | 
    
         | 
| 
       730 
     | 
    
         
            -
            If you want just a single result you can use `. 
     | 
| 
      
 807 
     | 
    
         
            +
            If you want just a single result you can use `.first` instead (this will
         
     | 
| 
       731 
808 
     | 
    
         
             
            force `.limit(1)`):
         
     | 
| 
       732 
809 
     | 
    
         | 
| 
       733 
810 
     | 
    
         
             
            ```ruby
         
     | 
| 
       734 
     | 
    
         
            -
            Honeybee.query(name: "Hutch"). 
     | 
| 
      
 811 
     | 
    
         
            +
            Honeybee.query(name: "Hutch").first # => <Honeybee...>
         
     | 
| 
       735 
812 
     | 
    
         
             
            ```
         
     | 
| 
       736 
813 
     | 
    
         | 
| 
       737 
814 
     | 
    
         
             
            If you know the id of the record you should use `.find(id)` instead of
         
     | 
| 
       738 
     | 
    
         
            -
            `.query(id: id). 
     | 
| 
      
 815 
     | 
    
         
            +
            `.query(id: id).first` (so that the sent request is
         
     | 
| 
       739 
816 
     | 
    
         
             
            `GET ../:layout/records/:id` instead of `POST ../:layout/_find`).
         
     | 
| 
       740 
817 
     | 
    
         | 
| 
       741 
818 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -746,6 +823,52 @@ Note also that if you use `.find(id)` your `.query()` parameters (as well as 
     | 
|
| 
       746 
823 
     | 
    
         
             
            limit, offset and sort parameters) will be discarded as they're not supported
         
     | 
| 
       747 
824 
     | 
    
         
             
            by the single record endpoint.
         
     | 
| 
       748 
825 
     | 
    
         | 
| 
      
 826 
     | 
    
         
            +
             
     | 
| 
      
 827 
     | 
    
         
            +
            ### Finding records in batches
         
     | 
| 
      
 828 
     | 
    
         
            +
             
     | 
| 
      
 829 
     | 
    
         
            +
            Sometimes you want to iterate over a very large number of records to do some
         
     | 
| 
      
 830 
     | 
    
         
            +
            processing, but requesting them all at once would result in one huge request to
         
     | 
| 
      
 831 
     | 
    
         
            +
            the Data API, and loading too many records in memory all at once.
         
     | 
| 
      
 832 
     | 
    
         
            +
             
     | 
| 
      
 833 
     | 
    
         
            +
            To mitigate this problem you can use `.find_in_batches` and `.find_each`. If
         
     | 
| 
      
 834 
     | 
    
         
            +
            you've used ActiveRecord you're probably familiar with how they operate:
         
     | 
| 
      
 835 
     | 
    
         
            +
             
     | 
| 
      
 836 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 837 
     | 
    
         
            +
            # Find records in batches of 100 each
         
     | 
| 
      
 838 
     | 
    
         
            +
            Honeybee.query(hive: "Queensville").find_in_batches(batch_size: 100) do |batch|
         
     | 
| 
      
 839 
     | 
    
         
            +
              dispatch_bees(batch)
         
     | 
| 
      
 840 
     | 
    
         
            +
            end
         
     | 
| 
      
 841 
     | 
    
         
            +
             
     | 
| 
      
 842 
     | 
    
         
            +
            # Iterate over all records using batches
         
     | 
| 
      
 843 
     | 
    
         
            +
            Honeybee.query(hive: "Queensville").find_each(batch_size: 100) do |bee|
         
     | 
| 
      
 844 
     | 
    
         
            +
              bee.dispatch
         
     | 
| 
      
 845 
     | 
    
         
            +
            end
         
     | 
| 
      
 846 
     | 
    
         
            +
            ```
         
     | 
| 
      
 847 
     | 
    
         
            +
             
     | 
| 
      
 848 
     | 
    
         
            +
            `.find_in_batches` yields collections of records (batches), while `.find_each`
         
     | 
| 
      
 849 
     | 
    
         
            +
            yields individual records, but using batches behind the scenes.
         
     | 
| 
      
 850 
     | 
    
         
            +
             
     | 
| 
      
 851 
     | 
    
         
            +
            Both methods accept a block-less form in which case they return an
         
     | 
| 
      
 852 
     | 
    
         
            +
            `Enumerator`:
         
     | 
| 
      
 853 
     | 
    
         
            +
             
     | 
| 
      
 854 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 855 
     | 
    
         
            +
            batch_enum = Honeybee.find_in_batches
         
     | 
| 
      
 856 
     | 
    
         
            +
             
     | 
| 
      
 857 
     | 
    
         
            +
            batch = batch_enum.next # => Spyke::Collection
         
     | 
| 
      
 858 
     | 
    
         
            +
             
     | 
| 
      
 859 
     | 
    
         
            +
            batch_enum.each do |batch|
         
     | 
| 
      
 860 
     | 
    
         
            +
              process_batch(batch)
         
     | 
| 
      
 861 
     | 
    
         
            +
            end
         
     | 
| 
      
 862 
     | 
    
         
            +
             
     | 
| 
      
 863 
     | 
    
         
            +
            record_enum = Honeybee.find_each
         
     | 
| 
      
 864 
     | 
    
         
            +
             
     | 
| 
      
 865 
     | 
    
         
            +
            record_enum.next # => Honeybee
         
     | 
| 
      
 866 
     | 
    
         
            +
            ```
         
     | 
| 
      
 867 
     | 
    
         
            +
             
     | 
| 
      
 868 
     | 
    
         
            +
            NOTE: By its nature, batch processing is subject to race conditions if other
         
     | 
| 
      
 869 
     | 
    
         
            +
            processes are modifying the database.
         
     | 
| 
      
 870 
     | 
    
         
            +
             
     | 
| 
      
 871 
     | 
    
         
            +
             
     | 
| 
       749 
872 
     | 
    
         
             
            ### Container fields
         
     | 
| 
       750 
873 
     | 
    
         | 
| 
       751 
874 
     | 
    
         
             
            You can define container fields on your model class with `container`:
         
     | 
| 
         @@ -783,6 +906,7 @@ bee.photo.upload(filename_or_io) # Upload a file to the container 
     | 
|
| 
       783 
906 
     | 
    
         
             
            * `:content_type` - The MIME content type to use (defaults to
         
     | 
| 
       784 
907 
     | 
    
         
             
              `application/octet-stream`)
         
     | 
| 
       785 
908 
     | 
    
         | 
| 
      
 909 
     | 
    
         
            +
             
     | 
| 
       786 
910 
     | 
    
         
             
            ### Script execution
         
     | 
| 
       787 
911 
     | 
    
         | 
| 
       788 
912 
     | 
    
         
             
            The Data API allows running scripts as part of many types of requests.
         
     | 
| 
         @@ -870,7 +994,7 @@ separately, under their matching key. 
     | 
|
| 
       870 
994 
     | 
    
         
             
            ```ruby
         
     | 
| 
       871 
995 
     | 
    
         
             
            bee.save(script: { presort: "My Presort Script", after: "My Script" })
         
     | 
| 
       872 
996 
     | 
    
         | 
| 
       873 
     | 
    
         
            -
            Honeybee.last_request_metadata 
     | 
| 
      
 997 
     | 
    
         
            +
            Honeybee.last_request_metadata.script
         
     | 
| 
       874 
998 
     | 
    
         
             
            # => { after: { result: "oh hi", error: "0" }, presort: { result: "lo", error: "0" } }
         
     | 
| 
       875 
999 
     | 
    
         
             
            ```
         
     | 
| 
       876 
1000 
     | 
    
         | 
| 
         @@ -884,7 +1008,7 @@ is performed on that scope. 
     | 
|
| 
       884 
1008 
     | 
    
         | 
| 
       885 
1009 
     | 
    
         
             
            ```ruby
         
     | 
| 
       886 
1010 
     | 
    
         
             
            # Find one Honeybee record executing a presort and after script
         
     | 
| 
       887 
     | 
    
         
            -
            Honeybee.script(presort: ["My Presort Script", "parameter"], after: "My Script"). 
     | 
| 
      
 1011 
     | 
    
         
            +
            Honeybee.script(presort: ["My Presort Script", "parameter"], after: "My Script").first
         
     | 
| 
       888 
1012 
     | 
    
         
             
            ```
         
     | 
| 
       889 
1013 
     | 
    
         | 
| 
       890 
1014 
     | 
    
         
             
            The model class' `.last_request_metadata` will be set in case you need to get the result.
         
     | 
| 
         @@ -896,6 +1020,32 @@ to retrieving single records, in that case you'll have to use 
     | 
|
| 
       896 
1020 
     | 
    
         
             
            `.last_request_metadata`.
         
     | 
| 
       897 
1021 
     | 
    
         | 
| 
       898 
1022 
     | 
    
         | 
| 
      
 1023 
     | 
    
         
            +
            ### Setting global field values
         
     | 
| 
      
 1024 
     | 
    
         
            +
             
     | 
| 
      
 1025 
     | 
    
         
            +
            You can call `.set_globals` on any `FmRest::Spyke::Base` model to set glabal
         
     | 
| 
      
 1026 
     | 
    
         
            +
            field values on the database that model is configured for.
         
     | 
| 
      
 1027 
     | 
    
         
            +
             
     | 
| 
      
 1028 
     | 
    
         
            +
            You can pass it either a hash of fully qualified field names
         
     | 
| 
      
 1029 
     | 
    
         
            +
            (table_name::field_name), or 1-level-deep nested hashes, with the outer being a
         
     | 
| 
      
 1030 
     | 
    
         
            +
            table name and the inner keys being the field names:
         
     | 
| 
      
 1031 
     | 
    
         
            +
             
     | 
| 
      
 1032 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1033 
     | 
    
         
            +
            Honeybee.set_globals(
         
     | 
| 
      
 1034 
     | 
    
         
            +
              "beeTable::myVar"      => "value",
         
     | 
| 
      
 1035 
     | 
    
         
            +
              "beeTable::myOtherVar" => "also a value"
         
     | 
| 
      
 1036 
     | 
    
         
            +
            )
         
     | 
| 
      
 1037 
     | 
    
         
            +
             
     | 
| 
      
 1038 
     | 
    
         
            +
            # Equivalent to the above example
         
     | 
| 
      
 1039 
     | 
    
         
            +
            Honeybee.set_globals(beeTable: { myVar: "value", myOtherVar: "also a value" })
         
     | 
| 
      
 1040 
     | 
    
         
            +
             
     | 
| 
      
 1041 
     | 
    
         
            +
            # Combined
         
     | 
| 
      
 1042 
     | 
    
         
            +
            Honeybee.set_globals(
         
     | 
| 
      
 1043 
     | 
    
         
            +
              "beeTable::myVar" => "value",
         
     | 
| 
      
 1044 
     | 
    
         
            +
              beeTable: { myOtherVar: "also a value" }
         
     | 
| 
      
 1045 
     | 
    
         
            +
            )
         
     | 
| 
      
 1046 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1047 
     | 
    
         
            +
             
     | 
| 
      
 1048 
     | 
    
         
            +
             
     | 
| 
       899 
1049 
     | 
    
         
             
            ## Logging
         
     | 
| 
       900 
1050 
     | 
    
         | 
| 
       901 
1051 
     | 
    
         
             
            If using fmrest-ruby + Spyke in a Rails app pretty log output will be set up
         
     | 
| 
         @@ -964,7 +1114,7 @@ FM Data API reference: https://fmhelp.filemaker.com/docs/18/en/dataapi/ 
     | 
|
| 
       964 
1114 
     | 
    
         
             
            | Get container data                  | Manual*                       | Yes                              |
         
     | 
| 
       965 
1115 
     | 
    
         
             
            | Upload container data               | Manual*                       | Yes                              |
         
     | 
| 
       966 
1116 
     | 
    
         
             
            | Perform a find request              | Manual*                       | Yes                              |
         
     | 
| 
       967 
     | 
    
         
            -
            | Set global field values             | Manual*                       |  
     | 
| 
      
 1117 
     | 
    
         
            +
            | Set global field values             | Manual*                       | Yes
         
     | 
| 
       968 
1118 
     | 
    
         
             
            | Run a script                        | Manual*                       | Yes                              |
         
     | 
| 
       969 
1119 
     | 
    
         
             
            | Run a script with another request   | Manual*                       | Yes                              |
         
     | 
| 
       970 
1120 
     | 
    
         | 
    
        data/fmrest.gemspec
    CHANGED
    
    | 
         @@ -23,8 +23,8 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       23 
23 
     | 
    
         
             
              spec.add_dependency 'faraday', '>= 0.9.0', '< 2.0'
         
     | 
| 
       24 
24 
     | 
    
         
             
              spec.add_dependency 'faraday_middleware', '>= 0.9.1', '< 2.0'
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
              spec.add_development_dependency "bundler" 
     | 
| 
       27 
     | 
    
         
            -
              spec.add_development_dependency "rake" 
     | 
| 
      
 26 
     | 
    
         
            +
              spec.add_development_dependency "bundler"
         
     | 
| 
      
 27 
     | 
    
         
            +
              spec.add_development_dependency "rake"
         
     | 
| 
       28 
28 
     | 
    
         
             
              spec.add_development_dependency "rspec", "~> 3.0"
         
     | 
| 
       29 
29 
     | 
    
         
             
              spec.add_development_dependency "spyke"
         
     | 
| 
       30 
30 
     | 
    
         
             
              spec.add_development_dependency "webmock"
         
     | 
    
        data/lib/fmrest.rb
    CHANGED
    
    | 
         @@ -4,16 +4,21 @@ require "faraday" 
     | 
|
| 
       4 
4 
     | 
    
         
             
            require "faraday_middleware"
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            require "fmrest/version"
         
     | 
| 
       7 
     | 
    
         
            -
            require "fmrest/ 
     | 
| 
      
 7 
     | 
    
         
            +
            require "fmrest/connection_settings"
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            module FmRest
         
     | 
| 
      
 10 
     | 
    
         
            +
              autoload :V1,         "fmrest/v1"
         
     | 
| 
      
 11 
     | 
    
         
            +
              autoload :TokenStore, "fmrest/token_store"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       10 
13 
     | 
    
         
             
              class << self
         
     | 
| 
       11 
14 
     | 
    
         
             
                attr_accessor :token_store
         
     | 
| 
       12 
15 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
      
 16 
     | 
    
         
            +
                def default_connection_settings=(settings)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @default_connection_settings = ConnectionSettings.wrap(settings, skip_validation: true)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
20 
     | 
    
         
             
                def default_connection_settings
         
     | 
| 
       16 
     | 
    
         
            -
                  @default_connection_settings || {}
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @default_connection_settings || ConnectionSettings.new({}, skip_validation: true)
         
     | 
| 
       17 
22 
     | 
    
         
             
                end
         
     | 
| 
       18 
23 
     | 
    
         | 
| 
       19 
24 
     | 
    
         
             
                def config=(connection_hash)
         
     | 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module FmRest
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Wrapper class for connection settings hash, with a number of purposes:
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # * Provide indifferent access (base hash can have either string or symbol
         
     | 
| 
      
 7 
     | 
    
         
            +
              #   keys)
         
     | 
| 
      
 8 
     | 
    
         
            +
              # * Method access
         
     | 
| 
      
 9 
     | 
    
         
            +
              # * Default values
         
     | 
| 
      
 10 
     | 
    
         
            +
              # * Basic validation
         
     | 
| 
      
 11 
     | 
    
         
            +
              # * Normalization (e.g. aliased settings)
         
     | 
| 
      
 12 
     | 
    
         
            +
              # * Useful error messages
         
     | 
| 
      
 13 
     | 
    
         
            +
              class ConnectionSettings
         
     | 
| 
      
 14 
     | 
    
         
            +
                class MissingSetting < ArgumentError; end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                PROPERTIES = %i(
         
     | 
| 
      
 17 
     | 
    
         
            +
                  host
         
     | 
| 
      
 18 
     | 
    
         
            +
                  database
         
     | 
| 
      
 19 
     | 
    
         
            +
                  username
         
     | 
| 
      
 20 
     | 
    
         
            +
                  password
         
     | 
| 
      
 21 
     | 
    
         
            +
                  token
         
     | 
| 
      
 22 
     | 
    
         
            +
                  token_store
         
     | 
| 
      
 23 
     | 
    
         
            +
                  autologin
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ssl
         
     | 
| 
      
 25 
     | 
    
         
            +
                  proxy
         
     | 
| 
      
 26 
     | 
    
         
            +
                  log
         
     | 
| 
      
 27 
     | 
    
         
            +
                  coerce_dates
         
     | 
| 
      
 28 
     | 
    
         
            +
                  date_format
         
     | 
| 
      
 29 
     | 
    
         
            +
                  timestamp_format
         
     | 
| 
      
 30 
     | 
    
         
            +
                  time_format
         
     | 
| 
      
 31 
     | 
    
         
            +
                  timezone
         
     | 
| 
      
 32 
     | 
    
         
            +
                ).freeze
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                # NOTE: password intentionally left non-required since it's only really
         
     | 
| 
      
 35 
     | 
    
         
            +
                # needed when no token exists, and should only be required when logging in
         
     | 
| 
      
 36 
     | 
    
         
            +
                REQUIRED = %i(
         
     | 
| 
      
 37 
     | 
    
         
            +
                  host
         
     | 
| 
      
 38 
     | 
    
         
            +
                  database
         
     | 
| 
      
 39 
     | 
    
         
            +
                ).freeze
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                DEFAULT_DATE_FORMAT = "MM/dd/yyyy"
         
     | 
| 
      
 42 
     | 
    
         
            +
                DEFAULT_TIME_FORMAT = "HH:mm:ss"
         
     | 
| 
      
 43 
     | 
    
         
            +
                DEFAULT_TIMESTAMP_FORMAT = "#{DEFAULT_DATE_FORMAT} #{DEFAULT_TIME_FORMAT}"
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                DEFAULTS = {
         
     | 
| 
      
 46 
     | 
    
         
            +
                  autologin:        true,
         
     | 
| 
      
 47 
     | 
    
         
            +
                  log:              false,
         
     | 
| 
      
 48 
     | 
    
         
            +
                  date_format:      DEFAULT_DATE_FORMAT,
         
     | 
| 
      
 49 
     | 
    
         
            +
                  time_format:      DEFAULT_TIME_FORMAT,
         
     | 
| 
      
 50 
     | 
    
         
            +
                  timestamp_format: DEFAULT_TIMESTAMP_FORMAT,
         
     | 
| 
      
 51 
     | 
    
         
            +
                  coerce_dates:     false
         
     | 
| 
      
 52 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def self.wrap(settings, skip_validation: false)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  if settings.kind_of?(self)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    settings.validate unless skip_validation
         
     | 
| 
      
 57 
     | 
    
         
            +
                    return settings
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
                  new(settings, skip_validation: skip_validation)
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def initialize(settings, skip_validation: false)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @settings = settings.to_h.dup
         
     | 
| 
      
 64 
     | 
    
         
            +
                  normalize
         
     | 
| 
      
 65 
     | 
    
         
            +
                  validate unless skip_validation
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                PROPERTIES.each do |p|
         
     | 
| 
      
 69 
     | 
    
         
            +
                  define_method(p) do
         
     | 
| 
      
 70 
     | 
    
         
            +
                    get(p)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  define_method("#{p}!") do
         
     | 
| 
      
 74 
     | 
    
         
            +
                    r = get(p)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    raise MissingSetting, "Missing required setting: `#{p}'" if r.nil?
         
     | 
| 
      
 76 
     | 
    
         
            +
                    r
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  define_method("#{p}?") do
         
     | 
| 
      
 80 
     | 
    
         
            +
                    !!get(p)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def [](key)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  raise ArgumentError, "Unknown setting `#{key}'" unless PROPERTIES.include?(key.to_sym)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  get(key)
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def to_h
         
     | 
| 
      
 90 
     | 
    
         
            +
                  PROPERTIES.each_with_object({}) do |p, h|
         
     | 
| 
      
 91 
     | 
    
         
            +
                    v = get(p)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    h[p] = v unless v == DEFAULTS[p]
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                def merge(other, **keyword_args)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  other = self.class.wrap(other, skip_validation: true)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  self.class.new(to_h.merge(other.to_h), **keyword_args)
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def validate
         
     | 
| 
      
 102 
     | 
    
         
            +
                  missing = REQUIRED.select { |r| get(r).nil? }.map { |m| "`#{m}'" }
         
     | 
| 
      
 103 
     | 
    
         
            +
                  raise MissingSetting, "Missing required setting(s): #{missing.join(', ')}" unless missing.empty?
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  unless username? || token?
         
     | 
| 
      
 106 
     | 
    
         
            +
                    raise MissingSetting, "A minimum of `username' or `token' are required to be able to establish a connection"
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                private
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def get(key)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  return @settings[key.to_sym] if @settings.has_key?(key.to_sym)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  return @settings[key.to_s] if @settings.has_key?(key.to_s)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  DEFAULTS[key.to_sym]
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def normalize
         
     | 
| 
      
 119 
     | 
    
         
            +
                  if !get(:username) && account_name = get(:account_name)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    @settings[:username] = account_name
         
     | 
| 
      
 121 
     | 
    
         
            +
                  end
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     |