what_now 0.0.5 → 0.0.6

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c129b4ea1aed4491608b1aa54462eb469f06f69b
4
- data.tar.gz: 25a68c0f5b0fd9048c495cdb18447050f846a43f
3
+ metadata.gz: 544af77f51167e74302cbd4bf98ca400d2b9a695
4
+ data.tar.gz: d89bba664223614756de397c960463218e483f36
5
5
  SHA512:
6
- metadata.gz: 16dec7d805e9ba4759b3b2835b597e7e31f57bed8828b74dbd3d00d8a8116db1e0d1425e881e12a1027c06aa962ea51c73ad3386abdf8fb3b5748107cab4652a
7
- data.tar.gz: 73ef733a215b66af92f7d3fadbc9b9deaed33be14f273480a4601273d2650a289ae05a29f17969e510e9e4a6855e08afb9c61203466ee2706fb8846cb1acb739
6
+ metadata.gz: 5f584e61ff6da4c4d6fcd99da6740086bff4cf0e3ffd10ef490b0268d6aed26d2bce98eee9a9586b0bf87874eaa23a552d5fa2a347c23e637c87944c02734c3c
7
+ data.tar.gz: 51ccf2fb7e922057fe0fdd0d2cce6c35228bcace881fef56572c87d0ccdbdfe967fbbc1c716ebb1288a58151e5709828211e93d8e55a6e9c39da1fdce85a2695
data/README.md CHANGED
@@ -26,10 +26,18 @@ Will return all the todos in the following format inside files:
26
26
 
27
27
  * TODO this is the text
28
28
 
29
- Where 'this is the text' will be returned, together with the path and line number
30
-
31
- You can also use the options *--dir, -d*, in order to specify the directory in which
32
- todos are going to be looked for, and *--ext -e* for checking only certain file extension
33
- (without prepending the '.', this means, call *--ext rb* instead of *--ext .rb*).
34
- You can optionally ignore casing for your *TODO* comments, in which case you may
35
- pass the option *--ignorecase, -i*.
29
+ Where 'this is the text' will be returned, together with the relative path and line number
30
+
31
+ The output is colored, but if redirected to a non TTY device, a simpler, colorless format
32
+ will be used.
33
+
34
+ Options
35
+ -------
36
+ * *--dir, -d*: Specify the directory in which to search the TODO's. It can be an absolute
37
+ path of anyhwere in the system, or a relative path to the current directory.
38
+ * *--ext, -e*: Specify the extension of the files to consider. You can pass it with or without
39
+ the dot (.rb or rb are both valid).
40
+ * *--regex, -r*: Specify the regular expression (Perl-like) that the file names
41
+ must match.
42
+ * *--ignorecase, -i*: When passed, this options matches both the strings *TODO* and *todo*.
43
+ By default, this options is deactivated.
data/bin/wnow CHANGED
@@ -2,21 +2,37 @@
2
2
  require 'thor'
3
3
  require 'colorize'
4
4
  require 'what_now'
5
+ require 'file_filter'
6
+ require 'pathname'
5
7
 
6
8
  class Wnow < Thor
9
+ include Filtering
7
10
  default_task :find
8
11
 
9
12
  desc 'find', 'search files in directory for todo elements (default command)'
10
13
  option :dir, aliases: '-d'
11
14
  option :ext, aliases: '-e'
15
+ option :regex, aliases: '-r'
12
16
  option :ignorecase, aliases: '-i'
13
17
  def find
18
+ # Directory handling
19
+ current_dir = Pathname.getwd
14
20
  dir = options[:dir] || Dir.pwd
15
- ext = options[:ext] ? "/**/*.#{options[:ext]}" : '/**/*.*'
16
- creator = TodoCreator.new(
17
- ignorecase: options[:ignorecase],
18
- pretty: STDOUT.tty?)
19
- TodoFinder.new(dir + ext, creator).find.each do |todo|
21
+ absolute_dir = Pathname.new(dir).expand_path.to_s
22
+
23
+ filter = NoDirectories
24
+ .chain(NoBinaries)
25
+ .chain(OnlyDirectory.new absolute_dir)
26
+ filter.chain(OnlyExtension.new options[:ext]) if options[:ext]
27
+ filter.chain(MatchRegex.new options[:regex]) if options[:regex]
28
+
29
+ relative_paths = Dir[absolute_dir + '/**/*'].map do |p|
30
+ Pathname.new(p).relative_path_from(current_dir).to_s
31
+ end
32
+ targets = filter.filter relative_paths
33
+
34
+ creator = TodoCreator.new(ignorecase: options[:ignorecase], pretty: STDOUT.tty?)
35
+ TodoFinder.new(targets, creator).find.each do |todo|
20
36
  puts todo
