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,60 @@
1
+ require "spec_helper"
2
+
3
+ describe "Categories" do
4
+ setup_integration_client
5
+
6
+ it "is listed on found posts" do
7
+ post = client.posts(per_page: 1).first
8
+
9
+ expect(post.categories).to_not be_empty
10
+
11
+ category = post.categories.first
12
+ expect(category.id).to be_kind_of(Integer)
13
+ expect(category.name_html).to be_instance_of(String)
14
+ expect(category.slug).to be_instance_of(String)
15
+
16
+ expect(post.category_ids).to eq post.categories.map(&:id)
17
+ end
18
+
19
+ it "can be listed" do
20
+ categories = client.categories
21
+ expect(categories.size).to be > 0
22
+
23
+ category = categories.first
24
+ expect(category.id).to be_kind_of(Integer)
25
+ expect(category.name_html).to be_instance_of(String)
26
+ expect(category.slug).to be_instance_of(String)
27
+ end
28
+
29
+ it "can be found" do
30
+ existing = find_existing_category
31
+ expect(client.find_category(existing.id)).to eq existing
32
+ end
33
+
34
+ it "can be created" do
35
+ category = client.create_category(name: "New category")
36
+
37
+ expect(category.id).to be_kind_of(Integer)
38
+ expect(category.name_html).to eq "New category"
39
+ expect(category.slug).to eq "new-category"
40
+
41
+ expect(client.categories(per_page: 100).map(&:name_html)).to include "New category"
42
+ end
43
+
44
+ it "can be updated" do
45
+ existing = find_existing_category
46
+ client.update_category(existing.id, name: "Updated name")
47
+ expect(client.find_category(existing.id).name_html).to eq "Updated name"
48
+ end
49
+
50
+ it "uses HTML for the name" do
51
+ category = client.create_category(name: "Sort & Find")
52
+ expect(category.name_html).to eq "Sort & Find"
53
+ end
54
+
55
+ def find_existing_category
56
+ categories = client.categories(per_page: 1)
57
+ expect(categories).to_not be_empty
58
+ categories.first
59
+ end
60
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe "Category assignment" do
4
+ setup_integration_client
5
+ let(:existing_post) { find_existing_post }
6
+
7
+ it "can be set for multiple categories when creating a post" do
8
+ category = client.create_category(name: "Assignment test 1")
9
+ post = client.create_post(category_ids: [category.id], title: "Assignment test")
10
+
11
+ expect(post.categories).to eq [category]
12
+ end
13
+
14
+ it "can be changed when updating a post" do
15
+ category = client.create_category(name: "Assignment test 2")
16
+ post = find_existing_post
17
+
18
+ client.update_post(post.id, category_ids: [category.id])
19
+
20
+ post = client.find_post(post.id)
21
+ expect(post.categories).to eq [category]
22
+ end
23
+
24
+ def find_existing_post
25
+ posts = client.posts(per_page: 1)
26
+ expect(posts).to_not be_empty
27
+ posts.first
28
+ end
29
+ end
@@ -0,0 +1,118 @@
1
+ require "spec_helper"
2
+
3
+ describe "Posts (CRUD)" do
4
+ setup_integration_client
5
+
6
+ it "can list posts" do
7
+ posts = client.posts(per_page: 1)
8
+ expect(posts.size).to be 1
9
+
10
+ post = posts.first
11
+ expect(post).to be_instance_of WordpressClient::Post
12
+ expect(post.title_html).to be_instance_of(String)
13
+ end
14
+
15
+ it "can get specific posts" do
16
+ existing_post = find_existing_post
17
+
18
+ found_post = client.find_post(existing_post.id)
19
+ expect(found_post.id).to eq existing_post.id
20
+ expect(found_post.title_html).to eq existing_post.title_html
21
+
22
+ expect { client.find_post(888888) }.to raise_error(WordpressClient::NotFoundError)
23
+ end
24
+
25
+ it "can create a post" do
26
+ data = {
27
+ title: "A newly created post",
28
+ status: "publish",
29
+ }
30
+
31
+ post = client.create_post(data)
32
+
33
+ expect(post.id).to be_kind_of Integer
34
+
35
+ # Try to find the post to determine if it was persisted or not
36
+ all_posts = client.posts(per_page: 100)
37
+ expect(all_posts.map(&:id)).to include post.id
38
+ expect(all_posts.map(&:title_html)).to include "A newly created post"
39
+ end
40
+
41
+ it "raises a validation error if post could not be created" do
42
+ expect {
43
+ client.create_post(status: "not really valid")
44
+ }.to raise_error(WordpressClient::ValidationError, /status/)
45
+ end
46
+
47
+ it "can update a post" do
48
+ post = find_existing_post
49
+
50
+ client.update_post(post.id, title: "Updated title")
51
+
52
+ expect(client.find_post(post.id).title_html).to eq "Updated title"
53
+ end
54
+
55
+ it "raises errors if post could not be updated" do
56
+ existing_post = find_existing_post
57
+
58
+ expect {
59
+ client.update_post(existing_post.id, status: "not really valid")
60
+ }.to raise_error(WordpressClient::ValidationError, /status/)
61
+
62
+ expect {
63
+ client.update_post(888888, title: "Never existed in the first place")
64
+ }.to raise_error(WordpressClient::NotFoundError)
65
+ end
66
+
67
+ it "correctly handles HTML" do
68
+ post = client.create_post(
69
+ title: "HTML test & verify",
70
+ content: '<p class="hello-world">Hello world</p>',
71
+ )
72
+
73
+ expect(post.content_html.strip).to eq '<p class="hello-world">Hello world</p>'
74
+ expect(post.title_html.strip).to eq 'HTML test &amp; verify'
75
+ end
76
+
77
+ it "can move a post to the trash can" do
78
+ post = find_existing_post
79
+
80
+ expect(
81
+ client.delete_post(post.id)
82
+ ).to eq true
83
+
84
+ found_post = client.find_post(post.id)
85
+ expect(found_post.id).to eq post.id
86
+ expect(found_post.status).to eq "trash"
87
+ end
88
+
89
+ it "can permanently delete a post" do
90
+ post = find_existing_post
91
+
92
+ expect(
93
+ client.delete_post(post.id, force: true)
94
+ ).to eq true
95
+
96
+ expect {
97
+ client.find_post(post.id)
98
+ }.to raise_error(WordpressClient::NotFoundError)
99
+ end
100
+
101
+ it "raises an error when deleting a post that does not exist" do
102
+ post_id = 99999999
103
+
104
+ expect {
105
+ client.find_post(post_id)
106
+ }.to raise_error(WordpressClient::NotFoundError)
107
+
108
+ expect {
109
+ client.delete_post(99999999)
110
+ }.to raise_error(WordpressClient::NotFoundError)
111
+ end
112
+
113
+ def find_existing_post
114
+ posts = client.posts(per_page: 1)
115
+ expect(posts).to_not be_empty
116
+ posts.first
117
+ end
118
+ end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+
3
+ describe "Posts (finding)" do
4
+ setup_integration_client
5
+
6
+ it "sorts on publication date by default" do
7
+ client.create_post(status: "publish", date: "2012-05-05T15:00:00Z", title: "Oldest")
8
+ client.create_post(status: "publish", date: "2012-06-15T15:00:00Z", title: "Newest")
9
+ client.create_post(status: "publish", date: "2012-05-20T15:00:00Z", title: "Older")
10
+
11
+ posts = client.posts(per_page: 10)
12
+
13
+ expect(posts.first.date).to be > posts.last.date
14
+ expect(posts.map(&:id)).to eq posts.sort_by(&:date).reverse.map(&:id)
15
+ end
16
+
17
+ it "can list articles in a specific category" do
18
+ category = client.create_category(name: "Filtering time", slug: "filtering")
19
+ post = client.create_post(
20
+ category_ids: [category.id],
21
+ status: "publish",
22
+ title: "Some title",
23
+ )
24
+
25
+ expect(client.posts(category_slug: "filtering").map(&:id)).to eq [post.id]
26
+ end
27
+
28
+ it "can list articles in a specific tag" do
29
+ tag = client.create_tag(name: "Taggged", slug: "tagged")
30
+ post = client.create_post(
31
+ tag_ids: [tag.id],
32
+ status: "publish",
33
+ title: "Some title",
34
+ )
35
+
36
+ expect(client.posts(tag_slug: "tagged").map(&:id)).to eq [post.id]
37
+ end
38
+
39
+ it "can list articles in a specific tag and category" do
40
+ category = client.create_category(name: "Fishing", slug: "fish")
41
+ tag = client.create_tag(name: "Soup", slug: "soup")
42
+ post = client.create_post(
43
+ category_ids: [category.id],
44
+ tag_ids: [tag.id],
45
+ status: "publish",
46
+ title: "Some title",
47
+ )
48
+
49
+ _post_without_tag = client.create_post(
50
+ category_ids: [category.id],
51
+ status: "publish",
52
+ title: "Some title",
53
+ )
54
+
55
+ _post_without_category = client.create_post(
56
+ tag_ids: [tag.id],
57
+ status: "publish",
58
+ title: "Some title",
59
+ )
60
+
61
+ expect(client.posts(category_slug: "fish", tag_slug: "soup").map(&:id)).to eq [post.id]
62
+ end
63
+
64
+ describe "finding by slug" do
65
+ it "finds the matching post" do
66
+ post = client.create_post(title: "Oh hai", slug: "oh-hai")
67
+ found = client.find_by_slug("oh-hai")
68
+ expect(found.id).to eq post.id
69
+ end
70
+
71
+ it "raises NotFoundError when no post can be found" do
72
+ expect {
73
+ client.find_by_slug("clearly-does-not-exist-anywhere")
74
+ }.to raise_error(WordpressClient::NotFoundError, /clearly/)
75
+ end
76
+
77
+ it "finds on the slug even if the title is wildly different" do
78
+ post = client.create_post(
79
+ title: "Updated title that doesn't match slug",
80
+ slug: "original-concise-title",
81
+ )
82
+ found = client.find_by_slug("original-concise-title")
83
+ expect(found.id).to eq post.id
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,27 @@
1
+ require "spec_helper"
2
+
3
+ describe "Post meta" do
4
+ setup_integration_client
5
+
6
+ it "can be set on a post" do
7
+ post = client.create_post(title: "Metadata creation", meta: {foo: "bar"})
8
+ expect(post.meta).to eq("foo" => "bar")
9
+ end
10
+
11
+ it "can be updated on a post" do
12
+ post = client.create_post(title: "Metadata creation", meta: {before: "now"})
13
+ post = client.update_post(post.id, meta: {"before" => "then", "after" => "now"})
14
+ expect(post.meta).to eq("before" => "then", "after" => "now")
15
+ end
16
+
17
+ it "can remove metadata for a post" do
18
+ post = client.create_post(title: "Metadata creation", meta: {one: "1", two: "2", three: "3"})
19
+ post = client.update_post(post.id, meta: {three: "3", two: nil})
20
+ expect(post.meta).to eq("three" => "3")
21
+ end
22
+
23
+ it "does not contain HTML data" do
24
+ post = client.create_post(title: "Metadata HTML", meta: {foo: "bar&baz"})
25
+ expect(post.meta).to eq("foo" => "bar&baz")
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ require "spec_helper"
2
+
3
+ describe "Posts with attachments" do
4
+ setup_integration_client
5
+
6
+ it "exposes featured image as a Media instance" do
7
+ media = find_or_upload_media
8
+ post = client.create_post(title: "With media", featured_image: media.id)
9
+
10
+ expect(post.featured_image).to be_instance_of(WordpressClient::Media)
11
+ expect(post.featured_image.slug).to eq media.slug
12
+ expect(post.featured_image.guid).to eq media.guid
13
+ expect(post.featured_image.source_url).to eq media.source_url
14
+ expect(post.featured_image_id).to eq media.id
15
+ end
16
+
17
+ def find_or_upload_media
18
+ client.media(per_page: 1).first ||
19
+ client.upload_file(fixture_path("thoughtful.jpg"), mime_type: "image/jpeg")
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe "Tag assignment" do
4
+ setup_integration_client
5
+ let(:existing_post) { find_existing_post }
6
+
7
+ it "can be set for multiple tags when creating a post" do
8
+ tag = client.create_tag(name: "Assignment test 1")
9
+ post = client.create_post(tag_ids: [tag.id], title: "Assignment test")
10
+
11
+ expect(post.tags).to eq [tag]
12
+ end
13
+
14
+ it "can be changed when updating a post" do
15
+ tag = client.create_tag(name: "Assignment test 2")
16
+ post = find_existing_post
17
+
18
+ client.update_post(post.id, tag_ids: [tag.id])
19
+
20
+ post = client.find_post(post.id)
21
+ expect(post.tags).to eq [tag]
22
+ end
23
+
24
+ def find_existing_post
25
+ posts = client.posts(per_page: 1)
26
+ expect(posts).to_not be_empty
27
+ posts.first
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+
3
+ describe "Tags" do
4
+ setup_integration_client
5
+
6
+ it "can be found" do
7
+ existing = find_or_create_tag
8
+ expect(client.find_tag(existing.id)).to eq existing
9
+ end
10
+
11
+ it "can be created" do
12
+ tag = client.create_tag(name: "New tag")
13
+
14
+ expect(tag.id).to be_kind_of(Integer)
15
+ expect(tag.name_html).to eq "New tag"
16
+ expect(tag.slug).to eq "new-tag"
17
+
18
+ expect(client.tags(per_page: 100).map(&:name_html)).to include "New tag"
19
+ end
20
+
21
+ it "can be updated" do
22
+ existing = find_or_create_tag
23
+ client.update_tag(existing.id, name: "Updated name")
24
+ expect(client.find_tag(existing.id).name_html).to eq "Updated name"
25
+ end
26
+
27
+ it "uses HTML for the name" do
28
+ tag = client.create_tag(name: "Sort & Find")
29
+ expect(tag.name_html).to eq "Sort &amp; Find"
30
+ end
31
+
32
+ def find_or_create_tag
33
+ tags = client.tags(per_page: 1)
34
+ tags.first || client.create_tag(name: "Autocreated")
35
+ end
36
+ end
@@ -0,0 +1,63 @@
1
+ require "spec_helper"
2
+
3
+ module WordpressClient
4
+ describe Media do
5
+ let(:fixture) { json_fixture("image-media.json") }
6
+
7
+ it "can be parsed from JSON data" do
8
+ media = Media.parse(fixture)
9
+
10
+ expect(media.id).to eq 5
11
+ expect(media.title_html).to eq "thoughtful"
12
+ expect(media.slug).to eq "thoughtful"
13
+ expect(media.description).to eq ""
14
+
15
+ expect(media.guid).to eq "http://example.com/wp-content/uploads/2015/11/thoughtful.jpg"
16
+ expect(media.source_url).to eq "http://example.com/wp-content/uploads/2015/11/thoughtful.jpg"
17
+ expect(media.link).to eq "http://example.com/?attachment_id=5"
18
+
19
+ expect(media.date).to_not be_nil
20
+ expect(media.updated_at).to_not be_nil
21
+ end
22
+
23
+ it "exposes media information" do
24
+ expect(Media.parse(fixture).media_details).to eq fixture.fetch("media_details")
25
+ end
26
+
27
+ it "tries to read guid from source_url if guid is not present" do
28
+ # This happens for associated attachments of posts, for example.
29
+ fixture.delete("guid")
30
+ fixture["source_url"] = "http://example.com/image.jpg"
31
+
32
+ media = Media.parse(fixture)
33
+ expect(media.guid).to eq "http://example.com/image.jpg"
34
+ expect(media.source_url).to eq "http://example.com/image.jpg"
35
+ end
36
+
37
+ describe "dates" do
38
+ it "uses GMT times if available" do
39
+ media = Media.parse(fixture.merge(
40
+ "date_gmt" => "2001-01-01T15:00:00",
41
+ "date" => "2001-01-01T12:00:00",
42
+ "modified_gmt" => "2001-01-01T15:00:00",
43
+ "modified" => "2001-01-01T12:00:00",
44
+ ))
45
+
46
+ expect(media.date).to eq Time.utc(2001, 1, 1, 15, 0, 0)
47
+ expect(media.updated_at).to eq Time.utc(2001, 1, 1, 15, 0, 0)
48
+ end
49
+
50
+ it "falls back to local time if no GMT date is provided" do
51
+ media = Media.parse(fixture.merge(
52
+ "date_gmt" => nil,
53
+ "date" => "2001-01-01T12:00:00",
54
+ "modified_gmt" => nil,
55
+ "modified" => "2001-01-01T12:00:00",
56
+ ))
57
+
58
+ expect(media.date).to eq Time.local(2001, 1, 1, 12, 0, 0)
59
+ expect(media.updated_at).to eq Time.local(2001, 1, 1, 12, 0, 0)
60
+ end
61
+ end
62
+ end
63
+ end