timetrap 1.15.1 → 1.15.4

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
- SHA1:
3
- metadata.gz: 14a3e67d1e91f4f7184416c8efd811211911fb40
4
- data.tar.gz: 027cbe432274e39ac145ab725bb8a9bf1d719a5a
2
+ SHA256:
3
+ metadata.gz: 5c87342eeaac646bc3e628a35549cb078a8aa5ad2e469dc14afe4bca7051bfe2
4
+ data.tar.gz: 9041a05cc7f251e4bcbff0dcf7335bdc613c1ab0a49103bd1068cca432823d22
5
5
  SHA512:
6
- metadata.gz: 5cb63a6a5f3c7f93f3aba05d2799bb4e311990009945f71369f80344db16219f8c917fd02719ae991d1ac6ce1baea47479aed3547d6709ed2122edb1fd407975
7
- data.tar.gz: 1318b4902b8c0d84e57be18830a9b51525b3a76c64fdfe1e46243d29818adeb61e549d7bb848706f02eac1009cc7b0e5c17b039a8483fc305940ceeb9fe52799
6
+ metadata.gz: 9b5e45261a137be9e44e07f127e60a21849517208df71dfabbe1a14483e4819aceec31892f02b4c2f0bf245674084a895e6398cc1c6e3c55910479e7763b26d7
7
+ data.tar.gz: c92729865ec97ff09633a2d5a195584eee9b94ae9168cde285b8df83c832a23d75b19741de86fda8f9aeb3fd225f2a7a53f84d225212d27adb5931559c8501f3
@@ -0,0 +1,29 @@
1
+ <!--- Provide a general summary of your changes in the Title above -->
2
+
3
+ ## Description
4
+ <!--- Describe your changes in detail -->
5
+
6
+ ## Motivation and Context
7
+ <!--- Why is this change required? What problem does it solve? -->
8
+ <!--- If it fixes an open issue, please link to the issue here. -->
9
+
10
+ ## How Has This Been Tested?
11
+ <!--- Please describe in detail how you tested your changes. -->
12
+ <!--- Include details of your testing environment, and the tests you ran to -->
13
+ <!--- see how your change affects other areas of the code, etc. -->
14
+
15
+ ## Screenshots (if appropriate):
16
+
17
+ ## Types of changes
18
+ <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
19
+ - [ ] Bug fix (non-breaking change which fixes an issue)
20
+ - [ ] New feature (non-breaking change which adds functionality)
21
+ - [ ] Breaking change (fix or feature that would cause existing functionality to change)
22
+
23
+ ## Checklist:
24
+ <!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
25
+ <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
26
+ - [ ] My code follows the code style of this project.
27
+ - [ ] I have updated the documentation accordingly.
28
+ - [ ] I have added tests to cover my changes.
29
+ - [ ] All new and existing tests passed.
@@ -0,0 +1,16 @@
1
+ name: rspec
2
+ on:
3
+ - push
4
+ - pull_request
5
+
6
+ jobs:
7
+ test:
8
+ name: Run Rspec
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: '2.7'
15
+ - run: bundle install
16
+ - run: rspec spec
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ pkg
3
3
  *.swp
4
4
  Gemfile.lock
5
5
  .bundle
6
+ vendor/bundle
data/.rspec CHANGED
@@ -1,2 +1 @@
1
- --color
2
- --profile
1
+ --require spec_helper
data/.travis.yml CHANGED
@@ -1,6 +1,9 @@
1
+ before_install:
2
+ - gem uninstall -v '< 2' -i $(rvm gemdir)@global -ax bundler || true
3
+ - gem install bundler -v '~> 2'
1
4
  language: ruby
2
5
  rvm:
3
- - 2.1.8
4
- - 2.2.4
5
- - 2.3.3
6
6
  - 2.4.0
7
+ - 2.5.0
8
+ - 2.6.0
9
+ - 2.7.0
data/CONTRIBUTORS CHANGED
@@ -16,3 +16,4 @@
16
16
  * Patrick Davey
17
17
  * Kyle Brett
18
18
  * Nam D. Nguyen (namdnguyen)
19
+ * Bèr Kessels (berkes)
data/README.md CHANGED
@@ -13,6 +13,15 @@ To install:
13
13
 
14
14
  This will place a ``t`` executable in your path.
15
15
 
