whale 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/whale +108 -22
  3. data/lib/whale.rb +44 -61
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84a82a8527b5b787b69deb1dbb9c229db5cb356f
4
- data.tar.gz: 4818466b7d1d8ca1d26a250754ec95fa451d188d
3
+ metadata.gz: 10aefc6ce7f9102e028c417e9e8d7a5b2f1f1ce0
4
+ data.tar.gz: 7a3dcd424e6c33d208b18ae7e3a29b7d078b564d
5
5
  SHA512:
6
- metadata.gz: b8c2f2f092f0e34d3726267ddfdfb87bba0efb1850495d62c8b145561b95075611b64fe00e400a8d1c5ace6110ada2232cca73599e69113cfa951fd054078eab
7
- data.tar.gz: 231ace61fa951ce4b6a705c3eba13a3cda42f253e867bda0eacf4103bbfc1022958b53f886e1a2b3febe63ca78d2713c0ea9e42ec3ee6b6caab42091454f0193
6
+ metadata.gz: 4e308539f6f2ac801178c4edb1fbabbec49ac1e31459c5d3662e96732deb7ca19a7edaa9fbabc2e1ed37b9a3449010277dd780aab335fb4699808471e8d6c5a6
7
+ data.tar.gz: 6653cc61f4da672de5e76f677c2845ba1b66a326333206115f57bb6b1f1658d0f5b4b256afd84bd67ad1cf942b5a41eec68087b84d524e0f980da7069321abbb
data/bin/whale CHANGED
@@ -1,18 +1,34 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'whale'
3
+ require 'logger'
4
+
3
5
 
4
6
  USAGE = <<ENDUSAGE
5
7
  Usage:
6
- whale [-h] [-f tag] [-s tag] [-e id] files..
8
+ whale [files] [-h] [-p paths] [-r] [-f filter] [-e id] [-s tag]
9
+ [-w] [-n] [-t] [-v] [-d]
7
10
  ENDUSAGE
8
11
 
9
12
  HELP = <<ENDHELP
13
+ files Space-separated list of files
10
14
  -h, --help View this message
11
- -f, --filter List entries with the tag
12
- -s, --sort Sort entries by the tag value
15
+ -p, --paths Colon-separated list of paths to search
16
+ -r, --recursive Search for files recursively
17
+ -f, --filter Filter entries by tags
13
18
  -e, --edit Open the editor to given entry
14
- -w, --write Write the entries to file
15
- --version Show the version
19
+ -s, --sort Sort entries by tag value
20
+ -w, --write Write the entries to stdout
21
+ -n, --name List the tag names
22
+ -t, --tags Show the tag values
23
+ -v, --version Show the version number
24
+ -d, --debug Set the logging level to debug
25
+
26
+ -p, --paths
27
+ Specify the paths to search. Each path is separated by a colon. Any files
28
+ with the .wl or .whale extension are read.
29
+
30
+ -r, --recursive
31
+ Search for files recursively.
16
32
 
17
33
  -f, --filter filter
18
34
  Show entries with tags satisfying the filter. The filter is a string
@@ -22,28 +38,54 @@ HELP = <<ENDHELP
22
38
  Options for regexes are specified by writing /(?option:regex)/.
23
39
  The symbols for AND, OR, and NOT are &, |, and *, respectively.
24
40
 
25
- -s, --sort tag
26
- Sort the entries by tag.
27
-
28
41
  -e, --edit id
29
42
  Edit the entry with id id using the text editor specified by the environment
30
43
  EDITOR.
31
44
 
45
+ -n, --name
46
+ List all tags read.
47
+
48
+ -t, --tags tags
49
+ Show the values of the given tags.
50
+
51
+ -s, --sort tag
52
+ Sort the entries by the given tag value.
53
+
32
54
  -w, --write
33
55
  Write the entries, including title, body, and tags, to stdout.
56
+
57
+ -v, --version
58
+ Show the version number.
59
+
60
+ -d, --debug
61
+ Set the logging level to debug.
62
+
63
+ Environment
64
+ EDITOR
65
+ The editor for the edit command.
66
+
67
+ WHALEPATH
68
+ A colon separated list of default paths to search when paths is not
69
+ provided.
34
70
  ENDHELP
