tomatoharvest 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f508e0466cc97b1f3b0d619bdfacbd2344b8cb8f
4
- data.tar.gz: 3284680bde183e758259ec764f3ed84209931501
3
+ metadata.gz: 6f52afcccd4a11b6c0c6cda0692079bebcf3a96f
4
+ data.tar.gz: 32a18edb8874add209ba439cb094906c603d5d88
5
5
  SHA512:
6
- metadata.gz: 5544110fda87a0bb5b50d50f3a4e3d6e2da532088317ba286c6f4571950f33811d7ff03455a9ed2b2fc2668ca8d65a4eec134e70a39c0dd3e9f1a49e7295d418
7
- data.tar.gz: 7786c551b13055b700232d439cfe52f5c890199579e5b60f7e6aac41bdb0732b73c2116e11a74d9d30f060e651dc04346d3f93bf20ac5ac0047d0d16293c9beb
6
+ metadata.gz: 8945effcbc9b58c7eb8669447893a22d1bbad71eb19799ce268332ec1c9da69e2a93a9a4f50570846b0c8856d1a867c3a4f805b6aa5646a82429ca0756b93ec2
7
+ data.tar.gz: e5c24caf89932bb39c25fd0e912dce5bad88a123ee0363f522b979fe0677f24f0f115aa8533067455fc9043f5ebe95186aab0ee339f5222e180126adf2c8e2a5
data/.gitignore CHANGED
@@ -19,4 +19,5 @@ tmp
19
19
  *.so
20
20
  *.o
21
21
  *.a
22
+ *.swp
22
23
  mkmf.log
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in po.gemspec
4
3
  gemspec
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # TomatoHarvest
2
2
  Command line pomodoro timer that logs to Harvest.
3
3
 
