morning-pages-journal 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +70 -0
- data/Rakefile +1 -0
- data/bin/mp +58 -0
- data/lib/morning-pages-journal.rb +6 -0
- data/lib/morning-pages-journal/journal.rb +236 -0
- data/lib/morning-pages-journal/version.rb +7 -0
- data/morning-pages-journal.gemspec +28 -0
- data/spec/morning-pages-journal/page_spec.rb +5 -0
- data/spec/spec_helper.rb +17 -0
- metadata +109 -0
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
Morning Pages Journal
|
2
|
+
=====================
|
3
|
+
|
4
|
+
A command line tool to manage morning pages.
|
5
|
+
|
6
|
+
Morning Pages are three pages of longhand, stream of consciousness writing,
|
7
|
+
done first thing in the morning. *There is no wrong way to do Morning Pages*–
|
8
|
+
they are not high art. They are not even “writing.” They are about
|
9
|
+
anything and everything that crosses your mind– and they are for your eyes
|
10
|
+
only. Morning Pages provoke, clarify, comfort, cajole, prioritize and
|
11
|
+
synchronize the day at hand. Do not over-think Morning Pages: just put
|
12
|
+
three pages of anything on the page…and then do three more pages tomorrow.
|
13
|
+
|
14
|
+
I thought I had a great idea. I wrote the code, then I found out somebody else did it. Anyway, here is my code...
|
15
|
+
|
16
|
+
Usage
|
17
|
+
-----
|
18
|
+
|
19
|
+
### To open current day morning page in editor:
|
20
|
+
|
21
|
+
$ mp
|
22
|
+
|
23
|
+
### To list morning pages and progress:
|
24
|
+
|
25
|
+
$ mp list # current month
|
26
|
+
$ mp list -w # current week
|
27
|
+
$ mp list -d # curent day
|
28
|
+
$ mp list -m # current month
|
29
|
+
$ mp list -y # current year
|
30
|
+
|
31
|
+
### To get stats:
|
32
|
+
|
33
|
+
$ mp stat # current month
|
34
|
+
$ mp stat -w # current week
|
35
|
+
$ mp stat -d # curent day
|
36
|
+
$ mp stat -m # current month
|
37
|
+
$ mp stat -y # current year
|
38
|
+
|
39
|
+
### To count words:
|
40
|
+
|
41
|
+
$ mp words # current month
|
42
|
+
$ mp words -w # current week
|
43
|
+
$ mp words -d # curent day
|
44
|
+
$ mp words -m # current month
|
45
|
+
$ mp words -y # current year
|
46
|
+
|
47
|
+
Configuration
|
48
|
+
-------------
|
49
|
+
|
50
|
+
A `~/.mp.yml` file will be created that looks like
|
51
|
+
|
52
|
+
editor: mate
|
53
|
+
folder: ~/.morning-pages/
|
54
|
+
words: 750
|
55
|
+
stats: 50
|
56
|
+
|
57
|
+
You can change setting manually or use
|
58
|
+
|
59
|
+
mp config <key> <value>
|
60
|
+
mp config editor vi
|
61
|
+
|
62
|
+
You can also specify a config file and run any of the commands
|
63
|
+
|
64
|
+
mp -c custom.yaml list
|
65
|
+
|
66
|
+
|
67
|
+
Installation
|
68
|
+
------------
|
69
|
+
|
70
|
+
gem install morning-pages-journal
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/mp
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'trollop'
|
5
|
+
require 'morning-pages-journal'
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
def range(sub_opts)
|
9
|
+
range = (sub_opts[:day] && :day) || (sub_opts[:week] && :week) || (sub_opts[:month] && :month) || (sub_opts[:year] && :year) || :month
|
10
|
+
end
|
11
|
+
|
12
|
+
SUB_COMMANDS = %w(list stat words config)
|
13
|
+
opts = Trollop.options do
|
14
|
+
banner "Usage: mp [options] [list | stat | words] [options]"
|
15
|
+
opt :config,"Configuration file", :required => false, :type => :string, :default => "~/.mp.yml"
|
16
|
+
stop_on SUB_COMMANDS
|
17
|
+
end
|
18
|
+
|
19
|
+
cli = MorningPagesJournal::CLI.new(opts)
|
20
|
+
|
21
|
+
if ARGV.empty?
|
22
|
+
cli.open
|
23
|
+
else
|
24
|
+
cmd = ARGV.shift # get the subcomman
|
25
|
+
|
26
|
+
def parse_options
|
27
|
+
Trollop.options do
|
28
|
+
opt :day,"Show only for the day", :required => false, :default => false
|
29
|
+
opt :week,"Show only for the week", :required => false , :default => false
|
30
|
+
opt :month,"Show only for the month", :required => false, :default => false
|
31
|
+
opt :year,"Show only for the year", :required => false, :default => false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
case cmd
|
36
|
+
when "list"
|
37
|
+
sub_opts = parse_options
|
38
|
+
|
39
|
+
cli.list(:range => range(sub_opts))
|
40
|
+
when "stat"
|
41
|
+
sub_opts = parse_options
|
42
|
+
|
43
|
+
cli.stat(:range => range(sub_opts))
|
44
|
+
when "words"
|
45
|
+
sub_opts = parse_options
|
46
|
+
|
47
|
+
cli.words(:range => range(sub_opts))
|
48
|
+
when "config"
|
49
|
+
raise Trollop::die("config requires 2 arguments key and value") if ARGV.size != 2
|
50
|
+
key = ARGV.shift
|
51
|
+
value = ARGV.shift
|
52
|
+
|
53
|
+
cli.update_config(key, value)
|
54
|
+
else
|
55
|
+
puts "Unknown command '#{cmd}'".color(:red)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'rainbow'
|
3
|
+
|
4
|
+
module MorningPagesJournal
|
5
|
+
|
6
|
+
class Page
|
7
|
+
attr_reader :file_path
|
8
|
+
|
9
|
+
def initialize(file_path)
|
10
|
+
@file_path = file_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def date
|
14
|
+
return @date if @date
|
15
|
+
|
16
|
+
s = @file_path.split("/")
|
17
|
+
|
18
|
+
year_month = s[s.size-2]
|
19
|
+
day = s[s.size-1].split(".")[0]
|
20
|
+
|
21
|
+
@date = Date.parse("#{year_month}-#{day}")
|
22
|
+
@date
|
23
|
+
end
|
24
|
+
|
25
|
+
def words
|
26
|
+
@count ||= `wc -w #{@file_path}`.split(" ")[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
def word_count(hash = Hash.new(0))
|
30
|
+
content.split.inject(hash) { |h,v| h[v] += 1; h }
|
31
|
+
end
|
32
|
+
|
33
|
+
def content
|
34
|
+
File.open(@file_path).read
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class Journal
|
40
|
+
|
41
|
+
def initialize(options)
|
42
|
+
raise "Base folder is required" unless options[:base_folder]
|
43
|
+
@base_folder = options[:base_folder]
|
44
|
+
end
|
45
|
+
|
46
|
+
def today_folder
|
47
|
+
folder = Date.today.strftime("%Y-%m")
|
48
|
+
File.join(@base_folder,folder)
|
49
|
+
end
|
50
|
+
|
51
|
+
def today_file
|
52
|
+
file_name = Date.today.strftime("%d")
|
53
|
+
File.join(today_folder,"#{file_name}.txt")
|
54
|
+
end
|
55
|
+
|
56
|
+
def pages
|
57
|
+
@pages ||= pages_files.collect { |path| Page.new(path) }.sort { |a, b| a.date <=> a.date }
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_page(date)
|
61
|
+
@pages.select {|p| p.date == date }.first
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_day(range = :month)
|
65
|
+
return if pages.empty?
|
66
|
+
max = pages.last.date
|
67
|
+
min = date_starting_at(max, range)
|
68
|
+
|
69
|
+
min.upto(max).each do |date|
|
70
|
+
yield date
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def min_date(range = :month)
|
75
|
+
# pages.first.date
|
76
|
+
date_starting_at(max_date,range)
|
77
|
+
end
|
78
|
+
|
79
|
+
def max_date
|
80
|
+
pages.last.date
|
81
|
+
end
|
82
|
+
|
83
|
+
def word_count(range=:month)
|
84
|
+
hash = Hash.new(0)
|
85
|
+
pages_from(min_date(range),max_date).each do |p|
|
86
|
+
p.word_count(hash)
|
87
|
+
end
|
88
|
+
hash
|
89
|
+
end
|
90
|
+
|
91
|
+
def average_words
|
92
|
+
w = pages.collect {|p| p.words.to_i}
|
93
|
+
w.inject(0.0) { |sum, el| sum + el } / w.size
|
94
|
+
end
|
95
|
+
|
96
|
+
def pages_from(from,to)
|
97
|
+
pages.select do |p|
|
98
|
+
p.date >= from && p.date <= to
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def pages_files
|
105
|
+
Dir.glob("#{File.expand_path(@base_folder)}/**/*").reject {|fn| File.directory?(fn) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def date_starting_at(date,range)
|
109
|
+
case range
|
110
|
+
when :day then date
|
111
|
+
when :week then date - (date.wday-1)
|
112
|
+
when :month then date - (date.mday-1)
|
113
|
+
when :year then date - (date.yday-1)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class CLI
|
119
|
+
|
120
|
+
def initialize(command_line_arguments)
|
121
|
+
@opts = command_line_arguments
|
122
|
+
@opts[:config] = File.expand_path(@opts[:config])
|
123
|
+
end
|
124
|
+
|
125
|
+
def opts
|
126
|
+
@opts
|
127
|
+
end
|
128
|
+
|
129
|
+
def config
|
130
|
+
create_config_if_not_exists!
|
131
|
+
|
132
|
+
@config ||= YAML::load(File.open(opts[:config]))
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def config_words
|
137
|
+
config["words"] || 750
|
138
|
+
end
|
139
|
+
|
140
|
+
def stats_words
|
141
|
+
(config["stats"] || 50).to_i
|
142
|
+
end
|
143
|
+
|
144
|
+
def open
|
145
|
+
journal = MorningPagesJournal::Journal.new(:base_folder => config["folder"])
|
146
|
+
editor = config["editor"]
|
147
|
+
|
148
|
+
system "mkdir -p #{journal.today_folder} ; #{editor} #{journal.today_file}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def list(options)
|
152
|
+
journal = MorningPagesJournal::Journal.new(:base_folder => config["folder"])
|
153
|
+
|
154
|
+
month_name = ""
|
155
|
+
total_words = 0
|
156
|
+
journal.each_day(options[:range]) do |date|
|
157
|
+
if month_name != date.strftime("%B")
|
158
|
+
month_name = date.strftime("%B")
|
159
|
+
puts month_name
|
160
|
+
puts "-" * 100
|
161
|
+
end
|
162
|
+
|
163
|
+
page = journal.get_page(date)
|
164
|
+
|
165
|
+
words,color = if page
|
166
|
+
if page.words.to_i < config_words
|
167
|
+
[page.words.to_i, :red]
|
168
|
+
else
|
169
|
+
[page.words.to_i, :green]
|
170
|
+
end
|
171
|
+
else
|
172
|
+
[0,:yellow]
|
173
|
+
end
|
174
|
+
|
175
|
+
total_words += words
|
176
|
+
puts "#{date.strftime('%a, %d')}\t#{words.to_s.color(color)}\t" + ("="* (words/10).to_i).color(color)
|
177
|
+
|
178
|
+
if date.strftime('%a') == "Sat"
|
179
|
+
puts ""
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def stat(options)
|
185
|
+
journal = MorningPagesJournal::Journal.new(:base_folder => config["folder"])
|
186
|
+
|
187
|
+
min_date = journal.min_date(options[:range])
|
188
|
+
days = ((journal.max_date+1) - min_date).to_i
|
189
|
+
morning_pages = journal.pages_from(min_date,journal.max_date).size
|
190
|
+
missed = days - morning_pages
|
191
|
+
|
192
|
+
puts "Average Words:\t#{journal.average_words.to_i}\n"
|
193
|
+
puts "From:\t#{min_date}"
|
194
|
+
puts "To:\t#{journal.max_date}"
|
195
|
+
puts "Total days:\t#{days}"
|
196
|
+
puts "Morning pages:\t#{morning_pages}"
|
197
|
+
puts "Missed:\t#{missed}"
|
198
|
+
puts "\n"
|
199
|
+
end
|
200
|
+
|
201
|
+
def words(options)
|
202
|
+
journal = MorningPagesJournal::Journal.new(:base_folder => config["folder"])
|
203
|
+
journal.word_count(options[:range]).sort_by {|k,v| v}.reverse.take(stats_words).each do |e|
|
204
|
+
puts "#{e[0]}\t#{e[1]}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def update_config(key,value)
|
209
|
+
c = config
|
210
|
+
c[key] = value
|
211
|
+
|
212
|
+
File.open(opts[:config],"w+") do |f|
|
213
|
+
f.write YAML::dump(c)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def create_config_if_not_exists!
|
220
|
+
unless File.exists?(opts[:config])
|
221
|
+
File.open(opts[:config],"w+") do |f|
|
222
|
+
f.write YAML::dump({
|
223
|
+
"editor" => "mate",
|
224
|
+
"folder" => "~/.morning-pages/",
|
225
|
+
"words" => 750,
|
226
|
+
"stats" => 50
|
227
|
+
})
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "morning-pages-journal/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "morning-pages-journal"
|
7
|
+
s.version = Morning::Pages::Journal::VERSION
|
8
|
+
s.authors = ["Federico Dayan"]
|
9
|
+
s.email = ["federico.dayan@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Command line tool to manage morning pages}
|
12
|
+
s.description = %q{Morning pages are three pages of writing done every day. This tool organizes pages and keep track of progress. It does not share anything of course }
|
13
|
+
|
14
|
+
s.rubyforge_project = "morning-pages-journal"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
|
25
|
+
s.add_dependency 'trollop'
|
26
|
+
s.add_dependency 'rainbow'
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: morning-pages-journal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Federico Dayan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-13 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: trollop
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rainbow
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! 'Morning pages are three pages of writing done every day. This tool
|
63
|
+
organizes pages and keep track of progress. It does not share anything of course '
|
64
|
+
email:
|
65
|
+
- federico.dayan@gmail.com
|
66
|
+
executables:
|
67
|
+
- mp
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- .rspec
|
73
|
+
- Gemfile
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- bin/mp
|
77
|
+
- lib/morning-pages-journal.rb
|
78
|
+
- lib/morning-pages-journal/journal.rb
|
79
|
+
- lib/morning-pages-journal/version.rb
|
80
|
+
- morning-pages-journal.gemspec
|
81
|
+
- spec/morning-pages-journal/page_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
homepage: ''
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project: morning-pages-journal
|
103
|
+
rubygems_version: 1.8.18
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Command line tool to manage morning pages
|
107
|
+
test_files:
|
108
|
+
- spec/morning-pages-journal/page_spec.rb
|
109
|
+
- spec/spec_helper.rb
|