jgrep 1.1.3 → 1.2.1

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/bin/jgrep CHANGED
@@ -3,67 +3,87 @@
3
3
  require 'jgrep'
4
4
  require 'optparse'
5
5
 
6
- options = {:flat => false}
6
+ options = {:flat => false, :start => nil}
7
7
 
8
- OptionParser.new do |opts|
9
- opts.banner = "Usage: jgrep [options] \"expression\""
10
- opts.on("-s", "--simple FIELD", "Display a single field from each of the resulting json documents") do |field|
11
- options[:field] = field
12
- end
13
- opts.on("-f", "--flat", "Display non pretty json") do
14
- options[:flat] = true
15
- end
16
- opts.on("-v", "--verbose", "Verbose output") do
17
- JGrep::verbose_on
18
- end
8
+ begin
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: jgrep [options] \"expression\""
11
+ opts.on("-s", "--simple FIELD[s]", "Display a single field from each of the resulting json documents") do |field|
12
+ if (field.split(" ")).size > 1
13
+ options[:field] = field.split(" ")
14
+ else
15
+ options[:field] = field
16
+ end
17
+ end
19
18
 
20
- end.parse!
19
+ opts.on("-c", "--compact", "Display non pretty json") do
20
+ options[:flat] = true
21
+ end
22
+
23
+ opts.on("-f", "--flatten", "Makes output as flat as possible") do
24
+ JGrep::flatten_on
25
+ end
26
+
27
+ opts.on("-i", "--input [FILENAME]", "Specify input file to parse") do |filename|
28
+ options[:file] = filename
29
+ end
30
+
31
+ opts.on("-v", "--verbose", "Verbose output") do
32
+ JGrep::verbose_on
33
+ end
34
+
35
+ opts.on("--start [FIELD]", "Where in the data to start from") do |field|
36
+ options[:start] = field
37
+ end
38
+ end.parse!
39
+ rescue OptionParser::InvalidOption => e
40
+ puts e.to_s.capitalize
41
+ exit 1
42
+ end
21
43
 
22
44
  begin
23
45
  expression = nil
24
46
 
25
47
  #Identify the expression from command line arguments
26
48
  ARGV.each do |argument|
27
- if argument =~ /<|>|=/
49
+ if argument =~ /<|>|=|\+|-/
28
50
  expression = argument
29
51
  ARGV.delete(argument)
30
52
  end
31
53
  end
32
54
 
33
- #Load json from standard in if tty is false
55
+ expression = "" if expression.nil?
56
+
57
+ #Load json from standard input if tty is false
34
58
  #else find and load file from command line arugments
35
59
  unless STDIN.tty?
36
60
  json = STDIN.read
37
61
  else
38
- if ARGV.size == 1
39
- json = File.read(ARGV.first)
62
+ if options[:file]
63
+ json = File.read(options[:file])
40
64
  else
41
65
  raise "No json input specified"
42
66
  end
43
67
  end
44
68
 
45
69
  unless options[:field]
46
- result = JGrep::jgrep((json), expression)
70
+ result = JGrep::jgrep((json), expression, nil, options[:start])
47
71
  unless result == []
48
72
  (options[:flat] == false) ? puts(JSON.pretty_generate(result)) : puts(result.to_json)
49
73
  end
50
- else
51
- if options[:field] =~ /=|<|>/
52
- raise "'-s' flag requires a field to display"
53
- end
54
74
 
55
- result = JGrep::jgrep((json), expression)
75
+ else
76
+ if options[:field].is_a? Array
77
+ JGrep::validate_filters(options[:field])
78
+ puts(JSON.pretty_generate(JGrep::jgrep((json), expression, options[:field], options[:start])))
56
79
 
