shreddies 0.1.0 → 0.5.1
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/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +140 -1
- data/lib/shreddies/as_json.rb +6 -10
- data/lib/shreddies/json.rb +71 -13
- data/lib/shreddies/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6daee669edb85f0a632236f2eacdec946b07d9419a25ae88f6beb6bd09c4a19
|
4
|
+
data.tar.gz: f673da386a57e68668a356c8687a88a5f0ae7ec934ebab439b48d5fd2d53ef64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec6d5dc108f25b5d4f820574ae8051e5cc594b24d101de65f8145f476bf6f782db0361c4ffdacf6505b7fbb96708e5b6cd4ed7ff704ad4e3bd2d5653a86e3e49
|
7
|
+
data.tar.gz: 4c330bf894abf811866bd21ad77460cd7073ad89d5e97719bc2e2af73bf2d1255808b3487e6880d22c52f5e8a130a19d3b52b09c4ecd711abb4dcc71f52a46ca
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Shreddies is a JSON serialization library for Rails that focuses on simplicity and speed. No more "magic" DSL's - just plain old Ruby objects! It's primarily intended to serialize Rails models as JSON, but will also work with pretty much anything at all.
|
4
4
|
|
5
|
+
Shreddies primary principle is to be explicit. So a serializer will return nothing until you define some methods. This gives you complete control and everything is a known quantity - no surprises.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
@@ -20,7 +22,7 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
## Usage
|
22
24
|
|
23
|
-
Serializers should be named after your models and located in "`app/serializers`". Any public methods you define will be serialized, and you can quickly expose methods from the serialized subject using the `delegate` class method.
|
25
|
+
Serializers should be named after your models and located in "`app/serializers`". Any public methods you define will be serialized, and you can quickly expose methods from the serialized subject using the `delegate` class method, which simply delegates to the subject.
|
24
26
|
|
25
27
|
```ruby
|
26
28
|
# app/serializers/user_serializer.rb
|
@@ -65,12 +67,122 @@ Model collections and array's are also supported:
|
|
65
67
|
User.all.as_json
|
66
68
|
```
|
67
69
|
|
70
|
+
### Collection and Single Modules
|
71
|
+
|
72
|
+
You may find that you don't want or need to return as much data in collections of objects, or may want to include differtent data. So if a serializer defines a `Collection` module, and a collection or array is being rendered, then that Collection module will automatically be included:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
ArticleSerializer < Shreddies::Json
|
76
|
+
module Collection
|
77
|
+
def url
|
78
|
+
"https://blah.com/#{subject.slug}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
Conversely, you can define a `Single` module, and that will be included when rendering a single object.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
ArticleSerializer < Shreddies::Json
|
88
|
+
module Single
|
89
|
+
def body
|
90
|
+
'this body is really, really long, and I do not want it returned in lists.'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
### ActiveRecord Associations
|
97
|
+
|
98
|
+
ActiveRecord associations are supported with no additional work on your part. Shreddies will simply call `#as_json` on any method that returns an ActiveRecord model or relation.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
# app/serializers/user_serializer.rb
|
102
|
+
class UserSerializer < Shreddies::Json
|
103
|
+
delegate :articles
|
104
|
+
|
105
|
+
def latest_article
|
106
|
+
articles.latest
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
And if you need to be specific about what you render, just call the serializer or `#as_json` directly:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# app/serializers/user_serializer.rb
|
115
|
+
class UserSerializer < Shreddies::Json
|
116
|
+
def articles
|
117
|
+
subject.articles.as_json index_by: :slug
|
118
|
+
end
|
119
|
+
|
120
|
+
def latest_article
|
121
|
+
LatestArticleSerializer.render articles.latest
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
### `before_render` callback
|
127
|
+
|
128
|
+
You can define a `#before_render` private method in your serializers, which will act as a callback. It receives the object to be output, and expects you to return the object, which allows you to modify it before rendering.
|
129
|
+
|
68
130
|
### Options
|
69
131
|
|
70
132
|
Both `#as_json` and `.render` accepts an `options` hash, which will be forwarded to the serializer class, and available as `options`. This allows you to pass arbitrary options and use them in your serializer.
|
71
133
|
|
72
134
|
The following standard options are supported, and provide additional built-in functionality:
|
73
135
|
|
136
|
+
#### `serializer`
|
137
|
+
|
138
|
+
By default `#as_json` will look for a serializer named after your model. So a `User` model will automatically use the `UserSerializer`. Sometimes you want to use a different serializer class, in which case you can use the `serializer` option:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
User.all.as_json serializer: User::AdminSerializer
|
142
|
+
```
|
143
|
+
|
144
|
+
#### `module`
|
145
|
+
|
146
|
+
You can pass one or module names in the `module` option, and these modules will be included into the serializer. This is great for selectively including attributes and methods.
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
Article.all.as_json module: :WithBody
|
150
|
+
```
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
ArticleSerializer < Shreddies::Json
|
154
|
+
module WithBody
|
155
|
+
def body
|
156
|
+
'This article body is really, really long'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
The `Collection` and `Single` modules can be defined and they will be automatically included. The Collection module will be included when rendering an array or ActiveRecord collection (`ActiveRecord::Relation`), and the Single module will be included when rendering a single obejct.
|
163
|
+
|
164
|
+
#### `transform_keys` (default: true)
|
165
|
+
|
166
|
+
If false, the returned keys will not be transformed. The default is to deeply transform all keys to camelCase.
|
167
|
+
|
168
|
+
#### `except`
|
169
|
+
|
170
|
+
Pass one or more attribute names as a Symbol or Array of Symbols, and these will be excluded from the results:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
User.all.as_json(except: [:first_name, :age])
|
174
|
+
```
|
175
|
+
|
176
|
+
#### `only`
|
177
|
+
|
178
|
+
Pass one or more attribute names as a Symbol or Array of Symbols, and _ONLY_ these will be included in the results:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
User.all.as_json(only: :first_name)
|
182
|
+
```
|
183
|
+
|
184
|
+
> Attributes must still be defined within the Serializer.
|
185
|
+
|
74
186
|
#### `index_by`
|
75
187
|
|
76
188
|
Give this option a property of your serialized subject as a Symbol, and the returned collection will be a Hash keyed by that property.
|
@@ -99,6 +211,33 @@ User.all.as_json index_by: :id
|
|
99
211
|
}
|
100
212
|
```
|
101
213
|
|
214
|
+
### Serializer Inheritance
|
215
|
+
|
216
|
+
A serializer can inherit from any other serializer, which is a great way to create custom views:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
# app/serializers/user_serializer.rb
|
220
|
+
class UserSerializer < Shreddies::Json
|
221
|
+
delegate :id, :first_name, :last_name, :email
|
222
|
+
|
223
|
+
def name
|
224
|
+
"#{first_name} #{last_name}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class User::AdministratorSerializer < UserSerializer
|
229
|
+
def type
|
230
|
+
'administrator'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
```
|
234
|
+
|
235
|
+
Then call it like any other serializer:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
User::AdministratorSerializer.render(user)
|
239
|
+
```
|
240
|
+
|
102
241
|
## Development
|
103
242
|
|
104
243
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/shreddies/as_json.rb
CHANGED
@@ -7,12 +7,10 @@ module Shreddies
|
|
7
7
|
serializer = options.delete(:serializer) || "#{model_name}Serializer"
|
8
8
|
|
9
9
|
if serializer.is_a?(String) || serializer.is_a?(Symbol)
|
10
|
-
serializer.to_s.
|
11
|
-
else
|
12
|
-
serializer.render self, options
|
10
|
+
serializer = serializer.to_s.safe_constantize
|
13
11
|
end
|
14
|
-
|
15
|
-
super
|
12
|
+
|
13
|
+
serializer ? serializer.render_as_json(self, options) : super
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -21,12 +19,10 @@ module Shreddies
|
|
21
19
|
serializer = options.delete(:serializer) || "#{model_name}Serializer"
|
22
20
|
|
23
21
|
if serializer.is_a?(String) || serializer.is_a?(Symbol)
|
24
|
-
serializer.to_s.
|
25
|
-
else
|
26
|
-
serializer.render self, options
|
22
|
+
serializer = serializer.to_s.safe_constantize
|
27
23
|
end
|
28
|
-
|
29
|
-
super
|
24
|
+
|
25
|
+
serializer ? serializer.render_as_json(self, options) : super
|
30
26
|
end
|
31
27
|
end
|
32
28
|
end
|
data/lib/shreddies/json.rb
CHANGED
@@ -6,20 +6,28 @@ module Shreddies
|
|
6
6
|
# Render a subject as json, where `subject` is a single object (usually a Rails model), or an
|
7
7
|
# array/collection of objects.
|
8
8
|
#
|
9
|
+
# If subject is an array/collection then it will look for a `Collection` module prepend it to
|
10
|
+
# the `module` option.
|
11
|
+
#
|
9
12
|
# A Hash of options can be given as the second argument:
|
10
13
|
# - index_by - Key the returned array by the value, transforming it from an array to a hash.
|
14
|
+
# - module - A Symbol or String of a local module to include. Or an array of several
|
15
|
+
# modules, where each will be mixed in in order. Use this to mix in groups of
|
16
|
+
# attributes. Eg. `ArticleSerializer.render(data, module: :WithBody)`.
|
11
17
|
#
|
12
18
|
def render(subject, options = {})
|
13
19
|
index_by = options.delete(:index_by)
|
14
20
|
|
15
21
|
if subject.is_a?(Array) || subject.is_a?(ActiveRecord::Relation)
|
22
|
+
collection_options = options.merge(from_collection: true)
|
23
|
+
|
16
24
|
if index_by
|
17
25
|
mapped = {}
|
18
26
|
subject.each do |x|
|
19
|
-
mapped[x[index_by]] = new(x,
|
27
|
+
mapped[x[index_by]] = new(x, collection_options)
|
20
28
|
end
|
21
29
|
else
|
22
|
-
mapped = subject.map { |x| new(x,
|
30
|
+
mapped = subject.map { |x| new(x, collection_options) }
|
23
31
|
end
|
24
32
|
|
25
33
|
mapped.as_json
|
@@ -31,31 +39,81 @@ module Shreddies
|
|
31
39
|
alias render_as_json render
|
32
40
|
end
|
33
41
|
|
34
|
-
# Monkey patches Rails Module#delegate so that the `:to` argument defaults
|
42
|
+
# Monkey patches Rails Module#delegate so that the `:to` argument defaults to `:subject`.
|
35
43
|
def self.delegate(*methods, to: :subject, prefix: nil, allow_nil: nil, private: nil)
|
36
44
|
super(*methods, to: to, prefix: prefix, allow_nil: allow_nil, private: private)
|
37
45
|
end
|
38
46
|
|
39
47
|
attr_reader :subject, :options
|
40
48
|
|
41
|
-
def initialize(subject,
|
42
|
-
@subject = subject
|
43
|
-
@options =
|
49
|
+
def initialize(subject, opts = {})
|
50
|
+
@subject = subject.is_a?(Hash) ? OpenStruct.new(subject) : subject
|
51
|
+
@options = { transform_keys: true }.merge(opts)
|
52
|
+
|
53
|
+
extend_with_modules
|
44
54
|
end
|
45
55
|
|
56
|
+
# Travel through the ancestors that are serializers (class name ends with "Serializer"), and
|
57
|
+
# call all public instance methods, returning a hash.
|
46
58
|
def as_json
|
47
|
-
|
59
|
+
output = {}.with_indifferent_access
|
60
|
+
methods = Set.new(public_methods(false))
|
61
|
+
|
62
|
+
self.class.ancestors.each do |ancestor|
|
63
|
+
if ancestor.to_s.end_with?('Serializer')
|
64
|
+
methods.merge ancestor.public_instance_methods(false)
|
65
|
+
end
|
66
|
+
end
|
48
67
|
|
49
|
-
methods
|
50
|
-
if
|
51
|
-
|
68
|
+
# Filter out methods using the `only` or `except` options.
|
69
|
+
if @options[:only]
|
70
|
+
@options[:only] = Array(@options[:only])
|
71
|
+
methods = methods.select { |x| @options[:only].include? x }
|
72
|
+
elsif @options[:except]
|
73
|
+
methods = methods.excluding(@options[:except])
|
52
74
|
end
|
53
75
|
|
54
|
-
methods.
|
55
|
-
|
76
|
+
methods.map do |attr|
|
77
|
+
res = public_send(attr)
|
78
|
+
if res.is_a?(ActiveRecord::Relation) || res.is_a?(ActiveRecord::Base)
|
79
|
+
res = res.as_json(transform_keys: @options[:transform_keys])
|
80
|
+
end
|
81
|
+
|
82
|
+
output[attr] = res
|
83
|
+
end
|
84
|
+
|
85
|
+
output = before_render(output)
|
86
|
+
|
87
|
+
return output unless @options[:transform_keys]
|
88
|
+
|
89
|
+
output.deep_transform_keys { |key| key.to_s.camelize :lower }
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def before_render(output)
|
95
|
+
output
|
96
|
+
end
|
97
|
+
|
98
|
+
def extend_with_modules
|
99
|
+
self.class.ancestors.reverse.each do |ancestor|
|
100
|
+
next unless ancestor.to_s.end_with?('Serializer')
|
101
|
+
|
102
|
+
# Extend with Collection module if it exists, and a collection is being rendered. Otherwise,
|
103
|
+
# extend with the Single module if that exists.
|
104
|
+
if @options[:from_collection]
|
105
|
+
(collection_mod = "#{ancestor}::Collection".safe_constantize) && extend(collection_mod)
|
106
|
+
else
|
107
|
+
(single_mod = "#{ancestor}::Single".safe_constantize) && extend(single_mod)
|
108
|
+
end
|
56
109
|
end
|
57
110
|
|
58
|
-
|
111
|
+
# Extend with the :module option if given.
|
112
|
+
if @options[:module]
|
113
|
+
Array(@options[:module]).each do |m|
|
114
|
+
extend m.is_a?(Module) ? m : "#{self.class}::#{m}".constantize
|
115
|
+
end
|
116
|
+
end
|
59
117
|
end
|
60
118
|
end
|
61
119
|
end
|
data/lib/shreddies/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shreddies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Moss
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|