rubygrep 0.0.2 → 0.0.4

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: 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