rubygrep 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c838b17156364b9342c90840746c64351f30129
4
- data.tar.gz: a423599ac57581506b04012e7175a9d789625b56
3
+ metadata.gz: 8ac2a3d55254f307dc0b56908c0bcd0bc54ade54
4
+ data.tar.gz: e2e6fa9f6846737e61cf85f152adad84c54c0a21
5
5
  SHA512:
6
- metadata.gz: 3fc0f69e927d0ed753cc527f1f22a826456d0f58945af3c9399d82c129839c8629760a8e7037c877a8857288622448e5f0bdf7aec58ee1cbc143e92fe9578b28
7
- data.tar.gz: 1913cc0bb2554f01c6243531f72893cd4534336a923e9a694a79a646b7d5924ee11aa71ad47eedd59cfb0bb8e69498bc289f03a4d66060e196a7374adacc95d4
6
+ metadata.gz: b4ab09265f720b24aa175e4913d52b5c71c28bbfba35a39e7a7efd3c0d0dbf8c6363c72a4532b215e20d4d58952f11d671e3d0b070384bb731e6e579f8495316
7
+ data.tar.gz: 71e1fdee9f2c3cc00feae3b7a5c98a30dc02ef54d654f221d1390dcfb8792731ec8d7f3d3b8f812a3c30a24edb4faa5c4fceec0e1ef5c1d5f5238fa62c6306ab
data/README.md CHANGED
@@ -1,24 +1,20 @@
1
1
  # Rubygrep
2
2
 
3
- TODO: Write a gem description
3
+ Searches for regexp in files and print out strings that match.
4
4
 
5
5
  ## Installation
6
6
 
7
- Add this line to your application's Gemfile:
8
-
9
- gem 'rubygrep'
10
-
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
7
  $ gem install rubygrep
18
8
 
19
9
  ## Usage
20
10
 
21
- TODO: Write usage instructions here
11
+ Usage: rubygrep [options] expression file1 [file2 file3 ...]
12
+ -r, --recursive recursively read directories
13
+ -i, --ignore-case Ignore case when matching strings.
14
+ -v, --invert-selection Invert the sense of matching, to select non-matching lines.
15
+ -n, --line-number Prefix each line of output with the 1-based line number within its input file.
16
+ -H, --with-filename Print the file name for each match. This is the default when there is more than one file to search.
17
+
22
18
 
23
19
  ## Contributing
24
20
 
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygrep'
4
+
5
+ options = Rubygrep::RubyGrepOptions.new(ARGV)
6
+ Rubygrep::GrepManager.new(options).run
@@ -2,6 +2,7 @@ require 'rubygrep/version'
2
2
  require 'rubygrep/grep_manager'
3
3
  require 'rubygrep/ruby_grep_options'
4
4
  require 'rubygrep/file_reader'
5
+ require 'rubygrep/file_searcher'
5
6
  require 'rubygrep/matcher'
6
7
  require 'rubygrep/outputter'
7
8
  require 'optparse'
@@ -10,7 +11,7 @@ module Rubygrep
10
11
 
11
12
  def self.grep
12
13
  options = RubyGrepOptions.new(ARGV)
13
- GrepManager.new(options, options.expression, options.file_names).run
14
+ GrepManager.new(options).run
14
15
  end
15
16
 
16
17
  end
@@ -1,11 +1,15 @@
1
1
  module Rubygrep
2
2
  class FileReader
3
+
3
4
  attr_accessor :file_names, :skip_current_file, :options
4
5
 
5
6
  def initialize(file_names, options = {})
6
7
  @options = options
7
- @file_names = []
8
- process_with_options(file_names)
8
+ @file_names = FileSearcher.new(options).search(file_names)
9
+ end
10
+
11
+ def has_several_files?
12
+ file_names.length > 1
9
13
  end
10
14
 
11
15
  def each_line
@@ -14,6 +18,7 @@ module Rubygrep
14
18
  num = 0
