bitsa 0.20 → 0.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e349d67cf9375ec6e32227b26e321d4edcd33ee4
4
+ data.tar.gz: 35b9220b6a324d289df631bfd31ef388f4d7711e
5
+ SHA512:
6
+ metadata.gz: 1f5129e82b0321c4ded98e5c8f3ec728d4035e2b8a9a919f88eef38a022aa448ce30e3500629ec52188c31f93e1d7e9f2b698659f6cec6606b1337724470aa68
7
+ data.tar.gz: 265afe4c6f4eac44420d61426bd9163be95bd2837755dad9cdec8c821c82ae3150c7f4cef51a61850e7d820e2244b0fc4d455d9c19529af676f2b4dd52090640
@@ -1,31 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bitsa (0.20)
4
+ bitsa (0.30)
5
5
  gdata_19 (~> 1.1.3)
6
- trollop (= 1.15)
6
+ trollop (= 2.0)
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- diff-lcs (1.1.3)
12
- fakeweb (1.2.8)
11
+ diff-lcs (1.2.5)
12
+ docile (1.1.5)
13
+ fakeweb (1.3.0)
13
14
  gdata_19 (1.1.5)
14
- multi_json (1.3.5)
15
- rake (0.9.2.2)
16
- rspec (2.5.0)
17
- rspec-core (~> 2.5.0)
18
- rspec-expectations (~> 2.5.0)
19
- rspec-mocks (~> 2.5.0)
20
- rspec-core (2.5.2)
21
- rspec-expectations (2.5.0)
22
- diff-lcs (~> 1.1.2)
23
- rspec-mocks (2.5.0)
24
- simplecov (0.6.4)
15
+ multi_json (1.10.1)
16
+ rake (10.3.2)
17
+ rspec (3.1.0)
18
+ rspec-core (~> 3.1.0)
19
+ rspec-expectations (~> 3.1.0)
20
+ rspec-mocks (~> 3.1.0)
21
+ rspec-core (3.1.7)
22
+ rspec-support (~> 3.1.0)
23
+ rspec-expectations (3.1.2)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.1.0)
26
+ rspec-mocks (3.1.3)
27
+ rspec-support (~> 3.1.0)
28
+ rspec-support (3.1.2)
29
+ simplecov (0.9.1)
30
+ docile (~> 1.1.0)
25
31
  multi_json (~> 1.0)
26
- simplecov-html (~> 0.5.3)
27
- simplecov-html (0.5.3)
28
- trollop (1.15)
32
+ simplecov-html (~> 0.8.0)
33
+ simplecov-html (0.8.0)
34
+ trollop (2.0)
29
35
 
30
36
  PLATFORMS
31
37
  ruby
@@ -33,7 +39,7 @@ PLATFORMS
33
39
  DEPENDENCIES
34
40
  bitsa!
35
41
  bundler (>= 1.0.0)
36
- fakeweb (~> 1.2.8)
42
+ fakeweb (~> 1.3.0)
37
43
  rake
38
- rspec (~> 2.5.0)
44
+ rspec (~> 3.1.0)
39
45
  simplecov
data/HISTORY CHANGED
@@ -1,3 +1,14 @@
1
+ === 0.30
2
+
3
+ * 1 other:
4
+ * Remove Ruby 1.8.x support
5
+ * Upgrade RSPEC to 3.1.0
6
+ * Upgrade trollop to 2.0
7
+
8
+ * 1 minor enhancements
9
+ * Add auto_check_interval_days to configuration file and
10
+ --auto-check to arguments
11
+
1
12
  === 0.20
2
13
 
3
14
  * 2 minor enhancements
@@ -20,4 +31,3 @@
20
31
 
21
32
  * 1 major enhancement:
22
33
  * Initial release
23
-
@@ -1,7 +1,9 @@
1
1
  = Bitsa
2
2
 
3
- * http://github.com/colbell/bitsa
3
+ http://github.com/colbell/bitsa
4
4
 