35
71
 
36
- args = { :files => [] }
72
+ args = { :files => [], :recursive => false }
37
73
  unflagged_args = [:files]
38
74
  next_arg = unflagged_args.first
75
+
39
76
  ARGV.each do |arg|
40
77
  case arg
41
78
  when '-h','--help' then args[:help] = true
79
+ when '-p','--paths' then next_arg = :paths
42
80
  when '-f','--filter' then next_arg = :filter
43
- when '-s','--sort' then next_arg = :sort
44
81
  when '-e','--edit' then next_arg = :edit
45
- when '-w', '--write' then args[:write] = true
46
- when '--version' then args[:version] = true
82
+ when '-n','--name' then args[:name] = true
83
+ when '-t','--tags' then next_arg = :tags
84
+ when '-s','--sort' then next_arg = :sort
85
+ when '-r','--recursive' then args[:recursive] = true
86
+ when '-w','--write' then args[:write] = true
87
+ when '-v','--version' then args[:version] = true
88
+ when '-d','--debug' then args[:debug] = true
47
89
  else
48
90
  if next_arg == :files
49
91
  args[:files] << arg
@@ -54,24 +96,56 @@ ARGV.each do |arg|
54
96
  end
55
97
  end
56
98
  end
99
+
100
+ logger = Logger.new(STDOUT)
101
+ logger.level = Logger::WARN
102
+ logger.level = Logger::DEBUG if args[:debug]
103
+ logger.level = Logger::UNKNOWN if args[:write]
104
+
57
105
  if args[:version]
58
106
  puts "whale.rb version #{MAJOR_VERSION}.#{MINOR_VERSION}.#{REVISION}"
59
107
  exit
60
108
  end
109
+
110
+ paths = []
111
+ paths += ENV['WHALEPATH'].split(':') if ENV['WHALEPATH']
112
+ paths += args[:paths].split(':') if args[:paths]
113
+
114
+ logger.debug(paths)
115
+
116
+ paths.each { |p| args[:files] += \
117
+ list_files_in_path(p, args[:recursive], logger) }
118
+
119
+ logger.debug(args[:files])
120
+
61
121
  if args[:help] or args[:files].empty?
62
122
  puts USAGE
63
123
  puts HELP if args[:help]
64
124
  exit
65
125
  end
126
+
66
127
  entries = []
67
- args[:files].each { |f| entries += parse_file(f) }
68
- puts "Parsed #{args[:files].length} files and #{entries.length} entries"
128
+ args[:files].each do |file|
129
+ begin
130
+ entries += parse_file(file, logger)
131
+ rescue Errno::ENOENT
132
+ logger.warn("File not found: #{file}")
133
+ rescue Errno::EISDIR
134
+ logger.warn("#{file} is a directory. Use -p to specify paths")
135
+ end
136
+ end
137
+
138
+ logger.info("Parsed #{args[:files].length} files " +
139
+ "and #{entries.length} entries")
140
+
69
141
  if args[:filter]
70
142
  filter = Filter.new
71
- filter.parse_filter args[:filter]
72
- filter_entries(entries, filter)
143
+ filter.pgrse_filter(args[:filter], logger)
144
+ filter_entries(entries, filter, logger)
73
145
  end
74
- sort_entries_by(entries, args[:sort]) if args[:sort]
146
+
147
+ sort_entries_by(entries, args[:sort], logger) if args[:sort]
148
+
75
149
  if args[:edit]
76
150
  i = args[:edit].to_i - 1
77
151
  e = entries[i]
@@ -79,15 +153,27 @@ if args[:edit]
79
153
  puts "Invalid ID"
80
154
  exit
81
155
  end
82
- open_editor(ENV['EDITOR'].to_sym, e.tags[:file], e.tags[:line])
156
+ open_editor(ENV['EDITOR'].to_sym, e.tags[:file], e.tags[:line], logger)
157
+ end
158
+
159
+ if args[:name] and !args[:write]
160
+ list_tags get_all_tags(entries)
83
161
  end
84
162
 
85
163
  write_entries(entries) if args[:write]
86
164
 
