tumblr-rb 1.3.0 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
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