bitsa 0.20 → 0.30

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