wordpress_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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