ru 0.0.1 → 0.0.2

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: f8867edf34abc0c93881bf181ddd252327e1d0e0
4
- data.tar.gz: c91965c74627b8aa655c0750afe64c474cfc72f2
3
+ metadata.gz: 3d87a8521fa49c32d596d3ce2262e4290d6bcd59
4
+ data.tar.gz: 35e812eb5e9d7f9363eee79f6db24534aa8de4bd
5
5
  SHA512:
6
- metadata.gz: ceaa60765f4da15a9291e139658ca5aa39add08528c190464f4e64c287493232c0d89230e76fb631f21be8e16312651336afea9cfa56554d1da170aa138cf24d
7
- data.tar.gz: 77e3c8de6f19ae7047598477f591228e5fed269433d6acf35237d8e1124fd5138c3c77c324b314c1a0c017dc1068b508f373f138788fa47eaf9bbd426415fc89
6
+ metadata.gz: 65186bcdf73bdd0dbc649ea53deed817d6f9eea00cef20698095289b17d6ee4fdf85f5544d6be40cc9ec355843fbf3c2551269b52ce32028592fa199b0e8c17d
7
+ data.tar.gz: a4f3e8b3e722d3533a76ffdd8651c92c2080354d697327573311487462fa6d1738d710cbfb61362113427f6f5241065a3db823823e41aa12066d9157f26d20d5
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ Gemfile.lock
3
+ *.gem
4
+ log/*.log
5
+ .bundle/
6
+ pkg/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ script: bundle exec rspec
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1
7
+ - 2.2
data/Appraisals ADDED
@@ -0,0 +1,7 @@
1
+ appraise "activesupport-3" do
2
+ gem "activesupport", "3.2.20"
3
+ end
4
+
5
+ appraise "activesupport-4" do
6
+ gem "activesupport", "4.1.7"
7
+ end
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 Tom Benner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,278 @@
1
+ Ru
2
+ =====
3
+ Ruby in your shell!
4
+
5
+ <img src="https://raw.github.com/tombenner/ru/master/doc/logo.png" />
6
+
7
+ Overview
8
+ --------
9
+
10
+ Ru brings Ruby's expressiveness, cleanliness, and readability to the command line.
11
+
12
+ It lets you avoid looking up pesky options in man pages and Googling how to write a transformation in bash that would take you approximately 1s to write in Ruby.
13
+
14
+ For example, to center a file's lines, use [String#center](http://ruby-doc.org/core-2.0/String.html#method-i-center):
15
+
16
+ ```bash
17
+ ru 'map(:center, 80)' myfile
18
+ ```
19
+
20
+ Using traditional tools, this isn't as easy or readable:
21
+
22
+ ```bash
23
+ awk 'printf "%" int(40+length($0)/2) "s\n", $0' myfile
24
+ ```
25
+
26
+ For another example, let's compare summing the lines of a list of integers using Ru vs. a traditional approach:
27
+
28
+ ```bash
29
+ ru 'map(:to_i).sum' myfile
30
+ ```
31
+
32
+ ```bash
33
+ awk '{s+=$1} END {print s}' myfile
34
+ ```
35
+
36
+ Any method from Ruby Core and Active Support can be used. Ru also provides some new methods to make transformations easier. Here are some variations on the above example:
37
+
38
+ ```bash
39
+ ru 'map(:to_i, 10).sum' myfile
40
+ ru 'map(:to_i).reduce(&:+)' myfile
41
+ ru 'each_line.to_i.to_a.sum' myfile
42
+ ru 'grep(/^\d+$/).map(:to_i).sum' myfile
43
+ ru 'reduce(0) { |sum, n| sum + n.to_i }' myfile
44
+ ru 'each_line.match(/(\d+)/)[1].to_i.to_a.sum' myfile
45
+ ```
46
+
47
+ See [Examples](#examples) and [Methods](#methods) for more.
48
+
49
+ Installation
50
+ ------------
51
+
52
+ ```bash
53
+ gem install ru
54
+ ```
55
+
56
+ You can now use Ruby in your shell!
57
+
58
+ For example, to sum a list of integers:
59
+
60
+ ```bash
61
+ $ echo "2\n3" | ru 'map(:to_i).sum'
62
+ 5
63
+ ```
64
+
65
+ Usage
66
+ -----
67
+
68
+ See [Examples](#examples) below, too!
69
+
70
+ Ru reads from stdin:
71
+
72
+ ```bash
73
+ $ echo "2\n3" | ru 'map(:to_i).sum'
74
+ 5
75
+ $ cat myfile | ru 'map(:to_i).sum'
76
+ 5
77
+ ```
78
+
79
+ Or from file(s):
80
+
81
+ ```bash
82
+ $ ru 'map(:to_i).sum' myfile
83
+ 5
84
+ $ ru 'map(:to_i).sum' myfile myfile
85
+ 10
86
+ ```
87
+
88
+ You can also run Ruby code without any input by prepending a `! `:
89
+
90
+ ```bash
91
+ $ ru '! 2 + 3'
92
+ 5
93
+ ```
94
+
95
+ In addition to the methods provided by Ruby Core and Active Support, Ru provides other methods for performing transformations, like `each_line`, `files`, and `grep`. See [Methods](#methods) for more.
96
+
97
+ Examples
98
+ --------
99
+
100
+ Let's compare the readability and conciseness of Ru relative to existing tools:
101
+
102
+ #### Center lines
103
+
104
+ ##### ru
105
+ ```bash
106
+ ru 'map(:center, 80)' myfile
107
+ ```
108
+
109
+ ##### awk
110
+ ```bash
111
+ awk 'printf "%" int(40+length($0)/2) "s\n", $0' myfile
112
+ ```
113
+
114
+ ##### sed
115
+ [Script](https://www.gnu.org/software/sed/manual/sed.html#Centering-lines)
116
+
117
+ #### Sum a list of integers
118
+
119
+ ##### ru
120
+ ```bash
121
+ ru 'map(:to_i).sum' myfile
122
+ ```
123
+
124
+ ##### awk
125
+ ```bash
126
+ awk '{s+=$1} END {print s}' myfile
127
+ ```
128
+
129
+ ##### paste
130
+ ```bash
131
+ paste -s -d+ myfile | bc
132
+ ```
133
+
134
+ #### Print the 5th line
135
+
136
+ ##### ru
137
+ ```bash
138
+ ru '[4]' myfile
139
+ ```
140
+
141
+ ##### sed
142
+ ```bash
143
+ sed '5q;d' myfile
144
+ ```
145
+
146
+ #### Print all lines except the first and last
147
+
148
+ ##### ru
149
+ ```bash
150
+ ru '[1..-2]' myfile
151
+ ```
152
+
153
+ ##### sed
154
+ ```bash
155
+ sed '1d;$d' myfile
156
+ ```
157
+
158
+ #### Sort an Apache access log by response time (decreasing, with time prepended)
159
+
160
+ ##### ru
161
+ ```bash
162
+ ru 'map { |line| [line[/(\d+)( ".+"){2}$/, 1].to_i, line] }.sort.reverse.map(:join, " ")' access.log
163
+ ```
164
+
165
+ ##### awk
166
+ ```bash
167
+ awk --re-interval '{ match($0, /(([^[:space:]]+|\[[^\]]+\]|"[^"]+")[[:space:]]+){7}/, m); print m[2], $0 }' access.log | sort -nk 1
168
+ ```
169
+ [Source](https://coderwall.com/p/ueazhw)
170
+
171
+ Methods
172
+ -------
173
+
174
+ In addition to the methods provided by Ruby Core and Active Support, Ru provides other methods for performing transformations.
175
+
176
+ #### each_line
177
+
178
+ Provides a shorthand for calling methods on each iteration of the input. Best explained by example:
179
+
180
+ ```bash
181
+ ru 'each_line.strip.center(80)' myfile
182
+ ```
183
+
184
+ If you'd like to transform it back into a list, call `to_a`:
185
+
186
+ ```bash
187
+ ru 'each_line.strip.to_a.map(:center, 80)' myfile
188
+ ```
189
+
190
+ #### files
191
+
192
+ Converts the lines to `Ru::File` objects (see Ru::File below).
193
+
194
+ ```bash
195
+ $ echo "foo.txt" | ru 'files.map(:updated_at).map(:strftime, ""%Y-%m-%d")'
196
+ 2014-11-08
197
+ ```
198
+
199
+ #### format(format='l')
200
+
201
+ Formats a list of `Ru::File`s. You'll typically call this after calling `files` to transform them into strings:
202
+
203
+ ```bash
204
+ $ ru 'files.format'
205
+ 644 tom staff 3 2014-10-26 09:06 bar.txt
206
+ 644 tom staff 11 2014-11-04 08:29 foo.txt
207
+ ```
208
+
209
+ The default format, `'l'`, is shown above. It prints `[omode, owner, group, size, date, name]`.
210
+
211
+ #### grep
212
+
213
+ Selects lines which match the given regex.
214
+
215
+ ```bash
216
+ $ echo "john\npaul\ngeorge" | ru 'grep(/o[h|r]/)'
217
+ john
218
+ george
219
+ ```
220
+
221
+ #### map
222
+
223
+ This is the same as `Array#map`, but it adds a new syntax that allows you to easily pass arguments to a method. For example:
224
+
225
+ ```bash
226
+ $ echo "john\npaul" | ru 'map(:[], 0)'
227
+ j
228
+ p
229
+ $ echo "john\npaul" | ru 'map(:center, 8, ".")'
230
+ ..john..
231
+ ..paul..
232
+ ```
233
+
234
+ Note that the examples above can also be performed with `each_line`:
235
+
236
+ ```bash
237
+ $ echo "john\npaul" | ru 'each_line[0]'
238
+ $ echo "john\npaul" | ru 'each_line.center(8, ".")'
239
+ ```
240
+
241
+ Ru::File
242
+ ------------
243
+
244
+ The [`files`](#files) method returns an enumerable of `Ru::File`s, which are similar to Ruby Core's [`File`](http://ruby-doc.org/core-2.0/File.html). Each one has the following methods:
245
+
246
+ * `basename`
247
+ * `created_at` (alias for ctime)
248
+ * `ctime`
249
+ * `extname`
250
+ * `format` (see the [`format`](#formatformatl) method above)
251
+ * `ftype`
252
+ * `gid`
253
+ * `group`
254
+ * `mode`
255
+ * `mtime`
256
+ * `name` (alias for basename)
257
+ * `omode`
258
+ * `owner`
259
+ * `size`
260
+ * `to_s` (alias for name)
261
+ * `uid`
262
+ * `updated_at` (alias for mtime)
263
+ * `world_readable?`
264
+
265
+
266
+ Testing
267
+ -------
268
+
269
+ Nested Hstore is tested against ActiveRecord 3 and 4. If you'd like to submit a PR, please be sure to use [Appraisal](https://github.com/thoughtbot/appraisal) to test your changes in both contexts:
270
+
271
+ ```bash
272
+ appraisal rspec
273
+ ```
274
+
275
+ License
276
+ -------
277
+
278
+ Ru is released under the MIT License. Please see the MIT-LICENSE file for details.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib"
9
+ t.libs << "test"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ t.verbose = true
12
+ end
data/bin/ru ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/ru'
4
+
5
+ begin
6
+ args = ARGV
7
+ command = args.shift
8
+ stdin = nil
9
+ # If the command starts with a '!', run it without stdin
10
+ if command.present? && command.start_with?('! ')
11
+ command = command[2..-1]
12
+ # If args is empty, read from stdin
13
+ elsif args.empty?
14
+ stdin = STDIN.read
15
+ end
16
+ process = Ru::Process.new({
17
+ command: command,
18
+ args: args,
19
+ stdin: stdin
20
+ })
21
+ puts process.run
22
+ rescue => e
23
+ STDERR.puts e.message
24
+ STDERR.puts e.backtrace.join("\n")
25
+ exit 1
26
+ end
data/doc/logo.png ADDED
Binary file
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "3.2.20"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ ru (0.0.1)
5
+ activesupport
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (3.2.20)
11
+ i18n (~> 0.6, >= 0.6.4)
12
+ multi_json (~> 1.0)
13
+ appraisal (1.0.2)
14
+ bundler
15
+ rake
16
+ thor (>= 0.14.0)
17
+ diff-lcs (1.2.5)
18
+ i18n (0.6.11)
19
+ multi_json (1.10.1)
20
+ rake (10.3.2)
21
+ rspec (3.1.0)
22
+ rspec-core (~> 3.1.0)
23
+ rspec-expectations (~> 3.1.0)
24
+ rspec-mocks (~> 3.1.0)
25
+ rspec-core (3.1.7)
26
+ rspec-support (~> 3.1.0)
27
+ rspec-expectations (3.1.2)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.1.0)
30
+ rspec-mocks (3.1.3)
31
+ rspec-support (~> 3.1.0)
32
+ rspec-support (3.1.2)
33
+ thor (0.19.1)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ activesupport (= 3.2.20)
40
+ appraisal
41
+ rspec
42
+ ru!
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "4.1.7"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ ru (0.0.1)
5
+ activesupport
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.1.7)
11
+ i18n (~> 0.6, >= 0.6.9)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 1.1)
16
+ appraisal (1.0.2)
17
+ bundler
18
+ rake
19
+ thor (>= 0.14.0)
20
+ diff-lcs (1.2.5)
21
+ i18n (0.6.11)
22
+ json (1.8.1)
23
+ minitest (5.4.2)
24
+ rake (10.3.2)
25
+ rspec (3.1.0)
26
+ rspec-core (~> 3.1.0)
27
+ rspec-expectations (~> 3.1.0)
28
+ rspec-mocks (~> 3.1.0)
29
+ rspec-core (3.1.7)
30
+ rspec-support (~> 3.1.0)
31
+ rspec-expectations (3.1.2)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.1.0)
34
+ rspec-mocks (3.1.3)
35
+ rspec-support (~> 3.1.0)
36
+ rspec-support (3.1.2)
37
+ thor (0.19.1)
38
+ thread_safe (0.3.4)
39
+ tzinfo (1.2.2)
40
+ thread_safe (~> 0.1)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ activesupport (= 4.1.7)
47
+ appraisal
48
+ rspec
49
+ ru!
data/lib/ru.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'active_support/all'
2
+
1
3
  directory = File.dirname(File.absolute_path(__FILE__))
2
4
  Dir.glob("#{directory}/ru/*.rb") { |file| require file }
3
5
 
data/lib/ru/array.rb ADDED
@@ -0,0 +1,86 @@
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_stdout
52
+ self
53
+ end
54
+
55
+ def to_a
56
+ @data
57
+ end
58
+
59
+ def to_ary
60
+ to_a
61
+ end
62
+
63
+ def to_s
64
+ self.to_a.join("\n")
65
+ end
66
+
67
+ def ==(other)
68
+ self.to_a == other.to_a
69
+ end
70
+
71
+ def method_missing(method, *args, &block)
72
+ delegate_to_array(method, *args, &block)
73
+ end
74
+
75
+ private
76
+
77
+ def delegate_to_array(method, *args, &block)
78
+ result = to_a.send(method, *args, &block)
79
+ if result.kind_of?(Enumerable)
80
+ self.class.new(result)
81
+ else
82
+ result
83
+ end
84
+ end
85
+ end
86
+ 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,44 @@
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(array)
16
+ @array = array
17
+ end
18
+
19
+ def to_a
20
+ Ru::Array.new(@array)
21
+ end
22
+
23
+ def to_dotsch_output
24
+ to_a.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ def method_missing(method, *args, &block)
30
+ map_method(method, *args, &block)
31
+ end
32
+
33
+ def map_method(method, *args, &block)
34
+ @array.map! { |item| item.send(method, *args, &block) }
35
+ self
36
+ end
37
+
38
+ redefined_methods.each do |method|
39
+ define_method(method) do |*args|
40
+ map_method(method, *args)
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/ru/process.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Ru
2
+ class Process
3
+ def initialize(options={})
4
+ @command = options[:command]
5
+ @args = options[:args]
6
+ @stdin = options[:stdin]
7
+ if @command.kind_of?(String) && @command.start_with?('[')
8
+ @command = 'to_stdout' + @command
9
+ end
10
+ end
11
+
12
+ def run
13
+ paths = @args
14
+ if @stdin.blank? && paths.present?
15
+ @stdin = paths.map { |path| ::File.open(path).read }.join("\n")
16
+ end
17
+ lines = @stdin.present? ? @stdin.split("\n") : []
18
+ array = Ru::Array.new(lines)
19
+ output = array.instance_eval(@command) || @stdin
20
+ if output.respond_to?(:to_dotsch_output)
21
+ output = output.to_dotsch_output
22
+ end
23
+ if output.kind_of?(::Array)
24
+ output = output.join("\n")
25
+ end
26
+ output.to_s
27
+ end
28
+ end
29
+ end
data/lib/ru/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ru
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  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']
5
+ s.email = ['tombenner@gmail.com']
6
+ s.description = s.summary = %q{Ruby in your shell!}
7
+ s.homepage = 'https://github.com/tombenner/ru'
8
+
9
+ s.files = `git ls-files`.split($\)
10
+ s.name = 'ru'
11
+ s.executables = ['ru']
12
+ s.require_paths = ['lib']
13
+ s.version = Ru::VERSION
14
+ s.license = 'MIT'
15
+
16
+ s.add_dependency 'activesupport'
17
+
18
+ s.add_development_dependency 'appraisal'
19
+ s.add_development_dependency 'rspec'
20
+ end
@@ -0,0 +1,39 @@
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(lines, 'map(:to_i).sum')
12
+ out.should == '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(lines, '[4]')
21
+ out.should == '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
+ out.should == <<-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
+ end
38
+ end
39
+ 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(lines, 'each_line.strip.center(10)')
12
+ out.should == " 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(lines, '(each_line.to_i+1)')
20
+ out.should == ('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(lines, 'each_line.reverse')
28
+ out.should == "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(lines, 'map.with_index { |line, index| "#{(index+1).to_s.rjust(6)} #{line}" }')
36
+ out.should == " 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(lines, 'length')
44
+ out.should == "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(lines, '[0,2]')
52
+ out.should == "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(lines, '[2..-1]')
60
+ out.should == "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(lines, 'uniq')
68
+ out.should == "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(lines, 'select { |line| self.count(line) > 1 }')
76
+ out.should == "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(lines, 'select { |line| self.count(line) == 1 }')
84
+ out.should == "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(lines, 'to_s.squeeze("\n")')
92
+ out.should == "john\npaul\ngeorge\nringo"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+ 66.249.64.13 - - [18/Sep/2004:11:07:48 +1000] "GET /robots.txt HTTP/1.0" 200 468 "-" "Googlebot/2.1"
2
+ 66.249.64.13 - - [18/Sep/2004:11:07:48 +1000] "GET / HTTP/1.0" 200 6433 "-" "Googlebot/2.1"
3
+ 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET /robots.txt HTTP/1.0" 200 468 "-" "Googlebot/2.1"
4
+ 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET / HTTP/1.0" 200 6433 "-" "Googlebot/2.1"
5
+ 66.249.64.14 - - [18/Sep/2004:11:07:48 +1000] "GET /file.txt HTTP/1.0" 200 584912 "-" "Googlebot/2.1"
@@ -0,0 +1 @@
1
+ bar
@@ -0,0 +1,3 @@
1
+ foo
2
+ foo
3
+ foo
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ru::Array do
4
+ describe "Array method" do
5
+ it "returns a Ru::Array" do
6
+ array = described_class.new(%w{john paul george ringo})
7
+ array.sort.should be_a(Ru::Array)
8
+ end
9
+ end
10
+
11
+ describe "#each_line" do
12
+ it "calls to_s" do
13
+ array = described_class.new((1..3).to_a)
14
+ array.each_line.to_s.to_a.should == described_class.new(('1'..'3'))
15
+ end
16
+
17
+ it "calls methods with arguments" do
18
+ array = described_class.new((1..3).to_a)
19
+ array.each_line.modulo(2).to_a.should == [1, 0, 1]
20
+ end
21
+ end
22
+
23
+ describe "#map" do
24
+ it "takes one argument" do
25
+ array = described_class.new(%w{john paul george ringo})
26
+ array.map(:reverse).should == %w{nhoj luap egroeg ognir}
27
+ end
28
+ it "takes two arguments" do
29
+ array = described_class.new(%w{john paul george ringo})
30
+ array.map(:[], 0).should == %w{j p g r}
31
+ end
32
+
33
+ it "returns a Ru::Array" do
34
+ array = described_class.new(%w{john paul george ringo})
35
+ array.map(:[], 0).should be_a(Ru::Array)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ru::Iterator do
4
+ describe "#to_a" do
5
+ it "returns the array" do
6
+ iterator = described_class.new(%w{john paul george ringo})
7
+ iterator.to_a.should == %w{john paul george ringo}
8
+ end
9
+ end
10
+
11
+ describe "#to_dotsch_output" do
12
+ it "returns the string" do
13
+ iterator = described_class.new(%w{john paul george ringo})
14
+ iterator.to_dotsch_output.should == "john\npaul\ngeorge\nringo"
15
+ end
16
+
17
+ context "with a method called on it" do
18
+ it "returns the string" do
19
+ iterator = described_class.new(%w{john paul george ringo})
20
+ iterator.to_s.to_dotsch_output.should == "john\npaul\ngeorge\nringo"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ru::Process do
4
+ include FixturesHelper
5
+ include ProcessHelper
6
+
7
+ describe "#run" do
8
+ it "runs []" do
9
+ lines = %w{john paul george ringo}
10
+ out = run(lines, '[1,2]')
11
+ out.should == "paul\ngeorge"
12
+ end
13
+
14
+ it "runs files" do
15
+ paths = [
16
+ fixture_path('files', 'bar.txt'),
17
+ fixture_path('files', 'foo.txt')
18
+ ]
19
+ out = run(paths, 'files')
20
+ out.should == "bar.txt\nfoo.txt"
21
+ end
22
+
23
+ it "runs format" do
24
+ paths = [
25
+ fixture_path('files', 'bar.txt'),
26
+ fixture_path('files', 'foo.txt')
27
+ ]
28
+ out = run(paths, "files.format('l')")
29
+ lines = out.split("\n")
30
+ lines.length.should == 2
31
+ lines.each do |line|
32
+ # 644 tom staff 11 2014-11-04 08:29 foo.txt
33
+ line.should =~ /^\d{3}\t\w+\t\w+\t\d+\t\d{4}\-\d{2}-\d{2}\t\d{2}:\d{2}\t[\w\.]+$/
34
+ end
35
+ end
36
+
37
+ it "runs grep" do
38
+ lines = %w{john paul george ringo}
39
+ out = run(lines, "grep(/o[h|r]/)")
40
+ out.should == "john\ngeorge"
41
+ end
42
+
43
+ it "runs map with two arguments" do
44
+ lines = %w{john paul george ringo}
45
+ out = run(lines, 'map(:[], 0)')
46
+ out.should == %w{j p g r}.join("\n")
47
+ end
48
+
49
+ it "runs sort" do
50
+ lines = %w{john paul george ringo}
51
+ out = run(lines, 'sort')
52
+ out.should == lines.sort.join("\n")
53
+ end
54
+
55
+ it "takes files as arguments" do
56
+ out = run('', 'to_s', fixture_path('files', 'foo.txt'))
57
+ out.should == "foo\nfoo\nfoo"
58
+ end
59
+
60
+ context "an undefined method" do
61
+ it "raises a NoMethodError" do
62
+ lines = %w{john paul george ringo}
63
+ expect { out = run(lines, 'foo') }.to raise_error(NoMethodError)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,20 @@
1
+ require 'ru'
2
+
3
+ require 'support/fixtures_helper'
4
+ require 'support/process_helper'
5
+
6
+ RSpec.configure do |config|
7
+ # ## Mock Framework
8
+ #
9
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
10
+ #
11
+ # config.mock_with :mocha
12
+ # config.mock_with :flexmock
13
+ # config.mock_with :rr
14
+
15
+ # Run specs in random order to surface order dependencies. If you find an
16
+ # order dependency and want to debug it, you can fix the order by providing
17
+ # the seed, which is printed after each run.
18
+ # --seed 1234
19
+ config.order = "random"
20
+ end
@@ -0,0 +1,6 @@
1
+ module FixturesHelper
2
+ def fixture_path(*args)
3
+ directory = Pathname.new(File.dirname(File.absolute_path(__FILE__)))
4
+ directory.join('..', 'fixtures', *args)
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module ProcessHelper
2
+ def run(stdin, *args)
3
+ if stdin.kind_of?(Array)
4
+ stdin = stdin.join("\n")
5
+ end
6
+ command = args.shift
7
+ process = Ru::Process.new({
8
+ command: command,
9
+ args: args,
10
+ stdin: stdin
11
+ })
12
+ process.run
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ru
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Benner
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2014-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: appraisal
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,15 +52,45 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
- description: ''
55
+ description: Ruby in your shell!
42
56
  email:
43
57
  - tombenner@gmail.com
44
- executables: []
58
+ executables:
59
+ - ru
45
60
  extensions: []
46
61
  extra_rdoc_files: []
47
62
  files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Appraisals
66
+ - Gemfile
67
+ - MIT-LICENSE
68
+ - README.md
69
+ - Rakefile
70
+ - bin/ru
71
+ - doc/logo.png
72
+ - gemfiles/activesupport_3.gemfile
73
+ - gemfiles/activesupport_3.gemfile.lock
74
+ - gemfiles/activesupport_4.gemfile
75
+ - gemfiles/activesupport_4.gemfile.lock
48
76
  - lib/ru.rb
77
+ - lib/ru/array.rb
78
+ - lib/ru/file.rb
79
+ - lib/ru/iterator.rb
80
+ - lib/ru/process.rb
49
81
  - lib/ru/version.rb
82
+ - ru.gemspec
83
+ - spec/examples/misc_examples_spec.rb
84
+ - spec/examples/sed_examples_spec.rb
85
+ - spec/fixtures/files/access.log
86
+ - spec/fixtures/files/bar.txt
87
+ - spec/fixtures/files/foo.txt
88
+ - spec/lib/array_spec.rb
89
+ - spec/lib/iterator_spec.rb
90
+ - spec/lib/process_spec.rb
91
+ - spec/spec_helper.rb
92
+ - spec/support/fixtures_helper.rb
93
+ - spec/support/process_helper.rb
50
94
  homepage: https://github.com/tombenner/ru
51
95
  licenses:
52
96
  - MIT
@@ -70,5 +114,5 @@ rubyforge_project:
70
114
  rubygems_version: 2.2.2
71
115
  signing_key:
72
116
  specification_version: 4
73
- summary: ''
117
+ summary: Ruby in your shell!
74
118
  test_files: []