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