ru2 2.1.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.
data/lib/ru/array.rb ADDED
@@ -0,0 +1,88 @@
1
+ module Ru
2
+ class Array
3
+ def initialize(array)
4
+ @data = array.to_a
5
+ end
6
+
7
+ def each_line
8
+ Ru::Iterator.new(self)
9
+ end
10
+
11
+ def files
12
+ @data.map! do |line|
13
+ Ru::File.new(line)
14
+ end
15
+ self
16
+ end
17
+
18
+ def format(format='l')
19
+ @data.map! do |item|
20
+ item.format(format)
21
+ end
22
+ self
23
+ end
24
+
25
+ def grep(pattern)
26
+ if pattern.kind_of?(String)
27
+ pattern = Regexp.new(pattern)
28
+ end
29
+ select! do |item|
30
+ item.to_s =~ pattern
31
+ end
32
+ self
33
+ end
34
+
35
+ def map(method=nil, *args, &block)
36
+ if method.nil? && !block_given?
37
+ to_a.map
38
+ elsif method.nil?
39
+ array = to_a.map(&block)
40
+ self.class.new(array)
41
+ else
42
+ array = to_a.map { |item| item.send(method, *args) }
43
+ self.class.new(array)
44
+ end
45
+ end
46
+
47
+ def select(*args, &block)
48
+ delegate_to_array(:select, *args, &block)
49
+ end
50
+
51
+ def to_a
52
+ @data
53
+ end
54
+
55
+ def to_ary
56
+ to_a
57
+ end
58
+
59
+ def to_s
60
+ to_a.join("\n")
61
+ end
62
+
63
+ def to_self
64
+ self
65
+ end
66
+
67
+ def ==(other)
68
+ self.to_a == other.to_a
69
+ end
70
+
71
+ private
72
+
73
+ def method_missing(method, *args, &block)
74
+ delegate_to_array(method, *args, &block)
75
+ end
76
+
77
+ def delegate_to_array(method, *args, &block)
78
+ result = @data.send(method, *args, &block)
79
+ if result.kind_of? Enumerator::Lazy
80
+ Ru::Stream.new(result)
81
+ elsif result.kind_of? ::Array
82
+ self.class.new(result)
83
+ else
84
+ result
85
+ end
86
+ end
87
+ end
88
+ end
data/lib/ru/file.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'etc'
2
+
3
+ module Ru
4
+ class File
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def basename
10
+ delegate_to_file(:basename)
11
+ end
12
+
13
+ def ctime
14
+ delegate_to_file(:ctime)
15
+ end
16
+
17
+ def extname
18
+ delegate_to_file(:extname)
19
+ end
20
+
21
+ def format(format='l')
22
+ separator = "\t"
23
+ case format
24
+ when 'l'
25
+ datetime = ctime.strftime(%w{%Y-%m-%d %H:%M}.join(separator))
26
+ return [omode, owner, group, size, datetime, name].join(separator)
27
+ else
28
+ raise 'format not supported'
29
+ end
30
+ end
31
+
32
+ def ftype
33
+ delegate_to_file(:ftype)
34
+ end
35
+
36
+ def gid
37
+ delegate_to_stat(:gid)
38
+ end
39
+
40
+ def group
41
+ Etc.getgrgid(gid).name
42
+ end
43
+
44
+ def mode
45
+ delegate_to_stat(:mode)
46
+ end
47
+
48
+ def mtime
49
+ delegate_to_file(:mtime)
50
+ end
51
+
52
+ def omode
53
+ '%o' % world_readable?
54
+ end
55
+
56
+ def owner
57
+ Etc.getpwuid(uid).name
58
+ end
59
+
60
+ def size
61
+ delegate_to_file(:size)
62
+ end
63
+
64
+ def to_s
65
+ name
66
+ end
67
+
68
+ def uid
69
+ delegate_to_stat(:uid)
70
+ end
71
+
72
+ def world_readable?
73
+ delegate_to_stat(:world_readable?)
74
+ end
75
+
76
+ def <=>(other)
77
+ name <=> other.name
78
+ end
79
+
80
+ alias_method :name, :basename
81
+ alias_method :created_at, :ctime
82
+ alias_method :updated_at, :mtime
83
+
84
+ private
85
+
86
+ def delegate_to_file(method)
87
+ ::File.send(method, @path)
88
+ end
89
+
90
+ def delegate_to_stat(method)
91
+ ::File.stat(@path).send(method)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,56 @@
1
+ module Ru
2
+ class Iterator
3
+ class << self
4
+ def redefined_methods
5
+ @redefined_methods ||= begin
6
+ preserved_methods = %{initialize method_missing respond_to? to_a}
7
+ [].public_methods.select do |method|
8
+ method = method.to_s
9
+ method =~ /^[a-z]/ && !preserved_methods.include?(method)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ def initialize(enum)
16
+ @enum = enum
17
+ end
18
+
19
+ def to_a
20
+ case @enum
21
+ when ::Array
22
+ Ru::Array.new(@enum)
23
+ when Ru::Array
24
+ @enum
25
+ when Ru::Stream
26
+ @enum.to_a
27
+ end
28
+ end
29
+
30
+ def to_stdout
31
+ case @enum
32
+ when ::Array
33
+ @enum.join("\n")
34
+ else
35
+ @enum.to_s
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def method_missing(method, *args, &block)
42
+ map_method(method, *args, &block)
43
+ end
44
+
45
+ def map_method(method, *args, &block)
46
+ @enum = @enum.map { |item| item.send(method, *args, &block) }
47
+ self
48
+ end
49
+
50
+ redefined_methods.each do |method|
51
+ define_method(method) do |*args|
52
+ map_method(method, *args)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ require 'erb'
2
+
3
+ module Ru
4
+ class OptionPrinter
5
+ def exists?(option_key)
6
+ options[option_key].present?
7
+ end
8
+
9
+ def run(option_key, option_value=nil)
10
+ send(options[option_key], *option_value)
11
+ end
12
+
13
+ private
14
+
15
+ def options
16
+ {
17
+ help: :get_help,
18
+ version: :get_version
19
+ }
20
+ end
21
+
22
+ def get_help
23
+ namespace = OpenStruct.new(version: Ru::VERSION)
24
+ template_path = ::File.expand_path("../../../doc/help.erb", __FILE__)
25
+ template = ::File.open(template_path).read
26
+ ERB.new(template).result(namespace.instance_eval { binding })
27
+ end
28
+
29
+ def get_version
30
+ Ru::VERSION
31
+ end
32
+ end
33
+ end
data/lib/ru/process.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'optparse'
2
+
3
+ module Ru
4
+ class Process
5
+
6
+ def initialize
7
+ @option_printer = OptionPrinter.new
8
+ end
9
+
10
+ def run
11
+ output = process_options
12
+ return output if output
13
+
14
+ args = ARGV.dup
15
+ @code = args.shift
16
+
17
+ if @code.blank?
18
+ $stderr.puts @option_printer.run(:help)
19
+ return
20
+ end
21
+
22
+ @stdin = get_stdin(args, @options[:stream]) unless @code.start_with?('! ')
23
+ @code = prepare_code(@code)
24
+
25
+ context =
26
+ if @stdin.nil?
27
+ Ru::Array.new([])
28
+ else
29
+ if @options[:stream]
30
+ if @options[:binary]
31
+ Ru::Stream.new(@stdin.each_byte.lazy)
32
+ else
33
+ Ru::Stream.new(@stdin.each_line.lazy.map { |line| line.chomp("\n".freeze) })
34
+ end
35
+ else
36
+ if @options[:binary]
37
+ Ru::Array.new(@stdin.bytes)
38
+ else
39
+ # Prevent 'invalid byte sequence in UTF-8'
40
+ @stdin.encode!('UTF-8', 'UTF-8', invalid: :replace)
41
+ Ru::Array.new(@stdin.split("\n"))
42
+ end
43
+ end
44
+ end
45
+ output = context.instance_eval(@code) || @stdin
46
+
47
+ prepare_output(output)
48
+ end
49
+
50
+ private
51
+
52
+ def prepare_code(code)
53
+ if code.kind_of?(String)
54
+ if code.start_with?('[')
55
+ code = 'to_self' + code
56
+ elsif code.start_with?('! ')
57
+ code = code[2..-1]
58
+ end
59
+ end
60
+ code
61
+ end
62
+
63
+ def prepare_output(output)
64
+ if output.respond_to?(:to_stdout)
65
+ output = output.to_stdout
66
+ end
67
+ if output.kind_of?(::Array)
68
+ output = output.join("\n")
69
+ end
70
+ output.to_s
71
+ end
72
+
73
+ def process_options
74
+ @options = get_options
75
+ @options.each do |option, value|
76
+ if @option_printer.exists?(option)
77
+ return @option_printer.run(option)
78
+ end
79
+ end
80
+ nil
81
+ end
82
+
83
+ def get_options
84
+ options = {}
85
+ OptionParser.new do |opts|
86
+ opts.on("-h", "--help", "Print help") do
87
+ options[:help] = true
88
+ end
89
+
90
+ opts.on("-v", "--version", "Print version") do
91
+ options[:version] = true
92
+ end
93
+
94
+ opts.on('-s', '--stream', 'Stream mode') do
95
+ options[:stream] = true
96
+ end
97
+
98
+ opts.on('-b', '--binary', 'Binary mode') do
99
+ options[:binary] = true
100
+ end
101
+ end.parse!
102
+ options
103
+ end
104
+
105
+ def get_stdin(paths, stream)
106
+ if paths.present?
107
+ if stream
108
+ ::File.open(paths[0])
109
+ else
110
+ paths.map { |path| ::File.read(path) }.join("\n")
111
+ end
112
+ else
113
+ if stream
114
+ $stdin
115
+ else
116
+ $stdin.read
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
data/lib/ru/stream.rb ADDED
@@ -0,0 +1,126 @@
1
+ class Enumerator::Lazy
2
+ def [](number_or_range, length=1)
3
+ if number_or_range.kind_of? Range
4
+ range = number_or_range
5
+ if range.begin < 0 || range.end < -1 || (range.exclude_end? && range.end == -1)
6
+ raise ArgumentError, "Stream doesn't support negative indexes, except x..-1"
7
+ end
8
+ if range.end == -1
9
+ drop(range.begin)
10
+ else
11
+ each_with_index.select { |_, index|
12
+ range.cover?(index)
13
+ }
14
+ end
15
+ else
16
+ number = number_or_range
17
+ if number >= 0
18
+ result = drop(number)
19
+ if length >= 0
20
+ result.take(length)
21
+ else
22
+ nil
23
+ end
24
+ else
25
+ raise ArgumentError, "Stream doesn't support negative indexes"
26
+ end
27
+ end
28
+ end
29
+
30
+ def length
31
+ count
32
+ end
33
+
34
+ def uniq
35
+ seen = Set.new
36
+ select { |item|
37
+ seen.add?(item)
38
+ }
39
+ end
40
+ end
41
+
42
+ module Ru
43
+ class Stream
44
+
45
+ attr :stream
46
+
47
+ # @param [Enumerator::Lazy] stream
48
+ def initialize(stream)
49
+ raise ArgumentError unless stream.kind_of? Enumerator::Lazy
50
+ @stream = stream
51
+ end
52
+
53
+ def each_line
54
+ Ru::Iterator.new(self)
55
+ end
56
+
57
+ def files
58
+ self.class.new @stream.map { |line| Ru::File.new(line) }
59
+ end
60
+
61
+ def format(format='l')
62
+ self.class.new @stream.map { |item| item.format(format) }
63
+ end
64
+
65
+ def grep(pattern)
66
+ if pattern.kind_of? String
67
+ pattern = Regexp.new(pattern)
68
+ end
69
+ self.class.new @stream.select { |item| item.to_s =~ pattern }
70
+ end
71
+
72
+ def map(method=nil, *args, &block)
73
+ if method.nil? && !block_given?
74
+ each_line
75
+ elsif method.nil?
76
+ self.class.new @stream.map(&block)
77
+ else
78
+ self.class.new @stream.map { |item| item.send(method, *args) }
79
+ end
80
+ end
81
+
82
+ def select(*args, &block)
83
+ self.class.new @stream.select(*args, &block)
84
+ end
85
+
86
+ def to_a
87
+ Ru::Array.new(@stream.to_a)
88
+ end
89
+
90
+ alias_method :to_ary, :to_a
91
+
92
+ def to_s
93
+ result = ''
94
+ result.concat @stream.next.to_s
95
+ loop do
96
+ item = @stream.next.to_s
97
+ result.concat "\n".freeze
98
+ result.concat item
99
+ end
100
+ result
101
+ rescue StopIteration
102
+ result
103
+ end
104
+
105
+ def to_self
106
+ self
107
+ end
108
+
109
+ def ==(other)
110
+ other.is_a?(self.class) && self.stream == other.stream
111
+ end
112
+
113
+ private
114
+
115
+ def method_missing(method, *args, &block)
116
+ result = @stream.send(method, *args, &block)
117
+ if result.kind_of? Enumerator::Lazy
118
+ self.class.new(result)
119
+ elsif result.kind_of? ::Array
120
+ Ru::Array.new(result)
121
+ else
122
+ result
123
+ end
124
+ end
125
+ end
126
+ end
data/lib/ru/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Ru
2
+ VERSION = '2.1.4'
3
+ end
data/lib/ru.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'active_support/all'
2
+
3
+ directory = File.dirname(File.absolute_path(__FILE__))
4
+ Dir.glob("#{directory}/ru/*.rb") { |file| require file }
5
+
6
+ module Ru
7
+ end
data/ru.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../lib/ru/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.authors = ['Tom Benner', 'Alexander Pavlenko']
5
+ s.email = ['tombenner@gmail.com', 'alerticus@gmail.com']
6
+ s.description = s.summary = %q{Ruby in your shell!}
7
+ s.homepage = 'https://github.com/AlexanderPavlenko/ru'
8
+
9
+ s.files = `git ls-files`.split($\)
10
+ s.name = 'ru2'
11
+ s.executables = ['ru']
12
+ s.require_paths = ['lib']
13
+ s.version = Ru::VERSION
14
+ s.license = 'MIT'
15
+
16
+ s.add_dependency 'activesupport', '>= 3.2.0'
17
+
18
+ s.add_development_dependency 'appraisal', '~> 1.0'
19
+ s.add_development_dependency 'rspec', '~> 3.1'
20
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'misc examples' do
4
+ include FixturesHelper
5
+ include ProcessHelper
6
+
7
+ # http://stackoverflow.com/questions/450799/shell-command-to-sum-integers-one-per-line
8
+ context "summing integers" do
9
+ it "sums" do
10
+ lines = (1..10).to_a.map(&:to_s)
11
+ out = run('map(:to_i).sum', lines)
12
+ expect(out).to eq('55')
13
+ end
14
+ end
15
+
16
+ # http://stackoverflow.com/questions/6022384/bash-tool-to-get-nth-line-from-a-file
17
+ context "printing the nth line" do
18
+ it "prints" do
19
+ lines = (1..10).to_a.map(&:to_s)
20
+ out = run('[4]', lines)
21
+ expect(out).to eq('5')
22
+ end
23
+ end
24
+
25
+ # https://coderwall.com/p/ueazhw
26
+ context "sorting an Apache access log by response time" do
27
+ it "sorts" do
28
+ file = fixture_path('files', 'access.log')
29
+ out = run(['map { |line| [line[/(\d+)( ".+"){2}$/, 1].to_i, line] }.sort.reverse.map(:join, " ")', file])
30
+ expect(out).to eq(<<-EOF.strip
31
+ 584912 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET /file.txt HTTP/1.0" 200 584912 "-" "Googlebot/2.1"
32
+ 6433 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET / HTTP/1.0" 200 6433 "-" "Googlebot/2.1"
33
+ 6433 66.249.64.13 - - [18/Sep/2004:11:07:48 +1000] "GET / HTTP/1.0" 200 6433 "-" "Googlebot/2.1"
34
+ 468 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET /robots.txt HTTP/1.0" 200 468 "-" "Googlebot/2.1"
35
+ 468 66.249.64.13 - - [18/Sep/2004:11:07:48 +1000] "GET /robots.txt HTTP/1.0" 200 468 "-" "Googlebot/2.1"
36
+ EOF
37
+ )
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ # https://www.gnu.org/software/sed/manual/sed.html#Examples
4
+ describe 'sed examples' do
5
+ include FixturesHelper
6
+ include ProcessHelper
7
+
8
+ context "centering lines" do
9
+ it "centers" do
10
+ lines = %w{john paul george} + [' ringo ']
11
+ out = run('each_line.strip.center(10)', lines)
12
+ expect(out).to eq(" john \n paul \n george \n ringo ")
13
+ end
14
+ end
15
+
16
+ context "increment a number" do
17
+ it "increments" do
18
+ lines = ('5'..'10').to_a
19
+ out = run('(each_line.to_i+1)', lines)
20
+ expect(out).to eq(('6'..'11').to_a.join("\n"))
21
+ end
22
+ end
23
+
24
+ context "reverse characters of lines" do
25
+ it "reverses" do
26
+ lines = %w{john paul george ringo}
27
+ out = run('each_line.reverse', lines)
28
+ expect(out).to eq("nhoj\nluap\negroeg\nognir")
29
+ end
30
+ end
31
+
32
+ context "numbering lines" do
33
+ it "numbers" do
34
+ lines = %w{john paul george ringo}
35
+ out = run('map.with_index { |line, index| "#{(index+1).to_s.rjust(6)} #{line}" }', lines)
36
+ expect(out).to eq(" 1 john\n 2 paul\n 3 george\n 4 ringo")
37
+ end
38
+ end
39
+
40
+ context "counting lines" do
41
+ it "counts" do
42
+ lines = %w{john paul george ringo}
43
+ out = run('length', lines)
44
+ expect(out).to eq("4")
45
+ end
46
+ end
47
+
48
+ context "printing the first lines" do
49
+ it "prints" do
50
+ lines = %w{john paul george ringo}
51
+ out = run('[0,2]', lines)
52
+ expect(out).to eq("john\npaul")
53
+ end
54
+ end
55
+
56
+ context "printing the last lines" do
57
+ it "prints" do
58
+ lines = %w{john paul george ringo}
59
+ out = run('[2..-1]', lines)
60
+ expect(out).to eq("george\nringo")
61
+ end
62
+ end
63
+
64
+ context "make duplicate lines unique" do
65
+ it "dedupes" do
66
+ lines = %w{john john paul george george george ringo}
67
+ out = run('uniq', lines)
68
+ expect(out).to eq("john\npaul\ngeorge\nringo")
69
+ end
70
+ end
71
+
72
+ context "print duplicated lines of input" do
73
+ it "prints" do
74
+ lines = %w{john john paul george george george ringo}
75
+ out = run('select { |line| self.count(line) > 1 }', lines)
76
+ expect(out).to eq("john\njohn\ngeorge\ngeorge\ngeorge")
77
+ end
78
+ end
79
+
80
+ context "remove all duplicated lines" do
81
+ it "removes" do
82
+ lines = %w{john john paul george george george ringo}
83
+ out = run('select { |line| self.count(line) == 1 }', lines)
84
+ expect(out).to eq("paul\nringo")
85
+ end
86
+ end
87
+
88
+ context "squeezing blank lines" do
89
+ it "squeezes" do
90
+ lines = "john\n\npaul\ngeorge\n\n\nringo"
91
+ out = run('to_s.squeeze("\n")', lines)
92
+ expect(out).to eq("john\npaul\ngeorge\nringo")
93
+ end
94
+ end
95
+ end