notes-cli 1.1.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NDVlYWYwYTAwMGM0ZDVmMTlkMTYzOTFkZGM4NDk2NDRkYTgxMzIwMQ==
5
- data.tar.gz: !binary |-
6
- ZTY5ZGIxZjFhNDYxMzYxNmJlYTZjMWI2YWYxNDFkNjZkOTc3OGY1Yw==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- MGQ4NTExODhiZmYwYTk1NjI5N2JmMzRmYTFjMGI1YzdmOWVhZTQwYTg1NDMz
10
- ZjgyMTI4ZTgxN2U5MTY4NzNkOWMyODMwODkwYTM2NDkyYjcyZjYzZGU2YjUx
11
- MTgxZTEzZjk4YzI0MTUzY2Y1NzcxOGQyMDgxMTBlMTgyYTUyYzY=
12
- data.tar.gz: !binary |-
13
- YTIwZjcwOGZjYTQyZTlhYjBkNGVlNmZlOTcwZDZhZDU3NmJhZjlhNmY5Y2Ri
14
- MzQ5YzE0Y2U4ZmQ0YjU2YWEwYzQ1ZWUxYTUzNDU5NTQzOWY5YjYwNDA2ZWZm
15
- ZjI2MzVkYzNlMWQ5MzQ5YWVkMGMzNWFjMDc5MjdjN2M2NWExZTk=
2
+ SHA1:
3
+ metadata.gz: 8226fe40296b703c5bfd3e60e83ef9cb29bc4a18
4
+ data.tar.gz: 1a01f8184c299f371ba8e1483214981a5da02184
5
+ SHA512:
6
+ metadata.gz: 78a371528ef86d9288193d841b06810a17162f8a55573ad5962fc313b8d1460eaa1333fb65a89f34acfca11c2450484c161ad1b487daed2badcacf8cc2240ac6
7
+ data.tar.gz: 8bcfa278c5f87e1e2eca7f741e9151176fd13c5cadbd1391a634fb0f028e3ca730f25c815cd36764500da5e4300417808d6495d98840bc5862b62963b0c30b18
data/.gitignore ADDED
@@ -0,0 +1,44 @@
1
+ # Numerous always-ignore extensions
2
+ *.diff
3
+ *.err
4
+ *.orig
5
+ *.log
6
+ *.rej
7
+ *.swo
8
+ *.swp
9
+ *.vi
10
+ *~
11
+ *.sass-cache
12
+
13
+ # OS or Editor folders
14
+ .DS_Store
15
+ .cache
16
+ .project
17
+ .settings
18
+ .tmproj
19
+ nbproject
20
+ Thumbs.db
21
+
22
+ # Dreamweaver added files
23
+ _notes
24
+ dwsync.xml
25
+
26
+ # Komodo
27
+ *.komodoproject
28
+ .komodotools
29
+
30
+ # Folders to ignore
31
+ .hg
32
+ .svn
33
+ .CVS
34
+ intermediate
35
+ publish
36
+ .idea
37
+ .bundle
38
+ db/*.sqlite3
39
+ log/*.log
40
+ tmp/
41
+ .sass-cache/
42
+ dump.rdb
43
+ *.gem
44
+ Makefile
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ notes-cli
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sourcing.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright © 2013 Andrew Berls, http://andrewberls.com/
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the “Software”), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ ## Notes - A tool for managing source code annotations
2
+
3
+ Notes is a tool for tracking source code annotations such as TODO or FIXME. A command-line interface searches files
4
+ in a directory and prints annotations, while a web interface provides visualization and filtering tools.
5
+
6
+
7
+ Default annotations searched for are: __TODO__, __FIXME__, and __OPTIMIZE__. Custom annotations or directories to ignore (such as log directories) can be specified with command-line arguments, detailed further.
8
+
9
+
10
+ ### INSTALLATION:
11
+ `gem install notes-cli`
12
+
13
+ This will install the `notes` executable on your system.
14
+
15
+ ## Using the web interface
16
+
17
+ Notes ships with a web interface for displaying and filtering annotations. It can be run as a standalone server,
18
+ or mounted as a Rack endpoint within another application (e.g., a Rails app)
19
+
20
+ ![](https://dl.dropboxusercontent.com/u/7949088/notes-cli/notes-web2.png)
21
+
22
+ ### As a standalone server:
23
+
24
+ Once the gem is installed, you can start a server with the `notes server` command, run from the directory
25
+ you wish to search in. The port can be customized with the `-p` flag (e.g. `notes server -p 8000`).
26
+ The default port is 9292.
27
+
28
+ ### Mounted in a Rails application:
29
+
30
+ Notes can expose its web interface as part of a host application. First, add `notes-cli` as a dependency in your Gemfile and run `bundle install`. Next, add the following to `config/routes.rb`:
31
+
32
+ ```ruby
33
+ require 'notes-cli/web'
34
+
35
+ mount Notes::Web => '/notes'
36
+ ```
37
+
38
+ Now, after starting a server normally, you can browse to `'/notes'` (or whichever URL you chose) in your application
39
+ to access the web interface.
40
+
41
+
42
+ ## Using the CLI
43
+
44
+ Usage: `notes [DIRECTORY=. | FILES=<...>] [-f FLAGS] [-e EXCLUDES]`
45
+
46
+ ### OPTIONS:
47
+ ```
48
+ -f, --flags # List of custom annotations, ex: '-f broken refactor' (case insensitive)
49
+ -e, --exclude # List of directories to ignore, ex: '-e tmp/ log/'
50
+ -h, --help # Display the help menu
51
+ ```
52
+
53
+ ### EXAMPLES:
54
+ ```
55
+ notes # Show default annotations for all files in current directory (default)
56
+ notes app/ -f broken # Only examine files in the app/ directory and add the 'broken' flag
57
+ notes -e tmp/ log/ # Ignore any files in tmp/ or log/
58
+ notes one.rb two.rb # Show default annotations for one.rb and two.rb
59
+ ```
60
+
61
+ A sample run might look like the following:
62
+ ```
63
+ $ notes src/ -f failing
64
+
65
+ app/models/user.rb:
66
+ ln 2: # TODO: Condense this eventually
67
+ ln 34: # OPTIMIZE: This can be prettier
68
+
69
+ test/unit/group.rb
70
+ ln 72: # FAILING
71
+ ```
data/bin/notes CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  require 'notes-cli'
4
4
 
5
+ if ARGV.any? { |arg| ["-v", "--version"].include?(arg) }
6
+ puts Notes::VERSION
7
+ exit
8
+ end
9
+
5
10
  if ARGV.any? { |arg| ["-h", "--help"].include?(arg) }
6
11
  puts %Q{\
7
12
  Recursively search source files for annotations. Default annotations are TODO, OPTIMIZE, and FIXME.
@@ -12,6 +17,7 @@ Options:
12
17
  -f, --flags # List of custom annotations, ex: '-f broken refactor' (case insensitive)
13
18
  -e, --exclude # List of directories to ignore, ex: '-e tmp/ log/'
14
19
  -h, --help # Display this menu
20
+ -v, --version # Display the currently installed version
15
21
 
16
22
  Examples:
17
23
  notes # Show default annotations for all files in current directory (default)
@@ -19,8 +25,18 @@ Examples:
19
25
  notes app/ -e logs/ # Show default annotations for files in app/ directory, excluding files in logs/
20
26
  notes one.rb two.rb # Show default annotations for one.rb and two.rb
21
27
 
28
+ You can also start a standalone server for the web interface with `notes server`
29
+ The default port is 9292.
30
+
31
+ Server options:
32
+ -p, --port # Set the port to run on, ex: `notes server -p 3000`
33
+
22
34
  }
23
35
  exit(0)
24
36
  end
25
37
 
26
- Notes::CLI.new.find_all
38
+ if ARGV.first == 'server'
39
+ Notes::Server.new(ARGV).start
40
+ else
41
+ Notes::CLI.new(ARGV).find_all
42
+ end
data/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../lib/notes-cli', __FILE__)
2
+ require File.expand_path('../lib/notes-cli/web', __FILE__)
3
+
4
+ run Notes::Web
data/lib/notes-cli.rb CHANGED
@@ -1,2 +1,112 @@
1
- require 'notes-cli/opts'
1
+ module Notes
2
+ extend self
3
+
4
+ # The root directory in which we're searching
5
+ def root
6
+ defined?(Rails) ? Rails.root : Dir.pwd
7
+ end
8
+
9
+ # Are we being included into a Rails project?
10
+ #
11
+ # TODO: this is pretty hacky but we can't rely on the definition
12
+ # of the Rails constant from the CLI and it works on both 3 & 4
13
+ def rails?
14
+ path = root.to_s + '/config/application.rb'
15
+ return false unless File.exists?(path)
16
+ File.read(path).include?('Rails::Application')
17
+ end
18
+
19
+ # Are we in a git repo?
20
+ def git?
21
+ Dir.chdir(root) do
22
+ `git status 2>/dev/null`
23
+ return $?.success?
24
+ end
25
+ end
26
+
27
+ # Convert a filename to a relative path
28
+ #
29
+ # filename - String
30
+ #
31
+ # Ex:
32
+ # shortname("/Users/andrew/code/notes-cli/bin/notes")
33
+ # => "bin/notes"
34
+ #
35
+ # Returns String
36
+ def shortname(filename)
37
+ filename.gsub(Dir.pwd, '').gsub(/^\//, '')
38
+ end
39
+
40
+ # Parse raw output from git-blame(1)
41
+ # (results not interpreted except for SHA)
42
+ #
43
+ # Returns Hash
44
+ def blame(filename, line_num)
45
+ fields = {}
46
+
47
+ begin
48
+ Dir.chdir(root) do
49
+ blame = `git blame -L#{line_num},#{line_num} --line-porcelain -- #{filename} 2>/dev/null`.split("\n")
50
+ sha = blame.shift.split(' ').first
51
+ fields['sha'] = sha if sha != '0'*40 # Only use actual commit SHAs
52
+
53
+ blame.each do |line|
54
+ fieldname, *values = line.split(' ')
55
+ fields[fieldname] = values.join(' ')
56
+ end
57
+ end
58
+ rescue
59
+ end
60
+
61
+ fields
62
+ end
63
+
64
+ COLORS = {
65
+ 'yellow' => 33
66
+ }
67
+
68
+ def colorize(color, str)
69
+ "\e[#{COLORS[color]};1m#{str}\033[0m"
70
+ end
71
+
72
+ # Determine if a file handle should be rejected based on type and
73
+ # directories specified in options[:exclude]
74
+ #
75
+ # excluded - Array of directories to exclude from search
76
+ # f - A String filename
77
+ #
78
+ # Return Boolean
79
+ def is_directory_or_excluded?(excluded, f)
80
+ is_in_excluded_dir = excluded.any? { |dir| File.dirname(f).include?(dir) }
81
+ File.directory?(f) || is_in_excluded_dir
82
+ end
83
+
84
+ # Return an array of valid filenames for parsing
85
+ #
86
+ # options
87
+ # :locations - Array of files and directories to search
88
+ # :exclude - Array of directories to exclude from search
89
+ #
90
+ # Return Array<String>
91
+ def valid_files(options)
92
+ locations = options[:locations]
93
+ excluded = options[:exclude]
94
+
95
+ locations.flat_map do |loc|
96
+ if File.directory?(loc)
97
+ Dir[ File.join(loc, "**/*") ]
98
+ .reject do |f| is_directory_or_excluded?(excluded, f) end
99
+ else
100
+ loc
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ require 'notes-cli/version'
108
+ require 'notes-cli/options'
109
+ require 'notes-cli/tasks'
110
+ require 'notes-cli/stats'
2
111
  require 'notes-cli/cli'
112
+ require 'notes-cli/server'
data/lib/notes-cli/cli.rb CHANGED
@@ -1,62 +1,20 @@
1
-
2
1
  module Notes
3
2
  class CLI
4
- attr_accessor :options
5
-
6
- def initialize
7
- @options = Opts.parse(ARGV)
8
- end
9
-
10
- # Print a formatted task, with highlighting
11
- def print_task(task)
12
- flag_regex = Regexp.new(@options[:flags].join('|'), true)
13
- color = 33 # yellow
14
- line = task[:line].gsub(flag_regex) do |flag|
15
- "\e[#{color};1m#{flag}\033[0m"
16
- end
17
-
18
- puts " ln #{task[:line_num]}: #{line}"
19
- end
20
-
21
- # Scan a file for annotations and output numbered lines for each
22
- def parse_file(filename)
23
- name = filename.gsub(Dir.pwd, '')
24
- counter = 1
25
- tasks = []
26
-
27
- begin
28
- File.read(filename).each_line do |line|
29
- if @options[:flags].any? { |flag| line =~ /#{flag}/i }
30
- tasks << {
31
- :line_num => counter,
32
- :line => line.strip
33
- }
34
- end
35
- counter += 1
36
- end
37
- rescue
38
- # Error occurred reading the file (ex. invalid byte sequence in UTF-8)
39
- # Move on quietly
40
- end
41
3
 
42
- if !tasks.empty?
43
- name.slice!(0) if name.start_with?("/")
44
- puts "#{name}:"
45
- tasks.each { |t| print_task(t) }
46
- puts ""
47
- end
4
+ def initialize(argv)
5
+ @options = Notes::Options.parse(argv)
48
6
  end
49
7
 
50
8
  # Read and parse all files as specified in the options
9
+ # Prints filenames along with all tasks found per file
10
+ # Only outputs to console; returns nothing
51
11
  def find_all
52
- @options[:locations].each do |loc|
53
- if File.directory?(loc)
54
- Dir[ File.join(loc, "**/*") ].reject do |f|
55
- File.directory?(f) || @options[:exclude].any? { |dir| File.dirname(f).include?(dir) }
56
- end.each { |f| parse_file(f) }
57
- else
58
- parse_file(loc)
59
- end
12
+ task_map = Notes::Tasks.all(@options).group_by(&:filename)
13
+
14
+ task_map.each do |filename, tasks|
15
+ puts "#{filename}:"
16
+ tasks.each { |task| puts ' ' + task.to_s }
17
+ puts ''
60
18
  end
61
19
  end
62
20
 
@@ -1,5 +1,8 @@
1
+ # Internal options parser
2
+
1
3
  module Notes
2
- module Opts
4
+ module Options
5
+ extend self
3
6
 
4
7
  DEFAULT_OPTIONS = {
5
8
  :flags => %w(TODO FIXME OPTIMIZE),
@@ -12,17 +15,25 @@ module Notes
12
15
  EXCLUDE_FLAGS = ['-e', '--exclude']
13
16
  ALL_FLAGS = FLAG_FLAGS + EXCLUDE_FLAGS
14
17
 
18
+ def default_excludes
19
+ if Notes.rails?
20
+ %w(tmp log)
21
+ else
22
+ []
23
+ end
24
+ end
25
+
15
26
  # Parse ARGV into a directory and list of argument groups
16
27
  # For example, given ['app/', -f', 'refactor', 'broken', '--exclude', 'tmp', 'log']:
17
28
  # => [ ['app/'], ['-f', 'refactor', 'broken'], ['--exclude', 'tmp', 'log'] ]
18
29
  #
19
- def self.arg_groups(args)
30
+ def arg_groups(args)
20
31
  result = []
21
32
  buf = []
22
33
 
23
- # No dir was passed, use current dir
34
+ # No dir was passed, use default
24
35
  if args.empty? || args.first.start_with?('-')
25
- result << [ Dir.pwd ]
36
+ result << [ Notes.root ]
26
37
  end
27
38
 
28
39
  args.each do |arg|
@@ -36,11 +47,12 @@ module Notes
36
47
  result << buf
37
48
  end
38
49
 
39
- # Append any command line arguments to a default set of arguments
40
- def self.parse(args)
50
+ # Append received command line arguments to a default set of arguments
51
+ # Returns Hash
52
+ def parse(args)
41
53
  arg_list = arg_groups(args)
42
- options = DEFAULT_OPTIONS
43
-
54
+ options = DEFAULT_OPTIONS.dup
55
+ options[:exclude] += default_excludes
44
56
  options[:locations] = arg_list.shift
45
57
 
46
58
  arg_list.reject(&:empty?).each do |set|
@@ -48,8 +60,8 @@ module Notes
48
60
  args.map! { |arg| arg.delete("/") } # "log/" => "log"
49
61
 
50
62
  case flag
51
- when '-f', '--flags' then options[:flags].concat(args)
52
- when '-e', '--exclude' then options[:exclude].concat(args)
63
+ when '-f', '--flags' then options[:flags] += args
64
+ when '-e', '--exclude' then options[:exclude] += args
53
65
  else puts "Unknown argument: #{flag}"
54
66
  end
55
67
  end
@@ -57,5 +69,10 @@ module Notes
57
69
  options
58
70
  end
59
71
 
72
+ # Return the default set of flags and locations
73
+ def defaults
74
+ parse({})
75
+ end
76
+
60
77
  end
61
78
  end