4
+ [![Code Climate](https://codeclimate.com/github/samuelreh/tomatoharvest.png)](https://codeclimate.com/github/samuelreh/tomatoharvest)
5
+
4
6
  ## Installation
5
7
 
6
8
  $ gem install tomatoharvest
data/TODO CHANGED
@@ -1,4 +1,2 @@
1
1
  * Test Tmux
2
- * Fix 0 hr bug
3
- * Refactor
4
2
  * Count down?
@@ -6,30 +6,34 @@ module TomatoHarvest
6
6
 
7
7
  desc "add", "add a task"
8
8
  def add(name)
9
+ list = ListLoader.from_file
9
10
  task = Task.new(name)
10
- List.add(task)
11
+ list.add(task)
12
+ list.save!
11
13
  say "#{task.name} added with id #{task.id}"
12
14
  end
13
15
 
14
16
  desc "list", "list all tasks"
15
17
  def list
16
- list = List.all.map do |task|
18
+ list = ListLoader.from_file
19
+ table = list.map do |task|
17
20
  [task.id, task.name]
18
21
  end
19
- list.unshift(['id', 'name'])
22
+ table.unshift(['id', 'name'])
20
23
 
21
24
  shell = Thor::Base.shell.new
22
- shell.print_table(list)
25
+ shell.print_table(table)
23
26
  end
24
27
 
25
28
  desc "start", "start a task"
26
29
  def start(id, minutes = DEFAULT_MINUTES)
27
- task = List.find(id)
30
+ list = ListLoader.from_file
31
+ task = list.find(id)
28
32
  config = Config.load.merge("name" => task.name)
29
33
  entry = TimeEntry.build_and_test(config)
30
34
 
31
35
  say "Timer started for #{task.name}"
32
- Timer.start(task.id, minutes: minutes, time_entry: entry)
36
+ Timer.start(list, task.id, minutes: minutes, time_entry: entry)
33
37
  end
34
38
 
35
39
  desc "stop", "stop current timer"
@@ -41,6 +45,12 @@ module TomatoHarvest
41
45
  end
42
46
  end
43
47
 
48
+ desc "remove", "remove a task"
49
+ def remove(id)
50
+ list = ListLoader.from_file
51
+ task = list.remove(id)
52
+ say "#{id} removed"
53
+ end
44
54
 
45
55
  end
46
56
  end
@@ -1,21 +1,38 @@
1
1
  module TomatoHarvest
2
2
  class Config
3
- CONFIG_PATH = File.expand_path("#{ENV['$HOME']}/.tomaconfig")
4
- LOCAL_CONFIG_PATH = File.join(Dir.pwd, '.tomaconfig')
3
+ DIR_NAME = '.toma'
5
4
 
6
- def self.load(options = {})
7
- if !(File.exists? CONFIG_PATH)
8
- File.open(CONFIG_PATH, 'w') do |file|
9
- YAML.dump({}, file)
5
+ HOME_DIR = ENV['HOME']
6
+ GLOBAL_DIR = File.join(HOME_DIR, DIR_NAME)
7
+ LOCAL_DIR = File.join(Dir.pwd, DIR_NAME)
8
+
9
+ def self.load
10
+ old_config = merge_config(old_config_path)
11
+
12
+ global_path = config_path(GLOBAL_DIR)
13
+ global_config = merge_config(global_path, old_config)
14
+
15
+ local_path = config_path(LOCAL_DIR)
16
+ merge_config(local_path, global_config)
17
+ end
18
+
19
+ def self.merge_config(path, base = {})
20
+ mergable =
21
+ if File.exists?(path)
22
+ YAML.load_file(path)
23
+ else
24
+ {}
10
25
  end
11
- end
12
26
 
13
- hash = YAML.load_file(CONFIG_PATH)
14
- if File.exists? LOCAL_CONFIG_PATH
15
- hash.merge!(YAML.load_file(LOCAL_CONFIG_PATH))
16
- end
27
+ base.merge(mergable)
28
+ end
29
+
30
+ def self.config_path(directory)
31
+ File.join(directory, 'config.yaml')
32
+ end
17
33
 
18
- hash
34
+ def self.old_config_path
35
+ File.join(HOME_DIR, '.tomaconfig')
19
36
  end
20
37
  end
21
38
  end
@@ -1,48 +1,47 @@
1
1
  require 'yaml'
2
+ require 'forwardable'
2
3
 
3
4
  module TomatoHarvest
4
5
  class List
5
- PATH = File.expand_path("#{ENV['$HOME']}/.toma")
6
+ extend ::Forwardable
6
7
 
7
8
  attr_reader :items
8
9
 
9
- alias :all :items
10
+ def_delegators :items, :count, :map
10
11
 
11
- def self.add(item)
12
- new.tap do |list|
13
- list.add(item)
14
- list.save
15
- end
16
- end
17
-
18
- def self.all
19
- new.all
12
+ def self.init_and_load(*args)
13
+ new(*args).load!
20
14
  end
21
15
 
22
- def self.find(id)
23
- new.find(id)
16
+ def initialize(path, items = nil)
17
+ @path = path
18
+ @items = items || []
24
19
  end
25
20
 
26
- def initialize
27
- if File.exists?(PATH)
28
- @items = load_list
29
- else
30
- @items = []
21
+ def load!
22
+ if File.exists?(@path) && items = YAML.load_file(@path)
23
+ @items = items
31
24
  end
25
+
26
+ self
32
27
  end
33
28
 
34
29
  def find(id)
35
- # TODO speed this up with an algo
36
- all.find do |item|
30
+ @items.find do |item|
37
31
  item.id == id.to_i
38
32
  end
39
33
  end
40
34
 
41
- def save
42
- yaml = YAML::dump(@items)
43
- File.open(PATH, "w+") do |f|
35
+ def save!
36
+ dir = File.dirname(@path)
37
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
38
+
39
+ yaml = YAML.dump(@items)
40
+ File.open(@path, "w+") do |f|
44
41
  f.write(yaml)
45
42
  end
43
+
44
+ self
46
45
  end
47
46
 
48
47
  def add(item)
@@ -56,21 +55,11 @@ module TomatoHarvest
56
55
  @items << item
57
56
  end
58
57
 
59
- private
60
-
61
- def load_list
62
- string = ""
63
-
64
- # better way to do this?
65
- File.open(PATH, "r") do |f|
66
- while line = f.gets
67
- string += line
68
- end
58
+ def remove(id)
59
+ @items.delete_if do |item|
60
+ item.id == id.to_i
69
61
  end
70
-
71
- YAML::load(string)
72
62
  end
73
63
 
74
64
  end
75
65
  end
76
-
@@ -0,0 +1,45 @@
1
+ module TomatoHarvest
2
+
3
+ class ListLoader
4
+
5
+ FILENAME = 'list.yaml'
6
+
7
+ class << self
8
+
9
+ def from_file
10
+ local_path = list_path(TomatoHarvest::Config::LOCAL_DIR)
11
+
12
+ if File.exists? local_path
13
+ List.init_and_load(local_path)
14
+ else
15
+ load_old_list || List.init_and_load(global_path)
16
+ end
17
+ end
18
+
19
+ def load_old_list
20
+ old_path = File.join(TomatoHarvest::Config::HOME_DIR, '.toma')
21
+
22
+ if exists_as_file(old_path)
23
+ old_list = List.init_and_load(old_path)
24
+ File.delete old_path
25
+ List.new(global_path, old_list.items).save!
26
+ end
27
+ end
28
+
29
+ def exists_as_file(path)
30
+ File.exists?(path) && !File.directory?(path)
31
+ end
32
+
33
+ def global_path
34
+ list_path(TomatoHarvest::Config::GLOBAL_DIR)
35
+ end
36
+
37
+ def list_path(dir)
38
+ File.join(dir, FILENAME)
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -33,6 +33,7 @@ module TomatoHarvest
33
33
 
34
34
  def log(seconds)
35
35
  hours = seconds_to_hours(seconds)
36
+ return if hours == 0
36
37
  options = {
37
38
  notes: notes,
38
39
  hours: hours,
@@ -2,32 +2,32 @@ require 'daemons'
2
2
 
3
3
  module TomatoHarvest
4
4
  class Timer
5
- PID_DIR = '~'
6
- PID_NAME = '.toma'
5
+ SLEEP_LENGTH = 1
6
+ PID_NAME = 'pid'
7
7
 
8
- def self.start(task_id, options = {})
9
- new(task_id, options).start
8
+ def self.start(*args)
9
+ new(*args).start
10
10
  end
11
11
 
12
12
  def self.stop
13
- if monitor = Daemons::Monitor.find(File.expand_path(DIR), APP_NAME)
13
+ if monitor = Daemons::Monitor.find(pid_dir, PID_NAME)
14
14
  monitor.stop
15
15
  true
16
16
  end
17
17
  end
18
18
 
19
- def initialize(task_id, options = {})
19
+ def initialize(list, task_id, options = {})
20
20
  @minutes = options[:minutes]
21
21
  @time_entry = options[:time_entry]
22
22
  @notifier = Notifier.new
23
- @list = List.new
23
+ @list = list
24
24
  @task = @list.find(task_id)
25
25
  @timer = 0
26
26
  @tmux = Tmux.new
27
27
  end
28
28
 
29
29
  def start
30
- if Daemons.daemonize(app_name: PID_NAME, dir: PID_DIR, dir_mode: :normal)
30
+ if Daemons.daemonize(app_name: PID_NAME, dir: self.class.pid_dir, dir_mode: :normal)
31
31
  at_exit { save_and_log }
32
32
  run_timer
33
33
  else
@@ -36,13 +36,17 @@ module TomatoHarvest
36
36
  end
37
37
  end
38
38
 
39
+ def self.pid_dir
40
+ TomatoHarvest::Config::GLOBAL_DIR
41
+ end
42
+
39
43
  private
40
44
 
41
45
  def run_timer
42
46
  @notifier.notify "Pomodoro started for #{@minutes} minutes", :subtitle => @task.name
43
47
 
44
48
  (@minutes * 60).times do |i|
45
- sleep 1
49
+ sleep SLEEP_LENGTH
46
50
  @timer += 1
47
51
  @tmux.update(@timer)
48
52
  end
@@ -50,7 +54,7 @@ module TomatoHarvest
50
54
 
51
55
  def save_and_log
52
56
  @task.log_pomodoro(@timer)
53
- @list.save
57
+ @list.save!
54
58
  @time_entry.log(@timer) if @time_entry
55
59
  @notifier.notify "Pomodoro finished", :subtitle => "Pomodoro finished!"
56
60
  @tmux.update(0)
@@ -15,7 +15,7 @@ module TomatoHarvest
15
15
  end
16
16
 
17
17
  def write_tmux_time(time)
18
- path = File.join(ENV['HOME'],'.tomatmux')
18
+ path = File.join(TomatoHarvest::Config::GLOBAL_DIR, 'tmux')
19
19
  File.open(path, 'w') do |file|
20
20
  file.write tmux_time(time)
21
21
  end
@@ -1,3 +1,3 @@
1
1
  module TomatoHarvest
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/tomatoharvest.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'tomatoharvest/version'
2
2
  require 'tomatoharvest/list'
3
+ require 'tomatoharvest/list_loader'
3
4
  require 'tomatoharvest/task'
4
5
  require 'tomatoharvest/timer'
5
6
  require 'tomatoharvest/cli'
data/spec/helper.rb CHANGED
@@ -5,47 +5,29 @@ require 'tomatoharvest'
5
5
  require 'webmock/rspec'
6
6
  require 'minitest/unit'
7
7
 
8
+ require 'support/file_helpers'
9
+ require 'support/harvest_helpers'
10
+
8
11
  WebMock.disable_net_connect!(allow_localhost: true)
9
12
 
10
13
  RSpec.configure do |c|
11
14
  c.include MiniTest::Assertions
15
+ c.include FileHelpers
16
+ c.include HarvestHelpers
12
17
 
13
18
  #
14
19
  # Speed up the timer
15
20
  #
16
- c.before :all do
17
- class TomatoHarvest::Timer
18
- def sleep(time)
19
- super(time/100000)
20
- end
21
- end
21
+ c.before :each do
22
+ const = 'TomatoHarvest::Timer::SLEEP_LENGTH'
23
+ stub_const(const, 1/100000)
22
24
  end
23
25
 
24
26
  #
25
27
  # Stub HTTP requests
26
28
  #
27
29
  c.before do
28
- body = {
29
- projects: [ {
30
- name: 'Pomdoro',
31
- id: 1,
32
- tasks: [
33
- {
34
- name: 'Ruby Development',
35
- id: 1
36
- }
37
- ]
38
- } ],
39
- day_entries: []
40
- }
41
-
42
- stub_request(:get, /https:\/\/user:password@domain.harvestapp.com\/daily\/.*/).
43
- with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>'Harvestable/2.0.0'}).
44
- to_return(:status => 200, :body => body.to_json, :headers => {})
45
-
46
- stub_request(:post, "https://user:password@domain.harvestapp.com/daily/add").
47
- with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>'Harvestable/2.0.0'}).
48
- to_return(:status => 200, :body => "", :headers => {})
30
+ stub_harvest
49
31
  end