16
+ ***UPDATE***: The current gem release seems to be having issues installing and the gem ownership on RubyGems.org is currently in the process of being transferred. Meanwhile, here is how to build the latest master from source:
17
+
18
+ $ git clone https://github.com/samg/timetrap
19
+ $ cd timetrap
20
+ $ gem build timetrap
21
+ $ gem install timetrap_X.X.X.gem
22
+
23
+ If you have errors while parsing the documentation, use `--no-document` option when installing the gem, or other option is to `gem install rdoc` before installing the `timetrap`. This is a known issue from [rdoc](https://github.com/ruby/rdoc/commit/5f9603f35d8e520c761015810c005e4a5beb97c3)
24
+
16
25
  ### Basic Usage
17
26
 
18
27
  $ # get help
@@ -64,7 +73,7 @@ If you make a mistake use the `edit` command.
64
73
  You check out with the `out` command.
65
74
 
66
75
  $ t out
67
- Checked out of sheet "coding"
76
+ Checked out of entry "document timetrap" in sheet "coding"
68
77
 
69
78
  Running `edit` when you're checked out will edit the last entry you checked out
70
79
  of.
@@ -113,7 +122,7 @@ Command line flags also have short versions.
113
122
  $ # equivalent to the command above
114
123
  $ t i -a "5 minutes ago"
115
124
 
116
- You can consult the Chronic gem (http://chronic.rubyforge.org/) for a full
125
+ You can consult the Chronic gem (https://github.com/mojombo/chronic) for a full
117
126
  list of parsable time formats, but all of these should work.
118
127
 
119
128
  $ t out --at "in 30 minutes"
@@ -308,7 +317,7 @@ Commands
308
317
  --------
309
318
 
310
319
  **archive**
311
- Archives the selected entries (by moving them to a sheet called ``_[SHEET]``)
320
+ Archive the selected entries (by moving them to a sheet called ``_[SHEET]``)
312
321
  These entries can be seen by running ``t display _[SHEET]``.
313
322
 
314
323
  usage: ``t archive [--start DATE] [--end DATE] [--grep REGEX] [SHEET]``
@@ -320,8 +329,8 @@ Commands
320
329
  usage: ``t backend``
321
330
 
322
331
  **configure**
323
- Creates a config file at ``~/.timetrap.yml`` or ``ENV['TIMETRAP_CONFIG_FILE']`` if
324
- one doesn't exist. If one does exist it will update it with new
332
+ Create a config file at ``~/.timetrap.yml`` or ``ENV['TIMETRAP_CONFIG_FILE']`` if
333
+ one doesn't exist. If one does exist, update it with new
325
334
  configuration options preserving any user overrides. Prints path to config
326
335
  file. This file may contain ERB.
327
336
 
@@ -344,7 +353,7 @@ Commands
344
353
  usage: ``t display [--ids] [--round] [--start DATE] [--end DATE] [--format FMT] [--grep REGEX] [SHEET | all | full]``
345
354
 
346
355
  **edit**
347
- Inserts a note associated with the an entry in the timesheet, or edits the
356
+ Insert a note associated with the an entry in the timesheet, or edit the
348
357
  start or end times. Defaults to the current entry, or previously running
349
358
  entry. An ``--id`` flag can be passed with the entry's id (see display.)
350
359
 
@@ -512,6 +521,7 @@ fpath=(/path/to/timetrap-1.x.y/gem/completions/zsh $fpath)
512
521
  ```
513
522
 
514
523
  #### Notes editing
524
+
515
525
  If you use the note_editor setting, then it is possible to use
516
526
  an editor for writing your notes. If you use a non terminal based
517
527
  editor (like atom, sublime etc.) then you will need to make timetrap
@@ -522,6 +532,29 @@ As of when this command was added, for atom you would use `atom --wait`
522
532
  and for sublime `subl -w`. If you use a console based editor (vim, emacs,
523
533
  nano) then it should just work.
524
534
 
535
+ Development
536
+ -----------
537
+
538
+ Get `bundler` in case you don't have it:
539
+
540
+ gem install bundler
541
+
542
+ Set a local path for the project's dependencies:
543
+
544
+ bundle config set --local path 'vendor/bundle'
545
+
546
+ Install timetrap's dependencies:
547
+
548
+ bundle install
549
+
550
+ Now you can run your local timetrap installation:
551
+
552
+ bundle exec t
553
+
554
+ Or run the test suite:
555
+
556
+ bundle exec rspec
557
+
525
558
  Special Thanks
526
559
  --------------
527
560
 
@@ -3,8 +3,9 @@ _timetrap ()
3
3
  {
4
4
  cur="${COMP_WORDS[COMP_CWORD]}"
5
5
  cmd="${COMP_WORDS[1]}"
6
+
6
7
  if [[ ( $cmd = s* || $cmd = d* ) && "$COMP_CWORD" = 2 ]]; then
7
- COMPREPLY=($(compgen -W "$(echo "select distinct sheet from entries where sheet not like '\_%';" | sqlite3 ~/.timetrap.db)" $cur))
8
+ COMPREPLY=($(compgen -W "$(echo "select distinct sheet from entries where sheet not like '\_%';" | t b)" $cur))
8
9
  return
9
10
  elif [[ "$COMP_CWORD" = 1 ]]; then
10
11
  CMDS="archive backend configure display edit in kill list now out resume sheet week month"
data/lib/timetrap/cli.rb CHANGED
@@ -71,6 +71,7 @@ COMMAND is one of:
71
71
  -z, --append Append to the current note instead of replacing it
72
72
  the delimiter between appended notes is
73
73
  configurable (see configure)
74
+ -c, --clear Allow an empty note, can be used to clear existing notes
74
75
  -m, --move <sheet> Move to another sheet
75
76
 
76
77
  * in - Start the timer for the current timesheet.
@@ -247,14 +248,18 @@ COMMAND is one of:
247
248
  end
248
249
 
249
250
  if Config['note_editor']
250
- if args['-z']
251
+ if args['-c']
252
+ entry.update :note => ''
253
+ elsif args['-z']
251
254
  note = [entry.note, get_note_from_external_editor].join(Config['append_notes_delimiter'])
252
255
  entry.update :note => note
253
256
  elsif editing_a_note?
254
257
  entry.update :note => get_note_from_external_editor(entry.note)
255
258
  end
256
259
  else
257
- if unused_args =~ /.+/
260
+ if args['-c']
261
+ entry.update :note => ''
262
+ elsif unused_args =~ /.+/
258
263
  note = unused_args
259
264
  if args['-z']
260
265
  note = [entry.note, note].join(Config['append_notes_delimiter'])
@@ -324,7 +329,9 @@ COMMAND is one of:
324
329
  def out
325
330
  if Config['auto_checkout']
326
331
  stopped = Timer.stop_all(args['-a']).each do |checked_out_of|
327
- warn "Checked out of sheet #{checked_out_of.sheet.inspect}."
332
+ note = Timer.last_checkout.note
333
+ entry = note_blank?(note) ? Timer.last_checkout.id : note.inspect
334
+ warn "Checked out of entry #{entry} in sheet #{checked_out_of.sheet.inspect}."
328
335
  end
329
336
  if stopped.empty?
330
337
  warn "No running entries to stop."
@@ -332,7 +339,9 @@ COMMAND is one of:
332
339
  else
333
340
  sheet = sheet_name_from_string(unused_args)
334
341
  if Timer.stop sheet, args['-a']
335
- warn "Checked out of sheet #{sheet.inspect}."
342
+ note = Timer.last_checkout.note
343
+ entry = note_blank?(note) ? Timer.last_checkout.id : note.inspect
344
+ warn "Checked out of entry #{entry} in sheet #{sheet.inspect}."
336
345
  else
337
346
  warn "No running entry on sheet #{sheet.inspect}."
338
347
  end
@@ -388,7 +397,13 @@ COMMAND is one of:
388
397
  end
389
398
 
390
399
  Timer.current_sheet = sheet
391
- warn "Switching to sheet #{sheet.inspect}"
400
+ if Timer.last_sheet == sheet
401
+ warn "Already on sheet #{sheet.inspect}"
402
+ elsif Entry.sheets.include?(sheet)
403
+ warn "Switching to sheet #{sheet.inspect}"
404
+ else
405
+ warn "Switching to sheet #{sheet.inspect} (new sheet)"
406
+ end
392
407
  end
393
408
 
394
409
  def list
@@ -473,6 +488,10 @@ COMMAND is one of:
473
488
 
474
489
  private
475
490
 
491
+ def note_blank?(note)
492
+ note.inspect.to_s.gsub('"', '').strip.size.zero?
493
+ end
494
+
476
495
  def unused_args
477
496
  args.unused.join(' ')
478
497
  end
@@ -6,7 +6,7 @@ module Timetrap
6
6
  def initialize entries
7
7
  @output = entries.inject("start,end,note,sheet\n") do |out, e|
8
8
  next(out) unless e.end
9
- out << %|"#{e.start.strftime(time_format)}","#{e.end.strftime(time_format)}","#{e.note}","#{e.sheet}"\n|
9
+ out << %|"#{e.start.strftime(time_format)}","#{e.end.strftime(time_format)}","#{escape(e.note)}","#{e.sheet}"\n|
10
10
  end
11
11
  end
12
12
 
@@ -14,6 +14,10 @@ module Timetrap
14
14
  def time_format
15
15
  "%Y-%m-%d %H:%M:%S"
16
16
  end
17
+
18
+ def escape(note)
19
+ note.gsub %q{"}, %q{""}
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -4,7 +4,7 @@ rescue LoadError
4
4
  raise <<-ERR
5
5
  The icalendar gem must be installed for ical output.
6
6
  To install it:
7
- $ [sudo] gem install icalendar -v"~>1.1.5"
7
+ $ [sudo] gem install icalendar
8
8
  ERR
9
9
  end
10
10
 
@@ -22,21 +22,13 @@ module Timetrap
22
22
  end
23
23
 
24
24
  def initialize entries
25
- entries.each do |e|
26
- next unless e.end
27
- calendar.event do
28
-
29
- # hack around an issue in ical gem in ruby 1.9
30
- unless respond_to? :<=>
31
- def <=> other
32
- dtstart > other.dtstart ? 1 : 0
33
- end
34
- end
35
-
36
- dtstart DateTime.parse(e.start.to_s)
37
- dtend DateTime.parse(e.end.to_s)
38
- summary e.note
39
- description e.note
25
+ entries.each do |entry|
26
+ next unless entry.end
27
+ calendar.event do |e|
28
+ e.dtstart = DateTime.parse(entry.start.to_s)
29
+ e.dtend = DateTime.parse(entry.end.to_s)
30
+ e.summary = entry.note
31
+ e.description = entry.note
40
32
  end
41
33
  end
42
34
  calendar.publish
@@ -16,7 +16,12 @@ module Timetrap
16
16
  def initialize entries
17
17
  @output = entries.map do |e|
18
18
  next unless e.end
19
- e.values
19
+
20
+ e.values.inject({}) do |h, (k,v)|
21
+ h[k] = v
22
+ h[k] = e.public_send(k) if %i[end start].include?(k)
23
+ h
24
+ end
20
25
  end.compact.to_json
21
26
  end
22
27
  end
@@ -40,16 +40,16 @@ module Timetrap
40
40
 
41
41
  def selected_entries
42
42
  ee = if (sheet = sheet_name_from_string(unused_args)) == 'all'
43
- Timetrap::Entry.filter('sheet not like ? escape "!"', '!_%')
43
+ Timetrap::Entry.where(Sequel.lit('sheet not like ? escape "!"', '!_%'))
44
44
  elsif (sheet = sheet_name_from_string(unused_args)) == 'full'
45
- Timetrap::Entry.filter()
45
+ Timetrap::Entry.where()
46
46
  elsif sheet =~ /.+/
47
- Timetrap::Entry.filter('sheet = ?', sheet)
47
+ Timetrap::Entry.where(sheet: sheet)
48
48
  else
49
- Timetrap::Entry.filter('sheet = ?', Timer.current_sheet)
49
+ Timetrap::Entry.where(sheet: Timer.current_sheet)
50
50
  end
51
- ee = ee.filter('start >= ?', Date.parse(Timer.process_time(args['-s']).to_s)) if args['-s']
52
- ee = ee.filter('start <= ?', Date.parse(Timer.process_time(args['-e']).to_s) + 1) if args['-e']
51
+ ee = ee.filter(Sequel.lit('start >= ?', Date.parse(Timer.process_time(args['-s']).to_s))) if args['-s']
52
+ ee = ee.filter(Sequel.lit('start <= ?', Date.parse(Timer.process_time(args['-e']).to_s) + 1)) if args['-e']
53
53
  ee = ee.order(:start)
54
54
  if args['-g']
55
55
  re = Regexp::new(args['-g'])
@@ -1,7 +1,5 @@
1
1
  module Timetrap
2
2
  class Entry < Sequel::Model
3
- plugin :schema
4
-
5
3
  class << self
6
4
  # a class level instance variable that controls whether or not all entries
7
5
  # should respond to #start and #end with times rounded to 15 minute
@@ -68,27 +66,10 @@ module Timetrap
68
66
  end
69
67
 
70
68
  private
71
- # do a quick pseudo migration. This should only get executed on the first run
72
- set_schema do
73
- primary_key :id
74
- column :note, String
75
- column :start, DateTime
76
- column :end, DateTime
77
- column :sheet, String
78
- end
79
- create_table unless table_exists?
69
+
80
70
  end
81
71
 
82
72
  class Meta < Sequel::Model(:meta)
83
- plugin :schema
84
-
85
- set_schema do
86
- primary_key :id
87
- column :key, String
88
- column :value, String
89
- end
90
- create_table unless table_exists?
91
-
92
73
  def value
93
74
  self[:value].to_s
94
75
  end
@@ -0,0 +1,47 @@
1
+ # Defines the table schemas. Acts as migration.
2
+ #
3
+ # Npte the executing below the classes. These create the tables
4
+ module Timetrap
5
+ class Schema
6
+ def self.create_table!
7
+ DB.drop_table(self::TABLE)
8
+ create_table
9
+ end
10
+
11
+ def self.create_table
12
+ self.new.create_table
13
+ end
14
+
15
+ def self.try_create_table
16
+ create_table unless DB.table_exists?(self::TABLE)
17
+ end
18
+ end
19
+
20
+ class EntrySchema < Schema
21
+ TABLE = :entries
22
+ def create_table
23
+ DB.create_table(TABLE) do
24
+ primary_key :id
25
+ column :note, String
26
+ column :start, DateTime
27
+ column :end, DateTime
28
+ column :sheet, String
29
+ end
30
+ end
31
+ end
32
+
33
+ class MetaSchema < Schema
34
+ TABLE = :meta
35
+ def create_table
36
+ DB.create_table(TABLE) do
37
+ primary_key :id
38
+ column :key, String
39
+ column :value, String
40
+ end
41
+ end
42
+ end
43
+
44
+ ## Executes the schema, acting as a small migration
45
+ EntrySchema.try_create_table
46
+ MetaSchema.try_create_table
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Timetrap
2
- VERSION = '1.15.1'
2
+ VERSION = '1.15.4'
3
3
  end
data/lib/timetrap.rb CHANGED
@@ -18,8 +18,12 @@ module Timetrap
18
18
  DB_NAME = defined?(TEST_MODE) ? nil : Timetrap::Config['database_file']
19
19
  # connect to database. This will create one if it doesn't exist
20
20
  DB = Sequel.sqlite DB_NAME
21
- CLI.args = Getopt::Declare.new(<<-EOF)
21
+ # only declare cli options when run as standalone
22
+ if %w[dev_t t timetrap].include?(File.basename($PROGRAM_NAME)) || defined?(TEST_MODE)
23
+ CLI.args = Getopt::Declare.new(<<-EOF)
22
24
  #{CLI::USAGE}
23
- EOF
25
+ EOF
26
+ end
24
27
  end
28
+ require File.expand_path(File.join(File.dirname(__FILE__), 'timetrap', 'schema'))
25
29
  require File.expand_path(File.join(File.dirname(__FILE__), 'timetrap', 'models'))
@@ -0,0 +1,40 @@
1
+ # Constants
2
+ TEST_MODE = true
3
+
4
+ # Vendors
5
+ require 'fakefs/safe'
6
+
7
+ # Load support files from the spec/support directory
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
9
+
10
+ # timetrap
11
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'timetrap'))
12
+
13
+ # Config
14
+ RSpec.configure do |config|
15
+ # Use color in STDOUT
16
+ config.color = true
17
+ # Use color not only in STDOUT but also in pagers and files
18
+ config.tty = true
19
+
20
+ # Display 10 slowest tests after a test run
21
+ config.profile_examples = false
22
+
23
+ # Use the specified formatter
24
+ # :documentation, :progress, :html, :json, CustomFormatterClass
25
+ config.formatter = :progress
26
+
27
+ # Specify order for spec to be run in
28
+ # TODO: make sure all specs pass when set to :rand
29
+ # config.order = :rand
30
+
31
+ # We are stubbing stderr and stdout, if you want to capture
32
+ # any of your output in tests, simply add :write_stdout_stderr => true
33
+ # as metadata to the end of your test
34
+ config.after(:each, write_stdout_stderr: true) do
35
+ $stderr.rewind
36
+ $stdout.rewind
37
+ File.write("stderr.txt", $stderr.read)
38
+ File.write("stdout.txt", $stdout.read)
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ def local_time(str)
2
+ Timetrap::Timer.process_time(str)
3
+ end
4
+
5
+ def local_time_cli(str)
6
+ local_time(str).strftime('%Y-%m-%d %H:%M:%S')
7
+ end
@@ -0,0 +1,9 @@
1
+ def with_rounding_on
2
+ old_round = Timetrap::Entry.round
3
+ begin
4
+ Timetrap::Entry.round = true
5
+ block_return_value = yield
6
+ ensure
7
+ Timetrap::Entry.round = old_round
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ def with_stubbed_config(options = {})
2
+ defaults = Timetrap::Config.defaults.dup
3
+ allow(Timetrap::Config).to receive(:[]) do |k|
4
+ defaults.merge(options)[k]
5
+ end
6
+ yield if block_given?
7
+ end