granola 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf1c567fa58a115fc7f8d55fa10ca0e0d482baa9
4
- data.tar.gz: a6c5f64bf66e7445fc3f11c48ff046d66fd701c6
3
+ metadata.gz: 6b87c4322ddf8aa97dd49c26607172fe2119d681
4
+ data.tar.gz: 959742990805ff8890d821177d1c70c5804e5c18
5
5
  SHA512:
6
- metadata.gz: a6918a2d025d20e08ea0f2756adafaa5d0dbf5134a2ad05059edfff3eba26e31d36ffbd7d0ae72a394243edad1d125317b09b40e4eb02aeba0fd31a2eb3e3e55
7
- data.tar.gz: 2117365b5ee8f946ab307a6d2020d8beb0173d06adb26fd3e974272cfa95a8911f348ca5e78023f9c0b1755c6e21fceb332f45d76bb87a947050eae907e63284
6
+ metadata.gz: 4cbdca00522d796177eaa836f822355fbda7959033b71a1eefaea8ceac9dcf48517fd8395c628cf3ab137fa30433d8e66e7a063eb74a42c88be8560caac63251
7
+ data.tar.gz: 05a3665f2b5e13385c014de61e6a213520a339ae162159ca4e86f5e753dc506c2ad2d02016119aa016198ae25b5fa56ffac28ec9e6f5ee5f0f08f1f208ba4240
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
- # Granola, a JSON serializer [![Build Status](https://travis-ci.org/foca/granola.svg?branch=master)](https://travis-ci.org/foca/granola)
1
+ # Granola, a JSON serializer [![Build Status](https://img.shields.io/travis/foca/granola.svg)](https://travis-ci.org/foca/granola) [![RubyGem](https://img.shields.io/gem/v/granola.svg)](https://rubygems.org/gems/granola)
2
2
 
