each_line 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f1eaf44d03e2482b4ae470c4fce5f1131ddad39eaf30b77a47adef06f88c48fd
4
+ data.tar.gz: ab46c5dd059939e71c8d2b0187b8dae117038e44f1ece2685bb5d51b57b1236f
5
+ SHA512:
6
+ metadata.gz: 3f98034a942c4bde96d8b2b2945cb8ed06880f88bdbfd59653985ebcb9fb86b4a914e49b92c3fac26464879426e3558ac1558020ff638d579637e6a40081d20e
7
+ data.tar.gz: 17c181e81aa1bfda91f66b59d596a8fab72e1ca9fe9fd7ad3eb73e14edc78d24692c91977ff5624fa1b05ea1f854bcb6f8b513e612693b36cef5fc835516149e
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Josh Bodah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,125 @@
1
+ # each_line
2
+
3
+ a text processing command-line tool that is driven by Ruby's `#each_line`
4
+
5
+ ## Why this project?
6
+
7
+ I've been juggling various command-line tools for a number of years.
8
+ Tools like `xargs`, `grep`, `awk`, and `sed` can certainly get the job done, but I found myself having to go
9
+ back to the documentation constantly for some of these.
10
+ If I ever needed to do anything more sophisticated that basic processing I found it more
11
+ productive pipe the text into `ruby` instead.
12
+ I use Ruby a lot so I'm much more comfortable with those APIs too.
13
+
14
+ e.g.
15
+
16
+ ```
17
+ $ (echo hello; echo world) | ruby -pe '$_ = $_[3]'
18
+ l
19
+ l
20
+
21
+ # Or
22
+ $ (echo hello; echo world; echo jello) | ruby -e 'puts $stdin.each_line.select { |line| line =~ /ello/ }'
23
+ hello
24
+ jello
25
+ ```
26
+
27
+ This worked better, but `ruby` still felt a bit cumbersome.
28
+ Sometimes I wanted to filter.
29
+ Sometimes I wanted to map.
30
+ Sometimes I wanted to reduce things into JSON.
31
+ I always forgot to `puts` my result and had to skip back to the beginning of iteration.
32
+ `ruby` had all the features, but again I found myself fighting the tools
33
+
34
+ Thus the goal of this project is to provide another interface for Ruby that I think strikes a better balance
35
+ for ad-hoc text processing on the command-line.
36
+
37
+ ## Installation
38
+
39
+ ```
40
+ gem install each_line
41
+ ```
42
+
43
+ ## Examples
44
+
45
+ ```rb
46
+ # Run a script directly against #each_line
47
+ $ (echo hello; echo world) | each_line 'to_a.map(&:strip).join(",")'
48
+ hello,world
49
+
50
+ # If your Ruby supports numbered parameters then you might find that this interface is all you really need
51
+ $ (echo hello; echo world; echo jello) | each_line 'select { _1 =~ /ello/ }'
52
+ hello
53
+ jello
54
+
55
+ # Run a block method (e.g. map, select, etc) on #each_line
56
+ # Use the -m option to specify the method to be run
57
+ # The first argument will be treated as the block body
58
+ # By default block args are positionally bound to $_1, $_2, etc
59
+ # If your version of Ruby supports numbered parameters (e.g. _1, _2, etc) you may find it easier do something along the lines of the example above
60
+ $ (echo hello; echo world; echo jello) | each_line -m select '$_1 =~ /ello/'
61
+ hello
62
+ jello
63
+
64
+ $ (echo hello; echo world; echo jello) | each_line -m reject '$_1 =~ /ello/'
65
+ world
66
+
67
+ # The -m option can also specify a chain of methods which can be useful in some situations
68
+ # Any block or args specified will be passed to the last method in the chain
69
+ $ (echo hello; echo world; echo jello) | each_line -m with_index.map '"#{$_2}\t#{$_1}"'
70
+ 0 hello
71
+ 1 world
72
+ 2 jello
73
+
74
+ # The chain of methods can also accept args and blocks
75
+ # The last method in the chain must use the -a and -b options
76
+ $ (echo hello; echo world; echo jello) | each_line -m 'each_slice(2).map' '$_1.inspect'
77
+ ["hello\n", "world\n"]
78
+ ["jello\n"]
79
+
80
+ # A more complicated example using #reduce
81
+ # The -a option can be used to specify args for reduce; args should be comma-separated
82
+ $ (echo hello; echo world; echo jello) | each_line -m reduce -a '{}' '$_1[$_2.strip] = $_2[3]; $_1'
83
+ {"hello"=>"l", "world"=>"l", "jello"=>"l"}
84
+
85
+ # We can customize the names of block args too to avoid mixing up the block args
86
+ # Use either -v or --block_vars and pass a comma-separated list of arg names
87
+ $ (echo hello; echo world; echo jello) | each_line -m reduce -a '{}' -v '$acc,$line' '$acc[$line.strip] = $line[3]; $acc'
88
+ {"hello"=>"l", "world"=>"l", "jello"=>"l"}
89
+
90
+ # A finalizer can be specified as well
91
+ # The -f option can accept a script to run
92
+ # Additionally the -r and -I options can be used to require libraries and modify the load-path similarly to how they do with the `ruby` executable
93
+ $ (echo hello; echo world; echo jello) | each_line -m reduce -a '{}' -v '$acc,$line' '$acc[$line.strip] = $line[3]; $acc' -f to_json -rjson
94
+ {"hello":"l","world":"l","jello":"l"}
95
+
96
+ # An initializer can be specified
97
+ # This is an arbitrary chunk of code that will be run once before any scripting is done
98
+ # You can use the -i option to specify an intializer
99
+ $ (echo hello; echo world; echo jello) | each_line -i '$lookup = %w(alpha beta charlie)' -m each_with_index.map -v '$el,$idx' '$lookup[$idx]'
100
+ alpha
101
+ beta
102
+ charlie
103
+
104
+ # The -d option can be used to specify a delimiter other than new-line
105
+ $ echo hello world jello | each_line -d ' ' -m map '$_1.gsub /e/, "q"'
106
+ hqllo
107
+ world
108
+ jqllo
109
+
110
+ $ echo hello,world,jello | each_line -d ',' -m map '$_1.gsub /e/, "q"'
111
+ hqllo,
112
+ world,
113
+ jqllo
114
+
115
+ # You can pass the --strip option which will strip off the delimiter before it is passed to your block
116
+ $ echo hello,world,jello | each_line --strip -d ',' -m map '$_1.inspect'
117
+ "hello"
118
+ "world"
119
+ "jello\n"
120
+
121
+ $ (echo hello; echo world; echo jello) | each_line --strip -m map '$_1.inspect'
122
+ "hello"
123
+ "world"
124
+ "jello"
125
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "each_line"
3
+ spec.version = "0.1.0"
4
+ spec.authors = ["Josh Bodah"]
5
+ spec.email = ["joshuabodah@gmail.com"]
6
+
7
+ spec.summary = %q{a text processing command-line tool that is driven by Ruby's `#each_line`}
8
+ spec.homepage = "https://github.com/jbodah/each_line"
9
+ spec.license = "MIT"
10
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
11
+
12
+ spec.metadata["homepage_uri"] = spec.homepage
13
+
14
+ # Specify which files should be added to the gem when it is released.
15
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,98 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ options = {
6
+ method: nil,
7
+ block: nil,
8
+ args: [],
9
+ block_vars: nil,
10
+ finalizer: nil,
11
+ requires: [],
12
+ intializer: nil,
13
+ delimiter: $/,
14
+ strip: false,
15
+ }
16
+ OptionParser.new do |opts|
17
+ opts.banner = "Usage: each_line [options]"
18
+
19
+ opts.on("-m METHOD", "Specify the method to use") do |m|
20
+ options[:method] = m
21
+ end
22
+
23
+ opts.on("-b BLOCK_STR", "Specify the block to pass to the method") do |block_str|
24
+ options[:block] = block_str
25
+ end
26
+
27
+ opts.on("-a ARG_STR", "Specify the args to pass to the method") do |args_str|
28
+ options[:args] = eval("[" + args_str + "]")
29
+ end
30
+
31
+ opts.on("-v BLOCK_VARS", "--block_vars BLOCK_VARS", "Customize the names used for each block variable") do |block_vars|
32
+ options[:block_vars] = block_vars.split(',')
33
+ end
34
+
35
+ opts.on("-f STRING", "A finalizer string that we will eval against the final output before printing") do |finalizer|
36
+ options[:finalizer] = finalizer
37
+ end
38
+
39
+ opts.on("-r PATH", "Requires the given library. You can specify this multiple times") do |r|
40
+ options[:requires] << r
41
+ end
42
+
43
+ opts.on("-I DIRECTORY", "Adds the given directory to the load-path") do |dir|
44
+ $: << dir
45
+ end
46
+
47
+ opts.on("-i STRING", "An initializer string that we will eval before doing any other processing") do |initializer|
48
+ options[:initializer] = initializer
49
+ end
50
+
51
+ opts.on("-d DELIMITER", "Used to split on characters other than new-line") do |delimiter|
52
+ options[:delimiter] = delimiter
53
+ end
54
+
55
+ opts.on("--strip", "Strips off the delimiter before it is processed") do |strip|
56
+ options[:strip] = true
57
+ end
58
+ end.parse!
59
+
60
+ options[:requires].each { |r| require r }
61
+
62
+ eval(options[:initializer]) if options[:initializer]
63
+
64
+ target = $stdin.each_line(options[:delimiter])
65
+
66
+ target = target.lazy.map { |part| part.sub(/#{options[:delimiter]}$/, '') } if options[:strip]
67
+
68
+ if options[:method]
69
+ block_str = options[:block] || ARGV[0]
70
+ args = options[:args]
71
+ preamble, _, method = options[:method].rpartition('.')
72
+ if preamble.size > 0
73
+ target = target.instance_eval(preamble)
74
+ end
75
+
76
+ if block_str
77
+ block = proc do |*block_args|
78
+ block_args.each_with_index do |block_arg, idx|
79
+ block_var = options[:block_vars] ? options[:block_vars][idx] : "$_#{idx+1}"
80
+ eval("#{block_var} = block_arg")
81
+ end
82
+ eval(block_str)
83
+ end
84
+ result = target.public_send(method, *args, &block)
85
+ else
86
+ result = target.public_send(method, *args)
87
+ end
88
+ else
89
+ result = target.instance_eval(ARGV[0])
90
+ end
91
+
92
+ result = result.force if result.is_a?(Enumerator::Lazy)
93
+
94
+ if options[:finalizer]
95
+ result = result.instance_eval(options[:finalizer])
96
+ end
97
+
98
+ puts result
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: each_line
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Bodah
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-05-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - joshuabodah@gmail.com
16
+ executables:
17
+ - each_line
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - each_line.gemspec
26
+ - exe/each_line
27
+ homepage: https://github.com/jbodah/each_line
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://github.com/jbodah/each_line
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.0
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 3.1.2
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: a text processing command-line tool that is driven by Ruby's `#each_line`
51
+ test_files: []