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 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'