tomatoharvest 0.0.1 → 0.1.0

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