twat 0.6.3 → 0.9.2

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.
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