wordpress_client 0.0.1

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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +1065 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +11 -0
  7. data/Guardfile +29 -0
  8. data/LICENSE +21 -0
  9. data/README.md +63 -0
  10. data/Rakefile +1 -0
  11. data/circle.yml +3 -0
  12. data/lib/wordpress_client.rb +25 -0
  13. data/lib/wordpress_client/category.rb +4 -0
  14. data/lib/wordpress_client/client.rb +142 -0
  15. data/lib/wordpress_client/connection.rb +186 -0
  16. data/lib/wordpress_client/errors.rb +10 -0
  17. data/lib/wordpress_client/media.rb +37 -0
  18. data/lib/wordpress_client/media_parser.rb +48 -0
  19. data/lib/wordpress_client/paginated_collection.rb +53 -0
  20. data/lib/wordpress_client/post.rb +62 -0
  21. data/lib/wordpress_client/post_parser.rb +113 -0
  22. data/lib/wordpress_client/replace_metadata.rb +81 -0
  23. data/lib/wordpress_client/replace_terms.rb +62 -0
  24. data/lib/wordpress_client/rest_parser.rb +17 -0
  25. data/lib/wordpress_client/tag.rb +4 -0
  26. data/lib/wordpress_client/term.rb +34 -0
  27. data/lib/wordpress_client/version.rb +3 -0
  28. data/spec/category_spec.rb +8 -0
  29. data/spec/client_spec.rb +411 -0
  30. data/spec/connection_spec.rb +270 -0
  31. data/spec/docker/Dockerfile +40 -0
  32. data/spec/docker/README.md +37 -0
  33. data/spec/docker/dbdump.sql.gz +0 -0
  34. data/spec/docker/htaccess +10 -0
  35. data/spec/docker/restore-dbdump.sh +13 -0
  36. data/spec/fixtures/category.json +1 -0
  37. data/spec/fixtures/image-media.json +1 -0
  38. data/spec/fixtures/invalid-post-id.json +1 -0
  39. data/spec/fixtures/post-with-forbidden-metadata.json +1 -0
  40. data/spec/fixtures/post-with-metadata.json +1 -0
  41. data/spec/fixtures/simple-post.json +1 -0
  42. data/spec/fixtures/tag.json +1 -0
  43. data/spec/fixtures/thoughtful.jpg +0 -0
  44. data/spec/fixtures/validation-error.json +1 -0
  45. data/spec/integration/attachments_crud_spec.rb +51 -0
  46. data/spec/integration/categories_spec.rb +60 -0
  47. data/spec/integration/category_assignment_spec.rb +29 -0
  48. data/spec/integration/posts_crud_spec.rb +118 -0
  49. data/spec/integration/posts_finding_spec.rb +86 -0
  50. data/spec/integration/posts_metadata_spec.rb +27 -0
  51. data/spec/integration/posts_with_attachments_spec.rb +21 -0
  52. data/spec/integration/tag_assignment_spec.rb +29 -0
  53. data/spec/integration/tags_spec.rb +36 -0
  54. data/spec/media_spec.rb +63 -0
  55. data/spec/paginated_collection_spec.rb +64 -0
  56. data/spec/post_spec.rb +114 -0
  57. data/spec/replace_metadata_spec.rb +56 -0
  58. data/spec/replace_terms_spec.rb +51 -0
  59. data/spec/shared_examples/term_examples.rb +37 -0
  60. data/spec/spec_helper.rb +28 -0
  61. data/spec/support/docker_runner.rb +49 -0
  62. data/spec/support/fixtures.rb +19 -0
  63. data/spec/support/integration_macros.rb +10 -0
  64. data/spec/support/wordpress_server.rb +103 -0
  65. data/spec/tag_spec.rb +8 -0
  66. data/wordpress_client.gemspec +27 -0
  67. metadata +219 -0
