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,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