57
- result.each do |r|
58
- options[:field].split(".").each do |node|
59
- unless r.is_a? Array
60
- r = r[node]
61
- else
62
- break
63
- end
64
- end
65
- unless r.nil?
66
- (!r.is_a?(Array) && !r.is_a?(Hash)) ? puts(r) : puts(JSON.pretty_generate(r))
80
+ else
81
+ JGrep::validate_filters(options[:field])
82
+ result = JGrep::jgrep((json), expression, options[:field], options[:start])
83
+ unless result.first.is_a?(Hash) || result.flatten.first.is_a?(Hash)
84
+ result.map{|x| puts x unless x.nil?}
85
+ else
86
+ puts(JSON.pretty_generate(result))
67
87
  end
68
88
  end
69
89
  end
data/jgrep.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "jgrep"
3
- s.version = "1.1.3"
3
+ s.version = "1.2.1"
4
4
 
5
5
  s.authors = ["P Loubser"]
6
- s.date = %q{2011-07-27}
6
+ s.date = %q{2011-08-01}
7
7
  s.default_executable = "jgrep"
8
8
  s.description = "Compare a list of json documents to a simple logical language and returns matches as output"
9
9
  s.email = ["ploubser@gmail.com"]
data/lib/jgrep.rb CHANGED
@@ -7,38 +7,60 @@ require 'json'
7
7
 
8
8
  module JGrep
9
9
  @verbose = false
10
+ @flatten = false
10
11
 
11
12
  def self.verbose_on
12
13
  @verbose = true
13
14
  end
14
15
 
16
+ def self.flatten_on
17
+ @flatten = true
18
+ end
19
+
15
20
  #Method parses json and returns documents that match the logical expression
16
- def self.jgrep(json, expression)
21
+ def self.jgrep(json, expression, filters = nil, start = nil)
17
22
  errors = ""
18
23
  begin
19
- call_stack = Parser.new(expression).execution_stack
20
- result = []
24
+ JSON.create_id = nil
21
25
  json = JSON.parse(json)
22
- json.each do |document|
23
- begin
24
- if eval_statement(document, call_stack)
25
- result << document
26
- end
27
- rescue Exception => e
28
- if @verbose
29
- pp document
30
- STDERR.puts "Error - #{e} \n\n"
31
- else
32
- errors = "One or more the json documents could not be parsed. Run jgrep -v for to display documents"
26
+ if json.is_a? Hash
27
+ json = [json]
28
+ end
29
+
30
+ json = filter_json(json, start).flatten if start
31
+
32
+ result = []
33
+ unless expression == ""
34
+ call_stack = Parser.new(expression).execution_stack
35
+
36
+ json.each do |document|
37
+ begin
38
+ if eval_statement(document, call_stack)
39
+ result << document
40
+ end
41
+ rescue Exception => e
42
+ if @verbose
43
+ require 'pp'
44
+ pp document
45
+ STDERR.puts "Error - #{e} \n\n"
46
+ else
47
+ errors = "One or more the json documents could not be parsed. Run jgrep -v for to display documents"
48
+ end
33
49
  end
34
50
  end
51
+ else
52
+ result = json
35
53
  end
36
54
 
37
55
  unless errors == ""
38
56
  puts errors
39
57
  end
40
58
 
41
- return result
59
+ unless filters
60
+ return result
61
+ else
62
+ filter_json(result, filters)
63
+ end
42
64
 
43
65
  rescue JSON::ParserError => e
44
66
  STDERR.puts "Error. Invalid JSON given"
@@ -47,6 +69,54 @@ module JGrep
47
69
 
48
70
  end
49
71
 
