hatenadiary-legacy 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df2818a88d8b430ede88c6706fd43055826fd849
4
+ data.tar.gz: 7ef4f5ab24b6e970ecfe47957478d2fabf4ec691
5
+ SHA512:
6
+ metadata.gz: d69a727c148d1c214e19dda0100bcb4aef383f20bcda4490a86341ad7659309c6a9ee9122cd0bf0e9739e5cebfa6b11932f6ec08824ad00a1743be0263056bb2
7
+ data.tar.gz: 0a6ca4c9a5b04e55577fef1c1b823207a09e4c418bbb86572881397909c5ff1a90504736ba44d2b957900d7298fbda94c10b892f076c66a76ca4316d60f24091
data/ChangeLog ADDED
@@ -0,0 +1,53 @@
1
+ 2015-02-11 arikui <arikui.ruby@gmail.com>
2
+
3
+ * rename to `hatenadiary-legacy'.
4
+
5
+ * test/test_hatenadiary.rb: use `flexmock' instead of `mocha'
6
+
7
+ * Rakefile: version up tp 0.0.7
8
+
9
+
10
+ 2011-02-24 arikui <arikui.ruby@gmail.com>
11
+
12
+ * lib/hatenadiary.rb: use constant Mechanize instead of WWW::Mechanize because
13
+ it is going to be deprecated.
14
+
15
+ * test/test_hatenadiary.rb: add #test_default_mechanizer to touch a constant Mechanize.
16
+
17
+ * Rakefile: version up to 0.0.6
18
+
19
+ 2009-12-18 arikui <arikui.ruby@gmail.com>
20
+
21
+ * lib/hatenadiary.rb: remove gem() version requirement.
22
+
23
+ * Rakefile: version up to 0.0.5
24
+ (gemspec) fix dependence about nokogiri.
25
+
26
+ 2009-12-04 arikui <arikui.ruby@gmail.com>
27
+
28
+ * Rakefile: define common settings as constant.
29
+ include gemspec.
30
+ (task 'gemspec') added. this task updates `hatenadiary.gemspec' along to spec written in Rakefile.
31
+ It means, don't have to correct `hatenadiary.gemspec' by hand.
32
+ (task 'gem') added.
33
+ (task 'cutter') added. this task uploads gem to GemCutter.
34
+
35
+ * hatenadiary.gemspec: version up to 0.0.4
36
+
37
+ 2009-11-29 arikui <arikui.ruby@gmail.com>
38
+
39
+ * test/test_hatenadiary.rb: replace test by mocha used version.
40
+
41
+ * lib/hatenadiary.rb: use hpricot -> nokogiri 1.3.3
42
+ respond to hatena group diary.
43
+ (HatenaDiary::Client#post) change interface.
44
+ (HatenaDiary::Client#initialize): interface extended for test.
45
+
46
+ 2009-07-03 arikui <arikui.ruby@gmail.com>
47
+
48
+ * lib/hatenadiary.rb: use nokogiri -> hpricot
49
+
50
+ 2009-06-22 arikui <arikui.ruby@gmail.com>
51
+
52
+ * lib/hatenadiary.rb: first version.
53
+
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2009 arikui <arikui.ruby@gmail.com>
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above
11
+ copyright notice, this list of conditions and the following
12
+ disclaimer in the documentation and/or other materials provided
13
+ with the distribution.
14
+ * Neither the name of the arikui-hatenadiary nor the names of its
15
+ contributors may be used to endorse or promote products derived
16
+ from this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,23 @@
1
+ = hatenadiary-legacy
2
+
3
+ It is a library provides
4
+ a client for Hatena Diary to post and delete blog entries.
5
+
6
+ No longer this library is only maintained.
7
+
8
+ Developed on:
9
+ - ruby 2.1.3p242 (2014-09-19 revision 47630) [i386-mingw32]
10
+ - Gem mechanize 2.7.3
11
+
12
+ This list means these environments are enable to make all of /test pass.
13
+
14
+
15
+ == Dependences
16
+
17
+ - mechanize
18
+
19
+
20
+ == Signature
21
+
22
+ arikui (arikui.ruby _at_ gmail.com)
23
+
@@ -0,0 +1,187 @@
1
+ #
2
+ # Distributes under The modified BSD license.
3
+ #
4
+ # Copyright (c) 2009 arikui <http://d.hatena.ne.jp/arikui1911/>
5
+ # All rights reserved.
6
+ #
7
+
8
+ require 'rubygems'
9
+ require 'nokogiri'
10
+ require 'mechanize'
11
+
12
+ # depend on Ruby version
13
+ module HatenaDiary
14
+ module Util
15
+ if RUBY_VERSION >= '1.9'
16
+ def encode_to_utf8(str)
17
+ str.encode(Encoding::UTF_8)
18
+ end
19
+ else
20
+ require 'kconv'
21
+
22
+ def encode_to_utf8(str)
23
+ Kconv.toutf8(str)
24
+ end
25
+ end
26
+
27
+ module_function :encode_to_utf8
28
+ end
29
+ end
30
+
31
+
32
+ module HatenaDiary
33
+ #
34
+ # Allocates Client object and makes it login, execute a received block,
35
+ # and then logout.
36
+ #
37
+ # :call-seq:
38
+ # login(username, password, proxy = nil){|client| ... }
39
+ #
40
+ def login(*args, &block)
41
+ Client.login(*args, &block)
42
+ end
43
+ module_function :login
44
+
45
+ class LoginError < RuntimeError
46
+ def set_account(username, password)
47
+ @username = username
48
+ @password = password
49
+ self
50
+ end
51
+
52
+ attr_reader :username
53
+ attr_reader :password
54
+ end
55
+
56
+ class Client
57
+ def self.mechanizer
58
+ @mechanizer ||= Mechanize
59
+ end
60
+
61
+ def self.mechanizer=(klass)
62
+ @mechanizer = klass
63
+ end
64
+
65
+ # Allocates Client object.
66
+ #
67
+ # If block given, login and execute a received block, and then logout ensurely.
68
+ #
69
+ # [username] Hatena ID
70
+ # [password] Password for _username_
71
+ # [proxy] Proxy configuration; [proxy_url, port_no] | nil
72
+ #
73
+ def self.login(username, password, proxy = nil, &block)
74
+ client = new(username, password)
75
+ client.set_proxy(*proxy) if proxy
76
+ return client unless block_given?
77
+ client.transaction(&block)
78
+ end
79
+
80
+ # Allocates Client object.
81
+ #
82
+ # [username] Hatena ID
83
+ # [password] Password for _username_
84
+ def initialize(username, password, agent = self.class.mechanizer.new)
85
+ @agent = agent
86
+ @username = username
87
+ @password = password
88
+ @current_account = nil
89
+ end
90
+
91
+ # Configure proxy.
92
+ def set_proxy(url, port)
93
+ @agent.set_proxy(url, port)
94
+ end
95
+
96
+ # Login and execute a received block, and then logout ensurely.
97
+ def transaction(username = nil, password = nil)
98
+ raise LocalJumpError, "no block given" unless block_given?
99
+ login(username, password)
100
+ begin
101
+ yield(self)
102
+ ensure
103
+ logout
104
+ end
105
+ end
106
+
107
+ # Returns a client itself was logined or not.
108
+ #
109
+ # -> true | false
110
+ def login?
111
+ @current_account ? true : false
112
+ end
113
+
114
+ # Does login.
115
+ #
116
+ # If _username_ or _password_ are invalid, raises HatenaDiary::LoginError .
117
+ def login(username = nil, password = nil)
118
+ username ||= @username
119
+ password ||= @password
120
+ form = @agent.get("https://www.hatena.ne.jp/login").forms.first
121
+ form["name"] = username
122
+ form["password"] = password
123
+ form["persistent"] = "true"
124
+ response = form.submit
125
+ @current_account = [username, password]
126
+ case response.title
127
+ when "Hatena" then response
128
+ when "Login - Hatena" then raise LoginError.new("login failure").set_account(username, password)
129
+ else raise Exception, '[BUG] must not happen (maybe cannot follow hatena spec)'
130
+ end
131
+ end
132
+
133
+ # Does logout if already logined.
134
+ def logout
135
+ return unless login?
136
+ @agent.get("https://www.hatena.ne.jp/logout")
137
+ account = @current_account
138
+ @current_account = nil
139
+ account
140
+ end
141
+
142
+ # Posts an entry to Hatena diary service.
143
+ #
144
+ # Raises HatenaDiary::LoginError unless logined.
145
+ #
146
+ # options
147
+ # [:trivial] check a checkbox of trivial updating.
148
+ # [:group] assign hatena-group name. edit group diary.
149
+ #
150
+ # Invalid options were ignored.
151
+ def post(yyyy, mm, dd, title, body, options = {})
152
+ title = Util.encode_to_utf8(title)
153
+ body = Util.encode_to_utf8(body)
154
+ form = get_form(yyyy, mm, dd, options[:group]){|r| r.form_with(:name => 'edit') }
155
+ form["year"] = "%04d" % yyyy
156
+ form["month"] = "%02d" % mm
157
+ form["day"] = "%02d" % dd
158
+ form["title"] = title
159
+ form["body"] = body
160
+ form["trivial"] = "true" if options[:trivial]
161
+ @agent.submit form, form.button_with(:name => 'edit')
162
+ end
163
+
164
+ # Deletes an entry from Hatena diary service.
165
+ #
166
+ # Raises HatenaDiary::LoginError unless logined.
167
+ #
168
+ # options
169
+ # [:group] assign hatena-group name. edit group diary.
170
+ #
171
+ # Invalid options were ignored.
172
+ def delete(yyyy, mm, dd, options = {})
173
+ get_form(yyyy, mm, dd, options[:group]){|r| r.forms.last }.submit
174
+ end
175
+
176
+ private
177
+
178
+ def get_form(yyyy, mm, dd, group = nil)
179
+ raise LoginError, "not login yet" unless login?
180
+ vals = [group ? "#{group}.g" : "d",
181
+ @current_account[0],
182
+ yyyy, mm, dd]
183
+ yield @agent.get("http://%s.hatena.ne.jp/%s/edit?date=%04d%02d%02d" % vals)
184
+ end
185
+ end
186
+ end
187
+
@@ -0,0 +1,266 @@
1
+ require 'test/unit'
2
+ require 'flexmock'
3
+ require 'hatenadiary'
4
+
5
+
6
+ class TestHatenaDiaryAPI < Test::Unit::TestCase
7
+ include FlexMock::TestCase
8
+
9
+ def test_default_mechanizer
10
+ HatenaDiary::Client.mechanizer = nil
11
+ assert_equal Mechanize, HatenaDiary::Client.mechanizer
12
+ end
13
+
14
+ def setup
15
+ @username = 'USERNAME'
16
+ @password = 'PASSWORD'
17
+ @proxy_url = "PROXY_URL"
18
+ @proxy_port = "PROXY_PORT"
19
+ end
20
+
21
+ def test_api_login
22
+ flexmock HatenaDiary::Client
23
+ HatenaDiary::Client.should_receive(:login)
24
+ HatenaDiary.login
25
+ end
26
+
27
+ def setup_mocks
28
+ flexmock HatenaDiary::Client
29
+ @client = flexmock("client")
30
+ end
31
+
32
+ def test_login
33
+ setup_mocks
34
+ HatenaDiary::Client.should_receive(:new).with(@username, @password).and_return(@client)
35
+ @client.should_receive(:transaction).and_yield('block delegate check')
36
+ HatenaDiary::Client.login(@username, @password) do |str|
37
+ assert_equal 'block delegate check', str
38
+ end
39
+ end
40
+
41
+ def test_login_with_proxy
42
+ setup_mocks
43
+ HatenaDiary::Client.should_receive(:new).with(@username, @password).and_return(@client)
44
+ @client.should_receive(:set_proxy).with(@proxy_url, @proxy_port)
45
+ @client.should_receive(:transaction).yields('block delegate check')
46
+ HatenaDiary::Client.login(@username, @password, [@proxy_url, @proxy_port]) do |str|
47
+ assert_equal 'block delegate check', str
48
+ end
49
+ end
50
+
51
+ def test_login_without_block
52
+ setup_mocks
53
+ HatenaDiary::Client.should_receive(:new).with(@username, @password).and_return(@client)
54
+ assert_equal @client, HatenaDiary::Client.login(@username, @password)
55
+ end
56
+
57
+ def test_login_without_block_with_proxy
58
+ setup_mocks
59
+ HatenaDiary::Client.should_receive(:new).with(@username, @password).and_return(@client)
60
+ @client.should_receive(:set_proxy).with(@proxy_url, @proxy_port)
61
+ assert_equal @client, HatenaDiary::Client.login(@username, @password, [@proxy_url, @proxy_port])
62
+ end
63
+ end
64
+
65
+
66
+ class TestHatenaDiary < Test::Unit::TestCase
67
+ include FlexMock::TestCase
68
+
69
+ def setup
70
+ @username = 'USERNAME'
71
+ @password = 'PASSWORD'
72
+ @agent = flexmock("agent")
73
+ @client = HatenaDiary::Client.new(@username, @password, @agent)
74
+ end
75
+
76
+ def test_set_proxy
77
+ proxy_url = 'PROXY_URL'
78
+ proxy_port = 'PROXY_PORT'
79
+ @agent.should_receive(:set_proxy).with(proxy_url, proxy_port)
80
+ @client.set_proxy(proxy_url, proxy_port)
81
+ end
82
+
83
+ def test_logout_without_login
84
+ @client.logout
85
+ end
86
+
87
+ module MockHash
88
+ def [](k)
89
+ (@_table ||= {})[k]
90
+ end
91
+
92
+ def []=(k, v)
93
+ (@_table ||= {})[k] = v
94
+ end
95
+
96
+ def to_h
97
+ @_table ||= {}
98
+ end
99
+ end
100
+
101
+ def login_mocking(submit_response_page_title)
102
+ login_page = flexmock("login_page")
103
+ form = flexmock("form").extend(MockHash)
104
+ forms = [form]
105
+ response = flexmock("response")
106
+ @agent.should_receive(:get).with("https://www.hatena.ne.jp/login").and_return(login_page)
107
+ login_page.should_receive(:forms).and_return(forms)
108
+ form.should_receive(:submit).and_return(response)
109
+ response.should_receive(:title).and_return(submit_response_page_title)
110
+ form
111
+ end
112
+
113
+ def logout_mocking
114
+ @agent.should_receive(:get).with("https://www.hatena.ne.jp/logout")
115
+ end
116
+
117
+ def test_login_and_logout
118
+ # before login
119
+ assert !@client.login?
120
+ # login
121
+ form = login_mocking("Hatena")
122
+ @client.login
123
+ assert @client.login?
124
+ assert_equal form["name"], @username
125
+ assert_equal form["password"], @password
126
+ assert_equal form["persistent"], "true"
127
+ # logout
128
+ logout_mocking
129
+ @client.logout
130
+ assert !@client.login?
131
+ end
132
+
133
+ def test_login_failure
134
+ login_mocking "Login - Hatena"
135
+ begin
136
+ @client.login
137
+ rescue HatenaDiary::LoginError => ex
138
+ assert_equal @username, ex.username
139
+ assert_equal @password, ex.password
140
+ else
141
+ flunk "login error must be raised."
142
+ end
143
+ end
144
+
145
+ def test_login_if_hatena_changed
146
+ login_mocking "*jumbled pagetitle*"
147
+ begin
148
+ @client.login
149
+ rescue Exception => ex
150
+ assert /must not happen/ =~ ex.message
151
+ else
152
+ flunk "exception must be raised"
153
+ end
154
+ end
155
+
156
+ def test_transaction
157
+ assert !@client.login?
158
+ login_mocking "Hatena"
159
+ logout_mocking
160
+ @client.transaction do |client|
161
+ assert @client.equal?(client)
162
+ assert @client.login?
163
+ end
164
+ assert !@client.login?
165
+ end
166
+
167
+ def test_transaction_without_block
168
+ assert !@client.login?
169
+ assert_raise LocalJumpError do
170
+ @client.transaction
171
+ end
172
+ assert !@client.login?
173
+ end
174
+
175
+ def post_mocking(host, date_str)
176
+ edit_page = flexmock("edit_page")
177
+ form = flexmock("form").extend(MockHash)
178
+ button = Object.new
179
+ login_mocking "Hatena"
180
+ logout_mocking
181
+ @agent.should_receive(:get).with("http://#{host}.hatena.ne.jp/#{@username}/edit?date=#{date_str}").and_return(edit_page)
182
+ edit_page.should_receive(:form_with).with(:name => 'edit').and_return(form)
183
+ form.should_receive(:button_with).with(:name => 'edit').and_return(button)
184
+ @agent.should_receive(:submit).with(form, button)
185
+ form
186
+ end
187
+
188
+ def test_post
189
+ form = post_mocking("d", "12340506")
190
+ @client.transaction do |client|
191
+ client.post 1234, 5, 6, 'TITLE', 'BODY'
192
+ end
193
+ expected = {
194
+ "year" => "1234",
195
+ "month" => "05",
196
+ "day" => "06",
197
+ "title" => "TITLE",
198
+ "body" => "BODY",
199
+ }
200
+ assert_equal expected, form.to_h
201
+ assert !form["trivial"]
202
+ end
203
+
204
+ def test_post_trivial
205
+ form = post_mocking("d", "20071108")
206
+ @client.transaction do |client|
207
+ client.post 2007, 11, 8, 'TITLE', 'BODY', :trivial => true
208
+ end
209
+ assert_equal "true", form["trivial"]
210
+ end
211
+
212
+ def test_post_group
213
+ post_mocking "hoge.g", "12340506"
214
+ @client.transaction do |client|
215
+ client.post 1234, 5, 6, 'TITLE', 'BODY', :group => 'hoge'
216
+ end
217
+ end
218
+
219
+ def test_post_group_trivial
220
+ form = post_mocking("hoge.g", "12340506")
221
+ @client.transaction do |client|
222
+ client.post 1234, 5, 6, 'TITLE', 'BODY', :group => 'hoge', :trivial => true
223
+ end
224
+ assert_equal "true", form["trivial"]
225
+ end
226
+
227
+ def test_post_without_login
228
+ assert_raise HatenaDiary::LoginError do
229
+ @client.post 1999, 5, 26, "TITLE", "BODY\n"
230
+ end
231
+ end
232
+
233
+ def delete_mocking(host, date_str)
234
+ edit_page = flexmock("edit_page")
235
+ form = flexmock("form").extend(MockHash)
236
+ forms = [form]
237
+ button = Object.new
238
+ login_mocking "Hatena"
239
+ logout_mocking
240
+ @agent.should_receive(:get).with("http://#{host}.hatena.ne.jp/#{@username}/edit?date=#{date_str}").and_return(edit_page)
241
+ edit_page.should_receive(:forms).returns(forms)
242
+ form.should_receive(:submit)
243
+ form
244
+ end
245
+
246
+ def test_delete
247
+ delete_mocking "d", "12340506"
248
+ @client.transaction do |client|
249
+ client.delete 1234, 5, 6
250
+ end
251
+ end
252
+
253
+ def test_delete_group
254
+ delete_mocking "piyo.g", "12340506"
255
+ @client.transaction do |client|
256
+ client.delete 1234, 5, 6, :group => 'piyo'
257
+ end
258
+ end
259
+
260
+ def test_delete_without_login
261
+ assert_raise HatenaDiary::LoginError do
262
+ @client.delete 2009, 8, 30
263
+ end
264
+ end
265
+ end
266
+
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hatenadiary-legacy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - arikui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mechanize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: test-unit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: flexmock
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A client for Hatena Diary to post and delete blog entries.
56
+ email: arikui.ruby@gmail.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files:
60
+ - README
61
+ - LICENSE
62
+ - ChangeLog
63
+ files:
64
+ - ChangeLog
65
+ - LICENSE
66
+ - README
67
+ - lib/hatenadiary.rb
68
+ - test/test_hatenadiary.rb
69
+ homepage: http://wiki.github.com/arikui1911/hatenadiary-legacy
70
+ licenses: []
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options:
74
+ - "--title"
75
+ - hatenadiary-legacy documentation
76
+ - "--opname"
77
+ - index.html
78
+ - "--line-numbers"
79
+ - "--main"
80
+ - README
81
+ - "--inline-source"
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.4.5
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: It is a library provides a client for Hatena Diary to post and delete blog
100
+ entries.
101
+ test_files:
102
+ - test/test_hatenadiary.rb