woli 0.0.1.pre1
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.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +26 -0
- data/Rakefile +9 -0
- data/bin/woli +6 -0
- data/lib/woli.rb +36 -0
- data/lib/woli/cli.rb +13 -0
- data/lib/woli/cli/edit.rb +41 -0
- data/lib/woli/cli/list.rb +10 -0
- data/lib/woli/cli/notify.rb +12 -0
- data/lib/woli/cli/status.rb +17 -0
- data/lib/woli/config.rb +19 -0
- data/lib/woli/date_parser.rb +44 -0
- data/lib/woli/diary.rb +30 -0
- data/lib/woli/diary_entry.rb +21 -0
- data/lib/woli/repositories/files.rb +61 -0
- data/lib/woli/version.rb +3 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/woli/date_parser_spec.rb +68 -0
- data/spec/woli/diary_entry_spec.rb +47 -0
- data/spec/woli/diary_spec.rb +69 -0
- data/spec/woli/repositories/files_spec.rb +98 -0
- data/templates/default_config.yml +12 -0
- data/woli.gemspec +44 -0
- metadata +193 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Jiří Stránský
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Woli (as in WOrth LIving)
|
2
|
+
|
3
|
+
Geek's diary keeper, currently in pre-alpha state :)
|
4
|
+
|
5
|
+
## Why would a grown person keep a diary?
|
6
|
+
|
7
|
+
* The ability to recall why a particular time of our life was worth living is crucial for your
|
8
|
+
long term happiness. Can you recall what you did during this week four years ago? If you can't,
|
9
|
+
it doesn't mean the week was not worth living, but it does mean that you can't derive a feeling
|
10
|
+
of happiness and satisfaction from knowing *why* it was worth living. Keeping a diary helps you
|
11
|
+
recall. You won't lose memories that are worth keeping.
|
12
|
+
|
13
|
+
* A diary helps you reflect on how you spent your time. In a long run, keeping a diary increases
|
14
|
+
your ability to recognize what's really important for you, what makes you happy. You will be
|
15
|
+
able to shape your days to contain more of those happy moments and important stuff. You will
|
16
|
+
be able to point your life in a direction that will bring you satisfaction and happiness.
|
17
|
+
|
18
|
+
The project was ignited by Radovan Bahbouh's inspiring TEDx talk "The Best Investment"
|
19
|
+
([youtube video](http://www.youtube.com/watch?v=LNvExTsM2Xg), in Czech).
|
20
|
+
|
21
|
+
## Tips
|
22
|
+
|
23
|
+
* At the end of the day, try to recall what important things happened. Recall why this day was
|
24
|
+
worth living. Write it down.
|
25
|
+
|
26
|
+
* Do not write just what happened, write also how you felt about it.
|
data/Rakefile
ADDED
data/bin/woli
ADDED
data/lib/woli.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
if ENV['DEBUG']
|
2
|
+
require 'pry'
|
3
|
+
end
|
4
|
+
require "woli/config"
|
5
|
+
require "woli/diary"
|
6
|
+
require "woli/diary_entry"
|
7
|
+
require "woli/version"
|
8
|
+
require "woli/repositories/files"
|
9
|
+
|
10
|
+
module Woli
|
11
|
+
def self.config
|
12
|
+
@config ||= Woli::Config.load_user_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.diary
|
16
|
+
@diary ||= Woli::Diary.new(self.repository)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.editor
|
20
|
+
config['editor'] || ENV['EDITOR'] || 'vim'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.repository
|
24
|
+
@repository ||= instantiate_repository
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
private
|
29
|
+
def instantiate_repository
|
30
|
+
repository_class = config['repository_class'].split('::').reduce(Kernel) do |result_const, nested_const|
|
31
|
+
result_const = result_const.const_get(nested_const.to_sym)
|
32
|
+
end
|
33
|
+
repository_class.new(config['repository_config'])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/woli/cli.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Woli
|
2
|
+
class Cli
|
3
|
+
desc 'edit DAY', 'Edit a diary entry for a given day.'
|
4
|
+
long_desc <<-END
|
5
|
+
Edit a diary entry for a given day.
|
6
|
+
|
7
|
+
#{DateParser.parse_date_long_desc}
|
8
|
+
END
|
9
|
+
def edit(fuzzy_date = 'today')
|
10
|
+
date = DateParser.parse_date(fuzzy_date)
|
11
|
+
entry = Woli.repository.load_entry(date) || DiaryEntry.new(date, '', Woli.repository)
|
12
|
+
|
13
|
+
temp_file_name = generate_temp_file_name(entry)
|
14
|
+
save_text_to_temp_file(entry, temp_file_name)
|
15
|
+
edit_file_in_editor(temp_file_name)
|
16
|
+
load_text_from_temp_file(entry, temp_file_name)
|
17
|
+
|
18
|
+
entry.persist
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def save_text_to_temp_file(entry, temp_file_name)
|
24
|
+
File.write(temp_file_name, entry.text)
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_text_from_temp_file(entry, temp_file_name)
|
28
|
+
entry.text = File.read(temp_file_name)
|
29
|
+
File.delete(temp_file_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_temp_file_name(entry)
|
33
|
+
"/tmp/woli_edit_entry_#{entry.date.strftime('%Y_%m_%d')}.#{Woli.config['edit_entry_extension']}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def edit_file_in_editor(file_name)
|
37
|
+
tty = `tty`.strip
|
38
|
+
`#{Woli.editor} < #{tty} > #{tty} #{file_name}`
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Woli
|
2
|
+
class Cli
|
3
|
+
desc 'notify', 'Get notified when you forget to write entries.'
|
4
|
+
def notify
|
5
|
+
notify_config = Woli.config['notification']['missing_entries']
|
6
|
+
|
7
|
+
if Woli.diary.missing_entries_count >= notify_config['days']
|
8
|
+
`#{notify_config['command']}`
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Woli
|
2
|
+
class Cli
|
3
|
+
desc 'status', 'Print basic information about the diary.'
|
4
|
+
def status
|
5
|
+
entries_dates = Woli.diary.all_entries_dates
|
6
|
+
date_format = '%d-%m-%Y'
|
7
|
+
coverage = entries_dates.count / Float(entries_dates.last - entries_dates.first + 1)
|
8
|
+
|
9
|
+
puts "Woli Diary Status"
|
10
|
+
puts "================="
|
11
|
+
puts "First entry: #{entries_dates.first.strftime(date_format)}"
|
12
|
+
puts "Last entry: #{entries_dates.last.strftime(date_format)}"
|
13
|
+
puts "Coverage: %2d %" % (coverage * 100).round
|
14
|
+
puts "Missing entries since the last one: #{Woli.diary.missing_entries_count}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/woli/config.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Woli
|
4
|
+
class Config
|
5
|
+
CONFIG_FILE_NAME = "#{ENV['HOME']}/.woli/config.yml"
|
6
|
+
DEFAULT_CONFIG_FILE_NAME = File.join(File.dirname(__FILE__),
|
7
|
+
'../../templates/default_config.yml')
|
8
|
+
|
9
|
+
def self.load_user_config
|
10
|
+
create_default_config_file(CONFIG_FILE_NAME) unless File.exists?(CONFIG_FILE_NAME)
|
11
|
+
YAML.load_file(CONFIG_FILE_NAME)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.create_default_config_file(file_name)
|
15
|
+
FileUtils.mkdir_p(File.dirname(file_name))
|
16
|
+
FileUtils.cp(DEFAULT_CONFIG_FILE_NAME, file_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Woli
|
2
|
+
module DateParser
|
3
|
+
def self.parse_date_long_desc
|
4
|
+
<<-END
|
5
|
+
Specifying DAY:
|
6
|
+
|
7
|
+
1) Implicitly: If no date is given, 'today' is assumed.
|
8
|
+
|
9
|
+
2) Via a keyword: woli edit DAYNAME
|
10
|
+
(Where DAYNAME can be:
|
11
|
+
'today' or 't';
|
12
|
+
'yesterday' or 'y')
|
13
|
+
|
14
|
+
3) Via a date: woli edit DD[-MM[-YYYY]]
|
15
|
+
(Use hyphens, dots or slashes to separate day/month/year.
|
16
|
+
If year or month is not given, current year / current month is assumed.)
|
17
|
+
|
18
|
+
4) Via days ago: woli edit ^DAYS
|
19
|
+
(Where DAYS is a number of days ago. ^1 is yesterday, ^7 is a week ago etc.)
|
20
|
+
END
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse_date(fuzzy_date)
|
24
|
+
case fuzzy_date
|
25
|
+
when 'today', 't', nil
|
26
|
+
Date.today
|
27
|
+
when 'yesterday', 'y'
|
28
|
+
Date.today - 1
|
29
|
+
when /\A\^(?<days_ago>\d+)/
|
30
|
+
Date.today - Integer($~[:days_ago])
|
31
|
+
when /\A(?<day>\d+)([-\.\/](?<month>\d+)([-\.\/](?<year>\d+))?)?\Z/
|
32
|
+
today = Date.today
|
33
|
+
year = $~[:year] ? Integer($~[:year]) : today.year
|
34
|
+
month = $~[:month] ? Integer($~[:month]) : today.month
|
35
|
+
day = $~[:day] ? Integer($~[:day]) : today.day
|
36
|
+
Date.new(year, month, day)
|
37
|
+
else
|
38
|
+
self.class.task_help(shell, 'edit')
|
39
|
+
puts
|
40
|
+
raise MalformattedArgumentError, "'#{fuzzy_date}' is not a valid way to specify a date."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/woli/diary.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Woli
|
2
|
+
class Diary
|
3
|
+
attr_reader :repository
|
4
|
+
|
5
|
+
def initialize(repository)
|
6
|
+
@repository = repository
|
7
|
+
end
|
8
|
+
|
9
|
+
def all_entries_dates
|
10
|
+
@repository.all_entries_dates
|
11
|
+
end
|
12
|
+
|
13
|
+
def entry(date)
|
14
|
+
@repository.load_entry(date)
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_or_create_entry(date)
|
18
|
+
entry = @repository.load_entry(date)
|
19
|
+
return entry if entry
|
20
|
+
|
21
|
+
DiaryEntry.new(date, '', @repository)
|
22
|
+
end
|
23
|
+
|
24
|
+
def missing_entries_count
|
25
|
+
last_entry_date = @repository.all_entries_dates.last
|
26
|
+
return 0 unless last_entry_date
|
27
|
+
Date.today - last_entry_date
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Woli
|
2
|
+
class DiaryEntry
|
3
|
+
attr_reader :repository, :date
|
4
|
+
attr_accessor :text
|
5
|
+
|
6
|
+
def initialize(date, text, repository)
|
7
|
+
@date = date
|
8
|
+
@text = text
|
9
|
+
@repository = repository
|
10
|
+
end
|
11
|
+
|
12
|
+
def persist
|
13
|
+
if text.strip.length > 0
|
14
|
+
repository.save_entry(self)
|
15
|
+
else
|
16
|
+
puts "Empty entry -- removing."
|
17
|
+
repository.delete_entry(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Woli
|
2
|
+
module Repositories
|
3
|
+
class Files
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def all_entries_dates
|
11
|
+
all_entries_files.map { |filename|
|
12
|
+
match_data = filename.match /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2}).#{Regexp.escape(config['entry_extension'])}\Z/
|
13
|
+
Date.new(match_data[:year].to_i, match_data[:month].to_i, match_data[:day].to_i)
|
14
|
+
}.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_entry(date)
|
18
|
+
file_name = path_for_entry_date(date)
|
19
|
+
return nil unless File.exists? file_name
|
20
|
+
|
21
|
+
text = File.read(path_for_entry_date(date))
|
22
|
+
DiaryEntry.new(date, text, self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def save_entry(entry)
|
26
|
+
File.write(path_for_entry_date(entry.date), entry.text)
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_entry(entry)
|
30
|
+
file_name = path_for_entry_date(entry.date)
|
31
|
+
return unless File.exists? file_name
|
32
|
+
|
33
|
+
File.delete(file_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def diary_path
|
39
|
+
File.expand_path(config['path'])
|
40
|
+
end
|
41
|
+
|
42
|
+
def path_for_entry_date(date)
|
43
|
+
File.join(
|
44
|
+
diary_path,
|
45
|
+
date.strftime('%Y'),
|
46
|
+
date.strftime('%m'),
|
47
|
+
date.strftime("%Y-%m-%d.#{config['entry_extension']}")
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_entries_files
|
52
|
+
entry_like_filenames = Dir.glob(File.join(diary_path, '**', '*.' + config['entry_extension']))
|
53
|
+
|
54
|
+
# Globbing is not enough -> filter the results further with regexps.
|
55
|
+
entry_like_filenames.select do |filename|
|
56
|
+
filename =~ %r{#{Regexp.escape(diary_path)}/[0-9]{4}/[0-9]{2}/[0-9]{4}-[0-9]{2}-[0-9]{2}.#{Regexp.escape(config['entry_extension'])}\Z}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/woli/version.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'woli/date_parser'
|
3
|
+
|
4
|
+
describe Woli::DateParser do
|
5
|
+
before do
|
6
|
+
@parser = Woli::DateParser
|
7
|
+
@today = Date.new(2012, 7, 15)
|
8
|
+
Timecop.freeze(@today)
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
Timecop.return
|
13
|
+
end
|
14
|
+
|
15
|
+
it "parses nil as today" do
|
16
|
+
@parser.parse_date(nil).must_equal @today
|
17
|
+
end
|
18
|
+
|
19
|
+
it "parses 't' and 'today' as today" do
|
20
|
+
@parser.parse_date('t').must_equal @today
|
21
|
+
@parser.parse_date('today').must_equal @today
|
22
|
+
end
|
23
|
+
|
24
|
+
it "parses 'y' and 'yesterday' as yesterday" do
|
25
|
+
@parser.parse_date('y').must_equal (@today - 1)
|
26
|
+
@parser.parse_date('yesterday').must_equal (@today - 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "parses '^2' as 2 days ago" do
|
30
|
+
@parser.parse_date('^2').must_equal (@today - 2)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "parses '^50' as 50 days ago" do
|
34
|
+
@parser.parse_date('^50').must_equal (@today - 50)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "parses '20' as 20th day of current month" do
|
38
|
+
parsed = @parser.parse_date('20')
|
39
|
+
parsed.year.must_equal @today.year
|
40
|
+
parsed.month.must_equal @today.month
|
41
|
+
parsed.day.must_equal 20
|
42
|
+
end
|
43
|
+
|
44
|
+
it "parses '20.2' as 20th February of current year" do
|
45
|
+
parsed = @parser.parse_date('20.2')
|
46
|
+
parsed.year.must_equal @today.year
|
47
|
+
parsed.month.must_equal 2
|
48
|
+
parsed.day.must_equal 20
|
49
|
+
end
|
50
|
+
|
51
|
+
it "parses '20/2/2011' as 20th February 2011" do
|
52
|
+
parsed = @parser.parse_date('20/2/2011')
|
53
|
+
parsed.year.must_equal 2011
|
54
|
+
parsed.month.must_equal 2
|
55
|
+
parsed.day.must_equal 20
|
56
|
+
end
|
57
|
+
|
58
|
+
it "parses '20.4', '20-4', '20/4', '20-4-2012' as the same dates (current year is 2012)" do
|
59
|
+
date1 = @parser.parse_date('20.4')
|
60
|
+
date2 = @parser.parse_date('20-4')
|
61
|
+
date3 = @parser.parse_date('20/4')
|
62
|
+
date4 = @parser.parse_date('20-4-2012')
|
63
|
+
|
64
|
+
date1.must_equal date2
|
65
|
+
date1.must_equal date3
|
66
|
+
date1.must_equal date4
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'woli/diary_entry'
|
3
|
+
|
4
|
+
describe Woli::DiaryEntry do
|
5
|
+
before do
|
6
|
+
@repository = mock 'repository'
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "empty entry" do
|
10
|
+
before do
|
11
|
+
@entry = Woli::DiaryEntry.new(Date.new(2012, 7, 14), '', @repository)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "removes itself from the repository when persisting its state" do
|
15
|
+
@repository.expects(:delete_entry).with(@entry)
|
16
|
+
@entry.persist
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "pseudoempty entry (whitespace only)" do
|
21
|
+
before do
|
22
|
+
@entry = Woli::DiaryEntry.new(Date.new(2012, 7, 14), "\n\n \n", @repository)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "removes itself from the repository when persisting its state" do
|
26
|
+
@repository.expects(:delete_entry).with(@entry)
|
27
|
+
@entry.persist
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "filled in entry" do
|
32
|
+
before do
|
33
|
+
text = <<-END
|
34
|
+
* Today I had the best ice cream.
|
35
|
+
|
36
|
+
* I slipped on a banana and didn't break anything. Yay!
|
37
|
+
END
|
38
|
+
@entry = Woli::DiaryEntry.new(Date.new(2012, 7, 14), text, @repository)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "saves itself into the repository when persisting its state" do
|
42
|
+
@repository.expects(:save_entry).with(@entry)
|
43
|
+
@entry.persist
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'woli/diary'
|
3
|
+
|
4
|
+
describe Woli::Diary do
|
5
|
+
before do
|
6
|
+
@existing_entry = mock 'existing diary entry'
|
7
|
+
@existing_entry_date = Date.new(2012, 7, 1)
|
8
|
+
@nonexistent_entry_date = Date.new(2012, 7, 16)
|
9
|
+
|
10
|
+
@repository = mock 'repository'
|
11
|
+
@repository.stubs(:load_entry).with(@existing_entry_date).returns(@existing_entry)
|
12
|
+
@repository.stubs(:load_entry).with(@nonexistent_entry_date).returns(nil)
|
13
|
+
@repository.stubs(:all_entries_dates).returns([@existing_entry_date])
|
14
|
+
|
15
|
+
@diary = Woli::Diary.new(@repository)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#entry" do
|
19
|
+
it "loads an existing entry" do
|
20
|
+
@diary.entry(@existing_entry_date).must_equal @existing_entry
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns nil for a non-existent entry" do
|
24
|
+
@diary.entry(@nonexistent_entry_date).must_equal nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#load_or_create_entry" do
|
29
|
+
before do
|
30
|
+
@new_entry = :fake_new_entry
|
31
|
+
class Woli::DiaryEntry ; end
|
32
|
+
Woli::DiaryEntry.stubs(:new).with(@nonexistent_entry_date, '', @repository).returns(@new_entry)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "loads an existing entry" do
|
36
|
+
@diary.load_or_create_entry(@existing_entry_date).must_equal @existing_entry
|
37
|
+
end
|
38
|
+
|
39
|
+
it "creates a new entry for a non-existent entry" do
|
40
|
+
@diary.load_or_create_entry(@nonexistent_entry_date).must_equal @new_entry
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#all_entries_dates" do
|
45
|
+
it "delegates the call to the repository" do
|
46
|
+
@diary.all_entries_dates.must_equal @repository.all_entries_dates
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#missing_entries_count" do
|
51
|
+
it "returns 0 when the last entry is for today" do
|
52
|
+
Timecop.freeze(@repository.all_entries_dates.last) do
|
53
|
+
@diary.missing_entries_count.must_equal 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns 1 when the last entry is for yesterday" do
|
58
|
+
Timecop.freeze(@repository.all_entries_dates.last + 1) do
|
59
|
+
@diary.missing_entries_count.must_equal 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns 7 when the last entry is for a day week ago" do
|
64
|
+
Timecop.freeze(@repository.all_entries_dates.last + 7) do
|
65
|
+
@diary.missing_entries_count.must_equal 7
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require 'woli/repositories/files'
|
3
|
+
|
4
|
+
describe Woli::Repositories::Files do
|
5
|
+
before do
|
6
|
+
@config = {
|
7
|
+
'path' => '/tmp/fake_diary_path',
|
8
|
+
'entry_extension' => 'md'
|
9
|
+
}
|
10
|
+
|
11
|
+
@repository = Woli::Repositories::Files.new(@config)
|
12
|
+
|
13
|
+
@entry = stub(
|
14
|
+
:date => Date.new(2012, 7, 6),
|
15
|
+
:text => 'entry text',
|
16
|
+
:repository => @repository
|
17
|
+
)
|
18
|
+
@entry_path = "#{@config['path']}/2012/07/2012-07-06.#{@config['entry_extension']}"
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#all_entries_dates" do
|
22
|
+
before do
|
23
|
+
Dir.stubs(:glob)
|
24
|
+
.with("#{@config['path']}/**/*.#{@config['entry_extension']}")
|
25
|
+
.returns([
|
26
|
+
"#{@config['path']}/2012/07/2012-07-06.#{@config['entry_extension']}",
|
27
|
+
"#{@config['path']}/2012/07/2012-07-04.#{@config['entry_extension']}",
|
28
|
+
"#{@config['path']}/2012/07/2012-07-05.#{@config['entry_extension']}",
|
29
|
+
"#{@config['path']}/2012/07/maliciously_named_file.#{@config['entry_extension']}",
|
30
|
+
"#{@config['path']}/2012/07/2012-07-03-this-aint-right.#{@config['entry_extension']}"
|
31
|
+
])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns correct and sorted dates for correctly named entry files, ignores others" do
|
35
|
+
@repository.all_entries_dates.must_equal [
|
36
|
+
Date.new(2012, 7, 4),
|
37
|
+
Date.new(2012, 7, 5),
|
38
|
+
Date.new(2012, 7, 6)
|
39
|
+
]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#load_entry" do
|
44
|
+
it "loads an entry from a file" do
|
45
|
+
new_entry = :fake_new_entry
|
46
|
+
|
47
|
+
File.expects(:exists?)
|
48
|
+
.with(@entry_path)
|
49
|
+
.returns(true)
|
50
|
+
|
51
|
+
File.expects(:read)
|
52
|
+
.with(@entry_path)
|
53
|
+
.returns('entry text')
|
54
|
+
|
55
|
+
class Woli::DiaryEntry; end
|
56
|
+
Woli::DiaryEntry.expects(:new).with(@entry.date, 'entry text', @repository).returns(new_entry)
|
57
|
+
|
58
|
+
@repository.load_entry(@entry.date).must_equal(new_entry)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns nil if file for that date doesn't exist" do
|
62
|
+
File.expects(:exists?)
|
63
|
+
.with(@entry_path)
|
64
|
+
.returns(false)
|
65
|
+
|
66
|
+
@repository.load_entry(@entry.date).must_be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#save_entry" do
|
71
|
+
it "writes the text into a file" do
|
72
|
+
File.expects(:write).with(@entry_path, @entry.text).returns(@entry.text.length)
|
73
|
+
|
74
|
+
@repository.save_entry(@entry)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#delete_entry" do
|
79
|
+
it "does nothing if the file does not exist" do
|
80
|
+
File.expects(:exists?)
|
81
|
+
.with(@entry_path)
|
82
|
+
.returns(false)
|
83
|
+
|
84
|
+
@repository.delete_entry(@entry).must_be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it "deletes the file with entry contents" do
|
88
|
+
File.expects(:exists?)
|
89
|
+
.with(@entry_path)
|
90
|
+
.returns(true)
|
91
|
+
File.expects(:delete)
|
92
|
+
.with(@entry_path)
|
93
|
+
.returns(1)
|
94
|
+
|
95
|
+
@repository.delete_entry(@entry)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
repository_class: 'Woli::Repositories::Files'
|
2
|
+
repository_config:
|
3
|
+
path: ~/.woli/diary
|
4
|
+
entry_extension: md
|
5
|
+
|
6
|
+
editor:
|
7
|
+
edit_entry_extension: md
|
8
|
+
|
9
|
+
notification:
|
10
|
+
missing_entries:
|
11
|
+
days: 1
|
12
|
+
command: "notify-send 'Woli diary is hungry for entries.'"
|
data/woli.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/woli/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jiří Stránský"]
|
6
|
+
gem.email = ["jistr@jistr.com"]
|
7
|
+
gem.description = <<-END
|
8
|
+
Woli (as in WOrth LIving) helps you record your memories. Write
|
9
|
+
a short note about every day you live and make sure your days are worth
|
10
|
+
living. Later, review your notes to bring back the memories.
|
11
|
+
Change your life if you are not happy with what's in your diary.
|
12
|
+
END
|
13
|
+
gem.summary = "Woli, the diary keeper."
|
14
|
+
gem.homepage = "http://github.com/jistr/woli"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($\)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.name = "woli"
|
20
|
+
gem.require_paths = ["lib"]
|
21
|
+
gem.version = Woli::VERSION
|
22
|
+
|
23
|
+
gem.add_dependency 'thor', '~> 0.15.2'
|
24
|
+
|
25
|
+
# == DEVELOPMENT DEPENDENCIES ==
|
26
|
+
# Smart irb
|
27
|
+
gem.add_development_dependency 'pry'
|
28
|
+
|
29
|
+
# Specs
|
30
|
+
gem.add_development_dependency 'minitest'
|
31
|
+
gem.add_development_dependency 'mocha'
|
32
|
+
gem.add_development_dependency 'timecop'
|
33
|
+
|
34
|
+
# Running tests during development
|
35
|
+
gem.add_development_dependency 'guard'
|
36
|
+
gem.add_development_dependency 'guard-minitest'
|
37
|
+
# Linux Guard watching
|
38
|
+
gem.add_development_dependency 'rb-inotify'
|
39
|
+
# Linux Guard notifications
|
40
|
+
gem.add_development_dependency 'libnotify'
|
41
|
+
|
42
|
+
# Pretty printed test output
|
43
|
+
gem.add_development_dependency 'turn'
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: woli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jiří Stránský
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: &16096140 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.15.2
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *16096140
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: pry
|
27
|
+
requirement: &16095460 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *16095460
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: minitest
|
38
|
+
requirement: &16094260 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *16094260
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: mocha
|
49
|
+
requirement: &16093600 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *16093600
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: timecop
|
60
|
+
requirement: &16092680 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *16092680
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard
|
71
|
+
requirement: &16091900 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *16091900
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: guard-minitest
|
82
|
+
requirement: &16091300 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *16091300
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rb-inotify
|
93
|
+
requirement: &16106680 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *16106680
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: libnotify
|
104
|
+
requirement: &16106120 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *16106120
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: turn
|
115
|
+
requirement: &16105220 !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ! '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: *16105220
|
124
|
+
description: ! " Woli (as in WOrth LIving) helps you record your memories. Write\n
|
125
|
+
\ a short note about every day you live and make sure your days are worth\n living.
|
126
|
+
Later, review your notes to bring back the memories.\n Change your life if you
|
127
|
+
are not happy with what's in your diary.\n"
|
128
|
+
email:
|
129
|
+
- jistr@jistr.com
|
130
|
+
executables:
|
131
|
+
- woli
|
132
|
+
extensions: []
|
133
|
+
extra_rdoc_files: []
|
134
|
+
files:
|
135
|
+
- .gitignore
|
136
|
+
- Gemfile
|
137
|
+
- Guardfile
|
138
|
+
- LICENSE
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- bin/woli
|
142
|
+
- lib/woli.rb
|
143
|
+
- lib/woli/cli.rb
|
144
|
+
- lib/woli/cli/edit.rb
|
145
|
+
- lib/woli/cli/list.rb
|
146
|
+
- lib/woli/cli/notify.rb
|
147
|
+
- lib/woli/cli/status.rb
|
148
|
+
- lib/woli/config.rb
|
149
|
+
- lib/woli/date_parser.rb
|
150
|
+
- lib/woli/diary.rb
|
151
|
+
- lib/woli/diary_entry.rb
|
152
|
+
- lib/woli/repositories/files.rb
|
153
|
+
- lib/woli/version.rb
|
154
|
+
- spec/spec_helper.rb
|
155
|
+
- spec/woli/date_parser_spec.rb
|
156
|
+
- spec/woli/diary_entry_spec.rb
|
157
|
+
- spec/woli/diary_spec.rb
|
158
|
+
- spec/woli/repositories/files_spec.rb
|
159
|
+
- templates/default_config.yml
|
160
|
+
- woli.gemspec
|
161
|
+
homepage: http://github.com/jistr/woli
|
162
|
+
licenses: []
|
163
|
+
post_install_message:
|
164
|
+
rdoc_options: []
|
165
|
+
require_paths:
|
166
|
+
- lib
|
167
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
|
+
none: false
|
169
|
+
requirements:
|
170
|
+
- - ! '>='
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
segments:
|
174
|
+
- 0
|
175
|
+
hash: -2448595754877266534
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>'
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 1.3.1
|
182
|
+
requirements: []
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 1.8.11
|
185
|
+
signing_key:
|
186
|
+
specification_version: 3
|
187
|
+
summary: Woli, the diary keeper.
|
188
|
+
test_files:
|
189
|
+
- spec/spec_helper.rb
|
190
|
+
- spec/woli/date_parser_spec.rb
|
191
|
+
- spec/woli/diary_entry_spec.rb
|
192
|
+
- spec/woli/diary_spec.rb
|
193
|
+
- spec/woli/repositories/files_spec.rb
|