README.md in jsonify-0.0.3 vs README.md in jsonify-0.0.4

- old
+ new

@@ -1,6 +1,6 @@ -# 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> +# Jsonify &mdash; a builder for JSON [![Build Status](http://travis-ci.org/bsiggelkow/jsonify.png)](http://travis-ci.org/bsiggelkow/jsonify) [Jsonify](https://github.com/bsiggelkow/jsonify) is to JSON as [Builder](https://github.com/jimweirich/builder) is to XML. ## Goal @@ -29,10 +29,16 @@ `gem install jsonify` ## Usage +In the examples that follow, the JSON output is usually shown "prettified". Is this only +for illustration purposes, as the default behavior for Jsonify is not to prettify the output. +You can enable prettification by passing `:pretty => true` to the Jsonify::Builder constructor; however, +pretty printing is a relatively costly operation and should not be used in production (unless, of course, you explicitly +want to show this format). + ### Standalone # Create some objects that represent a person and associated hyperlinks @person = Struct.new(:first_name,:last_name).new('George','Burdell') @links = [ ['self', 'http://example.com/people/123'], @@ -84,28 +90,266 @@ json.hello do json.world "Jsonify is Working!" end +#### Partials + +You can use partials with Jsonify view templates, but how you use them will +depend on how the information they return is structured. An important to keep in +mind is that a partial, no matter what kind it is, always a returns a string. + +##### Jsonify partials + +Any Jsonify partial &mdash; that is, the file has a `.jsonify` extension &mdash; +will return, by design, a string that is valid JSON. It will represent either a JSON object, +and be wrapped in curly braces ({}), or a JSON array, and be wrapped in square brackets ([]). + +To incorporate such a value into a Jsonify template, Jsonify provides the `ingest!` method. + +You can use this method to add JSON, rendered by a partial, to the builder. +Let's assume this our main template, `index.jsonify`: + + json << 1 + json.ingest! (render :partial=>'my_partial') + +From the first line, you can tell that an array will be created so this line uses the append operator. +On the second line, a partial is being added to the builder. Note that you cannot simply place `render :parial ...` +on a line by itself as you can do with other templates like `erb` and `haml`; you have to explicitly tell Jsonify +to add it. + +Let's say that the partial file, `_my_partial.jsonify`, is as follows: + + json << 3 + json << 4 + +This `json` variable is a separate instance of the Jsonify::Builder, distinct from the builder instance, +in the main template. This partial will end up generating the following string: + + "[3,4]" + +The `ingest!` method will actually parse this string back into a Jsonify-based object, and add it +to the builder's current state. The resulting output will be: + + "[1,[3,4]]" + +##### Other partials + +You can also use output from non-Jsonify templates (e.g. erb); just remember that the output from a template +is always a string and that you have to tell the builder how to include the result of the partial. +For example, suppose I have the partial `_today.erb` with the following content: + + <%= Date.today %> + +You can then incorporate this partial into your Jsonify template just as you would any other string value: + + json << 1 + json.ingest! (render :partial=>'my_partial') + json << {:date => (render :partial => 'today')} + + renders ... + + [1,[3,4],{"date":"2011-07-30"}] + +### Usage Patterns + +Jsonify is designed to support construction of an valid JSON representation and +is entirely based on the [JSON specification](http://json.org). + +JSON is built on two fundamental structures: + + * __object__: a collection of name-value pairs -- in Jsonify this is a `JsonObject` + * __array__: an ordered list of values -- in Jsonify this is a `JsonArray` + +Jsonify adheres to the JSON specification and provides explicit support +for working with these primary structures. At the top most level, a JSON string +must be one of these structures and Jsonify ensures that this condition is met. + +#### JSON Objects + +A JSON object, sometimes +referred to as an ___object literal___, is a common structure familiar +to most developers. Its analogous to the nested element structured common +in XML. The [JSON RFC](http://www.ietf.org/rfc/rfc4627.txt) states that +"the names within an object SHOULD be unique". Jsonify elevates this recommendation +by backing the JsonObject with a `Hash`; an object must have unique keys and the last one in, wins. + + json = Jsonify::Builder.new + json.person do # start a new JsonObject where the key is 'foo' + json.name 'George Burdell' # add a pair to this object + json.skills ['engineering','bombing'] # adds a pair with an array value + json.name 'George P. Burdell' + end + +compiles to ... + + { + "person": { + "name": "George P. Burdell", + "skills": [ + "engineering", + "bombing" + ] + } + } + +It's perfectly legitimate for a JSON representation to simply be a collection +of name-value pairs without a ___root___ element. Jsonify supports this by +simply allowing you to specify the pairs that make up the object. + + json = Jsonify::Builder.new + json.location 'Library Coffeehouse' + json.neighborhood 'Brookhaven' + + compiles to ... + + { + "location": "Library Coffeehouse", + "neighborhood": "Brookhaven" + } + +If the ___name___ you want contains whitespace or other characters not allowed in a Ruby method name, use `tag!`. + + json.tag!("my location", 'Library Coffeehouse') + json.neighborhood 'Brookhaven' + + { + "my location": "Library Coffeehouse", + "neighborhood": "Brookhaven" + } + +Jsonify also supports a hash-style interface for creating JSON objects. + + json = Jsonify::Builder.new + + json[:foo] = :bar + json[:go] = :far + + compiles to ... + + { + "foo": "bar", + "go": "far" + } + +You can these hash-style methods within a block as well ... + + json.homer do + json[:beer] = "Duffs" + json[:spouse] = "Marge" + end + + compiles to ... + + { + "homer": { + "beer": "Duffs", + "spouse": "Marge" + } + } + +If you prefer a more method-based approach, you can use the `store!` method passing it the key and value. + + json.store!(:foo, :bar) + json.store!(:go, :far) + +#### JSON Arrays + +A JSON array is an ordered list of JSON values. A JSON value can be a simple value, +like a string or a number, or a supported JavaScript primitive like true, false, or null. +A JSON value can also be a JSON object or another JSON array. Jsonify strives to make +this kind of construction possible in a buider-style. + +Jsonify supports JSON array construction through two approaches: `method_missing` and `append!`. + +##### method_missing + +Pass an array and a block to `method_missing` (or `tag!`), and Jsonify will iterate +over that array, and create a JSON array where each array item is the result of the block. +If you pass an array that has a length of 5, you will end up with a JSON array that has 5 items. +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`) +or symbol (for `tag!`). + +So this construct is really doing two things -- creating a JSON pair, and creating a JSON array as the value of the pair. + + json = Jsonify::Builder.new(:pretty => true) + json.letters('a'..'c') do |letter| + letter.upcase + end + +compiles to ... + + { + "letters": [ + "A", + "B", + "C" + ] + } + +Another way to handle this particular example is to get rid of the block entirely. +Simply pass the array directly &mdash; the result will be the same. + + json.letters ('a'..'c').map(&:upcase) + +##### append! + +But what if we don't want to start with an object? How do we tell Jsonify to start with an array instead? + +You can use `append!` (passing one or more values), or `<<` (which accepts only a single value) to +the builder and it will assume you are adding values to a JSON array. + + json = Jsonify::Builder.new + json.append! 'a'.upcase, 'b'.upcase, 'c'.upcase + + [ + "A", + "B", + "C" + ] + +or more idiomatically ... + + json.append! *('a'..'c').map(&:upcase) + +The append ___operator___, `<<`, can be used to push a single value into the array: + + json << 'a'.upcase + json << 'b'.upcase + json << 'c'.upcase + +Of course, standard iteration works here as well ... + + json = Jsonify::Builder.new + ('a'..'c').each do |letter| + json << letter.upcase + end + +#### Mixing JSON Arrays and Objects + +___coming soon___ + ## Documentation [Yard Docs](http://rubydoc.info/github/bsiggelkow/jsonify/master/frames) <a name='related'/> <h2>Related Projects</h2> - [Argonaut](https://github.com/jbr/argonaut) - [JSON Builder](https://github.com/dewski/json_builder) - [RABL](https://github.com/nesquena/rabl) +- [Representative](https://github.com/mdub/representative) - [Tokamak](https://github.com/abril/tokamak) ## TODOs 1. Benchmark performance 1. Document how partials can be used +1. Clean up specs ## Roadmap 1. Split Rails template handling into separate gem -1. Add support for Sinatra and Padrino (maybe separate gems) +1. Add support for Sinatra and Padrino (Tilt integration?) ## License This project is released under the MIT license.