15
19
  if next_file
16
20
  next_file.each_line do |str|
21
+ str = str.encode("UTF-16be", :invalid=>:replace, :replace=>"").encode('UTF-8') unless str.valid_encoding?
17
22
  yield({str: str, path: file_name, str_num: num+=1})
18
23
  if skip_current_file
19
24
  @skip_current_file = false
@@ -30,32 +35,8 @@ module Rubygrep
30
35
 
31
36
  private
32
37
 
33
- def process_with_options(file_names, current_folder = '.')
34
- file_names.each do |file_name|
35
- if File.directory?(file_name) && options[:recursive]
36
- process_with_options(inner_files(file_name), relative_path(file_name, current_folder))
37
- elsif File.file?(file_name)
38
- @file_names << relative_path(file_name, current_folder)
39
- else
40
- puts "No such file or directory #{file_name}"
41
- end
42
- end
43
- end
44
-
45
- def inner_files(folder_name)
46
- Dir.entries(folder_name).delete_if {|file| file =~ /^\./}
47
- end
48
-
49
- def relative_path(file_name, folder)
50
- if file_name =~ /^\\|\./
51
- file_name
52
- else
53
- "#{folder}/#{file_name}"
54
- end
55
- end
56
-
57
38
  def open_file(file_name)
58
- File.open(File.expand_path(file_name, Dir.getwd))
39
+ File.open(file_name)
59
40
  rescue Errno::ENOENT
60
41
  puts "No such file or directory #{file_name}"
61
42
  false
@@ -0,0 +1,47 @@
1
+ module Rubygrep
2
+ class FileSearcher
3
+ attr_accessor :options, :found_file_names
4
+
5
+ def initialize(options = {})
6
+ @options = options
7
+ @found_file_names = []
8
+ end
9
+
10
+ def search(file_names)
11
+ search_with_options(file_names)
12
+ found_file_names
13
+ end
14
+
15
+ private
16
+
17
+ def search_with_options(file_names, current_folder = '.')
18
+ file_names.each do |file_name|
19
+ file_path = relative_path(file_name, current_folder)
20
+ if File.directory?(file_path) && options[:recursive]
21
+ search_with_options(inner_files(file_path), file_path)
22
+ elsif text_file?(file_path)
23
+ found_file_names << file_path
24
+ elsif !File.exists?(file_path)
25
+ puts "No such file or directory #{file_path}"
26
+ end
27
+ end
28
+ end
29
+
30
+ def inner_files(folder_name)
31
+ Dir.entries(folder_name).select {|entry| entry != '.' and entry != '..'}
32
+ end
33
+
34
+ def relative_path(file_name, folder)
35
+ if file_name =~ /^\\/ || folder == '.'
36
+ file_name
37
+ else
38
+ "#{folder}/#{file_name}"
39
+ end
40
+ end
41
+
42
+ def text_file?(file_name)
43
+ File.file?(file_name)
44
+ end
45
+
46
+ end
47
+ end
@@ -2,17 +2,23 @@ module Rubygrep
2
2
  class GrepManager
3
3
  attr_reader :file_reader, :matcher, :outputter
4
4
 
5
- def initialize(options, expression, file_names)
6
- @file_reader = FileReader.new(file_names, options.file_reader_options)
7
- @matcher = Matcher.new(expression, options.matcher_options)
5
+ def initialize(options)
6
+ @file_reader = FileReader.new(options.file_names, options.file_reader_options)
7
+ options.set_multi_file_mode if @file_reader.has_several_files?
8
+ @matcher = Matcher.new(options.expression, options.matcher_options)
8
9
  @outputter = Outputter.new(options.outputter_options)
9
10
  end
10
11
 
11
12
  def run
12
13
  file_reader.each_line do |line_data|
