tumblr4r 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +4 -0
- data/README +48 -0
- data/Rakefile +145 -0
- data/lib/tumblr4r.rb +510 -0
- data/test/test_helper.rb +3 -0
- data/test/tumblr4r_test.rb +379 -0
- metadata +89 -0
data/ChangeLog
ADDED
data/README
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= tumblr4r
|
2
|
+
|
3
|
+
* http://github.com/tmaeda/tumblr4r
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
Tumblr API wrapper for Ruby.
|
8
|
+
|
9
|
+
== Synopsis
|
10
|
+
|
11
|
+
Finding by conditions.
|
12
|
+
require 'rubygems'
|
13
|
+
require 'tumblr4r'
|
14
|
+
|
15
|
+
site_a = Tumblr4r::Site.new("site_a.tumblr.com")
|
16
|
+
posts = site_a.find(:all)
|
17
|
+
quote_posts = site_a.find(:all, :type => "quote")
|
18
|
+
posts_offset_and_limit = site_a.find(:all, :offset => 50, :limit => 20)
|
19
|
+
quote_search = site_a.find(:all, :type => "quote", :search => "foo")
|
20
|
+
quote_tagged = site_a.find(:all, :type => "quote", :tagged => "bar")
|
21
|
+
|
22
|
+
Finding by id.
|
23
|
+
post = site_a.find(12345678)
|
24
|
+
|
25
|
+
Posting.
|
26
|
+
site_b = Tumblr4r::Site.new("site_b.tumblr.com", "foo@example.com", "password")
|
27
|
+
site_b.save(post[0])
|
28
|
+
|
29
|
+
Deleting.
|
30
|
+
site_b.delete(post[0].post_id)
|
31
|
+
|
32
|
+
== Installation
|
33
|
+
|
34
|
+
gem install tumblr4r
|
35
|
+
|
36
|
+
== Problems
|
37
|
+
|
38
|
+
* Can't get private posts yet.
|
39
|
+
* Can't upload audio and video data yet.
|
40
|
+
* Can't handle feeds yet.
|
41
|
+
|
42
|
+
|
43
|
+
== Copyright
|
44
|
+
|
45
|
+
Author:: Tomoki MAEDA <http://twitter.com/tmaeda>
|
46
|
+
Copyright:: Copyright (c) 2009 Tomoki MAEDA
|
47
|
+
License:: Ruby's license
|
48
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'lib/tumblr4r'
|
12
|
+
include FileUtils
|
13
|
+
|
14
|
+
NAME = "tumblr4r"
|
15
|
+
AUTHOR = "Tomoki MAEDA"
|
16
|
+
EMAIL = "tmaeda@ruby-sapporo.org"
|
17
|
+
DESCRIPTION = "Tumblr API Wrapper for Ruby"
|
18
|
+
RUBYFORGE_PROJECT = "tumblr4r"
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
BIN_FILES = %w( )
|
21
|
+
|
22
|
+
VERS = Tumblr4r::VERSION
|
23
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
25
|
+
RDOC_OPTS = [
|
26
|
+
'--title', "#{NAME} documentation",
|
27
|
+
"--charset", "utf-8",
|
28
|
+
"--opname", "index.html",
|
29
|
+
"--line-numbers",
|
30
|
+
"--main", "README",
|
31
|
+
"--inline-source",
|
32
|
+
]
|
33
|
+
|
34
|
+
task :default => [:test]
|
35
|
+
task :package => [:clean]
|
36
|
+
|
37
|
+
Rake::TestTask.new("test") do |t|
|
38
|
+
t.libs << "test"
|
39
|
+
t.pattern = "test/**/*_test.rb"
|
40
|
+
t.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
spec = Gem::Specification.new do |s|
|
44
|
+
s.name = NAME
|
45
|
+
s.version = VERS
|
46
|
+
s.platform = Gem::Platform::RUBY
|
47
|
+
s.has_rdoc = true
|
48
|
+
s.extra_rdoc_files = ["README", "ChangeLog"]
|
49
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
50
|
+
s.summary = DESCRIPTION
|
51
|
+
s.description = DESCRIPTION
|
52
|
+
s.author = AUTHOR
|
53
|
+
s.email = EMAIL
|
54
|
+
s.homepage = HOMEPATH
|
55
|
+
s.executables = BIN_FILES
|
56
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
57
|
+
s.bindir = "bin"
|
58
|
+
s.require_path = "lib"
|
59
|
+
#s.autorequire = ""
|
60
|
+
s.test_files = Dir["test/*_test.rb"]
|
61
|
+
|
62
|
+
s.add_runtime_dependency('activesupport', '>=2.3.2')
|
63
|
+
s.add_development_dependency('pit', '>=0.0.6')
|
64
|
+
s.required_ruby_version = '>= 1.8.6'
|
65
|
+
|
66
|
+
s.files = %w(README ChangeLog Rakefile) +
|
67
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
68
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
69
|
+
Dir.glob("examples/**/*.rb") +
|
70
|
+
Dir.glob("tools/*.rb") +
|
71
|
+
Dir.glob("rails/*.rb")
|
72
|
+
|
73
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
74
|
+
end
|
75
|
+
|
76
|
+
Rake::GemPackageTask.new(spec) do |p|
|
77
|
+
p.need_tar = true
|
78
|
+
p.gem_spec = spec
|
79
|
+
end
|
80
|
+
|
81
|
+
task :install do
|
82
|
+
name = "#{NAME}-#{VERS}.gem"
|
83
|
+
sh %{rake package}
|
84
|
+
sh %{sudo gem install pkg/#{name}}
|
85
|
+
end
|
86
|
+
|
87
|
+
task :uninstall => [:clean] do
|
88
|
+
sh %{sudo gem uninstall #{NAME}}
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
Rake::RDocTask.new do |rdoc|
|
93
|
+
rdoc.rdoc_dir = 'html'
|
94
|
+
rdoc.options += RDOC_OPTS
|
95
|
+
rdoc.template = "resh"
|
96
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
97
|
+
if ENV['DOC_FILES']
|
98
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
99
|
+
else
|
100
|
+
rdoc.rdoc_files.include('README', 'ChangeLog')
|
101
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
102
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "Publish to RubyForge"
|
107
|
+
task :rubyforge => [:rdoc, :package] do
|
108
|
+
require 'rubyforge'
|
109
|
+
Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, 'tmaeda').upload
|
110
|
+
end
|
111
|
+
|
112
|
+
desc 'Package and upload the release to rubyforge.'
|
113
|
+
task :release => [:clean, :package] do |t|
|
114
|
+
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
|
115
|
+
abort "Versions don't match #{v} vs #{VERS}" unless v == VERS
|
116
|
+
pkg = "pkg/#{NAME}-#{VERS}"
|
117
|
+
|
118
|
+
require 'rubyforge'
|
119
|
+
rf = RubyForge.new.configure
|
120
|
+
puts "Logging in"
|
121
|
+
rf.login
|
122
|
+
|
123
|
+
c = rf.userconfig
|
124
|
+
# c["release_notes"] = description if description
|
125
|
+
# c["release_changes"] = changes if changes
|
126
|
+
c["preformatted"] = true
|
127
|
+
|
128
|
+
files = [
|
129
|
+
"#{pkg}.tgz",
|
130
|
+
"#{pkg}.gem"
|
131
|
+
].compact
|
132
|
+
|
133
|
+
puts "Releasing #{NAME} v. #{VERS}"
|
134
|
+
rf.add_release RUBYFORGE_PROJECT, NAME, VERS, *files
|
135
|
+
end
|
136
|
+
|
137
|
+
desc 'Show information about the gem.'
|
138
|
+
task :debug_gem do
|
139
|
+
puts spec.to_ruby
|
140
|
+
end
|
141
|
+
|
142
|
+
desc 'Update gem spec'
|
143
|
+
task :gemspec do
|
144
|
+
open("#{NAME}.gemspec", 'w').write spec.to_ruby
|
145
|
+
end
|
data/lib/tumblr4r.rb
ADDED
@@ -0,0 +1,510 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'active_support'
|
5
|
+
require 'logger'
|
6
|
+
require 'cgi'
|
7
|
+
module Tumblr4r
|
8
|
+
VERSION = '0.7.0'
|
9
|
+
class TumblrError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
module POST_TYPE
|
13
|
+
REGULAR = "regular"
|
14
|
+
PHOTO = "photo"
|
15
|
+
QUOTE = "quote"
|
16
|
+
LINK = "link"
|
17
|
+
CHAT = "conversation"
|
18
|
+
AUDIO = "audio"
|
19
|
+
VIDEO = "video"
|
20
|
+
end
|
21
|
+
|
22
|
+
# ConnectionオブジェクトとParserオブジェクトを組み合わせて、
|
23
|
+
# TumblrAPIとRubyオブジェクトの相互変換を行う
|
24
|
+
# TODO: private な post だけを取得する API が無いのだなぁ
|
25
|
+
# * Webから更新したものがAPIで取得できるデータに反映されるには少しタイムラグがあるようだ
|
26
|
+
# * Webから更新しちゃうと、POST日時の秒が丸められてしまう
|
27
|
+
class Site
|
28
|
+
attr_accessor :hostname, :email, :password, :name, :timezone, :title, :cname,
|
29
|
+
:description, :feeds
|
30
|
+
attr_accessor :logger
|
31
|
+
# TODO: 変数名もうちょっと考える
|
32
|
+
API_READ_LIMIT = 50
|
33
|
+
@@default_log_level = Logger::INFO
|
34
|
+
cattr_accessor :default_log_level
|
35
|
+
|
36
|
+
class << self
|
37
|
+
# TODO: unit test
|
38
|
+
def find(hostname, email=nil, password=nil, http=nil, &block)
|
39
|
+
site = self.new(hostname, email, password, http)
|
40
|
+
result = site.find(:all)
|
41
|
+
if block_given?
|
42
|
+
result.each do |post|
|
43
|
+
yield post
|
44
|
+
end
|
45
|
+
else
|
46
|
+
return result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(hostname, email=nil, password=nil, http = nil, logger = nil)
|
52
|
+
@hostname = hostname
|
53
|
+
@email = email
|
54
|
+
@password = password
|
55
|
+
@logger = logger || Logger.new(STDERR)
|
56
|
+
@logger.level = @@default_log_level
|
57
|
+
@conn = XMLConnection.new(http || @hostname, email, password, @logger)
|
58
|
+
@parser = XMLParser.new
|
59
|
+
self.site_info
|
60
|
+
end
|
61
|
+
|
62
|
+
# TODO: ここの再帰取得ロジックはTumblrAPIとは独立してるので
|
63
|
+
# TumblrAPIとは独立した形に切り出したり、TumblrAPIとは切り離してテストを書きたいものだ
|
64
|
+
# @param [Symbol|Integer] id_or_type :all, id
|
65
|
+
# @return [Array<Post>|Post]
|
66
|
+
def find(id_or_type, options = { })
|
67
|
+
params = { }
|
68
|
+
return result if options[:offset] && options[:offset].to_i < 0
|
69
|
+
[:type, :filter, :tagged, :search].each do |option|
|
70
|
+
params[option] = options[option] if options[option]
|
71
|
+
end
|
72
|
+
|
73
|
+
if id_or_type == :all
|
74
|
+
result = []
|
75
|
+
# 取得開始位置の初期化
|
76
|
+
params[:start] = options[:offset] || 0
|
77
|
+
# goal の設定
|
78
|
+
total = self.count(options)
|
79
|
+
if options[:limit]
|
80
|
+
goal = [total - params[:start],
|
81
|
+
options[:limit] - params[:start]].min
|
82
|
+
else
|
83
|
+
goal = total - params[:start]
|
84
|
+
end
|
85
|
+
# 取得件数の初期化
|
86
|
+
if goal < 0
|
87
|
+
return result
|
88
|
+
elsif goal < API_READ_LIMIT
|
89
|
+
params[:num] = goal
|
90
|
+
else
|
91
|
+
params[:num] = API_READ_LIMIT # :num を指定しないとデフォルトでは20件しかとれない
|
92
|
+
end
|
93
|
+
|
94
|
+
loop do
|
95
|
+
xml = @conn.get(params)
|
96
|
+
posts, start, total = @parser.posts(xml)
|
97
|
+
@logger.info("size: #{posts.size}")
|
98
|
+
@logger.info("start: #{start}")
|
99
|
+
@logger.info("total: #{total}")
|
100
|
+
result += posts
|
101
|
+
if result.size >= goal || posts.size == 0
|
102
|
+
# Tumblr API の total で得られる値は全く信用ならない。
|
103
|
+
# 検索条件を考慮した件数を返してくれない。
|
104
|
+
# (つまり、goalは信用ならない)ので、posts.sizeも終了判定に利用する。
|
105
|
+
# TODO: もしくは:numの値を足し合わせていって、それとgoalを比較する?
|
106
|
+
break
|
107
|
+
end
|
108
|
+
# 取得開始位置の調整
|
109
|
+
params[:start] += params[:num]
|
110
|
+
# 取得件数の調整
|
111
|
+
if (goal - result.size) >= API_READ_LIMIT
|
112
|
+
params[:num] = API_READ_LIMIT
|
113
|
+
else
|
114
|
+
params[:num] = goal - result.size
|
115
|
+
end
|
116
|
+
end
|
117
|
+
return result
|
118
|
+
elsif id_or_type.kind_of?(Integer)
|
119
|
+
xml = @conn.get({:id => id_or_type})
|
120
|
+
posts, start, total = @parser.posts(xml)
|
121
|
+
@logger.info("size: #{posts.size}")
|
122
|
+
@logger.info("start: #{start}")
|
123
|
+
@logger.info("total: #{total}")
|
124
|
+
return posts[0]
|
125
|
+
else
|
126
|
+
raise ArgumentError
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def count(options = { })
|
131
|
+
params = { }
|
132
|
+
[:id, :type, :filter, :tagged, :search].each do |option|
|
133
|
+
params[option] = options[option] if options[option]
|
134
|
+
end
|
135
|
+
params[:num] = 1
|
136
|
+
params[:start] = 0
|
137
|
+
xml = @conn.get(params)
|
138
|
+
posts, start, total = @parser.posts(xml)
|
139
|
+
return total
|
140
|
+
end
|
141
|
+
|
142
|
+
def site_info
|
143
|
+
xml = @conn.get(:num => 1)
|
144
|
+
@parser.siteinfo(self, xml)
|
145
|
+
end
|
146
|
+
|
147
|
+
def save(post)
|
148
|
+
post_id = @conn.write(post.params)
|
149
|
+
new_post = self.find(post_id)
|
150
|
+
return new_post
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# Postおよびその子クラスは原則として単なるData Transfer Objectとし、
|
156
|
+
# 何かのロジックをこの中に実装はしない。
|
157
|
+
class Post
|
158
|
+
attr_accessor :post_id, # Integer
|
159
|
+
:url, # String
|
160
|
+
:url_with_slug, # String
|
161
|
+
:type, # String
|
162
|
+
:date_gmt,
|
163
|
+
:date,
|
164
|
+
:unix_timestamp, # Integer
|
165
|
+
:format, # String("html"|"markdown")
|
166
|
+
:tags, # Array<String>
|
167
|
+
:bookmarklet, # true|false
|
168
|
+
:private, # Integer(0|1)
|
169
|
+
:generator # String
|
170
|
+
|
171
|
+
@@default_generator = nil
|
172
|
+
cattr_accessor :default_generator
|
173
|
+
|
174
|
+
def initialize
|
175
|
+
@generator = @@default_generator || "Tumblr4R"
|
176
|
+
@tags = []
|
177
|
+
end
|
178
|
+
|
179
|
+
def params
|
180
|
+
{"type" => @type,
|
181
|
+
"generator" => @generator,
|
182
|
+
"date" => @date,
|
183
|
+
"private" => @private,
|
184
|
+
"tags" => @tags.join(","),
|
185
|
+
"format" => @format
|
186
|
+
}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Regular < Post
|
191
|
+
attr_accessor :regular_title, :regular_body
|
192
|
+
|
193
|
+
def params
|
194
|
+
super.merge!({"title" => @regular_title, "body" => @regular_body })
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# TODO: Feed の扱いをどうするか
|
199
|
+
class Feed < Post
|
200
|
+
attr_accessor :regular_body, :feed_item, :from_feed_id
|
201
|
+
# TODO: titleのあるfeed itemってあるのか?
|
202
|
+
end
|
203
|
+
|
204
|
+
class Photo < Post
|
205
|
+
attr_accessor :photo_caption, :photo_link_url, :photo_url
|
206
|
+
#TODO: photo_url の max-width って何?
|
207
|
+
attr_accessor :data
|
208
|
+
|
209
|
+
# TODO: data をどうやってPOSTするか考える
|
210
|
+
# 生のデータを持たせるんじゃなく、TumblrPostDataみたいな
|
211
|
+
# クラスにラップして、それを各POSTのivarに保持させる?
|
212
|
+
def params
|
213
|
+
super.merge!(
|
214
|
+
{"source" => @photo_url,
|
215
|
+
"caption" => @photo_caption,
|
216
|
+
"click-through-url" => @photo_link_url,
|
217
|
+
"data" => @data})
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class Quote < Post
|
222
|
+
attr_accessor :quote_text, :quote_source
|
223
|
+
|
224
|
+
def params
|
225
|
+
super.merge!(
|
226
|
+
{"quote" => @quote_text,
|
227
|
+
"source" => @quote_source})
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class Link < Post
|
232
|
+
attr_accessor :link_text, :link_url, :link_description
|
233
|
+
def params
|
234
|
+
super.merge!(
|
235
|
+
{"name" => @link_text,
|
236
|
+
"url" => @link_url,
|
237
|
+
"description" => @link_description})
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class Chat < Post
|
242
|
+
attr_accessor :conversation_title, :conversation_text
|
243
|
+
# <conversation><line name="..." label="...">text</line>のリスト</conversation>
|
244
|
+
|
245
|
+
def params
|
246
|
+
super.merge!(
|
247
|
+
{"title" => @conversation_title,
|
248
|
+
"conversation" => @conversation_text})
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
class Audio < Post
|
253
|
+
attr_accessor :audio_plays, :audio_caption, :audio_player
|
254
|
+
attr_accessor :data
|
255
|
+
|
256
|
+
def params
|
257
|
+
super.merge!(
|
258
|
+
{"data" => @data,
|
259
|
+
"caption" => @audio_caption})
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class Video < Post
|
264
|
+
attr_accessor :video_caption, :video_source, :video_player
|
265
|
+
attr_accessor :data, :title
|
266
|
+
# TODO: title は vimeo へのアップロードのときのみ有効らしい
|
267
|
+
# TODO: embed を使うか、アップロードしたdataを使うかってのは
|
268
|
+
# Tumblr側で勝手に判断されるのかなぁ?
|
269
|
+
def params
|
270
|
+
super.merge!(
|
271
|
+
{"embed" => @video_source,
|
272
|
+
"data" => @data,
|
273
|
+
"title" => @title,
|
274
|
+
"caption" => @video_caption})
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Tumblr XML API への薄いラッパー。
|
279
|
+
# Rubyオブジェクトからの変換やRubyオブジェクトへの変換などは
|
280
|
+
# Parserクラスで行う。Parserクラスへの依存関係は一切持たない。
|
281
|
+
class XMLConnection
|
282
|
+
attr_accessor :logger, :group, :authenticated
|
283
|
+
def initialize(http_or_hostname, email=nil, password=nil, logger = nil)
|
284
|
+
case http_or_hostname
|
285
|
+
when String
|
286
|
+
@conn = Net::HTTP.new(http_or_hostname)
|
287
|
+
when Net::HTTP
|
288
|
+
@conn = http_or_hostname
|
289
|
+
else
|
290
|
+
raise ArgumentError.new("http_or_hostname must be String or Net::HTTP")
|
291
|
+
end
|
292
|
+
@email= email
|
293
|
+
@password = password
|
294
|
+
if @email && @password
|
295
|
+
begin
|
296
|
+
@authenticated = authenticate
|
297
|
+
rescue TumblrError
|
298
|
+
@authenticated = false
|
299
|
+
end
|
300
|
+
end
|
301
|
+
@group = @conn.address
|
302
|
+
@logger = logger || Logger.new(STDERR)
|
303
|
+
end
|
304
|
+
|
305
|
+
# @param [Hash] options :id, :type, :filter, :tagged, :search, :start, :num
|
306
|
+
def get(options = { })
|
307
|
+
params = options.map{|k, v|
|
308
|
+
"#{k}=#{v}"
|
309
|
+
}.join("&")
|
310
|
+
req = "/api/read?#{params}"
|
311
|
+
logger.info(req)
|
312
|
+
res = @conn.get(req)
|
313
|
+
logger.debug(res.body)
|
314
|
+
case res
|
315
|
+
when Net::HTTPOK
|
316
|
+
return res.body
|
317
|
+
when Net::HTTPNotFound
|
318
|
+
raise TumblrError.new("no such site(#{@hostname})")
|
319
|
+
else
|
320
|
+
raise TumblrError.new("unexpected response #{res.inspect}")
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @return true if email and password are valid
|
325
|
+
# @raise TumblrError if email or password is invalid
|
326
|
+
def authenticate
|
327
|
+
response = nil
|
328
|
+
http = Net::HTTP.new("www.tumblr.com")
|
329
|
+
response = http.post('/api/authenticate',
|
330
|
+
"email=#{CGI.escape(@email)}&password=#{CGI.escape(@password)}")
|
331
|
+
|
332
|
+
case response
|
333
|
+
when Net::HTTPOK
|
334
|
+
return true
|
335
|
+
else
|
336
|
+
raise TumblrError.new(response.inspect + "\n" + response.body)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# @return [Integer] post_id if success
|
341
|
+
# @raise [TumblrError] if fail
|
342
|
+
def write(options)
|
343
|
+
raise TumblrError.new("email or password is invalid") unless authenticated
|
344
|
+
|
345
|
+
response = nil
|
346
|
+
http = Net::HTTP.new("www.tumblr.com")
|
347
|
+
params = options.merge({"email" => @email, "password" => @password, "group" => @group})
|
348
|
+
query_string = params.delete_if{|k,v| v == nil }.map{|k,v| "#{k}=#{CGI.escape(v.to_s)}" unless v.nil?}.join("&")
|
349
|
+
logger.debug("#### query_string: #{query_string}")
|
350
|
+
response = http.post('/api/write', query_string)
|
351
|
+
case response
|
352
|
+
when Net::HTTPSuccess
|
353
|
+
return response.body.to_i
|
354
|
+
else
|
355
|
+
msg = response.inspect + "\n"
|
356
|
+
response.each{|k,v| msg += "#{k}: #{v}\n"}
|
357
|
+
msg += response.body
|
358
|
+
raise TumblrError.new(msg)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# @params [Integer] post_id
|
363
|
+
def delete(post_id)
|
364
|
+
raise TumblrError.new("email or password is invalid") unless authenticated
|
365
|
+
response = nil
|
366
|
+
http = Net::HTTP.new("www.tumblr.com")
|
367
|
+
params = {"post-id" => post_id, "email" => @email, "password" => @password, "group" => @group}
|
368
|
+
query_string = params.delete_if{|k,v| v == nil }.map{|k,v| "#{k}=#{CGI.escape(v.to_s)}" unless v.nil?}.join("&")
|
369
|
+
logger.debug("#### query_string: #{query_string}")
|
370
|
+
response = http.post('/api/delete', query_string)
|
371
|
+
case response
|
372
|
+
when Net::HTTPSuccess
|
373
|
+
logger.debug("#### response: #{response.code}: #{response.body}")
|
374
|
+
return true
|
375
|
+
else
|
376
|
+
msg = response.inspect + "\n"
|
377
|
+
response.each{|k,v| msg += "#{k}: #{v}\n"}
|
378
|
+
msg += response.body
|
379
|
+
raise TumblrError.new(msg)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
# Tumblr XML API
|
385
|
+
class XMLParser
|
386
|
+
# @param [Site] site xmlをパースした結果を埋める入れ物
|
387
|
+
# @param [String] xml TumblrAPIのレスポンスのXMLそのまま
|
388
|
+
def siteinfo(site, xml)
|
389
|
+
xml_doc = REXML::Document.new(xml)
|
390
|
+
tumblelog = REXML::XPath.first(xml_doc, "//tumblr/tumblelog")
|
391
|
+
site.name = tumblelog.attributes["name"]
|
392
|
+
site.timezone = tumblelog.attributes["timezone"]
|
393
|
+
site.title = tumblelog.attributes["title"]
|
394
|
+
site.cname = tumblelog.attributes["cname"]
|
395
|
+
site.description = tumblelog.text
|
396
|
+
# tumblelog.elements["/feeds"]}
|
397
|
+
# TODO: feeds は後回し
|
398
|
+
return site
|
399
|
+
end
|
400
|
+
|
401
|
+
# XMLをパースしてオブジェクトのArrayを作る
|
402
|
+
# @param [String] xml APIからのレスポンス全体
|
403
|
+
# @return [Array<Post>, Integer, Integer] 各種Postの子クラスのArray, start, total
|
404
|
+
def posts(xml)
|
405
|
+
rexml_doc = REXML::Document.new(xml)
|
406
|
+
rexml_posts = REXML::XPath.first(rexml_doc, "//tumblr/posts")
|
407
|
+
start = rexml_posts.attributes["start"]
|
408
|
+
total = rexml_posts.attributes["total"]
|
409
|
+
posts = []
|
410
|
+
rexml_posts.elements.each("//posts/post") do |rexml_post|
|
411
|
+
post_type = rexml_post.attributes["type"]
|
412
|
+
post = nil
|
413
|
+
case post_type
|
414
|
+
when POST_TYPE::REGULAR
|
415
|
+
post = self.regular(Regular.new, rexml_post)
|
416
|
+
when POST_TYPE::PHOTO
|
417
|
+
post = self.photo(Photo.new, rexml_post)
|
418
|
+
when POST_TYPE::QUOTE
|
419
|
+
post = self.quote(Quote.new, rexml_post)
|
420
|
+
when POST_TYPE::LINK
|
421
|
+
post = self.link(Link.new, rexml_post)
|
422
|
+
when POST_TYPE::CHAT
|
423
|
+
post = self.chat(Chat.new, rexml_post)
|
424
|
+
when POST_TYPE::AUDIO
|
425
|
+
post = self.audio(Audio.new, rexml_post)
|
426
|
+
when POST_TYPE::VIDEO
|
427
|
+
post = self.video(Video.new, rexml_post)
|
428
|
+
else
|
429
|
+
raise TumblrError.new("unknown post type #{post_type}")
|
430
|
+
end
|
431
|
+
posts << post
|
432
|
+
end
|
433
|
+
return posts, start.to_i, total.to_i
|
434
|
+
end
|
435
|
+
|
436
|
+
# TODO: この辺りの設計についてはもう少し考慮の余地がある?
|
437
|
+
# みんな同じような構造(まずはpost(post, rexml_post)呼んでその後独自処理)してるし、
|
438
|
+
# 引数にpostとrexml_postをもらってくるってのもなんかイケてない気がする。
|
439
|
+
def post(post, rexml_post)
|
440
|
+
post.post_id = rexml_post.attributes["id"].to_i
|
441
|
+
post.url = rexml_post.attributes["url"]
|
442
|
+
post.url_with_slug = rexml_post.attributes["url-with-slug"]
|
443
|
+
post.type = rexml_post.attributes["type"]
|
444
|
+
# TODO: time 関係の型をStringじゃなくTimeとかにする?
|
445
|
+
post.date_gmt = rexml_post.attributes["date-gmt"]
|
446
|
+
post.date = rexml_post.attributes["date"]
|
447
|
+
post.unix_timestamp = rexml_post.attributes["unix-timestamp"].to_i
|
448
|
+
post.format = rexml_post.attributes["format"]
|
449
|
+
post.tags = rexml_post.get_elements("tag").map(&:text)
|
450
|
+
post.bookmarklet = (rexml_post.attributes["bookmarklet"] == "true")
|
451
|
+
post
|
452
|
+
end
|
453
|
+
|
454
|
+
def regular(post, rexml_post)
|
455
|
+
post = self.post(post, rexml_post)
|
456
|
+
post.regular_title = rexml_post.elements["regular-title"].try(:text) || ""
|
457
|
+
post.regular_body = rexml_post.elements["regular-body"].try(:text) || ""
|
458
|
+
post
|
459
|
+
end
|
460
|
+
|
461
|
+
def photo(post, rexml_post)
|
462
|
+
post = self.post(post, rexml_post)
|
463
|
+
post.type
|
464
|
+
post.photo_caption = rexml_post.elements["photo-caption"].try(:text) || ""
|
465
|
+
post.photo_link_url = rexml_post.elements["photo-link-url"].try(:text) || ""
|
466
|
+
post.photo_url = rexml_post.elements["photo-url"].try(:text) || ""
|
467
|
+
post
|
468
|
+
end
|
469
|
+
|
470
|
+
def quote(post, rexml_post)
|
471
|
+
post = self.post(post, rexml_post)
|
472
|
+
post.quote_text = rexml_post.elements["quote-text"].try(:text) || ""
|
473
|
+
post.quote_source = rexml_post.elements["quote-source"].try(:text) || ""
|
474
|
+
post
|
475
|
+
end
|
476
|
+
|
477
|
+
def link(post, rexml_post)
|
478
|
+
post = self.post(post, rexml_post)
|
479
|
+
post.link_text = rexml_post.elements["link-text"].try(:text) || ""
|
480
|
+
post.link_url = rexml_post.elements["link-url"].try(:text) || ""
|
481
|
+
post.link_description = rexml_post.elements["link-description"].try(:text) || ""
|
482
|
+
post
|
483
|
+
end
|
484
|
+
|
485
|
+
def chat(post, rexml_post)
|
486
|
+
post = self.post(post, rexml_post)
|
487
|
+
post.conversation_title = rexml_post.elements["conversation-title"].try(:text) || ""
|
488
|
+
post.conversation_text = rexml_post.elements["conversation-text"].try(:text) || ""
|
489
|
+
post
|
490
|
+
end
|
491
|
+
|
492
|
+
def audio(post, rexml_post)
|
493
|
+
post = self.post(post, rexml_post)
|
494
|
+
post.audio_plays = (rexml_post.attributes["audio-plays"] == "1")
|
495
|
+
post.audio_caption = rexml_post.elements["audio-caption"].try(:text) || ""
|
496
|
+
post.audio_player = rexml_post.elements["audio-player"].try(:text) || ""
|
497
|
+
post
|
498
|
+
end
|
499
|
+
|
500
|
+
def video(post, rexml_post)
|
501
|
+
post = self.post(post, rexml_post)
|
502
|
+
post.video_caption = rexml_post.elements["video-caption"].try(:text) || ""
|
503
|
+
post.video_source = rexml_post.elements["video-source"].try(:text) || ""
|
504
|
+
post.video_player = rexml_post.elements["video-player"].try(:text) || ""
|
505
|
+
post
|
506
|
+
end
|
507
|
+
|
508
|
+
end
|
509
|
+
|
510
|
+
end # module
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require "test/unit"
|
3
|
+
require 'pit'
|
4
|
+
class Tumblr4rTest < Test::Unit::TestCase
|
5
|
+
include Tumblr4r
|
6
|
+
READ_TEST_HOST = "tumblr4rtest.tumblr.com"
|
7
|
+
WRITE_TEST_HOST = "tumblr4rwritetest.tumblr.com"
|
8
|
+
TOTAL_COUNT = 114
|
9
|
+
QUOTE_COUNT = 102
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@site = Site.new(READ_TEST_HOST)
|
13
|
+
writetest_conf = Pit.get("tumblr4rwritetest",
|
14
|
+
:require => {"email" => "required email",
|
15
|
+
"password" => "required password"})
|
16
|
+
@write_site = Site.new(WRITE_TEST_HOST,
|
17
|
+
writetest_conf["email"],
|
18
|
+
writetest_conf["password"])
|
19
|
+
@large_site = Site.new("tumblr4rfindtest.tumblr.com")
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def test_initialize
|
27
|
+
assert_equal READ_TEST_HOST, @site.hostname
|
28
|
+
assert_nil @site.email
|
29
|
+
assert_nil @site.password
|
30
|
+
|
31
|
+
assert_equal "tumblr4rtest", @site.name
|
32
|
+
assert_equal "Asia/Tokyo", @site.timezone
|
33
|
+
assert_equal "tumblr4rテスト", @site.title
|
34
|
+
assert_nil @site.cname
|
35
|
+
assert_equal "tumblr4rのテスト用サイトです。\r\ntumblrサイコー", @site.description
|
36
|
+
# TODO: feeds は後回し
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_count
|
40
|
+
total = @large_site.count
|
41
|
+
assert_equal TOTAL_COUNT, total
|
42
|
+
|
43
|
+
total = @large_site.count(:type => "quote")
|
44
|
+
assert_equal QUOTE_COUNT, total
|
45
|
+
|
46
|
+
total = @large_site.count(:filter => "text")
|
47
|
+
assert_equal TOTAL_COUNT, total
|
48
|
+
|
49
|
+
total = @large_site.count(:tagged => "test")
|
50
|
+
assert_equal TOTAL_COUNT, total
|
51
|
+
|
52
|
+
total = @large_site.count(:search => "test")
|
53
|
+
assert_equal TOTAL_COUNT, total
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_find
|
57
|
+
posts = @site.find(:all)
|
58
|
+
assert_equal 8, posts.size
|
59
|
+
assert_equal Video, posts[0].class
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_find_all
|
63
|
+
posts = @large_site.find(:all)
|
64
|
+
assert_equal TOTAL_COUNT, posts.size
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_find_all_quote
|
68
|
+
posts = @large_site.find(:all, :type => "quote")
|
69
|
+
assert_equal QUOTE_COUNT, posts.size
|
70
|
+
end
|
71
|
+
|
72
|
+
# 実際に存在する件数より少なく指定した場合
|
73
|
+
def test_find_all_with_num
|
74
|
+
posts = @large_site.find(:all, :limit => 74)
|
75
|
+
assert_equal 74, posts.size
|
76
|
+
end
|
77
|
+
|
78
|
+
# 実際に存在する件数より多く指定した場合
|
79
|
+
def test_find_all_with_over_num
|
80
|
+
posts = @large_site.find(:all, :limit => 765)
|
81
|
+
assert_equal TOTAL_COUNT, posts.size
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_find_all_with_offset
|
85
|
+
posts = @large_site.find(:all, :offset => 12)
|
86
|
+
assert_equal TOTAL_COUNT-12, posts.size
|
87
|
+
end
|
88
|
+
|
89
|
+
# 実際に存在するよりも多いoffsetを指定した場合
|
90
|
+
def test_find_all_over_offset
|
91
|
+
posts = @large_site.find(:all, :type => "quote", :offset => TOTAL_COUNT + 1)
|
92
|
+
assert_equal 0, posts.size
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_find_with_type_regular
|
96
|
+
posts = @site.find(:all, :type => "regular")
|
97
|
+
assert_equal 2, posts.size
|
98
|
+
|
99
|
+
assert_equal Regular, posts[0].class
|
100
|
+
assert_equal 123459291, posts[0].post_id
|
101
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123459291", posts[0].url
|
102
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123459291/regular-test", posts[0].url_with_slug
|
103
|
+
assert_equal "regular", posts[0].type
|
104
|
+
assert_equal "2009-06-14 16:30:44 GMT", posts[0].date_gmt
|
105
|
+
assert_equal "Mon, 15 Jun 2009 01:30:44", posts[0].date
|
106
|
+
assert_equal 1244997044, posts[0].unix_timestamp
|
107
|
+
assert_equal "html", posts[0].format
|
108
|
+
assert_equal ["test", "regular"], posts[0].tags
|
109
|
+
assert_equal false, posts[0].bookmarklet
|
110
|
+
|
111
|
+
assert_equal "Text Postのテストです", posts[0].regular_title
|
112
|
+
assert_equal <<EOF.chomp, posts[0].regular_body
|
113
|
+
<p>テキストです。</p>
|
114
|
+
<p><b>ボールドです。</b></p>
|
115
|
+
<p><i>イタリックです。</i></p>
|
116
|
+
<p><strike>取り消し線です。</strike></p>
|
117
|
+
<ul>
|
118
|
+
<li>unordered 1</li>
|
119
|
+
<li>unordered 2</li>
|
120
|
+
</ul>
|
121
|
+
<ol>
|
122
|
+
<li>ordered 1</li>
|
123
|
+
<li>ordered 2</li>
|
124
|
+
</ol>
|
125
|
+
<p>ここからインデント</p>
|
126
|
+
<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">インデント開始<br/>ああああ<br/>インデント終了</blockquote>
|
127
|
+
<p>ここまでインデント</p>
|
128
|
+
EOF
|
129
|
+
|
130
|
+
assert_equal Regular, posts[1].class
|
131
|
+
assert_equal 122871637, posts[1].post_id
|
132
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/122871637", posts[1].url
|
133
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/122871637/tumblr4r", posts[1].url_with_slug
|
134
|
+
assert_equal "regular", posts[1].type
|
135
|
+
assert_equal "2009-06-13 12:46:23 GMT", posts[1].date_gmt
|
136
|
+
assert_equal "Sat, 13 Jun 2009 21:46:23", posts[1].date
|
137
|
+
assert_equal 1244897183, posts[1].unix_timestamp
|
138
|
+
assert_equal "html", posts[1].format
|
139
|
+
assert_equal [], posts[1].tags
|
140
|
+
assert_equal false, posts[1].bookmarklet
|
141
|
+
|
142
|
+
assert_equal "", posts[1].regular_title
|
143
|
+
assert_equal "Tumblr4rのテストです", posts[1].regular_body
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_find_with_type_photo
|
147
|
+
posts = @site.find(:all, :type => "photo")
|
148
|
+
assert_equal 1, posts.size
|
149
|
+
assert_equal Photo, posts[0].class
|
150
|
+
assert_equal 123461063, posts[0].post_id
|
151
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123461063", posts[0].url
|
152
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123461063/photo", posts[0].url_with_slug
|
153
|
+
assert_equal "photo", posts[0].type
|
154
|
+
assert_equal "2009-06-14 16:34:50 GMT", posts[0].date_gmt
|
155
|
+
assert_equal "Mon, 15 Jun 2009 01:34:50", posts[0].date
|
156
|
+
assert_equal 1244997290, posts[0].unix_timestamp
|
157
|
+
assert_equal "html", posts[0].format
|
158
|
+
assert_equal ["test", "photo"], posts[0].tags
|
159
|
+
assert_equal false, posts[0].bookmarklet
|
160
|
+
|
161
|
+
assert_equal "<p>Photoのテストです。</p>\n\n<p>ギコです。</p>", posts[0].photo_caption
|
162
|
+
assert_equal "http://www.google.co.jp/", posts[0].photo_link_url
|
163
|
+
assert_equal "http://5.media.tumblr.com/GyEYZujUYopiula4XKmXhCgmo1_250.jpg", posts[0].photo_url
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_find_with_type_quote
|
167
|
+
posts = @site.find(:all, :type => "quote")
|
168
|
+
assert_equal 1, posts.size
|
169
|
+
assert_equal Quote, posts[0].class
|
170
|
+
assert_equal 123470309, posts[0].post_id
|
171
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123470309", posts[0].url
|
172
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123470309/wikipedia-tumblr", posts[0].url_with_slug
|
173
|
+
assert_equal "quote", posts[0].type
|
174
|
+
assert_equal "2009-06-14 16:58:00 GMT", posts[0].date_gmt
|
175
|
+
assert_equal "Mon, 15 Jun 2009 01:58:00", posts[0].date
|
176
|
+
assert_equal 1244998680, posts[0].unix_timestamp
|
177
|
+
assert_equal "html", posts[0].format
|
178
|
+
assert_equal ["quote", "test"], posts[0].tags
|
179
|
+
assert_equal true, posts[0].bookmarklet
|
180
|
+
|
181
|
+
assert_equal <<EOF.chomp, posts[0].quote_text
|
182
|
+
Tumblelog/Tumblr(タンブルログ/タンブラー)は、メディアミックスウェブログサービス。米国 Davidville.incにより2007年3月1日にサービスが開始された。
|
183
|
+
EOF
|
184
|
+
assert_equal "<a href=\"http://ja.wikipedia.org/wiki/Tumblr\">Tumblelog - Wikipedia</a>", posts[0].quote_source
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_find_with_type_link
|
188
|
+
posts = @site.find(:all, :type => "link")
|
189
|
+
assert_equal 1, posts.size
|
190
|
+
assert_equal Link, posts[0].class
|
191
|
+
assert_equal 123470990, posts[0].post_id
|
192
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123470990", posts[0].url
|
193
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123470990/tumblr-link", posts[0].url_with_slug
|
194
|
+
assert_equal "link", posts[0].type
|
195
|
+
assert_equal "2009-06-14 17:00:00 GMT", posts[0].date_gmt
|
196
|
+
assert_equal "Mon, 15 Jun 2009 02:00:00", posts[0].date
|
197
|
+
assert_equal 1244998800, posts[0].unix_timestamp
|
198
|
+
assert_equal "html", posts[0].format
|
199
|
+
assert_equal ["test", "link"], posts[0].tags
|
200
|
+
assert_equal false, posts[0].bookmarklet
|
201
|
+
|
202
|
+
assert_equal "たんぶらー", posts[0].link_text
|
203
|
+
assert_equal "http://www.tumblr.com/", posts[0].link_url
|
204
|
+
assert_equal "ですくりぷしょん", posts[0].link_description
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_find_with_type_conversation
|
208
|
+
posts = @site.find(:all, :type => "conversation")
|
209
|
+
assert_equal 1, posts.size
|
210
|
+
assert_equal Chat, posts[0].class
|
211
|
+
assert_equal 123471808, posts[0].post_id
|
212
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123471808", posts[0].url
|
213
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123471808/pointcard-chat", posts[0].url_with_slug
|
214
|
+
assert_equal "conversation", posts[0].type
|
215
|
+
assert_equal "2009-06-14 17:01:54 GMT", posts[0].date_gmt
|
216
|
+
assert_equal "Mon, 15 Jun 2009 02:01:54", posts[0].date
|
217
|
+
assert_equal 1244998914, posts[0].unix_timestamp
|
218
|
+
assert_equal "html", posts[0].format
|
219
|
+
assert_equal ["test", "chat"], posts[0].tags
|
220
|
+
assert_equal false, posts[0].bookmarklet
|
221
|
+
|
222
|
+
assert_equal "なにそれこわい", posts[0].conversation_title
|
223
|
+
assert_equal <<EOF.chomp, posts[0].conversation_text
|
224
|
+
店員: 当店のポイントカードはお餅でしょうか\r
|
225
|
+
ぼく: えっ\r
|
226
|
+
店員: 当店のポイントカードはお餅ですか \r
|
227
|
+
ぼく: いえしりません\r
|
228
|
+
店員: えっ\r
|
229
|
+
ぼく: えっ\r
|
230
|
+
EOF
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_find_with_type_audio
|
234
|
+
posts = @site.find(:all, :type => "audio")
|
235
|
+
assert_equal 1, posts.size
|
236
|
+
assert_equal Audio, posts[0].class
|
237
|
+
assert_equal 131705561, posts[0].post_id
|
238
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/131705561", posts[0].url
|
239
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/131705561/tumblr4r-miku", posts[0].url_with_slug
|
240
|
+
assert_equal "audio", posts[0].type
|
241
|
+
assert_equal "2009-06-28 13:58:13 GMT", posts[0].date_gmt
|
242
|
+
assert_equal "Sun, 28 Jun 2009 22:58:13", posts[0].date
|
243
|
+
assert_equal 1246197493, posts[0].unix_timestamp
|
244
|
+
assert_equal "html", posts[0].format
|
245
|
+
assert_equal [], posts[0].tags
|
246
|
+
assert_equal false, posts[0].bookmarklet
|
247
|
+
|
248
|
+
assert_equal true, posts[0].audio_plays
|
249
|
+
assert_equal "tumblr4r miku", posts[0].audio_caption
|
250
|
+
assert_equal "<embed type=\"application/x-shockwave-flash\" src=\"http://tumblr4rtest.tumblr.com/swf/audio_player.swf?audio_file=http://www.tumblr.com/audio_file/131705561/GyEYZujUYp9df3nv1WMefTH8&color=FFFFFF\" height=\"27\" width=\"207\" quality=\"best\"></embed>", posts[0].audio_player
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_find_with_type_video
|
254
|
+
posts = @site.find(:all, :type => "video")
|
255
|
+
assert_equal 1, posts.size
|
256
|
+
assert_equal Video, posts[0].class
|
257
|
+
assert_equal 131714219, posts[0].post_id
|
258
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/131714219", posts[0].url
|
259
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/131714219/matrix-sappoloaded", posts[0].url_with_slug
|
260
|
+
assert_equal "video", posts[0].type
|
261
|
+
assert_equal "2009-06-28 14:22:56 GMT", posts[0].date_gmt
|
262
|
+
assert_equal "Sun, 28 Jun 2009 23:22:56", posts[0].date
|
263
|
+
assert_equal 1246198976, posts[0].unix_timestamp
|
264
|
+
assert_equal "html", posts[0].format
|
265
|
+
assert_equal [], posts[0].tags
|
266
|
+
assert_equal false, posts[0].bookmarklet
|
267
|
+
|
268
|
+
assert_equal "matrix sappoloaded", posts[0].video_caption
|
269
|
+
assert_equal "http://www.youtube.com/watch?v=FavWH5RhYpw", posts[0].video_source
|
270
|
+
assert_equal "<object width=\"400\" height=\"336\"><param name=\"movie\" value=\"http://www.youtube.com/v/FavWH5RhYpw&rel=0&egm=0&showinfo=0&fs=1\"></param><param name=\"wmode\" value=\"transparent\"></param><param name=\"allowFullScreen\" value=\"true\"></param><embed src=\"http://www.youtube.com/v/FavWH5RhYpw&rel=0&egm=0&showinfo=0&fs=1\" type=\"application/x-shockwave-flash\" width=\"400\" height=\"336\" allowFullScreen=\"true\" wmode=\"transparent\"></embed></object>", posts[0].video_player
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_find_with_tagged
|
274
|
+
posts = @site.find(:all, :tagged => "test")
|
275
|
+
assert_equal 5, posts.size
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_find_with_search
|
279
|
+
posts = @site.find(:all, :search => "matrix")
|
280
|
+
assert_equal 1, posts.size
|
281
|
+
end
|
282
|
+
|
283
|
+
def test_generator_default
|
284
|
+
post = Post.new
|
285
|
+
assert_equal "Tumblr4R", post.generator
|
286
|
+
|
287
|
+
Post.default_generator = "foo"
|
288
|
+
post2 = Post.new
|
289
|
+
assert_equal "foo", post2.generator
|
290
|
+
assert_equal "Tumblr4R", post.generator
|
291
|
+
|
292
|
+
|
293
|
+
Post.default_generator = nil
|
294
|
+
post3 = Post.new
|
295
|
+
assert_equal "Tumblr4R", post3.generator
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_find_by_id
|
299
|
+
post = @site.find(123459291)
|
300
|
+
|
301
|
+
assert_equal Regular, post.class
|
302
|
+
assert_equal 123459291, post.post_id
|
303
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123459291", post.url
|
304
|
+
assert_equal "http://tumblr4rtest.tumblr.com/post/123459291/regular-test", post.url_with_slug
|
305
|
+
assert_equal "regular", post.type
|
306
|
+
assert_equal "2009-06-14 16:30:44 GMT", post.date_gmt
|
307
|
+
assert_equal "Mon, 15 Jun 2009 01:30:44", post.date
|
308
|
+
assert_equal 1244997044, post.unix_timestamp
|
309
|
+
assert_equal "html", post.format
|
310
|
+
assert_equal ["test", "regular"], post.tags
|
311
|
+
assert_equal false, post.bookmarklet
|
312
|
+
|
313
|
+
assert_equal "Text Postのテストです", post.regular_title
|
314
|
+
assert_equal <<EOF.chomp, post.regular_body
|
315
|
+
<p>テキストです。</p>
|
316
|
+
<p><b>ボールドです。</b></p>
|
317
|
+
<p><i>イタリックです。</i></p>
|
318
|
+
<p><strike>取り消し線です。</strike></p>
|
319
|
+
<ul>
|
320
|
+
<li>unordered 1</li>
|
321
|
+
<li>unordered 2</li>
|
322
|
+
</ul>
|
323
|
+
<ol>
|
324
|
+
<li>ordered 1</li>
|
325
|
+
<li>ordered 2</li>
|
326
|
+
</ol>
|
327
|
+
<p>ここからインデント</p>
|
328
|
+
<blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;">インデント開始<br/>ああああ<br/>インデント終了</blockquote>
|
329
|
+
<p>ここまでインデント</p>
|
330
|
+
EOF
|
331
|
+
end
|
332
|
+
|
333
|
+
def test_quote_create
|
334
|
+
quotes = @site.find(:all, :type => Tumblr4r::POST_TYPE::QUOTE)
|
335
|
+
post = @write_site.save(quotes[0])
|
336
|
+
assert_not_equal quotes[0].post_id, post.post_id
|
337
|
+
assert_equal "http://#{WRITE_TEST_HOST}/post/#{post.post_id}", post.url
|
338
|
+
assert_equal "http://#{WRITE_TEST_HOST}/post/#{post.post_id}/tumblelog-tumblr", post.url_with_slug
|
339
|
+
assert_equal Tumblr4r::POST_TYPE::QUOTE, post.type
|
340
|
+
assert_equal quotes[0].date_gmt, post.date_gmt
|
341
|
+
assert_equal quotes[0].date, post.date
|
342
|
+
# TODO
|
343
|
+
# assert_equal 1245045480, post.unix_timestamp
|
344
|
+
assert_equal quotes[0].format, post.format
|
345
|
+
assert_equal quotes[0].tags, post.tags
|
346
|
+
assert_equal false, post.bookmarklet
|
347
|
+
|
348
|
+
assert_equal quotes[0].quote_text, post.quote_text
|
349
|
+
assert_equal quotes[0].quote_source, post.quote_source
|
350
|
+
end
|
351
|
+
|
352
|
+
def test_regular_create
|
353
|
+
regulars = @site.find(:all, :type => Tumblr4r::POST_TYPE::REGULAR)
|
354
|
+
post = @write_site.save(regulars[0])
|
355
|
+
end
|
356
|
+
|
357
|
+
def test_link_create
|
358
|
+
links = @site.find(:all, :type => Tumblr4r::POST_TYPE::LINK)
|
359
|
+
post = @write_site.save(links[0])
|
360
|
+
end
|
361
|
+
|
362
|
+
def test_photo_create
|
363
|
+
photos = @site.find(:all, :type => Tumblr4r::POST_TYPE::PHOTO)
|
364
|
+
post = @write_site.save(photos[0])
|
365
|
+
end
|
366
|
+
|
367
|
+
|
368
|
+
# def test_audio_create
|
369
|
+
# audios = @site.find(:all, :type => Tumblr4r::POST_TYPE::AUDIO)
|
370
|
+
# post = @write_site.save(audios[0])
|
371
|
+
# end
|
372
|
+
|
373
|
+
def test_video_create
|
374
|
+
videos = @site.find(:all, :type => Tumblr4r::POST_TYPE::VIDEO)
|
375
|
+
post = @write_site.save(videos[0])
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tumblr4r
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomoki MAEDA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-09 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.2
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: pit
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.6
|
34
|
+
version:
|
35
|
+
description: Tumblr API Wrapper for Ruby
|
36
|
+
email: tmaeda@ruby-sapporo.org
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
- ChangeLog
|
44
|
+
files:
|
45
|
+
- README
|
46
|
+
- ChangeLog
|
47
|
+
- Rakefile
|
48
|
+
- test/test_helper.rb
|
49
|
+
- test/tumblr4r_test.rb
|
50
|
+
- lib/tumblr4r.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://tumblr4r.rubyforge.org
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --title
|
56
|
+
- tumblr4r documentation
|
57
|
+
- --charset
|
58
|
+
- utf-8
|
59
|
+
- --opname
|
60
|
+
- index.html
|
61
|
+
- --line-numbers
|
62
|
+
- --main
|
63
|
+
- README
|
64
|
+
- --inline-source
|
65
|
+
- --exclude
|
66
|
+
- ^(examples|extras)/
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.8.6
|
74
|
+
version:
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0"
|
80
|
+
version:
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project: tumblr4r
|
84
|
+
rubygems_version: 1.3.1
|
85
|
+
signing_key:
|
86
|
+
specification_version: 2
|
87
|
+
summary: Tumblr API Wrapper for Ruby
|
88
|
+
test_files:
|
89
|
+
- test/tumblr4r_test.rb
|