shopify_api 14.0.0 → 14.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/.github/ISSUE_TEMPLATE/BUG_REPORT.md +23 -17
- data/.github/workflows/build.yml +2 -0
- data/.rubocop.yml +0 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +13 -1
- data/Gemfile.lock +24 -22
- data/dev.yml +1 -1
- data/docs/usage/custom_apps.md +3 -3
- data/docs/usage/graphql_storefront.md +13 -6
- data/docs/usage/rest.md +1 -1
- data/docs/usage/webhooks.md +2 -2
- data/lib/shopify_api/admin_versions.rb +2 -1
- data/lib/shopify_api/auth/session.rb +3 -3
- data/lib/shopify_api/clients/graphql/storefront.rb +26 -4
- data/lib/shopify_api/logger.rb +1 -1
- data/lib/shopify_api/rest/resources/2022_04/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2022_04/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2022_04/variant.rb +6 -6
- data/lib/shopify_api/rest/resources/2022_07/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2022_07/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2022_07/variant.rb +6 -6
- data/lib/shopify_api/rest/resources/2022_10/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2022_10/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2022_10/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2023_01/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2023_01/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2023_01/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2023_04/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2023_04/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2023_04/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2023_07/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2023_07/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2023_07/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2023_10/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2023_10/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2023_10/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2024_01/fulfillment_event.rb +5 -8
- data/lib/shopify_api/rest/resources/2024_01/inventory_item.rb +2 -2
- data/lib/shopify_api/rest/resources/2024_01/variant.rb +4 -4
- data/lib/shopify_api/rest/resources/2024_04/abandoned_checkout.rb +194 -0
- data/lib/shopify_api/rest/resources/2024_04/access_scope.rb +62 -0
- data/lib/shopify_api/rest/resources/2024_04/apple_pay_certificate.rb +109 -0
- data/lib/shopify_api/rest/resources/2024_04/application_charge.rb +113 -0
- data/lib/shopify_api/rest/resources/2024_04/application_credit.rb +95 -0
- data/lib/shopify_api/rest/resources/2024_04/article.rb +269 -0
- data/lib/shopify_api/rest/resources/2024_04/asset.rb +122 -0
- data/lib/shopify_api/rest/resources/2024_04/assigned_fulfillment_order.rb +92 -0
- data/lib/shopify_api/rest/resources/2024_04/balance.rb +58 -0
- data/lib/shopify_api/rest/resources/2024_04/blog.rb +166 -0
- data/lib/shopify_api/rest/resources/2024_04/cancellation_request.rb +87 -0
- data/lib/shopify_api/rest/resources/2024_04/carrier_service.rb +120 -0
- data/lib/shopify_api/rest/resources/2024_04/checkout.rb +213 -0
- data/lib/shopify_api/rest/resources/2024_04/collect.rb +146 -0
- data/lib/shopify_api/rest/resources/2024_04/collection.rb +114 -0
- data/lib/shopify_api/rest/resources/2024_04/collection_listing.rb +159 -0
- data/lib/shopify_api/rest/resources/2024_04/comment.rb +287 -0
- data/lib/shopify_api/rest/resources/2024_04/country.rb +141 -0
- data/lib/shopify_api/rest/resources/2024_04/currency.rb +61 -0
- data/lib/shopify_api/rest/resources/2024_04/custom_collection.rb +191 -0
- data/lib/shopify_api/rest/resources/2024_04/customer.rb +333 -0
- data/lib/shopify_api/rest/resources/2024_04/customer_address.rb +215 -0
- data/lib/shopify_api/rest/resources/2024_04/deprecated_api_call.rb +61 -0
- data/lib/shopify_api/rest/resources/2024_04/discount_code.rb +226 -0
- data/lib/shopify_api/rest/resources/2024_04/dispute.rb +115 -0
- data/lib/shopify_api/rest/resources/2024_04/dispute_evidence.rb +121 -0
- data/lib/shopify_api/rest/resources/2024_04/dispute_file_upload.rb +85 -0
- data/lib/shopify_api/rest/resources/2024_04/draft_order.rb +279 -0
- data/lib/shopify_api/rest/resources/2024_04/event.rb +152 -0
- data/lib/shopify_api/rest/resources/2024_04/fulfillment.rb +235 -0
- data/lib/shopify_api/rest/resources/2024_04/fulfillment_event.rb +167 -0
- data/lib/shopify_api/rest/resources/2024_04/fulfillment_order.rb +326 -0
- data/lib/shopify_api/rest/resources/2024_04/fulfillment_request.rb +101 -0
- data/lib/shopify_api/rest/resources/2024_04/fulfillment_service.rb +134 -0
- data/lib/shopify_api/rest/resources/2024_04/gift_card.rb +222 -0
- data/lib/shopify_api/rest/resources/2024_04/gift_card_adjustment.rb +122 -0
- data/lib/shopify_api/rest/resources/2024_04/image.rb +161 -0
- data/lib/shopify_api/rest/resources/2024_04/inventory_item.rb +112 -0
- data/lib/shopify_api/rest/resources/2024_04/inventory_level.rb +183 -0
- data/lib/shopify_api/rest/resources/2024_04/location.rb +171 -0
- data/lib/shopify_api/rest/resources/2024_04/locations_for_move.rb +60 -0
- data/lib/shopify_api/rest/resources/2024_04/marketing_event.rb +213 -0
- data/lib/shopify_api/rest/resources/2024_04/metafield.rb +348 -0
- data/lib/shopify_api/rest/resources/2024_04/mobile_platform_application.rb +114 -0
- data/lib/shopify_api/rest/resources/2024_04/order.rb +489 -0
- data/lib/shopify_api/rest/resources/2024_04/order_risk.rb +148 -0
- data/lib/shopify_api/rest/resources/2024_04/page.rb +198 -0
- data/lib/shopify_api/rest/resources/2024_04/payment.rb +144 -0
- data/lib/shopify_api/rest/resources/2024_04/payment_gateway.rb +147 -0
- data/lib/shopify_api/rest/resources/2024_04/payment_transaction.rb +114 -0
- data/lib/shopify_api/rest/resources/2024_04/payout.rb +101 -0
- data/lib/shopify_api/rest/resources/2024_04/policy.rb +73 -0
- data/lib/shopify_api/rest/resources/2024_04/price_rule.rb +227 -0
- data/lib/shopify_api/rest/resources/2024_04/product.rb +227 -0
- data/lib/shopify_api/rest/resources/2024_04/product_listing.rb +200 -0
- data/lib/shopify_api/rest/resources/2024_04/product_resource_feedback.rb +92 -0
- data/lib/shopify_api/rest/resources/2024_04/province.rb +136 -0
- data/lib/shopify_api/rest/resources/2024_04/recurring_application_charge.rb +176 -0
- data/lib/shopify_api/rest/resources/2024_04/redirect.rb +143 -0
- data/lib/shopify_api/rest/resources/2024_04/refund.rb +155 -0
- data/lib/shopify_api/rest/resources/2024_04/resource_feedback.rb +77 -0
- data/lib/shopify_api/rest/resources/2024_04/script_tag.rb +159 -0
- data/lib/shopify_api/rest/resources/2024_04/shipping_zone.rb +87 -0
- data/lib/shopify_api/rest/resources/2024_04/shop.rb +222 -0
- data/lib/shopify_api/rest/resources/2024_04/smart_collection.rb +220 -0
- data/lib/shopify_api/rest/resources/2024_04/storefront_access_token.rb +91 -0
- data/lib/shopify_api/rest/resources/2024_04/tender_transaction.rb +97 -0
- data/lib/shopify_api/rest/resources/2024_04/theme.rb +127 -0
- data/lib/shopify_api/rest/resources/2024_04/transaction.rb +188 -0
- data/lib/shopify_api/rest/resources/2024_04/usage_charge.rb +106 -0
- data/lib/shopify_api/rest/resources/2024_04/user.rb +142 -0
- data/lib/shopify_api/rest/resources/2024_04/variant.rb +212 -0
- data/lib/shopify_api/rest/resources/2024_04/webhook.rb +172 -0
- data/lib/shopify_api/version.rb +1 -1
- data/lib/shopify_api/webhooks/registry.rb +6 -6
- data/lib/shopify_api.rb +1 -0
- data/shopify_api.gemspec +0 -8
- metadata +77 -101
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a0006cbbd93fb9880c4677510cd1223b6119c18176f7fc821c56f46d9220eb2
|
|
4
|
+
data.tar.gz: 6b1bddbea59b7255f64659ec45cf2d9cd2e4fccf5aaf4a8804d526e37ff0a79c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 888a1c8560873f27a0d4c73460ab15145410392c6d35818b728e81a5e10813da1032e6638aed8445611dfc46fb2fa08e9afddaf2f54ca762c1a534725d571dc4
|
|
7
|
+
data.tar.gz: 79bbf5792589e217f4bae76db4d31a9d5b6c9b5e516c200037fd7d624f71a2f462015f264faf378244b73c661de76cbc2f751c278a0c4a60eb61c4b381c7c034
|
|
@@ -6,35 +6,41 @@ labels: "Type: Bug 🐛"
|
|
|
6
6
|
|
|
7
7
|
# Issue summary
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Before opening this issue, I have:
|
|
10
|
+
|
|
11
|
+
- [ ] Upgraded to the latest version of the package
|
|
12
|
+
- `shopify_api` version:
|
|
13
|
+
- Ruby version:
|
|
14
|
+
- Operating system:
|
|
15
|
+
- [ ] Set `log_level: :debug` [in my configuration](https://github.com/Shopify/shopify-api-ruby#setup-shopify-context), if applicable
|
|
16
|
+
- [ ] Found a reliable way to reproduce the problem that indicates it's a problem with the package
|
|
17
|
+
- [ ] Looked for similar issues in this repository
|
|
18
|
+
- [ ] Checked that this isn't an issue with a Shopify API
|
|
19
|
+
- If it is, please create a post in the [Shopify community forums](https://community.shopify.com/c/partners-and-developers/ct-p/appdev) or report it to [Shopify Partner Support](https://help.shopify.com/en/support/partners/org-select)
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Learn more: https://github.com/Shopify/shopify-api-ruby#setup-shopify-context
|
|
21
|
+
<!--
|
|
22
|
+
Write a short description of the issue here.
|
|
17
23
|
|
|
24
|
+
We can only fix issues for which there is a clear reproduction scenario.
|
|
25
|
+
The more context you can provide, the easier it becomes for us to investigate and fix the issue.
|
|
18
26
|
-->
|
|
19
27
|
|
|
20
|
-
- `shopify_api` version:
|
|
21
|
-
- Ruby version:
|
|
22
|
-
- Operating system:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
// Paste any relevant logs here
|
|
26
|
-
```
|
|
27
|
-
|
|
28
28
|
## Expected behavior
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
What do you think should happen?
|
|
31
31
|
|
|
32
32
|
## Actual behavior
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
What actually happens?
|
|
35
35
|
|
|
36
36
|
## Steps to reproduce the problem
|
|
37
37
|
|
|
38
38
|
1.
|
|
39
39
|
1.
|
|
40
40
|
1.
|
|
41
|
+
|
|
42
|
+
## Debug logs
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
// Paste any relevant logs here
|
|
46
|
+
```
|
data/.github/workflows/build.yml
CHANGED
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.0.6
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ Note: For changes to the API, see https://shopify.dev/changelog?filter=api
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
## 14.1.0
|
|
9
|
+
- [#1071](https://github.com/Shopify/shopify-api-ruby/issues/1071) Fix FulfillmentEvent class types
|
|
10
|
+
- Fix: InventoryItem class `harmonized_system_code` attribute type which can be either integer, string or nil
|
|
11
|
+
- Fix: Variant class `inventory_quantity` attribute type which can be either integer, string or nil
|
|
12
|
+
- [1293](https://github.com/Shopify/shopify-api-ruby/issues/1293) Add support for using Storefront private access tokens.
|
|
13
|
+
- [1302](https://github.com/Shopify/shopify-api-ruby/pull/1302) Deprecated passing the public Storefront access token as a positional parameter to the Storefront GraphQL client in favor of using the named parameter. (You probably want to use the private access token for this client anyway.)
|
|
14
|
+
- [1305](https://github.com/Shopify/shopify-api-ruby/pull/1305/) Adds support for the `2024-04` API version.
|
|
15
|
+
|
|
16
|
+
## 14.0.1
|
|
17
|
+
- [#1288](https://github.com/Shopify/shopify-api-ruby/pull/1288) Fix FeatureDeprecatedError being raised without a message.
|
|
18
|
+
- [1290](https://github.com/Shopify/shopify-api-ruby/pull/1290) Move deprecation of `ShopifyAPI::Webhooks::Handler#handle` to version 15.0.0
|
|
19
|
+
|
|
7
20
|
## 14.0.0
|
|
8
21
|
- [#1274](https://github.com/Shopify/shopify-api-ruby/pull/1274) ⚠️ [Breaking] Update sorbet and rbi dependencies. Remove support for ruby 2.7. Minimum required Ruby version is 3.0
|
|
9
22
|
- [#1282](https://github.com/Shopify/shopify-api-ruby/pull/1282) Fixes a bug where diffing attributes to update not take into account of Array changes and required ids.
|
data/Gemfile
CHANGED
|
@@ -4,10 +4,22 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
+
group :test, :development do
|
|
8
|
+
gem "pry-byebug"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
group :development do
|
|
12
|
+
gem "rake"
|
|
13
|
+
gem "rubocop"
|
|
14
|
+
gem "rubocop-shopify"
|
|
15
|
+
gem "rubocop-sorbet"
|
|
16
|
+
gem "sorbet"
|
|
17
|
+
gem "tapioca"
|
|
18
|
+
end
|
|
19
|
+
|
|
7
20
|
group :test do
|
|
8
21
|
gem "minitest"
|
|
9
22
|
gem "fakefs", require: false
|
|
10
23
|
gem "webmock"
|
|
11
24
|
gem "mocha"
|
|
12
|
-
gem "pry-byebug"
|
|
13
25
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
shopify_api (14.
|
|
4
|
+
shopify_api (14.1.0)
|
|
5
5
|
activesupport
|
|
6
6
|
concurrent-ruby
|
|
7
7
|
hash_diff
|
|
@@ -16,7 +16,7 @@ PATH
|
|
|
16
16
|
GEM
|
|
17
17
|
remote: https://rubygems.org/
|
|
18
18
|
specs:
|
|
19
|
-
activesupport (7.1.3)
|
|
19
|
+
activesupport (7.1.3.2)
|
|
20
20
|
base64
|
|
21
21
|
bigdecimal
|
|
22
22
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
@@ -30,15 +30,14 @@ GEM
|
|
|
30
30
|
public_suffix (>= 2.0.2, < 5.0)
|
|
31
31
|
ast (2.4.2)
|
|
32
32
|
base64 (0.2.0)
|
|
33
|
-
bigdecimal (3.1.
|
|
33
|
+
bigdecimal (3.1.7)
|
|
34
34
|
byebug (11.1.3)
|
|
35
35
|
coderay (1.1.3)
|
|
36
36
|
concurrent-ruby (1.2.3)
|
|
37
37
|
connection_pool (2.4.1)
|
|
38
38
|
crack (0.4.5)
|
|
39
39
|
rexml
|
|
40
|
-
drb (2.2.
|
|
41
|
-
ruby2_keywords
|
|
40
|
+
drb (2.2.1)
|
|
42
41
|
erubi (1.12.0)
|
|
43
42
|
fakefs (1.4.1)
|
|
44
43
|
hash_diff (1.1.1)
|
|
@@ -46,10 +45,12 @@ GEM
|
|
|
46
45
|
httparty (0.21.0)
|
|
47
46
|
mini_mime (>= 1.0.0)
|
|
48
47
|
multi_xml (>= 0.5.2)
|
|
49
|
-
i18n (1.14.
|
|
48
|
+
i18n (1.14.4)
|
|
50
49
|
concurrent-ruby (~> 1.0)
|
|
51
|
-
json (2.
|
|
52
|
-
jwt (2.
|
|
50
|
+
json (2.7.1)
|
|
51
|
+
jwt (2.8.1)
|
|
52
|
+
base64
|
|
53
|
+
language_server-protocol (3.17.0.3)
|
|
53
54
|
method_source (1.0.0)
|
|
54
55
|
mini_mime (1.1.5)
|
|
55
56
|
minitest (5.15.0)
|
|
@@ -79,26 +80,26 @@ GEM
|
|
|
79
80
|
rbi (0.1.8)
|
|
80
81
|
prism (>= 0.18.0, < 0.22)
|
|
81
82
|
sorbet-runtime (>= 0.5.9204)
|
|
82
|
-
regexp_parser (2.
|
|
83
|
-
rexml (3.2.
|
|
84
|
-
rubocop (1.
|
|
83
|
+
regexp_parser (2.9.0)
|
|
84
|
+
rexml (3.2.6)
|
|
85
|
+
rubocop (1.62.1)
|
|
85
86
|
json (~> 2.3)
|
|
87
|
+
language_server-protocol (>= 3.17.0)
|
|
86
88
|
parallel (~> 1.10)
|
|
87
|
-
parser (>= 3.
|
|
89
|
+
parser (>= 3.3.0.2)
|
|
88
90
|
rainbow (>= 2.2.2, < 4.0)
|
|
89
91
|
regexp_parser (>= 1.8, < 3.0)
|
|
90
92
|
rexml (>= 3.2.5, < 4.0)
|
|
91
|
-
rubocop-ast (>= 1.
|
|
93
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
|
92
94
|
ruby-progressbar (~> 1.7)
|
|
93
|
-
unicode-display_width (>=
|
|
94
|
-
rubocop-ast (1.
|
|
95
|
-
parser (>= 3.
|
|
95
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
96
|
+
rubocop-ast (1.31.2)
|
|
97
|
+
parser (>= 3.3.0.4)
|
|
96
98
|
rubocop-shopify (2.10.1)
|
|
97
99
|
rubocop (~> 1.35)
|
|
98
100
|
rubocop-sorbet (0.6.11)
|
|
99
101
|
rubocop (>= 0.90.0)
|
|
100
|
-
ruby-progressbar (1.
|
|
101
|
-
ruby2_keywords (0.0.5)
|
|
102
|
+
ruby-progressbar (1.13.0)
|
|
102
103
|
securerandom (0.3.1)
|
|
103
104
|
sorbet (0.5.11230)
|
|
104
105
|
sorbet-static (= 0.5.11230)
|
|
@@ -115,19 +116,19 @@ GEM
|
|
|
115
116
|
thor (>= 0.19.2)
|
|
116
117
|
syntax_tree (6.2.0)
|
|
117
118
|
prettier_print (>= 1.2.0)
|
|
118
|
-
tapioca (0.
|
|
119
|
+
tapioca (0.13.1)
|
|
119
120
|
bundler (>= 2.2.25)
|
|
120
121
|
netrc (>= 0.11.0)
|
|
121
122
|
parallel (>= 1.21.0)
|
|
122
123
|
rbi (>= 0.1.4, < 0.2)
|
|
123
|
-
sorbet-static-and-runtime (>= 0.5.
|
|
124
|
-
spoom (
|
|
124
|
+
sorbet-static-and-runtime (>= 0.5.11087)
|
|
125
|
+
spoom (>= 1.2.0)
|
|
125
126
|
thor (>= 1.2.0)
|
|
126
127
|
yard-sorbet
|
|
127
128
|
thor (1.3.0)
|
|
128
129
|
tzinfo (2.0.6)
|
|
129
130
|
concurrent-ruby (~> 1.0)
|
|
130
|
-
unicode-display_width (2.
|
|
131
|
+
unicode-display_width (2.5.0)
|
|
131
132
|
webmock (3.14.0)
|
|
132
133
|
addressable (>= 2.8.0)
|
|
133
134
|
crack (>= 0.3.2)
|
|
@@ -140,6 +141,7 @@ GEM
|
|
|
140
141
|
|
|
141
142
|
PLATFORMS
|
|
142
143
|
arm64-darwin-21
|
|
144
|
+
arm64-darwin-23
|
|
143
145
|
universal-darwin-22
|
|
144
146
|
x86_64-linux
|
|
145
147
|
|
data/dev.yml
CHANGED
data/docs/usage/custom_apps.md
CHANGED
|
@@ -10,7 +10,7 @@ You can follow instructions for [initializing the session object](#initializing-
|
|
|
10
10
|
Following is a basic example to construct a simple Session object. You can see full list of parameters for this object in the [class definition](https://github.com/Shopify/shopify-api-ruby/blob/main/lib/shopify_api/auth/session.rb)
|
|
11
11
|
```ruby
|
|
12
12
|
session = ShopifyAPI::Auth::Session.new(
|
|
13
|
-
shop: "#{your_shop_name}.myshopify.com"
|
|
13
|
+
shop: "#{your_shop_name}.myshopify.com",
|
|
14
14
|
access_token: "the_token_for_your_custom_app_found_in_admin"
|
|
15
15
|
)
|
|
16
16
|
|
|
@@ -23,7 +23,7 @@ Example:
|
|
|
23
23
|
def make_api_request(shop)
|
|
24
24
|
# 1. create session object
|
|
25
25
|
session = ShopifyAPI::Auth::Session.new(
|
|
26
|
-
shop: "#{your_shop_name}.myshopify.com"
|
|
26
|
+
shop: "#{your_shop_name}.myshopify.com",
|
|
27
27
|
access_token: "the_token_for_your_custom_app_found_in_admin"
|
|
28
28
|
)
|
|
29
29
|
|
|
@@ -49,7 +49,7 @@ Example:
|
|
|
49
49
|
def configure_app
|
|
50
50
|
# This method is called before making authenticated API calls
|
|
51
51
|
session = ShopifyAPI::Auth::Session.new(
|
|
52
|
-
shop: "#{your_shop_name}.myshopify.com"
|
|
52
|
+
shop: "#{your_shop_name}.myshopify.com",
|
|
53
53
|
access_token: "the_token_for_your_custom_app_found_in_admin"
|
|
54
54
|
)
|
|
55
55
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Make a Storefront API call
|
|
2
2
|
|
|
3
|
-
The library also allows you to send GraphQL requests to the [Shopify Storefront API](https://shopify.dev/docs/api/storefront). To do that, you can use `ShopifyAPI::Clients::Graphql::Storefront` with
|
|
3
|
+
The library also allows you to send GraphQL requests to the [Shopify Storefront API](https://shopify.dev/docs/api/storefront). To do that, you can use `ShopifyAPI::Clients::Graphql::Storefront` with either a [private or public Storefront access token](https://shopify.dev/docs/api/usage/authentication#access-tokens-for-the-storefront-api).
|
|
4
4
|
|
|
5
5
|
You can obtain Storefront API access tokens for both private apps and sales channels. Please read [our documentation](https://shopify.dev/docs/custom-storefronts/building-with-the-storefront-api/getting-started) to learn more about Storefront Access Tokens.
|
|
6
6
|
|
|
@@ -8,12 +8,14 @@ Below is an example of how you may query the Storefront API:
|
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
# Load the access token as per instructions above
|
|
11
|
-
|
|
11
|
+
storefront_private_access_token = ''
|
|
12
12
|
# your shop domain
|
|
13
13
|
shop_url = 'shop.myshopify.com'
|
|
14
14
|
|
|
15
|
-
# initialize the client with session and
|
|
16
|
-
client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url,
|
|
15
|
+
# initialize the client with session and a private Storefront access token
|
|
16
|
+
client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, private_token: storefront_private_access_token)
|
|
17
|
+
# or, alternatively with a public Storefront access token:
|
|
18
|
+
# client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url, public_token: storefront_public_access_token)
|
|
17
19
|
|
|
18
20
|
query = <<~QUERY
|
|
19
21
|
{
|
|
@@ -35,14 +37,19 @@ query = <<~QUERY
|
|
|
35
37
|
}
|
|
36
38
|
QUERY
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
# You may not need the "Shopify-Storefront-Buyer-IP" header, see its documentation:
|
|
41
|
+
# https://shopify.dev/docs/api/usage/authentication#making-server-side-requests
|
|
42
|
+
response = client.query(query: query, headers: { "Shopify-Storefront-Buyer-IP": request.ip })
|
|
39
43
|
# do something with the returned data
|
|
40
44
|
```
|
|
41
45
|
|
|
42
46
|
By default, the client uses the API version configured in `ShopifyAPI`. To use a different API version, set the optional `api_version` parameter. To experiment with prerelease API features, use `"unstable"` for the API version.
|
|
43
47
|
|
|
44
48
|
```ruby
|
|
45
|
-
client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url,
|
|
49
|
+
client = ShopifyAPI::Clients::Graphql::Storefront.new(shop_url,
|
|
50
|
+
private_token: storefront_private_access_token,
|
|
51
|
+
api_version: "unstable"
|
|
52
|
+
)
|
|
46
53
|
```
|
|
47
54
|
|
|
48
55
|
Want to make calls to the Admin API? Click [here](graphql.md)
|
data/docs/usage/rest.md
CHANGED
|
@@ -218,7 +218,7 @@ If the request is successful these methods will all return a [`ShopifyAPI::Clien
|
|
|
218
218
|
| Methods | Type | Notes |
|
|
219
219
|
|---------|------|-------|
|
|
220
220
|
| `code` |`Integer`| HTTP Response code, e.g. `200`|
|
|
221
|
-
| `
|
|
221
|
+
| `headers` |`Hash{String, [String]}` | HTTP Response headers |
|
|
222
222
|
| `body` | `Hash{String, Untyped}` | HTTP Response body |
|
|
223
223
|
| `prev_page_info` | `String` | See [Pagination](#pagination)|
|
|
224
224
|
| `next_page_info` | `String` | See [Pagination](#pagination)|
|
data/docs/usage/webhooks.md
CHANGED
|
@@ -18,10 +18,10 @@ If you want to register for an http webhook you need to implement a webhook hand
|
|
|
18
18
|
|
|
19
19
|
```ruby
|
|
20
20
|
module WebhookHandler
|
|
21
|
-
extend ShopifyAPI::Webhooks::
|
|
21
|
+
extend ShopifyAPI::Webhooks::WebhookHandler
|
|
22
22
|
|
|
23
23
|
class << self
|
|
24
|
-
def handle_webhook(data)
|
|
24
|
+
def handle_webhook(data:)
|
|
25
25
|
puts "Received webhook! topic: #{data.topic} shop: #{data.shop} body: #{data.body} webhook_id: #{data.webhook_id} api_version: #{data.api_version"
|
|
26
26
|
end
|
|
27
27
|
end
|
|
@@ -5,6 +5,7 @@ module ShopifyAPI
|
|
|
5
5
|
module AdminVersions
|
|
6
6
|
SUPPORTED_ADMIN_VERSIONS = T.let([
|
|
7
7
|
"unstable",
|
|
8
|
+
"2024-04",
|
|
8
9
|
"2024-01",
|
|
9
10
|
"2023-10",
|
|
10
11
|
"2023-07",
|
|
@@ -16,7 +17,7 @@ module ShopifyAPI
|
|
|
16
17
|
"2022-01",
|
|
17
18
|
], T::Array[String])
|
|
18
19
|
|
|
19
|
-
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2024-
|
|
20
|
+
LATEST_SUPPORTED_ADMIN_VERSION = T.let("2024-04", String)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
SUPPORTED_ADMIN_VERSIONS = ShopifyAPI::AdminVersions::SUPPORTED_ADMIN_VERSIONS
|
|
@@ -130,8 +130,8 @@ module ShopifyAPI
|
|
|
130
130
|
sig { params(other: T.nilable(Session)).returns(T::Boolean) }
|
|
131
131
|
def ==(other)
|
|
132
132
|
if other
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
|
|
134
|
+
id == other.id &&
|
|
135
135
|
shop == other.shop &&
|
|
136
136
|
state == other.state &&
|
|
137
137
|
scope == other.scope &&
|
|
@@ -140,7 +140,7 @@ module ShopifyAPI
|
|
|
140
140
|
online? == other.online? &&
|
|
141
141
|
associated_user == other.associated_user &&
|
|
142
142
|
shopify_session_id == other.shopify_session_id
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
else
|
|
145
145
|
false
|
|
146
146
|
end
|
|
@@ -5,8 +5,26 @@ module ShopifyAPI
|
|
|
5
5
|
module Clients
|
|
6
6
|
module Graphql
|
|
7
7
|
class Storefront < Client
|
|
8
|
-
sig
|
|
9
|
-
|
|
8
|
+
sig do
|
|
9
|
+
params(
|
|
10
|
+
shop: String,
|
|
11
|
+
storefront_access_token: T.nilable(String),
|
|
12
|
+
private_token: T.nilable(String),
|
|
13
|
+
public_token: T.nilable(String),
|
|
14
|
+
api_version: T.nilable(String),
|
|
15
|
+
).void
|
|
16
|
+
end
|
|
17
|
+
def initialize(shop, storefront_access_token = nil, private_token: nil, public_token: nil, api_version: nil)
|
|
18
|
+
unless storefront_access_token.nil?
|
|
19
|
+
warning = <<~WARNING
|
|
20
|
+
DEPRECATED: Use the named parameters for the Storefront token instead of passing
|
|
21
|
+
the public token as the second argument. Also, you may want to look into using
|
|
22
|
+
the Storefront private access token instead:
|
|
23
|
+
https://shopify.dev/docs/api/usage/authentication#getting-started-with-private-access
|
|
24
|
+
WARNING
|
|
25
|
+
ShopifyAPI::Logger.deprecated(warning, "15.0.0")
|
|
26
|
+
end
|
|
27
|
+
|
|
10
28
|
session = Auth::Session.new(
|
|
11
29
|
id: shop,
|
|
12
30
|
shop: shop,
|
|
@@ -14,7 +32,11 @@ module ShopifyAPI
|
|
|
14
32
|
is_online: false,
|
|
15
33
|
)
|
|
16
34
|
super(session: session, base_path: "/api", api_version: api_version)
|
|
17
|
-
@storefront_access_token = storefront_access_token
|
|
35
|
+
@storefront_access_token = T.let(T.must(private_token || public_token || storefront_access_token), String)
|
|
36
|
+
@storefront_auth_header = T.let(
|
|
37
|
+
private_token.nil? ? "X-Shopify-Storefront-Access-Token" : "Shopify-Storefront-Private-Token",
|
|
38
|
+
String,
|
|
39
|
+
)
|
|
18
40
|
end
|
|
19
41
|
|
|
20
42
|
sig do
|
|
@@ -26,7 +48,7 @@ module ShopifyAPI
|
|
|
26
48
|
).returns(HttpResponse)
|
|
27
49
|
end
|
|
28
50
|
def query(query:, variables: nil, headers: {}, tries: 1)
|
|
29
|
-
T.must(headers).merge!({
|
|
51
|
+
T.must(headers).merge!({ @storefront_auth_header => @storefront_access_token })
|
|
30
52
|
super(query: query, variables: variables, headers: headers, tries: tries)
|
|
31
53
|
end
|
|
32
54
|
end
|
data/lib/shopify_api/logger.rb
CHANGED
|
@@ -33,7 +33,7 @@ module ShopifyAPI
|
|
|
33
33
|
def deprecated(message, version)
|
|
34
34
|
return unless enabled_for_log_level?(:warn)
|
|
35
35
|
|
|
36
|
-
raise Errors::FeatureDeprecatedError unless valid_version(version)
|
|
36
|
+
raise Errors::FeatureDeprecatedError, message unless valid_version(version)
|
|
37
37
|
|
|
38
38
|
send_to_logger(:warn, message)
|
|
39
39
|
end
|
|
@@ -20,7 +20,7 @@ module ShopifyAPI
|
|
|
20
20
|
|
|
21
21
|
@address1 = T.let(nil, T.nilable(String))
|
|
22
22
|
@city = T.let(nil, T.nilable(String))
|
|
23
|
-
@country = T.let(nil, T.nilable(
|
|
23
|
+
@country = T.let(nil, T.nilable(String))
|
|
24
24
|
@created_at = T.let(nil, T.nilable(String))
|
|
25
25
|
@estimated_delivery_at = T.let(nil, T.nilable(String))
|
|
26
26
|
@fulfillment_id = T.let(nil, T.nilable(Integer))
|
|
@@ -30,7 +30,7 @@ module ShopifyAPI
|
|
|
30
30
|
@longitude = T.let(nil, T.nilable(Float))
|
|
31
31
|
@message = T.let(nil, T.nilable(String))
|
|
32
32
|
@order_id = T.let(nil, T.nilable(Integer))
|
|
33
|
-
@province = T.let(nil, T.nilable(
|
|
33
|
+
@province = T.let(nil, T.nilable(String))
|
|
34
34
|
@shop_id = T.let(nil, T.nilable(Integer))
|
|
35
35
|
@status = T.let(nil, T.nilable(String))
|
|
36
36
|
@updated_at = T.let(nil, T.nilable(String))
|
|
@@ -39,10 +39,7 @@ module ShopifyAPI
|
|
|
39
39
|
super(session: session, from_hash: from_hash)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
@has_one = T.let({
|
|
43
|
-
country: Country,
|
|
44
|
-
province: Province
|
|
45
|
-
}, T::Hash[Symbol, Class])
|
|
42
|
+
@has_one = T.let({}, T::Hash[Symbol, Class])
|
|
46
43
|
@has_many = T.let({}, T::Hash[Symbol, Class])
|
|
47
44
|
@paths = T.let([
|
|
48
45
|
{http_method: :delete, operation: :delete, ids: [:order_id, :fulfillment_id, :id], path: "orders/<order_id>/fulfillments/<fulfillment_id>/events/<id>.json"},
|
|
@@ -55,7 +52,7 @@ module ShopifyAPI
|
|
|
55
52
|
attr_reader :address1
|
|
56
53
|
sig { returns(T.nilable(String)) }
|
|
57
54
|
attr_reader :city
|
|
58
|
-
sig { returns(T.nilable(
|
|
55
|
+
sig { returns(T.nilable(String)) }
|
|
59
56
|
attr_reader :country
|
|
60
57
|
sig { returns(T.nilable(String)) }
|
|
61
58
|
attr_reader :created_at
|
|
@@ -75,7 +72,7 @@ module ShopifyAPI
|
|
|
75
72
|
attr_reader :message
|
|
76
73
|
sig { returns(T.nilable(Integer)) }
|
|
77
74
|
attr_reader :order_id
|
|
78
|
-
sig { returns(T.nilable(
|
|
75
|
+
sig { returns(T.nilable(String)) }
|
|
79
76
|
attr_reader :province
|
|
80
77
|
sig { returns(T.nilable(Integer)) }
|
|
81
78
|
attr_reader :shop_id
|
|
@@ -22,7 +22,7 @@ module ShopifyAPI
|
|
|
22
22
|
@country_code_of_origin = T.let(nil, T.nilable(String))
|
|
23
23
|
@country_harmonized_system_codes = T.let(nil, T.nilable(T::Array[T.untyped]))
|
|
24
24
|
@created_at = T.let(nil, T.nilable(String))
|
|
25
|
-
@harmonized_system_code = T.let(nil, T.nilable(Integer))
|
|
25
|
+
@harmonized_system_code = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
26
26
|
@id = T.let(nil, T.nilable(Integer))
|
|
27
27
|
@province_code_of_origin = T.let(nil, T.nilable(String))
|
|
28
28
|
@requires_shipping = T.let(nil, T.nilable(T::Boolean))
|
|
@@ -49,7 +49,7 @@ module ShopifyAPI
|
|
|
49
49
|
attr_reader :country_harmonized_system_codes
|
|
50
50
|
sig { returns(T.nilable(String)) }
|
|
51
51
|
attr_reader :created_at
|
|
52
|
-
sig { returns(T.nilable(Integer)) }
|
|
52
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
53
53
|
attr_reader :harmonized_system_code
|
|
54
54
|
sig { returns(T.nilable(Integer)) }
|
|
55
55
|
attr_reader :id
|
|
@@ -28,9 +28,9 @@ module ShopifyAPI
|
|
|
28
28
|
@inventory_item_id = T.let(nil, T.nilable(Integer))
|
|
29
29
|
@inventory_management = T.let(nil, T.nilable(String))
|
|
30
30
|
@inventory_policy = T.let(nil, T.nilable(String))
|
|
31
|
-
@inventory_quantity = T.let(nil, T.nilable(Integer))
|
|
32
|
-
@inventory_quantity_adjustment = T.let(nil, T.nilable(Integer))
|
|
33
|
-
@old_inventory_quantity = T.let(nil, T.nilable(Integer))
|
|
31
|
+
@inventory_quantity = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
32
|
+
@inventory_quantity_adjustment = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
33
|
+
@old_inventory_quantity = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
34
34
|
@option = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
|
35
35
|
@position = T.let(nil, T.nilable(Integer))
|
|
36
36
|
@presentment_prices = T.let(nil, T.nilable(T::Array[T.untyped]))
|
|
@@ -83,11 +83,11 @@ module ShopifyAPI
|
|
|
83
83
|
attr_reader :inventory_management
|
|
84
84
|
sig { returns(T.nilable(String)) }
|
|
85
85
|
attr_reader :inventory_policy
|
|
86
|
-
sig { returns(T.nilable(Integer)) }
|
|
86
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
87
87
|
attr_reader :inventory_quantity
|
|
88
|
-
sig { returns(T.nilable(Integer)) }
|
|
88
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
89
89
|
attr_reader :inventory_quantity_adjustment
|
|
90
|
-
sig { returns(T.nilable(Integer)) }
|
|
90
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
91
91
|
attr_reader :old_inventory_quantity
|
|
92
92
|
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
|
93
93
|
attr_reader :option
|
|
@@ -20,7 +20,7 @@ module ShopifyAPI
|
|
|
20
20
|
|
|
21
21
|
@address1 = T.let(nil, T.nilable(String))
|
|
22
22
|
@city = T.let(nil, T.nilable(String))
|
|
23
|
-
@country = T.let(nil, T.nilable(
|
|
23
|
+
@country = T.let(nil, T.nilable(String))
|
|
24
24
|
@created_at = T.let(nil, T.nilable(String))
|
|
25
25
|
@estimated_delivery_at = T.let(nil, T.nilable(String))
|
|
26
26
|
@fulfillment_id = T.let(nil, T.nilable(Integer))
|
|
@@ -30,7 +30,7 @@ module ShopifyAPI
|
|
|
30
30
|
@longitude = T.let(nil, T.nilable(Float))
|
|
31
31
|
@message = T.let(nil, T.nilable(String))
|
|
32
32
|
@order_id = T.let(nil, T.nilable(Integer))
|
|
33
|
-
@province = T.let(nil, T.nilable(
|
|
33
|
+
@province = T.let(nil, T.nilable(String))
|
|
34
34
|
@shop_id = T.let(nil, T.nilable(Integer))
|
|
35
35
|
@status = T.let(nil, T.nilable(String))
|
|
36
36
|
@updated_at = T.let(nil, T.nilable(String))
|
|
@@ -39,10 +39,7 @@ module ShopifyAPI
|
|
|
39
39
|
super(session: session, from_hash: from_hash)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
@has_one = T.let({
|
|
43
|
-
country: Country,
|
|
44
|
-
province: Province
|
|
45
|
-
}, T::Hash[Symbol, Class])
|
|
42
|
+
@has_one = T.let({}, T::Hash[Symbol, Class])
|
|
46
43
|
@has_many = T.let({}, T::Hash[Symbol, Class])
|
|
47
44
|
@paths = T.let([
|
|
48
45
|
{http_method: :delete, operation: :delete, ids: [:order_id, :fulfillment_id, :id], path: "orders/<order_id>/fulfillments/<fulfillment_id>/events/<id>.json"},
|
|
@@ -55,7 +52,7 @@ module ShopifyAPI
|
|
|
55
52
|
attr_reader :address1
|
|
56
53
|
sig { returns(T.nilable(String)) }
|
|
57
54
|
attr_reader :city
|
|
58
|
-
sig { returns(T.nilable(
|
|
55
|
+
sig { returns(T.nilable(String)) }
|
|
59
56
|
attr_reader :country
|
|
60
57
|
sig { returns(T.nilable(String)) }
|
|
61
58
|
attr_reader :created_at
|
|
@@ -75,7 +72,7 @@ module ShopifyAPI
|
|
|
75
72
|
attr_reader :message
|
|
76
73
|
sig { returns(T.nilable(Integer)) }
|
|
77
74
|
attr_reader :order_id
|
|
78
|
-
sig { returns(T.nilable(
|
|
75
|
+
sig { returns(T.nilable(String)) }
|
|
79
76
|
attr_reader :province
|
|
80
77
|
sig { returns(T.nilable(Integer)) }
|
|
81
78
|
attr_reader :shop_id
|
|
@@ -22,7 +22,7 @@ module ShopifyAPI
|
|
|
22
22
|
@country_code_of_origin = T.let(nil, T.nilable(String))
|
|
23
23
|
@country_harmonized_system_codes = T.let(nil, T.nilable(T::Array[T.untyped]))
|
|
24
24
|
@created_at = T.let(nil, T.nilable(String))
|
|
25
|
-
@harmonized_system_code = T.let(nil, T.nilable(Integer))
|
|
25
|
+
@harmonized_system_code = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
26
26
|
@id = T.let(nil, T.nilable(Integer))
|
|
27
27
|
@province_code_of_origin = T.let(nil, T.nilable(String))
|
|
28
28
|
@requires_shipping = T.let(nil, T.nilable(T::Boolean))
|
|
@@ -49,7 +49,7 @@ module ShopifyAPI
|
|
|
49
49
|
attr_reader :country_harmonized_system_codes
|
|
50
50
|
sig { returns(T.nilable(String)) }
|
|
51
51
|
attr_reader :created_at
|
|
52
|
-
sig { returns(T.nilable(Integer)) }
|
|
52
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
53
53
|
attr_reader :harmonized_system_code
|
|
54
54
|
sig { returns(T.nilable(Integer)) }
|
|
55
55
|
attr_reader :id
|
|
@@ -28,9 +28,9 @@ module ShopifyAPI
|
|
|
28
28
|
@inventory_item_id = T.let(nil, T.nilable(Integer))
|
|
29
29
|
@inventory_management = T.let(nil, T.nilable(String))
|
|
30
30
|
@inventory_policy = T.let(nil, T.nilable(String))
|
|
31
|
-
@inventory_quantity = T.let(nil, T.nilable(Integer))
|
|
32
|
-
@inventory_quantity_adjustment = T.let(nil, T.nilable(Integer))
|
|
33
|
-
@old_inventory_quantity = T.let(nil, T.nilable(Integer))
|
|
31
|
+
@inventory_quantity = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
32
|
+
@inventory_quantity_adjustment = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
33
|
+
@old_inventory_quantity = T.let(nil, T.nilable(T.any(Integer, String)))
|
|
34
34
|
@option = T.let(nil, T.nilable(T::Hash[T.untyped, T.untyped]))
|
|
35
35
|
@position = T.let(nil, T.nilable(Integer))
|
|
36
36
|
@presentment_prices = T.let(nil, T.nilable(T::Array[T.untyped]))
|
|
@@ -83,11 +83,11 @@ module ShopifyAPI
|
|
|
83
83
|
attr_reader :inventory_management
|
|
84
84
|
sig { returns(T.nilable(String)) }
|
|
85
85
|
attr_reader :inventory_policy
|
|
86
|
-
sig { returns(T.nilable(Integer)) }
|
|
86
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
87
87
|
attr_reader :inventory_quantity
|
|
88
|
-
sig { returns(T.nilable(Integer)) }
|
|
88
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
89
89
|
attr_reader :inventory_quantity_adjustment
|
|
90
|
-
sig { returns(T.nilable(Integer)) }
|
|
90
|
+
sig { returns(T.nilable(T.any(Integer, String))) }
|
|
91
91
|
attr_reader :old_inventory_quantity
|
|
92
92
|
sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
|
|
93
93
|
attr_reader :option
|