50
32
 
51
33
  #
@@ -53,29 +35,31 @@ RSpec.configure do |c|
53
35
  # Dont notify the terminal
54
36
  #
55
37
  c.before do
56
- Daemons.stub(daemonize: false)
57
- TerminalNotifier.stub(notify: true)
58
- TomatoHarvest::Tmux.any_instance.stub(update: true)
38
+ allow(Daemons).to receive(:daemonize) { false }
39
+ allow(TerminalNotifier).to receive(:notify) { true }
40
+ allow_any_instance_of(TomatoHarvest::Tmux).to receive(:update) { true }
41
+ end
42
+
43
+ # Stub Home dir
44
+ c.before(:each) do
45
+ stub_const('TomatoHarvest::Config::HOME_DIR', 'spec/')
59
46
  end
60
47
 
61
48
  #
62
- # Cleanup .toma and .tomaconfig
49
+ # Cleanup .toma/
63
50
  #
64
51
 
65
52
  [
66
- ["TomatoHarvest::Config::CONFIG_PATH", File.expand_path('spec/.tomaconfig')],
67
- ["TomatoHarvest::Config::LOCAL_CONFIG_PATH", File.expand_path('.tomaconfig')],
68
- ["TomatoHarvest::List::PATH", File.expand_path('spec/.toma')]
69
- ].each do |tuple|
70
- path = tuple[1]
71
-
53
+ ["TomatoHarvest::Config::GLOBAL_DIR", File.expand_path('spec/.toma/')],
54
+ ["TomatoHarvest::Config::LOCAL_DIR", File.expand_path('.toma/')]
55
+ ].each do |const, path|
72
56
  c.before :each do
