lhs 21.2.4 → 22.1.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/README.md +264 -116
- data/lhs.gemspec +1 -1
- data/lib/lhs.rb +8 -0
- data/lib/lhs/concerns/o_auth.rb +25 -0
- data/lib/lhs/concerns/record/chainable.rb +4 -4
- data/lib/lhs/concerns/record/configuration.rb +28 -11
- data/lib/lhs/concerns/record/request.rb +26 -7
- data/lib/lhs/concerns/record/update.rb +17 -0
- data/lib/lhs/config.rb +1 -1
- data/lib/lhs/interceptors/auto_oauth/interceptor.rb +33 -0
- data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
- data/lib/lhs/record.rb +6 -3
- data/lib/lhs/version.rb +1 -1
- data/spec/auto_oauth_spec.rb +169 -0
- data/spec/dummy/app/controllers/application_controller.rb +15 -0
- data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
- data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
- data/spec/dummy/app/models/providers/internal_services.rb +7 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/item/destroy_spec.rb +1 -1
- data/spec/proxy/record_identification_spec.rb +1 -1
- data/spec/record/all_spec.rb +1 -1
- data/spec/record/endpoints_spec.rb +1 -1
- data/spec/record/error_handling_integration_spec.rb +1 -1
- data/spec/record/handle_includes_errors_spec.rb +1 -1
- data/spec/record/has_many_spec.rb +1 -1
- data/spec/record/has_one_spec.rb +1 -1
- data/spec/record/includes_first_page_spec.rb +727 -0
- data/spec/record/includes_spec.rb +545 -579
- data/spec/record/includes_warning_spec.rb +1 -1
- data/spec/record/mapping_spec.rb +2 -2
- data/spec/record/references_spec.rb +1 -1
- data/spec/record/relation_caching_spec.rb +3 -3
- data/spec/record/update_spec.rb +62 -0
- data/spec/request_cycle_cache_spec.rb +3 -3
- metadata +30 -8
- data/spec/record/includes_all_spec.rb +0 -693
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b42b1f0f10d4d0dcf31de3e65ed4ca0c3f7bcfded40e9897231b5ff09a9c472
|
4
|
+
data.tar.gz: 1c041ff80320d79d73ef02e9af30278d58e779c239044c3485eac9b5e047f8ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a33c68f7d0c82a6c54482fa3ba3986bac1dccc83d9284951fa0f95bf92c0cd17fe3283c62c1c9dee067769d0fe81daf5ca3c86b8c6f84c07ccc0c3977b108cc
|
7
|
+
data.tar.gz: fe26fa077139d670284ef136e6f702549bd1fd8e8d2c3bfd5f5d43dcd1dba3bf3ac72f7dca8ae70628184c85ff83908558353e0d835fa00052d21ab0539b88d8
|
data/README.md
CHANGED
@@ -39,113 +39,118 @@ record.review # "Lunch was great
|
|
39
39
|
|
40
40
|
## Table of contents
|
41
41
|
* [LHS](#lhs)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
42
|
+
* [Quickstart](#quickstart)
|
43
|
+
* [Installation/Startup checklist](#installationstartup-checklist)
|
44
|
+
* [Record](#record)
|
45
|
+
* [Endpoints](#endpoints)
|
46
|
+
* [Configure endpoint hosts](#configure-endpoint-hosts)
|
47
|
+
* [Endpoint Priorities](#endpoint-priorities)
|
48
|
+
* [Provider](#provider)
|
49
|
+
* [Record inheritance](#record-inheritance)
|
50
|
+
* [Find multiple records](#find-multiple-records)
|
51
|
+
* [fetch](#fetch)
|
52
|
+
* [where](#where)
|
53
|
+
* [Reuse/Dry where statements: Use scopes](#reusedry-where-statements-use-scopes)
|
54
|
+
* [all](#all)
|
55
|
+
* [all with unpaginated endpoints](#all-with-unpaginated-endpoints)
|
56
|
+
* [Retrieve the amount of a collection of items: count vs. length](#retrieve-the-amount-of-a-collection-of-items-count-vs-length)
|
57
|
+
* [Find single records](#find-single-records)
|
58
|
+
* [find](#find)
|
59
|
+
* [find_by](#find_by)
|
60
|
+
* [first](#first)
|
61
|
+
* [last](#last)
|
62
|
+
* [Work with retrieved data](#work-with-retrieved-data)
|
63
|
+
* [Automatic detection/conversion of collections](#automatic-detectionconversion-of-collections)
|
64
|
+
* [Map complex data for easy access](#map-complex-data-for-easy-access)
|
65
|
+
* [Access and identify nested records](#access-and-identify-nested-records)
|
66
|
+
* [Relations / Associations](#relations--associations)
|
67
|
+
* [has_many](#has_many)
|
68
|
+
* [has_one](#has_one)
|
69
|
+
* [Unwrap nested items from the response body](#unwrap-nested-items-from-the-response-body)
|
70
|
+
* [Determine collections from the response body](#determine-collections-from-the-response-body)
|
71
|
+
* [Load additional data based on retrieved data](#load-additional-data-based-on-retrieved-data)
|
72
|
+
* [Chain complex queries](#chain-complex-queries)
|
73
|
+
* [Chain where queries](#chain-where-queries)
|
74
|
+
* [Expand plain collections of links: expanded](#expand-plain-collections-of-links-expanded)
|
75
|
+
* [Error handling with chains](#error-handling-with-chains)
|
76
|
+
* [Resolve chains: fetch](#resolve-chains-fetch)
|
77
|
+
* [Add request options to a query chain: options](#add-request-options-to-a-query-chain-options)
|
78
|
+
* [Control pagination within a query chain](#control-pagination-within-a-query-chain)
|
79
|
+
* [Record pagination](#record-pagination)
|
80
|
+
* [Pagination strategy](#pagination-strategy)
|
81
|
+
* [Pagination strategy: offset (default)](#pagination-strategy-offset-default)
|
82
|
+
* [Pagination strategy: page](#pagination-strategy-page)
|
83
|
+
* [Pagination strategy: start](#pagination-strategy-start)
|
84
|
+
* [Pagination strategy: link](#pagination-strategy-link)
|
85
|
+
* [Pagination keys](#pagination-keys)
|
86
|
+
* [limit_key](#limit_key)
|
87
|
+
* [pagination_key](#pagination_key)
|
88
|
+
* [total_key](#total_key)
|
89
|
+
* [Pagination links](#pagination-links)
|
90
|
+
* [next?](#next)
|
91
|
+
* [previous?](#previous)
|
92
|
+
* [Kaminari support (limited)](#kaminari-support-limited)
|
93
|
+
* [Build, create and update records](#build-create-and-update-records)
|
94
|
+
* [Create new records](#create-new-records)
|
95
|
+
* [create](#create)
|
96
|
+
* [Unwrap nested data when creation response nests created record data](#unwrap-nested-data-when-creation-response-nests-created-record-data)
|
97
|
+
* [Create records through associations: Nested sub resources](#create-records-through-associations-nested-sub-resources)
|
98
|
+
* [Start building new records](#start-building-new-records)
|
99
|
+
* [Change/Update existing records](#changeupdate-existing-records)
|
100
|
+
* [save](#save)
|
101
|
+
* [update](#update)
|
102
|
+
* [partial_update](#partial_update)
|
103
|
+
* [Endpoint url parameter injection during record creation/change](#endpoint-url-parameter-injection-during-record-creationchange)
|
104
|
+
* [Record validation](#record-validation)
|
105
|
+
* [Configure record validations](#configure-record-validations)
|
106
|
+
* [HTTP Status Codes for validation errors](#http-status-codes-for-validation-errors)
|
107
|
+
* [Reset validation errors](#reset-validation-errors)
|
108
|
+
* [Add validation errors](#add-validation-errors)
|
109
|
+
* [Validation errors for nested data](#validation-errors-for-nested-data)
|
110
|
+
* [Translation of validation errors](#translation-of-validation-errors)
|
111
|
+
* [Validation error types: errors vs. warnings](#validation-error-types-errors-vs-warnings)
|
112
|
+
* [Persistance failed: errors](#persistance-failed-errors)
|
113
|
+
* [Persistance succeeded: warnings](#persistance-succeeded-warnings)
|
114
|
+
* [Using ActiveModel::Validations none the less](#using-activemodelvalidations-none-the-less)
|
115
|
+
* [Use form_helper to create and update records](#use-form_helper-to-create-and-update-records)
|
116
|
+
* [Destroy records](#destroy-records)
|
117
|
+
* [Record getters and setters](#record-getters-and-setters)
|
118
|
+
* [Record setters](#record-setters)
|
119
|
+
* [Record getters](#record-getters)
|
120
|
+
* [Include linked resources (hyperlinks and hypermedia)](#include-linked-resources-hyperlinks-and-hypermedia)
|
121
|
+
* [Generate links from parameters](#generate-links-from-parameters)
|
122
|
+
* [Ensure the whole linked collection is included: includes_all](#ensure-the-whole-linked-collection-is-included-includes_all)
|
123
|
+
* [Include the first linked page or single item is included: include](#include-the-first-linked-page-or-single-item-is-included-include)
|
124
|
+
* [Include various levels of linked data](#include-various-levels-of-linked-data)
|
125
|
+
* [Identify and cast known records when including records](#identify-and-cast-known-records-when-including-records)
|
126
|
+
* [Apply options for requests performed to fetch included records](#apply-options-for-requests-performed-to-fetch-included-records)
|
127
|
+
* [Record batch processing](#record-batch-processing)
|
128
|
+
* [all](#all-1)
|
129
|
+
* [Using all, when endpoint does not implement response pagination meta data](#using-all-when-endpoint-does-not-implement-response-pagination-meta-data)
|
130
|
+
* [find_each](#find_each)
|
131
|
+
* [find_in_batches](#find_in_batches)
|
132
|
+
* [Convert/Cast specific record types: becomes](#convertcast-specific-record-types-becomes)
|
133
|
+
* [Assign attributes](#assign-attributes)
|
134
|
+
* [Request Cycle Cache](#request-cycle-cache)
|
135
|
+
* [Change store for LHS' request cycle cache](#change-store-for-lhs-request-cycle-cache)
|
136
|
+
* [Disable request cycle cache](#disable-request-cycle-cache)
|
137
|
+
* [Automatic Authentication (OAuth)](#automatic-authentication-oauth)
|
138
|
+
* [Configure multiple auth providers (even per endpoint)](#configure-multiple-auth-providers-even-per-endpoint)
|
139
|
+
* [Configure providers](#configure-providers)
|
140
|
+
* [Option Blocks](#option-blocks)
|
141
|
+
* [Request tracing](#request-tracing)
|
142
|
+
* [Extended Rollbar Logging](#extended-rollbar-logging)
|
143
|
+
* [Testing with LHS](#testing-with-lhs)
|
144
|
+
* [Test helper](#test-helper)
|
145
|
+
* [Stub](#stub)
|
146
|
+
* [stub_all](#stub_all)
|
147
|
+
* [Test query chains](#test-query-chains)
|
148
|
+
* [By explicitly resolving the chain: fetch](#by-explicitly-resolving-the-chain-fetch)
|
149
|
+
* [Without resolving the chain: where_values_hash](#without-resolving-the-chain-where_values_hash)
|
150
|
+
* [License](#license)
|
151
|
+
|
152
|
+
|
153
|
+
|
149
154
|
|
150
155
|
## Installation/Startup checklist
|
151
156
|
|
@@ -1602,6 +1607,21 @@ POST https://service.example.com/records/1z-5r1fkaj { body: "{ 'name': 'Starbuck
|
|
1602
1607
|
|
1603
1608
|
##### update
|
1604
1609
|
|
1610
|
+
###### Directly via Record
|
1611
|
+
|
1612
|
+
```ruby
|
1613
|
+
# app/controllers/some_controller.rb
|
1614
|
+
|
1615
|
+
Record.update(id: '1z-5r1fkaj', name: 'Steve')
|
1616
|
+
|
1617
|
+
```
|
1618
|
+
```
|
1619
|
+
GET https://service.example.com/records/1z-5r1fkaj
|
1620
|
+
{ name: 'Steve' }
|
1621
|
+
```
|
1622
|
+
|
1623
|
+
###### per Instance
|
1624
|
+
|
1605
1625
|
`update` persists the whole object after new parameters are applied through arguments.
|
1606
1626
|
|
1607
1627
|
`update` will return false if persisting fails. `update!` instead will raise an exception.
|
@@ -2088,7 +2108,7 @@ In a service-oriented architecture using [hyperlinks](https://en.wikipedia.org/w
|
|
2088
2108
|
|
2089
2109
|
When fetching records with LHS, you can specify in advance all the linked resources that you want to include in the results.
|
2090
2110
|
|
2091
|
-
With `includes`
|
2111
|
+
With `includes` LHS ensures that all matching and explicitly linked resources are loaded and merged (even if the linked resources are paginated).
|
2092
2112
|
|
2093
2113
|
Including linked resources/records is heavily influenced by [https://guides.rubyonrails.org/active_record_querying.html](https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations) and you should read it to understand this feature in all it's glory.
|
2094
2114
|
|
@@ -2107,16 +2127,16 @@ Presence.create(place: { href: Place.href_for(123) })
|
|
2107
2127
|
POST '/presences' { place: { href: "http://datastore/places/123" } }
|
2108
2128
|
```
|
2109
2129
|
|
2110
|
-
#### Ensure the whole linked collection is included
|
2130
|
+
#### Ensure the whole linked collection is included with includes
|
2111
2131
|
|
2112
|
-
In case endpoints are paginated and you are certain that you'll need all objects of a set and not only the first page/batch, use `
|
2132
|
+
In case endpoints are paginated and you are certain that you'll need all objects of a set and not only the first page/batch, use `includes`.
|
2113
2133
|
|
2114
2134
|
LHS will ensure that all linked resources are around by loading all pages (parallelized/performance optimized).
|
2115
2135
|
|
2116
2136
|
```ruby
|
2117
2137
|
# app/controllers/some_controller.rb
|
2118
2138
|
|
2119
|
-
customer = Customer.
|
2139
|
+
customer = Customer.includes(contracts: :products).find(1)
|
2120
2140
|
```
|
2121
2141
|
```
|
2122
2142
|
> GET https://service.example.com/customers/1
|
@@ -2143,14 +2163,14 @@ customer.contracts.first.products.first.name # Local Business Card
|
|
2143
2163
|
|
2144
2164
|
```
|
2145
2165
|
|
2146
|
-
#### Include the first linked page
|
2166
|
+
#### Include only the first linked page of a linked collection: includes_first_page
|
2147
2167
|
|
2148
|
-
`
|
2168
|
+
`includes_first_page` includes the first page/response when loading the linked resource. **If the endpoint is paginated, only the first page will be included.**
|
2149
2169
|
|
2150
2170
|
```ruby
|
2151
2171
|
# app/controllers/some_controller.rb
|
2152
2172
|
|
2153
|
-
customer = Customer.
|
2173
|
+
customer = Customer.includes_first_page(contracts: :products).find(1)
|
2154
2174
|
```
|
2155
2175
|
```
|
2156
2176
|
> GET https://service.example.com/customers/1
|
@@ -2174,7 +2194,7 @@ customer.contracts.first.products.first.name # Local Business Card
|
|
2174
2194
|
|
2175
2195
|
#### Include various levels of linked data
|
2176
2196
|
|
2177
|
-
The method syntax of `includes`
|
2197
|
+
The method syntax of `includes` allows you include hyperlinks stored in deep nested data strutures:
|
2178
2198
|
|
2179
2199
|
Some examples:
|
2180
2200
|
|
@@ -2194,7 +2214,7 @@ Record.includes(campaign: [:entry, :user])
|
|
2194
2214
|
|
2195
2215
|
#### Identify and cast known records when including records
|
2196
2216
|
|
2197
|
-
When including linked resources with `includes
|
2217
|
+
When including linked resources with `includes`, already defined records and their endpoints and configurations are used to make the requests to fetch the additional data.
|
2198
2218
|
|
2199
2219
|
That also means that options for endpoints of linked resources are applied when requesting those in addition.
|
2200
2220
|
|
@@ -2444,6 +2464,134 @@ LHS.configure do |config|
|
|
2444
2464
|
end
|
2445
2465
|
```
|
2446
2466
|
|
2467
|
+
## Automatic Authentication (OAuth)
|
2468
|
+
|
2469
|
+
LHS provides a way to have records automatically fetch and use OAuth authentication when performing requests within Rails.
|
2470
|
+
|
2471
|
+
In order to enable automatic oauth authentication, perform the following steps:
|
2472
|
+
|
2473
|
+
1. Make sure LHS is configured to perform `auto_oauth`. Provide a block that, when executed in the controller context, returns a valid access_token/bearer_token.
|
2474
|
+
```ruby
|
2475
|
+
# config/initializers/lhs.rb
|
2476
|
+
|
2477
|
+
LHS.configure do |config|
|
2478
|
+
config.auto_oauth = -> { access_token }
|
2479
|
+
end
|
2480
|
+
```
|
2481
|
+
|
2482
|
+
2. Opt-in records requiring oauth authentication:
|
2483
|
+
|
2484
|
+
```ruby
|
2485
|
+
# app/models/record.rb
|
2486
|
+
|
2487
|
+
class Record < LHS::Record
|
2488
|
+
oauth
|
2489
|
+
# ...
|
2490
|
+
end
|
2491
|
+
```
|
2492
|
+
|
2493
|
+
3. Include the `LHS::OAuth` context into your application controller:
|
2494
|
+
|
2495
|
+
```ruby
|
2496
|
+
# app/controllers/application_controller.rb
|
2497
|
+
|
2498
|
+
class ApplicationController < ActionController::Base
|
2499
|
+
include LHS::OAuth
|
2500
|
+
|
2501
|
+
# ...
|
2502
|
+
end
|
2503
|
+
```
|
2504
|
+
|
2505
|
+
4. Make sure you have the `LHC::Auth` interceptor enabled:
|
2506
|
+
|
2507
|
+
```ruby
|
2508
|
+
# config/initializers/lhc.rb
|
2509
|
+
|
2510
|
+
LHC.configure do |config|
|
2511
|
+
config.interceptors = [LHC::Auth]
|
2512
|
+
end
|
2513
|
+
```
|
2514
|
+
|
2515
|
+
Now you can perform requests based on the record that will be auto authenticated from now on:
|
2516
|
+
|
2517
|
+
```ruby
|
2518
|
+
# app/controllers/some_controller.rb
|
2519
|
+
|
2520
|
+
Record.find(1)
|
2521
|
+
```
|
2522
|
+
```
|
2523
|
+
https://records/1
|
2524
|
+
Authentication: 'Bearer token-12345'
|
2525
|
+
```
|
2526
|
+
|
2527
|
+
### Configure multiple auth providers (even per endpoint)
|
2528
|
+
|
2529
|
+
In case you need to configure multiple auth provider access_tokens within your application,
|
2530
|
+
make sure you provide a proc returning a hash when configuring `auto_oauth`,
|
2531
|
+
naming every single provider and the responsive method to retrieve the access_tokens in the controller context:
|
2532
|
+
|
2533
|
+
```ruby
|
2534
|
+
# config/initializers/lhs.rb
|
2535
|
+
LHS.configure do |config|
|
2536
|
+
config.auto_oauth = proc do
|
2537
|
+
{
|
2538
|
+
provider1: access_token_provider_1,
|
2539
|
+
provider2: access_token_provider_2
|
2540
|
+
}
|
2541
|
+
end
|
2542
|
+
end
|
2543
|
+
```
|
2544
|
+
|
2545
|
+
Then make sure you either define which provider to use on a record level:
|
2546
|
+
|
2547
|
+
```ruby
|
2548
|
+
# model/record.rb
|
2549
|
+
class Record < LHS::Record
|
2550
|
+
oauth(:provider1)
|
2551
|
+
#...
|
2552
|
+
end
|
2553
|
+
```
|
2554
|
+
|
2555
|
+
or on an endpoint level:
|
2556
|
+
|
2557
|
+
```ruby
|
2558
|
+
# model/record.rb
|
2559
|
+
class Record < LHS::Record
|
2560
|
+
endpoint 'https://service/records', oauth: :provider1
|
2561
|
+
#...
|
2562
|
+
end
|
2563
|
+
```
|
2564
|
+
|
2565
|
+
### Configure providers
|
2566
|
+
|
2567
|
+
If you're using LHS service providers, you can also configure auto auth on a provider level:
|
2568
|
+
|
2569
|
+
```ruby
|
2570
|
+
# app/models/providers/localsearch.rb
|
2571
|
+
module Providers
|
2572
|
+
class Localsearch < LHS::Record
|
2573
|
+
|
2574
|
+
provider(
|
2575
|
+
oauth: true
|
2576
|
+
)
|
2577
|
+
end
|
2578
|
+
end
|
2579
|
+
```
|
2580
|
+
|
2581
|
+
or with multiple auth providers:
|
2582
|
+
|
2583
|
+
```ruby
|
2584
|
+
# app/models/providers/localsearch.rb
|
2585
|
+
module Providers
|
2586
|
+
class Localsearch < LHS::Record
|
2587
|
+
|
2588
|
+
provider(
|
2589
|
+
oauth: :provider_1
|
2590
|
+
)
|
2591
|
+
end
|
2592
|
+
end
|
2593
|
+
```
|
2594
|
+
|
2447
2595
|
## Option Blocks
|
2448
2596
|
|
2449
2597
|
In order to apply options to all requests performed in a give block, LHS provides option blocks.
|