@@ -0,0 +1,17 @@
1
+ module WordpressClient
2
+ module RestParser
3
+ private
4
+ def rendered(name)
5
+ (data[name] || {})["rendered"]
6
+ end
7
+
8
+ def read_date(name)
9
+ # Try to read UTC time first
10
+ if (gmt_time = data["#{name}_gmt"])
11
+ Time.iso8601("#{gmt_time}Z")
12
+ elsif (local_time = data[name])
13
+ Time.iso8601(local_time)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ module WordpressClient
2
+ class Tag < Term
3
+ end
4
+ end
@@ -0,0 +1,34 @@
1
+ module WordpressClient
2
+ class Term
3
+ attr_reader :id, :name_html, :slug
4
+
5
+ def self.parse(data)
6
+ new(
7
+ id: data.fetch("id"),
8
+ name_html: data.fetch("name"),
9
+ slug: data.fetch("slug"),
10
+ )
11
+ end
12
+
13
+ def initialize(id:, name_html:, slug:)
14
+ @id = id
15
+ @name_html = name_html
16
+ @slug = slug
17
+ end
18
+
19
+ def ==(other)
20
+ if other.is_a? Term
21
+ other.class == self.class &&
22
+ other.id == id &&
23
+ other.name_html == name_html &&
24
+ other.slug == slug
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def inspect
31
+ "#<#{self.class} ##{id} #{name_html.inspect} (#{slug})>"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module WordpressClient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ require "spec_helper"
2
+ require "shared_examples/term_examples"
3
+
4
+ module WordpressClient
5
+ describe Category do
6
+ it_behaves_like Term, fixture_name: "category.json"
7
+ end
8
+ end
@@ -0,0 +1,411 @@
1
+ require "spec_helper"
2
+ require "tmpdir"
3
+
4
+ module WordpressClient
5
+ describe Client do
6
+ subject(:client) { Client.new(connection) }
7
+ let(:connection) { instance_double(Connection) }
8
+
9
+ describe "finding posts" do
10
+ it "has working pagination" do
11
+ expect(connection).to receive(:get_multiple).with(
12
+ Post, "posts", hash_including(page: 2, per_page: 13)
13
+ ).and_return []
14
+
15
+ expect(client.posts(per_page: 13, page: 2)).to eq []
16
+ end
17
+
18
+ it "embeds linked resources" do
19
+ expect(connection).to receive(:get_multiple).with(
20
+ Post, "posts", hash_including(_embed: nil)
21
+ ).and_return []
22
+
23
+ expect(client.posts).to eq []
24
+ end
25
+
26
+ it "can filter on category slugs" do
27
+ expect(connection).to receive(:get_multiple).with(
28
+ Post, "posts", hash_including(filter: {category_name: "my-cat"})
29
+ ).and_return []
30
+
31
+ expect(client.posts(category_slug: "my-cat")).to eq []
32
+ end
33
+
34
+ it "can filter on tag slugs" do
35
+ expect(connection).to receive(:get_multiple).with(
36
+ Post, "posts", hash_including(filter: {tag: "my-cat"})
37
+ ).and_return []
38
+
39
+ expect(client.posts(tag_slug: "my-cat")).to eq []
40
+ end
41
+
42
+ it "can filter on tag and category slugs" do
43
+ expect(connection).to receive(:get_multiple).with(
44
+ Post, "posts", hash_including(filter: {tag: "my-cat", category_name: "my-dog"})
45
+ ).and_return []
46
+
47
+ expect(client.posts(tag_slug: "my-cat", category_slug: "my-dog")).to eq []
48
+ end
49
+ end
50
+
51
+ describe "fetching a single post" do
52
+ it "embeds linked resources" do
53
+ post = instance_double(Post)
54
+
55
+ expect(connection).to receive(:get).with(
56
+ Post, "posts/5", _embed: nil, context: "edit"
57
+ ).and_return post
58
+
59
+ expect(client.find_post(5)).to eq post
60
+ end
61
+
62
+ it "can find using a slug" do
63
+ post = instance_double(Post)
64
+
65
+ expect(connection).to receive(:get_multiple).with(
66
+ Post, "posts", hash_including(filter: {name: "my-slug"})
67
+ ).and_return [post]
68
+
69
+ expect(client.find_by_slug("my-slug")).to eq post
70
+ end
71
+
72
+ it "raises NotFoundError when trying to find by slug yields no posts" do
73
+ expect(connection).to receive(:get_multiple).with(
74
+ Post, "posts", hash_including(filter: {name: "my-slug"}, per_page: 1)
75
+ ).and_return []
76
+
77
+ expect {
78
+ client.find_by_slug("my-slug")
79
+ }.to raise_error(NotFoundError, /my-slug/)
80
+ end
81
+ end
82
+
83
+ describe "creating a post" do
84
+ it "embeds linked resources when following redirect" do
85
+ post = instance_double(Post, id: 5)
86
+ attributes = {title: "Foo"}
87
+
88
+ expect(connection).to receive(:create).with(
89
+ Post, "posts", attributes, redirect_params: {_embed: nil}
90
+ ).and_return post
91
+
92
+ # We don't expect here as the `create` call below could be enough, but
93
+ # it's also very possible that we need to fetch the post again after
94
+ # doing other things to it.
95
+ allow(connection).to receive(:get).with(
96
+ Post, "posts/5", hash_including(_embed: nil)
97
+ ).and_return(post)
98
+
99
+ expect(client.create_post(attributes)).to eq post
100
+ end
101
+
102
+ it "adds metadata to the post" do
103
+ post = instance_double(Post, id: 5)
104
+ allow(connection).to receive(:create).and_return(post)
105
+
106
+ expect(ReplaceMetadata).to receive(:apply).with(
107
+ connection, post, {"hello" => "world"}
108
+ ).and_return(0)
109
+
110
+ client.create_post(title: "Foo", meta: {"hello" => "world"})
111
+ end
112
+
113
+ it "sets categories of the post" do
114
+ post = instance_double(Post, id: 5)
115
+ allow(connection).to receive(:create).and_return(post)
116
+
117
+ expect(ReplaceTerms).to receive(:apply_categories).with(
118
+ connection, post, [1, 3, 7]
119
+ ).and_return(0)
120
+
121
+ client.create_post(title: "Foo", category_ids: [1, 3, 7])
122
+ end
123
+
124
+ it "sets tags of the post" do
125
+ post = instance_double(Post, id: 5)
126
+ allow(connection).to receive(:create).and_return(post)
127
+
128
+ expect(ReplaceTerms).to receive(:apply_tags).with(
129
+ connection, post, [1, 3, 7]
130
+ ).and_return(0)
131
+
132
+ client.create_post(title: "Foo", tag_ids: [1, 3, 7])
133
+ end
134
+
135
+ it "refreshes the post if terms or categories changed" do
136
+ post = instance_double(Post, id: 5)
137
+ allow(connection).to receive(:create).and_return(post)
138
+
139
+ expect(ReplaceTerms).to receive(:apply_tags).and_return(1)
140
+ expect(ReplaceTerms).to receive(:apply_categories).and_return(1)
141
+ expect(ReplaceMetadata).to receive(:apply).and_return(1)
142
+
143
+ expect(connection).to receive(:get).with(
144
+ Post, "posts/5", hash_including(_embed: nil)
145
+ ).and_return(post)
146
+
147
+ client.create_post(title: "Foo", tag_ids: [], category_ids: [], meta: {})
148
+ end
149
+
150
+ it "does not refresh the post if neither terms nor categories changed" do
151
+ post = instance_double(Post, id: 5)
152
+ allow(connection).to receive(:create).and_return(post)
153
+
154
+ expect(ReplaceTerms).to receive(:apply_tags).and_return(0)
155
+ expect(ReplaceTerms).to receive(:apply_categories).and_return(0)
156
+ expect(ReplaceMetadata).to receive(:apply).and_return(0)
157
+
158
+ expect(connection).to_not receive(:get)
159
+
160
+ client.create_post(title: "Foo", tag_ids: [], category_ids: [], meta: {})
161
+ end
162
+ end
163
+
164
+ describe "updating a post" do
165
+ it "embeds linked resources" do
166
+ post = instance_double(Post)
167
+
168
+ expect(connection).to receive(:patch).with(
169
+ Post, "posts/5?_embed", hash_including(title: "Foo")
170
+ ).and_return(post)
171
+
172
+ expect(client.update_post(5, title: "Foo")).to eq post
173
+ end
174
+
175
+ it "adds metadata to the post" do
176
+ post = instance_double(Post, id: 5)
177
+ allow(connection).to receive(:patch).and_return(post)
178
+
179
+ expect(ReplaceMetadata).to receive(:apply).with(
180
+ connection, post, {"hello" => "world"}
181
+ ).and_return(0)
182
+
183
+ client.update_post(5, title: "Foo", meta: {"hello" => "world"})
184
+ end
185
+
186
+ it "changes categories of the post" do
187
+ post = instance_double(Post, id: 5)
188
+ allow(connection).to receive(:patch).and_return(post)
189
+
190
+ expect(ReplaceTerms).to receive(:apply_categories).with(
191
+ connection, post, [1, 3, 7]
192
+ ).and_return(0)
193
+
194
+ client.update_post(5, title: "Foo", category_ids: [1, 3, 7])
195
+ end
196
+
197
+ it "changes tags of the post" do
198
+ post = instance_double(Post, id: 5)
199
+ allow(connection).to receive(:patch).and_return(post)
200
+
201
+ expect(ReplaceTerms).to receive(:apply_tags).with(
202
+ connection, post, [1, 3, 7]
203
+ ).and_return(0)
204
+
205
+ client.update_post(5, title: "Foo", tag_ids: [1, 3, 7])
206
+ end
207
+
208
+ it "refreshes the post if terms or categories changed" do
209
+ post = instance_double(Post, id: 5)
210
+ allow(connection).to receive(:patch).and_return(post)
211
+
212
+ expect(ReplaceTerms).to receive(:apply_tags).and_return(1)
213
+ expect(ReplaceTerms).to receive(:apply_categories).and_return(1)
214
+ expect(ReplaceMetadata).to receive(:apply).and_return(1)
215
+
216
+ expect(connection).to receive(:get).with(
217
+ Post, "posts/5", hash_including(_embed: nil)
218
+ ).and_return(post)
219
+
220
+ client.update_post(5, title: "Foo", tag_ids: [], category_ids: [], meta: {})
221
+ end
222
+
223
+ it "does not refresh the post if neither terms nor categories changed" do
224
+ post = instance_double(Post, id: 5)
225
+ allow(connection).to receive(:patch).and_return(post)
226
+
227
+ expect(ReplaceTerms).to receive(:apply_tags).and_return(0)
228
+ expect(ReplaceTerms).to receive(:apply_categories).and_return(0)
229
+ expect(ReplaceMetadata).to receive(:apply).and_return(0)
230
+
231
+ expect(connection).to_not receive(:get)
232
+
233
+ client.update_post(5, title: "Foo", tag_ids: [], category_ids: [], meta: {})
234
+ end
235
+ end
236
+
237
+ describe "deleting posts" do
238
+ it "deletes a post without force by default" do
239
+ expect(connection).to receive(:delete).with(
240
+ "posts/1", {"force" => false}
241
+ ).and_return true
242
+
243
+ expect(client.delete_post(1)).to eq true
244
+ end
245
+
246
+ it "deletes a post without force" do
247
+ expect(connection).to receive(:delete).with(
248
+ "posts/1", {"force" => false}
249
+ ).and_return true
250
+
251
+ expect(client.delete_post(1, force: false)).to eq true
252
+ end
253
+
254
+ it "deletes a post with force" do
255
+ expect(connection).to receive(:delete).with(
256
+ "posts/1", {"force" => true}
257
+ ).and_return true
258
+
259
+ expect(client.delete_post(1, force: true)).to eq true
260
+ end
261
+ end
262
+
263
+ describe "categories" do
264
+ it "can be listed" do
265
+ expect(connection).to receive(:get_multiple).with(
266
+ Category, "terms/category", hash_including(page: 1, per_page: 10)
267
+ )
268
+ client.categories
269
+
270
+ expect(connection).to receive(:get_multiple).with(
271
+ Category, "terms/category", hash_including(page: 2, per_page: 60)
272
+ )
273
+ client.categories(page: 2, per_page: 60)
274
+ end
275
+
276
+ it "can be found" do
277
+ category = instance_double(Category)
278
+
279
+ expect(connection).to receive(:get).with(
280
+ Category, "terms/category/12"
281
+ ).and_return category
282
+
283
+ expect(client.find_category(12)).to eq category
284
+ end
285
+
286
+ it "can be created" do
287
+ category = instance_double(Category)
288
+
289
+ expect(connection).to receive(:create).with(
290
+ Category, "terms/category", name: "Foo"
291
+ ).and_return category
292
+
293
+ expect(client.create_category(name: "Foo")).to eq category
294
+ end
295
+
296
+ it "can be updated" do
297
+ category = instance_double(Category)
298
+
299
+ expect(connection).to receive(:patch).with(
300
+ Category, "terms/category/45", name: "New"
301
+ ).and_return category
302
+
303
+ expect(client.update_category(45, name: "New")).to eq category
304
+ end
305
+ end
306
+
307
+ describe "tags" do
308
+ it "can be listed" do
309
+ expect(connection).to receive(:get_multiple).with(
310
+ Tag, "terms/tag", hash_including(page: 1, per_page: 10)
311
+ )
312
+ client.tags
313
+
314
+ expect(connection).to receive(:get_multiple).with(
315
+ Tag, "terms/tag", hash_including(page: 2, per_page: 60)
316
+ )
317
+ client.tags(page: 2, per_page: 60)
318
+ end
319
+
320
+ it "can be found" do
321
+ tag = instance_double(Tag)
322
+
323
+ expect(connection).to receive(:get).with(
324
+ Tag, "terms/tag/12"
325
+ ).and_return tag
326
+
327
+ expect(client.find_tag(12)).to eq tag
328
+ end
329
+
330
+ it "can be created" do
331
+ tag = instance_double(Tag)
332
+
333
+ expect(connection).to receive(:create).with(
334
+ Tag, "terms/tag", name: "Foo"
335
+ ).and_return tag
336
+
337
+ expect(client.create_tag(name: "Foo")).to eq tag
338
+ end
339
+
340
+ it "can be updated" do
341
+ tag = instance_double(Tag)
342
+
343
+ expect(connection).to receive(:patch).with(
344
+ Tag, "terms/tag/45", name: "New"
345
+ ).and_return tag
346
+
347
+ expect(client.update_tag(45, name: "New")).to eq tag
348
+ end
349
+ end
350
+
351
+ describe "media" do
352
+ it "can be uploaded from IO objects" do
353
+ media = instance_double(Media)
354
+ io = double("io")
355
+
356
+ expect(connection).to receive(:upload).with(
357
+ Media, "media", io, mime_type: "text/plain", filename: "foo.txt"
358
+ ).and_return media
359
+
360
+ expect(client.upload(io, mime_type: "text/plain", filename: "foo.txt")).to eq media
361
+ end
362
+
363
+ it "can be uploaded from files" do
364
+ media = instance_double(Media)
365
+
366
+ Dir.mktmpdir do |dir|
367
+ file = File.join(dir, "test.txt")
368
+ File.write(file, "hello world")
369
+
370
+ expect(connection).to receive(:upload) do |_, _, io, filename:, mime_type:|
371
+ expect(filename).to eq "test.txt"
372
+ expect(mime_type).to eq "text/plain"
373
+
374
+ expect(io.read).to eq "hello world"
375
+ media
376
+ end
377
+
378
+ expect(client.upload_file(file, mime_type: "text/plain")).to eq media
379
+ end
380
+ end
381
+
382
+ it "can be found" do
383
+ media = instance_double(Media)
384
+
385
+ expect(connection).to receive(:get).with(Media, "media/7").and_return(media)
386
+
387
+ expect(client.find_media(7)).to eq media
388
+ end
389
+
390
+ it "can be listed" do
391
+ media = instance_double(Media)
392
+
393
+ expect(connection).to receive(:get_multiple).with(
394
+ Media, "media", per_page: 10, page: 1
395
+ ).and_return([media])
396
+
397
+ expect(client.media).to eq [media]
398
+ end
399
+
400
+ it "can be updated" do
401
+ media = instance_double(Media)
402
+
403
+ expect(connection).to receive(:patch).with(
404
+ Media, "media/7", title: "New"
405
+ ).and_return(media)
406
+
407
+ expect(client.update_media(7, title: "New")).to eq media
408
+ end
409
+ end
410
+ end
411
+ end