73
- stub_const(tuple[0], path)
74
- File.delete(path) if File.exists?(path)
57
+ stub_const(const, path)
58
+ FileUtils.rm_rf(path) if File.directory?(path)
75
59
  end
76
60
 
77
61
  c.after :each do
78
- File.delete(path) if File.exists?(path)
62
+ FileUtils.rm_rf(path) if File.directory?(path)
79
63
  end
80
64
  end
81
65
  end
@@ -22,6 +22,18 @@ describe TomatoHarvest::CLI do
22
22
 
23
23
  end
24
24
 
25
+ describe 'remove' do
26
+ before do
27
+ TomatoHarvest::CLI.start ['add', 'foo']
28
+ end
29
+
30
+ it 'removes task from the list' do
31
+ out = capture_io { TomatoHarvest::CLI.start ['remove', 1] }.join ''
32
+ expect(out).to match(/1 removed/)
33
+ end
34
+
35
+ end
36
+
25
37
  describe 'start' do
26
38
  before do
27
39
  TomatoHarvest::CLI.start ['add', 'foo']
@@ -48,10 +60,8 @@ describe TomatoHarvest::CLI do
48
60
  password: 'password'
49
61
  }
50
62
 
51
- path = TomatoHarvest::Config::CONFIG_PATH
52
- File.open(path, 'w') do |file|
53
- YAML::dump(options, file)
54
- end
63
+ path = TomatoHarvest::Config.config_path(TomatoHarvest::Config::GLOBAL_DIR)
64
+ create_yaml_file(path, options)
55
65
  end
56
66
 
57
67
  it 'starts the timer with specified length' do
@@ -63,4 +73,13 @@ describe TomatoHarvest::CLI do
63
73
 
64
74
  end
65
75
 
