mbbx6spp-twurl 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ module Twurl
2
+ class ConfigurationController < AbstractCommandController
3
+ UNRECOGNIZED_SETTING_MESSAGE = "Unknown configuration setting: '%s'"
4
+ def dispatch
5
+ case options.subcommands.first
6
+ when 'default'
7
+ if profile = case options.subcommands.size
8
+ when 2
9
+ OAuthClient.load_client_for_username(options.subcommands.last)
10
+ when 3
11
+ OAuthClient.load_client_for_username_and_consumer_key(*options.subcommands[-2, 2])
12
+ end
13
+
14
+ OAuthClient.rcfile.default_profile = profile
15
+ OAuthClient.rcfile.save
16
+ end
17
+ else
18
+ CLI.puts(UNRECOGNIZED_SETTING_MESSAGE % options.subcommands.first)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,170 @@
1
+ module Twurl
2
+ class OAuthClient
3
+ class << self
4
+ def rcfile(reload = false)
5
+ if reload || @rcfile.nil?
6
+ @rcfile = RCFile.new
7
+ end
8
+ @rcfile
9
+ end
10
+
11
+ def load_from_options(options)
12
+ if rcfile.has_oauth_profile_for_username_with_consumer_key?(options.username, options.consumer_key)
13
+ load_client_for_username_and_consumer_key(options.username, options.consumer_key)
14
+ elsif options.username || (options.command == 'authorize')
15
+ load_new_client_from_options(options)
16
+ else
17
+ load_default_client
18
+ end
19
+ end
20
+
21
+ def load_client_for_username_and_consumer_key(username, consumer_key)
22
+ user_profiles = rcfile[username]
23
+ if user_profiles && attributes = user_profiles[consumer_key]
24
+ new(attributes)
25
+ else
26
+ raise Exception, "No profile for #{username}"
27
+ end
28
+ end
29
+
30
+ def load_client_for_username(username)
31
+ if user_profiles = rcfile[username]
32
+ if user_profiles.values.size == 1
33
+ new(user_profiles.values.first)
34
+ else
35
+ raise Exception, "There is more than one consumer key associated with #{username}. Please specify which consumer key you want as well."
36
+ end
37
+ else
38
+ raise Exception, "No profile for #{username}"
39
+ end
40
+ end
41
+
42
+ def load_new_client_from_options(options)
43
+ new(options.oauth_client_options.merge('password' => options.password))
44
+ end
45
+
46
+ def load_default_client
47
+ raise Exception, "You must authorize first" unless rcfile.default_profile
48
+ load_client_for_username_and_consumer_key(*rcfile.default_profile)
49
+ end
50
+ end
51
+
52
+ OAUTH_CLIENT_OPTIONS = %w[username consumer_key consumer_secret token secret]
53
+ attr_reader *OAUTH_CLIENT_OPTIONS
54
+ attr_reader :password
55
+ def initialize(options = {})
56
+ @username = options['username']
57
+ @password = options['password']
58
+ @consumer_key = options['consumer_key']
59
+ @consumer_secret = options['consumer_secret']
60
+ @token = options['token']
61
+ @secret = options['secret']
62
+ configure_http!
63
+ end
64
+
65
+ [:get, :post, :put, :delete, :options, :head, :copy].each do |request_method|
66
+ class_eval(<<-EVAL, __FILE__, __LINE__)
67
+ def #{request_method}(url, options = {})
68
+ # configure_http!
69
+ access_token.#{request_method}(url, options)
70
+ end
71
+ EVAL
72
+ end
73
+
74
+ def perform_request_from_options(options)
75
+ send(options.request_method, options.path, options.data)
76
+ end
77
+
78
+ def exchange_credentials_for_access_token
79
+ response = begin
80
+ consumer.token_request(:post, consumer.access_token_path, nil, {}, client_auth_parameters)
81
+ rescue OAuth::Unauthorized
82
+ perform_pin_authorize_workflow
83
+ end
84
+ @token = response[:oauth_token]
85
+ @secret = response[:oauth_token_secret]
86
+ end
87
+
88
+ def client_auth_parameters
89
+ {:x_auth_username => username, :x_auth_password => password, :x_auth_mode => 'client_auth'}
90
+ end
91
+
92
+ def perform_pin_authorize_workflow
93
+ @request_token = consumer.get_request_token
94
+ CLI.puts("Go to #{generate_authorize_url} and paste in the supplied PIN")
95
+ pin = gets
96
+ access_token = @request_token.get_access_token(:oauth_verifier => pin.chomp)
97
+ {:oauth_token => access_token.token, :oauth_token_secret => access_token.secret}
98
+ end
99
+
100
+ def generate_authorize_url
101
+ request = consumer.create_signed_request(:get, consumer.authorize_path, @request_token, pin_auth_parameters)
102
+ params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map { |p|
103
+ k, v = p.split('=')
104
+ v =~ /"(.*?)"/
105
+ "#{k}=#{CGI::escape($1)}"
106
+ }.join('&')
107
+ "#{Twurl.options.base_url}#{request.path}?#{params}"
108
+ end
109
+
110
+ def pin_auth_parameters
111
+ {:oauth_callback => 'oob'}
112
+ end
113
+
114
+ def fetch_verify_credentials
115
+ access_token.get('/1/account/verify_credentials.json')
116
+ end
117
+
118
+ def authorized?
119
+ oauth_response = fetch_verify_credentials
120
+ oauth_response.class == Net::HTTPOK
121
+ end
122
+
123
+ def needs_to_authorize?
124
+ token.nil? || secret.nil?
125
+ end
126
+
127
+ def save
128
+ verify_has_username
129
+ self.class.rcfile << self
130
+ end
131
+
132
+ def verify_has_username
133
+ if username.nil? || username == ''
134
+ oauth_response = fetch_verify_credentials
135
+ oauth_response.body =~ /"screen_name"\s*:\s*"(.*?)"/
136
+ @username = $1
137
+ end
138
+ end
139
+
140
+ def to_hash
141
+ OAUTH_CLIENT_OPTIONS.inject({}) do |hash, attribute|
142
+ if value = send(attribute)
143
+ hash[attribute] = value
144
+ end
145
+ hash
146
+ end
147
+ end
148
+
149
+ def configure_http!
150
+ consumer.http.set_debug_output(Twurl.options.debug_output_io) if Twurl.options.trace
151
+ if Twurl.options.ssl?
152
+ consumer.http.use_ssl = true
153
+ consumer.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
154
+ end
155
+ end
156
+
157
+ def consumer
158
+ @consumer ||=
159
+ OAuth::Consumer.new(
160
+ consumer_key,
161
+ consumer_secret,
162
+ :site => Twurl.options.base_url
163
+ )
164
+ end
165
+
166
+ def access_token
167
+ @access_token ||= OAuth::AccessToken.new(consumer, token, secret)
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,96 @@
1
+ module Twurl
2
+ class RCFile
3
+ FILE = '.twurlrc'
4
+ class << self
5
+ attr_accessor :directory
6
+
7
+ def directory
8
+ @directory || ENV['HOME']
9
+ end
10
+
11
+ def file_path
12
+ File.join(directory, FILE)
13
+ end
14
+
15
+ def load
16
+ YAML.load_file(file_path)
17
+ rescue Errno::ENOENT
18
+ default_rcfile_structure
19
+ end
20
+
21
+ def default_rcfile_structure
22
+ {'profiles' => {}, 'configuration' => {}}
23
+ end
24
+ end
25
+
26
+ attr_reader :data
27
+ def initialize
28
+ @data = self.class.load
29
+ end
30
+
31
+ def empty?
32
+ data == self.class.default_rcfile_structure
33
+ end
34
+
35
+ def save
36
+ File.open(self.class.file_path, 'w') do |rcfile|
37
+ rcfile.write data.to_yaml
38
+ end
39
+ end
40
+
41
+ def [](username)
42
+ profiles[username]
43
+ end
44
+
45
+ def profiles
46
+ data['profiles']
47
+ end
48
+
49
+ def default_profile
50
+ configuration['default_profile']
51
+ end
52
+
53
+ def default_profile=(profile)
54
+ configuration['default_profile'] = [profile.username, profile.consumer_key]
55
+ end
56
+
57
+ def configuration
58
+ data['configuration']
59
+ end
60
+
61
+ def alias(name, path)
62
+ data['aliases'] ||= {}
63
+ data['aliases'][name] = path
64
+ save
65
+ end
66
+
67
+ def aliases
68
+ data['aliases']
69
+ end
70
+
71
+ def alias_from_options(options)
72
+ options.subcommands.each do |potential_alias|
73
+ if path = alias_from_name(potential_alias)
74
+ break path
75
+ end
76
+ end
77
+ end
78
+
79
+ def alias_from_name(name)
80
+ aliases[name]
81
+ end
82
+
83
+ def has_oauth_profile_for_username_with_consumer_key?(username, consumer_key)
84
+ user_profiles = self[username]
85
+ !user_profiles.nil? && !user_profiles[consumer_key].nil?
86
+ end
87
+
88
+ def <<(oauth_client)
89
+ client_from_file = self[oauth_client.username] || {}
90
+ client_from_file[oauth_client.consumer_key] = oauth_client.to_hash
91
+ (profiles[oauth_client.username] ||= {}).update(client_from_file)
92
+ self.default_profile = oauth_client unless default_profile
93
+ save
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,19 @@
1
+ module Twurl
2
+ class RequestController < AbstractCommandController
3
+ NO_URI_MESSAGE = "No URI specified"
4
+ def dispatch
5
+ if client.needs_to_authorize?
6
+ raise Exception, "You need to authorize first."
7
+ end
8
+ options.path ||= OAuthClient.rcfile.alias_from_options(options)
9
+ perform_request
10
+ end
11
+
12
+ def perform_request
13
+ response = client.perform_request_from_options(options)
14
+ CLI.puts response.body
15
+ rescue URI::InvalidURIError
16
+ CLI.puts NO_URI_MESSAGE
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module Twurl
2
+ module VERSION
3
+ MAJOR = '0'
4
+ MINOR = '6'
5
+ TINY = '1'
6
+ BETA = nil # Time.now.to_i.to_s
7
+ end
8
+
9
+ Version = [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY, VERSION::BETA].compact * '.'
10
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class Twurl::AccountInformationController::DispatchWithNoAuthorizedAccountsTest < Test::Unit::TestCase
4
+ attr_reader :options, :client, :controller
5
+ def setup
6
+ @options = Twurl::Options.new
7
+ @client = Twurl::OAuthClient.load_new_client_from_options(options)
8
+ @controller = Twurl::AccountInformationController.new(client, options)
9
+ mock(Twurl::OAuthClient.rcfile).empty? { true }
10
+ end
11
+
12
+ def test_message_indicates_when_no_accounts_are_authorized
13
+ mock(Twurl::CLI).puts(Twurl::AccountInformationController::NO_AUTHORIZED_ACCOUNTS_MESSAGE).times(1)
14
+
15
+ controller.dispatch
16
+ end
17
+ end
18
+
19
+ class Twurl::AccountInformationController::DispatchWithOneAuthorizedAccountTest < Test::Unit::TestCase
20
+ attr_reader :options, :client, :controller
21
+ def setup
22
+ @options = Twurl::Options.test_exemplar
23
+ @client = Twurl::OAuthClient.load_new_client_from_options(options)
24
+ mock(Twurl::OAuthClient.rcfile).save.times(1)
25
+ Twurl::OAuthClient.rcfile << client
26
+ @controller = Twurl::AccountInformationController.new(client, options)
27
+ end
28
+
29
+ def test_authorized_account_is_displayed_and_marked_as_the_default
30
+ mock(Twurl::CLI).puts(client.username).times(1).ordered
31
+ mock(Twurl::CLI).puts(" #{client.consumer_key} (default)").times(1).ordered
32
+
33
+ controller.dispatch
34
+ end
35
+ end
36
+
37
+ class Twurl::AccountInformationController::DispatchWithOneUsernameThatHasAuthorizedMultipleAccountsTest < Test::Unit::TestCase
38
+ attr_reader :default_client_options, :default_client, :other_client_options, :other_client, :controller
39
+ def setup
40
+ @default_client_options = Twurl::Options.test_exemplar
41
+ @default_client = Twurl::OAuthClient.load_new_client_from_options(default_client_options)
42
+
43
+ @other_client_options = Twurl::Options.test_exemplar
44
+ other_client_options.consumer_key = default_client_options.consumer_key.reverse
45
+ @other_client = Twurl::OAuthClient.load_new_client_from_options(other_client_options)
46
+ mock(Twurl::OAuthClient.rcfile).save.times(2)
47
+
48
+ Twurl::OAuthClient.rcfile << default_client
49
+ Twurl::OAuthClient.rcfile << other_client
50
+
51
+ @controller = Twurl::AccountInformationController.new(other_client, other_client_options)
52
+ end
53
+
54
+ def test_authorized_account_is_displayed_and_marked_as_the_default
55
+ mock(Twurl::CLI).puts(default_client.username).times(1)
56
+ mock(Twurl::CLI).puts(" #{default_client.consumer_key} (default)").times(1)
57
+ mock(Twurl::CLI).puts(" #{other_client.consumer_key}").times(1)
58
+
59
+ controller.dispatch
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class Twurl::AliasesController::DispatchTest < Test::Unit::TestCase
4
+ attr_reader :options, :client
5
+ def setup
6
+ @options = Twurl::Options.test_exemplar
7
+ @client = Twurl::OAuthClient.test_exemplar
8
+
9
+ # Clean slate
10
+ if Twurl::OAuthClient.rcfile.aliases
11
+ Twurl::OAuthClient.rcfile.aliases.clear
12
+ end
13
+
14
+ stub(Twurl::OAuthClient.rcfile).save
15
+ end
16
+
17
+ def test_when_no_subcommands_are_provided_and_no_aliases_exist_nothing_is_displayed
18
+ assert options.subcommands.empty?
19
+ mock(Twurl::CLI).puts(Twurl::AliasesController::NO_ALIASES_MESSAGE).times(1)
20
+
21
+ controller = Twurl::AliasesController.new(client, options)
22
+ controller.dispatch
23
+ end
24
+
25
+ def test_when_no_subcommands_are_provided_and_aliases_exist_they_are_displayed
26
+ assert options.subcommands.empty?
27
+
28
+ Twurl::OAuthClient.rcfile.alias('h', '/1/statuses/home_timeline.xml')
29
+ mock(Twurl::CLI).puts("h: /1/statuses/home_timeline.xml").times(1)
30
+
31
+ controller = Twurl::AliasesController.new(client, options)
32
+ controller.dispatch
33
+ end
34
+
35
+ def test_when_alias_and_value_are_provided_they_are_added
36
+ options.subcommands = ['h']
37
+ options.path = '/1/statuses/home_timeline.xml'
38
+ mock(Twurl::OAuthClient.rcfile).alias('h', '/1/statuses/home_timeline.xml').times(1)
39
+
40
+ controller = Twurl::AliasesController.new(client, options)
41
+ controller.dispatch
42
+ end
43
+
44
+ def test_when_no_path_is_provided_nothing_happens
45
+ options.subcommands = ['a']
46
+ assert_nil options.path
47
+
48
+ mock(Twurl::CLI).puts(Twurl::AliasesController::NO_PATH_PROVIDED_MESSAGE).times(1)
49
+
50
+ controller = Twurl::AliasesController.new(client, options)
51
+ controller.dispatch
52
+ end
53
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class Twurl::AuthorizationController::DispatchTest < Test::Unit::TestCase
4
+ attr_reader :options, :client, :controller
5
+ def setup
6
+ @options = Twurl::Options.new
7
+ @client = Twurl::OAuthClient.load_new_client_from_options(options)
8
+ @controller = Twurl::AuthorizationController.new(client, options)
9
+ end
10
+
11
+ def test_successful_authentication_saves_retrieved_access_token
12
+ mock(client).exchange_credentials_for_access_token.times(1)
13
+ mock(client).save.times(1)
14
+ mock(controller).raise(Twurl::Exception, Twurl::AuthorizationController::AUTHORIZATION_FAILED_MESSAGE).never
15
+ mock(Twurl::CLI).puts(Twurl::AuthorizationController::AUTHORIZATION_SUCCEEDED_MESSAGE).times(1)
16
+
17
+ controller.dispatch
18
+ end
19
+
20
+ module ErrorCases
21
+ def test_failed_authorization_does_not_save_client
22
+ mock(client).exchange_credentials_for_access_token { raise OAuth::Unauthorized }
23
+ mock(client).save.never
24
+ mock(controller).raise(Twurl::Exception, Twurl::AuthorizationController::AUTHORIZATION_FAILED_MESSAGE).times(1)
25
+
26
+ controller.dispatch
27
+ end
28
+ end
29
+ include ErrorCases
30
+ end