13
- match_data = matcher.matches?(line_data)
14
- if match_data
15
- outputter.out(match_data, line_data)
14
+ begin
15
+ match_data = matcher.matches?(line_data)
16
+ if match_data
17
+ outputter.out(match_data, line_data)
18
+ end
19
+ rescue Exception => e
20
+ file_reader.next_file!
21
+ outputter.error(e.message, line_data)
16
22
  end
17
23
  end
18
24
  end
@@ -1,5 +1,8 @@
1
1
  module Rubygrep
2
2
  class Outputter
3
+
4
+ COLORS = {red: 31, magenta: 35}
5
+
3
6
  attr_accessor :options
4
7
 
5
8
  def initialize(options = {})
@@ -10,16 +13,31 @@ module Rubygrep
10
13
  $stdout << format_with_options(match_data, line_data)
11
14
  end
12
15
 
16
+ def error(message, line_data)
17
+ $stdout << "#{colorize(line_data[:path])}: #{message}\n"
18
+ end
19
+
13
20
  private
14
21
 
15
- # puts line_data[:str_num].to_s + ' ' + line_data[:str].insert(match_data.begin(0),'|').insert(match_data.end(0) + 1,'|')
16
22
  def format_with_options(match_data, line_data)
17
- result = "#{line_data[:str]}"
23
+ result = colorize_match(match_data, "#{line_data[:str]}")
18
24
  if options[:line_number]
19
- result = "#{line_data[:str_num]}: #{result}"
25
+ result = "#{colorize(line_data[:str_num], :magenta)}: #{result}"
26
+ end
27
+ if options[:with_filename]
28
+ result = "#{colorize(line_data[:path], :magenta)}: #{result}"
20
29
  end
21
- result
30
+ result.include?("\n") ? result : "#{result}\n"
22
31
  end
23
32
 
33
+ def colorize(string, color = :red)
34
+ "\033[#{COLORS[color]}m#{string}\033[0m"
35
+ end
36
+
37
+ def colorize_match(match_data, string)
38
+ string.gsub(match_data.regexp) do |match|
39
+ colorize(match)
40
+ end
41
+ end
24
42
  end
25
43
  end
@@ -8,6 +8,10 @@ module Rubygrep
8
8
  parse_options(args)
9
9
  end
10
10
 
11
+ def set_multi_file_mode
12
+ outputter_options[:with_filename] = true
13
+ end
14
+
11
15
  private
12
16
 
13
17
  def parse_options(args)
@@ -24,7 +28,7 @@ module Rubygrep
24
28
 
25
29
  def options_parser
26
30
  OptionParser.new do |opts|
27
- opts.banner = 'Usage: rubygrep.grep [options] expression [file1 file2] ...'
31
+ opts.banner = 'Usage: rubygrep [options] expression file1 [file2 file3 ...]'
28
32
 
29
33
  #file_reader_options
30
34
  opts.on( '-r', '--recursive', 'recursively read directories' ) do
@@ -48,6 +52,10 @@ module Rubygrep
48
52
  opts.on('-n','--line-number','Prefix each line of output with the 1-based line number within its input file.') do
49
53
  outputter_options[:line_number] = true
50
54
  end
55
+
56
+ opts.on('-H','--with-filename', 'Print the file name for each match. This is the default when there is more than one file to search.') do
57
+ outputter_options[:with_filename] = true
58
+ end
51
59
  end
52
60
  end
53
61
 
@@ -1,3 +1,3 @@
1
1
  module Rubygrep
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -9,12 +9,12 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["andrey"]
10
10
  spec.email = ["om08uhkn@gmail.com"]
11
11
  spec.summary = %q{Analog of unix grep utility.}
12
- spec.description = %q{Globally searches regexp and prints it}
12
+ spec.description = %q{Globally searches for in files regexp and prints it}
13
13
  spec.homepage = "https://github.com/fatbeard2/rubygrep"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.executables = 'rubygrep'
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
@@ -1,30 +1,53 @@
1
1
  RSpec.describe Rubygrep::FileReader do