76
+ describe 'stop' do
77
+
78
+ it 'warns when timer isnt running' do
79
+ out = capture_io { TomatoHarvest::CLI.start ['stop'] }.join ''
80
+ expect(out).to match(/Timer not running/)
81
+ end
82
+
83
+ end
84
+
66
85
  end
@@ -1,8 +1,10 @@
1
+ require 'fileutils'
1
2
  require 'helper'
2
3
 
3
4
  describe TomatoHarvest::Config do
4
5
 
5
6
  describe '.load' do
7
+
6
8
  let(:global_options) do
7
9
  {
8
10
  project: 'Project',
@@ -11,9 +13,8 @@ describe TomatoHarvest::Config do
11
13
  end
12
14
 
13
15
  before do
14
- File.open(TomatoHarvest::Config::CONFIG_PATH, 'w') do |file|
15
- YAML::dump(global_options, file)
16
- end
16
+ path = TomatoHarvest::Config.config_path(TomatoHarvest::Config::GLOBAL_DIR)
17
+ create_yaml_file(path, global_options)
17
18
  end
18
19
 
19
20
  it 'loads from the yaml config file' do
@@ -26,15 +27,34 @@ describe TomatoHarvest::Config do
26
27
  options = {
27
28
  type: 'JS Development',
28
29
  }
29
- local_config = File.join(Dir.pwd, '.tomaconfig')
30
30
 
31
- File.open(local_config, 'w') do |file|
32
- YAML::dump(options, file)
33
- end
31
+ path = TomatoHarvest::Config.config_path(TomatoHarvest::Config::LOCAL_DIR)
32
+ create_yaml_file(path, options)
33
+
34
+ expected = global_options.merge(options)
35
+
36
+ expect(TomatoHarvest::Config.load).to eql(expected)
37
+ end
38
+
39
+ end
40
+
41
+ context 'when there is an old config file' do
42
+
43
+ let(:old_config) do
44
+ {
45
+ domain: 'fake.domain.name'
46
+ }
47
+ end
48
+
49
+ it 'loads it' do
50
+ old_config_path = File.join(TomatoHarvest::Config::HOME_DIR, '.tomaconfig')
51
+ expanded_path = File.expand_path(old_config_path)
52
+ create_yaml_file(expanded_path, old_config)
34
53
 
35
- result = global_options.merge(options)
54
+ expected = old_config.merge(global_options)
55
+ expect(TomatoHarvest::Config.load).to eql(expected)
36
56
 
37
- expect(TomatoHarvest::Config.load).to eql(result)
57
+ File.delete(old_config_path)
38
58
  end
39
59
 
40
60
  end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+
3
+ describe TomatoHarvest::ListLoader do
4
+
5
+ describe '.from_file' do
6
+
7
+ let(:filename) { TomatoHarvest::ListLoader::FILENAME }
8
+
9
+ it 'returns a list' do
10
+ list = described_class.from_file
11
+ expect(list).to be_an_instance_of(TomatoHarvest::List)
12
+ end
13
+
14
+ context 'when there is an old list' do
15
+
16
+ let(:items) { ['item'] }
17
+ let(:path) { File.join(TomatoHarvest::Config::HOME_DIR, '.toma') }
18
+
19
+ before do
20
+ create_yaml_file(path, items)
21
+ end
22
+
23
+ it 'moves it to the global location' do
24
+ list = described_class.from_file
25
+ expect(list.count).to eql(1)
26
+ end
27
+
28
+ it 'persists the list' do
29
+ described_class.from_file
30
+ list = described_class.from_file
31
+ expect(list.count).to eql(1)
32
+ end
33
+
34
+ end
35
+
36
+ context 'when there is a global list' do
37
+
38
+ let(:items) { ['item'] }
39
+
40
+ before do
41
+ path = File.join(TomatoHarvest::Config::GLOBAL_DIR, filename)
42
+ create_yaml_file(path, items)
43
+ end
44
+
45
+ it 'returns the global list' do
46
+ list = described_class.from_file
47
+ expect(list.items).to eql(items)
48
+ end
49
+
50
+ context 'when there is a local list' do
51
+ let(:local_items) { ['local_item'] }
52
+
53
+ before do
54
+ path = File.join(TomatoHarvest::Config::LOCAL_DIR, filename)
55
+ create_yaml_file(path, local_items)
56
+ end
57
+
58
+ it 'returns the local list' do
59
+ list = described_class.from_file
60
+ expect(list.items).to eql(local_items)
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -2,26 +2,35 @@ require 'helper'
2
2
 
3
3
  describe TomatoHarvest::List do
4
4
 
5
+ let(:path) { TomatoHarvest::ListLoader.global_path }
6
+ let(:list) { TomatoHarvest::List.new(path) }
7
+
5
8
  def add_task(name)
6
9
  task = TomatoHarvest::Task.new(name)
7
- TomatoHarvest::List.add(task)
10
+ list.add(task)
8
11
  end
9
12
 
10
- describe '.add' do
13
+ describe 'load!' do
11
14
 
12
- it 'adds to the list' do
13
- add_task('foo')
14
- expect(described_class.all.first).to be_an_instance_of(TomatoHarvest::Task)
15
+ it 'doesnt load if file is empty' do
16
+ create_file(path, "")
17
+ list.load!
18
+ expect(list.items).to eql([])
15
19
  end
16
20
 
17
21
  end
18
22
 
19
- describe '.list' do
23
+ describe '.add' do
24
+
25
+ it 'adds to the list' do
26
+ add_task('foo')
27
+ expect(list.items.first).to be_an_instance_of(TomatoHarvest::Task)
28
+ end
20
29
 
21
30
  it 'should have two items' do
22
31
  add_task('foo')
23
32
  add_task('bar')
24
- expect(described_class.all.count).to eql(2)
33
+ expect(list.count).to eql(2)
25
34
  end
26
35
 
27
36
  end
@@ -31,8 +40,8 @@ describe TomatoHarvest::List do
31
40
  it 'returns the task with the corresponding id' do
32
41
  add_task('foo')
33
42
  add_task('bar')
34
- expect(described_class.find(1).name).to eql('foo')
35
- expect(described_class.find(2).name).to eql('bar')
43
+ expect(list.find(1).name).to eql('foo')
44
+ expect(list.find(2).name).to eql('bar')
36
45
  end
37
46
 
38
47
  end
@@ -41,11 +50,20 @@ describe TomatoHarvest::List do
41
50
 
42
51
  it 'adds the task to the items array' do
43
52
  task = TomatoHarvest::Task.new('foo')
44
- list = described_class.new
45
53
  list.add(task)
46
54
  expect(list.items.first.id).to eql(1)
47
55
  end
48
56
 
49
57
  end
50
58
 
59
+ describe '.remove' do
60
+
61
+ it 'removes the task from the item array' do
62
+ add_task('foo')
63
+ list.remove(1)
64
+ expect(list.count).to eql(0)
65
+ end
66
+
67
+ end
68
+
51
69
  end
@@ -6,59 +6,64 @@ describe TomatoHarvest::TimeEntry do
6
6
 
7
7
  let(:entry) do
8
8
  described_class.new.tap do |entry|
9
- entry.stub(project: double, task: double)
9
+ allow(entry).to receive(:project) { double }
10
+ allow(entry).to receive(:task) { double }
10
11
  end
11
12
  end
12
13
 
13
14
  it "raises an error if project can't be found" do
14
- entry.stub(project: nil)
15
+ allow(entry).to receive(:project) { nil }
15
16
  expect{ entry.test }.to raise_error("Couldn't find project")
16
17
  end
17
18
 
18
19
  it "raises an error if task can't be found" do
19
- entry.stub(task: nil)
20
+ allow(entry).to receive(:task) { nil }
20
21
  expect{ entry.test }.to raise_error("Couldn't find task type")
21
22
  end
22
23
 
23
24
  end
24
25
 
25
26
  describe '#log' do
27
+ let(:options) do
28
+ {
29
+ 'domain' => 'domain',
30
+ 'username' => 'user',
31
+ 'password' => 'password',
32
+ 'project' => 'Pomodoro',
33
+ 'task' => 'Ruby Development',
34
+ 'name' => 'Template Refactoring'
35
+ }
36
+ end
26
37
 
27
- context 'task is already logged today' do
28
- let(:options) do
29
- {
30
- 'domain' => 'domain',
31
- 'username' => 'user',
32
- 'password' => 'password',
33
- 'project' => 'Pomodoro',
34
- 'task' => 'Ruby Development',
35
- 'name' => 'Template Refactoring'
36
- }
37
- end
38
-
39
- before do
40
- body = {
41
- projects: [ {
42
- name: 'Pomodoro',
43
- id: 1,
44
- tasks: [
45
- {
46
- name: 'Ruby Development',
47
- id: 1
48
- }
49
- ]
50
- } ],
51
-
52
- day_entries: [ {
53
- notes: 'Template Refactoring',
54
- project_id: 1,
55
- task_id: 1,
56
- hours: 1
57
- } ]
58
- }
38
+ let(:entries) { [] }
39
+
40
+ before do
41
+ body = {
42
+ projects: [ {
43
+ name: 'Pomodoro',
44
+ id: 1,
45
+ tasks: [
46
+ {
47
+ name: 'Ruby Development',
48
+ id: 1
49
+ }
50
+ ]
51
+ } ],
52
+ day_entries: entries
53
+ }
54
+
55
+ stub_request(:get, /https:\/\/user:password@domain.harvestapp.com\/daily\/.*/).
56
+ to_return(:status => 200, :body => body.to_json, :headers => {})
57
+ end
59
58
 
60
- stub_request(:get, /https:\/\/user:password@domain.harvestapp.com\/daily\/.*/).
61
- to_return(:status => 200, :body => body.to_json, :headers => {})
59
+ context 'task is already logged today' do
60
+ let(:entries) do
61
+ [ {
62
+ notes: 'Template Refactoring',
63
+ project_id: 1,
64
+ task_id: 1,
65
+ hours: 1
66
+ } ]
62
67
  end
63
68
 
64
69
  it 'updates exisiting entry' do
@@ -75,7 +80,21 @@ describe TomatoHarvest::TimeEntry do
75
80
  entry = TomatoHarvest::TimeEntry.new(options)
76
81
  entry.log(60 * 30)
77
82
 
78
- stub.should have_been_requested
83
+ expect(stub).to have_been_requested
84
+ end
85
+
86
+ end
87
+
88
+ context 'seconds are rounded to 0.00 hours' do
89
+
90
+ it 'should not log' do
91
+ update_url = "https://user:password@domain.harvestapp.com/daily/add"
92
+ stub = stub_request(:post, update_url)
93
+
94
+ entry = TomatoHarvest::TimeEntry.new(options)
95
+ entry.log(0)
96
+
97
+ expect(stub).not_to have_been_requested
79
98
  end
80
99
 
81
100
  end
@@ -6,10 +6,14 @@ describe TomatoHarvest::Timer do
6
6
 
7
7
  let(:task) { TomatoHarvest::Task.new('foo') }
8
8
 
9
+ let(:list) do
10
+ path = TomatoHarvest::ListLoader.global_path
11
+ TomatoHarvest::List.new(path)
12
+ end
13
+
9
14
  before do
10
- list = TomatoHarvest::List.new
11
15
  list.add(task)
12
- list.save
16
+ list.save!
13
17
  end
14
18
 
15
19
  def stub_notifier(minutes)
@@ -23,23 +27,23 @@ describe TomatoHarvest::Timer do
23
27
  end
24
28
 
25
29
  it 'can run for a custom length' do
26
- TomatoHarvest::Timer.start(task.id, minutes: 15)
30
+ TomatoHarvest::Timer.start(list, task.id, minutes: 15)
27
31
 
28
- reloaded_task = TomatoHarvest::List.find(task.id)
32
+ reloaded_task = list.find(task.id)
29
33
  expect(reloaded_task.logged_minutes).to eql(15.0)
30
34
  end
31
35
 
32
36
  it 'can be run twice' do
33
- TomatoHarvest::Timer.start(task.id, minutes: 20)
34
- TomatoHarvest::Timer.start(task.id, minutes: 20)
35
- reloaded_task = TomatoHarvest::List.find(task.id)
37
+ TomatoHarvest::Timer.start(list, task.id, minutes: 20)
38
+ TomatoHarvest::Timer.start(list, task.id, minutes: 20)
39
+ reloaded_task = list.find(task.id)
36
40
  expect(reloaded_task.logged_minutes).to eql(40.0)
37
41
  end
38
42
 
39
43
  it 'logs a time entry if passed in' do
40
44
  entry = double
41
- entry.should_receive(:log)
42
- TomatoHarvest::Timer.start(task.id, time_entry: entry, minutes: 25)
45
+ expect(entry).to receive(:log)
46
+ TomatoHarvest::Timer.start(list, task.id, time_entry: entry, minutes: 25)
43
47
  end
44
48
 
45
49
  end
@@ -0,0 +1,18 @@
1
+ module FileHelpers
2
+
3
+ # Creates a YAML config file at the specified path.
4
+ def create_yaml_file(path, options)
5
+ create_file(path, YAML::dump(options))
6
+ end
7
+
8
+ def create_file(path, contents)
9
+ dir = File.dirname(path)
10
+
11
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
12
+
13
+ File.open(path, 'w') do |file|
14
+ file.write(contents)
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,25 @@
1
+ module HarvestHelpers
2
+ def stub_harvest
3
+ body = {
4
+ projects: [ {
5
+ name: 'Pomdoro',
6
+ id: 1,
7
+ tasks: [
8
+ {
9
+ name: 'Ruby Development',
10
+ id: 1
11
+ }
12
+ ]
13
+ } ],
14
+ day_entries: []
15
+ }
16
+
17
+ stub_request(:get, /https:\/\/user:password@domain.harvestapp.com\/daily\/.*/).
18
+ with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>'Harvestable/2.0.0'}).
19
+ to_return(:status => 200, :body => body.to_json, :headers => {})
20
+
21
+ stub_request(:post, "https://user:password@domain.harvestapp.com/daily/add").
22
+ with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json; charset=utf-8', 'User-Agent'=>'Harvestable/2.0.0'}).
23
+ to_return(:status => 200, :body => "", :headers => {})
24
+ end
25
+ end
@@ -19,12 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec"
24
- spec.add_development_dependency "webmock"
22
+ spec.add_development_dependency "rake", "~> 10.3"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ spec.add_development_dependency "webmock", "~> 1.18"
25
25
 