3
3
  ![A tasty bowl of Granola](https://cloud.githubusercontent.com/assets/437/4827156/9e8d33da-5f76-11e4-8574-7803e84845f2.JPG)
4
4
 
5
-
6
5
  Granola aims to provide a simple interface to generate JSON responses based on
7
6
  your application's domain models. It doesn't make assumptions about anything and
8
7
  gets out of your way. You just write plain ruby.
@@ -11,7 +10,7 @@ gets out of your way. You just write plain ruby.
11
10
 
12
11
  ``` ruby
13
12
  class PersonSerializer < Granola::Serializer
14
- def serialized
13
+ def data
15
14
  {
16
15
  "name" => object.name,
17
16
  "email" => object.email,
@@ -38,7 +37,9 @@ Granola.json = Yajl::Encoder.method(:encode)
38
37
  ```
39
38
 
40
39
  If your project already uses [MultiJson][] then we will default to whatever it's
41
- using, so you shouldn't worry.
40
+ using, so you shouldn't worry. Be warned that using MultiJson instead of
41
+ using a library (such as Yajl) straight away incurs a small performance penalty
42
+ (see, and run, [the benchmark](./benchmarks/multi_json.rb)).
42
43
 
43
44
  [Yajl]: https://github.com/brianmario/yajl-ruby
44
45
  [MultiJson]: https://github.com/intridea/multi_json
@@ -63,10 +64,6 @@ granola(person) #=> This will infer PersonSerializer from a Person instance
63
64
  granola(person, with: AnotherSerializer)
64
65
  ```
65
66
 
66
- *NOTE* The method relies on being an `env` method that returns the Rack
67
- environment Hash in the same context where you call the method. See [the
68
- documentation](./lib/granola/rack.rb) for further details.
69
-
70
67
  This method returns a Rack response tuple that you can use like so (this example
71
68
  uses [Cuba][], but similar code will work for other frameworks):
72
69
 
@@ -99,7 +96,7 @@ generating the JSON response altogether. For example, using Cuba:
99
96
 
100
97
  ``` ruby
101
98
  class UserSerializer < Granola::Serializer
102
- def serialized
99
+ def data
103
100
  { "id" => object.id, "name" => object.name, "email" => object.email }
104
101
  end
105
102
 
@@ -141,12 +138,12 @@ class BaseSerializer < Granola::Serializer
141
138
  MIME_TYPES[:msgpack] = "application/x-msgpack".freeze
142
139
 
143
140
  def to_msgpack(*)
144
- MsgPack.pack(serialized)
141
+ MsgPack.pack(data)
145
142
  end
146
143
  end
147
144
  ```
148
145
 
149
- Now all serializers that inherit from `BaseSerializer` can be serialized into
146
+ Now all serializers that inherit from `BaseSerializer` can be data into
150
147
  MsgPack. In order to use this from our Rack helpers, you'd do:
151
148
 
152
149
  ``` ruby
@@ -159,5 +156,7 @@ This will set the correct MIME type.
159
156
 
160
157
  ## License
161
158
 
162
- This project is shared under the MIT license. See the attached LICENSE file for
163
- details.
159
+ This project is shared under the MIT license. See the attached [LICENSE][] file
160
+ for details.
161
+
162
+ [LICENSE]: ./LICENSE
data/lib/granola.rb CHANGED
@@ -1,117 +1,4 @@
1
1
  require "granola/version"
2
- require "json"
3
-
4
- module Granola
5
- class << self
6
- # Public: Get/Set a Proc that takes an Object and a Hash of options and
7
- # returns a JSON String.
8
- #
9
- # The default implementation uses the standard library's JSON module, but
10
- # you're welcome to swap it out.
11
- #
12
- # Example:
13
- #
14
- # require "yajl"
15
- # Granola.json = ->(obj, **opts) { Yajl::Encoder.encode(obj, opts) }
16
- attr_accessor :json
17
- end
18
-
19
- if defined?(MultiJson)
20
- self.json = MultiJson.method(:dump)
21
- else
22
- self.json = JSON.method(:generate)
23
- end
24
-
25
- # A Serializer describes how to serialize a certain type of object, by
26
- # declaring the structure of JSON objects.
27
- class Serializer
28
- attr_reader :object
29
-
30
- # Public: Map of the default MIME type for each given type of serialization
31
- # for this object.
32
- MIME_TYPES = { json: "application/json".freeze }
33
-
34
- # Public: Instantiates a list serializer that wraps around an iterable of
35
- # objects of the type expected by this serializer class.
36
- #
37
- # Example:
38
- #
39
- # serializer = PersonSerializer.list(people)
40
- # serializer.to_json
41
- #
42
- # Returns a Granola::List.
43
- def self.list(ary, *args)
44
- List.new(ary, *args, with: self)
45
- end
46
-
47
- # Public: Initialize the serializer with a given object.
48
- #
49
- # object - The domain model that we want to serialize into JSON.
50
- def initialize(object)
51
- @object = object
52
- end
53
-
54
- # Public: Returns a primitive Object that can be serialized into JSON,
55
- # meaning one of `nil`, `true`, `false`, a String, a Numeric, an Array of
56
- # primitive objects, or a Hash with String keys and primitive objects as
57
- # values.
58
- #
59
- # Raises NotImplementedError unless you override in subclasses.
60
- def serialized
61
- fail NotImplementedError
62
- end
63
-
64
- # Public: Generate the JSON String.
65
- #
66
- # **options - Any options to be passed to the `Granola.json` Proc.
67
- #
68
- # Returns a String.
69
- def to_json(**options)
70
- Granola.json.(serialized, options)
71
- end
72
-
73
- # Public: Returns the MIME type generated by this serializer. By default
74
- # this will be `application/json`, but you can override in your serializers
75
- # if your API uses a different MIME type (e.g. `application/my-app+json`).
76
- #
77
- # type - A Symbol describing the expected mime type.
78
- #
79
- # Returns a String.
80
- def mime_type(type = :json)
81
- MIME_TYPES.fetch(type)
82
- end
83
- end
84
-
85
- # Internal: The List serializer provides an interface for serializing lists of
86
- # objects, wrapping around a specific serializer. The preferred API for this
87
- # is to use `Granola::Serializer.list`.
88
- #
89
- # Example:
90
- #
91
- # serializer = Granola::List.new(people, with: PersonSerializer)
92
- # serializer.to_json
93
- #
94
- # You should use Serializer.list instead of this class.
95
- class List < Serializer
96
- # Internal: Get the serializer class to use for each item of the list.
97
- attr_reader :item_serializer
98
-
99
- # Public: Instantiate a new list serializer.
100
- #
101
- # list - An Array-like structure.
102
- # *args - Any other arguments that the item serializer takes.
103
- #
104
- # Keywords:
105
- # with: The subclass of Granola::Serializer to use when serializing
106
- # specific elements in the list.
107
- def initialize(list, *args, with: serializer)
108
- @item_serializer = with
109
- @list = list.map { |obj| @item_serializer.new(obj, *args) }
110
- end
111
-
112
- # Public: Returns an Array of Hashes that can be serialized into JSON.
113
- def serialized
114
- @list.map(&:serialized)
115
- end
116
- end
117
- end
2
+ require "granola/serializer"
3
+ require "granola/json"
4
+ require "granola/rack"
@@ -1,4 +1,4 @@
1
- require "granola"
1
+ require "granola/serializer"
2
2
 
3
3
  module Granola
4
4
  # Mixin to add caching-awareness to Serializers.
data/lib/granola/rack.rb CHANGED
@@ -1,58 +1,60 @@
1
1
  require "digest/md5"
2
2
  require "time"
3
- require "granola"
4
- require "granola/helper"
3
+ require "granola/serializer"
4
+ require "granola/util"
5
5
  require "granola/caching"
6
+ require "granola/json"
6
7
 
7
- # Mixin to render JSON in the context of a Rack application. See the #json
8
- # method for the specifics.
9
- module Granola::Rack
10
- def self.included(base)
11
- base.send(:include, Granola::Helper)
12
- end
8
+ module Granola
9
+ # Mixin to render JSON in the context of a Rack application. See the #json
10
+ # method for the specifics.
11
+ module Rack
12
+ # Public: Renders a JSON representation of an object using a
13
+ # Granola::Serializer. This takes care of setting the `Last-Modified` and
14
+ # `ETag` headers if appropriate.
15
+ #
16
+ # You can customize the response tuple by passing the status and the default
17
+ # headers, as in the following example:
18
+ #
19
+ # granola(user, status: 400, headers: { "X-Error" => "Boom!" })
20
+ #
21
+ # object - An object to serialize into JSON.
22
+ #
23
+ # Keywords:
24
+ # with: A specific serializer class to use. If this is `nil`,
25
+ # `Util.serializer_class_for` will be used to infer the
26
+ # serializer class.
27
+ # as: A Symbol with the type of serialization desired. Defaults to
28
+ # `:json` (and it's the only one available by default), but could
29
+ # be expanded with plugins to provide serialization to, for
30
+ # example, MsgPack.
31
+ # status: The HTTP status to return on stale responses. Defaults to `200`
32
+ # headers: A Hash of default HTTP headers. Defaults to an empty Hash.
33
+ # **opts: Any other keywords passed will be forwarded to the serializer's
34
+ # serialization backend call.
35
+ #
36
+ # Raises NameError if no specific serializer is provided and we fail to
37
+ # infer one for this object.
38
+ #
39
+ # Returns a Rack response tuple.
40
+ def granola(object, with: nil, status: 200, headers: {}, as: :json, **opts)
41
+ serializer = Granola::Util.serializer_for(object, with: with)
13
42
 
14
- # Public: Renders a JSON representation of an object using a
15
- # Granola::Serializer. This takes care of setting the `Last-Modified` and
16
- # `ETag` headers if appropriate.
17
- #
18
- # You can customize the response tuple by passing the status and the default
19
- # headers, as in the following example:
20
- #
21
- # granola(user, status: 400, headers: { "X-Error" => "Boom!" })
22
- #
23
- # object - An object to serialize into JSON.
24
- #
25
- # Keywords:
26
- # with: A specific serializer class to use. If this is `nil`,
27
- # `Helper.serializer_class_for` will be used to infer the
28
- # serializer class.
29
- # as: A Symbol with the type of serialization desired. Defaults to
30
- # `:json` (and it's the only one available with Granola by default)
31
- # but could be expanded with plugins to provide serialization to,
32
- # for example, MsgPack.
33
- # status: The HTTP status to return on stale responses. Defaults to `200`.
34
- # headers: A Hash of default HTTP headers. Defaults to an empty Hash.
35
- # **opts: Any other keywords passed will be forwarded to the serializer's
36
- # serialization backend call.
37
- #
38
- # Raises NameError if no specific serializer is provided and we fail to infer
39
- # one for this object.
40
- # Returns a Rack response tuple.
41
- def granola(object, with: nil, status: 200, headers: {}, as: :json, **opts)
42
- serializer = serializer_for(object, with: with)
43
+ if serializer.last_modified
44
+ headers["Last-Modified".freeze] = serializer.last_modified.httpdate
45
+ end
43
46
 
44
- if serializer.last_modified
45
- headers["Last-Modified".freeze] = serializer.last_modified.httpdate
46
- end
47
+ if serializer.cache_key
48
+ headers["ETag".freeze] = Digest::MD5.hexdigest(serializer.cache_key)
49
+ end
47
50
 
48
- if serializer.cache_key
49
- headers["ETag".freeze] = Digest::MD5.hexdigest(serializer.cache_key)
50
- end
51
+ headers["Content-Type".freeze] = serializer.mime_type(as)
51
52
 
52
- headers["Content-Type".freeze] = serializer.mime_type(as)
53
+ body = Enumerator.new do |yielder|
54
+ yielder << serializer.public_send(:"to_#{as}", opts)
55
+ end
53
56
 
54
- body = Enumerator.new { |y| y << serializer.public_send(:"to_#{as}", opts) }
55
-
56
- [status, headers, body]
57
+ [status, headers, body]
58
+ end
57
59
  end
58
60
  end
@@ -1,3 +1,3 @@
1
1
  module Granola
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: granola
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Sanguinetti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-15 00:00:00.000000000 Z
11
+ date: 2015-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cutest
@@ -49,7 +49,6 @@ files:
49
49
  - README.md
50
50
  - lib/granola.rb
51
51
  - lib/granola/caching.rb
52
- - lib/granola/helper.rb
53
52
  - lib/granola/rack.rb
54
53
  - lib/granola/version.rb
55
54
  homepage: http://github.com/foca/granola
@@ -1,53 +0,0 @@
1
- require "granola"
2
-
3
- module Granola::Helper
4
- # Public: Returns the serializer object for rendering a specific object. The
5
- # class will attempt to be inferred based on the class of the passed object,
6
- # but a specific serializer can be passed via a keyword argument `with`.
7
- #
8
- # object - The Object to serialize.
9
- #
10
- # Keywords
11
- # with: A specific serializer class to use. If this is `nil`,
12
- # `Helper.serializer_class_for` will be used to infer the serializer
13
- # class. This is ignored if `object` is already a Granola::Serializer.
14
- #
15
- # Raises NameError if no specific serializer is provided and we fail to infer
16
- # one for this object.
17
- # Returns an instance of a Granola::Serializer subclass.
18
- def serializer_for(object, with: nil)
19
- return object if Granola::Serializer === object
20
- serializer_class = with || Granola::Helper.serializer_class_for(object)
21
- method = object.respond_to?(:to_ary) ? :list : :new
22
- serializer_class.send(method, object)
23
- end
24
-
25
- # Internal: Infers the name of a serialized based on the class of the passed
26
- # object. The pattern is the Object's class + "Serializer". So
27
- # `PersonSerializer` for `Person`.
28
- #
29
- # object - An object of a class with a matching serializer.
30
- #
31
- # Raises NameError if no matching class exists.
32
- # Returns a Class.
33
- def self.serializer_class_for(object)
34
- object = object.respond_to?(:to_ary) ? object.to_ary.fetch(0, nil) : object
35
- name = object.class.name
36
- Object.const_get("%sSerializer" % name)
37
- rescue NameError
38
- case object
39
- when NilClass, TrueClass, FalseClass, Numeric, String
40
- PrimitiveTypesSerializer
41
- else
42
- raise
43
- end
44
- end
45
-
46
- # Internal: Null serializer that transparently handles rendering `nil` in case
47
- # it's passed.
48
- class PrimitiveTypesSerializer < Granola::Serializer
49
- def serialized
50
- object
51
- end
52
- end
53
- end