jekyll-spree-client 0.1.8 → 0.1.13
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 +73 -18
- data/lib/jekyll-spree-client.rb +14 -11
- data/lib/jekyll/spree_client.rb +50 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3313a5acca299870b21648a253999a7bda4fae1c50a8ada64ba08bf710ee677
|
4
|
+
data.tar.gz: 15ce97b96ab22eb484960cea96a98549bc6ba972ba82599442f67d5ed6c32925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 061be8dc0a119fd8b106e08dd5edc7506aa2d297fd213012f1442744349bcb8d0e955c1887597154510368e9c866e59e597d484f33ede18cf74e76caeb28c27e
|
7
|
+
data.tar.gz: 2a6e0c0e080ba05a8d94d7b383d445fe7e7aa96b113f053ece6b9528d9e80ad1bfc886f41cad37e97c5bfbb533ab44d4e4099600ef7e6501cc3d4a6ea05d4db3
|
data/README.md
CHANGED
@@ -16,16 +16,6 @@ conversion unless you store prices as strings.
|
|
16
16
|
|
17
17
|
You can use Integer and Floats now!
|
18
18
|
|
19
|
-
### Update
|
20
|
-
|
21
|
-
The issue is that Spree expects to receive prices in the locale format
|
22
|
-
(ie, "200,00") but will return them on Ruby's `to_s` conversion
|
23
|
-
("200.0") so we need to reconvert them to our current format and juggle
|
24
|
-
between them.
|
25
|
-
|
26
|
-
We'll deal with `Integer` for now until we find [a better
|
27
|
-
solution](https://github.com/spree/spree/issues/10556).
|
28
|
-
|
29
19
|
## Installation
|
30
20
|
|
31
21
|
Add this line to your site's Gemfile:
|
@@ -77,8 +67,10 @@ Add the required fields in their frontmatter:
|
|
77
67
|
---
|
78
68
|
layout: post
|
79
69
|
title: A product
|
70
|
+
description: With a description
|
80
71
|
sku: A unique ID
|
81
72
|
price: 100.00
|
73
|
+
cost_price: 50.0
|
82
74
|
stock: 100
|
83
75
|
weight: 100
|
84
76
|
height: 100
|
@@ -90,18 +82,64 @@ depth: 100
|
|
90
82
|
The layout doesn't matter, but we recommend to use a layout for
|
91
83
|
products.
|
92
84
|
|
85
|
+
### Layouts
|
86
|
+
|
87
|
+
You'll need four layouts and at least a corresponding post, one for each
|
88
|
+
step of the buying process.
|
89
|
+
|
90
|
+
* `cart` the cart, shows the order details
|
91
|
+
* `shipment` shipping and billing addresses, shipping rates apply here
|
92
|
+
* `payment` select payment methods
|
93
|
+
* `confirmation` order confirmation page
|
94
|
+
|
95
|
+
They'll be available on Liquid under the `site.STEP` keys.
|
96
|
+
|
97
|
+
### Hooks
|
98
|
+
|
99
|
+
If you need to do some data manipulation before and after
|
100
|
+
synchronization is done, you can use hooks. In a `_plugins/spree.rb`
|
101
|
+
file:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
Jekyll::Hooks.register :spree, :pre_render do |spree|
|
105
|
+
spree.local_products.each do |product|
|
106
|
+
product.data['_description'] = product.data['description']
|
107
|
+
product.data['description'] = product.data['productore']&.data&.dig('title')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
Jekyll::Hooks.register :spree, :post_render do |spree|
|
112
|
+
spree.local_products.each do |product|
|
113
|
+
product.data['description'] = product.data.delete '_description'
|
114
|
+
|
115
|
+
product.data.delete 'description' unless product.data['description']
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
This plugin gets the description value from another document (they're
|
121
|
+
linked using
|
122
|
+
[jekyll-linked-posts](https://0xacab.org/sutty/jekyll/jekyll-linked-posts/),
|
123
|
+
it's a real example!), and then recovers it.
|
124
|
+
|
125
|
+
* `pre_render` runs just before starting synchronization
|
126
|
+
* `post_render` run after synchronization and before writing changes
|
127
|
+
|
93
128
|
### Building the site
|
94
129
|
|
95
130
|
During site build, the plugin will communicate with the Spree API and
|
96
|
-
create products when they don't exist or update their price,
|
97
|
-
stock and the other values.
|
131
|
+
create products when they don't exist or locally update their price,
|
132
|
+
current stock and the other values.
|
98
133
|
|
99
134
|
Some fields will appear on the frontmatter:
|
100
135
|
|
101
136
|
```yaml
|
102
137
|
variant_id: 1
|
103
138
|
in_stock: true
|
104
|
-
|
139
|
+
errors:
|
140
|
+
time: 2020-10-10 00:00:00 UTC
|
141
|
+
messages:
|
142
|
+
- Something went wrong
|
105
143
|
```
|
106
144
|
|
107
145
|
* `variant_id` is the ID in the Spree database.
|
@@ -109,11 +147,27 @@ sync_error: 2020-10-10 00:00:00 UTC
|
|
109
147
|
* `in_stock` is true or false according to availability (Spree decides
|
110
148
|
this).
|
111
149
|
|
112
|
-
* `
|
113
|
-
here.
|
150
|
+
* `errors` if the synchronization fails there will be a timestamp and
|
151
|
+
some messages here.
|
114
152
|
|
115
153
|
## Notes
|
116
154
|
|
155
|
+
* Synchronization follows this rules.
|
156
|
+
|
157
|
+
If the product doesn't exist on Spree, it's created.
|
158
|
+
|
159
|
+
If it does, the Jekyll document is updated with price, weight,
|
160
|
+
dimensions and stock.
|
161
|
+
|
162
|
+
It means once the product is created on Spree, Spree manages this
|
163
|
+
variables.
|
164
|
+
|
165
|
+
But Product properties like description and metadata are updated from
|
166
|
+
Jekyll to Spree.
|
167
|
+
|
168
|
+
We want this to change in the near future but we need to think a way
|
169
|
+
to prevent conflicts.
|
170
|
+
|
117
171
|
* All products are created as "master variants" on Spree. There will be
|
118
172
|
no pictures, taxonomies or other values, only basic product
|
119
173
|
information.
|
@@ -126,11 +180,12 @@ sync_error: 2020-10-10 00:00:00 UTC
|
|
126
180
|
* Stock is added to the default stock location, meaning the first active
|
127
181
|
one. The API doesn't show or allows to query the actual default.
|
128
182
|
|
129
|
-
*
|
183
|
+
* `price` and `cost_price` are expressed in Spree's default currency.
|
130
184
|
|
131
185
|
* If you install the `jekyll-write-and-commit-changes` plugin it will
|
132
|
-
save the data back to disk when you build
|
133
|
-
environment variable
|
186
|
+
save the data back to disk when you build. Using the
|
187
|
+
`JEKYLL_ENV=production` environment variable will also commit changes
|
188
|
+
to git.
|
134
189
|
|
135
190
|
* Check the shipping category ID on Spree, since there's no API for it,
|
136
191
|
you need to provide it manually, default is `1`.
|
data/lib/jekyll-spree-client.rb
CHANGED
@@ -6,7 +6,20 @@ require_relative 'jekyll/spree_client'
|
|
6
6
|
# matter. The SKU needs to be unique, but this plugin doesn't ensure
|
7
7
|
# that. Duplicated SKUs will be silently ignored for now.
|
8
8
|
Jekyll::Hooks.register :site, :post_read do |site|
|
9
|
-
|
9
|
+
# Ordered articles
|
10
|
+
posts = site.site_payload['site']['posts']
|
11
|
+
|
12
|
+
# Make steps available to other articles
|
13
|
+
%w[cart shipment payment confirmation].each do |step|
|
14
|
+
site.config[step] = posts.find do |doc|
|
15
|
+
doc.data['layout'] == step
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if ENV['SPREE_API_KEY'].nil? || ENV['SPREE_API_KEY'].empty?
|
20
|
+
Jekyll.logger.warn "SPREE_API_KEY environment variable missing, skipping syncrhonization"
|
21
|
+
next
|
22
|
+
end
|
10
23
|
|
11
24
|
config = site.config['spree']&.transform_keys(&:to_sym) || {}
|
12
25
|
config.merge!(site: site, api_key: ENV['SPREE_API_KEY'], spree_url: ENV['SPREE_URL'])
|
@@ -18,14 +31,4 @@ Jekyll::Hooks.register :site, :post_read do |site|
|
|
18
31
|
if ENV['JEKYLL_ENV'] == 'production' && site.respond_to?(:repository)
|
19
32
|
site.repository.commit site.data.dig(site.config['locale'], 'spree', 'commit') || 'Spree synchronization'
|
20
33
|
end
|
21
|
-
|
22
|
-
# Ordered articles
|
23
|
-
posts = site.site_payload['site']['posts']
|
24
|
-
|
25
|
-
# Make steps available to other articles
|
26
|
-
%w[cart shipment payment confirmation].each do |step|
|
27
|
-
site.config[step] = posts.find do |doc|
|
28
|
-
doc.data['layout'] == step
|
29
|
-
end
|
30
|
-
end
|
31
34
|
end
|
data/lib/jekyll/spree_client.rb
CHANGED
@@ -33,7 +33,21 @@ module Jekyll
|
|
33
33
|
#
|
34
34
|
# @return [Array]
|
35
35
|
def variant_fields
|
36
|
-
@variant_fields ||= %w[price weight height width depth].freeze
|
36
|
+
@variant_fields ||= %w[price weight height width depth cost_price pay_what_you_can].freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
# Fields that can change locally and we need to sync to Spree
|
40
|
+
#
|
41
|
+
# @return [Array]
|
42
|
+
def product_fields
|
43
|
+
@product_fields ||= %w[name description meta_description meta_keywords meta_title].freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
# All fields, using during creation
|
47
|
+
#
|
48
|
+
# @return [Array]
|
49
|
+
def create_fields
|
50
|
+
@create_fields ||= product_fields + variant_fields
|
37
51
|
end
|
38
52
|
|
39
53
|
# Spree Client
|
@@ -56,6 +70,8 @@ module Jekyll
|
|
56
70
|
end
|
57
71
|
|
58
72
|
def sync!
|
73
|
+
Jekyll::Hooks.trigger :spree, :pre_render, self
|
74
|
+
|
59
75
|
# The API allows to query several SKUs at the same time, so we send
|
60
76
|
# groups of 10 products.
|
61
77
|
local_products.each_slice(10) do |products|
|
@@ -68,13 +84,18 @@ module Jekyll
|
|
68
84
|
products.each do |p|
|
69
85
|
mark_with_error p,
|
70
86
|
i18n.dig('spree', 'errors', 'api') || "Couldn't obtain variants, the store may be down or API key is incorrect"
|
71
|
-
local_save p.save
|
72
87
|
end
|
73
88
|
|
74
89
|
next
|
75
90
|
end
|
76
91
|
|
77
92
|
products.each do |product|
|
93
|
+
# Products have names, not titles, so we save them for later
|
94
|
+
product.data['_name'] = product.data['name'] if product.data.key? 'name'
|
95
|
+
product.data['name'] = product.data['title']
|
96
|
+
|
97
|
+
# Remove previous errors
|
98
|
+
product.data.delete 'errors'
|
78
99
|
# Find the corresponding Spree variant in the response
|
79
100
|
variant = response['variants'].find do |v|
|
80
101
|
v[sku_field] == product.data[sku_field]
|
@@ -86,8 +107,16 @@ module Jekyll
|
|
86
107
|
else
|
87
108
|
create product
|
88
109
|
end
|
110
|
+
|
111
|
+
product.data['name'] = product.data.delete('_name') if product.data.key? '_name'
|
89
112
|
end
|
90
113
|
end
|
114
|
+
|
115
|
+
Jekyll::Hooks.trigger :spree, :post_render, self
|
116
|
+
|
117
|
+
local_products.each do |product|
|
118
|
+
local_save product
|
119
|
+
end
|
91
120
|
end
|
92
121
|
|
93
122
|
private
|
@@ -150,6 +179,22 @@ module Jekyll
|
|
150
179
|
# @param [Hash]
|
151
180
|
# @return [true,false]
|
152
181
|
def update(product, variant)
|
182
|
+
# Sync local changes to Spree
|
183
|
+
remote_product = product.data.slice(*product_fields).transform_keys(&:to_sym)
|
184
|
+
# Only when they haven't changed, so we don't waste time sending a
|
185
|
+
# request.
|
186
|
+
unless remote_product == variant.slice(*product_fields).transform_keys(&:to_sym)
|
187
|
+
# XXX: We don't have the product ID with the variant but we have
|
188
|
+
# the slug.
|
189
|
+
remote_product[:id] = variant['slug']
|
190
|
+
remote_product[:sku] = product.data[sku_field]
|
191
|
+
|
192
|
+
unless remote_products.update **remote_product
|
193
|
+
Jekyll.logger.error "Couldn't update #{product.data['title']}"
|
194
|
+
mark_with_error product, error_messages
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
153
198
|
# XXX: The API replies with stringified numbers so we convert them
|
154
199
|
# back to the type we're using locally.
|
155
200
|
variant.slice(*variant_fields).each do |k, v|
|
@@ -162,9 +207,8 @@ module Jekyll
|
|
162
207
|
|
163
208
|
product.data['in_stock'] = variant['in_stock']
|
164
209
|
product.data['variant_id'] = variant['id']
|
165
|
-
product.data.delete 'errors'
|
166
210
|
|
167
|
-
|
211
|
+
product
|
168
212
|
end
|
169
213
|
|
170
214
|
# If the product doesn't exist, create it.
|
@@ -172,23 +216,18 @@ module Jekyll
|
|
172
216
|
# @param [Jekyll::Document]
|
173
217
|
# @return [true,false]
|
174
218
|
def create(product)
|
175
|
-
new_product = product.data.slice(*
|
219
|
+
new_product = product.data.slice(*create_fields).transform_keys(&:to_sym)
|
176
220
|
new_product[:sku] = product.data[sku_field]
|
177
|
-
new_product[:name] = product.data['title']
|
178
221
|
|
179
222
|
unless remote_products.create **new_product
|
180
223
|
Jekyll.logger.error "Couldn't create #{product.data['title']}"
|
181
224
|
mark_with_error product, error_messages
|
182
|
-
local_save product
|
183
225
|
|
184
226
|
return false
|
185
227
|
end
|
186
228
|
|
187
229
|
product.data['variant_id'] = remote_products.response.dig('master', 'id')
|
188
230
|
|
189
|
-
# Save the variant ID
|
190
|
-
local_save product
|
191
|
-
|
192
231
|
# Add the initial stock
|
193
232
|
spree.stock_items(id: product.data['variant_id'], stock_location_id: stock_location['id'])
|
194
233
|
.stock_movements.create(quantity: product.data['stock'])
|
@@ -202,7 +241,7 @@ module Jekyll
|
|
202
241
|
errors.map do |error|
|
203
242
|
field + ' ' + error
|
204
243
|
end
|
205
|
-
end
|
244
|
+
end&.flatten
|
206
245
|
end
|
207
246
|
end
|
208
247
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-spree-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- f
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: spree-api-client
|