26
- spec.add_dependency('thor', '~> 0.19')
27
- spec.add_dependency('harvested')
28
- spec.add_dependency('daemons')
29
- spec.add_dependency('terminal-notifier', '~> 1.4') if TomatoHarvest::OS.mac?
26
+ spec.add_dependency 'thor', '~> 0.19'
27
+ spec.add_dependency 'harvested', '~> 2.0'
28
+ spec.add_dependency 'daemons', '~> 1.1'
29
+ spec.add_dependency 'terminal-notifier', '~> 1.4' if TomatoHarvest::OS.mac?
30
30
  end
metadata CHANGED
@@ -1,125 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tomatoharvest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Reh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-10 00:00:00.000000000 Z
11
+ date: 2014-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '10.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '10.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: webmock
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '1.18'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '1.18'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: thor
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0.19'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.19'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: harvested
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '2.0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '2.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: daemons
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '1.1'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '1.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: terminal-notifier
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ~>
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '1.4'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ~>
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '1.4'
125
125
  description: Command line pomodoro timer that logs to Harvest.
@@ -130,7 +130,7 @@ executables:
130
130
  extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
- - .gitignore
133
+ - ".gitignore"
134
134
  - Gemfile
135
135
  - LICENSE.txt
136
136
  - README.md
@@ -141,6 +141,7 @@ files:
141
141
  - lib/tomatoharvest/cli.rb