72
+ #Strips filters from json documents and returns those values as a less bloated json document
73
+ def self.filter_json(documents, filters)
74
+ result = []
75
+
76
+ if filters.is_a? Array
77
+ documents.each do |doc|
78
+ tmp_json = {}
79
+ filters.each do |filter|
80
+ filtered_result = dig_path(doc, filter)
81
+ unless (filtered_result == doc) || filtered_result.nil?
82
+ tmp_json[filter] = filtered_result
83
+ end
84
+ end
85
+ result << tmp_json
86
+ end
87
+
88
+ result = result.flatten if (result.size == 1 && @flatten == true)
89
+ return result
90
+
91
+ else
92
+ documents.each do |r|
93
+ filtered_result = dig_path(r, filters)
94
+ unless (filtered_result == r) || filtered_result.nil?
95
+ result << filtered_result
96
+ end
97
+ end
98
+
99
+ result = result.flatten if (result.size == 1 && @flatten == true)
100
+ return result
101
+ end
102
+ end
103
+
104
+ #Validates if filters do not match any of the parser's logical tokens
105
+ def self.validate_filters(filters)
106
+ if filters.is_a? Array
107
+ filters.each do |filter|
108
+ if filter =~ /=|<|>|^and$|^or$|^!$|^not$/
109
+ raise "Invalid field for -s filter : '#{filter}'"
110
+ end
111
+ end
112
+ else
113
+ if filters =~ /=|<|>|^and$|^or$|^!$|^not$/
114
+ raise "Invalid field for -s filter : '#{filters}'"
115
+ end
116
+ end
117
+ return
118
+ end
119
+
50
120
  #Correctly format values so we can do the correct type of comparison
51
121
  def self.format(kvalue, value)
52
122
  if kvalue.to_s =~ /^\d+$/ && value.to_s =~ /^\d+$/
@@ -58,6 +128,34 @@ module JGrep
58
128
  end
59
129
  end
60
130
 
131
+ def self.present?(document, statement)
132
+ statement.split(".").each do |key|
133
+ if document.is_a? Hash
134
+ if document.has_value? nil
135
+ document.each do |k, v|
136
+ if document[k] == nil
137
+ document[k] = "null"
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ if document.is_a? Array
144
+ rval = false
145
+ document.each do |doc|
146
+ rval ||= present?(doc, key)
147
+ end
148
+ return rval
149
+ end
150
+
151
+ document = document[key]
152
+ if document.nil?
153
+ return false
154
+ end
155
+ end
156
+ return true
157
+ end
158
+
61
159
  #Check if key=value is present in document
62
160
  def self.has_object?(document, statement)
63
161
 
@@ -74,7 +172,11 @@ module JGrep
74
172
  key.split(".").each_with_index do |item,i|
75
173
  tmp = tmp[item]
76
174
  if tmp.nil?
77
- return false
175
+ if item == key.split(".").last
176
+ tmp = "null"
177
+ else
178
+ return false
179
+ end
78
180
  end
79
181
  result = false
80
182
  if tmp.is_a? Array
@@ -82,7 +184,7 @@ module JGrep
82
184
  end
83
185
  end
84
186
 
