wordpress_client 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +14 -0
- data/.yardopts +1 -0
- data/Changelog.md +3 -0
- data/README.md +15 -6
- data/Rakefile +35 -0
- data/lib/wordpress_client.rb +21 -2
- data/lib/wordpress_client/category.rb +2 -0
- data/lib/wordpress_client/client.rb +247 -37
- data/lib/wordpress_client/connection.rb +1 -0
- data/lib/wordpress_client/errors.rb +41 -6
- data/lib/wordpress_client/media.rb +32 -0
- data/lib/wordpress_client/media_parser.rb +1 -0
- data/lib/wordpress_client/paginated_collection.rb +61 -4
- data/lib/wordpress_client/post.rb +71 -0
- data/lib/wordpress_client/post_parser.rb +1 -0
- data/lib/wordpress_client/replace_metadata.rb +1 -0
- data/lib/wordpress_client/replace_terms.rb +1 -0
- data/lib/wordpress_client/rest_parser.rb +4 -0
- data/lib/wordpress_client/tag.rb +2 -0
- data/lib/wordpress_client/term.rb +30 -0
- data/lib/wordpress_client/version.rb +5 -1
- data/spec/client_spec.rb +2 -2
- data/spec/docker/Dockerfile +4 -4
- data/spec/docker/README.md +41 -14
- data/spec/integration/posts_finding_spec.rb +3 -3
- data/spec/support/docker_runner.rb +6 -2
- data/spec/support/wordpress_server.rb +5 -3
- data/wordpress_client.gemspec +1 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07d49e4293481ae704072d12288a60a97d1182ec
|
4
|
+
data.tar.gz: be101dbc5a42658d3b9639bf0cc6d1753f246d00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cec642f556f04ff4f5c418dda5c560956b7a1c1d3cd17184a2312a0a9d35daf5c9fb60834055375d3b27b0c1623d74b3b9bf860adb9334bc476d2c22b13f87a
|
7
|
+
data.tar.gz: d149bb98f48f1f164aef95cbad14d60669b553abd78f2968ccae16be2e0582d887d2af3b1e81f8dda8fa29e69958530a95dfe37168020ac04b40a3a8ba73f709
|
data/.codeclimate.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --embed-mixins
|
data/Changelog.md
ADDED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# WordpressClient
|
2
2
|
|
3
|
-
WordpressClient is a very simple client
|
3
|
+
WordpressClient is a very simple client for the Wordpress [WP REST API plugin][api] (version 2 beta 8.0).
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
[![Circle CI](https://circleci.com/gh/hemnet/wpclient.svg?style=svg)](https://circleci.com/gh/hemnet/wpclient) [![Code Climate](https://codeclimate.com/repos/5645938269568041da00cded/badges/5e870b57428f23c1f2ff/gpa.svg)](https://codeclimate.com/repos/5645938269568041da00cded/feed) [![Test Coverage](https://codeclimate.com/repos/5645938269568041da00cded/badges/5e870b57428f23c1f2ff/coverage.svg)](https://codeclimate.com/repos/5645938269568041da00cded/coverage)
|
5
|
+
[![Circle CI](https://circleci.com/gh/hemnet/wordpress_client.svg?style=svg)](https://circleci.com/gh/hemnet/wordpress_client) [![Code Climate](https://codeclimate.com/repos/5645938269568041da00cded/badges/5e870b57428f23c1f2ff/gpa.svg)](https://codeclimate.com/repos/5645938269568041da00cded/feed) [![Test Coverage](https://codeclimate.com/repos/5645938269568041da00cded/badges/5e870b57428f23c1f2ff/coverage.svg)](https://codeclimate.com/repos/5645938269568041da00cded/coverage) [![Gem Version](https://badge.fury.io/rb/wordpress_client.svg)](https://badge.fury.io/rb/wordpress_client)
|
8
6
|
|
9
7
|
## Usage
|
10
8
|
|
11
|
-
|
9
|
+
**[Read the full API documentation][docs]**
|
10
|
+
|
11
|
+
Initialize a client with a user name, password and API URL. You can then search for posts.
|
12
12
|
|
13
13
|
```ruby
|
14
14
|
client = WordpressClient.new(url: "https://example.com/wp-json/", username: "example", password: "example")
|
@@ -36,7 +36,7 @@ updated_post.title_html # => "Updated"
|
|
36
36
|
|
37
37
|
You need to install Docker and set it up for your machine. Note that you need `docker-machine` to run Docker on OS X.
|
38
38
|
|
39
|
-
Run tests using the normal `rspec` command after installing all bundles.
|
39
|
+
Run tests using the normal `rspec` command after installing all bundles.
|
40
40
|
|
41
41
|
```
|
42
42
|
bundle exec rspec
|
@@ -52,6 +52,12 @@ end
|
|
52
52
|
|
53
53
|
The normal `rspec` command will *not* use this filter in case it is ever committed accidentally, so CI can catch any problems.
|
54
54
|
|
55
|
+
## Releasing a new version
|
56
|
+
|
57
|
+
The normal gem release cycle works using `rake release`.
|
58
|
+
|
59
|
+
If you make changes to the docker image, you can release it using `rake docker:release`.
|
60
|
+
|
55
61
|
## Copyright & License
|
56
62
|
|
57
63
|
Copyright © 2015 Hemnet Service HNS AB
|
@@ -61,3 +67,6 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
61
67
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
62
68
|
|
63
69
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
70
|
+
|
71
|
+
[api]: http://v2.wp-api.org/
|
72
|
+
[docs]: http://www.rubydoc.info/gems/wordpress_client/
|
data/Rakefile
CHANGED
@@ -1 +1,36 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "yard"
|
3
|
+
require "wordpress_client/version"
|
4
|
+
|
5
|
+
YARD::Rake::YardocTask.new
|
6
|
+
|
7
|
+
namespace :docker do
|
8
|
+
DOCKER_DIR = File.expand_path("../spec/docker", __FILE__).freeze
|
9
|
+
IMAGE_NAME = "hemnet/wordpress_client_test".freeze
|
10
|
+
DEV_IMAGE = [IMAGE_NAME, "dev"].join(":").freeze
|
11
|
+
|
12
|
+
desc "Build the docker image"
|
13
|
+
task :build do
|
14
|
+
sh "docker", "build", "-t", DEV_IMAGE, DOCKER_DIR
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Release current dev build"
|
18
|
+
task release: :build do
|
19
|
+
version = prompt "Which version do you want to release"
|
20
|
+
raise "Invalid version string" unless version =~ /\A[\d.]+\z/
|
21
|
+
|
22
|
+
latest = prompt "Do you want this to be the :latest release? [Y/n]"
|
23
|
+
latest = (latest.empty? || latest.downcase == "y")
|
24
|
+
|
25
|
+
sh "docker", "tag", DEV_IMAGE, "#{IMAGE_NAME}:#{version}"
|
26
|
+
sh "docker", "tag", "-f", DEV_IMAGE, "#{IMAGE_NAME}:latest" if latest
|
27
|
+
|
28
|
+
sh "docker", "push", "#{IMAGE_NAME}:#{version}"
|
29
|
+
sh "docker", "push", "#{IMAGE_NAME}:latest" if latest
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def prompt(message)
|
34
|
+
print "#{message} > "
|
35
|
+
STDIN.gets.strip
|
36
|
+
end
|
data/lib/wordpress_client.rb
CHANGED
@@ -19,7 +19,26 @@ require "wordpress_client/replace_terms"
|
|
19
19
|
require "wordpress_client/replace_metadata"
|
20
20
|
|
21
21
|
module WordpressClient
|
22
|
-
|
23
|
-
|
22
|
+
# Initialize a new client using the provided connection details.
|
23
|
+
# You need to provide authentication details, and the user must have +edit+
|
24
|
+
# permissions on the blog if you want to read Post Meta, or to modify
|
25
|
+
# anything.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# client = WordpressClient.new(
|
29
|
+
# url: "https://blog.example.com/wp-json",
|
30
|
+
# username: "bot",
|
31
|
+
# password: ENV.fetch("WORDPRESS_PASSWORD"),
|
32
|
+
# )
|
33
|
+
#
|
34
|
+
# @see Client Client, for the methods available after creating the connection.
|
35
|
+
#
|
36
|
+
# @param url [String] The base URL to the wordpress install, including
|
37
|
+
# +/wp-json+.
|
38
|
+
# @param username [String] A valid username on the wordpress installation.
|
39
|
+
# @param password [String] The password for the provided user.
|
40
|
+
# @return {Client}
|
41
|
+
def self.new(url:, username:, password:)
|
42
|
+
Client.new(Connection.new(url: url, username: username, password: password))
|
24
43
|
end
|
25
44
|
end
|
@@ -4,6 +4,19 @@ module WordpressClient
|
|
4
4
|
@connection = connection
|
5
5
|
end
|
6
6
|
|
7
|
+
# @!group Posts
|
8
|
+
|
9
|
+
# Find {Post Posts} matching given parameters.
|
10
|
+
#
|
11
|
+
# @example Finding 5 posts in the Important category
|
12
|
+
# posts = client.posts(per_page: 5, category_slug: "important")
|
13
|
+
#
|
14
|
+
# @param page [Fixnum] Current page for pagination. Defaults to 1.
|
15
|
+
# @param per_page [Fixnum] Posts per page. Defaults to 10.
|
16
|
+
# @param category_slug [String, nil] Find posts belonging to a category with the given slug.
|
17
|
+
# @param tag_slug [String, nil] Find posts belonging to a tag with the given slug.
|
18
|
+
#
|
19
|
+
# @return {PaginatedCollection[Post]} Paginated collection of the found posts.
|
7
20
|
def posts(per_page: 10, page: 1, category_slug: nil, tag_slug: nil)
|
8
21
|
filter = {}
|
9
22
|
filter[:category_name] = category_slug if category_slug
|
@@ -13,23 +26,21 @@ module WordpressClient
|
|
13
26
|
)
|
14
27
|
end
|
15
28
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
connection.get_multiple(Tag, "terms/tag", page: page, per_page: per_page)
|
22
|
-
end
|
23
|
-
|
24
|
-
def media(per_page: 10, page: 1)
|
25
|
-
connection.get_multiple(Media, "media", page: page, per_page: per_page)
|
26
|
-
end
|
27
|
-
|
29
|
+
# Find the {Post} with the given ID, or raises an error if not found.
|
30
|
+
#
|
31
|
+
# @return {Post}
|
32
|
+
# @raise {NotFoundError}
|
33
|
+
# @raise {subclasses of Error} on other unexpected errors
|
28
34
|
def find_post(id)
|
29
35
|
connection.get(Post, "posts/#{id.to_i}", _embed: nil, context: "edit")
|
30
36
|
end
|
31
37
|
|
32
|
-
|
38
|
+
# Find the first {Post} with the given slug, or raises an error if it cannot be found.
|
39
|
+
#
|
40
|
+
# @return {Post}
|
41
|
+
# @raise {NotFoundError}
|
42
|
+
# @raise {subclasses of Error} on other unexpected errors
|
43
|
+
def find_post_by_slug(slug)
|
33
44
|
posts = connection.get_multiple(
|
34
45
|
Post, "posts", per_page: 1, page: 1, filter: {name: slug}, _embed: nil
|
35
46
|
)
|
@@ -40,18 +51,27 @@ module WordpressClient
|
|
40
51
|
end
|
41
52
|
end
|
42
53
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
# Create a new {Post} with the given attributes in Wordpress and return it.
|
55
|
+
#
|
56
|
+
# In addition to {http://v2.wp-api.org/reference/posts/ the accepted
|
57
|
+
# parameters of the API}, this method also takes the following keys:
|
58
|
+
# * +:meta+
|
59
|
+
# * +:category_ids+
|
60
|
+
# * +:tag_ids+
|
61
|
+
#
|
62
|
+
# @see http://v2.wp-api.org/reference/posts/ List of accepted parameters
|
63
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
64
|
+
# accepted parameters or the custom parameters listed
|
65
|
+
# above.
|
66
|
+
# @option attributes [Hash<String,String>] meta Hash of meta values.
|
67
|
+
# @option attributes [Array<Fixnum>] category_ids List of category IDs the
|
68
|
+
# Post should belong to.
|
69
|
+
# @option attributes [Array<Fixnum>] tag_ids List of tag IDs the Post
|
70
|
+
# should have.
|
71
|
+
#
|
72
|
+
# @return {Post}
|
73
|
+
# @raise {ValidationError}
|
74
|
+
# @raise {subclasses of Error} on other unexpected errors
|
55
75
|
def create_post(attributes)
|
56
76
|
post = connection.create(Post, "posts", attributes, redirect_params: {_embed: nil})
|
57
77
|
|
@@ -67,14 +87,34 @@ module WordpressClient
|
|
67
87
|
end
|
68
88
|
end
|
69
89
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
90
|
+
# Update the {Post} with the given id, setting the supplied attributes in
|
91
|
+
# Wordpress and returning an updated Post.
|
92
|
+
#
|
93
|
+
# In addition to {http://v2.wp-api.org/reference/posts/ the accepted
|
94
|
+
# parameters of the API}, this method also takes the following keys:
|
95
|
+
# * +:meta+
|
96
|
+
# * +:category_ids+
|
97
|
+
# * +:tag_ids+
|
98
|
+
#
|
99
|
+
# @example Changing the title of a Post
|
100
|
+
# new_post = client.update_post(post.id, title: "A better title")
|
101
|
+
# new_post.title_html #=> "A better title"
|
102
|
+
#
|
103
|
+
# @see http://v2.wp-api.org/reference/posts/ List of accepted parameters
|
104
|
+
# @param id [Fixnum] ID of the post to update.
|
105
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
106
|
+
# accepted parameters or the custom parameters listed
|
107
|
+
# above.
|
108
|
+
# @option attributes [Hash<String,String>] meta Hash of meta values.
|
109
|
+
# @option attributes [Array<Fixnum>] category_ids List of category IDs the
|
110
|
+
# Post should belong to.
|
111
|
+
# @option attributes [Array<Fixnum>] tag_ids List of tag IDs the Post
|
112
|
+
# should have.
|
113
|
+
#
|
114
|
+
# @return {Post}
|
115
|
+
# @raise {NotFoundError}
|
116
|
+
# @raise {ValidationError}
|
117
|
+
# @raise {subclasses of Error} on other unexpected errors
|
78
118
|
def update_post(id, attributes)
|
79
119
|
post = connection.patch(Post, "posts/#{id.to_i}?_embed", attributes)
|
80
120
|
|
@@ -90,22 +130,180 @@ module WordpressClient
|
|
90
130
|
end
|
91
131
|
end
|
92
132
|
|
133
|
+
# Deletes the {Post} with the given ID.
|
134
|
+
#
|
135
|
+
# @param id [Fixnum] The {Post} ID.
|
136
|
+
# @param force [Boolean] When +false+, the Post will be put in the "Trash"
|
137
|
+
# of Wordpress. +true+ causes the Post to be irrevocably deleted.
|
138
|
+
#
|
139
|
+
# @return true always
|
140
|
+
def delete_post(id, force: false)
|
141
|
+
connection.delete("posts/#{id.to_i}", {"force" => force})
|
142
|
+
end
|
143
|
+
|
144
|
+
# @!group Categories
|
145
|
+
|
146
|
+
# Find {Category Categories} in the Wordpress install.
|
147
|
+
#
|
148
|
+
# @return {PaginatedCollection[Category]}
|
149
|
+
def categories(per_page: 10, page: 1)
|
150
|
+
connection.get_multiple(Category, "terms/category", page: page, per_page: per_page)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Find {Category} with the given ID.
|
154
|
+
#
|
155
|
+
# @return {Category}
|
156
|
+
# @raise {NotFoundError}
|
157
|
+
# @raise {subclasses of Error} on other unexpected errors
|
158
|
+
def find_category(id)
|
159
|
+
connection.get(Category, "terms/category/#{id.to_i}")
|
160
|
+
end
|
161
|
+
|
162
|
+
# Create a new {Category} with the given attributes.
|
163
|
+
#
|
164
|
+
# @see http://v2.wp-api.org/reference/taxonomies/ List of accepted parameters
|
165
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
166
|
+
# parameters accepted by the API.
|
167
|
+
# @option attributes [String] name Name of the category (required).
|
168
|
+
# @option attributes [String] slug Slug of the category (optional).
|
169
|
+
# @option attributes [String] description Description of the category (optional).
|
170
|
+
#
|
171
|
+
# @return {Category} the new Category
|
172
|
+
# @raise {ValidationError}
|
173
|
+
# @raise {subclasses of Error} on other unexpected errors
|
174
|
+
def create_category(attributes)
|
175
|
+
connection.create(Category, "terms/category", attributes)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Update the {Category} with the given id, setting the supplied attributes.
|
179
|
+
#
|
180
|
+
# @see http://v2.wp-api.org/reference/taxonomies/ List of accepted parameters
|
181
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
182
|
+
# parameters accepted by the API.
|
183
|
+
# @option attributes [String] name Name of the category.
|
184
|
+
# @option attributes [String] slug Slug of the category.
|
185
|
+
# @option attributes [String] description Description of the category.
|
186
|
+
#
|
187
|
+
# @return {Category} the updated Category
|
188
|
+
# @raise {NotFoundError}
|
189
|
+
# @raise {ValidationError}
|
190
|
+
# @raise {subclasses of Error} on other unexpected errors
|
93
191
|
def update_category(id, attributes)
|
94
192
|
connection.patch(Category, "terms/category/#{id.to_i}", attributes)
|
95
193
|
end
|
96
194
|
|
195
|
+
# @!group Tags
|
196
|
+
|
197
|
+
# Find {Tag Tags} in the Wordpress install.
|
198
|
+
#
|
199
|
+
# @return {PaginatedCollection[Tag]}
|
200
|
+
def tags(per_page: 10, page: 1)
|
201
|
+
connection.get_multiple(Tag, "terms/tag", page: page, per_page: per_page)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Find {Tag} with the given ID.
|
205
|
+
#
|
206
|
+
# @return {Tag}
|
207
|
+
# @raise {NotFoundError}
|
208
|
+
# @raise {subclasses of Error} on other unexpected errors
|
209
|
+
def find_tag(id)
|
210
|
+
connection.get(Tag, "terms/tag/#{id.to_i}")
|
211
|
+
end
|
212
|
+
|
213
|
+
# Create a new {Tag} with the given attributes.
|
214
|
+
#
|
215
|
+
# @see http://v2.wp-api.org/reference/taxonomies/ List of accepted parameters
|
216
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
217
|
+
# parameters accepted by the API.
|
218
|
+
# @option attributes [String] name Name of the tag (required).
|
219
|
+
# @option attributes [String] slug Slug of the tag (optional).
|
220
|
+
# @option attributes [String] description Description of the tag (optional).
|
221
|
+
#
|
222
|
+
# @return {Tag} the new Tag
|
223
|
+
# @raise {ValidationError}
|
224
|
+
# @raise {subclasses of Error} on other unexpected errors
|
225
|
+
def create_tag(attributes)
|
226
|
+
connection.create(Tag, "terms/tag", attributes)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Update the {Tag} with the given id, setting the supplied attributes.
|
230
|
+
#
|
231
|
+
# @see http://v2.wp-api.org/reference/taxonomies/ List of accepted parameters
|
232
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
233
|
+
# parameters accepted by the API.
|
234
|
+
# @option attributes [String] name Name of the tag.
|
235
|
+
# @option attributes [String] slug Slug of the tag.
|
236
|
+
# @option attributes [String] description Description of the tag.
|
237
|
+
#
|
238
|
+
# @return {Tag} the updated Tag
|
239
|
+
# @raise {NotFoundError}
|
240
|
+
# @raise {ValidationError}
|
241
|
+
# @raise {subclasses of Error} on other unexpected errors
|
97
242
|
def update_tag(id, attributes)
|
98
243
|
connection.patch(Tag, "terms/tag/#{id.to_i}", attributes)
|
99
244
|
end
|
100
245
|
|
101
|
-
|
102
|
-
|
246
|
+
# @!group Media
|
247
|
+
|
248
|
+
# Find {Media} in the Wordpress install.
|
249
|
+
#
|
250
|
+
# @return {PaginatedCollection[Media]}
|
251
|
+
def media(per_page: 10, page: 1)
|
252
|
+
connection.get_multiple(Media, "media", page: page, per_page: per_page)
|
103
253
|
end
|
104
254
|
|
255
|
+
# Find {Media} with the given ID.
|
256
|
+
#
|
257
|
+
# @return {Media}
|
258
|
+
# @raise {NotFoundError}
|
259
|
+
# @raise {subclasses of Error} on other unexpected errors
|
260
|
+
def find_media(id)
|
261
|
+
connection.get(Media, "media/#{id.to_i}")
|
262
|
+
end
|
263
|
+
|
264
|
+
# Create a new {Media} by uploading a IO stream.
|
265
|
+
#
|
266
|
+
# You need to provide both MIME type and filename for Wordpress to accept
|
267
|
+
# the file.
|
268
|
+
#
|
269
|
+
# @example Uploading a JPEG from a request
|
270
|
+
# media = client.upload(
|
271
|
+
# request.body_stream, filename: "foo.jpg", mime_type: "image/jpeg"
|
272
|
+
# )
|
273
|
+
#
|
274
|
+
# @param io [IO-like object] IO stream (for example an open file) that will
|
275
|
+
# be the body of the media.
|
276
|
+
# @param mime_type [String] the MIME type of the IO stream
|
277
|
+
# @param filename [String] the filename that Wordpress should see. Requires
|
278
|
+
# a file extension to make Wordpress happy.
|
279
|
+
#
|
280
|
+
# @return {Media} the new Media
|
281
|
+
# @raise {ValidationError}
|
282
|
+
# @raise {subclasses of Error} on other unexpected errors
|
283
|
+
# @see #upload_file #upload_file - a shortcut for uploading files on disk
|
105
284
|
def upload(io, mime_type:, filename:)
|
106
285
|
connection.upload(Media, "media", io, mime_type: mime_type, filename: filename)
|
107
286
|
end
|
108
287
|
|
288
|
+
# Create a new {Media} by uploading a file from disk.
|
289
|
+
#
|
290
|
+
# You need to provide MIME type for Wordpress to accept the file. The
|
291
|
+
# filename that Wordpress sees will automatically be derived from the
|
292
|
+
# passed path.
|
293
|
+
#
|
294
|
+
# @example Uploading a JPEG from disk
|
295
|
+
# media = client.upload_file(
|
296
|
+
# "assets/ocean.jpg", mime_type: "image/jpeg"
|
297
|
+
# )
|
298
|
+
#
|
299
|
+
# @param filename [String] a path to a readable file.
|
300
|
+
# @param mime_type [String] the MIME type of the file.
|
301
|
+
#
|
302
|
+
# @return {Media} the new Media
|
303
|
+
# @raise {ValidationError}
|
304
|
+
# @raise {subclasses of Error} on other unexpected errors
|
305
|
+
# @see #upload #upload - for when you want to upload something that isn't a
|
306
|
+
# file on disk, or need extra flexibility
|
109
307
|
def upload_file(filename, mime_type:)
|
110
308
|
path = filename.to_s
|
111
309
|
File.open(path, 'r') do |file|
|
@@ -113,10 +311,22 @@ module WordpressClient
|
|
113
311
|
end
|
114
312
|
end
|
115
313
|
|
116
|
-
|
117
|
-
|
314
|
+
# Update the {Media} with the given id, setting the supplied attributes.
|
315
|
+
#
|
316
|
+
# @see http://v2.wp-api.org/reference/media/ List of accepted parameters
|
317
|
+
# @param attributes [Hash<Symbol,Object>] attribute list, containing
|
318
|
+
# parameters accepted by the API.
|
319
|
+
#
|
320
|
+
# @return {Media} The updated Media
|
321
|
+
# @raise {NotFoundError}
|
322
|
+
# @raise {ValidationError}
|
323
|
+
# @raise {subclasses of Error} on other unexpected errors
|
324
|
+
def update_media(id, attributes)
|
325
|
+
connection.patch(Media, "media/#{id.to_i}", attributes)
|
118
326
|
end
|
119
327
|
|
328
|
+
# @!endgroup
|
329
|
+
|
120
330
|
def inspect
|
121
331
|
"#<WordpressClient::Client #{connection.inspect}>"
|
122
332
|
end
|
@@ -1,10 +1,45 @@
|
|
1
1
|
module WordpressClient
|
2
|
-
|
2
|
+
# Base class for all errors emitted from this gem. Rescue this in order to
|
3
|
+
# catch everything.
|
4
|
+
#
|
5
|
+
# See the list of subclasses for more specific error types.
|
6
|
+
class Error < ::StandardError; end
|
3
7
|
|
4
|
-
|
8
|
+
# Raised when the clients attempt to do something that the user isn't
|
9
|
+
# authorized to do.
|
10
|
+
#
|
11
|
+
# This could happen if you try to delete a post and the user only has
|
12
|
+
# read-only access, for example.
|
13
|
+
# It would also happen if you provide bad authentication details.
|
14
|
+
#
|
15
|
+
# @see Error
|
16
|
+
class UnauthorizedError < Error; end
|
5
17
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
18
|
+
# Raised when a request times out.
|
19
|
+
#
|
20
|
+
# @see Error
|
21
|
+
class TimeoutError < Error; end
|
22
|
+
|
23
|
+
# Raised when the server had an error, or when the server returned something
|
24
|
+
# unexpected, despite saying everything went okay. It's the most generic
|
25
|
+
# "Something went wrong with the request" error.
|
26
|
+
#
|
27
|
+
# @see Error
|
28
|
+
class ServerError < Error; end
|
29
|
+
|
30
|
+
# Raised when trying to find a resource that doesn't exist. It will also
|
31
|
+
# happen when you try to update a resource that doesn't exist.
|
32
|
+
#
|
33
|
+
# Lack of authorization can also mask actual resources so it appears that
|
34
|
+
# they don't exist.
|
35
|
+
#
|
36
|
+
# @see Error
|
37
|
+
class NotFoundError < Error; end
|
38
|
+
|
39
|
+
# Raised when the server rejects the body of a request. The error message
|
40
|
+
# will often include information about why the body was rejected, but it is
|
41
|
+
# not guaranteed.
|
42
|
+
#
|
43
|
+
# @see Error
|
44
|
+
class ValidationError < Error; end
|
10
45
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
module WordpressClient
|
2
|
+
# Represents a media record in Wordpress.
|
2
3
|
class Media
|
3
4
|
attr_accessor(
|
4
5
|
:id, :slug, :title_html, :description,
|
@@ -6,10 +7,41 @@ module WordpressClient
|
|
6
7
|
:guid, :link, :media_details
|
7
8
|
)
|
8
9
|
|
10
|
+
# @!attribute [rw] title_html
|
11
|
+
# @return [String] the title of the media, HTML escaped
|
12
|
+
# @example
|
13
|
+
# media.title_html #=> "Sunset — Painting by some person"
|
14
|
+
|
15
|
+
# @!attribute [rw] date
|
16
|
+
# @return [Time, nil] the date of the media, in UTC if available
|
17
|
+
|
18
|
+
# @!attribute [rw] updated_at
|
19
|
+
# @return [Time, nil] the modification date of the media, in UTC if available
|
20
|
+
|
21
|
+
# @!attribute [rw] guid
|
22
|
+
# Returns the permalink/GUID – or +source_url+ – of the media.
|
23
|
+
#
|
24
|
+
# Media that are embedded in posts have a +source_url+ attribute and no
|
25
|
+
# +guid+, and stand-alone media has a +guid+ but no +source_url+. They
|
26
|
+
# are both backed by the same data, so this method handles both cases,
|
27
|
+
# and is aliased to both names.
|
28
|
+
#
|
29
|
+
# @return [String] the permalink/GUID – or +source_url+ – of the media
|
30
|
+
|
31
|
+
# @!attribute [rw] media_details
|
32
|
+
# Returns the media details if available.
|
33
|
+
#
|
34
|
+
# Media details cannot be documented here. It's up to you to handle this
|
35
|
+
# generic "payload" attribute the best way you can.
|
36
|
+
#
|
37
|
+
# @return [Hash<String,Object>] the media details returned from the server
|
38
|
+
|
39
|
+
# @api private
|
9
40
|
def self.parse(data)
|
10
41
|
MediaParser.parse(data)
|
11
42
|
end
|
12
43
|
|
44
|
+
# Creates a new instance, populating the fields with the passed values.
|
13
45
|
def initialize(
|
14
46
|
id: nil,
|
15
47
|
slug: nil,
|
@@ -1,9 +1,31 @@
|
|
1
1
|
require "delegate"
|
2
2
|
|
3
3
|
module WordpressClient
|
4
|
+
# Represents a paginated list of resources.
|
5
|
+
#
|
6
|
+
# @note This class has the full +Array+ interface by using
|
7
|
+
# +DelegateClass(Array)+. Methods do not show up in the documentation
|
8
|
+
# unless when manually documented.
|
4
9
|
class PaginatedCollection < DelegateClass(Array)
|
10
|
+
# @!method size
|
11
|
+
# @return [Fixnum] the number of records actually in this "page".
|
12
|
+
# @see Array#size
|
13
|
+
|
5
14
|
attr_reader :total, :current_page, :per_page
|
6
15
|
|
16
|
+
# @!attribute [r] total
|
17
|
+
# @return [Fixnum] the total hits in the full collection.
|
18
|
+
# @see #size #size is the size of the current "page".
|
19
|
+
|
20
|
+
# @!attribute [r] current_page
|
21
|
+
# @return [Fixnum] the current page number, where +1+ is the first page.
|
22
|
+
|
23
|
+
# @!attribute [r] per_page
|
24
|
+
# @return [Fixnum] the current page size setting, for example +30+.
|
25
|
+
|
26
|
+
# Create a new collection using the passed array +entries+.
|
27
|
+
#
|
28
|
+
# @param entries [Array] the original "page" array
|
7
29
|
def initialize(entries, total:, current_page:, per_page:)
|
8
30
|
super(entries)
|
9
31
|
@total = total
|
@@ -11,12 +33,15 @@ module WordpressClient
|
|
11
33
|
@per_page = per_page
|
12
34
|
end
|
13
35
|
|
14
|
-
#
|
15
|
-
# Pagination methods. Fulfilling will_paginate protocol
|
16
|
-
#
|
36
|
+
# @!group will_paginate protocol
|
17
37
|
|
18
38
|
alias total_entries total
|
19
39
|
|
40
|
+
# @note This method is used by +will_paginate+. By implementing this
|
41
|
+
# interface, you can use a {PaginatedCollection} in place of a
|
42
|
+
# +WillPaginate::Collection+ to render pagination details.
|
43
|
+
# @return [Fixnum] the total number of pages that can show the {#total}
|
44
|
+
# entries with {#per_page} records per page. +0+ if no entries.
|
20
45
|
def total_pages
|
21
46
|
if total.zero? || per_page.zero?
|
22
47
|
0
|
@@ -25,23 +50,53 @@ module WordpressClient
|
|
25
50
|
end
|
26
51
|
end
|
27
52
|
|
53
|
+
# @note This method is used by +will_paginate+. By implementing this
|
54
|
+
# interface, you can use a {PaginatedCollection} in place of a
|
55
|
+
# +WillPaginate::Collection+ to render pagination details.
|
56
|
+
# @return [Fixnum, nil] the next page number or +nil+ if on last page.
|
28
57
|
def next_page
|
29
58
|
if current_page < total_pages
|
30
59
|
current_page + 1
|
31
60
|
end
|
32
61
|
end
|
33
62
|
|
63
|
+
# @note This method is used by +will_paginate+. By implementing this
|
64
|
+
# interface, you can use a {PaginatedCollection} in place of a
|
65
|
+
# +WillPaginate::Collection+ to render pagination details.
|
66
|
+
# @return [Fixnum, nil] the previous page number or +nil+ if on first page.
|
34
67
|
def previous_page
|
35
68
|
if current_page > 1
|
36
69
|
current_page - 1
|
37
70
|
end
|
38
71
|
end
|
39
72
|
|
73
|
+
# @note This method is used by +will_paginate+. By implementing this
|
74
|
+
# interface, you can use a {PaginatedCollection} in place of a
|
75
|
+
# +WillPaginate::Collection+ to render pagination details.
|
76
|
+
# @return [Boolean] if the current page is out of bounds, e.g. less than 1
|
77
|
+
# or higher than {#total_pages}.
|
40
78
|
def out_of_bounds?
|
41
79
|
current_page < 1 || current_page > total_pages
|
42
80
|
end
|
43
81
|
|
44
|
-
# will_paginate
|
82
|
+
# @note This method is used by +will_paginate+. By implementing this
|
83
|
+
# interface, you can use a {PaginatedCollection} in place of a
|
84
|
+
# +WillPaginate::Collection+ to render pagination details.
|
85
|
+
#
|
86
|
+
# @note will_paginate < 3.0 has this method, but it's no longer present in
|
87
|
+
# newer will_paginate.
|
88
|
+
#
|
89
|
+
# Returns the offset of the current page.
|
90
|
+
#
|
91
|
+
# @example First page offset
|
92
|
+
# collection.per_page # => 20
|
93
|
+
# collection.current_page # => 1
|
94
|
+
# collection.offset #=> 0
|
95
|
+
# @example Later offset
|
96
|
+
# collection.per_page # => 20
|
97
|
+
# collection.current_page # => 3
|
98
|
+
# collection.offset #=> 40
|
99
|
+
#
|
45
100
|
def offset
|
46
101
|
if current_page > 0
|
47
102
|
(current_page - 1) * per_page
|
@@ -49,5 +104,7 @@ module WordpressClient
|
|
49
104
|
0
|
50
105
|
end
|
51
106
|
end
|
107
|
+
|
108
|
+
# @!endgroup
|
52
109
|
end
|
53
110
|
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require "time"
|
2
2
|
|
3
3
|
module WordpressClient
|
4
|
+
# Represents a post in Wordpress.
|
5
|
+
#
|
6
|
+
# @see http://v2.wp-api.org/reference/posts/ API documentation for Post
|
4
7
|
class Post
|
5
8
|
attr_accessor(
|
6
9
|
:id, :slug, :url, :guid, :status,
|
@@ -9,10 +12,60 @@ module WordpressClient
|
|
9
12
|
:categories, :tags, :meta, :featured_image
|
10
13
|
)
|
11
14
|
|
15
|
+
# @!attribute [rw] title_html
|
16
|
+
# @return [String] the title of the media, HTML escaped
|
17
|
+
# @example
|
18
|
+
# post.title_html #=> "Fire & diamonds!"
|
19
|
+
|
20
|
+
# @!attribute [rw] date
|
21
|
+
# @return [Time, nil] the date of the post, in UTC if available
|
22
|
+
|
23
|
+
# @!attribute [rw] updated_at
|
24
|
+
# @return [Time, nil] the modification date of the post, in UTC if available
|
25
|
+
|
26
|
+
# @!attribute [rw] guid
|
27
|
+
# @return [String] the permalink/GUID of the post for internal addressing
|
28
|
+
# @see #url
|
29
|
+
|
30
|
+
# @!attribute [rw] url
|
31
|
+
# @return [String] the URL (link) to the post
|
32
|
+
|
33
|
+
# @!attribute [rw] status
|
34
|
+
# @return ["publish", "future", "draft", "pending", "private", nil] the
|
35
|
+
# current status of the post, or +nil+ if undetermined
|
36
|
+
|
37
|
+
# @!attribute [rw] categories
|
38
|
+
# @return [Array[Category]] the {Category Categories} the post belongs to.
|
39
|
+
# @see Category
|
40
|
+
|
41
|
+
# @!attribute [rw] tags
|
42
|
+
# @return [Array[Tag]] the {Tag Tags} the post belongs to.
|
43
|
+
# @see Tag
|
44
|
+
|
45
|
+
# @!attribute [rw] featured_image
|
46
|
+
# @return [Media, nil] the featured image, as an instance of {Media}
|
47
|
+
# @see Media
|
48
|
+
|
49
|
+
# @!attribute [rw] meta
|
50
|
+
# Returns the Post meta, as a +Hash+ of +String => String+.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# post.meta # => {"Mood" => "Happy", "reviewed_by" => "user:45"}
|
54
|
+
#
|
55
|
+
# @return [Hash<String,String>] the post meta, as a Hash.
|
56
|
+
# @see Category
|
57
|
+
# @see Client#update_post
|
58
|
+
|
59
|
+
# @!attribute [rw] meta_ids
|
60
|
+
# @api private
|
61
|
+
# Backs the {#meta_id_for} method. Used when constructing requests.
|
62
|
+
|
63
|
+
# @api private
|
12
64
|
def self.parse(data)
|
13
65
|
PostParser.parse(data)
|
14
66
|
end
|
15
67
|
|
68
|
+
# Construct a new instance with the given attributes.
|
16
69
|
def initialize(
|
17
70
|
id: nil,
|
18
71
|
slug: nil,
|
@@ -47,14 +100,32 @@ module WordpressClient
|
|
47
100
|
@meta_ids = meta_ids
|
48
101
|
end
|
49
102
|
|
103
|
+
# A list of all category ids for the post.
|
104
|
+
#
|
105
|
+
# You can pass this list, with IDs added or removed, to
|
106
|
+
# {Client#update_post} to change the category list.
|
107
|
+
#
|
108
|
+
# @return [Array[Fixnum]] the id of every category associated with this post
|
109
|
+
# @see Client#update_post
|
50
110
|
def category_ids() categories.map(&:id) end
|
51
111
|
|
112
|
+
# A list of all tag ids for the post.
|
113
|
+
#
|
114
|
+
# You can pass this list, with IDs added or removed, to
|
115
|
+
# {Client#update_post} to change the tag list.
|
116
|
+
#
|
117
|
+
# @return [Array[Fixnum]] the id of every tag associated with this post
|
118
|
+
# @see Client#update_post
|
52
119
|
def tag_ids() tags.map(&:id) end
|
53
120
|
|
121
|
+
# @return [Fixnum, nil] ID of the featured image associated with the post.
|
54
122
|
def featured_image_id
|
55
123
|
featured_image && featured_image.id
|
56
124
|
end
|
57
125
|
|
126
|
+
# @api private
|
127
|
+
# Used to determine the underlying ID of the different meta keys so they
|
128
|
+
# can be modified by {Client}. You should not use this for anything.
|
58
129
|
def meta_id_for(key)
|
59
130
|
@meta_ids[key] || raise(ArgumentError, "Post does not have meta #{key.inspect}")
|
60
131
|
end
|
data/lib/wordpress_client/tag.rb
CHANGED
@@ -1,7 +1,27 @@
|
|
1
1
|
module WordpressClient
|
2
|
+
# @abstract Implement a subclass for the resource type.
|
3
|
+
#
|
4
|
+
# Implementation for the abstract "term" in Wordpress.
|
5
|
+
#
|
6
|
+
# @see Category
|
7
|
+
# @see Tag
|
2
8
|
class Term
|
3
9
|
attr_reader :id, :name_html, :slug
|
4
10
|
|
11
|
+
# @!attribute [r] id
|
12
|
+
# @return [Fixnum] The ID of the resource in Wordpress.
|
13
|
+
|
14
|
+
# @!attribute [r] name_html
|
15
|
+
# @return [String] The name of the resource, HTML encoded.
|
16
|
+
# @example
|
17
|
+
# term.name_html #=> "Father & Daughter stuff"
|
18
|
+
|
19
|
+
# @!attribute [r] slug
|
20
|
+
# @return [String] The slug of the resource in Wordpress.
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
# Parses a data structure from a WP API response body into this term type.
|
5
25
|
def self.parse(data)
|
6
26
|
new(
|
7
27
|
id: data.fetch("id"),
|
@@ -16,6 +36,15 @@ module WordpressClient
|
|
16
36
|
@slug = slug
|
17
37
|
end
|
18
38
|
|
39
|
+
# @api private
|
40
|
+
# Compares another instance. All attributes in this list must be equal for
|
41
|
+
# the instances to be equal:
|
42
|
+
#
|
43
|
+
# * +id+
|
44
|
+
# * +name_html+
|
45
|
+
# * +slug+
|
46
|
+
#
|
47
|
+
# One must also not be a subclass of the other; they must be the exact same class.
|
19
48
|
def ==(other)
|
20
49
|
if other.is_a? Term
|
21
50
|
other.class == self.class &&
|
@@ -27,6 +56,7 @@ module WordpressClient
|
|
27
56
|
end
|
28
57
|
end
|
29
58
|
|
59
|
+
# Shows a nice representation of the term type.
|
30
60
|
def inspect
|
31
61
|
"#<#{self.class} ##{id} #{name_html.inspect} (#{slug})>"
|
32
62
|
end
|
data/spec/client_spec.rb
CHANGED
@@ -66,7 +66,7 @@ module WordpressClient
|
|
66
66
|
Post, "posts", hash_including(filter: {name: "my-slug"})
|
67
67
|
).and_return [post]
|
68
68
|
|
69
|
-
expect(client.
|
69
|
+
expect(client.find_post_by_slug("my-slug")).to eq post
|
70
70
|
end
|
71
71
|
|
72
72
|
it "raises NotFoundError when trying to find by slug yields no posts" do
|
@@ -75,7 +75,7 @@ module WordpressClient
|
|
75
75
|
).and_return []
|
76
76
|
|
77
77
|
expect {
|
78
|
-
client.
|
78
|
+
client.find_post_by_slug("my-slug")
|
79
79
|
}.to raise_error(NotFoundError, /my-slug/)
|
80
80
|
end
|
81
81
|
end
|
data/spec/docker/Dockerfile
CHANGED
@@ -13,11 +13,14 @@ ENV APP_USER admin
|
|
13
13
|
ENV APP_PASS P@ssw0rd
|
14
14
|
ENV WP_KEY ILoveFlappyjacks
|
15
15
|
|
16
|
+
# Make pretty permalinks work so API is accessible
|
17
|
+
COPY htaccess /var/www/html/wordpress/.htaccess
|
18
|
+
|
16
19
|
# Restore a copy of a set up database on first boot
|
17
20
|
COPY dbdump.sql.gz /tmp/dbdump.sql.gz
|
18
21
|
COPY restore-dbdump.sh /tmp/.restore-dbdump.sh
|
19
22
|
RUN chmod +x /tmp/.restore-dbdump.sh && \
|
20
|
-
echo "if [ -f /tmp/.restore-dbdump.sh ]; then /tmp/.restore-dbdump.sh; rm
|
23
|
+
echo "if [ -f /tmp/.restore-dbdump.sh ]; then /tmp/.restore-dbdump.sh; rm /tmp/.restore-dbdump.sh; fi" >> /root/.bashrc
|
21
24
|
|
22
25
|
### Install API and API Basic Auth plugins
|
23
26
|
|
@@ -35,6 +38,3 @@ RUN curl -SL -o /tmp/rest-api.zip https://downloads.wordpress.org/plugin/rest-ap
|
|
35
38
|
RUN curl -SL https://github.com/WP-API/Basic-Auth/archive/master.tar.gz \
|
36
39
|
| tar -xzC /var/www/html/wordpress/wp-content/plugins/ \
|
37
40
|
&& mv /var/www/html/wordpress/wp-content/plugins/Basic-Auth* /var/www/html/wordpress/wp-content/plugins/basic-auth
|
38
|
-
|
39
|
-
# Make pretty permalinks work so API is accessible
|
40
|
-
COPY htaccess /var/www/html/wordpress/.htaccess
|
data/spec/docker/README.md
CHANGED
@@ -1,15 +1,47 @@
|
|
1
|
-
#
|
1
|
+
# Docker image for WordpressClient
|
2
2
|
|
3
|
-
|
3
|
+
Use this docker image to run integration tests, either in this repo or in some client repo where you use the gem.
|
4
4
|
|
5
|
-
|
5
|
+
**Note:** This image is not meant for *any* production use. It's meant for integration tests, and nothing else.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
You need to publish the container's port 80 to whichever port you want your instance on, then set an environment variable for where the server should be accessible from as Wordpress needs to have this stored in the database.
|
10
|
+
|
11
|
+
You also need to run it with an interactive terminal in order for it to work.
|
12
|
+
|
13
|
+
```bash
|
14
|
+
docker run -dit -p 8080:80 -e WORDPRESS_HOST=localhost:8080 hemnet/wordpress_client_test:latest
|
15
|
+
```
|
16
|
+
|
17
|
+
I you want to see the ouput or access the environment in order to debug an issue, omit the `-d` option. It will boot into a `bash` shell with Apache running in the background. You can also use `docker attach` to attach to a running instance.
|
18
|
+
|
19
|
+
**Note:** When you exit a running container, everything stored in it will be kept on disk. It is recommended that you `docker rm` the image when you are done to clean up.
|
20
|
+
|
21
|
+
When the instance is up and running, you can log in as an administrator using `test`/`test` as username and password.
|
22
|
+
|
23
|
+
## Contents
|
24
|
+
|
25
|
+
This image is based on Appcontainer's Wordpress image, which runs Apache, MySQL and PHP5 on CentOS using bash. In addition, a DB dump is restored containing a set up blog so you don't need to do the first installation steps.
|
26
|
+
|
27
|
+
* Username `test`
|
28
|
+
* Password `test`
|
29
|
+
* Plugin `WP-API` is installed
|
30
|
+
* Plugin `Basic-Auth` for `WP-API` installed
|
31
|
+
* `.htaccess` for Pretty Permalinks is set up so the API works
|
32
|
+
|
33
|
+
## Developing the image
|
34
|
+
|
35
|
+
### Re-creating DB dump from scratch
|
36
|
+
|
37
|
+
If you need to regenerate the DB dump, remove the part where the file is copied and restored from the `Dockerfile`, then build and start the image.
|
6
38
|
|
7
39
|
```bash
|
8
|
-
docker build -t
|
9
|
-
docker run -it -p 8181:80
|
40
|
+
docker build -t wordpress_client_test:dev .
|
41
|
+
docker run -it -p 8181:80 wordpress_client_test:dev
|
10
42
|
```
|
11
43
|
|
12
|
-
You'll get a
|
44
|
+
You'll get a `bash` shell inside the container. Open your browser and go to the newly booted application (`localhost:8181` if you have native Docker; otherwise go to your `DOCKER_HOST_IP:8181` URL – see `echo $DOCKER_HOST` in your local shell).
|
13
45
|
|
14
46
|
You'll be greeted by the Wordpress installer. Fill in everything and complete the installation.
|
15
47
|
|
@@ -22,16 +54,11 @@ mysqldump -u "$MYSQL_USER" --password="$MYSQL_PASS" --host="$MYSQL_HOST" "$MYSQL
|
|
22
54
|
You can then copy the file to your host using the `docker cp` command:
|
23
55
|
|
24
56
|
```bash
|
25
|
-
docker ps | grep
|
57
|
+
docker ps | grep wordpress_client_test:dev
|
26
58
|
# See the container ID or name of your running container
|
27
59
|
docker cp THE-CONTAINER-ID:/tmp/dbdump.sql.gz .
|
28
60
|
```
|
29
61
|
|
30
|
-
|
31
|
-
|
32
|
-
Restore the DB loading code from the `Dockerfile`, commit this file to the repo and rebuild everything and verify that Wordpress is still set up correctly when the dump is restored.
|
62
|
+
Last step is to update `restore-dbdump.sh` to replace the hard-coded `WORDPRESS_HOST` placeholder with the one you used to generate the DB dump with. This corresponds with `localhost:8181` with native Docker if you followed these commands exactly. If you use `docker-machine`, you need to use your machine IP instead. (See *"Usage"* above)
|
33
63
|
|
34
|
-
|
35
|
-
datadump is a specfic IP. So you will need to change that hard coded
|
36
|
-
expectation to match the new IP used in your data dump. You can find out what
|
37
|
-
that IP is by examining the dumo but it is probably the value set in `$DOCKER_HOST`.
|
64
|
+
You can then shut down your container by logging out of the container terminal. Restore the DB loading code from the `Dockerfile`, commit this file to the repo and rebuild everything and verify that Wordpress is still set up correctly when the dump is restored.
|
@@ -64,13 +64,13 @@ describe "Posts (finding)" do
|
|
64
64
|
describe "finding by slug" do
|
65
65
|
it "finds the matching post" do
|
66
66
|
post = client.create_post(title: "Oh hai", slug: "oh-hai")
|
67
|
-
found = client.
|
67
|
+
found = client.find_post_by_slug("oh-hai")
|
68
68
|
expect(found.id).to eq post.id
|
69
69
|
end
|
70
70
|
|
71
71
|
it "raises NotFoundError when no post can be found" do
|
72
72
|
expect {
|
73
|
-
client.
|
73
|
+
client.find_post_by_slug("clearly-does-not-exist-anywhere")
|
74
74
|
}.to raise_error(WordpressClient::NotFoundError, /clearly/)
|
75
75
|
end
|
76
76
|
|
@@ -79,7 +79,7 @@ describe "Posts (finding)" do
|
|
79
79
|
title: "Updated title that doesn't match slug",
|
80
80
|
slug: "original-concise-title",
|
81
81
|
)
|
82
|
-
found = client.
|
82
|
+
found = client.find_post_by_slug("original-concise-title")
|
83
83
|
expect(found.id).to eq post.id
|
84
84
|
end
|
85
85
|
end
|
@@ -9,11 +9,15 @@ module DockerRunner
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def image_exists?(name)
|
12
|
-
|
12
|
+
name, tag = name.split(":", 2)
|
13
|
+
matcher = "^#{name} "
|
14
|
+
matcher << "[[:space:]]*#{tag}" if tag
|
15
|
+
|
16
|
+
system("docker images | grep -q #{matcher.shellescape}")
|
13
17
|
end
|
14
18
|
|
15
19
|
def build_image(name, path: Dir.pwd)
|
16
|
-
system("
|
20
|
+
system("docker build -t #{name.shellescape} #{path.shellescape}")
|
17
21
|
end
|
18
22
|
|
19
23
|
def run_container(name, port:, environment: {})
|
@@ -24,6 +24,8 @@ class WordpressServer
|
|
24
24
|
def password() "test" end
|
25
25
|
|
26
26
|
private
|
27
|
+
DOCKER_IMAGE_NAME = "hemnet/wordpress_client_test:dev".freeze
|
28
|
+
|
27
29
|
def host_with_port
|
28
30
|
"#{host}:#{port}"
|
29
31
|
end
|
@@ -62,14 +64,14 @@ class WordpressServer
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def build_container_if_missing
|
65
|
-
unless DockerRunner.image_exists?(
|
66
|
-
DockerRunner.build_image(
|
67
|
+
unless DockerRunner.image_exists?(DOCKER_IMAGE_NAME)
|
68
|
+
DockerRunner.build_image(DOCKER_IMAGE_NAME, path: "spec/docker")
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
72
|
def start_container
|
71
73
|
DockerRunner.run_container(
|
72
|
-
|
74
|
+
DOCKER_IMAGE_NAME,
|
73
75
|
port: port,
|
74
76
|
environment: {wordpress_host: host_with_port}
|
75
77
|
)
|
data/wordpress_client.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wordpress_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Magnus Bergmark
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-12-
|
12
|
+
date: 2015-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -81,6 +81,20 @@ dependencies:
|
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '1.22'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: yard
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.8.7
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.8.7
|
84
98
|
description: A simple client to the Wordpress API.
|
85
99
|
email:
|
86
100
|
- magnus.bergmark@gmail.com
|
@@ -89,10 +103,13 @@ executables: []
|
|
89
103
|
extensions: []
|
90
104
|
extra_rdoc_files: []
|
91
105
|
files:
|
106
|
+
- ".codeclimate.yml"
|
92
107
|
- ".gitignore"
|
93
108
|
- ".hound.yml"
|
94
109
|
- ".rubocop.yml"
|
95
110
|
- ".ruby-version"
|
111
|
+
- ".yardopts"
|
112
|
+
- Changelog.md
|
96
113
|
- Gemfile
|
97
114
|
- Guardfile
|
98
115
|
- LICENSE
|
@@ -217,3 +234,4 @@ test_files:
|
|
217
234
|
- spec/support/integration_macros.rb
|
218
235
|
- spec/support/wordpress_server.rb
|
219
236
|
- spec/tag_spec.rb
|
237
|
+
has_rdoc:
|