tumblr-rb 1.3.0 → 2.0.0.alpha

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.
Files changed (62) hide show
  1. data/.travis.yml +7 -0
  2. data/Gemfile +15 -8
  3. data/Gemfile.lock +65 -65
  4. data/LICENSE +4 -2
  5. data/README.md +31 -84
  6. data/Rakefile +8 -68
  7. data/bin/tumblr +3 -133
  8. data/lib/tumblr.rb +7 -184
  9. data/lib/tumblr/authentication.rb +71 -0
  10. data/lib/tumblr/client.rb +148 -0
  11. data/lib/tumblr/command_line_interface.rb +222 -0
  12. data/lib/tumblr/credentials.rb +31 -0
  13. data/lib/tumblr/post.rb +253 -171
  14. data/lib/tumblr/post/answer.rb +17 -0
  15. data/lib/tumblr/post/audio.rb +22 -10
  16. data/lib/tumblr/post/chat.rb +25 -0
  17. data/lib/tumblr/post/link.rb +22 -9
  18. data/lib/tumblr/post/photo.rb +29 -10
  19. data/lib/tumblr/post/quote.rb +17 -10
  20. data/lib/tumblr/post/text.rb +18 -0
  21. data/lib/tumblr/post/video.rb +26 -11
  22. data/lib/tumblr/version.rb +3 -0
  23. data/lib/tumblr/views/error.erb +6 -0
  24. data/lib/tumblr/views/form.erb +11 -0
  25. data/lib/tumblr/views/layout.erb +41 -0
  26. data/lib/tumblr/views/success.erb +6 -0
  27. data/man/tumblr.1 +67 -65
  28. data/man/tumblr.1.html +131 -108
  29. data/man/tumblr.1.ronn +76 -57
  30. data/man/tumblr.5 +48 -68
  31. data/man/tumblr.5.html +106 -114
  32. data/man/tumblr.5.ronn +38 -51
  33. data/spec/fixtures/posts.json +10 -0
  34. data/spec/fixtures/typical_animated_gif.gif +0 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/spec/tumblr/authentication_spec.rb +57 -0
  37. data/spec/tumblr/client_spec.rb +223 -0
  38. data/spec/tumblr/credentials_spec.rb +63 -0
  39. data/spec/tumblr/post_spec.rb +125 -0
  40. data/tumblr-rb.gemspec +16 -89
  41. metadata +101 -102
  42. data/lib/tumblr/authenticator.rb +0 -18
  43. data/lib/tumblr/post/conversation.rb +0 -15
  44. data/lib/tumblr/post/regular.rb +0 -14
  45. data/lib/tumblr/reader.rb +0 -191
  46. data/lib/tumblr/writer.rb +0 -39
  47. data/test/fixtures/vcr_cassettes/authenticate/authenticate.yml +0 -39
  48. data/test/fixtures/vcr_cassettes/read/all_pages.yml +0 -34
  49. data/test/fixtures/vcr_cassettes/read/authenticated.yml +0 -40
  50. data/test/fixtures/vcr_cassettes/read/authentication_failure.yml +0 -33
  51. data/test/fixtures/vcr_cassettes/read/like.yml +0 -31
  52. data/test/fixtures/vcr_cassettes/read/mwunsch.yml +0 -101
  53. data/test/fixtures/vcr_cassettes/read/optional.yml +0 -48
  54. data/test/fixtures/vcr_cassettes/read/pages.yml +0 -36
  55. data/test/fixtures/vcr_cassettes/read/tumblrgemtest.yml +0 -42
  56. data/test/fixtures/vcr_cassettes/read/unlike.yml +0 -31
  57. data/test/fixtures/vcr_cassettes/write/delete.yml +0 -31
  58. data/test/fixtures/vcr_cassettes/write/edit.yml +0 -31
  59. data/test/fixtures/vcr_cassettes/write/reblog.yml +0 -31
  60. data/test/fixtures/vcr_cassettes/write/write.yml +0 -31
  61. data/test/helper.rb +0 -44
  62. data/test/test_tumblr.rb +0 -710