142
142
  - lib/tomatoharvest/config.rb
143
143
  - lib/tomatoharvest/list.rb
144
+ - lib/tomatoharvest/list_loader.rb
144
145
  - lib/tomatoharvest/notifier.rb
145
146
  - lib/tomatoharvest/notifier/notification_center.rb
146
147
  - lib/tomatoharvest/os.rb
@@ -153,9 +154,12 @@ files:
153
154
  - spec/helper.rb
154
155
  - spec/lib/tomatoharvest/cli_spec.rb
155
156
  - spec/lib/tomatoharvest/config_spec.rb
157
+ - spec/lib/tomatoharvest/list_loader_spec.rb
156
158
  - spec/lib/tomatoharvest/list_spec.rb
157
159
  - spec/lib/tomatoharvest/time_entry_spec.rb
158
160
  - spec/lib/tomatoharvest/timer_spec.rb
161
+ - spec/support/file_helpers.rb
162
+ - spec/support/harvest_helpers.rb
159
163
  - tomatoharvest.gemspec
160
164
  homepage: http://github.com/samuelreh/tomatoharvest/
161
165
  licenses:
@@ -167,17 +171,17 @@ require_paths:
167
171
  - lib
168
172
  required_ruby_version: !ruby/object:Gem::Requirement
169
173
  requirements:
170
- - - '>='
174
+ - - ">="
171
175
  - !ruby/object:Gem::Version
172
176
  version: '0'
173
177
  required_rubygems_version: !ruby/object:Gem::Requirement
174
178
  requirements:
175
- - - '>='
179
+ - - ">="
176
180
  - !ruby/object:Gem::Version
177
181
  version: '0'
178
182
  requirements: []
179
183
  rubyforge_project:
180
- rubygems_version: 2.0.3
184
+ rubygems_version: 2.2.2
181
185
  signing_key:
182
186
  specification_version: 4
183
187
  summary: Log your pomodoros to Harvest
@@ -185,7 +189,10 @@ test_files:
185
189
  - spec/helper.rb
186
190
  - spec/lib/tomatoharvest/cli_spec.rb
187
191
  - spec/lib/tomatoharvest/config_spec.rb
192
+ - spec/lib/tomatoharvest/list_loader_spec.rb
188
193
  - spec/lib/tomatoharvest/list_spec.rb
189
194
  - spec/lib/tomatoharvest/time_entry_spec.rb
190
195
  - spec/lib/tomatoharvest/timer_spec.rb
196
+ - spec/support/file_helpers.rb
197
+ - spec/support/harvest_helpers.rb
191
198
  has_rdoc: