tumblr-rb 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +58 -0
- data/README.md +87 -3
- data/Rakefile +6 -1
- data/lib/tumblr.rb +147 -2
- data/lib/tumblr/authenticator.rb +15 -0
- data/lib/tumblr/post.rb +173 -0
- data/lib/tumblr/post/audio.rb +16 -0
- data/lib/tumblr/post/conversation.rb +15 -0
- data/lib/tumblr/post/link.rb +15 -0
- data/lib/tumblr/post/photo.rb +16 -0
- data/lib/tumblr/post/quote.rb +16 -0
- data/lib/tumblr/post/regular.rb +14 -0
- data/lib/tumblr/post/video.rb +17 -0
- data/lib/tumblr/reader.rb +158 -0
- data/lib/tumblr/writer.rb +30 -0
- data/test/fixtures/vcr_cassettes/authenticate/authenticate.yml +39 -0
- data/test/fixtures/vcr_cassettes/read/authenticated.yml +40 -0
- data/test/fixtures/vcr_cassettes/read/authentication_failure.yml +33 -0
- data/test/fixtures/vcr_cassettes/read/mwunsch.yml +101 -0
- data/test/fixtures/vcr_cassettes/read/optional.yml +48 -0
- data/test/fixtures/vcr_cassettes/write/delete.yml +31 -0
- data/test/fixtures/vcr_cassettes/write/edit.yml +31 -0
- data/test/fixtures/vcr_cassettes/write/write.yml +31 -0
- data/test/helper.rb +20 -0
- data/test/test_tumblr.rb +591 -2
- data/{tumblr.gemspec → tumblr-rb.gemspec} +29 -9
- metadata +27 -6
- data/.document +0 -5
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
---
|
2
|
+
dependencies:
|
3
|
+
contest:
|
4
|
+
group:
|
5
|
+
- :test
|
6
|
+
version: ">= 0"
|
7
|
+
rake:
|
8
|
+
group:
|
9
|
+
- :test
|
10
|
+
version: ">= 0"
|
11
|
+
weary:
|
12
|
+
group:
|
13
|
+
- :default
|
14
|
+
version: ">= 0.7.1"
|
15
|
+
vcr:
|
16
|
+
group:
|
17
|
+
- :test
|
18
|
+
version: ">= 0"
|
19
|
+
jeweler:
|
20
|
+
group:
|
21
|
+
- :test
|
22
|
+
version: ">= 0"
|
23
|
+
redgreen:
|
24
|
+
group:
|
25
|
+
- :test
|
26
|
+
version: ">= 0"
|
27
|
+
specs:
|
28
|
+
- json_pure:
|
29
|
+
version: 1.2.2
|
30
|
+
- git:
|
31
|
+
version: 1.2.5
|
32
|
+
- redgreen:
|
33
|
+
version: 1.2.2
|
34
|
+
- rubyforge:
|
35
|
+
version: 2.0.4
|
36
|
+
- ruby-hmac:
|
37
|
+
version: 0.4.0
|
38
|
+
- crack:
|
39
|
+
version: 0.1.7
|
40
|
+
- oauth:
|
41
|
+
version: 0.3.6
|
42
|
+
- weary:
|
43
|
+
version: 0.7.1
|
44
|
+
- rake:
|
45
|
+
version: 0.8.7
|
46
|
+
- gemcutter:
|
47
|
+
version: 0.5.0
|
48
|
+
- jeweler:
|
49
|
+
version: 1.4.0
|
50
|
+
- contest:
|
51
|
+
version: 0.1.2
|
52
|
+
- fakeweb:
|
53
|
+
version: 1.2.8
|
54
|
+
- vcr:
|
55
|
+
version: 0.1.2
|
56
|
+
hash: c4f81bd854c0ac103c3a0434d4d004dd192b9b45
|
57
|
+
sources: []
|
58
|
+
|
data/README.md
CHANGED
@@ -1,13 +1,97 @@
|
|
1
1
|
# tumblr
|
2
2
|
|
3
|
-
Ruby wrapper and command line tool for the [Tumblr API](http://www.tumblr.com/docs/en/api). In case there
|
3
|
+
Ruby wrapper and command line tool for the [Tumblr API](http://www.tumblr.com/docs/en/api). In case there weren't enough of those already. This one is powered by the [Weary](http://github.com/mwunsch/weary) gem.
|
4
|
+
|
5
|
+
[RDoc](http://rdoc.info/projects/mwunsch/tumblr) | [Gem](http://rubygems.org/gems/tumblr-rb) | [Metrics](http://getcaliper.com/caliper/project?repo=git%3A%2F%2Fgithub.com%2Fmwunsch%2Ftumblr.git)
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
7
|
-
gem install tumblr-rb
|
9
|
+
gem install tumblr-rb
|
10
|
+
|
11
|
+
## Getting Started
|
12
|
+
|
13
|
+
Like [Jekyll](http://tom.preston-werner.com/jekyll/), and [Mustache](http://defunkt.github.com/mustache/), Tumblr gem will transform documents preceded by a [YAML](http://www.yaml.org/) frontmatter block.
|
14
|
+
|
15
|
+
YAML frontmatter beings with `---` on a single line, followed by YAML, ending with another `---` on a single line, e.g.
|
16
|
+
|
17
|
+
---
|
18
|
+
type: quote
|
19
|
+
source: Billy Shakespeare
|
20
|
+
state: draft
|
21
|
+
tags: hamlet, shakespeare
|
22
|
+
---
|
23
|
+
"To be or not to be."
|
24
|
+
|
25
|
+
Understood YAML parameters are taken from the Tumblr API: http://www.tumblr.com/docs/en/api#api_write
|
26
|
+
|
27
|
+
#### All Posts
|
28
|
+
|
29
|
+
type regular, photo, link, quote, conversation, video, audio
|
30
|
+
will take a guess if ommitted.
|
31
|
+
|
32
|
+
state published, queue, draft, submission
|
33
|
+
|
34
|
+
format html or markdown
|
35
|
+
|
36
|
+
tags comma-separated list of tags
|
37
|
+
|
38
|
+
date post date
|
39
|
+
|
40
|
+
private true if the post is private
|
41
|
+
|
42
|
+
slug A custom string to appear in the post's URL
|
43
|
+
|
44
|
+
group id for a secondary blog
|
45
|
+
|
46
|
+
generator description of the publishing application
|
47
|
+
|
48
|
+
send-to-twitter Twitter status update if the tumblelog has enabled it
|
49
|
+
|
50
|
+
publish-on if the post state is 'queue', publish on this date
|
51
|
+
|
52
|
+
#### Additional parameters for specific Post Types
|
53
|
+
|
54
|
+
regular title
|
55
|
+
|
56
|
+
photo caption, click-through-url
|
57
|
+
|
58
|
+
quote source
|
59
|
+
|
60
|
+
link name, description
|
61
|
+
|
62
|
+
conversation title
|
63
|
+
|
64
|
+
video title, caption
|
65
|
+
|
66
|
+
audio caption
|
67
|
+
|
68
|
+
To publish to Tumblr, do this:
|
69
|
+
|
70
|
+
request = Tumblr.new(username, password).post(document)
|
71
|
+
request.perform do |response|
|
72
|
+
if response.success?
|
73
|
+
puts response.body # Returns the new post's id.
|
74
|
+
else
|
75
|
+
puts "Something went wrong: #{response.code} #{response.message}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
## Goals
|
80
|
+
|
81
|
+
+ Full API coverage. Leave no method behind.
|
82
|
+
+ Well tested. Like a good Rubyist.
|
83
|
+
+ Obnoxiously simple CLI. *nix idioms are wonderful.
|
84
|
+
+ Kind-of-sort-of proof-of-concept for [Weary](http://github.com/mwunsch/weary).
|
85
|
+
|
86
|
+
## TODO:
|
87
|
+
|
88
|
+
+ Make the CLI
|
89
|
+
+ Make the manpages for the CLI
|
90
|
+
+ More convenience methods for the Tumblr class.
|
91
|
+
+ File-uploading for Photos, Videos, Audio (needs to get into Weary)
|
8
92
|
|
9
93
|
## Copyright
|
10
94
|
|
11
95
|
The Tumblr gem is Copyright (c) 2010 Mark Wunsch and is licensed under the [MIT License](http://creativecommons.org/licenses/MIT/).
|
12
96
|
|
13
|
-
Tumblr is Copyright (c) Tumblr, Inc.
|
97
|
+
Tumblr is Copyright (c) Tumblr, Inc. The Tumblr gem is NOT affiliated with Tumblr.
|
data/Rakefile
CHANGED
@@ -21,7 +21,7 @@ begin
|
|
21
21
|
gem.email = "mark@markwunsch.com"
|
22
22
|
gem.homepage = "http://github.com/mwunsch/tumblr"
|
23
23
|
gem.authors = ["Mark Wunsch"]
|
24
|
-
gem.add_dependency 'weary'
|
24
|
+
gem.add_dependency 'weary', '>= 0.7.1'
|
25
25
|
gem.add_development_dependency "bundler", ">= 0.9.7"
|
26
26
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
27
27
|
end
|
@@ -63,3 +63,8 @@ Rake::RDocTask.new do |rdoc|
|
|
63
63
|
rdoc.rdoc_files.include('README*')
|
64
64
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
65
65
|
end
|
66
|
+
|
67
|
+
desc "Open an irb session preloaded with this library"
|
68
|
+
task :console do
|
69
|
+
sh "irb -rubygems -I lib -r tumblr"
|
70
|
+
end
|
data/lib/tumblr.rb
CHANGED
@@ -1,5 +1,150 @@
|
|
1
1
|
require 'weary'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require 'tumblr/post'
|
4
|
+
require 'tumblr/reader'
|
5
|
+
require 'tumblr/writer'
|
6
|
+
require 'tumblr/authenticator'
|
7
|
+
|
8
|
+
class Tumblr
|
9
|
+
VERSION = "0.1.0"
|
10
|
+
GENERATOR = "The Tumblr Gem v#{VERSION}"
|
11
|
+
|
12
|
+
def initialize(*credentials)
|
13
|
+
@credentials = {:email => credentials[0], :password => credentials[1]} unless credentials.blank?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Convenience method for Reader#read
|
17
|
+
def read(username, parameters={})
|
18
|
+
reader = !@credentials.blank? ? Reader.new(@credentials[:email],@credentials[:password]) : Reader.new
|
19
|
+
reader.read(username, parameters)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Post a document to Tumblr. If the document has a post-id, it will be edited.
|
23
|
+
def post(doc)
|
24
|
+
raise 'Requires an e-mail address and password' unless @credentials
|
25
|
+
tumblr_post = if doc.is_a?(Tumblr::Post)
|
26
|
+
doc.to_h
|
27
|
+
elsif doc.respond_to?(:keys)
|
28
|
+
doc
|
29
|
+
else
|
30
|
+
Tumblr.parse(doc).to_h
|
31
|
+
end
|
32
|
+
writer = Writer.new(@credentials[:email],@credentials[:password])
|
33
|
+
tumblr_post.has_key?(:'post-id') ? writer.edit(tumblr_post) : writer.write(tumblr_post)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Parse a post out of a string
|
37
|
+
def self.parse(doc)
|
38
|
+
document = {}
|
39
|
+
if doc =~ /^(\s*---(.*)---\s*)/m
|
40
|
+
document[:data] = YAML.load(Regexp.last_match[2].strip)
|
41
|
+
document[:body] = doc.sub(Regexp.last_match[1],'').strip
|
42
|
+
else
|
43
|
+
document[:data] = {'type' => infer_post_type(doc)}
|
44
|
+
document[:body] = doc
|
45
|
+
end
|
46
|
+
create_post document
|
47
|
+
end
|
48
|
+
|
49
|
+
# Guess the Type of Post for a given documents
|
50
|
+
def self.infer_post_type(doc)
|
51
|
+
begin
|
52
|
+
url = URI.parse(doc)
|
53
|
+
if url.is_a?(URI::HTTP)
|
54
|
+
(url.host.include?('youtube.com') || url.host.include?('vimeo.com')) ? :video : :link
|
55
|
+
else
|
56
|
+
:regular
|
57
|
+
end
|
58
|
+
rescue URI::InvalidURIError
|
59
|
+
:regular
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Map a post type key to its class
|
64
|
+
def self.map(key)
|
65
|
+
case key
|
66
|
+
when :regular
|
67
|
+
Post::Regular
|
68
|
+
when :photo
|
69
|
+
Post::Photo
|
70
|
+
when :quote
|
71
|
+
Post::Quote
|
72
|
+
when :link
|
73
|
+
Post::Link
|
74
|
+
when :conversation
|
75
|
+
Post::Conversation
|
76
|
+
when :video
|
77
|
+
Post::Video
|
78
|
+
when :audio
|
79
|
+
Post::Audio
|
80
|
+
else
|
81
|
+
raise "#{key} is not an understood Tumblr post type"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def self.create_post(document)
|
88
|
+
post_data = document[:data]
|
89
|
+
type = (post_data['type'] || infer_post_type(document[:body])).to_sym
|
90
|
+
post = case type
|
91
|
+
when :regular
|
92
|
+
create_regular document
|
93
|
+
when :photo
|
94
|
+
create_photo document
|
95
|
+
when :audio
|
96
|
+
create_audio document
|
97
|
+
when :quote, :link, :conversation, :video
|
98
|
+
create_simple type, document
|
99
|
+
else
|
100
|
+
raise "#{type} is not a recognized Tumblr post type."
|
101
|
+
end
|
102
|
+
basic_setup post, post_data
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.basic_setup(post, post_data)
|
106
|
+
%w(format state private slug date group generator).each do |basic|
|
107
|
+
post.send "#{basic}=".intern, post_data[basic] if post_data[basic]
|
108
|
+
end
|
109
|
+
%w(tags send-to-twitter publish-on).each do |attribute|
|
110
|
+
post.send attribute.gsub('-','_').intern, post_data[attribute] if post_data[attribute]
|
111
|
+
end
|
112
|
+
post
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.setup_params(post, data)
|
116
|
+
post_params = post.class.parameters.collect {|param| param.to_s.gsub('_','-') }
|
117
|
+
post_params.each do |param|
|
118
|
+
post.send "#{param.gsub('-','_')}=".intern, data[param] if data[param]
|
119
|
+
end
|
120
|
+
post
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.create_regular(doc)
|
124
|
+
data = doc[:data]
|
125
|
+
post = Tumblr::Post::Regular.new(data['post-id'])
|
126
|
+
post.body = doc[:body]
|
127
|
+
setup_params post, data
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.create_photo(doc)
|
131
|
+
data = doc[:data]
|
132
|
+
post = Tumblr::Post::Photo.new(data['post-id'])
|
133
|
+
post.source = doc[:body]
|
134
|
+
setup_params post, data
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.create_audio(doc)
|
138
|
+
data = doc[:data]
|
139
|
+
post = Tumblr::Post::Audio.new(data['post-id'])
|
140
|
+
post.externally_hosted_url = doc[:body]
|
141
|
+
setup_params post, data
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.create_simple(type, doc)
|
145
|
+
data = doc[:data]
|
146
|
+
post = map(type).new(doc[:body], data['post-id'])
|
147
|
+
setup_params post, data
|
148
|
+
end
|
149
|
+
|
5
150
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Tumblr
|
2
|
+
class Authenticator < Weary::Base
|
3
|
+
|
4
|
+
def initialize(*credentials)
|
5
|
+
@defaults = {:email => credentials[0], :password => credentials[1]} unless credentials.blank?
|
6
|
+
end
|
7
|
+
|
8
|
+
# http://www.tumblr.com/docs/en/api#authenticate
|
9
|
+
post :authenticate do |auth|
|
10
|
+
auth.url = 'http://www.tumblr.com/api/authenticate'
|
11
|
+
auth.requires = [:email, :password]
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/lib/tumblr/post.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# An object that represents a post. From:
|
2
|
+
# http://www.tumblr.com/docs/en/api#api_write
|
3
|
+
class Tumblr
|
4
|
+
class Post
|
5
|
+
BASIC_PARAMS = [:date,:tags,:format,:group,:generator,:private,
|
6
|
+
:slug,:state,:'send-to-twitter',:'publish-on']
|
7
|
+
POST_PARAMS = [:title,:body,:source,:caption,:'click-through-url',
|
8
|
+
:quote,:name,:url,:description,:conversation,
|
9
|
+
:embed,:'externally-hosted-url']
|
10
|
+
|
11
|
+
def self.parameters(*attributes)
|
12
|
+
if !attributes.blank?
|
13
|
+
@parameters = attributes
|
14
|
+
attr_accessor *@parameters
|
15
|
+
end
|
16
|
+
@parameters
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :type, :state, :post_id, :format
|
20
|
+
attr_accessor :slug, :date, :group, :generator
|
21
|
+
|
22
|
+
def initialize(post_id = nil)
|
23
|
+
@post_id = post_id if post_id
|
24
|
+
end
|
25
|
+
|
26
|
+
def private=(bool)
|
27
|
+
@private = bool ? true : false
|
28
|
+
end
|
29
|
+
|
30
|
+
def private?
|
31
|
+
@private
|
32
|
+
end
|
33
|
+
|
34
|
+
def tags(*post_tags)
|
35
|
+
@tags = post_tags.join(',') if !post_tags.blank?
|
36
|
+
@tags
|
37
|
+
end
|
38
|
+
|
39
|
+
def state=(published_state)
|
40
|
+
allowed_states = [:published, :draft, :submission, :queue]
|
41
|
+
if !allowed_states.include?(published_state.to_sym)
|
42
|
+
raise "Not a recognized published state. Must be one of #{allowed_states.inspect}"
|
43
|
+
end
|
44
|
+
@state = published_state.to_sym
|
45
|
+
end
|
46
|
+
|
47
|
+
def format=(markup)
|
48
|
+
markup_format = markup.to_sym
|
49
|
+
if markup_format.eql?(:html) || markup_format.eql?(:markdown)
|
50
|
+
@format = markup_format
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_to_twitter(status=false)
|
55
|
+
if status
|
56
|
+
if status.to_sym.eql?(:no)
|
57
|
+
@send_to_twitter = false
|
58
|
+
else
|
59
|
+
@send_to_twitter = status
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@send_to_twitter
|
63
|
+
end
|
64
|
+
|
65
|
+
def publish_on(pubdate=nil)
|
66
|
+
@publish_on = pubdate if state.eql?(:queue) && pubdate
|
67
|
+
@publish_on
|
68
|
+
end
|
69
|
+
|
70
|
+
# Convert to a hash to be used in post writing/editing
|
71
|
+
def to_h
|
72
|
+
post_hash = {}
|
73
|
+
basics = [:post_id, :type, :date, :tags, :format, :group, :generator,
|
74
|
+
:slug, :state, :send_to_twitter, :publish_on]
|
75
|
+
params = basics.select {|opt| respond_to?(opt) && send(opt) }
|
76
|
+
params |= self.class.parameters.select {|opt| send(opt) } unless self.class.parameters.blank?
|
77
|
+
params.each { |key| post_hash[key.to_s.gsub('_','-').to_sym] = send(key) } unless params.empty?
|
78
|
+
post_hash[:private] = 1 if private?
|
79
|
+
post_hash
|
80
|
+
end
|
81
|
+
|
82
|
+
# Publish this post to Tumblr
|
83
|
+
def write(email, password)
|
84
|
+
Writer.new(email,password).write(to_h)
|
85
|
+
end
|
86
|
+
|
87
|
+
def edit(email, password)
|
88
|
+
Writer.new(email,password).edit(to_h)
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete(email, password)
|
92
|
+
Writer.new(email,password).delete(to_h)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Write to Tumblr and set state to Publish
|
96
|
+
def publish_now(email, password)
|
97
|
+
self.state = :published
|
98
|
+
return edit(email,password) if post_id
|
99
|
+
write(email,password)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Save as a draft
|
103
|
+
def save_as_draft(email, password)
|
104
|
+
self.state = :draft
|
105
|
+
return edit(email,password) if post_id
|
106
|
+
write(email,password)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Adds to Queue. Pass an additional date to publish at a specific date.
|
110
|
+
def add_to_queue(email, password, pubdate = nil)
|
111
|
+
self.state = :queue
|
112
|
+
self.publish_on(pubdate) if pubdate
|
113
|
+
return edit(email,password) if post_id
|
114
|
+
write(email,password)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Convert post to a YAML representation
|
118
|
+
def to_yaml
|
119
|
+
post = {}
|
120
|
+
post['data'] = post_data
|
121
|
+
post['body'] = to_h[post_body].to_s
|
122
|
+
YAML.dump(post)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Convert post to a string for writing to a file
|
126
|
+
def to_s
|
127
|
+
post_string = YAML.dump(post_data)
|
128
|
+
post_string += "---\x0D\x0A"
|
129
|
+
post_string += YAML.load(to_yaml)['body']
|
130
|
+
post_string
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def post_data
|
136
|
+
data = {}
|
137
|
+
to_h.each_pair do |key,value|
|
138
|
+
data[key.to_s] = value.to_s
|
139
|
+
end
|
140
|
+
data.reject! {|key,value| key.eql?(post_body.to_s) }
|
141
|
+
data
|
142
|
+
end
|
143
|
+
|
144
|
+
def post_body
|
145
|
+
case type
|
146
|
+
when :regular
|
147
|
+
:body
|
148
|
+
when :photo
|
149
|
+
:source
|
150
|
+
when :quote
|
151
|
+
:quote
|
152
|
+
when :link
|
153
|
+
:url
|
154
|
+
when :conversation
|
155
|
+
:conversation
|
156
|
+
when :video
|
157
|
+
:embed
|
158
|
+
when :audio
|
159
|
+
:'externally-hosted-url'
|
160
|
+
else
|
161
|
+
raise "#{type} is not a recognized Tumblr post type."
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
require 'tumblr/post/regular'
|
168
|
+
require 'tumblr/post/photo'
|
169
|
+
require 'tumblr/post/quote'
|
170
|
+
require 'tumblr/post/link'
|
171
|
+
require 'tumblr/post/conversation'
|
172
|
+
require 'tumblr/post/video'
|
173
|
+
require 'tumblr/post/audio'
|