2
2
 
3
- let (:one_file) { 'text1.txt' }
4
- let (:several_files) {%w(text1.txt ./text2.txt ../text3.txt ../folder/text4.txt /root/woop/foo/bar/text5.txt)}
3
+ let (:several_files) {%w(text1.txt ./text2.txt ../text3.txt ../folder/text4.txt /etc/woop/text5.txt)}
4
+ let (:no_access_files) {%w(/root/smth)}
5
+
6
+ before :each do
7
+ allow(File).to receive(:open) do |file_name|
8
+ if several_files.include?(file_name)
9
+ StringIO.new("#{file_name}\n#{file_name}\n#{file_name}\n")
10
+ elsif no_access_files.include?(file_name)
11
+ raise Errno::EACCES
12
+ else
13
+ raise Errno::ENOENT
14
+ end
15
+ end
5
16
 
6
- it 'lookup for one file' do
7
- allow(File).to receive(:file?).with(one_file).and_return(true)
8
- reader = Rubygrep::FileReader.new([one_file])
9
- expect(reader.file_names).to eq(%w(text1.txt))
17
+ allow(File).to receive(:file?) do |file_name|
18
+ several_files.include?(file_name) ||
19
+ no_access_files.include?(file_name)
20
+ end
10
21
  end
11
22
 
12
23
  context 'with several files' do
13
- let (:reader) {Rubygrep::FileReader.new(several_files)}
14
- before do
15
- allow(File).to receive(:file?) { |file_name| several_files.include?(file_name) }
24
+ subject(:reader) { Rubygrep::FileReader.new(several_files) }
25
+
26
+ it 'should read files line by line' do
27
+ reader.each_line do |line_data|
28
+ expect(line_data[:str]).to eq(line_data[:path]+"\n")
29
+ expect(line_data[:str_num]).to be_between(1,3)
30
+ end
16
31
  end
17
32
 
18
- it 'should lookup for several files' do
19
- expect(reader.file_names).to eq(several_files)
33
+ it 'should read all lines' do
34
+ expect { |b| reader.each_line(&b) }.to yield_control.exactly(15).times
20
35
  end
21
36
 
22
- it 'should lookup for several files' do
23
- expect { reader.each_line }.to yield_with_args(several_files)
37
+ it 'should skip files' do
38
+ reader.each_line do |line_data|
39
+ reader.next_file!
40
+ expect(line_data[:str_num]).to eq(1)
41
+ end
24
42
  end
25
43
 
44
+ it 'should respond to has_several_files? message' do
45
+ expect(reader.has_several_files?).to eq(true)
46
+ end
26
47
  end
27
48
 
28
49
 
29
50
 
51
+
52
+
30
53
  end
