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 CHANGED
@@ -17,5 +17,6 @@ tmtags
17
17
  coverage
18
18
  rdoc
19
19
  pkg
20
+ .bundle
20
21
 
21
22
  ## PROJECT::SPECIFIC
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gem 'weary','>= 0.7.1'
5
5
  group :test do
6
6
  gem 'rake'
7
7
  gem 'redgreen'
8
- gem 'fakeweb'
8
+ gem 'contest'
9
+ gem 'vcr'
9
10
  gem 'jeweler'
10
11
  end
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 wasn't enough of those. This one is powered by the [Weary](http://github.com/mwunsch/weary) gem.
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. I am in no way affiliated with Tumblr.
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
- module Tumblr
4
- VERSION = "0.0.1"
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
@@ -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'