5
+ {<img src="https://badge.fury.io/rb/bitsa.png" alt="Gem Version" />}[http://badge.fury.io/rb/bitsa]
6
+ {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/colbell/bitsa]
5
7
 
6
8
  A command line tool written in Ruby to access your GMail/Google Apps
7
9
  contacts. Designed to be used from Mutt but should be able to be used
@@ -12,6 +14,8 @@ from any email client that supports calling external programs.
12
14
 
13
15
  Since version 0.20 bitsa should work with both Ruby 1.8.7 and 1.9.x.
14
16
 
17
+ Since version 0.30 bitsa should work with Ruby 1.9.x. and above.
18
+
15
19
  gem install bitsa
16
20
 
17
21
 
@@ -25,6 +29,7 @@ for the login.
25
29
  :login: myself@gmail.com
26
30
  :password: mypassword
27
31
  :cache_file_path: ~/.bitsa_cache.yml
32
+ :auto_check: 1
28
33
 
29
34
  The configuration file is not mandatory, you can pass in your email address
30
35
  and password on the command line, see Usage.
@@ -32,6 +37,10 @@ and password on the command line, see Usage.
32
37
  If you have no configuration file or if <tt>cache_file_path</tt> is not specified in the
33
38
  configuration file it will default to <tt>~/.bitsa_cache.yml</tt>
34
39
 
40
+ If <tt>auto_check: X</tt> is greater than 0 then
41
+ whenever you run a search it will check if the cache is more than X
42
+ days old and if so will first run the update subcommand. Defaults to 1.
43
+
35
44
  If you store your email password in the configuration file you should
36
45
  ensue that it is only readable by you:
37
46
 
@@ -132,6 +141,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
132
141
  GNU General Public License for more details.
133
142
 
134
143
  You should have received a copy of the GNU General Public License
135
- along with this program. If not, see [http://www.gnu.org/licenses/].
144
+ along with this program. If not, see {http://www.gnu.org/licenses/}[http://www.gnu.org/licenses/].
136
145
 
137
146
  -----
data/bin/bitsa CHANGED
@@ -32,4 +32,3 @@ args.parse(ARGV)
32
32
 
33
33
  app = Bitsa::BitsaApp.new
34
34
  app.run(args.global_opts, args.cmd, args.search_data)
35
-
@@ -12,20 +12,20 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Command line GMail Contacts lookup tool.}
13
13
  s.description = %q{Allows you to lookup GMail contacts and cache contacts locally from the command line.}
14
14
  s.required_rubygems_version = ">= 1.3.6"
15
- #s.required_ruby_version = "< 1.9.0"
15
+ s.required_ruby_version = ">= 1.9.0"
16
16
 
17
17
  s.extra_rdoc_files = ["README.rdoc", "HISTORY", "COPYING"]
18
18
  s.rdoc_options = ["--main", "README.rdoc"]
19
19
 
20
- s.add_dependency "trollop", "1.15"
20
+ s.add_dependency "trollop", "2.0"
21
21
 
22
22
  # Use the fork until the real one works with Ruby 1.9X
23
23
  #s.add_dependency "gdata", "~> 1.1.2"
24
24
  s.add_dependency "gdata_19", "~> 1.1.3"
25
25
 
26
26
  s.add_development_dependency "bundler", ">= 1.0.0"
27
- s.add_development_dependency "rspec", "~> 2.5.0"
28
- s.add_development_dependency "fakeweb", "~> 1.2.8"
27
+ s.add_development_dependency "rspec", "~> 3.1.0"
28
+ s.add_development_dependency "fakeweb", "~> 1.3.0"
29
29
  s.add_development_dependency "simplecov"
30
30
  s.add_development_dependency "rake"
31
31
 
@@ -29,20 +29,30 @@ module Bitsa
29
29
  def run(global_opts, cmd, search_data)
30
30
  settings = Settings.new
31
31
  settings.load(ConfigFile.new(global_opts[:config_file]), global_opts)
32
- cache = ContactsCache.new(settings.cache_file_path, 1)
33
-
34
- if cmd == "reload"
35
- cache.clear!
36
- end
37
-
38
- if ["reload", "update"].include?(cmd) || cache.stale?
39
- loader = GmailContactsLoader.new(settings.login, settings.password)
40
- loader.update_cache(cache)
41
- end
42
-
43
- if cmd == "search"
44
- puts "" # Force first entry to be displayed in mutt
45
- cache.search(search_data).each {|k,v| puts "#{k}\t#{v}"}
32
+ cache = ContactsCache.new(settings.cache_file_path, settings.auto_check)
33
+
34
+ if cmd == "skel"
35
+ puts <<-EOS
36
+ ---
37
+ :login: myself@gmail.com
38
+ :password: mypassword
39
+ :cache_file_path: ~/.bitsa_cache.yml
40
+ :auto_check: 1
41
+ EOS
42
+ else
43
+ if cmd == "reload"
44
+ cache.clear!
45
+ end
46
+
47
+ if ["reload", "update"].include?(cmd) || cache.stale?
48
+ loader = GmailContactsLoader.new(settings.login, settings.password)
49
+ loader.update_cache(cache)
50
+ end
51
+
52
+ if cmd == "search"
53
+ puts "" # Force first entry to be displayed in mutt
54
+ cache.search(search_data).each {|k,v| puts "#{k}\t#{v}"}
55
+ end
46
56
  end
47
57
  end
48
58
  end
@@ -27,7 +27,7 @@ module Bitsa #:nodoc:
27
27
  # is used to handle the parsing.
28
28
  class ArgsProcessor
29
29
  # Valid commands.
30
- SUB_COMMANDS = %w(update reload search)
30
+ SUB_COMMANDS = %w(update reload search skel)
31
31
 
32
32
  # Global options passed on the command line.
33
33
  attr_reader :global_opts
@@ -50,7 +50,10 @@ Usage: bitsa [global-options] [subcommand] [command-opts]
50
50
 
51
51
  Global options are:
52
52
  EOS
53
- opt :config_file, "Configuration file", :default => "~/.bitsa_config.yml"
53
+ opt :config_file, "Configuration file", type: String,
54
+ default: "~/.bitsa_config.yml"
55
+ opt :auto_check, "Autocheck interval in days. 0 to disable (default: 1)",
56
+ type: Integer
54
57
  opt :login, "Login", :type => String
55
58
  opt :password, "Password", :type => String
56
59
 
@@ -62,6 +65,7 @@ bitsa subcommands
62
65
  update: get the latest changes from Gmail
63
66
  reload: Clear all cached addresses and reload from Gmail
64
67
  search: Search for the passed string
68
+ skel: Write a skeleton configuration file to standard output
65
69
 
66
70
  Information about this program
67
71
  EOS
@@ -72,7 +76,7 @@ EOS
72
76
 
73
77
  if cmd == "search"
74
78
  @search_data << args.shift unless args.empty?
75
- elsif !["search", "update", "reload"].include?(cmd)
79
+ elsif !ArgsProcessor::SUB_COMMANDS.include?(cmd)
76
80
  Trollop::die "unknown subcommand '#{cmd}'"
77
81
  end
78
82
  end
@@ -34,9 +34,10 @@ module Bitsa #:nodoc:
34
34
  # Date/Time cache was last updated.
35
35
  attr_accessor :source_last_modified
36
36
 
37
- # Load cache from file system. After <tt>lifespan_days</tt> the cache is considered stale.
37
+ # Load cache from file system. After <tt>lifespan_days</tt> the cache
38
+ # is considered stale.
38
39
  def initialize(cache_file_path, lifespan_days)
39
- @cache_file_path = File.expand_path(cache_file_path || "~/.bitsa_cache.yml")
40
+ @cache_file_path = File.expand_path(cache_file_path) # || "~/.bitsa_cache.yml")
40
41
  @lifespan_days = lifespan_days
41
42
  @addresses = {}
42
43
  @source_source_last_modified = nil
@@ -44,8 +45,9 @@ module Bitsa #:nodoc:
44
45
  end
45
46
 
46
47
  def stale?
47
- (@source_last_modified.nil? ||
48
- (DateTime.parse(@source_last_modified)+@lifespan_days) < DateTime.now)
48
+ @lifespan_days && @lifespan_days > 0 &&
49
+ (@source_last_modified.nil? ||
50
+ (DateTime.parse(@source_last_modified) + @lifespan_days) < DateTime.now)
49
51
  end
50
52
 
51
53
  # Remove all entries from cache.
@@ -59,10 +61,9 @@ module Bitsa #:nodoc:
59
61
  end
60
62
 
61
63
  def search(qry)
62
- qry ||= ""
63
- rg = Regexp.new(qry, Regexp::IGNORECASE)
64
+ rg = Regexp.new(qry || "", Regexp::IGNORECASE)
64
65
 
65
- # Flattens.each_slices to an array with [email1, name1, email2, name2] etc.
66
+ # Flatten to an array with [email1, name1, email2, name2] etc.
66
67
  results = @addresses.values.flatten.each_slice(2).find_all do |e, n|
67
68
  e.match(rg) || n.match(rg)
68
69
  end
@@ -24,13 +24,13 @@ module Bitsa #:nodoc:
24
24
  # Loads Contacts from Gmail into a <tt>ContactsCache</tt> object.
25
25
  class GmailContactsLoader
26
26
 
27
- # Number of contacts to retrieve as a single chunk.
28
- @@FETCH_SIZE = 25
29
-
30
- # Ctor specifying the Gmail (or Google Apps) user name and password.
31
- def initialize(user, pw) #, lifespan_days)
27
+ # Ctor specifying the Gmail (or Google Apps) user name and
28
+ # password and optionally the number of records to retrieve in
29
+ # each chunk.
30
+ def initialize(user, pw, fetch_size = 25)
32
31
  @user = user
33
32
  @pw = pw
33
+ @fetch_size = fetch_size
34
34
  end
35
35
 
36
36
  # Refresh the passsed <tt>ContactsCache</tt> with the latest contact
@@ -40,8 +40,8 @@ module Bitsa #:nodoc:
40
40
  client.clientlogin(@user, @pw)
41
41
 
42
42
  idx = 1
43
- until load_chunk(client, idx, cache) < @@FETCH_SIZE
44
- idx += @@FETCH_SIZE
43
+ until load_chunk(client, idx, cache) < @fetch_size
44
+ idx += @fetch_size
45
45
  end
46
46
  cache.source_last_modified = DateTime.now.to_s
47
47
  cache.save
@@ -51,12 +51,8 @@ module Bitsa #:nodoc:
51
51
 
52
52
  def load_chunk(client, idx, cache)
53
53
  last_modified = nil
54
- url = "https://www.google.com/m8/feeds/contacts/#{@user}/thin"
55
- url += "?orderby=lastmodified"
56
- url += "&showdeleted=true"
57
- url += "&max-results=#{@@FETCH_SIZE}"
58
- url += "&start-index=#{idx}"
59
- url += "&updated-min=#{CGI.escape(cache.source_last_modified)}" if cache.source_last_modified
54
+ url = generate_loader_url(idx, cache)
55
+
60
56
  feed = client.get(url).to_xml
61
57
  feed.elements.each('entry') do |entry|
62
58
  name = entry.elements['title'].text
@@ -76,5 +72,15 @@ module Bitsa #:nodoc:
76
72
  end
77
73
  feed.elements.count
78
74
  end
75
+
76
+ def generate_loader_url(idx, cache)
77
+ url = "https://www.google.com/m8/feeds/contacts/#{@user}/thin"
78
+ url += "?orderby=lastmodified"
79
+ url += "&showdeleted=true"
80
+ url += "&max-results=#{@fetch_size}"
81
+ url += "&start-index=#{idx}"
82
+ url += "&updated-min=#{CGI.escape(cache.source_last_modified)}" if cache.source_last_modified
83
+ url
84
+ end
79
85
  end
80
86
  end
@@ -31,6 +31,9 @@ module Bitsa #:nodoc:
31
31
  # Path to file to store cached contact information in.
32
32
  attr_reader :cache_file_path
33
33
 
34
+ # Number of days before auto checking
35
+ attr_reader :auto_check
36
+
34
37
  # Load settings from a hash of data from the configuration file and
35
38
  # options passed on the command line.
36
39
  #
@@ -40,10 +43,15 @@ module Bitsa #:nodoc:
40
43
  @login = config_file_hash.data[:login]
41
44
  @password = config_file_hash.data[:password]
42
45
  @cache_file_path = config_file_hash.data[:cache_file_path]
46
+ @auto_check = config_file_hash.data[:auto_check]
43
47
 
44
48
  @login = options[:login] if options[:login]
45
49
  @password = options[:password] if options[:password]
46
50
  @cache_file_path = options[:cache_file_path] if options[:cache_file_path]
51
+ @auto_check = options[:auto_check] if options[:auto_check]
52
+
53
+ @auto_check ||= 1
54
+ @cache_file_path ||= "~/.bitsa_cache.yml"
47
55
  end
48
56
 
49
57
  end
@@ -18,5 +18,5 @@
18
18
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
19
 
20
20
  module Bitsa #:nodoc:
21
- VERSION = "0.20"
21
+ VERSION = "0.30"
22
22
  end
data/rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p327@bitsa
@@ -1,5 +1,9 @@
1
1
  #require "rubygems"
2
- require "rspec" # Requied for rcov to work
2
+
3
+ require "rspec" # Required for rcov
4
+ require 'simplecov'
5
+ SimpleCov.start
6
+
3
7
  # require "test/unit"
4
8
  # require "shoulda"
5
9
  # require 'mocha'
@@ -0,0 +1,123 @@
1
+ require "helper"
2
+ require "bitsa/args_processor"
3
+
4
+ RSpec.shared_examples "a valid set of args" do |ap|
5
+ context :config_file do
6
+ specify { expect(ap.global_opts[:config_file]).to eq("somefile") }
7
+ end
8
+ context :login do
9
+ specify { expect(ap.global_opts[:login]).to eq("someone") }
10
+ end
11
+ context :password do
12
+ specify { expect(ap.global_opts[:password]).to eq("mypassword") }
13
+ end
14
+ context :autocheck do
15
+ specify { expect(ap.global_opts[:auto_check]).to eq(1) }
16
+ end
17
+ end
18
+
19
+ def valid_long_args
20
+ args = []
21
+ args << "--config-file"
22
+ args << "somefile"
23
+ args << "--login"
24
+ args << "someone"
25
+ args << "--password"
26
+ args << "mypassword"
27
+ args << "--auto-check"
28
+ args << "1"
29
+ args << "update"
30
+ args
31
+ end
32
+
33
+
34
+ describe Bitsa::ArgsProcessor do
35
+ let(:ap) { Bitsa::ArgsProcessor.new }
36
+
37
+ it "should raise SystemExit if an invalid command passed" do
38
+ expect { ap.parse(["unknown"]) }.to raise_error(SystemExit)
39
+ end
40
+
41
+ it "should raise SystemExit if nothing passed" do
42
+ expect { ap.parse([]) }.to raise_error(SystemExit)
43
+ end
44
+
45
+ context "passed" do
46
+ [["reload"], ['skel'], ["search", "data"], ["update"]].each do |cmd, data|
47
+ context "#{cmd} command" do
48
+ before { ap.parse([cmd, data]) }
49
+ specify {
50
+ expect(ap.cmd).to eq(cmd)
51
+ }
52
+ end
53
+ end
54
+ end
55
+
56
+ context "passing valid long arguments" do
57
+ ap = Bitsa::ArgsProcessor.new
58
+ ap.parse(valid_long_args)
59
+ it_behaves_like "a valid set of args", ap
60
+ end
61
+
62
+ context "passing valid short arguments" do
63
+ args = []
64
+ args << "-c"
65
+ args << "somefile"
66
+ args << "-l"
67
+ args << "someone"
68
+ args << "-p"
69
+ args << "mypassword"
70
+ args << "-a"
71
+ args << "1"
72
+ args << "update"
73
+
74
+ ap = Bitsa::ArgsProcessor.new
75
+ ap.parse(args)
76
+ it_behaves_like "a valid set of args", ap
77
+ end
78
+
79
+ context "Alphabetic --auto-check argument passed" do
80
+ let(:args) { valid_long_args.map { |x| x == "1" ? "a" : x } }
81
+
82
+ specify {
83
+ expect { Bitsa::ArgsProcessor.new.parse(args) }.to raise_error(SystemExit)
84
+ }
85
+ end
86
+
87
+ context "Zero --auto-check argument passed" do
88
+ let(:ap) { Bitsa::ArgsProcessor.new }
89
+ before(:each) { ap.parse(valid_long_args.map { |x| x == "1" ? "0" : x }) }
90
+
91
+ context "auto-check" do
92
+ specify { expect(ap.global_opts[:auto_check]).to eq(0) }
93
+ end
94
+ end
95
+
96
+ context "Not passing --auto-check" do
97
+ let(:ap) { Bitsa::ArgsProcessor.new }
98
+ before(:each) { ap.parse ['update']}
99
+
100
+ context "auto-check" do
101
+ specify { expect(ap.global_opts[:auto_check]).to be_nil }
102
+ end
103
+ end
104
+
105
+ context "Not passing --config-file" do
106
+ before(:each) { ap.parse ['update']}
107
+ let(:ap) { Bitsa::ArgsProcessor.new }
108
+
109
+ context "config_file" do
110
+ specify { expect(ap.global_opts[:config_file]).to eq("~/.bitsa_config.yml") }
111
+ end
112
+ end
113
+
114
+ context "passing --search with some search data" do
115
+ before(:each) { ap.parse ['search', 'something']}
116
+ let(:ap) { Bitsa::ArgsProcessor.new }
117
+
118
+ context "search_data" do
119
+ specify { expect(ap.search_data).to eq "something" }
120
+ end
121
+ end
122
+
123
+ end