87
- if !args[:write]
165
+ tags_to_list = [:title, :date, :tags]
166
+ tags_format = [45, 10, 24]
167
+
168
+ if args[:tags]
169
+ tags = args[:tags].split(' ')
170
+ tags.each do |t|
171
+ tags_to_list << t.to_sym
172
+ tags_format << 24
173
+ end
174
+ end
175
+
176
+ if !args[:write] and !args[:name]
88
177
  all_tags = get_all_tags entries
89
- debug(list_tags(all_tags))
90
- tags_to_list = [:title, :date, :tags]
91
- tags_format = [45, 10, 25]
92
178
  list_entries(entries, tags_to_list, tags_format)
93
179
  end
@@ -1,26 +1,12 @@
1
1
  # File: whale.rb
2
2
 
3
3
  require 'set'
4
-
5
- DEBUG = true
4
+ require 'logger'
6
5
 
7
6
  MAJOR_VERSION = 0
8
- MINOR_VERSION = 0
7
+ MINOR_VERSION = 1
9
8
  REVISION = 0
10
9
 
11
-
12
- def debug(msg)
13
- puts msg if DEBUG
14
- end
15
-
16
- def error(msg)
17
- puts "Error: #{msg}"
18
- end
19
-
20
- def warning(msg)
21
- puts "Warning: #{msg}"
22
- end
23
-
24
10
  $DEFAULT_TAGS = [:title, :body, :line, :file, :tags]
25
11
 
26
12
  class Entry
@@ -45,10 +31,9 @@ class Filter
45
31
 
46
32
  # For regex options (e.g. ignorecase) use the (?opt:source) notation,
47
33
  # e.g. /(?i-mx:hEllo .*)/
48
- def parse_filter(f)
49
- debug("parsing #{f}")
34
+ def parse_filter(f, logger)
35
+ logger.debug("parsing #{f}")
50
36
  regex = false
51
- quote = nil
52
37
  escape = false
53
38
  # literal = false
54
39
  token = 0
@@ -56,34 +41,22 @@ class Filter
56
41
  (0...f.length).each do |i|
57
42
  if f[i] == '\\' or escape
58
43
  escape ^= true
59
- elsif f[i] == '"' or f[i] == "'"
60
- unless regex
61
- if quote == f[i]
62
- stack << f[token, i - token]
63
- quote = nil
64
- else
65
- token = i + 1
66
- quote = f[i]
67
- end
68
- end
69
44
  elsif f[i] == '/'
70
- unless quote
71
- if regex
72
- stack << Regexp.new(f[token, i - token])
73
- regex = false
74
- else
75
- token = i + 1
76
- regex = true
77
- end
45
+ if regex
46
+ stack << Regexp.new(f[token, i - token])
47
+ regex = false
48
+ else
49
+ token = i + 1
50
+ regex = true
78
51
  end
79
- elsif !regex and !quote
52
+ elsif !regex
80
53
  stack << :FILTER_AND if f[i] == '&'
81
54
  stack << :FILTER_OR if f[i] == '|'
82
55
  stack << :FILTER_NOT if f[i] == '*'
83
56
  stack << :FILTER_EQ if f[i] == '='
84
57
  end
85
58
  end
86
- debug(stack)
59
+ logger.debug(stack)
87
60
  @stack = stack
88
61
  end
89
62
 
@@ -96,7 +69,7 @@ class Filter
96
69
  end
97
70
 
98
71
  # apply the stack to the entry tags
99
- def filter(entry)
72
+ def filter(entry, logger)
100
73
  s = []
101
74
  last_tags = []
102
75
  eq = false
@@ -108,13 +81,13 @@ class Filter
108
81
  when :FILTER_EQ then eq = true
109
82
  else
110
83
  if eq
111
- debug("last tags: #{last_tags}")
84
+ logger.debug("last tags: #{last_tags}")
112
85
  match = false
113
86
  s.pop
114
87
  last_tags.each do |u|
115
88
  if t.match entry.tags[u]
116
89
  match = true
117
- debug("#{t} matches #{entry.tags[u]}")
90
+ logger.debug("#{t} matches #{entry.tags[u]}")
118
91
  break
119
92
  end
120
93
  end
@@ -126,18 +99,18 @@ class Filter
126
99
  end
127
100
  end
128
101
  end