21
37
  puts if STDOUT.tty?
22
38
  end
@@ -0,0 +1,73 @@
1
+ # Module for handling filtering of
2
+ # files
3
+
4
+ require 'ptools'
5
+ require 'pathname'
6
+
7
+ module Filtering
8
+ class Filter
9
+ attr_reader :predicates
10
+
11
+ def initialize &predicate
12
+ @predicates = [predicate]
13
+ end
14
+
15
+ def apply file
16
+ @predicates.reduce(true) do |result, p|
17
+ result && p.call(file)
18
+ end
19
+ end
20
+
21
+ def chain other=nil
22
+ raise ArgumentError if (not other and not block_given?)
23
+ @predicates += other.predicates if other
24
+ @predicates << Proc.new if block_given?
25
+ self
26
+ end
27
+
28
+ def filter files
29
+ files.keep_if do |f|
30
+ self.apply f
31
+ end
32
+ end
33
+ end
34
+
35
+ NoDirectories = Filter.new do |path|
36
+ not File.directory? path
37
+ end
38
+
39
+ NoBinaries = Filter.new do |path|
40
+ not File.binary? path
41
+ end
42
+
43
+ class OnlyDirectory < Filter
44
+ def initialize subdirectory
45
+ base = Pathname.new(subdirectory).expand_path.to_s
46
+ @predicates = []
47
+ @predicates << proc do |path|
48
+ file = Pathname.new(path).expand_path.to_s
49
+ file.start_with? base
50
+ end
51
+ end
52
+ end
53
+
54
+ class OnlyExtension < Filter
55
+ def initialize ext
56
+ extension = ext.start_with?('.') ? ext[1..-1] : ext
57
+ @predicates = []
58
+ @predicates << proc do |path|
59
+ path.match(/\.#{extension}$/i) ? true : false
60
+ end
61
+ end
62
+ end
63
+
64
+ class MatchRegex < Filter
65
+ def initialize regex
66
+ @predicates = []
67
+ @predicates << proc do |path|
68
+ path.match(regex) ? true : false
69
+ end
70
+ end
71
+ end
72
+ end
73
+
data/lib/what_now.rb CHANGED
@@ -13,22 +13,15 @@ class TodoCreator
13
13
  def match(line, path, line_number)
14
14
  regex = @ignorecase ? /TODO:?\s*(.+)$/i : /TODO:?\s*(.+)$/
15
15
  text = regex.match(line)
16
- @todo_class.new(text[1], shortened_path(path), line_number) if text
16
+ @todo_class.new(text[1], path, line_number) if text
17
17
  rescue ArgumentError
18
18
  nil
19
19
  end
20
-
21
- private
22
- def shortened_path(path)
23
- path[Dir.pwd.length+1..path.length]
24
- end
25
20
  end
26
21
 
27
22
  class TodoFinder
28
- def initialize(pattern, creator)
29
- @paths = Dir[pattern].delete_if do |path|
30
- File.directory?(path) || File.binary?(path)
31
- end
23
+ def initialize(paths, creator)
24
+ @paths = paths
32
25
  @creator = creator
33
26
  end
34
27
 
@@ -0,0 +1,188 @@
1
+ require_relative 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require 'fakefs/safe'
5
+
6
+ module FakeFS
7
+ class File
8
+ def self.binary? file
9
+ file == '/sample/d2/f3'
10
+ end
11
+ end
12
+ end
13
+
14
+ FileFilter = Filtering::Filter
15
+
16
+ describe FileFilter do
17
+ subject do
18
+ FileFilter.new do |file|
19
+ true
20
+ end
21
+ end
22
+
23
+ describe 'public interface' do
24
+ it 'must respond to apply' do
25
+ subject.must_respond_to :apply
26
+ end
27
+
28
+ it 'must respond to chain' do
29
+ subject.must_respond_to :chain
30
+ end
31
+
32
+ it 'must respond to filter' do
33
+ subject.must_respond_to :filter
34
+ end
35
+ end
36
+
37
+ describe '#apply' do
38
+ it 'is expected to return result of predicate' do
39
+ subject.apply(nil).must_equal true
40
+ end
41
+ end
42
+
43
+ describe '#chain' do
44
+ subject do
45
+ FileFilter.new do |x|
46
+ x > 0
47
+ end.chain do |x|
48
+ x % 2 == 0
49
+ end
50
+ end
51
+
52
+ it 'returns an instance of FileFilter' do
53
+ subject.must_be_instance_of FileFilter
54
+ end
55
+
56
+ it 'applies all filters when called' do
57
+ subject.filter([-2, -1, 0, 1, 2, 3, 4]).must_equal [2, 4]
58
+ end
59
+
60
+ describe 'with no arguments' do
61
+ it 'raises an exception' do
62
+ proc do
63
+ FileFilter.new do
64
+ true
65
+ end.chain
66
+ end.must_raise ArgumentError
67
+ end
68
+ end
69
+
70
+ describe 'with instance of filter' do
71
+ subject do
72
+ f1 = FileFilter.new do |f|
73
+ f % 2 == 0
74
+ end
75
+ FileFilter.new do |f|
76
+ f > 0
77
+ end.chain f1
78
+ end
79
+
80
+ it 'returns an instance of Filter' do
81
+ subject.chain(FileFilter.new { |x| x > 0 }).must_be_instance_of FileFilter
82
+ end
83
+
84
+ it 'applies all filters when called' do
85
+ [-2, -1, 0, 1, 2, 3, 4].keep_if do |n|
86
+ subject.apply(n)
87
+ end.must_equal [2, 4]
88
+ end
89
+ end
90
+ end
91
+
92
+ describe '#filter' do
93
+ subject do
94
+ FileFilter.new do |x|
95
+ x > 2
96
+ end
97
+ end
98
+
99
+ it 'correctly filters the elements' do
100
+ subject.filter([-2, -1, 0, 1, 2, 3, 4]).must_equal [3, 4]
101
+ end
102
+ end
103
+ end
104
+
105
+ describe 'specific filters' do
106
+ before do
107
+ FakeFS.activate!
108
+ FileUtils.mkdir_p 'sample/d1'
109
+ FileUtils.mkdir_p 'sample/d2'
110
+ FileUtils.touch 'sample/d1/f1.rb'
111
+ FileUtils.touch 'sample/d1/f2_rb'
112
+ FileUtils.touch 'sample/d2/f3'
113
+ end
114
+
115
+ after do
116
+ FakeFS.deactivate!
117
+ end
118
+
119
+ let :paths do
120
+ Dir['sample/**/*']
121
+ end
122
+
123
+ describe 'directory filter' do
124
+ subject do
125
+ Filtering::NoDirectories
126
+ end
127
+
128
+ it 'only returns files' do
129
+ subject.filter(paths).sort!.must_equal ['/sample/d1/f1.rb', '/sample/d1/f2_rb', '/sample/d2/f3'].sort!
130
+ end
131
+ end
132
+
133
+ describe 'binary filter' do
134
+ subject do
135
+ Filtering::NoBinaries
136
+ end
137
+
138
+ it 'only returns non-binaries' do
139
+ subject.filter(paths).wont_include '/sample/d2/f3'
140
+ end
141
+ end
142
+
143
+ describe 'directory filter' do
144
+ subject do
145
+ Filtering::OnlyDirectory.new '/sample/d1'
146
+ end
147
+
148
+ it 'only returns elements in given subdirectory' do
149
+ subject.filter(paths).sort!.must_equal ['/sample/d1', '/sample/d1/f1.rb', '/sample/d1/f2_rb'].sort!
150
+ end
151
+ end
152
+
153
+ describe 'extension filter' do
154
+ subject do
155
+ Filtering::OnlyExtension.new 'rb'
156
+ end
157
+
158
+ it 'only returns elements with the given extension' do
159
+ subject.filter(paths).sort!.must_equal ['/sample/d1/f1.rb']
160
+ end
161
+
162
+ it 'still returns extension when extension specified with dot' do
163
+ Filtering::OnlyExtension.new('.rb').filter(paths).must_equal ['/sample/d1/f1.rb']
164
+ end
165
+
166
+ it 'does not match a file that ends with same letter as extension' do
167
+ subject.filter(paths).wont_include '/sample/d1/f2_rb'
168
+ end
169
+
170
+ it 'treats the extension case insensitively' do
171
+ Filtering::OnlyExtension.new('RB').filter(paths).must_equal ['/sample/d1/f1.rb']
172
+ end
173
+ end
174
+
175
+ describe 'regex filter' do
176
+ subject do
177
+ Filtering::MatchRegex.new(/f[0-9]/)
178
+ end
179
+
180
+ it 'only returns elements that match regular expression' do
181
+ subject.filter(paths).sort!.must_equal ['/sample/d1/f1.rb', '/sample/d1/f2_rb', '/sample/d2/f3'].sort!
182
+ end
183
+
184
+ it 'accepts a string as a regular expression' do
185
+ Filtering::MatchRegex.new('f[0-9]').filter(paths).sort!.must_equal ['/sample/d1/f1.rb', '/sample/d1/f2_rb', '/sample/d2/f3'].sort!
186
+ end
187
+ end
188
+ end
@@ -56,8 +56,8 @@ end
56
56
  describe TodoFinder do
57
57
  describe 'search single file' do
58
58
  subject do
59
- path = File.dirname(__FILE__) + '/example_file.txt'
60
- TodoFinder.new(path, TodoCreator.new).find
59
+ paths = Dir[File.dirname(__FILE__) + '/example_file.txt']
60
+ TodoFinder.new(paths, TodoCreator.new).find
61
61
  end
62
62
 
63
63
  it 'found 2 todos' do
@@ -74,15 +74,17 @@ describe TodoFinder do
74
74
 
75
75
  describe 'search with a pattern' do
76
76
  it 'considers only specified pattern' do
77
+ paths = Dir[File.dirname(__FILE__)+'/**/*.txt']
77
78
  results = TodoFinder.new(
78
- File.dirname(__FILE__)+'/**/*.txt',
79
+ paths,
79
80
  TodoCreator.new).find
80
81
  results.length.must_equal 2
81
82
  end
82
83
 
83
84
  it 'returns empty array if nothing was found' do
85
+ paths = Dir[File.dirname(__FILE__)+'/**/*.mooo']
84
86
  results = TodoFinder.new(
85
- File.dirname(__FILE__)+'/**/*.mooo',
87
+ paths,
86
88
  TodoCreator.new).find
87
89
  results.must_equal []
88
90
  end
data/what_now.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'what_now'
3
- s.version = '0.0.5'
3
+ s.version = '0.0.6'
4
4
  s.date = '2014-02-27'
5
5
  s.summary = 'Find todo comments in your code'
6
6
  s.description = 'Executable for finding todo comments on directories'
@@ -19,4 +19,5 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.add_development_dependency 'minitest-reporters'
21
21
  s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'fakefs'
22
23
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: what_now
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edgar Cabrera
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fakefs
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: Executable for finding todo comments on directories
84
98
  email: edgar@cafeinacode.com
85
99
  executables:
@@ -90,9 +104,11 @@ files:
90
104
  - LICENSE
91
105
  - README.md
92
106
  - bin/wnow
107
+ - lib/file_filter.rb
93
108
  - lib/todo.rb
94
109
  - lib/what_now.rb
95
110
  - spec/example_file.txt
111
+ - spec/file_filter_spec.rb
96
112
  - spec/spec_helper.rb
97
113
  - spec/todo_spec.rb
98
114
  - spec/what_now_spec.rb
@@ -126,3 +142,4 @@ test_files:
126
142
  - spec/example_file.txt
127
143
  - spec/spec_helper.rb
128
144
  - spec/todo_spec.rb
145
+ - spec/file_filter_spec.rb