jr-cli 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.travis.yml +14 -0
- data/CHANGELOG.md +19 -4
- data/Guardfile +35 -0
- data/README.md +53 -45
- data/Rakefile +13 -0
- data/bin/jr +24 -14
- data/features/core_ext.feature +72 -0
- data/features/json_processing.feature +247 -0
- data/features/support/env.rb +1 -0
- data/jr-cli.gemspec +8 -1
- data/lib/jr/cli/core_ext/enumerable.rb +17 -0
- data/lib/jr/cli/core_ext/enumerator.rb +6 -0
- data/lib/jr/cli/core_ext/hash.rb +22 -0
- data/lib/jr/cli/core_ext/kernel.rb +7 -0
- data/lib/jr/cli/core_ext.rb +4 -0
- data/lib/jr/cli/version.rb +1 -1
- data/resources/img/jr.gif +0 -0
- data/test/unit/core_ext/enumerable_test.rb +30 -0
- data/test/unit/core_ext/enumerator_test.rb +18 -0
- data/test/unit/core_ext/hash_test.rb +54 -0
- data/test/unit/core_ext/kernel_test.rb +13 -0
- metadata +124 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fbe53abe97c6501e6e1d78f1bfdded7ef8cfd97
|
4
|
+
data.tar.gz: 3cd6173a84e84c40e856c56a7638197f3764d9f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23149a324db87a7cc6dee03e34bdde6f30d679c69fd77c73ec9f37f7cb93205e2a01bf8a1c6c65640ea3f8e5f9a7a6584f8ed6c30d734ae59d9ca27e58b99396
|
7
|
+
data.tar.gz: a3e3aebf4c2ae87f9e80dc4bd4ff76127ba27c8bfb34ea67386af1c2c6ca21c7fe9651badaa6b275ad19f3d959fcf3d7413eccf704ffe533a148c2b82281ca1c
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
0.0
|
2
|
-
|
1
|
+
## 0.2.0 (2015-08-19)
|
2
|
+
|
3
|
+
* [BREAKING CHANGE] Now `-r` is alias for `--raw-output` and use `--require` to require library
|
4
|
+
* Colorize output by default
|
5
|
+
* Add core\_ext modules for `Hash`, `Enumerable` and `Enumerator`
|
6
|
+
* Add `--compact-output` option
|
7
|
+
* Add `--color-output` option
|
8
|
+
* Add `--monochrome-output` option
|
9
|
+
|
10
|
+
## 0.1.0 (2014-11-06)
|
11
|
+
|
12
|
+
* Add `-r` option to require library
|
13
|
+
|
14
|
+
## 0.0.3 (2014-11-05)
|
15
|
+
|
16
|
+
* Handle SIGINT
|
17
|
+
|
18
|
+
## 0.0.2 (2014-10-06)
|
3
19
|
|
4
20
|
* JSON Enumerator is returned as Enumerator::Lazy by default
|
5
21
|
* Add Kernel#itself method for Ruby 2.1 or earlier
|
6
22
|
|
7
|
-
0.0.1 (2014-10-03)
|
8
|
-
------------------
|
23
|
+
## 0.0.1 (2014-10-03)
|
9
24
|
|
10
25
|
* Initial release
|
data/Guardfile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
guard :test do
|
19
|
+
watch(%r{^test/.+_test\.rb$})
|
20
|
+
watch('test/test_helper.rb') { 'test' }
|
21
|
+
|
22
|
+
# Non-rails
|
23
|
+
watch(%r{^lib/jr/cli/(.+)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
|
24
|
+
end
|
25
|
+
|
26
|
+
guard "cucumber" do
|
27
|
+
watch(%r{^lib/.+\.rb$}) { "features" }
|
28
|
+
|
29
|
+
watch(%r{^features/.+\.feature$})
|
30
|
+
watch(%r{^features/support/.+$}) { "features" }
|
31
|
+
|
32
|
+
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) do |m|
|
33
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "features"
|
34
|
+
end
|
35
|
+
end
|
data/README.md
CHANGED
@@ -31,7 +31,13 @@ You can also read JSON not from files but from STDIN.
|
|
31
31
|
|
32
32
|
### options
|
33
33
|
|
34
|
-
|
34
|
+
```
|
35
|
+
--require FILE require the FILE before execution
|
36
|
+
-c, --compact-output output each JSON in single line
|
37
|
+
-r, --raw-output output strings as raw output
|
38
|
+
-C, --color-output output with colors even if writing to a pipe or a file
|
39
|
+
-M, --monochrome-output output without colors
|
40
|
+
```
|
35
41
|
|
36
42
|
## jr filter tutorial
|
37
43
|
|
@@ -40,67 +46,69 @@ Let's process JSON of GitHub API!
|
|
40
46
|
At first, download JSON of repos into your local to avoid API rate limit.
|
41
47
|
|
42
48
|
```
|
43
|
-
$ curl -s 'https://api.github.com/users/yuya-takeyama/repos' > repos.json
|
49
|
+
$ curl -s 'https://api.github.com/users/yuya-takeyama/repos?per_page=100' > repos.json
|
44
50
|
```
|
45
51
|
|
46
|
-
###
|
52
|
+
### Unwrap Array with `Enumerable#unwrap`
|
47
53
|
|
48
|
-
Because response from `GET /users/:username/repos` is wrapped with `Array`,
|
54
|
+
Because response from `GET /users/:username/repos` is wrapped with `Array`, unwrap it using `Enumerable#unwrap`.
|
55
|
+
It's a built-in method of jr.
|
49
56
|
You'll get stream of JSON reperesents repositories.
|
50
57
|
|
51
58
|
```
|
52
|
-
$ jr '
|
59
|
+
$ jr 'unwrap' repos.json
|
53
60
|
```
|
54
61
|
|
55
|
-
|
56
|
-
|
57
|
-
### map to reduce data
|
62
|
+
### Aggregate data with methods of `Enumerable`
|
58
63
|
|
59
|
-
|
60
|
-
So reduce data using `#map` method.
|
64
|
+
`Enumerable` has many useful methods and you can transform data with them.
|
61
65
|
|
62
66
|
```
|
63
|
-
$
|
64
|
-
|
65
|
-
"
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
$j r 'unwrap.group_by(&:language).map{|k, v| [k, v.size] }.sort_by{|k, v| -v }' repos.json
|
68
|
+
[
|
69
|
+
"Ruby",
|
70
|
+
28
|
71
|
+
]
|
72
|
+
[
|
73
|
+
"PHP",
|
74
|
+
22
|
75
|
+
]
|
76
|
+
[
|
77
|
+
"Go",
|
78
|
+
17
|
79
|
+
]
|
74
80
|
(...omitted...)
|
75
|
-
|
76
|
-
"
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
[
|
82
|
+
"VimL",
|
83
|
+
1
|
84
|
+
]
|
85
|
+
[
|
86
|
+
"CoffeeScript",
|
87
|
+
1
|
88
|
+
]
|
89
|
+
[
|
90
|
+
"Perl",
|
91
|
+
1
|
92
|
+
]
|
85
93
|
```
|
86
94
|
|
87
|
-
###
|
95
|
+
### Output as text
|
88
96
|
|
89
|
-
|
90
|
-
Let's aggregate it and count by languages.
|
97
|
+
You can transform JSONs into String and output as raw text using `-r` option.
|
91
98
|
|
92
99
|
```
|
93
|
-
$ jr '
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
$ jr 'unwrap.group_by(&:language).map{|k, v| [k, v.size] }.sort_by{|k, v| -v }.map{|l, s| "#{s}\t#{l}" }' -r repos.json
|
101
|
+
28 Ruby
|
102
|
+
22 PHP
|
103
|
+
17 Go
|
104
|
+
12 JavaScript
|
105
|
+
11
|
106
|
+
3 CSS
|
107
|
+
2 Shell
|
108
|
+
2 C
|
109
|
+
1 VimL
|
110
|
+
1 CoffeeScript
|
111
|
+
1 Perl
|
104
112
|
```
|
105
113
|
|
106
114
|
## Basic mechanism
|
data/Rakefile
CHANGED
@@ -1,2 +1,15 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require 'cucumber/rake/task'
|
2
4
|
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.libs << "lib"
|
8
|
+
t.test_files = FileList['test/**/*_test.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
12
|
+
t.cucumber_opts = "features --format pretty"
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => [:test, :features]
|
data/bin/jr
CHANGED
@@ -1,27 +1,26 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
lib = File.expand_path('../../lib', __FILE__)
|
5
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
|
7
4
|
require 'jr/cli/version'
|
8
5
|
require 'optparse'
|
9
6
|
require 'yajl'
|
7
|
+
require 'coderay'
|
10
8
|
|
11
9
|
opt = OptionParser.new
|
12
10
|
opt.banner = "jr - Command-line JSON processor for Rubyists [Ver. #{Jr::Cli::VERSION}]\n\n" +
|
13
11
|
"Usage: jr [options] <jr filter> [file...]"
|
14
12
|
opt.version = Jr::Cli::VERSION
|
15
|
-
opt.on('
|
13
|
+
opt.on('--require FILE', 'require the FILE before execution') {|file| require file }
|
14
|
+
pretty = true
|
15
|
+
opt.on('-c', '--compact-output', 'output each JSON in single line') { pretty = false }
|
16
|
+
raw_output = false
|
17
|
+
opt.on('-r', '--raw-output', 'output strings as raw output') { raw_output = true }
|
18
|
+
color_output = STDOUT.tty?
|
19
|
+
opt.on('--color-output', '-C', 'output with colors even if writing to a pipe or a file') { color_output = true }
|
20
|
+
opt.on('--monochrome-output', '-M', 'output without colors') { color_output = false }
|
16
21
|
opt.parse! ARGV
|
17
22
|
|
18
|
-
|
19
|
-
module Kernel
|
20
|
-
def itself
|
21
|
-
self
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
23
|
+
require 'jr/cli/core_ext'
|
25
24
|
|
26
25
|
trap('INT') { exit 130 }
|
27
26
|
|
@@ -34,12 +33,23 @@ result = Enumerator.new do |yielder|
|
|
34
33
|
end
|
35
34
|
end.lazy.instance_eval(ARGV[0])
|
36
35
|
|
37
|
-
encoder = Yajl::Encoder.new(pretty:
|
36
|
+
encoder = Yajl::Encoder.new(pretty: pretty)
|
37
|
+
print_json = ->(data) do
|
38
|
+
if raw_output and data.is_a? String
|
39
|
+
puts data
|
40
|
+
else
|
41
|
+
if color_output
|
42
|
+
puts CodeRay.scan(encoder.encode(data), :json).terminal
|
43
|
+
else
|
44
|
+
puts encoder.encode(data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
38
48
|
|
39
49
|
if result.is_a? Array or result.kind_of? Enumerator
|
40
50
|
result.each do |data|
|
41
|
-
|
51
|
+
print_json.call data
|
42
52
|
end
|
43
53
|
else
|
44
|
-
|
54
|
+
print_json.call result
|
45
55
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
Feature: core_ext
|
2
|
+
|
3
|
+
jr introduce some extended method for basic objects
|
4
|
+
|
5
|
+
Scenario: attribute reader
|
6
|
+
Given a file named "input.json" with:
|
7
|
+
"""
|
8
|
+
{"name":"foo"}
|
9
|
+
{"name":"bar"}
|
10
|
+
{"name":"baz"}
|
11
|
+
"""
|
12
|
+
When I run `jr 'map(&:name)' input.json`
|
13
|
+
Then the output should contain exactly:
|
14
|
+
"""
|
15
|
+
"foo"
|
16
|
+
"bar"
|
17
|
+
"baz"
|
18
|
+
|
19
|
+
"""
|
20
|
+
|
21
|
+
Scenario: attribute checker
|
22
|
+
Given a file named "input.json" with:
|
23
|
+
"""
|
24
|
+
{"ua":"Chrome"}
|
25
|
+
{"ua":"Safari","is_crawler":false}
|
26
|
+
{"ua":"Googlebot","is_crawler":true}
|
27
|
+
"""
|
28
|
+
When I run `jr 'select(&:is_crawler?)' input.json`
|
29
|
+
Then the output should contain exactly:
|
30
|
+
"""
|
31
|
+
{
|
32
|
+
"ua": "Googlebot",
|
33
|
+
"is_crawler": true
|
34
|
+
}
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
Scenario: unwrap Array and Hash
|
39
|
+
Given a file named "input.json" with:
|
40
|
+
"""
|
41
|
+
["a","b",["c"]]
|
42
|
+
{"a":"A","b":"B","c":{"cc":"CC"}}
|
43
|
+
"""
|
44
|
+
When I run `jr 'unwrap' input.json`
|
45
|
+
Then the output should contain exactly:
|
46
|
+
"""
|
47
|
+
"a"
|
48
|
+
"b"
|
49
|
+
[
|
50
|
+
"c"
|
51
|
+
]
|
52
|
+
"A"
|
53
|
+
"B"
|
54
|
+
{
|
55
|
+
"cc": "CC"
|
56
|
+
}
|
57
|
+
|
58
|
+
"""
|
59
|
+
|
60
|
+
Scenario: number of JSON
|
61
|
+
Given a file named "input.json" with:
|
62
|
+
"""
|
63
|
+
{"name":"foo"}
|
64
|
+
{"name":"bar"}
|
65
|
+
{"name":"baz"}
|
66
|
+
"""
|
67
|
+
When I run `jr 'size' input.json`
|
68
|
+
Then the output should contain exactly:
|
69
|
+
"""
|
70
|
+
3
|
71
|
+
|
72
|
+
"""
|
@@ -0,0 +1,247 @@
|
|
1
|
+
Feature: JSON processing
|
2
|
+
|
3
|
+
jr is jq like JSON processor for Rubyist
|
4
|
+
|
5
|
+
Scenario: Filter single JSON of Object
|
6
|
+
Given a file named "input.json" with:
|
7
|
+
"""
|
8
|
+
{"foo":"bar"}
|
9
|
+
"""
|
10
|
+
When I run `jr 'self' input.json`
|
11
|
+
Then the output should contain exactly:
|
12
|
+
"""
|
13
|
+
{
|
14
|
+
"foo": "bar"
|
15
|
+
}
|
16
|
+
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: Filter multiple JSONs of Object
|
20
|
+
Given a file named "input.json" with:
|
21
|
+
"""
|
22
|
+
{"foo":"bar"}
|
23
|
+
{"baz":"foobar"}
|
24
|
+
{"qux":"quux"}
|
25
|
+
"""
|
26
|
+
When I run `jr 'self' input.json`
|
27
|
+
Then the output should contain exactly:
|
28
|
+
"""
|
29
|
+
{
|
30
|
+
"foo": "bar"
|
31
|
+
}
|
32
|
+
{
|
33
|
+
"baz": "foobar"
|
34
|
+
}
|
35
|
+
{
|
36
|
+
"qux": "quux"
|
37
|
+
}
|
38
|
+
|
39
|
+
"""
|
40
|
+
|
41
|
+
Scenario: Filter JSON using Ruby methods
|
42
|
+
Given a file named "input.json" with:
|
43
|
+
"""
|
44
|
+
{"id":1,"name":"foo"}
|
45
|
+
{"id":2,"name":"bar"}
|
46
|
+
{"id":3,"name":"baz"}
|
47
|
+
{"id":4,"name":"foobar"}
|
48
|
+
{"id":5,"name":"qux"}
|
49
|
+
"""
|
50
|
+
When I run `jr 'select{|j| j[:id].odd? }.map{|j| {name: j[:name]} }' input.json`
|
51
|
+
Then the output should contain exactly:
|
52
|
+
"""
|
53
|
+
{
|
54
|
+
"name": "foo"
|
55
|
+
}
|
56
|
+
{
|
57
|
+
"name": "baz"
|
58
|
+
}
|
59
|
+
{
|
60
|
+
"name": "qux"
|
61
|
+
}
|
62
|
+
|
63
|
+
"""
|
64
|
+
|
65
|
+
Scenario: Read multiple files
|
66
|
+
Given a file named "input1.json" with:
|
67
|
+
"""
|
68
|
+
{"foo":"bar"}
|
69
|
+
"""
|
70
|
+
And a file named "input2.json" with:
|
71
|
+
"""
|
72
|
+
{"baz":"foobar"}
|
73
|
+
"""
|
74
|
+
When I run `jr 'self' input1.json input2.json`
|
75
|
+
Then the output should contain exactly:
|
76
|
+
"""
|
77
|
+
{
|
78
|
+
"foo": "bar"
|
79
|
+
}
|
80
|
+
{
|
81
|
+
"baz": "foobar"
|
82
|
+
}
|
83
|
+
|
84
|
+
"""
|
85
|
+
|
86
|
+
Scenario: Filter single JSON of Array
|
87
|
+
Given a file named "input.json" with:
|
88
|
+
"""
|
89
|
+
[1,2,3]
|
90
|
+
"""
|
91
|
+
When I run `jr 'self' input.json`
|
92
|
+
Then the output should contain exactly:
|
93
|
+
"""
|
94
|
+
[
|
95
|
+
1,
|
96
|
+
2,
|
97
|
+
3
|
98
|
+
]
|
99
|
+
|
100
|
+
"""
|
101
|
+
|
102
|
+
Scenario: Filter multiple JSONs of Array
|
103
|
+
Given a file named "input.json" with:
|
104
|
+
"""
|
105
|
+
[1,2,3]
|
106
|
+
[4,5,6]
|
107
|
+
[7,8,9]
|
108
|
+
"""
|
109
|
+
When I run `jr 'self' input.json`
|
110
|
+
Then the output should contain exactly:
|
111
|
+
"""
|
112
|
+
[
|
113
|
+
1,
|
114
|
+
2,
|
115
|
+
3
|
116
|
+
]
|
117
|
+
[
|
118
|
+
4,
|
119
|
+
5,
|
120
|
+
6
|
121
|
+
]
|
122
|
+
[
|
123
|
+
7,
|
124
|
+
8,
|
125
|
+
9
|
126
|
+
]
|
127
|
+
|
128
|
+
"""
|
129
|
+
|
130
|
+
Scenario: Filter a string
|
131
|
+
Given a file named "input.json" with:
|
132
|
+
"""
|
133
|
+
"foo"
|
134
|
+
"""
|
135
|
+
When I run `jr 'self' input.json`
|
136
|
+
Then the output should contain exactly:
|
137
|
+
"""
|
138
|
+
"foo"
|
139
|
+
|
140
|
+
"""
|
141
|
+
|
142
|
+
Scenario: Filter multiple strings
|
143
|
+
Given a file named "input.json" with:
|
144
|
+
"""
|
145
|
+
"foo"
|
146
|
+
"bar"
|
147
|
+
"baz"
|
148
|
+
"""
|
149
|
+
When I run `jr 'self' input.json`
|
150
|
+
Then the output should contain exactly:
|
151
|
+
"""
|
152
|
+
"foo"
|
153
|
+
"bar"
|
154
|
+
"baz"
|
155
|
+
|
156
|
+
"""
|
157
|
+
|
158
|
+
Scenario: require rubygems using -r option
|
159
|
+
Given a file named "input.json" with:
|
160
|
+
"""
|
161
|
+
{"ua":"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"}
|
162
|
+
"""
|
163
|
+
When I run `jr --require woothee 'map{ |j| j.merge({woothee: Woothee.parse(j.ua)}) }' input.json`
|
164
|
+
Then the output should contain exactly:
|
165
|
+
"""
|
166
|
+
{
|
167
|
+
"ua": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
|
168
|
+
"woothee": {
|
169
|
+
"name": "Googlebot",
|
170
|
+
"category": "crawler",
|
171
|
+
"os": "UNKNOWN",
|
172
|
+
"os_version": "UNKNOWN",
|
173
|
+
"version": "UNKNOWN",
|
174
|
+
"vendor": "UNKNOWN"
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
"""
|
179
|
+
|
180
|
+
Scenario: Output each JSON in single line using --compact-output option
|
181
|
+
Given a file named "input.json" with:
|
182
|
+
"""
|
183
|
+
{
|
184
|
+
"name": "foo"
|
185
|
+
}
|
186
|
+
{
|
187
|
+
"name": "bar"
|
188
|
+
}
|
189
|
+
{
|
190
|
+
"name": "baz"
|
191
|
+
}
|
192
|
+
|
193
|
+
"""
|
194
|
+
When I run `jr --compact-output 'self' input.json`
|
195
|
+
Then the output should contain exactly:
|
196
|
+
"""
|
197
|
+
{"name":"foo"}
|
198
|
+
{"name":"bar"}
|
199
|
+
{"name":"baz"}
|
200
|
+
|
201
|
+
"""
|
202
|
+
|
203
|
+
Scenario: Output each JSON in single line using -c option
|
204
|
+
Given a file named "input.json" with:
|
205
|
+
"""
|
206
|
+
{
|
207
|
+
"name": "foo"
|
208
|
+
}
|
209
|
+
{
|
210
|
+
"name": "bar"
|
211
|
+
}
|
212
|
+
{
|
213
|
+
"name": "baz"
|
214
|
+
}
|
215
|
+
|
216
|
+
"""
|
217
|
+
When I run `jr -c 'self' input.json`
|
218
|
+
Then the output should contain exactly:
|
219
|
+
"""
|
220
|
+
{"name":"foo"}
|
221
|
+
{"name":"bar"}
|
222
|
+
{"name":"baz"}
|
223
|
+
|
224
|
+
"""
|
225
|
+
|
226
|
+
Scenario: Output strings as raw output using --raw-output option
|
227
|
+
Given a file named "input.json" with:
|
228
|
+
"""
|
229
|
+
{
|
230
|
+
"name": "foo"
|
231
|
+
}
|
232
|
+
{
|
233
|
+
"name": "bar"
|
234
|
+
}
|
235
|
+
{
|
236
|
+
"name": "baz"
|
237
|
+
}
|
238
|
+
|
239
|
+
"""
|
240
|
+
When I run `jr --raw-output 'map(&:name)' input.json`
|
241
|
+
Then the output should contain exactly:
|
242
|
+
"""
|
243
|
+
foo
|
244
|
+
bar
|
245
|
+
baz
|
246
|
+
|
247
|
+
"""
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
data/jr-cli.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["sign.of.the.wolf.pentagram@gmail.com"]
|
11
11
|
spec.summary = %q{jr: command-line JSON processor for Rubyists}
|
12
12
|
spec.description = %q{jr is jq like JSON processor. Its script can be written in Ruby}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/yuya-takeyama/jr"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -19,7 +19,14 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_runtime_dependency "yajl-ruby"
|
22
|
+
spec.add_runtime_dependency "coderay"
|
22
23
|
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
24
25
|
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "test-unit"
|
27
|
+
spec.add_development_dependency "aruba"
|
28
|
+
spec.add_development_dependency "guard"
|
29
|
+
spec.add_development_dependency "guard-test"
|
30
|
+
spec.add_development_dependency "guard-cucumber"
|
31
|
+
spec.add_development_dependency "woothee"
|
25
32
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Enumerable
|
2
|
+
def unwrap
|
3
|
+
Enumerator.new do |yielder|
|
4
|
+
each do |element|
|
5
|
+
if element.is_a? Hash
|
6
|
+
element.each_value do |sub_element|
|
7
|
+
yielder.yield sub_element
|
8
|
+
end
|
9
|
+
else
|
10
|
+
element.each do |sub_element|
|
11
|
+
yielder.yield sub_element
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Hash
|
2
|
+
def method_missing(method_name, *args)
|
3
|
+
return self[method_name] if key? method_name
|
4
|
+
name, suffix = split_name_and_suffix(method_name)
|
5
|
+
case suffix
|
6
|
+
when '?'
|
7
|
+
!!self[name.to_sym]
|
8
|
+
when '='
|
9
|
+
self[name.to_sym] = args[0]
|
10
|
+
else
|
11
|
+
self[name.to_sym]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
METHOD_SPLITTER = /(.*?)([\?=]?)$/
|
17
|
+
|
18
|
+
def split_name_and_suffix(method_name)
|
19
|
+
match = method_name.to_s.match(METHOD_SPLITTER)
|
20
|
+
[match[1], match[2]]
|
21
|
+
end
|
22
|
+
end
|
data/lib/jr/cli/version.rb
CHANGED
data/resources/img/jr.gif
CHANGED
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'jr/cli/core_ext/enumerable'
|
3
|
+
|
4
|
+
class EnumerableTest < Test::Unit::TestCase
|
5
|
+
sub_test_case '#unwrap' do
|
6
|
+
test 'unwrap Array of Arrays' do
|
7
|
+
assert do
|
8
|
+
input = [
|
9
|
+
['a', 'b', ['c']],
|
10
|
+
['A']
|
11
|
+
]
|
12
|
+
expected = ['a', 'b', ['c'], 'A']
|
13
|
+
|
14
|
+
input.unwrap.to_a == expected
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test 'unwrap Array of Hashes' do
|
19
|
+
assert do
|
20
|
+
input = [
|
21
|
+
{a: 'A', b: 'B', c: {cc: 'CC'}},
|
22
|
+
{aa: 'AA'},
|
23
|
+
]
|
24
|
+
expected = ['A', 'B', {cc: 'CC'}, 'AA']
|
25
|
+
|
26
|
+
input.unwrap.to_a == expected
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'jr/cli/core_ext/enumerator'
|
3
|
+
|
4
|
+
class EnumerableTest < Test::Unit::TestCase
|
5
|
+
sub_test_case '#size' do
|
6
|
+
test 'returns number of elements' do
|
7
|
+
enumerator = Enumerator.new do |yielder|
|
8
|
+
yielder.yield 1
|
9
|
+
yielder.yield 2
|
10
|
+
yielder.yield 3
|
11
|
+
end
|
12
|
+
|
13
|
+
assert do
|
14
|
+
enumerator.size == 3
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'jr/cli/core_ext/hash'
|
3
|
+
|
4
|
+
class HashTest < Test::Unit::TestCase
|
5
|
+
sub_test_case 'read propertya' do
|
6
|
+
test 'read the value' do
|
7
|
+
assert do
|
8
|
+
{foo: 'bar'}.foo == 'bar'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
test 'return nil if the key does not exist' do
|
13
|
+
assert do
|
14
|
+
{foo: 'bar'}.bar == nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
sub_test_case 'check key existence' do
|
20
|
+
test 'return true if the key exists and truthy' do
|
21
|
+
assert do
|
22
|
+
{foo: 'bar'}.foo? == true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
test 'return false if the key exists but it is false' do
|
27
|
+
assert do
|
28
|
+
{foo: false}.foo? == false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
test 'return false if the key exists but it is nil' do
|
33
|
+
assert do
|
34
|
+
{foo: nil}.foo? == false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test 'return false if the key does not exists' do
|
39
|
+
assert do
|
40
|
+
{foo: 'bar'}.bar? == false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
sub_test_case 'write property' do
|
46
|
+
test 'write value into the property' do
|
47
|
+
assert do
|
48
|
+
hash = {}
|
49
|
+
hash.foo = 'bar'
|
50
|
+
hash.foo == 'bar'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jr-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuya Takeyama
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yajl-ruby
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coderay
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,90 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: test-unit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aruba
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard-test
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-cucumber
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: woothee
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
55
153
|
description: jr is jq like JSON processor. Its script can be written in Ruby
|
56
154
|
email:
|
57
155
|
- sign.of.the.wolf.pentagram@gmail.com
|
@@ -61,16 +159,30 @@ extensions: []
|
|
61
159
|
extra_rdoc_files: []
|
62
160
|
files:
|
63
161
|
- ".gitignore"
|
162
|
+
- ".travis.yml"
|
64
163
|
- CHANGELOG.md
|
65
164
|
- Gemfile
|
165
|
+
- Guardfile
|
66
166
|
- LICENSE.txt
|
67
167
|
- README.md
|
68
168
|
- Rakefile
|
69
169
|
- bin/jr
|
170
|
+
- features/core_ext.feature
|
171
|
+
- features/json_processing.feature
|
172
|
+
- features/support/env.rb
|
70
173
|
- jr-cli.gemspec
|
174
|
+
- lib/jr/cli/core_ext.rb
|
175
|
+
- lib/jr/cli/core_ext/enumerable.rb
|
176
|
+
- lib/jr/cli/core_ext/enumerator.rb
|
177
|
+
- lib/jr/cli/core_ext/hash.rb
|
178
|
+
- lib/jr/cli/core_ext/kernel.rb
|
71
179
|
- lib/jr/cli/version.rb
|
72
180
|
- resources/img/jr.gif
|
73
|
-
|
181
|
+
- test/unit/core_ext/enumerable_test.rb
|
182
|
+
- test/unit/core_ext/enumerator_test.rb
|
183
|
+
- test/unit/core_ext/hash_test.rb
|
184
|
+
- test/unit/core_ext/kernel_test.rb
|
185
|
+
homepage: https://github.com/yuya-takeyama/jr
|
74
186
|
licenses:
|
75
187
|
- MIT
|
76
188
|
metadata: {}
|
@@ -90,8 +202,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
202
|
version: '0'
|
91
203
|
requirements: []
|
92
204
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
205
|
+
rubygems_version: 2.4.5
|
94
206
|
signing_key:
|
95
207
|
specification_version: 4
|
96
208
|
summary: 'jr: command-line JSON processor for Rubyists'
|
97
|
-
test_files:
|
209
|
+
test_files:
|
210
|
+
- features/core_ext.feature
|
211
|
+
- features/json_processing.feature
|
212
|
+
- features/support/env.rb
|
213
|
+
- test/unit/core_ext/enumerable_test.rb
|
214
|
+
- test/unit/core_ext/enumerator_test.rb
|
215
|
+
- test/unit/core_ext/hash_test.rb
|
216
|
+
- test/unit/core_ext/kernel_test.rb
|