129
- debug(s)
130
- warning("malformed filter") if s.length != 1
102
+ logger.debug(s)
103
+ logger.warn("Malformed filter") if s.length != 1
131
104
  return s.first
132
105
  end
133
106
 
134
107
  end
135
108
 
136
- def filter_entries(entries, filter)
137
- entries.delete_if { |a| !filter.filter(a) }
109
+ def filter_entries(entries, filter, logger)
110
+ entries.delete_if { |a| !filter.filter(a, logger) }
138
111
  end
139
112
 
140
- def sort_entries_by(entries, tag)
113
+ def sort_entries_by(entries, tag, logger)
141
114
  return entries.sort { |a, b| a.tags[tag] <=> b.tags[tag] }
142
115
  end
143
116
 
@@ -148,8 +121,8 @@ $EDITOR_CMDS = {
148
121
  }
149
122
  $EDITOR_CMDS.default = "ed %<file>s"
150
123
 
151
- def open_editor(editor, path, lineno)
152
- debug("opening at #{lineno}")
124
+ def open_editor(editor, path, lineno, logger)
125
+ logger.debug("opening at #{lineno}")
153
126
  args = {line: lineno, file: path}
154
127
  cmd = $EDITOR_CMDS[editor] % args
155
128
  exec(cmd)
@@ -230,36 +203,46 @@ def list_tags(tags)
230
203
  puts s.slice(0, s.length - 2)
231
204
  end
232
205
 
206
+ # List files in the path with the given extension
207
+ def list_files_in_path(path, recursive, logger)
208
+ file_glob = '*.{wl,whale}'
209
+ if recursive
210
+ glob_path = File.join(path, File.join('**', file_glob))
211
+ else
212
+ glob_path = File.join(path, file_glob)
213
+ end
214
+ return Dir.glob(glob_path)
215
+ end
233
216
 
234
217
  EMPTY_LINE = /\A\s*\Z/
235
218
  LABEL_LINE = /\A;(.*)\Z/
236
219
 
237
220
  # Extract entries from file.
238
- # param @file String the path of the file to parse
239
- # return Array the array of Entry
240
- def parse_file(file)
221
+ # param @file a file name to read
222
+ # return Array an array of Entry
223
+ def parse_file(file, logger)
241
224
  entries = []
242
225
  file_entry = Entry.new
243
- File.open(file, "r") do |f|
244
- entry = nil
245
- is_reading_tag = true
246
- lineno = 0
226
+ entry = nil
227
+ is_reading_tag = true
228
+ lineno = 0
229
+ File.open(file, 'r') do |f|
247
230
  f.each_line do |line|
248
231
  lineno += 1
249
232
  # skip if the line is whitespace
250
233
  next if EMPTY_LINE.match line
251
234
  if (m = LABEL_LINE.match line)
252
- debug("#{f.path}, #{lineno}, reading tag")
235
+ logger.debug("#{f.path}, #{lineno}, reading tag")
253
236
  is_reading_tag = true
254
237
  matched_line = m[1]
255
238
  if entry.nil?
256
- debug("adding file entry tags #{matched_line}")
239
+ logger.debug("adding file entry tags #{matched_line}")
257
240
  parse_tags file_entry, matched_line
258
241
  else
259
242
  parse_tags entry, matched_line
260
243
  end
261
244
  elsif is_reading_tag
262
- debug("#{f.path}, #{lineno}, new entry")
245
+ logger.debug("#{f.path}, #{lineno}, new entry")
263
246
  is_reading_tag = false
264
247
  entries << entry if !entry.nil?
265
248
  entry = Entry.new
@@ -271,10 +254,10 @@ def parse_file(file)
271
254
  end
272
255
  end
273
256
  entries << entry if !entry.nil?
274
- puts "Last entry is missing tag" if !is_reading_tag
257
+ logger.warn("#{file} missing last entry tags") if !is_reading_tag
275
258
  end
276
259
  # add the file level tags to each entry
277
- debug(file_entry.tags)
260
+ logger.debug(file_entry.tags)
278
261
  entries.each { |e| e.tags = file_entry.tags.merge(e.tags) }
279
262
  return entries
280
263
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whale
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryutaro Ikeda