jsonify 0.0.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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +84 -0
- data/Rakefile +5 -0
- data/jsonify.gemspec +28 -0
- data/lib/blank_slate.rb +109 -0
- data/lib/jsonify.rb +7 -0
- data/lib/jsonify/builder.rb +82 -0
- data/lib/jsonify/generate.rb +58 -0
- data/lib/jsonify/json_value.rb +110 -0
- data/lib/jsonify/rails.rb +19 -0
- data/lib/jsonify/version.rb +3 -0
- data/spec/builder_spec.rb +210 -0
- data/spec/generate_spec.rb +33 -0
- data/spec/json_value_spec.rb +15 -0
- data/spec/jsonify_spec.rb +4 -0
- data/spec/spec_helper.rb +7 -0
- metadata +149 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2011 Bill Siggelkow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Jsonify
|
|
2
|
+
|
|
3
|
+
[Jsonify](https://github.com/bsiggelkow/jsonify) Jsonify is to JSON as [Builder](https://github.com/jimweirich/builder) is to XML.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Jsonify provides a ___builder___ style engine for creating correct JSON representations of Ruby objects.
|
|
8
|
+
|
|
9
|
+
Jsonify hooks into Rails ActionView to allow you to create JSON view templates in much the same way that you can use Builder for XML templates.
|
|
10
|
+
|
|
11
|
+
## Motivation
|
|
12
|
+
|
|
13
|
+
JSON and XML are without a doubt the most common representations used by RESTful applications. Jsonify was built around the notion that these representations belong in the ___view___ layer of the application.
|
|
14
|
+
For XML representations, Rails makes this easy through its support of Builder templates, but, when it comes to JSON, there is no clear approach.
|
|
15
|
+
|
|
16
|
+
___more coming soon___
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
gem install jsonify
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Standalone
|
|
25
|
+
|
|
26
|
+
# Create some objects that represent a person and associated hyperlinks
|
|
27
|
+
person = Struct.new(:first_name,:last_name).new('George','Burdell')
|
|
28
|
+
links = [
|
|
29
|
+
['self', 'http://example.com/people/123'],
|
|
30
|
+
['school', 'http://gatech.edu'],
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Build this information as JSON
|
|
34
|
+
require 'jsonify'
|
|
35
|
+
json = Jsonify::Builder.new(:pretty => true)
|
|
36
|
+
|
|
37
|
+
json.result do
|
|
38
|
+
json.alumnus do
|
|
39
|
+
json.fname person.first_name
|
|
40
|
+
json.lname person.last_name
|
|
41
|
+
end
|
|
42
|
+
json.links do
|
|
43
|
+
json.map!(links) do |link|
|
|
44
|
+
{:rel => link.first, :href => link.last}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Evaluate the result to a string
|
|
50
|
+
json.compile!
|
|
51
|
+
|
|
52
|
+
Results in ...
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
"result": {
|
|
56
|
+
"alumnus": {
|
|
57
|
+
"fname": "George",
|
|
58
|
+
"lname": "Burdell"
|
|
59
|
+
},
|
|
60
|
+
"links": [
|
|
61
|
+
{
|
|
62
|
+
"rel": "self",
|
|
63
|
+
"href": "http://example.com/people/123"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"rel": "school",
|
|
67
|
+
"href": "http://gatech.edu"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### View Templates
|
|
75
|
+
|
|
76
|
+
___coming soon___
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
This project is released under the MIT license.
|
|
81
|
+
|
|
82
|
+
## Authors
|
|
83
|
+
|
|
84
|
+
* [Bill Siggelkow](https://github.com/bsiggelkow)
|
data/Rakefile
ADDED
data/jsonify.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "jsonify/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "jsonify"
|
|
7
|
+
s.version = Jsonify::VERSION
|
|
8
|
+
s.authors = ["Bill Siggelkow"]
|
|
9
|
+
s.email = ["bsiggelkow@me.com"]
|
|
10
|
+
s.homepage = "http://github.com/bsiggelkow/jsonify"
|
|
11
|
+
s.summary = %q{Turn Ruby objects into JSON}
|
|
12
|
+
s.description = %q{Turn Ruby objects into JSON -- correctly!}
|
|
13
|
+
|
|
14
|
+
s.rubyforge_project = s.name
|
|
15
|
+
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
s.add_dependency 'json'
|
|
22
|
+
s.add_dependency "actionpack", "~> 3.0.0"
|
|
23
|
+
|
|
24
|
+
s.add_development_dependency 'bundler'
|
|
25
|
+
s.add_development_dependency 'rake'
|
|
26
|
+
s.add_development_dependency 'rspec'
|
|
27
|
+
s.add_development_dependency 'autotest'
|
|
28
|
+
end
|
data/lib/blank_slate.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#--
|
|
3
|
+
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
|
|
6
|
+
# Permission is granted for use, copying, modification, distribution,
|
|
7
|
+
# and distribution of modified versions of this work as long as the
|
|
8
|
+
# above copyright notice is included.
|
|
9
|
+
#++
|
|
10
|
+
|
|
11
|
+
######################################################################
|
|
12
|
+
# BlankSlate provides an abstract base class with no predefined
|
|
13
|
+
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
|
14
|
+
# BlankSlate is useful as a base class when writing classes that
|
|
15
|
+
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
|
16
|
+
#
|
|
17
|
+
class BlankSlate
|
|
18
|
+
class << self
|
|
19
|
+
|
|
20
|
+
# Hide the method named +name+ in the BlankSlate class. Don't
|
|
21
|
+
# hide +instance_eval+ or any method beginning with "__".
|
|
22
|
+
def hide(name)
|
|
23
|
+
if instance_methods.include?(name.to_s) and
|
|
24
|
+
name !~ /^(__|instance_eval)/
|
|
25
|
+
@hidden_methods ||= {}
|
|
26
|
+
@hidden_methods[name.to_sym] = instance_method(name)
|
|
27
|
+
undef_method name
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_hidden_method(name)
|
|
32
|
+
@hidden_methods ||= {}
|
|
33
|
+
@hidden_methods[name] || superclass.find_hidden_method(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Redefine a previously hidden method so that it may be called on a blank
|
|
37
|
+
# slate object.
|
|
38
|
+
def reveal(name)
|
|
39
|
+
hidden_method = find_hidden_method(name)
|
|
40
|
+
fail "Don't know how to reveal method '#{name}'" unless hidden_method
|
|
41
|
+
define_method(name, hidden_method)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
instance_methods.each { |m| hide(m) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
######################################################################
|
|
49
|
+
# Since Ruby is very dynamic, methods added to the ancestors of
|
|
50
|
+
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
|
|
51
|
+
# list of available BlankSlate methods. We handle this by defining a
|
|
52
|
+
# hook in the Object and Kernel classes that will hide any method
|
|
53
|
+
# defined after BlankSlate has been loaded.
|
|
54
|
+
#
|
|
55
|
+
module Kernel
|
|
56
|
+
class << self
|
|
57
|
+
alias_method :blank_slate_method_added, :method_added
|
|
58
|
+
|
|
59
|
+
# Detect method additions to Kernel and remove them in the
|
|
60
|
+
# BlankSlate class.
|
|
61
|
+
def method_added(name)
|
|
62
|
+
result = blank_slate_method_added(name)
|
|
63
|
+
return result if self != Kernel
|
|
64
|
+
BlankSlate.hide(name)
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
######################################################################
|
|
71
|
+
# Same as above, except in Object.
|
|
72
|
+
#
|
|
73
|
+
class Object
|
|
74
|
+
class << self
|
|
75
|
+
alias_method :blank_slate_method_added, :method_added
|
|
76
|
+
|
|
77
|
+
# Detect method additions to Object and remove them in the
|
|
78
|
+
# BlankSlate class.
|
|
79
|
+
def method_added(name)
|
|
80
|
+
result = blank_slate_method_added(name)
|
|
81
|
+
return result if self != Object
|
|
82
|
+
BlankSlate.hide(name)
|
|
83
|
+
result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def find_hidden_method(name)
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
######################################################################
|
|
93
|
+
# Also, modules included into Object need to be scanned and have their
|
|
94
|
+
# instance methods removed from blank slate. In theory, modules
|
|
95
|
+
# included into Kernel would have to be removed as well, but a
|
|
96
|
+
# "feature" of Ruby prevents late includes into modules from being
|
|
97
|
+
# exposed in the first place.
|
|
98
|
+
#
|
|
99
|
+
class Module
|
|
100
|
+
alias blankslate_original_append_features append_features
|
|
101
|
+
def append_features(mod)
|
|
102
|
+
result = blankslate_original_append_features(mod)
|
|
103
|
+
return result if mod != Object
|
|
104
|
+
instance_methods.each do |name|
|
|
105
|
+
BlankSlate.hide(name)
|
|
106
|
+
end
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/jsonify.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Jsonify
|
|
2
|
+
class Builder < BlankSlate
|
|
3
|
+
|
|
4
|
+
def initialize(options={})
|
|
5
|
+
@verify = options[:verify].nil? ? false : options[:verify]
|
|
6
|
+
@pretty = options[:pretty].nil? ? false : options[:pretty]
|
|
7
|
+
reset!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def reset!
|
|
11
|
+
@level = 0
|
|
12
|
+
@stack = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Builder-style methods
|
|
16
|
+
def tag!(sym, *args, &block)
|
|
17
|
+
method_missing(sym, *args, &block)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def compile!
|
|
21
|
+
result = (@stack[0] ? @stack[0].evaluate : {}.to_json)
|
|
22
|
+
JSON.parse(result) if @verify
|
|
23
|
+
@pretty ? JSON.pretty_generate(JSON.parse(result)) : result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add!(value)
|
|
27
|
+
__current.add Generate.value(value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def method_missing(sym, *args, &block)
|
|
31
|
+
if block
|
|
32
|
+
pair = Generate.pair_value(sym)
|
|
33
|
+
__current.add(pair)
|
|
34
|
+
@level += 1
|
|
35
|
+
block.call(self)
|
|
36
|
+
pair.value = __current
|
|
37
|
+
@level -= 1
|
|
38
|
+
else
|
|
39
|
+
if sym && args && args.length > 0
|
|
40
|
+
__current.add Generate.pair_value(sym, args.length > 1 ? args : args.first)
|
|
41
|
+
end
|
|
42
|
+
__current
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def object!
|
|
47
|
+
__set_current JsonObject.new
|
|
48
|
+
yield __current
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def array!
|
|
52
|
+
__set_current JsonArray.new
|
|
53
|
+
@level += 1
|
|
54
|
+
yield @stack[@level-1]
|
|
55
|
+
@level -= 1
|
|
56
|
+
__current
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def map!(array)
|
|
60
|
+
__set_current JsonArray.new
|
|
61
|
+
array.each do |item|
|
|
62
|
+
__current << (yield item)
|
|
63
|
+
end
|
|
64
|
+
__current
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
alias_method :collect!, :map!
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Inheriting from BlankSlate requires these funky (aka non-idiomatic) method names
|
|
72
|
+
|
|
73
|
+
def __current
|
|
74
|
+
@stack[@level] ||= JsonObject.new
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def __set_current(val)
|
|
78
|
+
@stack[@level] = val
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Jsonify
|
|
2
|
+
module Generate
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
|
|
6
|
+
def value(val)
|
|
7
|
+
case val
|
|
8
|
+
when JsonValue; val
|
|
9
|
+
when String; string_value val
|
|
10
|
+
when Numeric; number_value val
|
|
11
|
+
when TrueClass; true_value
|
|
12
|
+
when FalseClass; false_value
|
|
13
|
+
when NilClass; null_value
|
|
14
|
+
when Array; array_value val
|
|
15
|
+
when Hash; object_value val
|
|
16
|
+
else string_value val
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pair_value(key,val=nil)
|
|
21
|
+
JsonPair.new(key,value(val))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def string_value(val)
|
|
25
|
+
JsonString.new(val)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def object_value(hash)
|
|
29
|
+
json_object = JsonObject.new
|
|
30
|
+
hash.each { |key,val| json_object.add( pair_value(key, val) ) }
|
|
31
|
+
json_object
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def array_value(vals)
|
|
35
|
+
JsonArray.new(Array(vals).map{ |v| value v })
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def number_value(val)
|
|
39
|
+
JsonNumber.new(val)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def true_value
|
|
43
|
+
@json_true ||= JsonTrue.new # memoize
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def false_value
|
|
47
|
+
@json_false ||= JsonFalse.new # memoize
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def null_value
|
|
51
|
+
@json_null ||= JsonNull.new # memoize
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Jsonify
|
|
2
|
+
class JsonValue
|
|
3
|
+
attr_accessor :values
|
|
4
|
+
|
|
5
|
+
def initialize(values=nil)
|
|
6
|
+
@values = values || []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def evaluate
|
|
10
|
+
wrap values.map {|v| v.evaluate}.join(',')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(jsonValue)
|
|
14
|
+
values << Generate.value(jsonValue)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class JsonObject < JsonValue
|
|
20
|
+
def initialize(values=nil)
|
|
21
|
+
@values = values || {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def wrap(joined_values)
|
|
25
|
+
"{#{joined_values}}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def values
|
|
29
|
+
@values.values
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add(val)
|
|
33
|
+
raise ArgumentError.new("Cannot add #{val} to JsonOject") unless (Array === val || JsonPair === val)
|
|
34
|
+
val = JsonPair.new(val.shift, val.length <= 1 ? val.first : val) if Array === val
|
|
35
|
+
@values.store(val.key, val)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias_method :<<, :add
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class JsonArray < JsonValue
|
|
43
|
+
|
|
44
|
+
def wrap(joined_values)
|
|
45
|
+
"[#{joined_values}]"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def add(value)
|
|
49
|
+
if JsonPair === value # wrap JsonPair in a JsonObject
|
|
50
|
+
object = JsonObject.new
|
|
51
|
+
object.add value
|
|
52
|
+
value = object
|
|
53
|
+
end
|
|
54
|
+
super(value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
alias_method :<<, :add
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class JsonPair < JsonValue
|
|
62
|
+
attr_accessor :key, :value
|
|
63
|
+
def initialize(key, value=nil)
|
|
64
|
+
@key = key.to_s
|
|
65
|
+
@value = Generate.value(value)
|
|
66
|
+
end
|
|
67
|
+
def evaluate
|
|
68
|
+
%Q{#{key.to_json}:#{value.evaluate}}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class JsonString < JsonValue
|
|
73
|
+
attr_accessor :value
|
|
74
|
+
def initialize(value)
|
|
75
|
+
@value = value.to_s
|
|
76
|
+
end
|
|
77
|
+
def evaluate
|
|
78
|
+
value.to_json
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class JsonNumber < JsonValue
|
|
83
|
+
attr_accessor :value
|
|
84
|
+
def initialize(value)
|
|
85
|
+
@value = value
|
|
86
|
+
end
|
|
87
|
+
def evaluate
|
|
88
|
+
value
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class JsonTrue < JsonValue
|
|
93
|
+
def evaluate
|
|
94
|
+
'true'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class JsonFalse < JsonValue
|
|
99
|
+
def evaluate
|
|
100
|
+
'false'
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class JsonNull < JsonValue
|
|
105
|
+
def evaluate
|
|
106
|
+
'null'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'action_view'
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Template::Handlers
|
|
5
|
+
class JsonifyBuilder < Template::Handler
|
|
6
|
+
include Compilable
|
|
7
|
+
|
|
8
|
+
self.default_format = Mime::JSON
|
|
9
|
+
|
|
10
|
+
def compile(template)
|
|
11
|
+
"json = ::Jsonify::Builder.new();" +
|
|
12
|
+
template.source +
|
|
13
|
+
";json.compile!;"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
ActionView::Template.register_template_handler :jsonify, ActionView::Template::Handlers::JsonifyBuilder
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Jsonify::Builder do
|
|
4
|
+
|
|
5
|
+
let(:json) { Jsonify::Builder.new }
|
|
6
|
+
|
|
7
|
+
describe 'base behavior' do
|
|
8
|
+
describe 'should render empty object on literal' do
|
|
9
|
+
it 'after initialization' do
|
|
10
|
+
json.compile!.should == "{}"
|
|
11
|
+
end
|
|
12
|
+
it 'after reset' do
|
|
13
|
+
json.foo 'bar'
|
|
14
|
+
json.reset!
|
|
15
|
+
json.compile!.should == "{}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
describe 'with verify set' do
|
|
19
|
+
it 'should report a parse error if the result is not parseable' do
|
|
20
|
+
json = Jsonify::Builder.new(:verify => true)
|
|
21
|
+
# Hackery to come up with a failing case
|
|
22
|
+
class FooBar
|
|
23
|
+
def evaluate
|
|
24
|
+
"foobar"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
json.instance_variable_set(:@stack, [FooBar.new])
|
|
28
|
+
lambda{ json.compile! }.should raise_error(JSON::ParserError)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
describe 'unicode characters' do
|
|
32
|
+
it 'should properly encode' do
|
|
33
|
+
json = Jsonify::Builder.new(:verify => true)
|
|
34
|
+
json.foo 'bar'.concat(16)
|
|
35
|
+
lambda { json.compile! }.should_not raise_error
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
describe "pretty printing" do
|
|
39
|
+
it "should not be pretty by default" do
|
|
40
|
+
json.foo do
|
|
41
|
+
json.bar 'baz'
|
|
42
|
+
end
|
|
43
|
+
non_pretty_results = '{"foo":{"bar":"baz"}}'
|
|
44
|
+
json.compile!.should == non_pretty_results
|
|
45
|
+
end
|
|
46
|
+
it "should be pretty when asked for" do
|
|
47
|
+
json = Jsonify::Builder.new(:pretty => true)
|
|
48
|
+
json.foo do
|
|
49
|
+
json.bar 'baz'
|
|
50
|
+
end
|
|
51
|
+
pretty_results = <<PRETTY_JSON
|
|
52
|
+
{
|
|
53
|
+
"foo": {
|
|
54
|
+
"bar": "baz"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
PRETTY_JSON
|
|
58
|
+
json.compile!.should == pretty_results.chomp
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe 'arrays' do
|
|
64
|
+
it 'simple array should work' do
|
|
65
|
+
json.array! do |ary|
|
|
66
|
+
ary << 1
|
|
67
|
+
ary << 2
|
|
68
|
+
end
|
|
69
|
+
json.compile!.should == "[1,2]"
|
|
70
|
+
end
|
|
71
|
+
it 'array of arrays should work' do
|
|
72
|
+
json.array! do |ary|
|
|
73
|
+
ary << json.array! {|a| a << 1}
|
|
74
|
+
ary << json.array! {|b| b << 2}
|
|
75
|
+
ary << 3
|
|
76
|
+
end
|
|
77
|
+
json.compile!.should == "[[1],[2],3]"
|
|
78
|
+
end
|
|
79
|
+
it 'array of hashes should work' do
|
|
80
|
+
json.array! do |ary|
|
|
81
|
+
ary << {foo: :bar}
|
|
82
|
+
ary << {go: :far}
|
|
83
|
+
end
|
|
84
|
+
json.compile!.should == "[{\"foo\":\"bar\"},{\"go\":\"far\"}]"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe 'objects' do
|
|
89
|
+
it 'simple object should work' do
|
|
90
|
+
json.object! do |obj|
|
|
91
|
+
obj << [:foo,:bar]
|
|
92
|
+
obj << [:go, :far]
|
|
93
|
+
end
|
|
94
|
+
json.compile!.should == "{\"foo\":\"bar\",\"go\":\"far\"}"
|
|
95
|
+
end
|
|
96
|
+
it 'should treat the first element of an array as a key' do
|
|
97
|
+
json.object! do |obj|
|
|
98
|
+
obj << [1, 2, 3]
|
|
99
|
+
obj << [4, 5]
|
|
100
|
+
end
|
|
101
|
+
json.compile!.should == '{"1":[2,3],"4":5}'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe 'using blocks' do
|
|
106
|
+
|
|
107
|
+
it 'complex hash' do
|
|
108
|
+
json.foo do
|
|
109
|
+
json.bar do
|
|
110
|
+
json.baz 'goo'
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
json.compile!.should == "{\"foo\":{\"bar\":{\"baz\":\"goo\"}}}"
|
|
114
|
+
end
|
|
115
|
+
it 'simple hash' do
|
|
116
|
+
json.foo do
|
|
117
|
+
json.baz :goo
|
|
118
|
+
end
|
|
119
|
+
json.compile!.should == "{\"foo\":{\"baz\":\"goo\"}}"
|
|
120
|
+
end
|
|
121
|
+
it 'hash with array' do
|
|
122
|
+
json.foo do
|
|
123
|
+
json.array! do |ary|
|
|
124
|
+
ary << 1
|
|
125
|
+
ary << 2
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
json.compile!.should == "{\"foo\":[1,2]}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'simple array with object' do
|
|
132
|
+
json.array! do |ary|
|
|
133
|
+
ary << 1
|
|
134
|
+
ary << (json.foo 'bar')
|
|
135
|
+
end
|
|
136
|
+
json.compile!.should == "[1,{\"foo\":\"bar\"}]"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'complex hash with array' do
|
|
140
|
+
json.foo do
|
|
141
|
+
json.bar do
|
|
142
|
+
json.baz 'goo'
|
|
143
|
+
json.years do
|
|
144
|
+
json.array! do |ary|
|
|
145
|
+
ary << 2011
|
|
146
|
+
ary << 2012
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
json.compile!.should == "{\"foo\":{\"bar\":{\"baz\":\"goo\",\"years\":[2011,2012]}}}"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
describe 'without blocks' do
|
|
156
|
+
describe 'complex array' do
|
|
157
|
+
it 'should work' do
|
|
158
|
+
json.bar [1,2,{:foo => 'goo'}]
|
|
159
|
+
json.compile!.should == "{\"bar\":[1,2,{\"foo\":\"goo\"}]}"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe 'super complex example' do
|
|
165
|
+
let(:links) {
|
|
166
|
+
link_class = Struct.new(:url,:type)
|
|
167
|
+
[
|
|
168
|
+
link_class.new('example.com', 'self'),
|
|
169
|
+
link_class.new('foo.com', 'parent')
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
it 'should work using array!' do
|
|
173
|
+
json.result do
|
|
174
|
+
json.person do
|
|
175
|
+
json.fname 'George'
|
|
176
|
+
json.lname 'Burdell'
|
|
177
|
+
end
|
|
178
|
+
json.links do
|
|
179
|
+
json.array! do |ary|
|
|
180
|
+
links.each do |link|
|
|
181
|
+
ary << { href: link.url, rel: link.type}
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
expected = "{\"result\":{\"person\":{\"fname\":\"George\",\"lname\":\"Burdell\"},\"links\":[{\"href\":\"example.com\",\"rel\":\"self\"},{\"href\":\"foo.com\",\"rel\":\"parent\"}]}}"
|
|
187
|
+
json.compile!.should == expected
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
[:map!, :collect!].each do |method|
|
|
191
|
+
it "should work using #{method} with argument" do
|
|
192
|
+
json.result do
|
|
193
|
+
json.person do
|
|
194
|
+
json.fname 'George'
|
|
195
|
+
json.lname 'Burdell'
|
|
196
|
+
end
|
|
197
|
+
json.links do
|
|
198
|
+
json.send(method,links) do |link|
|
|
199
|
+
{ href: link.url, rel: link.type}
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
expected = "{\"result\":{\"person\":{\"fname\":\"George\",\"lname\":\"Burdell\"},\"links\":[{\"href\":\"example.com\",\"rel\":\"self\"},{\"href\":\"foo.com\",\"rel\":\"parent\"}]}}"
|
|
204
|
+
json.compile!.should == expected
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Jsonify::Generate do
|
|
4
|
+
let(:links) do
|
|
5
|
+
{ :links =>
|
|
6
|
+
[
|
|
7
|
+
{:rel => 'foo', :href => 'goo'},
|
|
8
|
+
{:rel => 'bar', :href => 'baz'}
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
it 'should build json' do
|
|
13
|
+
json = Jsonify::Generate
|
|
14
|
+
result = json.value links
|
|
15
|
+
result.evaluate.should == '{"links":[{"rel":"foo","href":"goo"},{"rel":"bar","href":"baz"}]}'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe 'complex example' do
|
|
19
|
+
let(:jsonifier) { Jsonify::Generate }
|
|
20
|
+
|
|
21
|
+
it 'should work' do
|
|
22
|
+
json = jsonifier.object_value(
|
|
23
|
+
{"links" =>
|
|
24
|
+
jsonifier.array_value([
|
|
25
|
+
jsonifier.object_value( {"rel" => "foo", "href" => "goo"} ),
|
|
26
|
+
jsonifier.object_value( {"rel" => "bar", "href" => "baz"} )
|
|
27
|
+
])
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
json.evaluate.should == "{\"links\":[{\"rel\":\"foo\",\"href\":\"goo\"},{\"rel\":\"bar\",\"href\":\"baz\"}]}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Jsonify::JsonValue do
|
|
4
|
+
describe Jsonify::JsonPair do
|
|
5
|
+
let(:pair) { Jsonify::JsonPair.new('key',Jsonify::JsonString.new('value')) }
|
|
6
|
+
it 'should be constructed of a key and value' do
|
|
7
|
+
pair.key.should == 'key'
|
|
8
|
+
# pair.value.should ==
|
|
9
|
+
end
|
|
10
|
+
it 'should evaluate to key:value' do
|
|
11
|
+
pair.evaluate.should == "\"key\":\"value\""
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jsonify
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 0.0.1
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Bill Siggelkow
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-07-23 00:00:00 -04:00
|
|
14
|
+
default_executable:
|
|
15
|
+
dependencies:
|
|
16
|
+
- !ruby/object:Gem::Dependency
|
|
17
|
+
name: json
|
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
19
|
+
none: false
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: "0"
|
|
24
|
+
type: :runtime
|
|
25
|
+
prerelease: false
|
|
26
|
+
version_requirements: *id001
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: actionpack
|
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
30
|
+
none: false
|
|
31
|
+
requirements:
|
|
32
|
+
- - ~>
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: 3.0.0
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: *id002
|
|
38
|
+
- !ruby/object:Gem::Dependency
|
|
39
|
+
name: bundler
|
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: "0"
|
|
46
|
+
type: :development
|
|
47
|
+
prerelease: false
|
|
48
|
+
version_requirements: *id003
|
|
49
|
+
- !ruby/object:Gem::Dependency
|
|
50
|
+
name: rake
|
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
52
|
+
none: false
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: "0"
|
|
57
|
+
type: :development
|
|
58
|
+
prerelease: false
|
|
59
|
+
version_requirements: *id004
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: rspec
|
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
63
|
+
none: false
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: "0"
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: *id005
|
|
71
|
+
- !ruby/object:Gem::Dependency
|
|
72
|
+
name: autotest
|
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
74
|
+
none: false
|
|
75
|
+
requirements:
|
|
76
|
+
- - ">="
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: "0"
|
|
79
|
+
type: :development
|
|
80
|
+
prerelease: false
|
|
81
|
+
version_requirements: *id006
|
|
82
|
+
description: Turn Ruby objects into JSON -- correctly!
|
|
83
|
+
email:
|
|
84
|
+
- bsiggelkow@me.com
|
|
85
|
+
executables: []
|
|
86
|
+
|
|
87
|
+
extensions: []
|
|
88
|
+
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
|
|
91
|
+
files:
|
|
92
|
+
- .gitignore
|
|
93
|
+
- Gemfile
|
|
94
|
+
- LICENSE
|
|
95
|
+
- README.md
|
|
96
|
+
- Rakefile
|
|
97
|
+
- jsonify.gemspec
|
|
98
|
+
- lib/blank_slate.rb
|
|
99
|
+
- lib/jsonify.rb
|
|
100
|
+
- lib/jsonify/builder.rb
|
|
101
|
+
- lib/jsonify/generate.rb
|
|
102
|
+
- lib/jsonify/json_value.rb
|
|
103
|
+
- lib/jsonify/rails.rb
|
|
104
|
+
- lib/jsonify/version.rb
|
|
105
|
+
- spec/builder_spec.rb
|
|
106
|
+
- spec/generate_spec.rb
|
|
107
|
+
- spec/json_value_spec.rb
|
|
108
|
+
- spec/jsonify_spec.rb
|
|
109
|
+
- spec/spec_helper.rb
|
|
110
|
+
has_rdoc: true
|
|
111
|
+
homepage: http://github.com/bsiggelkow/jsonify
|
|
112
|
+
licenses: []
|
|
113
|
+
|
|
114
|
+
post_install_message:
|
|
115
|
+
rdoc_options: []
|
|
116
|
+
|
|
117
|
+
require_paths:
|
|
118
|
+
- lib
|
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
|
+
none: false
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
hash: 3514387947205950198
|
|
125
|
+
segments:
|
|
126
|
+
- 0
|
|
127
|
+
version: "0"
|
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
|
+
none: false
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
hash: 3514387947205950198
|
|
134
|
+
segments:
|
|
135
|
+
- 0
|
|
136
|
+
version: "0"
|
|
137
|
+
requirements: []
|
|
138
|
+
|
|
139
|
+
rubyforge_project: jsonify
|
|
140
|
+
rubygems_version: 1.5.2
|
|
141
|
+
signing_key:
|
|
142
|
+
specification_version: 3
|
|
143
|
+
summary: Turn Ruby objects into JSON
|
|
144
|
+
test_files:
|
|
145
|
+
- spec/builder_spec.rb
|
|
146
|
+
- spec/generate_spec.rb
|
|
147
|
+
- spec/json_value_spec.rb
|
|
148
|
+
- spec/jsonify_spec.rb
|
|
149
|
+
- spec/spec_helper.rb
|