jbuilder 0.3
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/MIT-LICENSE +20 -0
- data/README.md +73 -0
- data/jbuilder.gemspec +12 -0
- data/lib/jbuilder.rb +165 -0
- data/lib/jbuilder_template.rb +23 -0
- data/test/jbuilder_test.rb +195 -0
- metadata +72 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 David Heinemeier Hansson, 37signals
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Jbuilder
|
2
|
+
========
|
3
|
+
|
4
|
+
Jbuilder gives you a simple DSL for declaring JSON structures that beats massaging giant hash structures. This is particularly helpful when the generation process is fraught with conditionals and loops. Here's a simple example:
|
5
|
+
|
6
|
+
Jbuilder.encode do |json|
|
7
|
+
json.content format_content(@message.content)
|
8
|
+
json.(@message, :created_at, :updated_at)
|
9
|
+
|
10
|
+
json.author do |json|
|
11
|
+
json.name @message.creator.name.familiar
|
12
|
+
json.email_address @message.creator.email_address_with_name
|
13
|
+
json.url url_for(@message.creator, format: :json)
|
14
|
+
end
|
15
|
+
|
16
|
+
if current_user.admin?
|
17
|
+
json.visitors calculate_visitors(@message)
|
18
|
+
end
|
19
|
+
|
20
|
+
json.comments @message.comments, :content, :created_at
|
21
|
+
|
22
|
+
json.attachments @message.attachments do |json, attachment|
|
23
|
+
json.filename attachment.filename
|
24
|
+
json.url url_for(attachment)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
This will build the following structure:
|
29
|
+
|
30
|
+
{
|
31
|
+
"content": "<p>This is <i>serious</i> monkey business",
|
32
|
+
"created_at": "2011-10-29T20:45:28-05:00",
|
33
|
+
"updated_at": "2011-10-29T20:45:28-05:00",
|
34
|
+
|
35
|
+
"author": {
|
36
|
+
"name": "David H.",
|
37
|
+
"email_address": "'David Heinemeier Hansson' <david@heinemeierhansson.com>",
|
38
|
+
"url": "http://example.com/users/1-david.json"
|
39
|
+
},
|
40
|
+
|
41
|
+
"visitors": 15,
|
42
|
+
|
43
|
+
"comments": [
|
44
|
+
{ "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
|
45
|
+
{ "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
|
46
|
+
],
|
47
|
+
|
48
|
+
"attachment": [
|
49
|
+
{ "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
|
50
|
+
{ "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
|
51
|
+
]
|
52
|
+
}
|
53
|
+
|
54
|
+
You can either use Jbuilder stand-alone or directly as an ActionView template language. When required in Rails, you can create views ala show.json.jbuilder (the json is already yielded):
|
55
|
+
|
56
|
+
# Any helpers available to views are available to the builder
|
57
|
+
json.content format_content(@message.content)
|
58
|
+
json.(@message, :created_at, :updated_at)
|
59
|
+
|
60
|
+
json.author do |json|
|
61
|
+
json.name @message.creator.name.familiar
|
62
|
+
json.email_address @message.creator.email_address_with_name
|
63
|
+
json.url url_for(@message.creator, format: :json)
|
64
|
+
end
|
65
|
+
|
66
|
+
if current_user.admin?
|
67
|
+
json.visitors calculate_visitors(@message)
|
68
|
+
end
|
69
|
+
|
70
|
+
# You can use partials as well, just remember to pass in the json instance
|
71
|
+
json.partial! "api/comments/comments" @message.comments
|
72
|
+
|
73
|
+
Note: Jbuilder is similar to Garrett Bjerkhoel's json_builder, which I discovered after making this, but the DSL has taken a different turn and will retain the explicit yield style (vs json_builder's 3.0's move to instance_eval).
|
data/jbuilder.gemspec
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'jbuilder'
|
3
|
+
s.version = '0.3'
|
4
|
+
s.author = 'David Heinemeier Hansson'
|
5
|
+
s.email = 'david@37signals.com'
|
6
|
+
s.summary = 'Create JSON structures via a Builder-style DSL'
|
7
|
+
|
8
|
+
s.add_dependency 'activesupport', '>= 3.0.0'
|
9
|
+
s.add_dependency 'blankslate', '>= 2.1.2.4'
|
10
|
+
|
11
|
+
s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
|
12
|
+
end
|
data/lib/jbuilder.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'blankslate'
|
2
|
+
require 'active_support/ordered_hash'
|
3
|
+
require 'active_support/core_ext/array/access'
|
4
|
+
require 'active_support/core_ext/enumerable'
|
5
|
+
require 'active_support/json'
|
6
|
+
|
7
|
+
class Jbuilder < BlankSlate
|
8
|
+
# Yields a builder and automatically turns the result into a JSON string
|
9
|
+
def self.encode
|
10
|
+
new._tap { |jbuilder| yield jbuilder }.target!
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method(:__class__, find_hidden_method(:class))
|
14
|
+
define_method(:_tap, find_hidden_method(:tap))
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@attributes = ActiveSupport::OrderedHash.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# Turns the current element into an array and yields a builder to add a hash.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# json.comments do |json|
|
25
|
+
# json.child! { |json| json.content "hello" }
|
26
|
+
# json.child! { |json| json.content "world" }
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# { "comments": [ { "content": "hello" }, { "content": "world" } ]}
|
30
|
+
#
|
31
|
+
# More commonly, you'd use the combined iterator, though:
|
32
|
+
#
|
33
|
+
# json.comments(@post.comments) do |json, comment|
|
34
|
+
# json.content comment.formatted_content
|
35
|
+
# end
|
36
|
+
def child!
|
37
|
+
@attributes = [] unless @attributes.is_a? Array
|
38
|
+
@attributes << _new_instance._tap { |jbuilder| yield jbuilder }.attributes!
|
39
|
+
end
|
40
|
+
|
41
|
+
# Iterates over the passed collection and adds each iteration as an element of the resulting array.
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
#
|
45
|
+
# json.array!(@people) do |json, person|
|
46
|
+
# json.name person.name
|
47
|
+
# json.age calculate_age(person.birthday)
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# [ { "David", 32 }, { "Jamie", 31 } ]
|
51
|
+
#
|
52
|
+
# If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:
|
53
|
+
#
|
54
|
+
# json.(@people) { |json, person| ... }
|
55
|
+
#
|
56
|
+
# It's generally only needed to use this method for top-level arrays. If you have named arrays, you can do:
|
57
|
+
#
|
58
|
+
# json.people(@people) do |json, person|
|
59
|
+
# json.name person.name
|
60
|
+
# json.age calculate_age(person.birthday)
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# { "people": [ { "David", 32 }, { "Jamie", 31 } ] }
|
64
|
+
def array!(collection)
|
65
|
+
collection.each do |element|
|
66
|
+
child! do |child|
|
67
|
+
yield child, element
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Extracts the mentioned attributes from the passed object and turns them into attributes of the JSON.
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
#
|
76
|
+
# json.extract! @person, :name, :age
|
77
|
+
#
|
78
|
+
# { "David", 32 }, { "Jamie", 31 }
|
79
|
+
#
|
80
|
+
# If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:
|
81
|
+
#
|
82
|
+
# json.(@person, :name, :age)
|
83
|
+
def extract!(object, *attributes)
|
84
|
+
attributes.each do |attribute|
|
85
|
+
__send__ attribute, object.send(attribute)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if RUBY_VERSION > '1.9'
|
90
|
+
def call(*args)
|
91
|
+
case
|
92
|
+
when args.one?
|
93
|
+
array!(args.first) { |json, element| yield json, element }
|
94
|
+
when args.many?
|
95
|
+
extract!(*args)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the attributes of the current builder.
|
101
|
+
def attributes!
|
102
|
+
@attributes
|
103
|
+
end
|
104
|
+
|
105
|
+
# Encodes the current builder as JSON.
|
106
|
+
def target!
|
107
|
+
ActiveSupport::JSON.encode @attributes
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
private
|
112
|
+
def method_missing(method, *args)
|
113
|
+
case
|
114
|
+
when args.one? && block_given?
|
115
|
+
_yield_iteration(method, args.first) { |child, element| yield child, element }
|
116
|
+
when args.one?
|
117
|
+
_assign method, args.first
|
118
|
+
when args.empty? && block_given?
|
119
|
+
_yield_nesting(method) { |jbuilder| yield jbuilder }
|
120
|
+
when args.many? && args.first.is_a?(Enumerable)
|
121
|
+
_inline_nesting method, args.first, args.from(1)
|
122
|
+
when args.many?
|
123
|
+
_inline_extract method, args.first, args.from(1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def _assign(key, value)
|
128
|
+
@attributes[key] = value
|
129
|
+
end
|
130
|
+
|
131
|
+
# Overwrite in subclasses if you need to add initialization values
|
132
|
+
def _new_instance
|
133
|
+
__class__.new
|
134
|
+
end
|
135
|
+
|
136
|
+
def _yield_nesting(container)
|
137
|
+
@attributes[container] = _new_instance._tap { |jbuilder| yield jbuilder }.attributes!
|
138
|
+
end
|
139
|
+
|
140
|
+
def _inline_nesting(container, collection, attributes)
|
141
|
+
__send__(container) do |parent|
|
142
|
+
collection.each do |element|
|
143
|
+
parent.child! do |child|
|
144
|
+
attributes.each do |attribute|
|
145
|
+
child.__send__ attribute, element.send(attribute)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def _yield_iteration(container, collection)
|
153
|
+
__send__(container) do |parent|
|
154
|
+
parent.array!(collection) do |child, element|
|
155
|
+
yield child, element
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def _inline_extract(container, record, attributes)
|
161
|
+
__send__(container) { |parent| parent.extract! record, *attributes }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
require "jbuilder_template" if defined?(ActionView::Template)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class JbuilderTemplate < Jbuilder
|
2
|
+
def self.encode(context)
|
3
|
+
new(context)._tap { |jbuilder| yield jbuilder }.target!
|
4
|
+
end
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
super()
|
9
|
+
end
|
10
|
+
|
11
|
+
def partial!(partial_name, options = {})
|
12
|
+
@context.render(partial_name, options.merge(json: self))
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def _new_instance
|
17
|
+
__class__.new(@context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ActionView::Template.register_template_handler :jbuilder, Proc.new { |template|
|
22
|
+
"if defined?(json); #{template.source}; else; JbuilderTemplate.encode(self) do |json|;#{template.source};end; end;"
|
23
|
+
}
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'active_support/test_case'
|
3
|
+
|
4
|
+
require 'jbuilder'
|
5
|
+
|
6
|
+
class JbuilderTest < ActiveSupport::TestCase
|
7
|
+
test "single key" do
|
8
|
+
json = Jbuilder.encode do |json|
|
9
|
+
json.content "hello"
|
10
|
+
end
|
11
|
+
|
12
|
+
assert_equal "hello", JSON.parse(json)["content"]
|
13
|
+
end
|
14
|
+
|
15
|
+
test "multiple keys" do
|
16
|
+
json = Jbuilder.encode do |json|
|
17
|
+
json.title "hello"
|
18
|
+
json.content "world"
|
19
|
+
end
|
20
|
+
|
21
|
+
JSON.parse(json).tap do |parsed|
|
22
|
+
assert_equal "hello", parsed["title"]
|
23
|
+
assert_equal "world", parsed["content"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
test "extracting from object" do
|
28
|
+
person = Struct.new(:name, :age).new("David", 32)
|
29
|
+
|
30
|
+
json = Jbuilder.encode do |json|
|
31
|
+
json.extract! person, :name, :age
|
32
|
+
end
|
33
|
+
|
34
|
+
JSON.parse(json).tap do |parsed|
|
35
|
+
assert_equal "David", parsed["name"]
|
36
|
+
assert_equal 32, parsed["age"]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
test "extracting from object using call style for 1.9" do
|
41
|
+
person = Struct.new(:name, :age).new("David", 32)
|
42
|
+
|
43
|
+
json = Jbuilder.encode do |json|
|
44
|
+
json.(person, :name, :age)
|
45
|
+
end
|
46
|
+
|
47
|
+
JSON.parse(json).tap do |parsed|
|
48
|
+
assert_equal "David", parsed["name"]
|
49
|
+
assert_equal 32, parsed["age"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
test "nesting single child with block" do
|
54
|
+
json = Jbuilder.encode do |json|
|
55
|
+
json.author do |json|
|
56
|
+
json.name "David"
|
57
|
+
json.age 32
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
JSON.parse(json).tap do |parsed|
|
62
|
+
assert_equal "David", parsed["author"]["name"]
|
63
|
+
assert_equal 32, parsed["author"]["age"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
test "nesting multiple children with block" do
|
68
|
+
json = Jbuilder.encode do |json|
|
69
|
+
json.comments do |json|
|
70
|
+
json.child! { |json| json.content "hello" }
|
71
|
+
json.child! { |json| json.content "world" }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
JSON.parse(json).tap do |parsed|
|
76
|
+
assert_equal "hello", parsed["comments"].first["content"]
|
77
|
+
assert_equal "world", parsed["comments"].second["content"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
test "nesting single child with inline extract" do
|
82
|
+
person = Class.new do
|
83
|
+
attr_reader :name, :age
|
84
|
+
|
85
|
+
def initialize(name, age)
|
86
|
+
@name, @age = name, age
|
87
|
+
end
|
88
|
+
end.new("David", 32)
|
89
|
+
|
90
|
+
json = Jbuilder.encode do |json|
|
91
|
+
json.author person, :name, :age
|
92
|
+
end
|
93
|
+
|
94
|
+
JSON.parse(json).tap do |parsed|
|
95
|
+
assert_equal "David", parsed["author"]["name"]
|
96
|
+
assert_equal 32, parsed["author"]["age"]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
test "nesting multiple children from array" do
|
101
|
+
comments = [ Struct.new(:content, :id).new("hello", 1), Struct.new(:content, :id).new("world", 2) ]
|
102
|
+
|
103
|
+
json = Jbuilder.encode do |json|
|
104
|
+
json.comments comments, :content
|
105
|
+
end
|
106
|
+
|
107
|
+
JSON.parse(json).tap do |parsed|
|
108
|
+
assert_equal ["content"], parsed["comments"].first.keys
|
109
|
+
assert_equal "hello", parsed["comments"].first["content"]
|
110
|
+
assert_equal "world", parsed["comments"].second["content"]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
test "nesting multiple children from array with inline loop" do
|
116
|
+
comments = [ Struct.new(:content, :id).new("hello", 1), Struct.new(:content, :id).new("world", 2) ]
|
117
|
+
|
118
|
+
json = Jbuilder.encode do |json|
|
119
|
+
json.comments comments do |json, comment|
|
120
|
+
json.content comment.content
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
JSON.parse(json).tap do |parsed|
|
125
|
+
assert_equal ["content"], parsed["comments"].first.keys
|
126
|
+
assert_equal "hello", parsed["comments"].first["content"]
|
127
|
+
assert_equal "world", parsed["comments"].second["content"]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
test "nesting multiple children from array with inline loop on root" do
|
132
|
+
comments = [ Struct.new(:content, :id).new("hello", 1), Struct.new(:content, :id).new("world", 2) ]
|
133
|
+
|
134
|
+
json = Jbuilder.encode do |json|
|
135
|
+
json.(comments) do |json, comment|
|
136
|
+
json.content comment.content
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
JSON.parse(json).tap do |parsed|
|
141
|
+
assert_equal "hello", parsed.first["content"]
|
142
|
+
assert_equal "world", parsed.second["content"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
test "array nested inside nested hash" do
|
147
|
+
json = Jbuilder.encode do |json|
|
148
|
+
json.author do |json|
|
149
|
+
json.name "David"
|
150
|
+
json.age 32
|
151
|
+
|
152
|
+
json.comments do |json|
|
153
|
+
json.child! { |json| json.content "hello" }
|
154
|
+
json.child! { |json| json.content "world" }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
JSON.parse(json).tap do |parsed|
|
160
|
+
assert_equal "hello", parsed["author"]["comments"].first["content"]
|
161
|
+
assert_equal "world", parsed["author"]["comments"].second["content"]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
test "array nested inside array" do
|
166
|
+
json = Jbuilder.encode do |json|
|
167
|
+
json.comments do |json|
|
168
|
+
json.child! do |json|
|
169
|
+
json.authors do |json|
|
170
|
+
json.child! do |json|
|
171
|
+
json.name "david"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
assert_equal "david", JSON.parse(json)["comments"].first["authors"].first["name"]
|
179
|
+
end
|
180
|
+
|
181
|
+
test "top-level array" do
|
182
|
+
comments = [ Struct.new(:content, :id).new("hello", 1), Struct.new(:content, :id).new("world", 2) ]
|
183
|
+
|
184
|
+
json = Jbuilder.encode do |json|
|
185
|
+
json.array!(comments) do |json, comment|
|
186
|
+
json.content comment.content
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
JSON.parse(json).tap do |parsed|
|
191
|
+
assert_equal "hello", parsed.first["content"]
|
192
|
+
assert_equal "world", parsed.second["content"]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jbuilder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.3'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Heinemeier Hansson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70104549490040 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70104549490040
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: blankslate
|
27
|
+
requirement: &70104549489160 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.1.2.4
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70104549489160
|
36
|
+
description:
|
37
|
+
email: david@37signals.com
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- ./jbuilder.gemspec
|
43
|
+
- ./lib/jbuilder.rb
|
44
|
+
- ./lib/jbuilder_template.rb
|
45
|
+
- ./MIT-LICENSE
|
46
|
+
- ./README.md
|
47
|
+
- ./test/jbuilder_test.rb
|
48
|
+
homepage:
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.11
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Create JSON structures via a Builder-style DSL
|
72
|
+
test_files: []
|