shreddies 0.1.0 → 0.2.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
  SHA256:
3
- metadata.gz: 6770e333d8c5c90f6a7f1d56fd4d3e27fa32103c13473ed500d151ee20cd8826
4
- data.tar.gz: 387cc3e15bbaa1eb8ca3c1775fbc80d22df8c7edf79547dd1518149f07d70e68
3
+ metadata.gz: 1f3a9878d5d25ca7eb02bc3d9f3e4e4dc6f71ab6f970312331ed01fe864923b4
4
+ data.tar.gz: bfba9356df3012c94527f1214d1862c5a507f91aab243f43fc85c8a30b0d50e3
5
5
  SHA512:
6
- metadata.gz: 8a7ef85a2a7e0cf2bac9353ce5998f3b7b897c36da844e7169e13dfea62c377cf7a994b4e0bf27a763721a3853aa503303ff2248b65a6ade68b9db4748b027ec
7
- data.tar.gz: c2b54d864565890b793b63bdbdf6160d85ff7dd3a8de974034499139aedb7c2fb0974cb34ae05acda31d7d444caa480522f4c9408c6284709a6a1168f552f221
6
+ metadata.gz: '069be646751138acce14f15960e417c18dcb2f57981a49b77d7d1a3a71ef4c2c079c49be4535c8ae193e752cb90298ddca0a72d5f51d12a9f2f2860c940a1e84'
7
+ data.tar.gz: 8cf4dd148fe6ab7a6989e52462bc3fd3b14cddf592d17b45bd6470ec60f7482fe21e64a12af1909181bf789981b64a964b30f241e746ff3150de7bb4edfa3245
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /test/internal/db/*.sqlite
data/README.md CHANGED
@@ -20,7 +20,7 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
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.
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, which simply delegates to the subject.
24
24
 
25
25
  ```ruby
26
26
  # app/serializers/user_serializer.rb
@@ -65,12 +65,64 @@ Model collections and array's are also supported:
65
65
  User.all.as_json
66
66
  ```
67
67
 
68
+ 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:
69
+
70
+ ```ruby
71
+ ArticleSerializer < Shreddies::Json
72
+ module Collection
73
+ def url
74
+ "https://blah.com/#{subject.slug}"
75
+ end
76
+ end
77
+ end
78
+ ```
79
+
80
+ Conversely, you can define a `Single` module, and that will be included when rendering a single object.
81
+
82
+ ```ruby
83
+ ArticleSerializer < Shreddies::Json
84
+ module Single
85
+ def body
86
+ 'this body is really, really long, and I do not want it returned in lists.'
87
+ end
88
+ end
89
+ end
90
+ ```
91
+
68
92
  ### Options
69
93
 
70
94
  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
95
 
72
96
  The following standard options are supported, and provide additional built-in functionality:
73
97
 
98
+ #### `serializer`
99
+
100
+ 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:
101
+
102
+ ```ruby
103
+ User.all.as_json serializer: User::AdminSerializer
104
+ ```
105
+
106
+ #### `module`
107
+
108
+ 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.
109
+
110
+ ```ruby
111
+ Article.all.as_json module: :WithBody
112
+ ```
113
+
114
+ ```ruby
115
+ ArticleSerializer < Shreddies::Json
116
+ module WithBody
117
+ def body
118
+ 'This article body is really, really long'
119
+ end
120
+ end
121
+ end
122
+ ```
123
+
124
+ 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.
125
+
74
126
  #### `index_by`
75
127
 
76
128
  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 +151,33 @@ User.all.as_json index_by: :id
99
151
  }
100
152
  ```
101
153
 
154
+ ### Serializer Inheritance
155
+
156
+ A serializer can inherit from any other serializer, which is a great way to create custom views:
157
+
158
+ ```ruby
159
+ # app/serializers/user_serializer.rb
160
+ class UserSerializer < Shreddies::Json
161
+ delegate :id, :first_name, :last_name, :email
162
+
163
+ def name
164
+ "#{first_name} #{last_name}"
165
+ end
166
+ end
167
+
168
+ class User::AdministratorSerializer < UserSerializer
169
+ def type
170
+ 'administrator'
171
+ end
172
+ end
173
+ ```
174
+
175
+ Then call it like any other serializer:
176
+
177
+ ```ruby
178
+ User::AdministratorSerializer.render(user)
179
+ ```
180
+
102
181
  ## Development
103
182
 
104
183
  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.
@@ -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.constantize.render_as_json self, options
11
- else
12
- serializer.render self, options
10
+ serializer = serializer.to_s.safe_constantize
13
11
  end
14
- rescue NameError
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.constantize.render_as_json self, options
25
- else
26
- serializer.render self, options
22
+ serializer = serializer.to_s.safe_constantize
27
23
  end
28
- rescue NameError
29
- super
24
+
25
+ serializer ? serializer.render_as_json(self, options) : super
30
26
  end
31
27
  end
32
28
  end
@@ -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, options)
27
+ mapped[x[index_by]] = new(x, collection_options)
20
28
  end
21
29
  else
22
- mapped = subject.map { |x| new(x, options) }
30
+ mapped = subject.map { |x| new(x, collection_options) }
23
31
  end
24
32
 
25
33
  mapped.as_json
@@ -31,31 +39,62 @@ 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 tio `:subject`.
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
- attr_reader :subject, :options
47
+ attr_reader :subject, :options, :from_collection
40
48
 
41
49
  def initialize(subject, options)
42
50
  @subject = subject
43
51
  @options = options
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
  json = {}
60
+ methods = Set.new(public_methods(false))
48
61
 
49
- methods = public_methods(false)
50
- if self.class.superclass.to_s.end_with?('Serializer')
51
- methods.concat self.class.superclass.public_instance_methods(false)
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
52
66
  end
53
67
 
54
- methods.uniq.excluding(:subject, :options, :as_json).map do |attr|
55
- json[attr] = public_send(attr)
68
+ methods.map do |attr|
69
+ json[attr.to_s.camelize :lower] = public_send(attr)
56
70
  end
57
71
 
58
72
  json.deep_transform_keys { |key| key.to_s.camelize :lower }
59
73
  end
74
+
75
+ private
76
+
77
+ def extend_with_modules
78
+ self.class.ancestors.reverse.each do |ancestor|
79
+ next unless ancestor.to_s.end_with?('Serializer')
80
+
81
+ # Extend with Collection module if it exists, and a collection is being rendered. Otherwise,
82
+ # extend with the Single module if that exists.
83
+ if options[:from_collection]
84
+ (collection_mod = "#{ancestor}::Collection".safe_constantize) && extend(collection_mod)
85
+ else
86
+ (single_mod = "#{ancestor}::Single".safe_constantize) && extend(single_mod)
87
+ end
88
+ end
89
+
90
+ # Extend with the :module option if given.
91
+ if options[:module]
92
+ Array(options[:module]).each do |m|
93
+ mod = m.is_a?(Module) ? m : "#{self.class}::#{m}".constantize
94
+
95
+ extend mod
96
+ end
97
+ end
98
+ end
60
99
  end
61
100
  end
@@ -1,3 +1,3 @@
1
1
  module Shreddies
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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.0
4
+ version: 0.2.0
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-06-30 00:00:00.000000000 Z
11
+ date: 2020-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord