leenookx-json-mangler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1 @@
1
+ {"doc": {"title": "A test document", "author": "My Name", "published": "1999", "keywords": ["rubbish", "nonsense"]} }
@@ -0,0 +1 @@
1
+ { "doc": { "info": {"author": "My Name", "title": "Document name"}, "bibtex": {"published": "1999", "ISBN": "1234567890"} }, "background": {"submitted": "2001"} }
@@ -0,0 +1,9 @@
1
+ {
2
+ "doc": {
3
+ "title": "My example document",
4
+ "author": "leenookx",
5
+ "author": "Ghost author",
6
+ "keyword": "example",
7
+ "keyword": "keyword2"
8
+ }
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "doc": {
3
+ "title": "My example document",
4
+ "author": "leenookx",
5
+ "keyword": "example"
6
+ }
7
+ }
8
+
@@ -0,0 +1,23 @@
1
+ {
2
+ "glossary": {
3
+ "title": "example glossary",
4
+ "GlossDiv": {
5
+ "title": "S",
6
+ "GlossList": {
7
+ "GlossEntry": {
8
+ "ID": "SGML",
9
+ "SortAs": "SGML",
10
+ "GlossTerm": "Standard Generalized Markup Language",
11
+ "Acronym": "SGML",
12
+ "Abbrev": "ISO 8879:1986",
13
+ "GlossDef": {
14
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
15
+ "GlossSeeAlso": ["GML", "XML"]
16
+ },
17
+ "GlossSee": "markup"
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
23
+
@@ -0,0 +1,11 @@
1
+ Feature: Creation
2
+ In order to test that the JSON stream can be pruned
3
+ As a developer
4
+ I want to prune some mangling objects
5
+
6
+ Scenario: Prune branch from JSON stream Object
7
+ Given I already have a valid JSON mock stream
8
+ When I try and create a mangler object
9
+ And prune the 'glossary' branch
10
+ Then the 'glossary' branch is removed
11
+
@@ -0,0 +1,24 @@
1
+ Given /^I already have a valid JSON mock stream$/ do
2
+ @response = File.open('features/mocks/valid.json','r') {|f| f.readlines.to_s}
3
+ end
4
+
5
+ When /^I try and create a mangler object$/ do
6
+ @json_mangler = JSONMangler.new( @response )
7
+ end
8
+
9
+ Then /^the resultant object is valid$/ do
10
+ assert_equal(@json_mangler.valid, true)
11
+ end
12
+
13
+ Then /^I should get an invalid JSON mangler object$/ do
14
+ assert_equal(@json_mangler.valid, false)
15
+ end
16
+
17
+ Given /^I already have an empty JSON mock stream$/ do
18
+ @response = File.open('features/mocks/empty.json','r') {|f| f.readlines.to_s}
19
+ end
20
+
21
+ Then /^the output equals the input JSON mock stream$/ do
22
+ assert_not_equal("nil", @json_mangler.to_json)
23
+ end
24
+
@@ -0,0 +1,63 @@
1
+ Given /^I already have a JSON stream$/ do
2
+ @extraction_input = File.open('features/mocks/extraction.json', 'r') {|f| f.readlines.to_s}
3
+ end
4
+
5
+ When /^I try and extract a tag '(.*)' which doesn't exist$/ do |tag|
6
+ je = JSONExtractor.new
7
+ @extraction_results = je.extract(@extraction_input, "object", tag)
8
+ end
9
+
10
+ Then /^the resultant JSON is empty$/ do
11
+ extraction_comparison = File.open('features/test_comparisons/empty_extraction.json', 'r') {|f| f.readlines.to_s}
12
+
13
+ assert_equal(extraction_comparison, @extraction_results)
14
+ end
15
+
16
+ When /^I try and extract the authors name$/ do
17
+ je = JSONExtractor.new
18
+ @extraction_results = je.extract(@extraction_input, "object", "author")
19
+ end
20
+
21
+ Then /^the resultant JSON contains the author details$/ do
22
+ extraction_comparison = File.open('features/test_comparisons/author_extraction.json', 'r') {|f| f.readlines.to_s}
23
+
24
+ assert_equal(extraction_comparison, @extraction_results)
25
+ end
26
+
27
+ When /^I try and extract any tags where the publishing date was 1999$/ do
28
+ je = JSONExtractor.new
29
+ @extraction_results = je.extract(@extraction_input, "data", "1999")
30
+ end
31
+
32
+ Then /^the resultant JSON contains the publishing date$/ do
33
+ extraction_comparison = File.open('features/test_comparisons/published_extraction.json', 'r') {|f| f.readlines.to_s}
34
+
35
+ assert_equal(extraction_comparison, @extraction_results)
36
+ end
37
+
38
+ Given /^I already have a complicated JSON stream$/ do
39
+ @extraction_input = File.open('features/mocks/extraction2.json', 'r') {|f| f.readlines.to_s}
40
+ end
41
+
42
+ When /^I try and extract data from the keyword array$/ do
43
+ je = JSONExtractor.new
44
+ @extraction_results = je.extract(@extraction_input, "data", "nonsense")
45
+ end
46
+
47
+ Then /^the resultant JSON data contains the array$/ do
48
+ extraction_comparison = File.open('features/test_comparisons/array_extraction.json', 'r') {|f| f.readlines.to_s}
49
+
50
+ assert_equal(extraction_comparison, @extraction_results)
51
+ end
52
+
53
+ When /^I try and extract data from the keyword array with a depth of 2$/ do
54
+ je = JSONExtractor.new
55
+ @extraction_results = je.extract(@extraction_input, "data", "1999", 2)
56
+ end
57
+
58
+ Then /^the result JSON data contains the array and its parent$/ do
59
+ extraction_comparison = File.open('features/test_comparisons/2level_extraction.json', 'r') {|f| f.readlines.to_s}
60
+
61
+ assert_equal(extraction_comparison, @extraction_results)
62
+ end
63
+
@@ -0,0 +1,24 @@
1
+ Given /^I already have a valid JSON mock stream containing groups$/ do
2
+ @response = File.open('features/mocks/grouped.json', 'r') {|f| f.readlines.to_s}
3
+ end
4
+
5
+ When /^I group the contents of the mangling object$/ do
6
+ @json_mangler.group
7
+ end
8
+
9
+ Then /^the JSON mangler remains valid$/ do
10
+ assert_equal(@json_mangler, true)
11
+ end
12
+
13
+ Then /^the JSON stream is grouped correctly$/ do
14
+ pending
15
+ end
16
+
17
+ Given /^I already have a valid JSON mock stream containing no groups$/ do
18
+ @response = File.open('features/mocks/non_grouped.json', 'r') {|f| f.readlines.to_s}
19
+ end
20
+
21
+ Then /^the JSON stream remains the same as at the start$/ do
22
+ pending
23
+ end
24
+
@@ -0,0 +1,10 @@
1
+ When /^prune the '(.*)' branch$/ do |branch|
2
+ @json_mangler.prune( branch )
3
+ end
4
+
5
+ Then /^the '(.*)' branch is removed$/ do |branch|
6
+ response = JSON.parse( @json_mangler.to_json )
7
+
8
+ assert_equal(response[branch], nil)
9
+ end
10
+
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'spec/expectations'
3
+ require "test/unit"
4
+
5
+ # We'll have some unicode support please.
6
+ require 'cucumber/formatter/unicode'
7
+
8
+ mangler_file = File.join(File.dirname(__FILE__), *%w[.. .. lib json_mangler.rb])
9
+ require mangler_file
10
+ extractor_file = File.join(File.dirname(__FILE__), *%w[.. .. lib json_extractor.rb])
11
+ require extractor_file
12
+
13
+
14
+ World do
15
+ include Test::Unit::Assertions
16
+ end
17
+
@@ -0,0 +1 @@
1
+ { "results": {"title": "A test document", "author": "My Name", "published": "1999", "keywords": ["rubbish", "nonsense"]} }
@@ -0,0 +1 @@
1
+ { "results": {"keywords": ["rubbish", "nonsense"]} }
@@ -0,0 +1 @@
1
+ { "results": {"author": "My Name"} }
@@ -0,0 +1 @@
1
+ { "results": {} }
@@ -0,0 +1 @@
1
+ { "results": {"published": "1999"} }
@@ -0,0 +1,72 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{json-mangler}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["lee nookx"]
12
+ s.date = %q{2009-08-30}
13
+ s.description = %q{TODO}
14
+ s.email = %q{lnookx@googlemail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".autotest",
21
+ ".gitignore",
22
+ "History.txt",
23
+ "Manifest.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "features/create.feature",
28
+ "features/extraction.feature",
29
+ "features/group.feature",
30
+ "features/mocks/calais.json",
31
+ "features/mocks/empty.json",
32
+ "features/mocks/extraction.json",
33
+ "features/mocks/extraction2.json",
34
+ "features/mocks/grouped.json",
35
+ "features/mocks/non_grouped.json",
36
+ "features/mocks/valid.json",
37
+ "features/prune.feature",
38
+ "features/step_definitions/creation_steps.rb",
39
+ "features/step_definitions/extaction_steps.rb",
40
+ "features/step_definitions/groups_steps.rb",
41
+ "features/step_definitions/prune_steps.rb",
42
+ "features/support/env.rb",
43
+ "features/test_comparisons/2level_extraction.json",
44
+ "features/test_comparisons/array_extraction.json",
45
+ "features/test_comparisons/author_extraction.json",
46
+ "features/test_comparisons/empty_extraction.json",
47
+ "features/test_comparisons/published_extraction.json",
48
+ "json-mangler.gemspec",
49
+ "lib/json_extractor.rb",
50
+ "lib/json_mangler.rb",
51
+ "test/test_json_mangler.rb"
52
+ ]
53
+ s.has_rdoc = true
54
+ s.homepage = %q{http://github.com/leenookx/json-mangler}
55
+ s.rdoc_options = ["--charset=UTF-8"]
56
+ s.require_paths = ["lib"]
57
+ s.rubygems_version = %q{1.3.1}
58
+ s.summary = %q{JSON data format mangling tools.}
59
+ s.test_files = [
60
+ "test/test_json_mangler.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 2
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ else
69
+ end
70
+ else
71
+ end
72
+ end
@@ -0,0 +1,173 @@
1
+ require 'strscan'
2
+
3
+ # A basic JSON extraction utility inspired by the parser example given at
4
+ # http://rubyquiz.com/quiz155.html
5
+
6
+ class JSONExtractor
7
+
8
+ def initialize
9
+ @output = ""
10
+ @force_capture = false
11
+ end
12
+
13
+ def extract(input, extract_mode, search, depth = 1)
14
+ @output = ""
15
+ @force_capture = false
16
+ @capture_depth = depth - 1
17
+ @current_depth = 0
18
+
19
+ @input = StringScanner.new( input )
20
+ @searchval = '"' + search + '"'
21
+
22
+ if extract_mode == "object"
23
+ @mode = 1
24
+ elsif extract_mode == "data"
25
+ @mode = 2
26
+ else
27
+ error "Unknown mode '" + extract_mode + "'. Supported [object, data]"
28
+ end
29
+
30
+ puts "Extracting data based on '" + extract_mode + "' values."
31
+
32
+ parse_value
33
+
34
+ results = "{ \"results\": {" + @output + "} }\n"
35
+ end
36
+
37
+ private
38
+
39
+ def error(message)
40
+ if @input.eos?
41
+ raise "Unexpected end of input."
42
+ else
43
+ raise "#{message}: @ char #{@input.pos} :: #{@input.peek(@input.string.length)}"
44
+ end
45
+ end
46
+
47
+ def parse_value
48
+ trim_space
49
+ parse_object or
50
+ parse_array or
51
+ parse_string or
52
+ parse_number or
53
+ parse_keyword or
54
+ error("Illegal JSON value")
55
+ ensure
56
+ trim_space
57
+ end
58
+
59
+ def parse_object
60
+ if @input.scan(/\{\s*/)
61
+ output = ""
62
+ more_pairs = false
63
+ capture = false
64
+ while key = parse_string
65
+ @input.scan(/\s*:\s*/) or error("Expecting object separator")
66
+
67
+ if @mode == 1 and key == @searchval
68
+ capture = true
69
+ end
70
+
71
+ res = parse_value
72
+
73
+ if @mode == 2 and res == @searchval
74
+ @current_depth = @capture_depth
75
+ @force_capture = true
76
+ end
77
+
78
+ if @capture_depth > 0
79
+ output << key << ": " << res
80
+ end
81
+
82
+ if @force_capture or (@mode == 1 and key == @searchval and capture)
83
+ if @capture_depth == 0
84
+ output << key << ": " << res
85
+ end
86
+ capture = false
87
+ if @current_depth == 0
88
+ @output << output
89
+ @force_capture = false
90
+ else
91
+ @current_depth = @current_depth - 1
92
+ end
93
+ end
94
+
95
+ more_pairs = @input.scan(/\s*,\s*/) or break
96
+
97
+ if @capture_depth > 0
98
+ output << ", "
99
+ end
100
+ end
101
+ error("Missing object pair") if more_pairs
102
+ @input.scan(/\s*\}/) or error("Unclosed object")
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ def parse_array
109
+ if @input.scan(/\[\s*/)
110
+ array = "["
111
+ more_values = false
112
+ while contents = parse_value rescue nil
113
+ array << contents
114
+ more_values = @input.scan(/\s*,\s*/) or break
115
+ array << ", "
116
+ end
117
+ error("Missing value") if more_values
118
+ @input.scan(/\s*\]/) or error("Unclosed array")
119
+ array << "]"
120
+ array
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ def parse_string
127
+ if @input.scan(/"/)
128
+ string = '"'
129
+ while contents = parse_string_content || parse_string_escape
130
+ string << contents
131
+ end
132
+ @input.scan(/"/) or error("Unclosed string")
133
+ string = string + '"'
134
+ if @mode == 2 and string == @searchval
135
+ @force_capture = true
136
+ end
137
+ string
138
+ else
139
+ false
140
+ end
141
+ end
142
+
143
+ def parse_string_content
144
+ @input.scan(/[^\\"]+/) and @input.matched
145
+ end
146
+
147
+ def parse_string_escape
148
+ if @input.scan(%r{\\["\\/]})
149
+ @input.matched[-1]
150
+ elsif @input.scan(/\\[bfnrt]/)
151
+ eval(%Q{"#{@input.matched}"})
152
+ elsif @input.scan(/\\u[0-9a-fA-F]{4}/)
153
+ [Integer("0x#{@input.matched[2..-1]}")].pack("U")
154
+ else
155
+ false
156
+ end
157
+ end
158
+
159
+ def parse_number
160
+ @input.scan(/-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?\b/) and
161
+ eval(@input.matched)
162
+ end
163
+
164
+ def parse_keyword
165
+ @input.scan(/\b(?:true|false|null)\b/) and
166
+ eval(@input.matched.sub("null", "nil"))
167
+ end
168
+
169
+ def trim_space
170
+ @input.scan(/\s+/)
171
+ end
172
+ end
173
+
@@ -0,0 +1,39 @@
1
+ require 'json'
2
+
3
+ class JSONMangler
4
+ VERSION = '0.1.0'
5
+
6
+ def initialize( json_data )
7
+ ingest_json( json_data )
8
+ end
9
+
10
+ def valid
11
+ !@json_obj.nil?
12
+ end
13
+
14
+ def prune( keys )
15
+ if valid
16
+ keys.each do |key|
17
+ @json_obj.delete_if{|k, v| k == key }
18
+ end
19
+ end
20
+ end
21
+
22
+ def to_json
23
+ if valid
24
+ JSON.pretty_generate( @json_obj )
25
+ else
26
+ ""
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def ingest_json( json_string )
33
+ begin
34
+ @json_obj = JSON.parse( json_string )
35
+ rescue
36
+ @json_obj = nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "json_mangler"
3
+
4
+ class TestJsonMangler < Test::Unit::TestCase
5
+ def test_sanity
6
+ flunk "write tests or I will kneecap you"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: leenookx-json-mangler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - lee nookx
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-30 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TODO
17
+ email: lnookx@googlemail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .autotest
27
+ - .gitignore
28
+ - History.txt
29
+ - Manifest.txt
30
+ - README.rdoc
31
+ - Rakefile
32
+ - VERSION
33
+ - features/create.feature
34
+ - features/extraction.feature
35
+ - features/group.feature
36
+ - features/mocks/calais.json
37
+ - features/mocks/empty.json
38
+ - features/mocks/extraction.json
39
+ - features/mocks/extraction2.json
40
+ - features/mocks/grouped.json
41
+ - features/mocks/non_grouped.json
42
+ - features/mocks/valid.json
43
+ - features/prune.feature
44
+ - features/step_definitions/creation_steps.rb
45
+ - features/step_definitions/extaction_steps.rb
46
+ - features/step_definitions/groups_steps.rb
47
+ - features/step_definitions/prune_steps.rb
48
+ - features/support/env.rb
49
+ - features/test_comparisons/2level_extraction.json
50
+ - features/test_comparisons/array_extraction.json
51
+ - features/test_comparisons/author_extraction.json
52
+ - features/test_comparisons/empty_extraction.json
53
+ - features/test_comparisons/published_extraction.json
54
+ - json-mangler.gemspec
55
+ - lib/json_extractor.rb
56
+ - lib/json_mangler.rb
57
+ - test/test_json_mangler.rb
58
+ - LICENSE
59
+ has_rdoc: true
60
+ homepage: http://github.com/leenookx/json-mangler
61
+ licenses:
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: JSON data format mangling tools.
86
+ test_files:
87
+ - test/test_json_mangler.rb