leenookx-json-mangler 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.
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