hatenablog 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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