hatenablog 0.1.0 → 0.2.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: 0b71f66f0d8e827aa61f9c9298682f0a1a9c5de8
4
- data.tar.gz: 2d52e581da4e418ddae1877409b8826e46491e10
3
+ metadata.gz: cf376e1c7bc165c96b04d31cdca1c7ad519eee6d
4
+ data.tar.gz: 47dbbe4a2e6e7efff7208a418424a224e74a2ee0
5
5
  SHA512:
6
- metadata.gz: 0f4dbffdfb7822e0ccd725a9a3813638b69fb01037bb098bd839a95c304d40987ec731172f7adb59d020f9e57e5152573a9dd36bcfa81a87c3d248b4ca04aa6a
7
- data.tar.gz: f18c984318d3d9991ac17dc1c2ede1b44beb42d87638ebecda60a92faefee249b8a14a4e1785245e42ef5d0993884bd3957b0f1d803cd230ac992e5fd97300fe
6
+ metadata.gz: 1a20f773e2c91639daa1f3c446edcaae4206f83f547ae552381afbd3d6912565b75d87f2dd8cdae3ee7bed4bd773b81f4c97f2319e5041d924a263fc6d8e4d2a
7
+ data.tar.gz: a839ae31b99284ea6b40e9d736d1375afa90ccd66fcabb872decdb5ad727fd838ee46daaabb507ca702840b2a35b024e0f8cc1d561f18db94b0bf0039051b1a9
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.2.0
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Hatenablog
2
2
 
3
3
  A library for Hatenablog AtomPub API.
4
- This library supports following operations through OAuth authorization:
4
+ This gem supports following operations using OAuth authorization:
5
5
 
6
6
  - Get blog feeds, entries and categories
7
7
  - Post blog entries
@@ -26,7 +26,7 @@ Or install it yourself as:
26
26
 
27
27
  ### Get OAuth keys and tokens
28
28
 
29
- You need to set up OAuth 1.0a keys and tokens before using the gem.
29
+ You need to set up OAuth 1.0a keys and tokens before using this gem.
30
30
 
31
31
  #### 1. Get consumer key and consumer key secret
32
32
 
@@ -44,7 +44,7 @@ Execute this command:
44
44
 
45
45
  #### 3. Set up the YAML configuration file
46
46
 
47
- The default file name is `conf.yml`:
47
+ The default configuration file name is `conf.yml`:
48
48
 
49
49
  ```yml
50
50
  consumer_key: <Hatena application consumer key>
@@ -61,7 +61,7 @@ blog_id: <Hatenablog ID>
61
61
  require 'hatenablog'
62
62
 
63
63
  # Read the OAuth configuration from 'conf.yml'
64
- Hatenablog.create do |blog|
64
+ Hatenablog::Client.create do |blog|
65
65
  # Get each entry's content
66
66
  blog.entries.each do |entry|
67
67
  puts entry.content
@@ -73,12 +73,12 @@ Hatenablog.create do |blog|
73
73
  ['Test', 'Programming']) # categories
74
74
 
75
75
  # Update entry
76
- posted_entry = blog.update_entry(posted_entry.id,
77
- 'Revised Entry Title',
78
- posted_entry.content,
79
- posted_entry.categories)
76
+ updated_entry = blog.update_entry(posted_entry.id,
77
+ 'Revised Entry Title',
78
+ posted_entry.content,
79
+ posted_entry.categories)
80
80
 
81
81
  # Delete entry
82
- blog.delete_entry(posted_entry.id)
82
+ blog.delete_entry(updated_entry.id)
83
83
  end
84
84
  ```
data/Rakefile CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rake/testtask'
5
+ require 'yard'
6
+ require 'yard/rake/yardoc_task'
7
+
8
+ task :default => :test
5
9
 
6
10
  Rake::TestTask.new do |t|
7
11
  t.libs << "test"
8
- t.test_files = Dir["test/*_test.rb"]
12
+ t.test_files = Dir["test/hatenablog/*_test.rb"]
9
13
  t.verbose = true
10
14
  end
15
+
16
+ YARD::Rake::YardocTask.new do |t|
17
+ t.files = Dir["lib/*.rb"]
18
+ t.options = %w(--debug --verbose) if $trace
19
+ end
@@ -19,8 +19,15 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
+ spec.required_ruby_version = '>= 2.0'
23
+
22
24
  spec.add_development_dependency "bundler", "~> 1.10"
23
25
  spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rr", "~> 1.1.2"
27
+ spec.add_development_dependency "test-unit", "~> 3.1.4"
28
+ spec.add_development_dependency "test-unit-rr", "~> 1.0.3"
29
+ spec.add_development_dependency "yard", "~> 0.8.7"
24
30
 
25
- spec.add_dependency "oauth"
31
+ spec.add_dependency "nokogiri", "~> 1.6.6"
32
+ spec.add_dependency "oauth", "~> 0.4.7"
26
33
  end
@@ -1,272 +1,5 @@
1
- require 'rexml/document'
2
- require 'oauth'
3
-
4
- require 'blog_entry'
5
- require 'blog_feed'
6
- require 'configuration'
7
-
8
- class Hatenablog
9
- DEFAULT_CONFIG_PATH = './config.yml'
10
-
11
- COLLECTION_URI = "https://blog.hatena.ne.jp/%s/%s/atom/entry"
12
- MEMBER_URI = "https://blog.hatena.ne.jp/%s/%s/atom/entry/%s"
13
- CATEGORY_URI = "https://blog.hatena.ne.jp/%s/%s/atom/category"
14
-
15
- attr_writer :access_token
16
-
17
- # Create a new hatenablog AtomPub client from a configuration file.
18
- # @param [String] config_file configuration file path
19
- # @return [Hatenablog] created hatenablog client
20
- def self.create(config_file = DEFAULT_CONFIG_PATH)
21
- config = Configuration.new(config_file)
22
- blog = Hatenablog.new(config.consumer_key, config.consumer_secret,
23
- config.access_token, config.access_token_secret,
24
- config.user_id, config.blog_id)
25
- return blog unless block_given?
26
- yield blog
27
- end
28
-
29
- # Get a blog title.
30
- # @return [String] blog title
31
- def title
32
- feed = BlogFeed.load_xml(get_collection(collection_uri).body)
33
- feed.title
34
- end
35
-
36
- # Get a author name.
37
- # @return [String] blog author name
38
- def author_name
39
- feed = BlogFeed.load_xml(get_collection(collection_uri).body)
40
- feed.author_name
41
- end
42
-
43
- # Get blog entries array.
44
- # @param [Fixnum] page page number to get
45
- # @return [Array] blog entries
46
- def entries(page = 0)
47
- next_page_uri = collection_uri
48
- current_page = 0
49
- entries = []
50
- while current_page <= page
51
- feed = BlogFeed.load_xml(get_collection(next_page_uri).body)
52
- entries += feed.entries
53
-
54
- break unless feed.has_next?
55
- next_page_uri = feed.next_uri
56
- current_page += 1
57
- end
58
- entries
59
- end
60
-
61
- # Get blog categories array.
62
- # @return [Array] blog categories
63
- def categories
64
- categories_doc = REXML::Document.new(get_category_doc.body)
65
- categories_list = []
66
- categories_doc.elements.each('//atom:category') do |cat|
67
- categories_list << cat.attribute('term').to_s
68
- end
69
- categories_list
70
- end
71
-
72
- # Get a blog entry specified by its ID.
73
- # @param [String] entry_id entry ID
74
- # @return [BlogEntry] entry
75
- def get_entry(entry_id)
76
- response = get(member_uri(entry_id))
77
- BlogEntry.load_xml(response.body)
78
- end
79
-
80
- # Post a blog entry.
81
- # @param [String] title entry title
82
- # @param [String] content entry content
83
- # @param [Array] categories entry categories
84
- # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
85
- # @return [BlogEntry] posted entry
86
- def post_entry(title = '', content = '', categories = [], draft = 'no')
87
- entry_xml = entry_xml(title, content, categories, draft)
88
- response = post(entry_xml)
89
- BlogEntry.load_xml(response.body)
90
- end
91
-
92
- # Update a blog entry specified by its ID.
93
- # @param [String] entry_id updated entry ID
94
- # @param [String] title entry title
95
- # @param [String] content entry content
96
- # @param [Array] categories entry categories
97
- # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
98
- # @return [BlogEntry] updated entry
99
- def update_entry(entry_id, title = '', content = '', categories = [], draft = 'no')
100
- entry_xml = entry_xml(title, content, categories, draft)
101
- response = put(entry_xml, member_uri(entry_id))
102
- BlogEntry.load_xml(response.body)
103
- end
104
-
105
- # Delete a blog entry specified by its ID.
106
- # @param [String] entry_id deleted entry ID
107
- def delete_entry(entry_id)
108
- delete(member_uri(entry_id))
109
- end
110
-
111
- # Get Hatenablog AtomPub collection URI.
112
- # @param [String] user_id Hatena user ID
113
- # @param [String] blog_id Hatenablog ID
114
- # @return [String] Hatenablog AtomPub collection URI
115
- def collection_uri(user_id = @user_id, blog_id = @blog_id)
116
- COLLECTION_URI % [user_id, blog_id]
117
- end
118
-
119
- # Get Hatenablog AtomPub member URI.
120
- # @param [String] entry_id entry ID
121
- # @param [String] user_id Hatena user ID
122
- # @param [String] blog_id Hatenablog ID
123
- # @return [String] Hatenablog AtomPub member URI
124
- def member_uri(entry_id, user_id = @user_id, blog_id = @blog_id)
125
- MEMBER_URI % [user_id, blog_id, entry_id]
126
- end
127
-
128
- # Get Hatenablog AtomPub category document URI.
129
- # @param [String] user_id Hatena user ID
130
- # @param [String] blog_id Hatenablog ID
131
- # @return [String] Hatenablog AtomPub category document URI
132
- def category_doc_uri(user_id = @user_id, blog_id = @blog_id)
133
- CATEGORY_URI % [user_id, blog_id]
134
- end
135
-
136
- # Build a entry XML from arguments.
137
- # @param [String] title entry title
138
- # @param [String] content entry content
139
- # @param [Array] categories entry categories
140
- # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
141
- # @param [String] author_name entry author name
142
- # @return [String] XML string
143
- def entry_xml(title = '', content = '', categories = [], draft = 'no', author_name = @user_id)
144
- xml = <<XML
145
- <?xml version="1.0" encoding="utf-8"?>
146
- <entry xmlns="http://www.w3.org/2005/Atom"
147
- xmlns:app="http://www.w3.org/2007/app">
148
- <title>%s</title>
149
- <author><name>%s</name></author>
150
- <content type="text/x-markdown">%s</content>
151
- %s
152
- <app:control>
153
- <app:draft>%s</app:draft>
154
- </app:control>
155
- </entry>
156
- XML
157
-
158
- categories_tag = categories.inject('') do |s, c|
159
- s + "<category term=\"#{c}\" />\n"
160
- end
161
- xml % [title, author_name, content, categories_tag, draft]
162
- end
163
-
164
-
165
- private
166
-
167
- def initialize(consumer_key, consumer_secret, access_token, access_token_secret,
168
- user_id, blog_id)
169
- consumer = OAuth::Consumer.new(consumer_key, consumer_secret)
170
- @access_token = OAuthAccessToken.new(OAuth::AccessToken.new(consumer,
171
- access_token,
172
- access_token_secret))
173
-
174
- @user_id = user_id
175
- @blog_id = blog_id
176
- end
177
-
178
-
179
- def get(uri)
180
- @access_token.get(uri)
181
- end
182
-
183
- def get_collection(uri = collection_uri)
184
- unless uri.include?(collection_uri)
185
- raise ArgumentError.new('Invalid collection URI: ' + uri)
186
- end
187
- get(uri)
188
- end
189
-
190
- def get_category_doc
191
- get(category_doc_uri)
192
- end
193
-
194
- def post(entry_xml, uri = collection_uri)
195
- @access_token.post(uri, entry_xml)
196
- end
197
-
198
- def put(entry_xml, uri)
199
- @access_token.put(uri, entry_xml)
200
- end
201
-
202
- def delete(uri)
203
- @access_token.delete(uri)
204
- end
205
- end
206
-
207
- class OAuthAccessToken
208
-
209
- # Create a new OAuth 1.0a access token.
210
- # @param [OAuth::AccessToken] access_token access token object
211
- def initialize(access_token)
212
- @access_token = access_token
213
- end
214
-
215
- # HTTP GET method
216
- # @param [string] uri target URI
217
- # @return [Net::HTTPResponse] HTTP response
218
- def get(uri)
219
- begin
220
- response = @access_token.get(uri)
221
- rescue => problem
222
- raise 'Fail to GET: ' + problem.to_s
223
- end
224
- response
225
- end
226
-
227
- # HTTP POST method
228
- # @param [string] uri target URI
229
- # @param [string] body HTTP request body
230
- # @param [string] headers HTTP request headers
231
- # @return [Net::HTTPResponse] HTTP response
232
- def post(uri,
233
- body = '',
234
- headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
235
- begin
236
- response = @access_token.post(uri, body, headers)
237
- rescue => problem
238
- raise 'Fail to POST: ' + problem.to_s
239
- end
240
- response
241
- end
242
-
243
- # HTTP PUT method
244
- # @param [string] uri target URI
245
- # @param [string] body HTTP request body
246
- # @param [string] headers HTTP request headers
247
- # @return [Net::HTTPResponse] HTTP response
248
- def put(uri,
249
- body = '',
250
- headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
251
- begin
252
- response = @access_token.put(uri, body, headers)
253
- rescue => problem
254
- raise 'Fail to PUT: ' + problem.to_s
255
- end
256
- response
257
- end
258
-
259
- # HTTP DELETE method
260
- # @param [string] uri target URI
261
- # @param [string] headers HTTP request headers
262
- # @return [Net::HTTPResponse] HTTP response
263
- def delete(uri,
264
- headers = { 'Content-Type' => 'application/atom+xml; type=entry' })
265
- begin
266
- response = @access_token.delete(uri, headers)
267
- rescue => problem
268
- raise 'Fail to DELETE: ' + problem.to_s
269
- end
270
- response
271
- end
272
- end
1
+ require 'hatenablog/category'
2
+ require 'hatenablog/client'
3
+ require 'hatenablog/configuration'
4
+ require 'hatenablog/entry'
5
+ require 'hatenablog/feed'
@@ -0,0 +1,47 @@
1
+ require 'nokogiri'
2
+
3
+ module Hatenablog
4
+ class Category
5
+
6
+ # Create a new blog categories from a XML string.
7
+ # @param [String] xml XML string representation
8
+ # @return [Hatenablog::Category]
9
+ def self.load_xml(xml)
10
+ Hatenablog::Category.new(xml)
11
+ end
12
+
13
+ # @return [Array]
14
+ def categories
15
+ @categories.dup
16
+ end
17
+
18
+ def each
19
+ @categories.each do |category|
20
+ yield category
21
+ end
22
+ end
23
+
24
+ # If fixed, only categories in this categories can be used for a blog entry.
25
+ # @return [Boolean]
26
+ def fixed?
27
+ @fixed == 'yes'
28
+ end
29
+
30
+
31
+ private
32
+
33
+ def initialize(xml)
34
+ @document = Nokogiri::XML(xml)
35
+ parse_document
36
+ end
37
+
38
+ def parse_document
39
+ @categories = @document.css('atom|category').inject([]) do |categories, category|
40
+ categories << category['term'].to_s
41
+ end
42
+
43
+ @fixed = @document.at_css('app|categories')['fixed'].to_s
44
+ @fixed = 'no' if @fixed.nil?
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,272 @@
1
+ require 'oauth'
2
+
3
+ require 'hatenablog/category'
4
+ require 'hatenablog/entry'
5
+ require 'hatenablog/feed'
6
+ require 'hatenablog/configuration'
7
+
8
+ module Hatenablog
9
+ class Client
10
+ DEFAULT_CONFIG_PATH = './config.yml'.freeze
11
+
12
+ COLLECTION_URI = "https://blog.hatena.ne.jp/%s/%s/atom/entry".freeze
13
+ MEMBER_URI = "https://blog.hatena.ne.jp/%s/%s/atom/entry/%s".freeze
14
+ CATEGORY_URI = "https://blog.hatena.ne.jp/%s/%s/atom/category".freeze
15
+
16
+ attr_writer :access_token
17
+
18
+ # Create a new hatenablog AtomPub client from a configuration file.
19
+ # @param [String] config_file configuration file path
20
+ # @return [Hatenablog::Client] created hatenablog client
21
+ def self.create(config_file = DEFAULT_CONFIG_PATH)
22
+ config = Configuration.new(config_file)
23
+ blog = Hatenablog::Client.new(config.consumer_key, config.consumer_secret,
24
+ config.access_token, config.access_token_secret,
25
+ config.user_id, config.blog_id)
26
+ return blog unless block_given?
27
+ yield blog
28
+ end
29
+
30
+ # Get a blog title.
31
+ # @return [String] blog title
32
+ def title
33
+ feed = Feed.load_xml(get_collection(collection_uri).body)
34
+ feed.title
35
+ end
36
+
37
+ # Get a author name.
38
+ # @return [String] blog author name
39
+ def author_name
40
+ feed = Feed.load_xml(get_collection(collection_uri).body)
41
+ feed.author_name
42
+ end
43
+
44
+ # Get blog entries array.
45
+ # @param [Fixnum] page page number to get
46
+ # @return [Array] blog entries
47
+ def entries(page = 0)
48
+ next_page_uri = collection_uri
49
+ current_page = 0
50
+ entries = []
51
+ while current_page <= page
52
+ feed = Feed.load_xml(get_collection(next_page_uri).body)
53
+ entries += feed.entries
54
+
55
+ break unless feed.has_next?
56
+ next_page_uri = feed.next_uri
57
+ current_page += 1
58
+ end
59
+ entries
60
+ end
61
+
62
+ # Get blog categories array.
63
+ # @return [Array] blog categories
64
+ def categories
65
+ categories_doc = Category.new(get_category_doc.body)
66
+ categories_doc.categories
67
+ end
68
+
69
+ # Get a blog entry specified by its ID.
70
+ # @param [String] entry_id entry ID
71
+ # @return [Hatenablog::BlogEntry] entry
72
+ def get_entry(entry_id)
73
+ response = get(member_uri(entry_id))
74
+ Entry.load_xml(response.body)
75
+ end
76
+
77
+ # Post a blog entry.
78
+ # @param [String] title entry title
79
+ # @param [String] content entry content
80
+ # @param [Array] categories entry categories
81
+ # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
82
+ # @return [Hatenablog::BlogEntry] posted entry
83
+ def post_entry(title = '', content = '', categories = [], draft = 'no')
84
+ entry_xml = entry_xml(title, content, categories, draft)
85
+ response = post(entry_xml)
86
+ Entry.load_xml(response.body)
87
+ end
88
+
89
+ # Update a blog entry specified by its ID.
90
+ # @param [String] entry_id updated entry ID
91
+ # @param [String] title entry title
92
+ # @param [String] content entry content
93
+ # @param [Array] categories entry categories
94
+ # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
95
+ # @return [Hatenablog::BlogEntry] updated entry
96
+ def update_entry(entry_id, title = '', content = '', categories = [], draft = 'no')
97
+ entry_xml = entry_xml(title, content, categories, draft)
98
+ response = put(entry_xml, member_uri(entry_id))
99
+ Entry.load_xml(response.body)
100
+ end
101
+
102
+ # Delete a blog entry specified by its ID.
103
+ # @param [String] entry_id deleted entry ID
104
+ def delete_entry(entry_id)
105
+ delete(member_uri(entry_id))
106
+ end
107
+
108
+ # Get Hatenablog AtomPub collection URI.
109
+ # @param [String] user_id Hatena user ID
110
+ # @param [String] blog_id Hatenablog ID
111
+ # @return [String] Hatenablog AtomPub collection URI
112
+ def collection_uri(user_id = @user_id, blog_id = @blog_id)
113
+ COLLECTION_URI % [user_id, blog_id]
114
+ end
115
+
116
+ # Get Hatenablog AtomPub member URI.
117
+ # @param [String] entry_id entry ID
118
+ # @param [String] user_id Hatena user ID
119
+ # @param [String] blog_id Hatenablog ID
120
+ # @return [String] Hatenablog AtomPub member URI
121
+ def member_uri(entry_id, user_id = @user_id, blog_id = @blog_id)
122
+ MEMBER_URI % [user_id, blog_id, entry_id]
123
+ end
124
+
125
+ # Get Hatenablog AtomPub category document URI.
126
+ # @param [String] user_id Hatena user ID
127
+ # @param [String] blog_id Hatenablog ID
128
+ # @return [String] Hatenablog AtomPub category document URI
129
+ def category_doc_uri(user_id = @user_id, blog_id = @blog_id)
130
+ CATEGORY_URI % [user_id, blog_id]
131
+ end
132
+
133
+ # Build a entry XML from arguments.
134
+ # @param [String] title entry title
135
+ # @param [String] content entry content
136
+ # @param [Array] categories entry categories
137
+ # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
138
+ # @param [String] author_name entry author name
139
+ # @return [String] XML string
140
+ def entry_xml(title = '', content = '', categories = [], draft = 'no', author_name = @user_id)
141
+ xml = <<XML
142
+ <?xml version="1.0" encoding="utf-8"?>
143
+ <entry xmlns="http://www.w3.org/2005/Atom"
144
+ xmlns:app="http://www.w3.org/2007/app">
145
+ <title>%s</title>
146
+ <author><name>%s</name></author>
147
+ <content type="text/x-markdown">%s</content>
148
+ %s
149
+ <app:control>
150
+ <app:draft>%s</app:draft>
151
+ </app:control>
152
+ </entry>
153
+ XML
154
+
155
+ categories_tag = categories.inject('') do |s, c|
156
+ s + "<category term=\"#{c}\" />\n"
157
+ end
158
+ xml % [title, author_name, content, categories_tag, draft]
159
+ end
160
+
161
+
162
+ private
163
+
164
+ def initialize(consumer_key, consumer_secret, access_token, access_token_secret,
165
+ user_id, blog_id)
166
+ consumer = OAuth::Consumer.new(consumer_key, consumer_secret)
167
+ @access_token = OAuthAccessToken.new(OAuth::AccessToken.new(consumer,
168
+ access_token,
169
+ access_token_secret))
170
+
171
+ @user_id = user_id
172
+ @blog_id = blog_id
173
+ end
174
+
175
+
176
+ def get(uri)
177
+ @access_token.get(uri)
178
+ end
179
+
180
+ def get_collection(uri = collection_uri)
181
+ unless uri.include?(collection_uri)
182
+ raise ArgumentError.new('Invalid collection URI: ' + uri)
183
+ end
184
+ get(uri)
185
+ end
186
+
187
+ def get_category_doc
188
+ get(category_doc_uri)
189
+ end
190
+
191
+ def post(entry_xml, uri = collection_uri)
192
+ @access_token.post(uri, entry_xml)
193
+ end
194
+
195
+ def put(entry_xml, uri)
196
+ @access_token.put(uri, entry_xml)
197
+ end
198
+
199
+ def delete(uri)
200
+ @access_token.delete(uri)
201
+ end
202
+ end
203
+
204
+ class OAuthAccessToken
205
+
206
+ # Create a new OAuth 1.0a access token.
207
+ # @param [OAuth::AccessToken] access_token access token object
208
+ def initialize(access_token)
209
+ @access_token = access_token
210
+ end
211
+
212
+ # HTTP GET method
213
+ # @param [string] uri target URI
214
+ # @return [Net::HTTPResponse] HTTP response
215
+ def get(uri)
216
+ begin
217
+ response = @access_token.get(uri)
218
+ rescue => problem
219
+ raise OAuthError, 'Fail to GET: ' + problem.to_s
220
+ end
221
+ response
222
+ end
223
+
224
+ # HTTP POST method
225
+ # @param [string] uri target URI
226
+ # @param [string] body HTTP request body
227
+ # @param [string] headers HTTP request headers
228
+ # @return [Net::HTTPResponse] HTTP response
229
+ def post(uri,
230
+ body = '',
231
+ headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
232
+ begin
233
+ response = @access_token.post(uri, body, headers)
234
+ rescue => problem
235
+ raise OAuthError, 'Fail to POST: ' + problem.to_s
236
+ end
237
+ response
238
+ end
239
+
240
+ # HTTP PUT method
241
+ # @param [string] uri target URI
242
+ # @param [string] body HTTP request body
243
+ # @param [string] headers HTTP request headers
244
+ # @return [Net::HTTPResponse] HTTP response
245
+ def put(uri,
246
+ body = '',
247
+ headers = { 'Content-Type' => 'application/atom+xml; type=entry' } )
248
+ begin
249
+ response = @access_token.put(uri, body, headers)
250
+ rescue => problem
251
+ raise OAuthError, 'Fail to PUT: ' + problem.to_s
252
+ end
253
+ response
254
+ end
255
+
256
+ # HTTP DELETE method
257
+ # @param [string] uri target URI
258
+ # @param [string] headers HTTP request headers
259
+ # @return [Net::HTTPResponse] HTTP response
260
+ def delete(uri,
261
+ headers = { 'Content-Type' => 'application/atom+xml; type=entry' })
262
+ begin
263
+ response = @access_token.delete(uri, headers)
264
+ rescue => problem
265
+ raise OAuthError, 'Fail to DELETE: ' + problem.to_s
266
+ end
267
+ response
268
+ end
269
+ end
270
+
271
+ class OAuthError < StandardError; end
272
+ end
@@ -0,0 +1,31 @@
1
+ require 'yaml'
2
+
3
+ module Hatenablog
4
+ class Configuration
5
+ # For OAuth authorization.
6
+ attr_reader :consumer_key, :consumer_secret, :access_token, :access_token_secret
7
+
8
+ attr_reader :user_id, :blog_id
9
+
10
+ # Create a new configuration.
11
+ # @param [String] config_file configuration file path
12
+ # @return [Hatenablog::Configuration]
13
+ def initialize(config_file)
14
+ config = YAML.load_file(config_file)
15
+ unless config.has_key?('consumer_key') && config.has_key?('consumer_secret') &&
16
+ config.has_key?('access_token') && config.has_key?('access_token_secret') &&
17
+ config.has_key?('user_id') && config.has_key?('blog_id')
18
+ raise ConfigurationError, 'the configure file is incorrect'
19
+ end
20
+
21
+ @consumer_key = config['consumer_key']
22
+ @consumer_secret = config['consumer_secret']
23
+ @access_token = config['access_token']
24
+ @access_token_secret = config['access_token_secret']
25
+ @user_id = config['user_id']
26
+ @blog_id = config['blog_id']
27
+ end
28
+ end
29
+
30
+ class ConfigurationError < StandardError; end
31
+ end
@@ -0,0 +1,98 @@
1
+ require 'nokogiri'
2
+
3
+ module Hatenablog
4
+ class Entry
5
+ attr_reader :uri, :edit_uri, :id, :author_name, :title, :content
6
+
7
+ # Create a new blog entry from a XML string.
8
+ # @param [String] xml XML string representation
9
+ # @return [Hatenablog::Entry]
10
+ def self.load_xml(xml)
11
+ Hatenablog::Entry.new(xml)
12
+ end
13
+
14
+ # Create a new blog entry from arguments.
15
+ # @param [String] uri entry URI
16
+ # @param [String] edit_uri entry URI for editing
17
+ # @param [String] author_name entry author name
18
+ # @param [String] title entry title
19
+ # @param [String] content entry content
20
+ # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
21
+ # @param [Array] categories categories array
22
+ # @return [Hatenablog::Entry]
23
+ def self.create(uri: '', edit_uri: '', author_name: '', title: '',
24
+ content: '', draft: 'no', categories: [])
25
+ Hatenablog::Entry.new(self.build_xml(uri, edit_uri, author_name, title,
26
+ content, draft, categories))
27
+ end
28
+
29
+ # @return [Boolean]
30
+ def draft?
31
+ @draft == 'yes'
32
+ end
33
+
34
+ # @return [Array]
35
+ def categories
36
+ @categories.dup
37
+ end
38
+
39
+ def each_category
40
+ @categories.each do |category|
41
+ yield category
42
+ end
43
+ end
44
+
45
+ # @return [String]
46
+ def to_xml
47
+ @document.to_s.gsub(/\"/, "'")
48
+ end
49
+
50
+
51
+ private
52
+
53
+ def self.build_xml(uri, edit_uri, author_name, title, content, draft, categories)
54
+ xml = <<XML
55
+ <?xml version='1.0' encoding='UTF-8'?>
56
+ <entry xmlns:app='http://www.w3.org/2007/app' xmlns='http://www.w3.org/2005/Atom'>
57
+ <link href='%s' rel='edit'/>
58
+ <link href='%s' rel='alternate' type='text/html'/>
59
+ <author><name>%s</name></author>
60
+ <title>%s</title>
61
+ <content type='text/x-markdown'>%s</content>
62
+ %s
63
+ <app:control>
64
+ <app:draft>%s</app:draft>
65
+ </app:control>
66
+ </entry>
67
+ XML
68
+
69
+ categories_tag = categories.inject('') do |s, c|
70
+ s + "<category term=\"#{c}\" />\n"
71
+ end
72
+ xml % [edit_uri, uri, author_name, title, content, categories_tag, draft]
73
+ end
74
+
75
+ def initialize(xml)
76
+ @document = Nokogiri::XML(xml)
77
+ parse_document
78
+ end
79
+
80
+ def parse_document
81
+ @uri = @document.at_css('link[@rel="alternate"]')['href'].to_s
82
+ @edit_uri = @document.at_css('link[@rel="edit"]')['href'].to_s
83
+ @id = @edit_uri.split('/').last
84
+ @author_name = @document.at_css('author name').content
85
+ @title = @document.at_css('title').content
86
+ @content = @document.at_css('content').content
87
+ @draft = @document.at_css('entry app|control app|draft').content
88
+ @categories = parse_categories
89
+ end
90
+
91
+ def parse_categories
92
+ categories = @document.css('category').inject([]) do |categories, category|
93
+ categories << category['term'].to_s
94
+ end
95
+ categories
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,63 @@
1
+ require 'nokogiri'
2
+ require 'time'
3
+
4
+ require 'hatenablog/entry'
5
+
6
+ module Hatenablog
7
+ class Feed
8
+ attr_reader :uri, :next_uri, :title, :author_name, :updated
9
+
10
+ # Create a new blog feed from a XML string.
11
+ # @param [String] xml XML string representation
12
+ # @return [Hatenablog::Feed]
13
+ def self.load_xml(xml)
14
+ Hatenablog::Feed.new(xml)
15
+ end
16
+
17
+ # @return [Array]
18
+ def entries
19
+ @entries.dup
20
+ end
21
+
22
+ def each_entry
23
+ @entries.each do |entry|
24
+ yield entry
25
+ end
26
+ end
27
+
28
+ # Return true if this feed has next feed.
29
+ # @return [Boolean]
30
+ def has_next?
31
+ @next_uri != ''
32
+ end
33
+
34
+
35
+ private
36
+
37
+ def initialize(xml)
38
+ @document = Nokogiri::XML(xml)
39
+ parse_document
40
+ end
41
+
42
+ def parse_document
43
+ @uri = @document.at_css("feed link[@rel='alternate']")['href'].to_s
44
+ @next_uri = if @document.css("feed link[@rel='next']").empty?
45
+ ''
46
+ else
47
+ @document.at_css("feed link[@rel='next']")['href'].to_s
48
+ end
49
+ @title = @document.at_css('feed title').content
50
+ @author_name = @document.at_css('author name').content
51
+ @updated = Time.parse(@document.at_css('feed updated').content)
52
+ parse_entry
53
+ end
54
+
55
+ def parse_entry
56
+ @entries = @document.css('feed > entry').inject([]) do |entries, entry|
57
+ # add namespace 'app' to recognize XML correctly
58
+ entry['xmlns:app'] = 'http://www.w3.org/2007/app'
59
+ entries << Hatenablog::Entry.load_xml(entry.to_s)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,3 +1,3 @@
1
1
  module Hatenablog
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.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.1.0
4
+ version: 0.2.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: 2015-10-06 00:00:00.000000000 Z
11
+ date: 2015-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,20 +38,90 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rr
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: test-unit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.1.4
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.1.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: test-unit-rr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.0.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.0.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.7
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.7
97
+ - !ruby/object:Gem::Dependency
98
+ name: nokogiri
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.6.6
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.6
41
111
  - !ruby/object:Gem::Dependency
42
112
  name: oauth
43
113
  requirement: !ruby/object:Gem::Requirement
44
114
  requirements:
45
- - - ">="
115
+ - - "~>"
46
116
  - !ruby/object:Gem::Version
47
- version: '0'
117
+ version: 0.4.7
48
118
  type: :runtime
49
119
  prerelease: false
50
120
  version_requirements: !ruby/object:Gem::Requirement
51
121
  requirements:
52
- - - ">="
122
+ - - "~>"
53
123
  - !ruby/object:Gem::Version
54
- version: '0'
124
+ version: 0.4.7
55
125
  description: Hatenablog AtomPub API library
56
126
  email:
57
127
  - kymmt90@gmail.com
@@ -61,6 +131,7 @@ extensions: []
61
131
  extra_rdoc_files: []
62
132
  files:
63
133
  - ".gitignore"
134
+ - ".travis.yml"
64
135
  - Gemfile
65
136
  - LICENSE.txt
66
137
  - README.md
@@ -69,11 +140,12 @@ files:
69
140
  - bin/setup
70
141
  - exe/get_access_token
71
142
  - hatenablog.gemspec
72
- - lib/blog_category.rb
73
- - lib/blog_entry.rb
74
- - lib/blog_feed.rb
75
- - lib/configuration.rb
76
143
  - lib/hatenablog.rb
144
+ - lib/hatenablog/category.rb
145
+ - lib/hatenablog/client.rb
146
+ - lib/hatenablog/configuration.rb
147
+ - lib/hatenablog/entry.rb
148
+ - lib/hatenablog/feed.rb
77
149
  - lib/hatenablog/version.rb
78
150
  homepage: https://github.com/kymmt90/hatenablog
79
151
  licenses:
@@ -87,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
87
159
  requirements:
88
160
  - - ">="
89
161
  - !ruby/object:Gem::Version
90
- version: '0'
162
+ version: '2.0'
91
163
  required_rubygems_version: !ruby/object:Gem::Requirement
92
164
  requirements:
93
165
  - - ">="
@@ -100,3 +172,4 @@ signing_key:
100
172
  specification_version: 4
101
173
  summary: Hatenablog AtomPub API library
102
174
  test_files: []
175
+ has_rdoc:
@@ -1,46 +0,0 @@
1
- require 'rexml/document'
2
-
3
- class BlogCategory
4
-
5
- # Create a new blog categories from a XML string.
6
- # @param [String] xml XML string representation
7
- # @return [BlogCategory]
8
- def self.load_xml(xml)
9
- BlogCategory.new(xml)
10
- end
11
-
12
- # @return [Array]
13
- def categories
14
- @categories.dup
15
- end
16
-
17
- def each
18
- @categories.each do |category|
19
- yield category
20
- end
21
- end
22
-
23
- # If fixed, only categories in this categories can be used for a blog entry.
24
- # @return [Boolean]
25
- def fixed?
26
- @fixed == 'yes'
27
- end
28
-
29
-
30
- private
31
-
32
- def initialize(xml)
33
- @document = REXML::Document.new(xml)
34
- parse_document
35
- end
36
-
37
- def parse_document
38
- @categories = []
39
- @document.each_element("//atom:category") do |category|
40
- @categories << category.attribute('term').to_s
41
- end
42
-
43
- @fixed = @document.elements["/app:categories"].attribute('fixed').to_s
44
- @fixed = 'no' if @fixed.nil?
45
- end
46
- end
@@ -1,97 +0,0 @@
1
- require 'rexml/document'
2
-
3
- class BlogEntry
4
- attr_reader :uri, :edit_uri, :id, :author_name, :title, :content
5
-
6
- # Create a new blog entry from a XML string.
7
- # @param [String] xml XML string representation
8
- # @return [BlogEntry]
9
- def self.load_xml(xml)
10
- BlogEntry.new(xml)
11
- end
12
-
13
- # Create a new blog entry from arguments.
14
- # @param [String] uri entry URI
15
- # @param [String] edit_uri entry URI for editing
16
- # @param [String] author_name entry author name
17
- # @param [String] title entry title
18
- # @param [String] content entry content
19
- # @param [String] draft this entry is draft if 'yes', otherwise it is not draft
20
- # @param [Array] categories categories array
21
- # @return [BlogEntry]
22
- def self.create(uri: '', edit_uri: '', author_name: '', title: '',
23
- content: '', draft: 'no', categories: [])
24
- BlogEntry.new(self.build_xml(uri, edit_uri, author_name, title,
25
- content, draft, categories))
26
- end
27
-
28
- # @return [Boolean]
29
- def draft?
30
- @draft == 'yes'
31
- end
32
-
33
- # @return [Array]
34
- def categories
35
- @categories.dup
36
- end
37
-
38
- def each_category
39
- @categories.each do |category|
40
- yield category
41
- end
42
- end
43
-
44
- # @return [String]
45
- def to_xml
46
- @document.to_s
47
- end
48
-
49
-
50
- private
51
-
52
- def self.build_xml(uri, edit_uri, author_name, title, content, draft, categories)
53
- xml = <<XML
54
- <?xml version='1.0' encoding='UTF-8'?>
55
- <entry xmlns:app='http://www.w3.org/2007/app' xmlns='http://www.w3.org/2005/Atom'>
56
- <link href='%s' rel='edit'/>
57
- <link href='%s' rel='alternate' type='text/html'/>
58
- <author><name>%s</name></author>
59
- <title>%s</title>
60
- <content type='text/x-markdown'>%s</content>
61
- %s
62
- <app:control>
63
- <app:draft>%s</app:draft>
64
- </app:control>
65
- </entry>
66
- XML
67
-
68
- categories_tag = categories.inject('') do |s, c|
69
- s + "<category term=\"#{c}\" />\n"
70
- end
71
- xml % [edit_uri, uri, author_name, title, content, categories_tag, draft]
72
- end
73
-
74
- def initialize(xml)
75
- @document = REXML::Document.new(xml)
76
- parse_document
77
- end
78
-
79
- def parse_document
80
- @uri = @document.elements["//link[@rel='alternate']"].attribute('href').to_s
81
- @edit_uri = @document.elements["//link[@rel='edit']"].attribute('href').to_s
82
- @id = @edit_uri.split('/').last
83
- @author_name = @document.elements["//author/name"].text
84
- @title = @document.elements["//title"].text
85
- @content = @document.elements["//content"].text
86
- @draft = @document.elements["//app:draft"].text
87
- @categories = parse_categories
88
- end
89
-
90
- def parse_categories
91
- categories = []
92
- @document.each_element("//category") do |category|
93
- categories << category.attribute('term').to_s
94
- end
95
- categories
96
- end
97
- end
@@ -1,61 +0,0 @@
1
- require 'blog_entry'
2
- require 'rexml/document'
3
- require 'time'
4
-
5
- class BlogFeed
6
- attr_reader :uri, :next_uri, :title, :author_name, :updated
7
-
8
- # Create a new blog feed from a XML string.
9
- # @param [String] xml XML string representation
10
- # @return [BlogFeed]
11
- def self.load_xml(xml)
12
- BlogFeed.new(xml)
13
- end
14
-
15
- # @return [Array]
16
- def entries
17
- @entries.dup
18
- end
19
-
20
- def each_entry
21
- @entries.each do |entry|
22
- yield entry
23
- end
24
- end
25
-
26
- # Return true if this feed has next feed.
27
- # @return [Boolean]
28
- def has_next?
29
- @next_uri != ''
30
- end
31
-
32
-
33
- private
34
-
35
- def initialize(xml)
36
- @document = REXML::Document.new(xml)
37
- parse_document
38
- end
39
-
40
- def parse_document
41
- @uri = @document.elements["/feed/link[@rel='alternate']"].attribute('href').to_s
42
- @next_uri = if @document.elements["/feed/link[@rel='next']"].nil?
43
- ''
44
- else
45
- @document.elements["/feed/link[@rel='next']"].attribute('href').to_s
46
- end
47
- @title = @document.elements["/feed/title"].text
48
- @author_name = @document.elements["//author/name"].text
49
- @updated = Time.parse(@document.elements["/feed/updated"].text)
50
- parse_entry
51
- end
52
-
53
- def parse_entry
54
- @entries = []
55
- @document.elements.collect("//entry") do |entry|
56
- # add namespace 'app' to recognize XML correctly
57
- entry.add_attribute('xmlns:app', 'http://www.w3.org/2007/app')
58
- @entries << BlogEntry.load_xml(entry.to_s)
59
- end
60
- end
61
- end
@@ -1,21 +0,0 @@
1
- require 'yaml'
2
-
3
- class Configuration
4
- # For OAuth authorization.
5
- attr_reader :consumer_key, :consumer_secret, :access_token, :access_token_secret
6
-
7
- attr_reader :user_id, :blog_id
8
-
9
- # Create a new configuration.
10
- # @param [String] config_file configuration file path
11
- # @return [Configuration]
12
- def initialize(config_file)
13
- config = YAML.load_file(config_file)
14
- @consumer_key = config['consumer_key']
15
- @consumer_secret = config['consumer_secret']
16
- @access_token = config['access_token']
17
- @access_token_secret = config['access_token_secret']
18
- @user_id = config['user_id']
19
- @blog_id = config['blog_id']
20
- end
21
- end