woli 0.0.1.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|