@@ -0,0 +1,55 @@
1
+ RSpec.describe Rubygrep::FileSearcher do
2
+
3
+ let (:one_file) { 'text1.txt' }
4
+ let (:several_files) {%w(text1.txt ./text2.txt ../text3.txt ../folder/text4.txt /root/woop/foo/bar/text5.txt)}
5
+
6
+ let (:init_dirs) { %w(folder1 /home/user/me) }
7
+ let (:inner_files_tree) {{
8
+ 'folder1' => %w(inner1 inner2 inner3),
9
+ 'folder1/inner1' => %w(inner1.txt inner2.txt),
10
+ 'folder1/inner2' => %w(inner1.txt inner2.txt),
11
+ 'folder1/inner3' => %w(inner1.txt inner2.txt),
12
+ '/home/user/me' => %w(1.txt 2.txt 1 2),
13
+ '/home/user/me/1' => %w(1.txt 2.txt),
14
+ '/home/user/me/2' => %w(1.txt 2.txt)
15
+ }}
16
+ let (:result_files) do
17
+ inner_files_tree.map do |dir, files|
18
+ files.collect { |file_name| "#{dir}/#{file_name}" if file_name =~ /\.txt/ }
19
+ end.flatten.compact
20
+ end
21
+
22
+ def get_entries(dir_name)
23
+ inner_files_tree[dir_name]
24
+ end
25
+
26
+ let (:searcher) { Rubygrep::FileSearcher.new }
27
+ let (:recursive_searcher) { Rubygrep::FileSearcher.new({:recursive => true}) }
28
+
29
+ it 'lookup for one file' do
30
+ allow(File).to receive(:file?).with(one_file).and_return(true)
31
+ expect(searcher.search([one_file])).to eq(%w(text1.txt))
32
+ end
33
+
34
+ context 'with several files' do
35
+ it 'should lookup for several files' do
36
+ allow(File).to receive(:file?) { |file_name| several_files.include?(file_name) }
37
+ expect(searcher.search(several_files)).to eq(several_files)
38
+ end
39
+ end
40
+
41
+ context 'with -recursive option' do
42
+ before do
43
+ allow(File).to receive(:file?) { |file_name| file_name =~ /\.txt/ }
44
+ allow(File).to receive(:directory?) { |dir_name| !(dir_name =~ /\.txt/) }
45
+ allow(Dir).to receive(:entries) { |dir_name| get_entries(dir_name) }
46
+ end
47
+
48
+ it 'should recursivly pick up files' do
49
+ expect(recursive_searcher.search(init_dirs)).to eq(result_files)
50
+ end
51
+ end
52
+
53
+
54
+
55
+ end
@@ -0,0 +1,48 @@
1
+ describe Rubygrep::GrepManager do
2
+ text2 = <<-'HERE'
3
+ Lorem Ipsum is simply dummy text of the printing and
4
+ typesetting industry. Lorem Ipsum has been the industry\'s
5
+ standard dummy text ever since the 1500s, when an unknown
6
+ printer took a galley of type and scrambled it to make a type
7
+ specimen book. It has survived not only five centuries, but also
8
+ the leap into electronic typesetting, remaining essentially unchanged.
9
+ It was popularised in the 1960s with the release of Letraset sheets
10
+ containing Lorem Ipsum passages, and more recently with desktop
11
+ publishing software like Aldus PageMaker including versions of Lorem Ipsum.
12
+ HERE
13
+ text1 = <<-'HERE'
14
+ Contrary to popular belief, Lorem Ipsum is not simply random text
15
+ It has roots in a piece of classical Latin literature from 45 BC, making it over
16
+ 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College
17
+ in Virginia, looked up one of the more obscure Latin words, consectetur, from
18
+ a Lorem Ipsum passage, and going through the cites of the word in classical
19
+ literature, discovered the undoubtable source. Lorem Ipsum comes from
20
+ sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum"
21
+ (The Extremes of Good and Evil) by Cicero, written in 45 BC.
22
+ This book is a treatise on the theory of ethics, very popular
23
+ during the Renaissance. The first line of Lorem Ipsum,
24
+ "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.
25
+ HERE
26
+ match_output = <<-'HERE'
27
+ text1: 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College
28
+ text2: standard dummy text ever since the 1500s, when an unknown
29
+ text2: It was popularised in the 1960s with the release of Letraset sheets
30
+ HERE
31
+
32
+
33
+ let (:files) { %w(text1 text2)}
34
+
35
+ let (:file_contents) {{
36
+ 'text1' => text1,
37
+ 'text2' => text2
38
+ }}
39
+ let (:options) { Rubygrep::RubyGrepOptions.new(%w(\d\d\d\d text1 text2)) }
40
+
41
+ subject(:grep_manager) { Rubygrep::GrepManager.new(options) }
42
+
43
+ it 'should print out matched strings from files' do
44
+ allow(File).to receive(:file?).and_return(true)
45
+ allow(File).to receive(:open) { |file_name| (StringIO.new(file_contents[file_name])) }
46
+ expect { grep_manager.run }.to output(match_output).to_stdout
47
+ end
48
+ end
@@ -15,14 +15,14 @@ RSpec.describe Rubygrep::RubyGrepOptions do
15
15
  end
