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.
- data/.travis.yml +7 -0
- data/Gemfile +15 -8
- data/Gemfile.lock +65 -65
- data/LICENSE +4 -2
- data/README.md +31 -84
- data/Rakefile +8 -68
- data/bin/tumblr +3 -133
- data/lib/tumblr.rb +7 -184
- data/lib/tumblr/authentication.rb +71 -0
- data/lib/tumblr/client.rb +148 -0
- data/lib/tumblr/command_line_interface.rb +222 -0
- data/lib/tumblr/credentials.rb +31 -0
- data/lib/tumblr/post.rb +253 -171
- data/lib/tumblr/post/answer.rb +17 -0
- data/lib/tumblr/post/audio.rb +22 -10
- data/lib/tumblr/post/chat.rb +25 -0
- data/lib/tumblr/post/link.rb +22 -9
- data/lib/tumblr/post/photo.rb +29 -10
- data/lib/tumblr/post/quote.rb +17 -10
- data/lib/tumblr/post/text.rb +18 -0
- data/lib/tumblr/post/video.rb +26 -11
- data/lib/tumblr/version.rb +3 -0
- data/lib/tumblr/views/error.erb +6 -0
- data/lib/tumblr/views/form.erb +11 -0
- data/lib/tumblr/views/layout.erb +41 -0
- data/lib/tumblr/views/success.erb +6 -0
- data/man/tumblr.1 +67 -65
- data/man/tumblr.1.html +131 -108
- data/man/tumblr.1.ronn +76 -57
- data/man/tumblr.5 +48 -68
- data/man/tumblr.5.html +106 -114
- data/man/tumblr.5.ronn +38 -51
- data/spec/fixtures/posts.json +10 -0
- data/spec/fixtures/typical_animated_gif.gif +0 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/tumblr/authentication_spec.rb +57 -0
- data/spec/tumblr/client_spec.rb +223 -0
- data/spec/tumblr/credentials_spec.rb +63 -0
- data/spec/tumblr/post_spec.rb +125 -0
- data/tumblr-rb.gemspec +16 -89
- metadata +101 -102
- data/lib/tumblr/authenticator.rb +0 -18
- data/lib/tumblr/post/conversation.rb +0 -15
- data/lib/tumblr/post/regular.rb +0 -14
- data/lib/tumblr/reader.rb +0 -191
- data/lib/tumblr/writer.rb +0 -39
- data/test/fixtures/vcr_cassettes/authenticate/authenticate.yml +0 -39
- data/test/fixtures/vcr_cassettes/read/all_pages.yml +0 -34
- data/test/fixtures/vcr_cassettes/read/authenticated.yml +0 -40
- data/test/fixtures/vcr_cassettes/read/authentication_failure.yml +0 -33
- data/test/fixtures/vcr_cassettes/read/like.yml +0 -31
- data/test/fixtures/vcr_cassettes/read/mwunsch.yml +0 -101
- data/test/fixtures/vcr_cassettes/read/optional.yml +0 -48
- data/test/fixtures/vcr_cassettes/read/pages.yml +0 -36
- data/test/fixtures/vcr_cassettes/read/tumblrgemtest.yml +0 -42
- data/test/fixtures/vcr_cassettes/read/unlike.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/delete.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/edit.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/reblog.yml +0 -31
- data/test/fixtures/vcr_cassettes/write/write.yml +0 -31
- data/test/helper.rb +0 -44
- data/test/test_tumblr.rb +0 -710
    
        data/lib/tumblr.rb
    CHANGED
    
    | @@ -1,185 +1,8 @@ | |
| 1 | 
            -
            require ' | 
| 1 | 
            +
            require 'tumblr/version'
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
               | 
| 5 | 
            -
               | 
| 6 | 
            -
             | 
| 7 | 
            -
              
         | 
| 8 | 
            -
             | 
| 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
         |