hatenablog 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +4 -1
- data/README.md +124 -7
- data/lib/hatenablog/client.rb +36 -18
- data/lib/hatenablog/configuration.rb +18 -7
- data/lib/hatenablog/requester.rb +18 -30
- data/lib/hatenablog/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3508c942b368912bd0f8e1138811baacae21e75
|
4
|
+
data.tar.gz: 4f7171f31d4fc98732538d4ff8efd6ebe35e65a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bc1d22e6c16402eec2f778178d0bd3048586979037a6e18342f39572e6f89de915c530b4ab7e019dd57eaf9cc17dd5cf4cd1b1c08fac716f498ca0a8b531f29
|
7
|
+
data.tar.gz: 53f5a0c7ee0c419e358baf6a72dcfe5c09a7c77b6508a377515e2fcd6a9d60ec90d2a2dee123238cb1a5ef3b3e32316ad313e80728af5174ff1742769f28cfe4
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,8 +4,9 @@
|
|
4
4
|
[](https://codeclimate.com/github/kymmt90/hatenablog)
|
5
5
|
[](https://codeclimate.com/github/kymmt90/hatenablog/coverage)
|
6
6
|
|
7
|
-
A library for
|
8
|
-
|
7
|
+
> A library for Hatena Blog AtomPub API
|
8
|
+
|
9
|
+
This gem supports following operations through OAuth 1.0a or Basic authentication:
|
9
10
|
|
10
11
|
- Get blog feeds, entries and categories
|
11
12
|
- Post blog entries
|
@@ -28,7 +29,7 @@ Or install it yourself as:
|
|
28
29
|
|
29
30
|
$ gem install hatenablog
|
30
31
|
|
31
|
-
### Get OAuth
|
32
|
+
### Get OAuth credentials
|
32
33
|
|
33
34
|
You need to set up OAuth 1.0a keys and tokens before using this gem.
|
34
35
|
|
@@ -46,7 +47,7 @@ Execute this command:
|
|
46
47
|
Access token: <your access token>
|
47
48
|
Access token secret: <your access token secret>
|
48
49
|
|
49
|
-
#### 3. Set up the YAML configuration file
|
50
|
+
#### 3. [Optional] Set up the YAML configuration file
|
50
51
|
|
51
52
|
The default configuration file name is `config.yml`:
|
52
53
|
|
@@ -70,9 +71,13 @@ user_id: <%= ENV['USER_ID'] %>
|
|
70
71
|
blog_id: <%= ENV['BLOG_ID'] %>
|
71
72
|
```
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
`blog_id` means your Hatena Blog domain, like "example-user.hatenablog.com".
|
75
|
+
|
76
|
+
You also can set these configurations in your code as described in [the below section](#factories).
|
77
|
+
|
78
|
+
### [Optional] Get Basic authentication credentials
|
79
|
+
If you want to use Basic authentication, visit `http://blog.hatena.ne.jp/#{user_id}/#{blog_id}/config/detail`
|
80
|
+
and check your API key and set up `config.yml` like the following.
|
76
81
|
|
77
82
|
```yml
|
78
83
|
auth_type: basic
|
@@ -108,3 +113,115 @@ Hatenablog::Client.create do |blog|
|
|
108
113
|
blog.delete_entry(updated_entry.id)
|
109
114
|
end
|
110
115
|
```
|
116
|
+
|
117
|
+
## API
|
118
|
+
|
119
|
+
### Factories
|
120
|
+
|
121
|
+
You can create the client from the configuration file.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# Create the client from "./config.yml"
|
125
|
+
client = Hatenablog::Client.create
|
126
|
+
|
127
|
+
# Create the client from the specified configuration
|
128
|
+
client = Hatenablog::Client.create('../another_config.yml')
|
129
|
+
|
130
|
+
Hatenablog::Client.create do |client|
|
131
|
+
# Use the client in the block
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
You can also create the client with a block for configurations.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
client = Hatenablog::Client.new do |config|
|
139
|
+
config.consumer_key = 'XXXXXXXXXXXXXXXXXXXX'
|
140
|
+
config.consumer_secret = 'XXXXXXXXXXXXXXXXXXXX'
|
141
|
+
config.access_token = 'XXXXXXXXXXXXXXXXXXXX'
|
142
|
+
config.access_token_secret = 'XXXXXXXXXXXXXXXXXXXX'
|
143
|
+
config.user_id = 'example-user'
|
144
|
+
config.blog_id = 'example-user.hatenablog.com'
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
### Blog
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
client.title # Get the blog title
|
152
|
+
client.author_name # Get the blog author name
|
153
|
+
```
|
154
|
+
|
155
|
+
### Feeds
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
feed = client.next_feed # Get the first feed when no argument is passed
|
159
|
+
feed.uri
|
160
|
+
feed.next_uri # The next feed URI
|
161
|
+
feed.title
|
162
|
+
feed.author_name
|
163
|
+
feed.update # Updated datetime
|
164
|
+
|
165
|
+
feed.entries # entries in the feed
|
166
|
+
feed.each_entry do |entry|
|
167
|
+
# ...
|
168
|
+
end
|
169
|
+
feed.has_next? # true if the next page exists
|
170
|
+
next_feed = client.next_feed(feed)
|
171
|
+
```
|
172
|
+
|
173
|
+
### Entries
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
client.get_entry('0000000000000000000') # Get the entry specifed by its ID
|
177
|
+
client.entries # Get blog entries in the first page
|
178
|
+
client.entries(1) # Get blog entries in the first and the second page
|
179
|
+
client.all_entries # Get all entries in the blog
|
180
|
+
|
181
|
+
entry = client.post_entry('Example Title', # title
|
182
|
+
'This is the **example** entry.', # content
|
183
|
+
['Ruby', 'Rails'], # categories
|
184
|
+
'yes' # draft
|
185
|
+
)
|
186
|
+
entry.id
|
187
|
+
entry.uri
|
188
|
+
entry.edit_uri
|
189
|
+
entry.author_name
|
190
|
+
entry.title #=> 'Example Title'
|
191
|
+
entry.content #=> 'This is the **example** entry.'
|
192
|
+
entry.draft #=> 'yes'
|
193
|
+
entry.draft? #=> true
|
194
|
+
entry.categories #=> ['Ruby', 'Rails']
|
195
|
+
entry.updated # Updated datetime
|
196
|
+
|
197
|
+
client.update_entry(entry.id,
|
198
|
+
entry.title,
|
199
|
+
'This is the **modified** example entry.',
|
200
|
+
entry.categories,
|
201
|
+
'no')
|
202
|
+
client.delete_entry(entry.id)
|
203
|
+
```
|
204
|
+
|
205
|
+
### Categories
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
categories = client.categories # Get categories registered in the blog
|
209
|
+
categories.each do |cat|
|
210
|
+
puts cat
|
211
|
+
end
|
212
|
+
|
213
|
+
# When categories are fixed, you can only use those categories for your entries.
|
214
|
+
# ref: https://tools.ietf.org/html/rfc5023#section-7.2.1.1
|
215
|
+
categories.fixed?
|
216
|
+
```
|
217
|
+
|
218
|
+
## Contributing
|
219
|
+
|
220
|
+
1. Create your feature branch (`git checkout -b my-new-feature`)
|
221
|
+
2. Commit your changes (`git commit -am 'Add some feature'`)
|
222
|
+
3. Push to the branch (`git push origin my-new-feature`)
|
223
|
+
4. Create a new Pull Request
|
224
|
+
|
225
|
+
## License
|
226
|
+
|
227
|
+
MIT
|
data/lib/hatenablog/client.rb
CHANGED
@@ -24,6 +24,16 @@ module Hatenablog
|
|
24
24
|
yield blog
|
25
25
|
end
|
26
26
|
|
27
|
+
def initialize(config = nil)
|
28
|
+
if block_given?
|
29
|
+
yield config = Configuration.new
|
30
|
+
config.check_valid_or_raise
|
31
|
+
end
|
32
|
+
@requester = Requester.create(config)
|
33
|
+
@user_id = config.user_id
|
34
|
+
@blog_id = config.blog_id
|
35
|
+
end
|
36
|
+
|
27
37
|
# Get a blog title.
|
28
38
|
# @return [String] blog title
|
29
39
|
def title
|
@@ -38,20 +48,18 @@ module Hatenablog
|
|
38
48
|
feed.author_name
|
39
49
|
end
|
40
50
|
|
41
|
-
# Get blog entries
|
51
|
+
# Get a enumerator of blog entries.
|
42
52
|
# @param [Fixnum] page page number to get
|
43
|
-
# @return [
|
53
|
+
# @return [Hatenablog::Entries] enumerator of blog entries
|
44
54
|
def entries(page = 0)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
(1..page).each do
|
49
|
-
feed = next_feed(feed)
|
50
|
-
break if feed.nil?
|
51
|
-
entries += feed.entries
|
52
|
-
end
|
55
|
+
raise ArgumentError.new('page must be non-negative') if page < 0
|
56
|
+
Entries.new(self, page)
|
57
|
+
end
|
53
58
|
|
54
|
-
|
59
|
+
# Get all blog entries.
|
60
|
+
# @return [Hatenablog::Entries] enumerator of blog entries
|
61
|
+
def all_entries
|
62
|
+
Entries.new(self, nil)
|
55
63
|
end
|
56
64
|
|
57
65
|
# Get the next feed of the given feed.
|
@@ -166,15 +174,8 @@ module Hatenablog
|
|
166
174
|
builder.to_xml
|
167
175
|
end
|
168
176
|
|
169
|
-
|
170
177
|
private
|
171
178
|
|
172
|
-
def initialize(config)
|
173
|
-
@requester = Requester.create(config)
|
174
|
-
@user_id = config.user_id
|
175
|
-
@blog_id = config.blog_id
|
176
|
-
end
|
177
|
-
|
178
179
|
def get(uri)
|
179
180
|
@requester.get(uri)
|
180
181
|
end
|
@@ -202,4 +203,21 @@ module Hatenablog
|
|
202
203
|
@requester.delete(uri)
|
203
204
|
end
|
204
205
|
end
|
206
|
+
|
207
|
+
class Entries
|
208
|
+
include Enumerable
|
209
|
+
|
210
|
+
def initialize(client, page = 0)
|
211
|
+
@client = client
|
212
|
+
@page = page
|
213
|
+
end
|
214
|
+
|
215
|
+
def each
|
216
|
+
current_page = 0
|
217
|
+
until (@page && current_page > @page) || !(feed = @client.next_feed(feed))
|
218
|
+
feed.entries.each { |entry| yield entry }
|
219
|
+
current_page += 1
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
205
223
|
end
|
@@ -4,20 +4,31 @@ require 'ostruct'
|
|
4
4
|
|
5
5
|
module Hatenablog
|
6
6
|
class Configuration < OpenStruct
|
7
|
-
OAUTH_KEYS = %
|
8
|
-
BASIC_KEYS = %
|
7
|
+
OAUTH_KEYS = %i(consumer_key consumer_secret access_token access_token_secret user_id blog_id)
|
8
|
+
BASIC_KEYS = %i(api_key user_id blog_id)
|
9
9
|
|
10
10
|
# Create a new configuration.
|
11
11
|
# @param [String] config_file configuration file path
|
12
12
|
# @return [Hatenablog::Configuration]
|
13
13
|
def self.create(config_file)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
loaded_config = YAML.load(ERB.new(File.read(config_file)).result)
|
15
|
+
config = new(loaded_config)
|
16
|
+
config.check_valid_or_raise
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_valid_or_raise
|
20
|
+
unless (lacking_keys = self.send(:lacking_keys)).empty?
|
21
|
+
raise ConfigurationError, "Following keys are not setup. #{lacking_keys.map(&:to_s)}"
|
18
22
|
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
19
27
|
|
20
|
-
|
28
|
+
def lacking_keys
|
29
|
+
required_keys = self['auth_type'] == 'basic' ? BASIC_KEYS : OAUTH_KEYS
|
30
|
+
config_keys = to_h.keys
|
31
|
+
required_keys.select { |key| !config_keys.include?(key) }
|
21
32
|
end
|
22
33
|
end
|
23
34
|
|
data/lib/hatenablog/requester.rb
CHANGED
@@ -3,6 +3,9 @@ require 'oauth'
|
|
3
3
|
|
4
4
|
module Hatenablog
|
5
5
|
module Requester
|
6
|
+
ATOM_CONTENT_TYPE = 'application/atom+xml; type=entry'.freeze
|
7
|
+
DEFAULT_HEADER = { 'Content-Type' => ATOM_CONTENT_TYPE }
|
8
|
+
|
6
9
|
class RequestError < StandardError; end
|
7
10
|
|
8
11
|
def self.create(config)
|
@@ -25,12 +28,7 @@ module Hatenablog
|
|
25
28
|
# @param [string] uri target URI
|
26
29
|
# @return [Net::HTTPResponse] HTTP response
|
27
30
|
def get(uri)
|
28
|
-
|
29
|
-
response = @access_token.get(uri)
|
30
|
-
rescue => problem
|
31
|
-
raise RequestError, 'Fail to GET: ' + problem.to_s
|
32
|
-
end
|
33
|
-
response
|
31
|
+
request(:get, uri)
|
34
32
|
end
|
35
33
|
|
36
34
|
# HTTP POST method
|
@@ -38,15 +36,8 @@ module Hatenablog
|
|
38
36
|
# @param [string] body HTTP request body
|
39
37
|
# @param [string] headers HTTP request headers
|
40
38
|
# @return [Net::HTTPResponse] HTTP response
|
41
|
-
def post(uri,
|
42
|
-
|
43
|
-
headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
|
44
|
-
begin
|
45
|
-
response = @access_token.post(uri, body, headers)
|
46
|
-
rescue => problem
|
47
|
-
raise RequestError, 'Fail to POST: ' + problem.to_s
|
48
|
-
end
|
49
|
-
response
|
39
|
+
def post(uri, body = '', headers = DEFAULT_HEADER)
|
40
|
+
request(:post, uri, body: body, headers: headers)
|
50
41
|
end
|
51
42
|
|
52
43
|
# HTTP PUT method
|
@@ -54,29 +45,26 @@ module Hatenablog
|
|
54
45
|
# @param [string] body HTTP request body
|
55
46
|
# @param [string] headers HTTP request headers
|
56
47
|
# @return [Net::HTTPResponse] HTTP response
|
57
|
-
def put(uri,
|
58
|
-
|
59
|
-
headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
|
60
|
-
begin
|
61
|
-
response = @access_token.put(uri, body, headers)
|
62
|
-
rescue => problem
|
63
|
-
raise RequestError, 'Fail to PUT: ' + problem.to_s
|
64
|
-
end
|
65
|
-
response
|
48
|
+
def put(uri, body = '', headers = DEFAULT_HEADER)
|
49
|
+
request(:put, uri, body: body, headers: headers)
|
66
50
|
end
|
67
51
|
|
68
52
|
# HTTP DELETE method
|
69
53
|
# @param [string] uri target URI
|
70
54
|
# @param [string] headers HTTP request headers
|
71
55
|
# @return [Net::HTTPResponse] HTTP response
|
72
|
-
def delete(uri,
|
73
|
-
|
56
|
+
def delete(uri, headers = DEFAULT_HEADER)
|
57
|
+
request(:delete, uri, headers: headers)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def request(method, uri, body: nil, headers: nil)
|
74
63
|
begin
|
75
|
-
|
64
|
+
@access_token.send(method, *[uri, body, headers].compact)
|
76
65
|
rescue => problem
|
77
|
-
raise RequestError,
|
66
|
+
raise RequestError, "Fail to #{method.upcase}: " + problem.to_s
|
78
67
|
end
|
79
|
-
response
|
80
68
|
end
|
81
69
|
end
|
82
70
|
|
@@ -136,7 +124,7 @@ module Hatenablog
|
|
136
124
|
req.basic_auth @user_id, @api_key
|
137
125
|
if body
|
138
126
|
req.body = body
|
139
|
-
req.content_type =
|
127
|
+
req.content_type = ATOM_CONTENT_TYPE
|
140
128
|
end
|
141
129
|
|
142
130
|
http = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.port == 443)
|
data/lib/hatenablog/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hatenablog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kohei Yamamoto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|