16
16
 
17
17
  context 'with no additional options' do
18
- subject(:options_object) {create_options(%w(exp text text2))}
18
+ subject(:options_object) {create_options(%w(exp text text1 text2))}
19
19
 
20
20
  it 'should parse expression' do
21
21
  expect(options_object.expression).to eq('exp')
22
22
  end
23
23
 
24
24
  it 'should parse file_names' do
25
- expect(options_object.file_names).to eq(%w(text text2))
25
+ expect(options_object.file_names).to eq(%w(text text1 text2))
26
26
  end
27
27
 
28
28
  it 'should have blank options' do
@@ -30,10 +30,15 @@ RSpec.describe Rubygrep::RubyGrepOptions do
30
30
  expect(options_object.matcher_options).to eq({})
31
31
  expect(options_object.outputter_options).to eq({})
32
32
  end
33
+
34
+ it 'should set with_filename option to true' do
35
+ options_object.set_multi_file_mode
36
+ expect(options_object.outputter_options).to eq({:with_filename => true})
37
+ end
33
38
  end
34
39
 
35
40
  context 'with all additional options' do
36
- subject(:options_object) {create_options(%w(-r -i -v -n exp text text2))}
41
+ subject(:options_object) {create_options(%w(-r -i -v -n exp text2))}
37
42
  it 'should have parsed options' do
38
43
  expect(options_object.file_reader_options).to eq({recursive: true})
39
44
  expect(options_object.matcher_options).to eq({ignore_case: true, invert_selection: true})
@@ -2,6 +2,7 @@ require 'rubygrep/version'
2
2
  require 'rubygrep/grep_manager'
3
3
  require 'rubygrep/ruby_grep_options'
4
4
  require 'rubygrep/file_reader'
5
+ require 'rubygrep/file_searcher'
5
6
  require 'rubygrep/matcher'
6
7
  require 'rubygrep/outputter'
7
8
  require 'optparse'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubygrep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - andrey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-02 00:00:00.000000000 Z
11
+ date: 2014-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,10 +52,11 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description: Globally searches regexp and prints it
55
+ description: Globally searches for in files regexp and prints it
56
56
  email:
57
57
  - om08uhkn@gmail.com
58
- executables: []
58
+ executables:
59
+ - rubygrep
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
@@ -65,8 +66,10 @@ files:
65
66
  - LICENSE.txt
66
67
  - README.md
67
68
  - Rakefile
69
+ - bin/rubygrep
68
70
  - lib/rubygrep.rb
69
71
  - lib/rubygrep/file_reader.rb
72
+ - lib/rubygrep/file_searcher.rb
70
73
  - lib/rubygrep/grep_manager.rb
71
74
  - lib/rubygrep/matcher.rb
72
75
  - lib/rubygrep/outputter.rb
@@ -74,6 +77,8 @@ files:
74
77
  - lib/rubygrep/version.rb
75
78
  - rubygrep.gemspec
76
79
  - spec/file_reader_spec.rb
80
+ - spec/file_searcher_spec.rb
81
+ - spec/grep_manager_spec.rb
77
82
  - spec/matcher_spec.rb
78
83
  - spec/outputter_spec.rb
79
84
  - spec/ruby_grep_options_spec.rb
@@ -104,6 +109,8 @@ specification_version: 4
104
109
  summary: Analog of unix grep utility.
105
110
  test_files:
106
111
  - spec/file_reader_spec.rb
112
+ - spec/file_searcher_spec.rb
113
+ - spec/grep_manager_spec.rb
107
114
  - spec/matcher_spec.rb
108
115
  - spec/outputter_spec.rb
109
116
  - spec/ruby_grep_options_spec.rb