twat 0.6.3 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.rvmrc +1 -1
  2. data/.travis.yml +6 -0
  3. data/Gemfile +1 -4
  4. data/Gemfile.lock +43 -23
  5. data/Gemfile.travis +12 -0
  6. data/Rakefile +9 -0
  7. data/TODO +4 -1
  8. data/lib/twat.rb +30 -26
  9. data/lib/twat/argparse.rb +36 -79
  10. data/lib/twat/config.rb +22 -34
  11. data/lib/twat/endpoint.rb +11 -5
  12. data/lib/twat/endpoints/base.rb +39 -0
  13. data/lib/twat/endpoints/identica.rb +5 -1
  14. data/lib/twat/endpoints/twitter.rb +5 -1
  15. data/lib/twat/exceptions.rb +93 -46
  16. data/lib/twat/follow_mixin.rb +134 -0
  17. data/lib/twat/options.rb +28 -12
  18. data/lib/twat/subcommand.rb +34 -0
  19. data/lib/twat/subcommands/add.rb +17 -0
  20. data/lib/twat/subcommands/base.rb +122 -0
  21. data/lib/twat/subcommands/config.rb +15 -0
  22. data/lib/twat/subcommands/delete.rb +24 -0
  23. data/lib/twat/subcommands/finger.rb +23 -0
  24. data/lib/twat/subcommands/follow.rb +18 -0
  25. data/lib/twat/subcommands/follow_tag.rb +19 -0
  26. data/lib/twat/subcommands/list.rb +17 -0
  27. data/lib/twat/subcommands/mentions.rb +20 -0
  28. data/lib/twat/subcommands/set.rb +19 -0
  29. data/lib/twat/subcommands/track.rb +33 -0
  30. data/lib/twat/subcommands/update.rb +21 -0
  31. data/lib/twat/subcommands/update_config.rb +16 -0
  32. data/lib/twat/subcommands/version.rb +14 -0
  33. data/lib/twat/twatopt.rb +0 -0
  34. data/lib/twat/tweetstack.rb +38 -0
  35. data/lib/twat/version.rb +7 -0
  36. data/man/twat.1 +8 -5
  37. data/spec/argparse_spec.rb +48 -0
  38. data/spec/helpers/environment.rb +47 -0
  39. data/spec/helpers/fileutils.rb +18 -0
  40. data/spec/helpers/fixtures/core.rb +30 -0
  41. data/spec/helpers/fixtures/migrations.rb +13 -0
  42. data/spec/helpers/oauth.rb +15 -0
  43. data/spec/spec_helper.rb +13 -0
  44. data/spec/subcommands/add_spec.rb +61 -0
  45. data/spec/subcommands/config_spec.rb +13 -0
  46. data/spec/subcommands/delete_spec.rb +12 -0
  47. data/spec/subcommands/finger_spec.rb +41 -0
  48. data/spec/subcommands/follow_spec.rb +12 -0
  49. data/spec/subcommands/follow_tag_spec.rb +52 -0
  50. data/spec/subcommands/list_spec.rb +8 -0
  51. data/spec/subcommands/mentions_spec.rb +24 -0
  52. data/spec/subcommands/set_spec.rb +126 -0
  53. data/spec/subcommands/track_spec.rb +23 -0
  54. data/spec/subcommands/update_config_spec.rb +23 -0
  55. data/spec/subcommands/update_spec.rb +12 -0
  56. data/spec/subcommands/version_spec.rb +12 -0
  57. data/spec/twat_cli_spec.rb +102 -0
  58. data/spec/twat_spec.rb +12 -0
  59. data/twat.gemspec +6 -2
  60. metadata +135 -22
  61. data/lib/twat/actions.rb +0 -85
  62. data/lib/twat/actions/add.rb +0 -36
  63. data/lib/twat/actions/delete.rb +0 -14
  64. data/lib/twat/actions/follow.rb +0 -148
  65. data/lib/twat/actions/follow_user.rb +0 -11
  66. data/lib/twat/actions/setoption.rb +0 -14
  67. data/lib/twat/actions/show.rb +0 -12
  68. data/lib/twat/actions/tweet.rb +0 -14
  69. data/lib/twat/actions/updateconfig.rb +0 -9
  70. data/lib/twat/actions/user_feed.rb +0 -17
  71. data/lib/twat/actions/version.rb +0 -9
@@ -0,0 +1,7 @@
1
+ module Twat
2
+ VERSION_MAJOR = 0
3
+ VERSION_MINOR = 9
4
+ VERSION_PATCH = 2
5
+
6
+ VERSION = "#{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_PATCH}"
7
+ end
data/man/twat.1 CHANGED
@@ -1,16 +1,19 @@
1
- .TH twat 1 "October 2011" "Rich Healey" ""
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] <-a|-d|-l|-u [user]>
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
- generally, twat -an accountname will prompt you to authenticate via a given
12
- URL, and from that point on twat -n accountname "tweet" will post "tweet" to
13
- your twitter account.
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,13 @@
1
+ module Fixtures
2
+ module Migrations
3
+ extend self
4
+
5
+ def pre_v1
6
+ { :rich0H =>
7
+ { oauth_token: "I'mtotallyatokenbrah",
8
+ oauth_token_secret: "I'mtotallyasecretbrah",
9
+ endpoint: :twitter
10
+ } }
11
+ end
12
+ end
13
+ 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
@@ -0,0 +1,13 @@
1
+ require 'twat'
2
+ require 'mocha'
3
+
4
+ require 'fileutils'
5
+
6
+ Dir["#{File.expand_path(File.dirname(__FILE__))}/helpers/**/*.rb"].each do |f|
7
+ puts "Requiring #{f}"
8
+ require f
9
+ end
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_with :mocha
13
+ end
@@ -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,8 @@
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
+ end #}}}
7
+
8
+ 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