weibo 0.0.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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Scott Ballantyne
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = weibo
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Scott Ballantyne. See LICENSE for details.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "weibo"
8
+ gem.summary = %Q{a gem to help api integration for 新浪微博 (t.sina.com.cn)}
9
+ gem.description = %Q{this gem is an adaptation of John Nunemaker's Twitter gem. I modified it to make api integration for 新浪微博 (t.sina.com.cn) easier.}
10
+ gem.email = "scott@ekohe.com"
11
+ gem.homepage = "http://github.com/ballantyne/weibo"
12
+ gem.authors = ["Scott Ballantyne"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "weibo #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,66 @@
1
+ require 'forwardable'
2
+ require 'rubygems'
3
+
4
+ gem 'oauth', '~> 0.4.1'
5
+ require 'oauth'
6
+
7
+ gem 'hashie'
8
+ require 'hashie'
9
+
10
+ gem 'httparty', '>= 0.5.2'
11
+ require 'httparty'
12
+
13
+ module Weibo
14
+ module Config
15
+ mattr_accessor :api_key
16
+ end
17
+ class WeiboError < StandardError
18
+ attr_reader :data
19
+
20
+ def initialize(data)
21
+ @data = data
22
+
23
+ super
24
+ end
25
+ end
26
+ class RepeatedWeiboText < WeiboError; end
27
+ class RateLimitExceeded < WeiboError; end
28
+ class Unauthorized < WeiboError; end
29
+ class General < WeiboError; end
30
+
31
+ class Unavailable < StandardError; end
32
+ class InformWeibo < StandardError; end
33
+ class NotFound < StandardError; end
34
+ end
35
+
36
+ module Hashie
37
+ class Mash
38
+
39
+ # Converts all of the keys to strings, optionally formatting key name
40
+ def rubyify_keys!
41
+ keys.each{|k|
42
+ v = delete(k)
43
+ new_key = k.to_s.underscore
44
+ self[new_key] = v
45
+ v.rubyify_keys! if v.is_a?(Hash)
46
+ v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
47
+ }
48
+ self
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ directory = File.expand_path(File.dirname(__FILE__))
55
+
56
+ require File.join(directory, 'weibo', 'oauth')
57
+ require File.join(directory, 'weibo', 'oauth_hack')
58
+ require File.join(directory, 'weibo', 'httpauth')
59
+ require File.join(directory, 'weibo', 'request')
60
+ require File.join(directory, 'weibo', 'base')
61
+
62
+ # code is an adaptation of the twitter gem by John Nunemaker
63
+ # http://github.com/jnunemaker/twitter
64
+ # Copyright (c) 2009 John Nunemaker
65
+ #
66
+ # made to work with china's leading twitter service, 新浪微博
@@ -0,0 +1,332 @@
1
+ module Weibo
2
+ class Base
3
+ extend Forwardable
4
+
5
+ def_delegators :client, :get, :post, :put, :delete
6
+
7
+ attr_reader :client
8
+
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ def provinces(query={})
14
+ perform_get('/provinces.json', :query => query)
15
+ end
16
+
17
+ def status_search(q, query={})
18
+ q = URI.escape(q)
19
+ if q != ""
20
+ query = {:q => q}.merge(query)
21
+ end
22
+ perform_get('/statuses/search.json', :query => query)
23
+ end
24
+
25
+ def user_search(q, query={})
26
+ q = URI.escape(q)
27
+ if q != ""
28
+ query = {:q => q}.merge(query)
29
+ end
30
+ perform_get('/users/search.json', :query => query)
31
+ end
32
+
33
+ # statuses/public_timeline 最新公共微博
34
+ def firehose
35
+ perform_get('/statuses/public_timeline.json')
36
+ end
37
+
38
+
39
+ # Options: since_id, max_id, count, page
40
+ def home_timeline(query={})
41
+ perform_get('/statuses/home_timeline.json', :query => query)
42
+ end
43
+
44
+
45
+ # statuses/friends_timeline 最新关注人微博 (别名: statuses/home_timeline)
46
+ # Options: since_id, max_id, count, page, since
47
+ def friends_timeline(query={})
48
+ perform_get('/statuses/friends_timeline.json', :query => query)
49
+ end
50
+
51
+ # statuses/user_timeline 用户发表微博列表
52
+ # Options: id, user_id, screen_name, since_id, max_id, page, since, count
53
+ def user_timeline(query={})
54
+ perform_get('/statuses/user_timeline.json', :query => query)
55
+ end
56
+
57
+ # statuses/show 获取单条
58
+ def status(id)
59
+ perform_get("/statuses/show/#{id}.json")
60
+ end
61
+
62
+ def counts(query={})
63
+ perform_get("/statuses/counts.json", :query => query)
64
+ end
65
+
66
+ # Options: count
67
+ def retweets(id, query={ })
68
+ perform_get("/statuses/retweets/#{id}.json", :query => query)
69
+ end
70
+
71
+ # * statuses/update 发表微博
72
+ # Options: in_reply_to_status_id
73
+ def update(status, query={})
74
+ perform_post("/statuses/update.json", :body => {:status => status}.merge(query))
75
+ end
76
+
77
+ # DEPRECATED: Use #mentions instead
78
+ #
79
+ # Options: since_id, max_id, since, page
80
+ # statuses/mentions 最新 @用户的
81
+ def replies(query={})
82
+ warn("DEPRECATED: #replies is deprecated by Twitter; use #mentions instead")
83
+ perform_get('/statuses/mentions.json', :query => query)
84
+ end
85
+
86
+ # statuses/mentions 最新 @用户的
87
+ # Options: since_id, max_id, count, page
88
+ def mentions(query={})
89
+ perform_get('/statuses/mentions.json', :query => query)
90
+ end
91
+
92
+
93
+ def upload(status, file)
94
+ PostBodyHack.apply_hack(:status => status) do
95
+ perform_post("/statuses/upload.json", build_multipart_bodies(:pic => file, :status => status))
96
+ end
97
+ end
98
+
99
+ def reply(comment, query={})
100
+ perform_post("/statuses/reply.json", :body => {:comment => comment}.merge(query))
101
+ end
102
+
103
+ def comment(id, comment, query={})
104
+ perform_post("/statuses/comment.json", :body => {:id => id, :comment => comment}.merge(query))
105
+ end
106
+
107
+ def unread
108
+ perform_get('/statuses/unread.json')
109
+ end
110
+
111
+ def comments_timeline(query={})
112
+ perform_get('/statuses/comments_timeline.json', :query => query)
113
+ end
114
+
115
+ def comments_by_me(query={})
116
+ perform_get('/statuses/comments_by_me.json', :query => query)
117
+ end
118
+
119
+ def comments(query={})
120
+ perform_get('/statuses/comments.json', :query => query)
121
+ end
122
+
123
+ def comment_destroy(id)
124
+ perform_post("/statuses/comment_destroy/#{id}.json")
125
+ end
126
+
127
+ # statuses/destroy 删除
128
+ def status_destroy(id)
129
+ perform_post("/statuses/destroy/#{id}.json")
130
+ end
131
+
132
+ # statuses/repost 转发
133
+ def retweet(id)
134
+ perform_post("/statuses/repost/#{id}.json")
135
+ end
136
+
137
+ # statuses/repost 转发
138
+ def repost(id, status={})
139
+ perform_post("/statuses/repost.json", :body => {:id => id}.merge(status))
140
+ end
141
+
142
+ # statuses/friends 关注人列表
143
+ # Options: id, user_id, screen_name, page
144
+ def friends(query={})
145
+ perform_get('/statuses/friends.json', :query => query)
146
+ end
147
+
148
+ # statuses/followers 粉丝列表
149
+ # Options: id, user_id, screen_name, page
150
+ def followers(query={})
151
+ perform_get('/statuses/followers.json', :query => query)
152
+ end
153
+
154
+ def user(id, query={})
155
+ perform_get("/users/show/#{id}.json", :query => query)
156
+ end
157
+
158
+ def user_show(query={})
159
+ perform_get("/users/show.json", :query => query)
160
+ end
161
+
162
+
163
+ # direct_messages 我的私信列表
164
+ # Options: since, since_id, page
165
+ def direct_messages(query={})
166
+ perform_get("/direct_messages.json", :query => query)
167
+ end
168
+
169
+ # direct_messages/sent 我发送的私信列表
170
+ # Options: since, since_id, page
171
+ def direct_messages_sent(query={})
172
+ perform_get("/direct_messages/sent.json", :query => query)
173
+ end
174
+
175
+
176
+ # direct_messages/new 发送私信
177
+ def direct_message_create(user, screen_name, text)
178
+ perform_post("/direct_messages/new.json", :body => {:id => user, :screen_name => screen_name, :text => text})
179
+ end
180
+
181
+ # direct_messages/destroy 删除一条私信
182
+ def direct_message_destroy(id)
183
+ perform_post("/direct_messages/destroy/#{id}.json")
184
+ end
185
+
186
+ # friendships/create 关注某用户
187
+ def friendship_create(id, follow=false)
188
+ body = {}
189
+ body.merge!(:follow => follow) if follow
190
+ perform_post("/friendships/create/#{id}.json", :body => body)
191
+ end
192
+
193
+ # friendships/destroy 取消关注
194
+ def friendship_destroy(id)
195
+ perform_delete("/friendships/destroy/#{id}.json")
196
+ end
197
+
198
+ # friendships/exists 是否关注某用户(推荐使用friendships/show)
199
+ def friendship_exists?(a, b)
200
+ perform_get("/friendships/exists.json", :query => {:user_a => a, :user_b => b})
201
+ end
202
+
203
+ # friendships/show 是否关注某用户
204
+ def friendship_show(query)
205
+ perform_get("/friendships/show.json", :query => query)
206
+ end
207
+
208
+ # friends/ids 关注列表
209
+ # Options: id, user_id, screen_name
210
+ def friend_ids(query={})
211
+ perform_get("/friends/ids.json", :query => query, :mash => false)
212
+ end
213
+
214
+ # followers/ids 粉丝列表
215
+ # Options: id, user_id, screen_name
216
+ def follower_ids(query={})
217
+ perform_get("/followers/ids.json", :query => query, :mash => false)
218
+ end
219
+
220
+ # account/verify_credentials 验证身份是否合法
221
+ def verify_credentials
222
+ perform_get("/account/verify_credentials.json")
223
+ end
224
+
225
+ # unimplemented
226
+ # account/end_session 退出
227
+ def end_session(body={})
228
+ perform_post('/account/end_session.json')
229
+ end
230
+
231
+ # account/update_profile_image 更改头像
232
+ # file should respond to #read and #path
233
+ def update_profile_image(file)
234
+ PostBodyHack.apply_hack do
235
+ perform_post('/account/update_profile_image.json', build_multipart_bodies(:image => file))
236
+ end
237
+ end
238
+
239
+ # account/rate_limit_status 查看当前频率限制
240
+ def rate_limit_status
241
+ perform_get('/account/rate_limit_status.json')
242
+ end
243
+
244
+ # account/update_profile 更改资料
245
+ # One or more of the following must be present:
246
+ # name, email, url, location, description
247
+ def update_profile(body={})
248
+ perform_post('/account/update_profile.json', :body => body)
249
+ end
250
+
251
+ # favorites 收藏列表
252
+ # Options: id, page
253
+ def favorites(query={})
254
+ perform_get('/favorites.json', :query => query)
255
+ end
256
+
257
+ # favorites/create 添加收藏
258
+ def favorite_create(id)
259
+ perform_post("/favorites/create/#{id}.json")
260
+ end
261
+
262
+ # favorites/destroy 删除收藏
263
+ def favorite_destroy(id)
264
+ perform_post("/favorites/destroy/#{id}.json")
265
+ end
266
+
267
+ def help
268
+ perform_get('/help/test.json')
269
+ end
270
+
271
+ protected
272
+ def self.mime_type(file)
273
+ case
274
+ when file =~ /\.jpg/ then 'image/jpg'
275
+ when file =~ /\.gif$/ then 'image/gif'
276
+ when file =~ /\.png$/ then 'image/png'
277
+ else 'application/octet-stream'
278
+ end
279
+ end
280
+ def mime_type(f) self.class.mime_type(f) end
281
+
282
+ CRLF = "\r\n"
283
+ def self.build_multipart_bodies(parts)
284
+ boundary = Time.now.to_i.to_s(16)
285
+ body = ""
286
+ parts.each do |key, value|
287
+ esc_key = CGI.escape(key.to_s)
288
+ body << "--#{boundary}#{CRLF}"
289
+ if value.respond_to?(:read)
290
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
291
+ body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
292
+ body << value.read
293
+ else
294
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
295
+ end
296
+ body << CRLF
297
+ end
298
+ body << "--#{boundary}--#{CRLF*2}"
299
+ {
300
+ :body => body,
301
+ :headers => {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
302
+ }
303
+ end
304
+ def build_multipart_bodies(parts) self.class.build_multipart_bodies(parts) end
305
+
306
+ private
307
+ def perform_get(path, options={})
308
+ options[:query] = {} unless options[:query]
309
+ options[:query][:source] = Weibo::Config.api_key
310
+ Weibo::Request.get(self, path, options)
311
+ end
312
+
313
+ def perform_post(path, options={})
314
+ options[:query] = {} unless options[:query]
315
+ options[:query][:source] = Weibo::Config.api_key
316
+ Weibo::Request.post(self, path, options)
317
+ end
318
+
319
+ def perform_put(path, options={})
320
+ options[:query] = {} unless options[:query]
321
+ options[:query][:source] = Weibo::Config.api_key
322
+
323
+ Weibo::Request.put(self, path, options)
324
+ end
325
+
326
+ def perform_delete(path, options={})
327
+ options[:query] = {} unless options[:query]
328
+ options[:query][:source] = Weibo::Config.api_key
329
+ Weibo::Request.delete(self, path, options)
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,37 @@
1
+ module Weibo
2
+ class HTTPAuth
3
+ include HTTParty
4
+ format :plain
5
+
6
+ attr_reader :username, :password, :options
7
+
8
+ def initialize(username, password, options={})
9
+ @username, @password = username, password
10
+ @options = {:ssl => false}.merge(options)
11
+ options[:api_endpoint] ||= "api.t.sina.com.cn"
12
+
13
+ self.class.base_uri "http#{'s' if options[:ssl]}://#{options[:api_endpoint]}"
14
+ end
15
+
16
+ def get(uri, headers={})
17
+ self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
18
+ end
19
+
20
+ def post(uri, body={}, headers={})
21
+ self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
22
+ end
23
+
24
+ def put(uri, body={}, headers={})
25
+ self.class.put(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
26
+ end
27
+
28
+ def delete(uri, body={}, headers={})
29
+ self.class.delete(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
30
+ end
31
+
32
+ private
33
+ def basic_auth
34
+ @basic_auth ||= {:username => @username, :password => @password}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ module Weibo
2
+ class OAuth
3
+ extend Forwardable
4
+ def_delegators :access_token, :get, :post, :put, :delete
5
+
6
+ attr_reader :ctoken, :csecret, :consumer_options
7
+
8
+ def initialize(ctoken, csecret, options={})
9
+ @ctoken, @csecret, @consumer_options = ctoken, csecret, {}
10
+
11
+ if options[:sign_in]
12
+ @consumer_options[:authorize_path] = '/oauth/authorize'
13
+ end
14
+ end
15
+
16
+ def consumer
17
+ @consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => 'http://api.t.sina.com.cn'}.merge(consumer_options))
18
+ end
19
+
20
+ def set_callback_url(url)
21
+ clear_request_token
22
+ request_token(:oauth_callback => url)
23
+ end
24
+
25
+ # Note: If using oauth with a web app, be sure to provide :oauth_callback.
26
+ # Options:
27
+ # :oauth_callback => String, url that twitter should redirect to
28
+ def request_token(options={})
29
+ @request_token ||= consumer.get_request_token(options)
30
+ end
31
+
32
+ # For web apps use params[:oauth_verifier], for desktop apps,
33
+ # use the verifier is the pin that twitter gives users.
34
+ def authorize_from_request(rtoken, rsecret, verifier_or_pin)
35
+ request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
36
+ access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
37
+ @atoken, @asecret = access_token.token, access_token.secret
38
+ end
39
+
40
+ def access_token
41
+ @access_token ||= ::OAuth::AccessToken.new(consumer, @atoken, @asecret)
42
+ end
43
+
44
+ def authorize_from_access(atoken, asecret)
45
+ @atoken, @asecret = atoken, asecret
46
+ end
47
+
48
+ private
49
+ def clear_request_token
50
+ @request_token = nil
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ # This hack is used for Weibo upload
2
+ # as Sina Weibo doesn't follow the god damned OAuth specifications!!!
3
+
4
+ module PostBodyHack
5
+ mattr_accessor :parameters
6
+ def self.apply_hack(params={}, &block)
7
+ self.parameters = params.stringify_keys
8
+ rv = yield
9
+ self.parameters = {}
10
+ rv
11
+ end
12
+ end
13
+
14
+ class OAuth::RequestProxy::Base
15
+ def parameters_for_signature
16
+ params_for_signature = parameters.reject { |k,v| k == "oauth_signature" ||unsigned_parameters.include?(k) }
17
+ params_for_signature.merge!(PostBodyHack.parameters || {}) if defined? PostBodyHack
18
+ # params_for_signature
19
+ end
20
+ end
@@ -0,0 +1,122 @@
1
+ module Weibo
2
+ class Request
3
+ extend Forwardable
4
+
5
+ def self.get(client, path, options={})
6
+ new(client, :get, path, options).perform
7
+ end
8
+
9
+ def self.post(client, path, options={})
10
+ new(client, :post, path, options).perform
11
+ end
12
+
13
+ def self.put(client, path, options={})
14
+ new(client, :put, path, options).perform
15
+ end
16
+
17
+ def self.delete(client, path, options={})
18
+ new(client, :delete, path, options).perform
19
+ end
20
+
21
+ attr_reader :client, :method, :path, :options
22
+ def_delegators :client, :get, :post, :put, :delete
23
+
24
+ def initialize(client, method, path, options={})
25
+ @client, @method, @path, @options = client, method, path, {:mash => true, :query => {:source => Weibo::Config.api_key}}.merge(options)
26
+ end
27
+
28
+
29
+ def uri
30
+ @uri ||= begin
31
+ uri = URI.parse(path)
32
+ if options[:query] && options[:query] != {}
33
+ uri.query = to_query(options[:query])
34
+ options[:query]
35
+ end
36
+
37
+ uri.to_s
38
+ end
39
+ end
40
+
41
+ def perform
42
+ make_friendly(send("perform_#{method}"))
43
+ end
44
+
45
+ private
46
+ def perform_get
47
+ send(:get, uri, options[:headers])
48
+ end
49
+
50
+ def perform_post
51
+ send(:post, uri, options[:body], options[:headers])
52
+ end
53
+
54
+ def perform_put
55
+ send(:put, uri, options[:body], options[:headers])
56
+ end
57
+
58
+ def perform_delete
59
+ send(:delete, uri, options[:headers])
60
+ end
61
+
62
+ def make_friendly(response)
63
+ raise_errors(response)
64
+ data = parse(response)
65
+ options[:mash] ? mash(data) : data
66
+ end
67
+
68
+ def raise_errors(response)
69
+ case response.code.to_i
70
+ when 400
71
+ data = parse(response)
72
+ if data && data['error'] == "Error: repeated weibo text!"
73
+ raise RepeatedWeiboText.new(data), "(#{response.code}): #{response.message} - #{data['error']}"
74
+ else
75
+ raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
76
+ end
77
+ when 401
78
+ data = parse(response)
79
+ raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
80
+ when 403
81
+ data = parse(response)
82
+ raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
83
+ when 404
84
+ raise NotFound, "(#{response.code}): #{response.message}"
85
+ when 500
86
+ raise InformWeibo, "Weibo had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
87
+ when 502..503
88
+ raise Unavailable, "(#{response.code}): #{response.message}"
89
+ end
90
+ end
91
+
92
+ def parse(response)
93
+ Crack::JSON.parse(response.body)
94
+ end
95
+
96
+ def mash(obj)
97
+ if obj.is_a?(Array)
98
+ obj.map { |item| make_mash_with_consistent_hash(item) }
99
+ elsif obj.is_a?(Hash)
100
+ make_mash_with_consistent_hash(obj)
101
+ else
102
+ obj
103
+ end
104
+ end
105
+
106
+ # Lame workaround for the fact that mash doesn't hash correctly
107
+ def make_mash_with_consistent_hash(obj)
108
+ m = Hashie::Mash.new(obj)
109
+ def m.hash
110
+ inspect.hash
111
+ end
112
+ return m
113
+ end
114
+
115
+ def to_query(options)
116
+ options.inject([]) do |collection, opt|
117
+ collection << "#{opt[0]}=#{opt[1]}"
118
+ collection
119
+ end * '&'
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'weibo'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestWeibo < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{weibo}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Ballantyne"]
12
+ s.date = %q{2010-08-20}
13
+ s.description = %q{this gem is an adaptation of John Nunemaker's Twitter gem. I modified it to make api integration for 新浪微博 (t.sina.com.cn) easier.}
14
+ s.email = %q{scott@ekohe.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/weibo.rb",
27
+ "lib/weibo/base.rb",
28
+ "lib/weibo/httpauth.rb",
29
+ "lib/weibo/oauth.rb",
30
+ "lib/weibo/oauth_hack.rb",
31
+ "lib/weibo/request.rb",
32
+ "test/helper.rb",
33
+ "test/test_weibo.rb",
34
+ "weibo.gemspec"
35
+ ]
36
+ s.homepage = %q{http://github.com/ballantyne/weibo}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.6}
40
+ s.summary = %q{a gem to help api integration for 新浪微博 (t.sina.com.cn)}
41
+ s.test_files = [
42
+ "test/helper.rb",
43
+ "test/test_weibo.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
57
+ end
58
+ end
59
+
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: weibo
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 0
9
+ version: 0.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Scott Ballantyne
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-20 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ description: "this gem is an adaptation of John Nunemaker's Twitter gem. I modified it to make api integration for \xE6\x96\xB0\xE6\xB5\xAA\xE5\xBE\xAE\xE5\x8D\x9A (t.sina.com.cn) easier."
33
+ email: scott@ekohe.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.rdoc
41
+ files:
42
+ - .document
43
+ - .gitignore
44
+ - LICENSE
45
+ - README.rdoc
46
+ - Rakefile
47
+ - VERSION
48
+ - lib/weibo.rb
49
+ - lib/weibo/base.rb
50
+ - lib/weibo/httpauth.rb
51
+ - lib/weibo/oauth.rb
52
+ - lib/weibo/oauth_hack.rb
53
+ - lib/weibo/request.rb
54
+ - test/helper.rb
55
+ - test/test_weibo.rb
56
+ - weibo.gemspec
57
+ has_rdoc: true
58
+ homepage: http://github.com/ballantyne/weibo
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.6
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: "a gem to help api integration for \xE6\x96\xB0\xE6\xB5\xAA\xE5\xBE\xAE\xE5\x8D\x9A (t.sina.com.cn)"
87
+ test_files:
88
+ - test/helper.rb
89
+ - test/test_weibo.rb