85
- tmp, value = format(tmp, value.gsub(/"|'/, ""))
187
+ tmp, value = format(tmp, (value.gsub(/"|'/, "") unless value.nil?))
86
188
 
87
189
  case op
88
190
  when "="
@@ -170,6 +272,10 @@ module JGrep
170
272
  else
171
273
  result << has_object?(document, expression.values.first)
172
274
  end
275
+ when "+"
276
+ result << present?(document, expression.values.first)
277
+ when "-"
278
+ result << !(present?(document, expression.values.first))
173
279
  when "and"
174
280
  result << "&&"
175
281
  when "or"
@@ -185,4 +291,41 @@ module JGrep
185
291
 
186
292
  return eval(result.join(" "))
187
293
  end
294
+
295
+ def self.dig_path(json, path)
296
+
297
+ json = json[path.split(".").first] if json.is_a? Hash
298
+
299
+ if json.is_a?(Hash)
300
+ if path == path.split(".").first
301
+ return json
302
+ else
303
+ return dig_path(json, (path.match(/\./) ? path.split(".").drop(1).join(".") : path))
304
+ end
305
+
306
+ elsif json.is_a? Array
307
+ if path == path.split(".").first && (json.first.is_a?(Hash) && !(json.first.keys.include?(path)))
308
+ return json
309
+ else
310
+ tmp = []
311
+ json.each do |j|
312
+ tmp_path = dig_path(j, (path.match(/\./) ? path.split(".").drop(1).join(".") : path))
313
+ unless tmp_path.nil?
314
+ tmp << tmp_path
315
+ end
316
+ end
317
+ unless tmp.empty?
318
+ return tmp
319
+ end
320
+ end
321
+
322
+ elsif json.nil?
323
+ return nil
324
+
325
+ else
326
+ return json
327
+
328
+ end
329
+
330
+ end
188
331
  end
data/lib/parser/parser.rb CHANGED
@@ -33,7 +33,7 @@ module JGrep
33
33
  case c_token
34
34
 
35
35
  when "and"
36
- unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
36
+ unless (n_token =~ /not|statement|\(|\+|-/) || (scanner.token_index == scanner.arguments.size)
37
37
  raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement' or '('. Found '#{n_token_value}'"
38
38
  end
39
39
 
@@ -44,7 +44,7 @@ module JGrep
44
44
  end
45
45
 
46
46
  when "or"
47
- unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
47
+ unless (n_token =~ /not|statement|\(|\+|-/) || (scanner.token_index == scanner.arguments.size)
48
48
  raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement', '('. Found '#{n_token_value}'"
49
49
  end
50
50
 
@@ -55,7 +55,7 @@ module JGrep
55
55
  end
56
56
 
57
57
  when "not"
58
- unless n_token =~ /statement|\(|not/
58
+ unless n_token =~ /statement|\(|not|\+|-/
59
59
  raise "Error at column #{scanner.token_index}. \nExpected 'statement' or '('. Found '#{n_token_value}'"
60
60
  end
61
61
 
@@ -69,7 +69,7 @@ module JGrep
69
69
 
70
70
  if c_token_value =~ /!=/
71
71
  c_token_value = c_token_value.gsub("!=", "=")
72
- @execution_stack << {"not", "not"}
72
+ @execution_stack << {"not" => "not"}
73
73
  end
74
74
 
75
75
  unless n_token =~ /and|or|\)/
@@ -78,6 +78,21 @@ module JGrep
78
78
  end
79
79
  end
80
80
 
81
+ when "+"
82
+ unless n_token =~ /and|or|\)/
83
+ unless n_token.nil?
84
+ raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', ')'. Found '#{n_token_value}'"
85
+ end
86
+ end
87
+
88
+ when "-"
89
+ unless n_token =~ /and|or|\)/
90
+ unless n_token.nil?
91
+ raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', ')'. Found '#{n_token_value}'"
92
+ end
93
+ end
94
+
95
+
81
96
  when ")"
82
97
  unless (n_token =~ /|and|or|not|\(/)
83
98
  unless n_token.nil?
@@ -87,7 +102,7 @@ module JGrep
87
102
  parenth += 1
88
103
 
89
104
  when "("
90
- unless n_token =~ /statement|not|\(/
105
+ unless n_token =~ /statement|not|\(|\+|-/
91
106
  raise "Error at column #{scanner.token_index}. \nExpected 'statement', '(', not. Found '#{n_token_value}'"
92
107
  end
93
108
  parenth -= 1
@@ -54,6 +54,31 @@ module JGrep
54
54
  gen_statement
55
55
  end
56
56
 
57
+ when "+"
58
+ value = ""
59
+ i = @token_index + 1
60
+
61
+ begin
62
+ value += @arguments.split("")[i]
63
+ i += 1
64
+ end until (i >= @arguments.size) || (@arguments.split("")[i] =~ /\s|\)/)
65
+
66
+ @token_index = i - 1
67
+ return "+", value
68
+
69
+ when "-"
70
+ value = ""
71
+ i = @token_index + 1
72
+
73
+ begin
74
+ value += @arguments.split("")[i]
75
+ i += 1
76
+ end until (i >= @arguments.size) || (@arguments.split("")[i] =~ /\s|\)/)
77
+
78
+ @token_index = i - 1
79
+ return "-", value
80
+
81
+
57
82
  when " "
58
83
  return " ", " "
59
84
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
+ - 2
7
8
  - 1
8
- - 3
9
- version: 1.1.3
9
+ version: 1.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - P Loubser
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-07-27 00:00:00 +01:00
17
+ date: 2011-08-01 00:00:00 +01:00
18
18
  default_executable: jgrep
19
19
  dependencies: []
20
20