tumblr-rb 0.0.1 → 0.1.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.
- 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'
|