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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c7e933e59b738cfcda010915591ffe89e236651
4
- data.tar.gz: 0947478b9a9a5e8d706f6d196d4755bbfd6988d3
3
+ metadata.gz: e3508c942b368912bd0f8e1138811baacae21e75
4
+ data.tar.gz: 4f7171f31d4fc98732538d4ff8efd6ebe35e65a8
5
5
  SHA512:
6
- metadata.gz: a7715ef9b24771c3a73d4be1d93e9f8e873d909b0aa92442d7bbe4e706202c8b423287bbe479ba6ba899626f9fd75e11937a5c3ce33b1784bbd41a743ed7ad60
7
- data.tar.gz: df97bcf0b81ec75e4ab068d09ce597eb273fa780f60fbbf1ee82568b4ae087c144a3c8a30024ba548b6b219a92edfc6dda365b71e2917a28f7906b0954ddbd2d
6
+ metadata.gz: 9bc1d22e6c16402eec2f778178d0bd3048586979037a6e18342f39572e6f89de915c530b4ab7e019dd57eaf9cc17dd5cf4cd1b1c08fac716f498ca0a8b531f29
7
+ data.tar.gz: 53f5a0c7ee0c419e358baf6a72dcfe5c09a7c77b6508a377515e2fcd6a9d60ec90d2a2dee123238cb1a5ef3b3e32316ad313e80728af5174ff1742769f28cfe4
@@ -3,8 +3,11 @@ rvm:
3
3
  - 2.0.0
4
4
  - 2.1.0
5
5
  - 2.2.0
6
+ - 2.3.0
6
7
  env:
7
8
  - TZ=Asia/Tokyo
8
9
  addons:
9
10
  code_climate:
10
11
  repo_token: 309cf0784d00d2a6009566d28be111a8a0280cdeb2da280225eedf577b16beb5
12
+ after_success:
13
+ - bundle exec codeclimate-test-reporter
data/Gemfile CHANGED
@@ -1,6 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem "codeclimate-test-reporter", group: :test, require: nil
3
+ group :test do
4
+ gem 'simplecov'
5
+ gem 'codeclimate-test-reporter', '~> 1.0.0'
6
+ end
4
7
 
5
8
  # Specify your gem's dependencies in hatenablog.gemspec
6
9
  gemspec
data/README.md CHANGED
@@ -4,8 +4,9 @@
4
4
  [![Code Climate](https://codeclimate.com/github/kymmt90/hatenablog/badges/gpa.svg)](https://codeclimate.com/github/kymmt90/hatenablog)
5
5
  [![Test Coverage](https://codeclimate.com/github/kymmt90/hatenablog/badges/coverage.svg)](https://codeclimate.com/github/kymmt90/hatenablog/coverage)
6
6
 
7
- A library for Hatenablog AtomPub API.
8
- This gem supports following operations using OAuth or Basic authorization:
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 keys and tokens
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
- ### (OPTIONAL) Get Basic Authentication credentials
74
- If you want to use Basic Authentication, visit `http://blog.hatena.ne.jp/#{user_id}/#{blog_id}/config/detail`
75
- and check your API key (APIキー) and set up `config.yml` like the following.
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
@@ -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 array.
51
+ # Get a enumerator of blog entries.
42
52
  # @param [Fixnum] page page number to get
43
- # @return [Array] blog entries
53
+ # @return [Hatenablog::Entries] enumerator of blog entries
44
54
  def entries(page = 0)
45
- feed = Feed.load_xml(get_collection(collection_uri).body)
46
- entries = feed.entries
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
- entries
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 = %w(consumer_key consumer_secret access_token access_token_secret user_id blog_id)
8
- BASIC_KEYS = %w(api_key user_id blog_id)
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
- config = YAML.load(ERB.new(File.read(config_file)).result)
15
- keys = config['auth_type'] == 'basic' ? BASIC_KEYS : OAUTH_KEYS
16
- unless (lacking_keys = keys.select {|key| !config.has_key? key}).empty?
17
- raise ConfigurationError, "Following keys are not setup. #{lacking_keys}"
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
- new(config)
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
 
@@ -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
- begin
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
- body = '',
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
- body = '',
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
- headers = { 'Content-Type' => 'application/atom+xml; type=entry' })
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
- response = @access_token.delete(uri, headers)
64
+ @access_token.send(method, *[uri, body, headers].compact)
76
65
  rescue => problem
77
- raise RequestError, 'Fail to DELETE: ' + problem.to_s
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 = 'application/atom+xml; type=entry'
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)
@@ -1,3 +1,3 @@
1
1
  module Hatenablog
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
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.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-10-09 00:00:00.000000000 Z
11
+ date: 2016-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler