notes-cli 1.1.0 → 2.0.0

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