jsonify 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in jsonify.gemspec
3
+ # Gem dependencies are in jsonify.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Jsonify -- a builder for JSON <a href="http://travis-ci.org/bsiggelkow/jsonify"><img src="https://secure.travis-ci.org/bsiggelkow/jsonify.png" alt=""></a>
1
+ # Jsonify &mdash; a builder for JSON [![Build Status](http://travis-ci.org/bsiggelkow/jsonify.png)](http://travis-ci.org/bsiggelkow/jsonify)
2
2
 
3
3
  [Jsonify](https://github.com/bsiggelkow/jsonify) is to JSON as [Builder](https://github.com/jimweirich/builder) is to XML.
4
4
 
@@ -31,6 +31,12 @@ But an even greater motivation for me was emulating the simplicity of [Builder](
31
31
 
32
32
  ## Usage
33
33
 
34
+ In the examples that follow, the JSON output is usually shown "prettified". Is this only
35
+ for illustration purposes, as the default behavior for Jsonify is not to prettify the output.
36
+ You can enable prettification by passing `:pretty => true` to the Jsonify::Builder constructor; however,
37
+ pretty printing is a relatively costly operation and should not be used in production (unless, of course, you explicitly
38
+ want to show this format).
39
+
34
40
  ### Standalone
35
41
  # Create some objects that represent a person and associated hyperlinks
36
42
  @person = Struct.new(:first_name,:last_name).new('George','Burdell')
@@ -86,6 +92,242 @@ The Jsonify template handler exposes the `Jsonify::Builder` instance to your tem
86
92
  json.world "Jsonify is Working!"
87
93
  end
88
94
 
95
+ #### Partials
96
+
97
+ You can use partials with Jsonify view templates, but how you use them will
98
+ depend on how the information they return is structured. An important to keep in
99
+ mind is that a partial, no matter what kind it is, always a returns a string.
100
+
101
+ ##### Jsonify partials
102
+
103
+ Any Jsonify partial &mdash; that is, the file has a `.jsonify` extension &mdash;
104
+ will return, by design, a string that is valid JSON. It will represent either a JSON object,
105
+ and be wrapped in curly braces ({}), or a JSON array, and be wrapped in square brackets ([]).
106
+
107
+ To incorporate such a value into a Jsonify template, Jsonify provides the `ingest!` method.
108
+
109
+ You can use this method to add JSON, rendered by a partial, to the builder.
110
+ Let's assume this our main template, `index.jsonify`:
111
+
112
+ json << 1
113
+ json.ingest! (render :partial=>'my_partial')
114
+
115
+ From the first line, you can tell that an array will be created so this line uses the append operator.
116
+ On the second line, a partial is being added to the builder. Note that you cannot simply place `render :parial ...`
117
+ on a line by itself as you can do with other templates like `erb` and `haml`; you have to explicitly tell Jsonify
118
+ to add it.
119
+
120
+ Let's say that the partial file, `_my_partial.jsonify`, is as follows:
121
+
122
+ json << 3
123
+ json << 4
124
+
125
+ This `json` variable is a separate instance of the Jsonify::Builder, distinct from the builder instance,
126
+ in the main template. This partial will end up generating the following string:
127
+
128
+ "[3,4]"
129
+
130
+ The `ingest!` method will actually parse this string back into a Jsonify-based object, and add it
131
+ to the builder's current state. The resulting output will be:
132
+
133
+ "[1,[3,4]]"
134
+
135
+ ##### Other partials
136
+
137
+ You can also use output from non-Jsonify templates (e.g. erb); just remember that the output from a template
138
+ is always a string and that you have to tell the builder how to include the result of the partial.
139
+ For example, suppose I have the partial `_today.erb` with the following content:
140
+
141
+ <%= Date.today %>
142
+
143
+ You can then incorporate this partial into your Jsonify template just as you would any other string value:
144
+
145
+ json << 1
146
+ json.ingest! (render :partial=>'my_partial')
147
+ json << {:date => (render :partial => 'today')}
148
+
149
+ renders ...
150
+
151
+ [1,[3,4],{"date":"2011-07-30"}]
152
+
153
+ ### Usage Patterns
154
+
155
+ Jsonify is designed to support construction of an valid JSON representation and
156
+ is entirely based on the [JSON specification](http://json.org).
157
+
158
+ JSON is built on two fundamental structures:
159
+
160
+ * __object__: a collection of name-value pairs -- in Jsonify this is a `JsonObject`
161
+ * __array__: an ordered list of values -- in Jsonify this is a `JsonArray`
162
+
163
+ Jsonify adheres to the JSON specification and provides explicit support
164
+ for working with these primary structures. At the top most level, a JSON string
165
+ must be one of these structures and Jsonify ensures that this condition is met.
166
+
167
+ #### JSON Objects
168
+
169
+ A JSON object, sometimes
170
+ referred to as an ___object literal___, is a common structure familiar
171
+ to most developers. Its analogous to the nested element structured common
172
+ in XML. The [JSON RFC](http://www.ietf.org/rfc/rfc4627.txt) states that
173
+ "the names within an object SHOULD be unique". Jsonify elevates this recommendation
174
+ by backing the JsonObject with a `Hash`; an object must have unique keys and the last one in, wins.
175
+
176
+ json = Jsonify::Builder.new
177
+ json.person do # start a new JsonObject where the key is 'foo'
178
+ json.name 'George Burdell' # add a pair to this object
179
+ json.skills ['engineering','bombing'] # adds a pair with an array value
180
+ json.name 'George P. Burdell'
181
+ end
182
+
183
+ compiles to ...
184
+
185
+ {
186
+ "person": {
187
+ "name": "George P. Burdell",
188
+ "skills": [
189
+ "engineering",
190
+ "bombing"
191
+ ]
192
+ }
193
+ }
194
+
195
+ It's perfectly legitimate for a JSON representation to simply be a collection
196
+ of name-value pairs without a ___root___ element. Jsonify supports this by
197
+ simply allowing you to specify the pairs that make up the object.
198
+
199
+ json = Jsonify::Builder.new
200
+ json.location 'Library Coffeehouse'
201
+ json.neighborhood 'Brookhaven'
202
+
203
+ compiles to ...
204
+
205
+ {
206
+ "location": "Library Coffeehouse",
207
+ "neighborhood": "Brookhaven"
208
+ }
209
+
210
+ If the ___name___ you want contains whitespace or other characters not allowed in a Ruby method name, use `tag!`.
211
+
212
+ json.tag!("my location", 'Library Coffeehouse')
213
+ json.neighborhood 'Brookhaven'
214
+
215
+ {
216
+ "my location": "Library Coffeehouse",
217
+ "neighborhood": "Brookhaven"
218
+ }
219
+
220
+ Jsonify also supports a hash-style interface for creating JSON objects.
221
+
222
+ json = Jsonify::Builder.new
223
+
224
+ json[:foo] = :bar
225
+ json[:go] = :far
226
+
227
+ compiles to ...
228
+
229
+ {
230
+ "foo": "bar",
231
+ "go": "far"
232
+ }
233
+
234
+ You can these hash-style methods within a block as well ...
235
+
236
+ json.homer do
237
+ json[:beer] = "Duffs"
238
+ json[:spouse] = "Marge"
239
+ end
240
+
241
+ compiles to ...
242
+
243
+ {
244
+ "homer": {
245
+ "beer": "Duffs",
246
+ "spouse": "Marge"
247
+ }
248
+ }
249
+
250
+ If you prefer a more method-based approach, you can use the `store!` method passing it the key and value.
251
+
252
+ json.store!(:foo, :bar)
253
+ json.store!(:go, :far)
254
+
255
+ #### JSON Arrays
256
+
257
+ A JSON array is an ordered list of JSON values. A JSON value can be a simple value,
258
+ like a string or a number, or a supported JavaScript primitive like true, false, or null.
259
+ A JSON value can also be a JSON object or another JSON array. Jsonify strives to make
260
+ this kind of construction possible in a buider-style.
261
+
262
+ Jsonify supports JSON array construction through two approaches: `method_missing` and `append!`.
263
+
264
+ ##### method_missing
265
+
266
+ Pass an array and a block to `method_missing` (or `tag!`), and Jsonify will iterate
267
+ over that array, and create a JSON array where each array item is the result of the block.
268
+ If you pass an array that has a length of 5, you will end up with a JSON array that has 5 items.
269
+ That JSON array is then set as the value of the name-value pair, where the name comes from the method name (for `method_missing`)
270
+ or symbol (for `tag!`).
271
+
272
+ So this construct is really doing two things -- creating a JSON pair, and creating a JSON array as the value of the pair.
273
+
274
+ json = Jsonify::Builder.new(:pretty => true)
275
+ json.letters('a'..'c') do |letter|
276
+ letter.upcase
277
+ end
278
+
279
+ compiles to ...
280
+
281
+ {
282
+ "letters": [
283
+ "A",
284
+ "B",
285
+ "C"
286
+ ]
287
+ }
288
+
289
+ Another way to handle this particular example is to get rid of the block entirely.
290
+ Simply pass the array directly &mdash; the result will be the same.
291
+
292
+ json.letters ('a'..'c').map(&:upcase)
293
+
294
+ ##### append!
295
+
296
+ But what if we don't want to start with an object? How do we tell Jsonify to start with an array instead?
297
+
298
+ You can use `append!` (passing one or more values), or `<<` (which accepts only a single value) to
299
+ the builder and it will assume you are adding values to a JSON array.
300
+
301
+ json = Jsonify::Builder.new
302
+ json.append! 'a'.upcase, 'b'.upcase, 'c'.upcase
303
+
304
+ [
305
+ "A",
306
+ "B",
307
+ "C"
308
+ ]
309
+
310
+ or more idiomatically ...
311
+
312
+ json.append! *('a'..'c').map(&:upcase)
313
+
314
+ The append ___operator___, `<<`, can be used to push a single value into the array:
315
+
316
+ json << 'a'.upcase
317
+ json << 'b'.upcase
318
+ json << 'c'.upcase
319
+
320
+ Of course, standard iteration works here as well ...
321
+
322
+ json = Jsonify::Builder.new
323
+ ('a'..'c').each do |letter|
324
+ json << letter.upcase
325
+ end
326
+
327
+ #### Mixing JSON Arrays and Objects
328
+
329
+ ___coming soon___
330
+
89
331
  ## Documentation
90
332
 
91
333
  [Yard Docs](http://rubydoc.info/github/bsiggelkow/jsonify/master/frames)
@@ -95,16 +337,18 @@ The Jsonify template handler exposes the `Jsonify::Builder` instance to your tem
95
337
  - [Argonaut](https://github.com/jbr/argonaut)
96
338
  - [JSON Builder](https://github.com/dewski/json_builder)
97
339
  - [RABL](https://github.com/nesquena/rabl)
340
+ - [Representative](https://github.com/mdub/representative)
98
341
  - [Tokamak](https://github.com/abril/tokamak)
99
342
 
100
343
  ## TODOs
101
344
  1. Benchmark performance
102
345
  1. Document how partials can be used
346
+ 1. Clean up specs
103
347
 
104
348
  ## Roadmap
105
349
 
106
350
  1. Split Rails template handling into separate gem
107
- 1. Add support for Sinatra and Padrino (maybe separate gems)
351
+ 1. Add support for Sinatra and Padrino (Tilt integration?)
108
352
 
109
353
  ## License
110
354
 
@@ -134,12 +134,35 @@ module Jsonify
134
134
  @level -= 1
135
135
  end
136
136
 
137
+ # Ingest a full JSON representation (either an oject or array)
138
+ # into the builder. The value is parsed, objectified, and added to the
139
+ # current value at the top of the stack.
140
+ #
141
+ # @param [String] json_string a full JSON string (e.g. from a rendered partial)
142
+ def ingest!(json_string)
143
+ return if json_string.empty?
144
+ res = Jsonify::Generate.value(JSON.parse(json_string))
145
+ current = @stack[@level]
146
+ if current.nil?
147
+ @stack[@level] = res
148
+ elsif JsonObject === current
149
+ if JsonObject === res
150
+ @stack[@level].merge res
151
+ else
152
+ raise ArgumentError.new("Cannot add JSON array to JSON Object")
153
+ end
154
+ else # current is JsonArray
155
+ @stack[@level].add res
156
+ end
157
+ end
158
+
137
159
  private
138
160
 
139
161
  # BlankSlate requires the __<method> names
140
162
 
141
163
  def __store(key,value=nil)
142
- (@stack[@level] ||= JsonObject.new).add(key,value)
164
+ pair = (JsonPair === key ? key : JsonPair.new(key, value))
165
+ (@stack[@level] ||= JsonObject.new).add(pair)
143
166
  end
144
167
 
145
168
  def __append(value)
@@ -30,9 +30,15 @@ module Jsonify
30
30
  end
31
31
 
32
32
  def add(key, val=nil)
33
- pair = (JsonPair === key) ? key : JsonPair.new(key, val)
33
+ pair = ( JsonPair === key ? key : JsonPair.new(key, val) )
34
34
  @values.store(pair.key, pair)
35
35
  end
36
+
37
+ def merge(json_object)
38
+ json_object.values.each do |pair|
39
+ @values.store(pair.key, pair)
40
+ end
41
+ end
36
42
 
37
43
  alias_method :<<, :add
38
44
  alias_method :add!, :add # for consistency with the Builder api
@@ -1,3 +1,3 @@
1
1
  module Jsonify
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -70,8 +70,7 @@ PRETTY_JSON
70
70
  json.compile!.should == "[1,2]"
71
71
  end
72
72
  it 'with the append! method' do
73
- json.append!( 1,2 )
74
- .append! 3
73
+ json.append!( 1,2 ).append! 3
75
74
  json.compile!.should == "[1,2,3]"
76
75
  end
77
76
  end
@@ -81,9 +80,8 @@ PRETTY_JSON
81
80
  json.compile!.should == '{"foo":"bar"}'
82
81
  end
83
82
  it 'should support the store! message' do
84
- json.store!( "foo", "bar" )
85
- .store!( 'no', "whar" )
86
- json.compile!.should == '{"foo":"bar","no":"whar"}'
83
+ json.store!( "foo", "bar" ).store!( 'no', "whar" )
84
+ JSON.parse(json.compile!).should == JSON.parse('{"foo":"bar","no":"whar"}')
87
85
  end
88
86
  end
89
87
  end
@@ -103,7 +101,7 @@ PRETTY_JSON
103
101
  it 'array of hashes should work' do
104
102
  json << {:foo => :bar}
105
103
  json << {:go => :far}
106
- json.compile!.should == "[{\"foo\":\"bar\"},{\"go\":\"far\"}]"
104
+ json.compile!.should == '[{"foo":"bar"},{"go":"far"}]'
107
105
  end
108
106
  end
109
107
 
@@ -111,7 +109,8 @@ PRETTY_JSON
111
109
  it 'simple object should work' do
112
110
  json.foo :bar
113
111
  json.go :far
114
- json.compile!.should == "{\"foo\":\"bar\",\"go\":\"far\"}"
112
+ expected = '{"foo":"bar","go":"far"}'
113
+ JSON.parse(json.compile!).should == JSON.parse(expected)
115
114
  end
116
115
  it 'should handle arrays' do
117
116
  json[1] = [2, 3]
@@ -128,7 +127,8 @@ PRETTY_JSON
128
127
  json.tag!('buzz buzz','goo goo')
129
128
  end
130
129
  end
131
- json.compile!.should == "{\"foo foo\":{\"bar bar\":{\"buzz buzz\":\"goo goo\"}}}"
130
+ expected = '{"foo foo":{"bar bar":{"buzz buzz":"goo goo"}}}'
131
+ JSON.parse(json.compile!).should == JSON.parse(expected)
132
132
  end
133
133
 
134
134
  it 'complex hash' do
@@ -137,14 +137,14 @@ PRETTY_JSON
137
137
  json.baz 'goo'
138
138
  end
139
139
  end
140
- json.compile!.should == "{\"foo\":{\"bar\":{\"baz\":\"goo\"}}}"
140
+ json.compile!.should == '{"foo":{"bar":{"baz":"goo"}}}'
141
141
  end
142
142
 
143
143
  it 'simple hash' do
144
144
  json.foo do
145
145
  json.baz :goo
146
146
  end
147
- json.compile!.should == "{\"foo\":{\"baz\":\"goo\"}}"
147
+ json.compile!.should == '{"foo":{"baz":"goo"}}'
148
148
  end
149
149
 
150
150
  it 'hash with array' do
@@ -152,7 +152,7 @@ PRETTY_JSON
152
152
  json << 1
153
153
  json << 2
154
154
  end
155
- json.compile!.should == "{\"foo\":[1,2]}"
155
+ json.compile!.should == '{"foo":[1,2]}'
156
156
  end
157
157
 
158
158
  it 'hash with array by iteration' do
@@ -160,13 +160,20 @@ PRETTY_JSON
160
160
  json.foo(ary) do |n|
161
161
  n * 2
162
162
  end
163
- json.compile!.should == "{\"foo\":[2,4,6]}"
163
+ json.compile!.should == '{"foo":[2,4,6]}'
164
164
  end
165
165
 
166
166
  it 'simple array with object' do
167
167
  json << 1
168
168
  json << {:foo => :bar}
169
- json.compile!.should == "[1,{\"foo\":\"bar\"}]"
169
+ json.compile!.should == '[1,{"foo":"bar"}]'
170
+ end
171
+
172
+ it 'simple array with object via method_missing' do
173
+ json << 1
174
+ json << 2
175
+ json.foo :bar
176
+ json.compile!.should == "[1,2,{\"foo\":\"bar\"}]"
170
177
  end
171
178
 
172
179
  it 'complex hash with array' do
@@ -224,4 +231,49 @@ PRETTY_JSON
224
231
  JSON.parse(json.compile!).should == JSON.parse(expected)
225
232
  end
226
233
  end
234
+
235
+ describe 'ingest!' do
236
+ context 'a json object' do
237
+ let(:json_string) { '{"my girl":"Friday","my daughter":"Wednesday"}' }
238
+ context 'into' do
239
+ it 'nothing -- should replace it' do
240
+ json.ingest! json_string
241
+ JSON.parse(json.compile!).should == JSON.parse(json_string)
242
+ end
243
+ it 'json object -- should merge' do
244
+ json["my boy"] = "Monday"
245
+ json["my girl"] = "Sunday"
246
+ json.ingest! json_string
247
+ expected = '{"my boy":"Monday","my girl":"Friday","my daughter":"Wednesday"}'
248
+ JSON.parse(json.compile!).should == JSON.parse(expected)
249
+ end
250
+ it 'json array -- should add' do
251
+ json << 1 << 2
252
+ json.ingest! json_string
253
+ expected = '[1,2,{"my girl":"Friday","my daughter":"Wednesday"}]'
254
+ JSON.parse(json.compile!).should == JSON.parse(expected)
255
+ end
256
+ end
257
+ end
258
+ context 'a json array' do
259
+ let(:json_string) { '[1,2,3]' }
260
+ context 'into' do
261
+ it 'nothing -- should replace it' do
262
+ json.ingest! json_string
263
+ JSON.parse(json.compile!).should == JSON.parse(json_string)
264
+ end
265
+ it 'json object -- should raise error' do
266
+ json["my boy"] = "Monday"
267
+ json["my girl"] = "Sunday"
268
+ lambda{ json.ingest! json_string }.should raise_error( ArgumentError )
269
+ end
270
+ it 'json array -- should add' do
271
+ json << 1 << 2
272
+ json.ingest! json_string
273
+ expected = '[1,2,[1,2,3]]'
274
+ JSON.parse(json.compile!).should == JSON.parse(expected)
275
+ end
276
+ end
277
+ end
278
+ end
227
279
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jsonify
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.3
5
+ version: 0.0.4
6
6
  platform: ruby
7
7
  authors:
8
8
  - Bill Siggelkow
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-07-27 00:00:00 -04:00
13
+ date: 2011-07-30 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -144,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- hash: 2505547202778738501
147
+ hash: -1768495453963674133
148
148
  segments:
149
149
  - 0
150
150
  version: "0"
@@ -153,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
153
  requirements:
154
154
  - - ">="
155
155
  - !ruby/object:Gem::Version
156
- hash: 2505547202778738501
156
+ hash: -1768495453963674133
157
157
  segments:
158
158
  - 0
159
159
  version: "0"