granola 0.9.0 → 0.10.0
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.
- checksums.yaml +4 -4
- data/README.md +12 -13
- data/lib/granola.rb +3 -116
- data/lib/granola/caching.rb +1 -1
- data/lib/granola/rack.rb +49 -47
- data/lib/granola/version.rb +1 -1
- metadata +2 -3
- data/lib/granola/helper.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b87c4322ddf8aa97dd49c26607172fe2119d681
|
4
|
+
data.tar.gz: 959742990805ff8890d821177d1c70c5804e5c18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cbdca00522d796177eaa836f822355fbda7959033b71a1eefaea8ceac9dcf48517fd8395c628cf3ab137fa30433d8e66e7a063eb74a42c88be8560caac63251
|
7
|
+
data.tar.gz: 05a3665f2b5e13385c014de61e6a213520a339ae162159ca4e86f5e753dc506c2ad2d02016119aa016198ae25b5fa56ffac28ec9e6f5ee5f0f08f1f208ba4240
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
# Granola, a JSON serializer [](https://travis-ci.org/foca/granola) [](https://rubygems.org/gems/granola)
|
2
2
|
|
3
3
|

|
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
|
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
|
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(
|
141
|
+
MsgPack.pack(data)
|
145
142
|
end
|
146
143
|
end
|
147
144
|
```
|
148
145
|
|
149
|
-
Now all serializers that inherit from `BaseSerializer` can be
|
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
|
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 "
|
3
|
-
|
4
|
-
|
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"
|
data/lib/granola/caching.rb
CHANGED
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/
|
3
|
+
require "granola/serializer"
|
4
|
+
require "granola/util"
|
5
5
|
require "granola/caching"
|
6
|
+
require "granola/json"
|
6
7
|
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
+
if serializer.cache_key
|
48
|
+
headers["ETag".freeze] = Digest::MD5.hexdigest(serializer.cache_key)
|
49
|
+
end
|
47
50
|
|
48
|
-
|
49
|
-
headers["ETag".freeze] = Digest::MD5.hexdigest(serializer.cache_key)
|
50
|
-
end
|
51
|
+
headers["Content-Type".freeze] = serializer.mime_type(as)
|
51
52
|
|
52
|
-
|
53
|
+
body = Enumerator.new do |yielder|
|
54
|
+
yielder << serializer.public_send(:"to_#{as}", opts)
|
55
|
+
end
|
53
56
|
|
54
|
-
|
55
|
-
|
56
|
-
[status, headers, body]
|
57
|
+
[status, headers, body]
|
58
|
+
end
|
57
59
|
end
|
58
60
|
end
|
data/lib/granola/version.rb
CHANGED
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.
|
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-
|
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
|
data/lib/granola/helper.rb
DELETED
@@ -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
|