jgrep 1.1.3 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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