twat 0.6.3 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -1
- data/.travis.yml +6 -0
- data/Gemfile +1 -4
- data/Gemfile.lock +43 -23
- data/Gemfile.travis +12 -0
- data/Rakefile +9 -0
- data/TODO +4 -1
- data/lib/twat.rb +30 -26
- data/lib/twat/argparse.rb +36 -79
- data/lib/twat/config.rb +22 -34
- data/lib/twat/endpoint.rb +11 -5
- data/lib/twat/endpoints/base.rb +39 -0
- data/lib/twat/endpoints/identica.rb +5 -1
- data/lib/twat/endpoints/twitter.rb +5 -1
- data/lib/twat/exceptions.rb +93 -46
- data/lib/twat/follow_mixin.rb +134 -0
- data/lib/twat/options.rb +28 -12
- data/lib/twat/subcommand.rb +34 -0
- data/lib/twat/subcommands/add.rb +17 -0
- data/lib/twat/subcommands/base.rb +122 -0
- data/lib/twat/subcommands/config.rb +15 -0
- data/lib/twat/subcommands/delete.rb +24 -0
- data/lib/twat/subcommands/finger.rb +23 -0
- data/lib/twat/subcommands/follow.rb +18 -0
- data/lib/twat/subcommands/follow_tag.rb +19 -0
- data/lib/twat/subcommands/list.rb +17 -0
- data/lib/twat/subcommands/mentions.rb +20 -0
- data/lib/twat/subcommands/set.rb +19 -0
- data/lib/twat/subcommands/track.rb +33 -0
- data/lib/twat/subcommands/update.rb +21 -0
- data/lib/twat/subcommands/update_config.rb +16 -0
- data/lib/twat/subcommands/version.rb +14 -0
- data/lib/twat/twatopt.rb +0 -0
- data/lib/twat/tweetstack.rb +38 -0
- data/lib/twat/version.rb +7 -0
- data/man/twat.1 +8 -5
- data/spec/argparse_spec.rb +48 -0
- data/spec/helpers/environment.rb +47 -0
- data/spec/helpers/fileutils.rb +18 -0
- data/spec/helpers/fixtures/core.rb +30 -0
- data/spec/helpers/fixtures/migrations.rb +13 -0
- data/spec/helpers/oauth.rb +15 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/subcommands/add_spec.rb +61 -0
- data/spec/subcommands/config_spec.rb +13 -0
- data/spec/subcommands/delete_spec.rb +12 -0
- data/spec/subcommands/finger_spec.rb +41 -0
- data/spec/subcommands/follow_spec.rb +12 -0
- data/spec/subcommands/follow_tag_spec.rb +52 -0
- data/spec/subcommands/list_spec.rb +8 -0
- data/spec/subcommands/mentions_spec.rb +24 -0
- data/spec/subcommands/set_spec.rb +126 -0
- data/spec/subcommands/track_spec.rb +23 -0
- data/spec/subcommands/update_config_spec.rb +23 -0
- data/spec/subcommands/update_spec.rb +12 -0
- data/spec/subcommands/version_spec.rb +12 -0
- data/spec/twat_cli_spec.rb +102 -0
- data/spec/twat_spec.rb +12 -0
- data/twat.gemspec +6 -2
- metadata +135 -22
- data/lib/twat/actions.rb +0 -85
- data/lib/twat/actions/add.rb +0 -36
- data/lib/twat/actions/delete.rb +0 -14
- data/lib/twat/actions/follow.rb +0 -148
- data/lib/twat/actions/follow_user.rb +0 -11
- data/lib/twat/actions/setoption.rb +0 -14
- data/lib/twat/actions/show.rb +0 -12
- data/lib/twat/actions/tweet.rb +0 -14
- data/lib/twat/actions/updateconfig.rb +0 -9
- data/lib/twat/actions/user_feed.rb +0 -17
- data/lib/twat/actions/version.rb +0 -9
data/lib/twat/version.rb
ADDED
data/man/twat.1
CHANGED
@@ -1,16 +1,19 @@
|
|
1
|
-
.TH twat 1 "
|
1
|
+
.TH twat 1 "February 2012" "Rich Healey" ""
|
2
2
|
.SH NAME
|
3
3
|
twat \- a cli/ruby interface to twitter.
|
4
4
|
.SH SYNOPSIS
|
5
|
-
.B twat [-n account]
|
5
|
+
.B twat [-n account] [follow|finger|config|add|delete|set|version]
|
6
6
|
.B twat [-n account] tweet goes here
|
7
7
|
.SH DESCRIPTION
|
8
8
|
.B twat
|
9
9
|
gives a simple interface to twitter from the command line.
|
10
10
|
.SH USAGE
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
Configuration can be initially generated with
|
12
|
+
.IP twat config
|
13
|
+
and extended after this point with
|
14
|
+
.IP twat add ACCOUNTNAME
|
15
|
+
tweeting is achieved with
|
16
|
+
.IP twat tweet goes here
|
14
17
|
.SH OPTIONS
|
15
18
|
.IP -n account
|
16
19
|
Specify which account to connect with. If not specified, twat searches your
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
["-n", "--account"].each do |flag|
|
6
|
+
it "Should respect the #{flag} flag" do #{{{
|
7
|
+
with_config(Fixtures::multiuser_config) do
|
8
|
+
|
9
|
+
set_argv [flag, "secondAccount"]
|
10
|
+
|
11
|
+
$args = ::Twat::Args.new
|
12
|
+
conf = ::Twat::Config.new
|
13
|
+
|
14
|
+
conf.account.should == Fixtures::multiuser_config[:accounts][:secondAccount]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end #}}}
|
18
|
+
|
19
|
+
["-h", "--help"].each do |flag|
|
20
|
+
it "Should print help and bail on the #{flag} flag" do #{{{
|
21
|
+
set_argv [flag]
|
22
|
+
|
23
|
+
STDOUT.expects(:puts) # Getting the exact usage string TODO
|
24
|
+
lambda { args = ::Twat::Args.new }.should raise_error SystemExit
|
25
|
+
end #}}}
|
26
|
+
end
|
27
|
+
|
28
|
+
["-v", "--version"].each do |flag|
|
29
|
+
it "Should print the version and exit on #{flag}" do #{{{
|
30
|
+
set_argv [flag]
|
31
|
+
|
32
|
+
STDOUT.expects(:puts).with("twat #{::Twat::VERSION}")
|
33
|
+
lambda { args = ::Twat::Args.new }.should raise_error SystemExit
|
34
|
+
|
35
|
+
end #}}}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Should print a nice error message when called with a nonexistant account" do #{{{
|
39
|
+
with_config(Fixtures::multiuser_config) do
|
40
|
+
|
41
|
+
set_argv ["-n", "nonexistantaccount", "rawr", "update"]
|
42
|
+
|
43
|
+
STDOUT.expects(:puts).with(Twat::Exceptions::NoSuchAccount.new.msg)
|
44
|
+
Twat::Twat.new.cli_run
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
def set_argv(ary)
|
2
|
+
while ARGV.length > 0
|
3
|
+
ARGV.pop
|
4
|
+
end
|
5
|
+
|
6
|
+
ary.each { |e| ARGV << e }
|
7
|
+
end
|
8
|
+
|
9
|
+
def set_env(pairs)
|
10
|
+
orig = {}
|
11
|
+
begin
|
12
|
+
pairs.each do |k, v|
|
13
|
+
# TODO Check what happens when unsetting keys
|
14
|
+
orig[k] = ENV[k]
|
15
|
+
ENV[k] = v
|
16
|
+
end
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
orig.each do |k, v|
|
20
|
+
ENV[k] = v
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_config(cfg)
|
26
|
+
FileUtils.mktemp do |dir|
|
27
|
+
set_env('TWAT_CONFIG' => "#{dir}/twatrc") do
|
28
|
+
File.open("#{dir}/twatrc", 'w') do |conf|
|
29
|
+
conf.chmod(0600)
|
30
|
+
conf.puts(cfg.to_yaml)
|
31
|
+
end
|
32
|
+
|
33
|
+
yield
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def with_no_config
|
40
|
+
FileUtils.mktemp do |dir|
|
41
|
+
set_env('TWAT_CONFIG' => "#{dir}/twatrc") do
|
42
|
+
|
43
|
+
yield
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'mktemp'
|
2
|
+
module FileUtils extend self
|
3
|
+
|
4
|
+
include MkTemp
|
5
|
+
|
6
|
+
# Shamelessly poached from homebrew
|
7
|
+
def mktemp(opts={})
|
8
|
+
tmp_prefix = ENV['TWAT_TEMP'] || Dir.pwd
|
9
|
+
tmp=Pathname.new MkTemp.mktempdir("#{tmp_prefix}/twat_spec-XXXX")
|
10
|
+
raise "Couldn't create build sandbox" if not tmp.directory?
|
11
|
+
begin
|
12
|
+
yield tmp
|
13
|
+
ensure
|
14
|
+
tmp.rmtree unless opts[:clean] == false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Fixtures
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def multiuser_config
|
5
|
+
{ accounts: {
|
6
|
+
:rich0H =>
|
7
|
+
{ oauth_token: "rich0h's token",
|
8
|
+
oauth_token_secret: "rich0H's secret",
|
9
|
+
endpoint: :twitter
|
10
|
+
},
|
11
|
+
:secondAccount =>
|
12
|
+
{ oauth_token: "secondAccount's token",
|
13
|
+
oauth_token_secret: "secondAccount's secret",
|
14
|
+
endpoint: :twitter
|
15
|
+
}
|
16
|
+
},
|
17
|
+
:default => :rich0H
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid_config
|
22
|
+
{ accounts: {
|
23
|
+
:rich0H =>
|
24
|
+
{ oauth_token: "I'mtotallyatokenbrah",
|
25
|
+
oauth_token_secret: "I'mtotallyasecretbrah",
|
26
|
+
endpoint: :twitter
|
27
|
+
} } }
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
STUB_URL = "url"
|
2
|
+
STUB_PIN = "1234"
|
3
|
+
def mock_request_token
|
4
|
+
t = mock()
|
5
|
+
t.expects(:authorize_url).returns(STUB_URL)
|
6
|
+
t.expects(:get_access_token).with(oauth_verifier: STUB_PIN).returns(mock_access_token)
|
7
|
+
return t
|
8
|
+
end
|
9
|
+
|
10
|
+
def mock_access_token
|
11
|
+
t = mock()
|
12
|
+
t.expects(:token).returns("I'mtotallyatokenbrah")
|
13
|
+
t.expects(:secret).returns("I'mtotallyasecretbrah")
|
14
|
+
return t
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should bail if add is called without an argument" do #{{{
|
6
|
+
set_argv ["add"]
|
7
|
+
STDOUT.expects(:puts).with(Twat::Subcommands::Add.usage)
|
8
|
+
|
9
|
+
lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
10
|
+
end #}}}
|
11
|
+
|
12
|
+
it "Should create a config file if one does not already exist" do #{{{
|
13
|
+
FileUtils.mktemp do |dir|
|
14
|
+
set_env('TWAT_CONFIG' => "#{dir}/twatrc") do
|
15
|
+
# stub out the io
|
16
|
+
|
17
|
+
STDOUT.expects(:puts).with("Please authenticate the application at #{STUB_URL}, then enter pin")
|
18
|
+
STDIN.expects(:gets).returns(STUB_PIN)
|
19
|
+
|
20
|
+
# Stub out any oauth calls that talk externally
|
21
|
+
|
22
|
+
OAuth::Consumer.any_instance.expects(:get_request_token).returns(mock_request_token)
|
23
|
+
|
24
|
+
set_argv ["add", "rich0H"]
|
25
|
+
|
26
|
+
Twat::Twat.new.cli_run
|
27
|
+
|
28
|
+
YAML.load_file(ENV['TWAT_CONFIG']).should == Fixtures::valid_config
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end #}}}
|
32
|
+
|
33
|
+
it "Should call the twitter endpoint if specified" do #{{{
|
34
|
+
Twat::Endpoints::Twitter.any_instance.expects(:authorize_account)
|
35
|
+
|
36
|
+
set_argv ["--endpoint", "twitter", "add", "rich0H"]
|
37
|
+
Twat::Twat.new.cli_run
|
38
|
+
end #}}}
|
39
|
+
|
40
|
+
it "Should call the identi.ca endpoint if specified" do #{{{
|
41
|
+
Twat::Endpoints::Identica.any_instance.expects(:authorize_account)
|
42
|
+
|
43
|
+
set_argv ["--endpoint", "identi.ca", "add", "rich0H"]
|
44
|
+
Twat::Twat.new.cli_run
|
45
|
+
end #}}}
|
46
|
+
|
47
|
+
it "Should call the twitter endpoint by default" do #{{{
|
48
|
+
Twat::Endpoints::Twitter.any_instance.expects(:authorize_account)
|
49
|
+
|
50
|
+
set_argv ["add", "rich0H"]
|
51
|
+
Twat::Twat.new.cli_run
|
52
|
+
end #}}}
|
53
|
+
|
54
|
+
it "Should fail if asked for an invalid endpoint" do #{{{
|
55
|
+
STDOUT.expects(:puts).with(Twat::Exceptions::NoSuchEndpoint.new.msg)
|
56
|
+
|
57
|
+
set_argv ["--endpoint", "rawk", "add", "rich0H"]
|
58
|
+
Twat::Twat.new.cli_run
|
59
|
+
end #}}}
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should bail if config already exists" do #{{{
|
6
|
+
with_config(Fixtures::multiuser_config) do
|
7
|
+
STDOUT.expects(:puts).with(Twat::Exceptions::AlreadyConfigured.new.msg)
|
8
|
+
set_argv ["config"]
|
9
|
+
Twat::Twat.new.cli_run
|
10
|
+
end
|
11
|
+
end #}}}
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should bail if delete is called without an argument" do #{{{
|
6
|
+
set_argv ["delete"]
|
7
|
+
STDOUT.expects(:puts).with(Twat::Subcommands::Delete.usage)
|
8
|
+
|
9
|
+
lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
10
|
+
end #}}}
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should bail if finger is called without an argument" do #{{{
|
6
|
+
with_config(Fixtures::multiuser_config) do
|
7
|
+
set_argv ["finger"]
|
8
|
+
STDOUT.expects(:puts).with(Twat::Subcommands::Finger.usage)
|
9
|
+
|
10
|
+
lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
11
|
+
end
|
12
|
+
end #}}}
|
13
|
+
|
14
|
+
it "Should retrieve a tweet for user if called with only a user" do #{{{
|
15
|
+
with_config(Fixtures::multiuser_config) do
|
16
|
+
tweet = mock()
|
17
|
+
tweet.expects(:each).yields(tweet)
|
18
|
+
Twitter.expects(:user_timeline).with("hanke", :count => 1).returns(tweet)
|
19
|
+
Twat::Subcommands::Finger.any_instance.expects(:format).with(tweet)
|
20
|
+
|
21
|
+
set_argv ["finger", "hanke"]
|
22
|
+
|
23
|
+
STDOUT.expects(:puts)
|
24
|
+
Twat::Twat.new.cli_run
|
25
|
+
end
|
26
|
+
end #}}}
|
27
|
+
|
28
|
+
it "Should retrieve n tweets for user if invoked with count" do #{{{
|
29
|
+
with_config(Fixtures::multiuser_config) do
|
30
|
+
tweet = mock()
|
31
|
+
Twitter.expects(:user_timeline).with("hanke", :count => 3).returns([tweet, tweet, tweet])
|
32
|
+
Twat::Subcommands::Finger.any_instance.expects(:format).with(tweet).times(3)
|
33
|
+
|
34
|
+
set_argv ["finger", "hanke", "3"]
|
35
|
+
|
36
|
+
STDOUT.expects(:puts).times(3)
|
37
|
+
Twat::Twat.new.cli_run
|
38
|
+
end
|
39
|
+
end #}}}
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should bail if follow is called without an argument" do #{{{
|
6
|
+
set_argv ["follow"]
|
7
|
+
STDOUT.expects(:puts).with(Twat::Subcommands::Follow.usage)
|
8
|
+
|
9
|
+
lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
10
|
+
end #}}}
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should search home_timeline when called sans args" do #{{{
|
6
|
+
with_config(Fixtures::multiuser_config) do
|
7
|
+
Twitter.expects(:home_timeline)
|
8
|
+
Twat::Subcommands::FollowTag.any_instance.expects(:untested).returns(false).at_least_once
|
9
|
+
set_argv []
|
10
|
+
|
11
|
+
# lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
12
|
+
Twat::Twat.new.cli_run
|
13
|
+
end
|
14
|
+
end #}}}
|
15
|
+
|
16
|
+
it "Should search home_timeline when called as follow_tag" do #{{{
|
17
|
+
with_config(Fixtures::multiuser_config) do
|
18
|
+
Twitter.expects(:home_timeline)
|
19
|
+
Twat::Subcommands::FollowTag.any_instance.expects(:untested).returns(false).at_least_once
|
20
|
+
set_argv ["follow_tag"]
|
21
|
+
|
22
|
+
# lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
23
|
+
Twat::Twat.new.cli_run
|
24
|
+
end
|
25
|
+
end #}}}
|
26
|
+
|
27
|
+
it "Should call search with the argument if called with one" do #{{{
|
28
|
+
with_config(Fixtures::multiuser_config) do
|
29
|
+
mock_opts = mock()
|
30
|
+
Twitter.expects(:search).with("#hackmelb", count: 5)
|
31
|
+
Twat::Subcommands::FollowTag.any_instance.expects(:untested).returns(false).at_least_once
|
32
|
+
set_argv ["follow_tag", "#hackmelb"]
|
33
|
+
|
34
|
+
# lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
35
|
+
Twat::Twat.new.cli_run
|
36
|
+
end
|
37
|
+
end #}}}
|
38
|
+
|
39
|
+
it "Should cat together all commandline args" do #{{{
|
40
|
+
with_config(Fixtures::multiuser_config) do
|
41
|
+
mock_opts = mock()
|
42
|
+
Twitter.expects(:search).with("#hackmelb richo", count: 5)
|
43
|
+
Twat::Subcommands::FollowTag.any_instance.expects(:untested).returns(false).at_least_once
|
44
|
+
set_argv ["follow_tag", "#hackmelb", "richo"]
|
45
|
+
|
46
|
+
# lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
47
|
+
Twat::Twat.new.cli_run
|
48
|
+
end
|
49
|
+
end #}}}
|
50
|
+
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Twat do
|
4
|
+
|
5
|
+
it "Should search recent mentions" do #{{{
|
6
|
+
with_config(Fixtures::multiuser_config) do
|
7
|
+
Twitter.expects(:mentions)
|
8
|
+
Twat::Subcommands::Mentions.any_instance.expects(:untested).returns(false).at_least_once
|
9
|
+
set_argv ["mentions"]
|
10
|
+
|
11
|
+
Twat::Twat.new.cli_run
|
12
|
+
end
|
13
|
+
end #}}}
|
14
|
+
|
15
|
+
it "Should bail if called with arguments" do #{{{
|
16
|
+
with_config(Fixtures::multiuser_config) do
|
17
|
+
STDOUT.expects(:puts).with(Twat::Subcommands::Mentions.usage)
|
18
|
+
set_argv ["mentions", "rawr"]
|
19
|
+
|
20
|
+
lambda { Twat::Twat.new.cli_run }.should raise_error SystemExit
|
21
|
+
end
|
22
|
+
end #}}}
|
23
|
+
|
24
|
+
end
|