hatenabm 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/ChangeLog +6 -0
  2. data/README +53 -0
  3. data/lib/hatenabm.rb +160 -0
  4. data/test/test_hatenabm.rb +75 -0
  5. metadata +44 -0
data/ChangeLog ADDED
@@ -0,0 +1,6 @@
1
+ = HatenaBM Changelog
2
+
3
+ == Version 0.1.0
4
+ initial release
5
+
6
+ * initial release
data/README ADDED
@@ -0,0 +1,53 @@
1
+ = HatenaBM
2
+
3
+ HatenaBM is Hatena Bookmark (Japanese Social Bookmark Service) binding for Ruby.
4
+
5
+ Hatena Bookmark : http://b.hatena.ne.jp
6
+ Hatena Bookmark API Reference (Japanese) : http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%C3%A5%AF%A5%DE%A1%BC%A5%AFAtomAPI
7
+
8
+ == Installation
9
+
10
+ $ sudo gem install hatenabm
11
+
12
+ == Usage
13
+
14
+ require 'rubygems'
15
+ require 'hatenabm'
16
+ require 'pp'
17
+
18
+ # initialize
19
+ hbm = HatenaBM.new(
20
+ :user => "username",
21
+ :pass => "password"
22
+ )
23
+
24
+ # post new bookmark
25
+ hbm.post(
26
+ :title => "bookmark's title", # title
27
+ :link => "http://www.example.com", # url
28
+ :tags => "foo bar", # tags (separate space)
29
+ :summary => "this is example post." # description of this bookmark
30
+ )
31
+
32
+ # show recent bookmarks (Atom Format)
33
+ pp hbm.recent
34
+
35
+ # show specified bookmark
36
+ pp hbm.get(:eid => "4211817") # eid - bookmark's id
37
+
38
+ # modify posted bookmark
39
+ hbm.modify(
40
+ :eid => "421817", # eid
41
+ :tags => "bar com", # tags (separate space)
42
+ :summary => "modify bookmark's description"
43
+ )
44
+
45
+ # delete specified bookmark
46
+ hbm.delete(:eid => "421817") # eid
47
+
48
+ == Author
49
+ - drawnboy ( http://d.hatena.ne.jp/drawnboy ) <drawn.boy@gmail.com.nospam>
50
+ - s-tanaka ( http://d.hatena.ne.jp/ha-tan )is written by get_wsse method
51
+ - gorou ( http://rails2u.com )
52
+
53
+ License:: 2-clause BSD Lisence
data/lib/hatenabm.rb ADDED
@@ -0,0 +1,160 @@
1
+ require 'net/http'
2
+ require 'time'
3
+ require 'digest/sha1'
4
+ require 'base64'
5
+
6
+ # if using ruby 1.9.0 earlier, define HTTP DELETE Method
7
+ if RUBY_VERSION < '1.9.0'
8
+ module Net
9
+ class HTTP
10
+ class Delete < HTTPRequest
11
+ METHOD = 'DELETE'
12
+ REQUEST_HAS_BODY = false
13
+ RESPONSE_HAS_BODY = true
14
+ end
15
+ def delete(path,initheader = nil)
16
+ res = request(Delete.new(path,initheader))
17
+ res.value unless @newimpl
18
+ res
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ class HatenaBM
25
+
26
+ VERSION = "0.0.3"
27
+
28
+ HATENA_URL = "b.hatena.ne.jp"
29
+ FEED_PATH = "/atom/feed"
30
+ POST_PATH = "/atom/post"
31
+ EDIT_PATH = "/atom/edit/"
32
+
33
+ def initialize(options)
34
+ user = options[:user]
35
+ pass = options[:pass]
36
+ @wsse = get_wsse(user, pass)
37
+ end
38
+
39
+ # Post new bookmark
40
+ # args: title, link, summary, tags
41
+ def post(options)
42
+ title = options[:title]
43
+ link = options[:link]
44
+ summary = options[:summary] || nil
45
+ tags = options[:tags] || nil
46
+ header = {
47
+ "Content-Type" => 'application/x.atom+xml; charset="utf-8"',
48
+ "X-WSSE" => @wsse
49
+ }
50
+ tags = "[#{tags.split(/\s/).join('][')}]" unless tags.nil?
51
+
52
+ data =<<-EOF
53
+ <?xml version="1.0"?>
54
+ <entry xmlns="http://purl.org/atom/ns#">
55
+ <title>#{title}</title>
56
+ <link rel="related" type="text/html" href="#{link}" />
57
+ <summary type="text/plain">#{tags}#{summary}</summary>
58
+ </entry>
59
+ EOF
60
+
61
+ Net::HTTP.version_1_2
62
+ Net::HTTP.start(HATENA_URL){|http|
63
+ response = http.post(POST_PATH, toutf8(data), header)
64
+ raise "Post Error : #{response.code} - #{response.message}" unless response.code == "201"
65
+ return true
66
+ }
67
+ end
68
+
69
+ # Get recent bookmarks
70
+ def recent
71
+ header = { "X-WSSE" => @wsse }
72
+ Net::HTTP.version_1_2
73
+ Net::HTTP.start(HATENA_URL){|http|
74
+ response = http.get(FEED_PATH, header)
75
+ return response.body
76
+ }
77
+ end
78
+
79
+ # Get specified bookmark
80
+ # args : eid
81
+ def get(options)
82
+ eid = options[:eid] || nil
83
+ raise "Get Error : Invalid eid" if eid.nil?
84
+ api_path = EDIT_PATH + eid
85
+ header = { "X-WSSE"=> @wsse }
86
+
87
+ Net::HTTP.version_1_2
88
+ Net::HTTP.start(HATENA_URL){|http|
89
+ response = http.get(api_path, header)
90
+ return response.body
91
+ }
92
+ end
93
+
94
+ # Modify specified bookmark
95
+ # args: eid, title, tags
96
+ def modify(options)
97
+ eid = options[:eid] || nil
98
+ title = options[:title] || nil
99
+ tags = options[:tags] || nil
100
+ summary = options[:summary] || nil
101
+
102
+ raise "Edit Error : need eid" if eid.nil?
103
+
104
+ api_path = EDIT_PATH + eid
105
+ header = { "X-WSSE" => @wsse }
106
+ tags = "[#{tags.split(/\s/).join('][')}]" unless tags.nil?
107
+
108
+ data = "<?xml version=\"1.0\"?>"
109
+ data << "<entry xmlns=\"http://purl.org/atom/ns#\">"
110
+ data << "<title>#{title}</title>" unless title.nil?
111
+ data << "<summary type=\"text/plain\">#{tags}#{summary}</summary>" unless summary.nil?
112
+ data << "</entry>"
113
+
114
+ Net::HTTP.version_1_2
115
+ Net::HTTP.start(HATENA_URL){|http|
116
+ response = http.put(api_path, toutf8(data), header)
117
+ raise "Edit Error : #{response.code} - #{response.message}" unless response.code == "200"
118
+ return true
119
+ }
120
+
121
+ end
122
+
123
+ # Delete specified bookmark
124
+ # args: eid
125
+ def delete(options)
126
+ eid = options[:eid] || nil
127
+ raise "Delete Error : Invalid eid" if eid.nil?
128
+ api_path = EDIT_PATH + eid
129
+ header = { "X-WSSE" => @wsse }
130
+
131
+ Net::HTTP.version_1_2
132
+ Net::HTTP.start(HATENA_URL){|http|
133
+ response = http.delete(api_path, header)
134
+ raise "Delete Error : #{response.code} - #{response.message}" unless response.code == "200"
135
+ return true
136
+ }
137
+ end
138
+
139
+ # Calculate WSSE Header method
140
+ private
141
+ def get_wsse(user, pass)
142
+ created = Time.now.iso8601
143
+
144
+ nonce = ''
145
+ 20.times do
146
+ nonce << rand(256).chr
147
+ end
148
+
149
+ passdigest = Digest::SHA1.digest(nonce + created + pass)
150
+
151
+ return "UsernameToken Username=\"#{user}\", " +
152
+ "PasswordDigest=\"#{Base64.encode64(passdigest).chomp}\", " +
153
+ "Nonce=\"#{Base64.encode64(nonce).chomp}\", " +
154
+ "Created=\"#{created}\""
155
+ end
156
+
157
+ def toutf8(str)
158
+ Kconv.toutf8(str)
159
+ end
160
+ end
@@ -0,0 +1,75 @@
1
+ require 'test/unit'
2
+ require 'hatenabm'
3
+
4
+ class TestHatenaBM < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @hatenabm = HatenaBM.new(:user => "username", :pass => "password")
8
+ end
9
+
10
+ def test_recent
11
+ assert_match(
12
+ /^<\?xml version=\"1\.0\" encoding=\"utf\-8\"\?>/,
13
+ @hatenabm.recent
14
+ )
15
+ end
16
+
17
+ def test_post
18
+ assert_equal(
19
+ true,
20
+ @hatenabm.post(
21
+ :title => "bookmark's title",
22
+ :link => "http://www.example.com",
23
+ :tags => "foo bar",
24
+ :summary => "this is example post."
25
+ )
26
+ )
27
+ end
28
+
29
+ def test_get
30
+ assert_match(
31
+ /^<\?xml version=\"1\.0\" encoding=\"utf\-8\"\?>/,
32
+ @hatenabm.get(:eid => "4211817")
33
+ )
34
+ end
35
+
36
+ def test_modify
37
+ assert(
38
+ true,
39
+ @hatenabm.modify(
40
+ :eid => "421817",
41
+ :tags => "bar com",
42
+ :summary => "modify bookmark's description"
43
+ )
44
+ )
45
+ end
46
+
47
+ def test_delete
48
+ assert(
49
+ true,
50
+ @hatenabm.delete(:eid => "421817")
51
+ )
52
+ end
53
+
54
+ def test_invalid_access
55
+ assert_raises(RuntimeError) do
56
+ @hatenabm.post(:title => "invalid", :summary => "summary")
57
+ end
58
+
59
+ assert_raises(RuntimeError) do
60
+ @hatenabm.get(:invalide => "421817")
61
+ end
62
+
63
+ assert_raises(RuntimeError) do
64
+ @hatenabm.modify(:summary => "summary")
65
+ end
66
+
67
+ assert_raises(RuntimeError) do
68
+ @hatenabm.delete(:invalid => "421817")
69
+ end
70
+
71
+ assert_raises(RuntimeError) do
72
+ @hatenabm.delete(:eid => "invalid")
73
+ end
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: hatenabm
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2005-10-14
8
+ summary: Hatena Bookmark AtomAPI binding for Ruby
9
+ require_paths:
10
+ - lib
11
+ email: drawn.boy@gmail.com.nospam
12
+ homepage: http://hatenabm.rubyforge.org
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: cool
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - drawnboy
29
+ files:
30
+ - test/test_hatenabm.rb
31
+ - lib/hatenabm.rb
32
+ - README
33
+ - ChangeLog
34
+ test_files:
35
+ - test/test_hatenabm.rb
36
+ rdoc_options:
37
+ - "--main"
38
+ - README
39
+ extra_rdoc_files:
40
+ - README
41
+ executables: []
42
+ extensions: []
43
+ requirements: []
44
+ dependencies: []