@@ -1,185 +1,8 @@
1
- require 'weary'
1
+ require 'tumblr/version'
2
2
 
3
- class Tumblr
4
- VERSION = "1.3.0"
5
- GENERATOR = "The Tumblr Gem v#{VERSION}"
6
- USER_AGENT = "TumblrGem/#{VERSION} (+http://github.com/mwunsch/tumblr)"
7
-
8
- require 'tumblr/post'
9
- require 'tumblr/reader'
10
- require 'tumblr/writer'
11
- require 'tumblr/authenticator'
12
-
13
- def initialize(*credentials)
14
- @credentials = {:email => credentials[0], :password => credentials[1]} unless credentials.blank?
15
- end
16
-
17
- # Convenience method for Reader#read
18
- def read(username, parameters={})
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
- tumblr_post = if doc.is_a?(Tumblr::Post)
25
- doc.to_h
26
- elsif doc.respond_to?(:keys)
27
- doc
28
- else
29
- Tumblr.parse(doc).to_h
30
- end
31
- tumblr_post.has_key?(:'post-id') ? writer.edit(tumblr_post) : writer.write(tumblr_post)
32
- end
33
-
34
- def dashboard(parameters={})
35
- raise 'Requires an e-mail address and password' unless @credentials
36
- reader.dashboard(parameters)
37
- end
38
-
39
- def authenticate(theme = false)
40
- raise 'Requires an e-mail address and password' unless @credentials
41
- params = theme ? {:'include-theme' => 1} : {}
42
- Authenticator.new(@credentials[:email],@credentials[:password]).authenticate(params)
43
- end
44
-
45
- def pages(username)
46
- reader.pages(username)
47
- end
48
-
49
- def all_pages(username)
50
- reader.all_pages(username)
51
- end
52
-
53
- def reader
54
- if @credentials.blank?
55
- Reader.new
56
- else
57
- Reader.new(@credentials[:email],@credentials[:password])
58
- end
59
- end
60
-
61
- def writer
62
- raise 'Requires an e-mail address and password' unless @credentials
63
- Writer.new(@credentials[:email],@credentials[:password])
64
- end
65
-
66
- def self.execute(credentials, input)
67
- request = new(credentials[:email],credentials[:password]).post(input)
68
- request.perform
69
- end
70
-
71
- # Parse a post out of a string
72
- def self.parse(doc)
73
- document = {}
74
- if doc =~ /^(\s*---(.*)---\s*)/m
75
- document[:data] = YAML.load(Regexp.last_match[2].strip)
76
- document[:body] = doc.sub(Regexp.last_match[1],'').strip
77
- else
78
- document[:data] = {'type' => infer_post_type(doc)}
79
- document[:body] = doc
80
- end
81
- create_post document
82
- end
83
-
84
- # Guess the Type of Post for a given documents
85
- def self.infer_post_type(doc)
86
- begin
87
- url = URI.parse(doc)
88
- if url.is_a?(URI::HTTP)
89
- (url.host.include?('youtube.com') || url.host.include?('vimeo.com')) ? :video : :link
90
- else
91
- :regular
92
- end
93
- rescue URI::InvalidURIError
94
- :regular
95
- end
96
- end
97
-
98
- # Map a post type key to its class
99
- def self.map(key)
100
- case key
101
- when :regular
102
- Post::Regular
103
- when :photo
104
- Post::Photo
105
- when :quote
106
- Post::Quote
107
- when :link
108
- Post::Link
109
- when :conversation
110
- Post::Conversation
111
- when :video
112
- Post::Video
113
- when :audio
114
- Post::Audio
115
- else
116
- raise "#{key} is not an understood Tumblr post type"
117
- end
118
- end
119
-
120
- private
121
-
122
- def self.create_post(document)
123
- post_data = document[:data]
124
- type = (post_data['type'] || infer_post_type(document[:body])).to_sym
125
- post = case type
126
- when :regular
127
- create_regular document
128
- when :photo
129
- create_photo document
130
- when :audio
131
- create_audio document
132
- when :quote, :link, :conversation, :video
133
- create_simple type, document
134
- else
135
- raise "#{type} is not a recognized Tumblr post type."
136
- end
137
- basic_setup post, post_data
138
- end
139
-
140
- def self.basic_setup(post, post_data)
141
- %w(format state private slug date group generator reblog-key).each do |basic|
142
- post.send "#{basic}=".gsub('-','_').intern, post_data[basic] if post_data[basic]
143
- end
144
- %w(tags send-to-twitter publish-on).each do |attribute|
145
- post.send attribute.gsub('-','_').intern, post_data[attribute] if post_data[attribute]
146
- end
147
- post
148
- end
149
-
150
- def self.setup_params(post, data)
151
- post_params = post.class.parameters.collect {|param| param.to_s.gsub('_','-') }
152
- post_params.each do |param|
153
- post.send "#{param.gsub('-','_')}=".intern, data[param] if data[param]
154
- end
155
- post
156
- end
157
-
158
- def self.create_regular(doc)
159
- data = doc[:data]
160
- post = Tumblr::Post::Regular.new(data['post-id'])
161
- post.body = doc[:body]
162
- setup_params post, data
163
- end
164
-
165
- def self.create_photo(doc)
166
- data = doc[:data]
167
- post = Tumblr::Post::Photo.new(data['post-id'])
168
- post.source = doc[:body]
169
- setup_params post, data
170
- end
171
-
172
- def self.create_audio(doc)
173
- data = doc[:data]
174
- post = Tumblr::Post::Audio.new(data['post-id'])
175
- post.externally_hosted_url = doc[:body]
176
- setup_params post, data
177
- end
178
-
179
- def self.create_simple(type, doc)
180
- data = doc[:data]
181
- post = map(type).new(doc[:body], data['post-id'])
182
- setup_params post, data
183
- end
184
-
185
- end
3
+ module Tumblr
4
+ require "tumblr/client"
5
+ require "tumblr/post"
6
+
7
+ autoload :Authentication, "tumblr/authentication"
8
+ end
@@ -0,0 +1,71 @@
1
+ require 'sinatra/base'
2
+ require 'weary/request'
3
+ require 'weary/middleware'
4
+
5
+ module Tumblr
6
+ # http://www.tumblr.com/oauth/apps
7
+ class Authentication < Sinatra::Base
8
+ HOST = "http://www.tumblr.com/oauth"
9
+
10
+ enable :sessions
11
+ set :credential_path, nil
12
+
13
+ def request_token(key, secret, callback)
14
+ Weary::Request.new "#{HOST}/request_token", :POST do |req|
15
+ req.params :oauth_callback => callback
16
+ req.use Weary::Middleware::OAuth, :consumer_key => key,
17
+ :consumer_secret => secret
18
+ end
19
+ end
20
+
21
+ def access_token(token, token_secret, verifier, consumer_key, consumer_secret)
22
+ Weary::Request.new "#{HOST}/access_token", :POST do |req|
23
+ req.use Weary::Middleware::OAuth, :token => token,
24
+ :token_secret => token_secret,
25
+ :verifier => verifier,
26
+ :consumer_key => consumer_key,
27
+ :consumer_secret => consumer_secret
28
+ end
29
+ end
30
+
31
+ get "/" do
32
+ halt 400, erb(:form) unless params["key"] && params["secret"]
33
+ session[:consumer_key] = params["key"]
34
+ session[:consumer_secret] = params["secret"]
35
+ response = request_token(session[:consumer_key], session[:consumer_secret], url("/auth")).perform
36
+ if response.success?
37
+ result = Rack::Utils.parse_query(response.body)
38
+ logger.info(request.host)
39
+ session[:request_token_secret] = result["oauth_token_secret"]
40
+ redirect to("#{HOST}/authorize?oauth_token=#{result['oauth_token']}")
41
+ else
42
+ status response.status
43
+ erb response.body
44
+ end
45
+ end
46
+
47
+ get "/auth" do
48
+ halt 401, erb(:error) if params.empty?
49
+ token = params["oauth_token"]
50
+ verifier = params["oauth_verifier"]
51
+ response = access_token(token, session[:request_token_secret], verifier,
52
+ session[:consumer_key], session[:consumer_secret]).perform
53
+ if response.success?
54
+ require 'tumblr/credentials'
55
+ result = Rack::Utils.parse_query(response.body)
56
+ credentials = Tumblr::Credentials.new(settings.credential_path)
57
+ credentials.write session[:consumer_key],
58
+ session[:consumer_secret],
59
+ result["oauth_token"],
60
+ result["oauth_token_secret"]
61
+ @credential_path = credentials.path
62
+ status response.status
63
+ erb :success
64
+ else
65
+ status response.status
66
+ erb response.body
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,148 @@
1
+ require "weary"
2
+
3
+ module Tumblr
4
+ class Client < Weary::Client
5
+ API_VERSION = "v2"
6
+
7
+ USER_AGENT = "Tumblr API Client (Ruby)/#{Tumblr::VERSION} (+http://github.com/mwunsch/tumblr)"
8
+
9
+ POST_OPTIONS = [
10
+ :state, :tags, :tweet, :date, :format, :slug,
11
+ :title, :body, # Text posts
12
+ :caption, :link, :source, :data, #Photo posts
13
+ :quote, # Quote posts
14
+ :url, :description, # Link posts
15
+ :conversation, # Chat posts
16
+ :external_url, # Audio posts
17
+ :embed, # Video posts
18
+ :answer # Answer posts ??
19
+ ]
20
+
21
+ domain "http://api.tumblr.com/#{API_VERSION}"
22
+
23
+ user_agent USER_AGENT
24
+
25
+ get :info, "/blog/{hostname}/info" do |r|
26
+ r.required :api_key
27
+ end
28
+
29
+ get :avatar, "/blog/{hostname}/avatar" do |r|
30
+ r.optional :size
31
+ end
32
+
33
+ get :followers, "/blog/{hostname}/followers" do |r|
34
+ r.oauth!
35
+ r.optional :limit, :offset
36
+ end
37
+
38
+ get :posts, "/blog/{hostname}/posts" do |r|
39
+ r.required :api_key
40
+ r.optional :type, :id, :tag, :limit, :offset, :reblog_info,
41
+ :notes_info, :filter
42
+ end
43
+
44
+ get :queue, "/blog/{hostname}/posts/queue" do |r|
45
+ r.oauth!
46
+ end
47
+
48
+ get :draft, "/blog/{hostname}/posts/draft" do |r|
49
+ r.oauth!
50
+ end
51
+
52
+ get :submission, "/blog/{hostname}/posts/submission" do |r|
53
+ r.oauth!
54
+ end
55
+
56
+ post :post, "/blog/{hostname}/post" do |r|
57
+ r.oauth!
58
+ r.required :type
59
+ r.optional *POST_OPTIONS
60
+ end
61
+
62
+ post :edit, "/blog/{hostname}/post/edit" do |r|
63
+ r.oauth!
64
+ r.required :id
65
+ r.optional *POST_OPTIONS
66
+ end
67
+
68
+ post :reblog, "/blog/{hostname}/post/reblog" do |r|
69
+ r.oauth!
70
+ r.required :id, :reblog_key
71
+ r.optional *(POST_OPTIONS | [:comment])
72
+ end
73
+
74
+ post :delete, "/blog/{hostname}/post/delete" do |r|
75
+ r.oauth!
76
+ r.required :id
77
+ end
78
+
79
+ get :user, "/user/info" do |r|
80
+ r.oauth!
81
+ end
82
+
83
+ get :dashboard, "/user/dashboard" do |r|
84
+ r.oauth!
85
+ r.optional :limit, :offset, :type, :since_id, :reblog_info, :notes_info
86
+ end
87
+
88
+ get :likes, "/user/likes" do |r|
89
+ r.oauth!
90
+ r.optional :limit, :offset
91
+ end
92
+
93
+ get :following, "/user/following" do |r|
94
+ r.oauth!
95
+ r.optional :limit, :offset
96
+ end
97
+
98
+ post :follow, "/user/follow" do |r|
99
+ r.oauth!
100
+ r.required :url
101
+ end
102
+
103
+ post :unfollow, "/user/unfollow" do |r|
104
+ r.oauth!
105
+ r.required :url
106
+ end
107
+
108
+ post :like, "/user/like" do |r|
109
+ r.oauth!
110
+ r.required :id, :reblog_key
111
+ end
112
+
113
+ post :unlike, "/user/unlike" do |r|
114
+ r.oauth!
115
+ r.required :id, :reblog_key
116
+ end
117
+
118
+ get :tagged, "/tagged" do |r|
119
+ r.required :api_key, :tag
120
+ r.optional :before, :limit, :filter
121
+ end
122
+
123
+ def self.load(hostname = nil, path = nil)
124
+ require "tumblr/credentials"
125
+ credentials = Tumblr::Credentials.new(path).read
126
+ self.new(hostname, credentials)
127
+ end
128
+
129
+ def initialize(hostname = nil, oauth_params = {})
130
+ @defaults = {}
131
+ @defaults[:hostname] = hostname if hostname
132
+ [:consumer_key, :consumer_secret, :token, :token_secret].each do |param|
133
+ @defaults[param] = value_for_key_as_string_or_symbol(oauth_params, param) if hash_includes_key_as_string_or_symbol?(oauth_params, param)
134
+ end
135
+ @defaults[:api_key] = value_for_key_as_string_or_symbol(oauth_params, :consumer_key)
136
+ end
137
+
138
+ private
139
+
140
+ def value_for_key_as_string_or_symbol(hash, key)
141
+ hash[key.to_sym] || hash[key.to_s]
142
+ end
143
+
144
+ def hash_includes_key_as_string_or_symbol?(hash, key)
145
+ hash.keys.map(&:to_s).include? key.to_s
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,222 @@
1
+ require 'thor'
2
+ require 'tumblr'
3
+
4
+ module Tumblr
5
+ class CommandLineInterface < Thor
6
+ include Thor::Actions
7
+
8
+ default_task :pipe
9
+
10
+ class_option :credentials, :type => :string,
11
+ :desc => "The file path where your Tumblr OAuth keys are stored. Defaults to ~/.tumblr."
12
+ class_option :host, :type => :string,
13
+ :desc => 'The hostname of the blog you want to post to i.e. "YOUR-NAME.tumblr.com"'
14
+
15
+
16
+ check_unknown_options!(:except => [])
17
+
18
+
19
+ desc "pipe", "Pipe post content in from STDIN"
20
+ method_option :publish, :type => :boolean,
21
+ :aliases => "-p",
22
+ :desc => "Publish this post"
23
+ method_option :queue, :type => :boolean,
24
+ :aliases => "-q",
25
+ :desc => "Add this post to the queue"
26
+ method_option :draft, :type => :boolean,
27
+ :aliases => "-d",
28
+ :desc => "Save this post as a draft"
29
+ long_desc <<-LONGDESC
30
+ Publish a post to tumblr from STDIN for HOST.
31
+ It is assumed that STDIN contains a document formatted according to `tumblr(5)`.
32
+ If STDIN contains a URL, it will create a post using the same rules as `tumblr post`.
33
+
34
+ Writes the serialized post to STDOUT.
35
+ LONGDESC
36
+ def pipe
37
+ if !$stdin.tty?
38
+ puts post($stdin).serialize
39
+ else
40
+ invoke :help
41
+ end
42
+ end
43
+
44
+ desc "post <POST> | <FILE> | <URL>", "Posts to tumblr"
45
+ method_option :publish, :type => :boolean,
46
+ :aliases => "-p",
47
+ :desc => "Publish this post"
48
+ method_option :queue, :type => :boolean,
49
+ :aliases => "-q",
50
+ :desc => "Add this post to the queue"
51
+ method_option :draft, :type => :boolean,
52
+ :aliases => "-d",
53
+ :desc => "Save this post as a draft"
54
+ long_desc <<-LONGDESC
55
+ Post a POST to Tumblr for HOST. If a FILE path is given, the file will be read and posted to Tumblr.
56
+ It is assumed the post follows the `tumblr(5)` format.
57
+
58
+ If the FILE is an audio, image, or video file, it will create the respective post type on tumblr.
59
+
60
+ If a URL is given, a link post will be created.
61
+ If URL is a YouTube or Vimeo link, it will create a video post.
62
+ If URL is a SoundCloud or Spotify link, an audio post will be published.
63
+ LONGDESC
64
+ def post(arg)
65
+ client = get_client
66
+ post = if arg.respond_to? :read
67
+ Tumblr::Post.load arg.read
68
+ elsif File.file?(file_path = File.expand_path(arg))
69
+ Tumblr::Post.load_from_path file_path
70
+ else
71
+ Tumblr::Post.load arg.to_s
72
+ end
73
+ post.publish! if options[:publish]
74
+ post.queue! if options[:queue]
75
+ post.draft! if options[:draft]
76
+ response = post.post(client).perform
77
+ tumblr_error(response) unless response.success?
78
+ ui_success %Q(Post was successfully created! Post ID: #{response.parse["response"]["id"]}) if $stdin.tty?
79
+ post
80
+ end
81
+
82
+ desc "edit POST_ID", "Edit a post"
83
+ long_desc "Open up your $EDITOR to edit a published post."
84
+ long_desc <<-LONGDESC
85
+ Get a post from Tumblr and edit it.
86
+
87
+ Behaves similar to `git commit`, in that it will open up your editor in the foreground.
88
+ Look for a $TUMBLREDITOR environment variable, and if that's not found, will use $EDITOR.
89
+ LONGDESC
90
+ def edit(id)
91
+ client = get_client
92
+ response = client.posts(:id => id, :filter => :raw).perform
93
+ tumblr_error(response) unless response.success?
94
+ post = Tumblr::Post.create(response.parse["response"]["posts"].first)
95
+ require 'tempfile'
96
+ tmp_file = Tempfile.new("post_#{id}")
97
+ tmp_file.write(post.serialize)
98
+ tmp_file.rewind
99
+ ui_abort "Something went wrong editing the post." unless system "#{editor} #{tmp_file.path}"
100
+ edited_post = Tumblr::Post.load_from_path tmp_file.path
101
+ edited_response = edited_post.edit(client).perform
102
+ tumblr_error(edited_response) unless edited_response.success?
103
+ ui_success "Post #{id} successfully edited."
104
+ ensure
105
+ if tmp_file
106
+ tmp_file.close
107
+ tmp_file.unlink
108
+ end
109
+ end
110
+
111
+ desc "fetch POST_ID", "Fetch a post and write out its serialized form."
112
+ def fetch(id)
113
+ client = get_client
114
+ response = client.posts(:id => id, :filter => :raw).perform
115
+ tumblr_error(response) unless response.success?
116
+ post = Tumblr::Post.create(response.parse["response"]["posts"].first)
117
+ puts post.serialize
118
+ post
119
+ end
120
+
121
+ desc "delete POST_ID", "Delete a post"
122
+ def delete(id)
123
+ client = get_client
124
+ response = client.delete(:id => id).perform
125
+ tumblr_error(response) unless response.success?
126
+ ui_success "Post #{id} successfully deleted."
127
+ end
128
+
129
+ desc "authorize", "Authenticate and authorize the cli to post on your behalf"
130
+ option :port, :type => :string,
131
+ :default => "4567",
132
+ :desc => "Use PORT"
133
+ option :bind, :type => :string,
134
+ :default => "0.0.0.0",
135
+ :desc => "listen on BIND"
136
+ long_desc <<-LONGDESC
137
+ `tumblr authorize` will start up a server (listening on BIND and PORT) running
138
+ a small app to do the OAuth handshake with tumblr.
139
+
140
+ The app will open in the default browser, allowing you to authenticate to Tumblr
141
+ and authorize `tumblr` to do actions on your behalf. You will need to register
142
+ an application and enter the consumer key and consumer secret. The application will
143
+ write the OAuth keys to your CREDENTIALS file.
144
+
145
+ After authorization, you will be prompted to return to your terminal and shut down
146
+ the server (using CTRL-C).
147
+ LONGDESC
148
+ def authorize(*soak)
149
+ require 'tumblr/authentication'
150
+ sinatra_options = {
151
+ :port => options[:port],
152
+ :bind => options[:bind],
153
+ :credential_path => options[:credentials]
154
+ }
155
+ Tumblr::Authentication.run!(sinatra_options) do |server|
156
+ `open http://#{options[:bind]}:#{options[:port]}/`
157
+ end
158
+ if has_credentials?
159
+ ui_success "Success! Your Tumblr OAuth credentials were written to #{credentials.path}"
160
+ else
161
+ ui_abort "Something went wrong in authorization, and credentials were not correctly written to #{credentials.path}"
162
+ end
163
+ end
164
+
165
+ desc "version", "Print Tumblr version information"
166
+ def version
167
+ puts Tumblr::VERSION
168
+ end
169
+
170
+ private
171
+
172
+ def credentials
173
+ require 'tumblr/credentials'
174
+ Tumblr::Credentials.new(options[:credentials] || ENV["TUMBLRCRED"])
175
+ end
176
+
177
+ def editor
178
+ ENV["TUMBLREDITOR"] || ENV["EDITOR"]
179
+ end
180
+
181
+ def has_credentials?
182
+ !credentials.read.empty?
183
+ end
184
+
185
+ def check_credentials
186
+ ui_abort "Unable to find your OAuth keys. Run `tumblr authorize` to authenticate with Tumblr." unless has_credentials?
187
+ end
188
+
189
+ def get_host
190
+ host = ask("What is your Tumblr hostname?") if options[:host].nil? and $stdin.tty?
191
+ host ||= options[:host]
192
+ ui_abort "You need to provide a hostname i.e. --host=YOUR-NAME.tumblr.com" if host.nil? or host.empty?
193
+ host
194
+ end
195
+
196
+ def get_client
197
+ check_credentials
198
+ host = get_host
199
+ Tumblr::Client.load host, options[:credentials]
200
+ end
201
+
202
+ def tumblr_error(response)
203
+ parsed_response = response.parse
204
+ msg = parsed_response["response"].empty? ? response.parse["meta"]["msg"] : parsed_response["response"]["errors"]
205
+ ui_abort %Q(Tumblr returned a #{response.status} Error: #{msg})
206
+ end
207
+
208
+ def ui_abort(msg, exit_status = 1)
209
+ ui_puts msg, :red
210
+ exit exit_status
211
+ end
212
+
213
+ def ui_success(msg)
214
+ ui_puts msg, :green
215
+ end
216
+
217
+ def ui_puts(msg, color = nil)
218
+ say msg, $stdout.tty? ? color : nil
219
+ end
220
+
221
+ end
222
+ end