userializer 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 005acb6633b44efdcba248bb884d037db223f4fcd214922d0b3f652fc4e31bea
4
- data.tar.gz: 11297dd72a7cc9db42e90dc47567b66cbaea86ae511507fadc59bdef91e2d3ee
3
+ metadata.gz: 6ff350c75deb5f250911135a9ea0cd76b0d0edc451186f5a870895818476862d
4
+ data.tar.gz: 6faf6e27adbc666f620235d15ec4d8ceb62b54ef39416c6693c81d0f59cf84ff
5
5
  SHA512:
6
- metadata.gz: aed7569fce17d28d3f8d774487ce0743c4e5b11d4a90d41f077b52efe6013ced67e1eef9e8c4d10435a309a62a69beefdc1736571b1859b4af99a9783a2c1519
7
- data.tar.gz: ec13d1091403bce5a7d9b87a5f10e27e6f98494795117501fdf5155642c9f2ed802dc015ae4a5c46b5c125831572350d8ed923696b49b4dad9dadd37e08215d4
6
+ metadata.gz: 55bb0fae587d95804183d68495be2e19da3fe1d3f26acb5604fd0a05a715357a14ac6027af276d42c03f59e12067f54f35b85ee09452c2455987b9bcf4be3b8a
7
+ data.tar.gz: d0219c7986d8bb650d247f1d6e43612f84afc6ee58f7919da08767b760c8a720e1a9d5aef343a03a43c89a8981dfdc936507e2036dfefe81c5fbb72ea3806c1c
@@ -0,0 +1,18 @@
1
+ version: 2
2
+ jobs:
3
+ run_tests:
4
+ docker:
5
+ - image: circleci/ruby:2.7
6
+
7
+ working_directory: ~/userializer
8
+
9
+ steps:
10
+ - checkout
11
+ - run: bundle install --path=vendor/bundle
12
+ - run: bundle exec rspec --color --require spec_helper --format progress spec
13
+
14
+ workflows:
15
+ version: 2
16
+ test:
17
+ jobs:
18
+ - run_tests
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ Gemfile.lock
data/README.md CHANGED
@@ -20,7 +20,152 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO: Write usage instructions here
23
+ USerializer's DSL is relatively close to Active Model Serializer's,
24
+ while having a few additional features including:
25
+ * Attributes Conditional Declaration
26
+ * Attributes Inline Definition
27
+
28
+ ### Attributes Conditional Declaration
29
+
30
+ USerializer allows you to dynamically decide wether an attribute should
31
+ be serialized or not by passing its definition an `if` block as follows:
32
+ ```ruby
33
+ attributes :conditional_attr, if: proc { |_, opts| ... }
34
+ ```
35
+
36
+ Eg: Let's say you want to serialize an `Order` object but want to
37
+ include its `price` only if it's superior to *10*, your serializer
38
+ would look like the following:
39
+ ```ruby
40
+ class Order < ActiveRecord::Base
41
+ def price
42
+ 10
43
+ end
44
+ end
45
+
46
+ class OrderSerializer < USerializer::BaseSerializer
47
+ attributes :price, if: proc do |obj, _|
48
+ obj.price > 10
49
+ end
50
+ end
51
+ ```
52
+
53
+ In that case for example, the `price` attribute would be omitted from
54
+ the final response.
55
+
56
+ ### Attributes Inline Definition
57
+
58
+ Using AMS, the only way to rewrite an attribute prior to serialization
59
+ is to override it using a method with the same name, leading to
60
+ something like this:
61
+ ```ruby
62
+ class MyObject < ActiveRecord::Base
63
+ def random_attr
64
+ 0
65
+ end
66
+ end
67
+
68
+ class MyObjectSerializer < ActiveModel::Serializer
69
+ attributes :random_attr
70
+
71
+ def random_attr
72
+ object.random_attr + 1
73
+ end
74
+ end
75
+ ```
76
+
77
+ While this code works perfectly, it pushes the serialized attribute
78
+ value definition back from its declaration, causing developers to lose
79
+ focus when listing their serialized attributes because the overriding is
80
+ done farther.
81
+
82
+ With USerializer, all of this is done in an inline way, so that you can
83
+ override the attribute's value while declaring using a block as
84
+ follows:
85
+ ```ruby
86
+ attributes :your_attribute do |object, _|
87
+ ...
88
+ end
89
+ ```
90
+
91
+ Our `random_attr` serialization would then looks like this with
92
+ USerializer:
93
+ ```ruby
94
+ class MyObjectSerializer < USerializer::BaseSerializer
95
+ attributes :random_attr do |object, _|
96
+ object.random_attr + 1
97
+ end
98
+ end
99
+ ```
100
+
101
+ Way nicer, right?
102
+
103
+ ### Relationships
104
+
105
+ Just like AMS, USerializer supports `has_one` and `has_many`
106
+ relationships
107
+
108
+ ### Serialized Output
109
+
110
+ The following outputs will be based an on our `Order` object in
111
+ different situations:
112
+
113
+ * Order is serialized without any relationships:
114
+ ```json
115
+ {
116
+ "order": {
117
+ "id": 1,
118
+ "attr_1": "value_1",
119
+ "attr_2": "value_2",
120
+ "attr_3": "value_3",
121
+ }
122
+ }
123
+ ```
124
+
125
+ * Order has a `has_one` relationship with a `Client` model
126
+ ```json
127
+ {
128
+ "clients": [
129
+ {
130
+ "id": 4,
131
+ "name": "userializer client",
132
+ ...
133
+ }
134
+ ],
135
+ "order": {
136
+ "id": 1,
137
+ "attr_1": "value_1",
138
+ "attr_2": "value_2",
139
+ "attr_3": "value_3",
140
+ "client_id": 4
141
+ }
142
+ }
143
+ ```
144
+
145
+ * Order has a `has_many` relationship with an `Article` model
146
+ ```json
147
+ {
148
+ "articles": [
149
+ {
150
+ "id": 1,
151
+ "name": "Article #1",
152
+ ...
153
+ },
154
+ {
155
+ "id": 1,
156
+ "name": "Article #2",
157
+ ...
158
+ }
159
+ ],
160
+ "order": {
161
+ "id": 1,
162
+ "attr_1": "value_1",
163
+ "attr_2": "value_2",
164
+ "attr_3": "value_3",
165
+ "article_ids": [1, 2]
166
+ }
167
+ }
168
+ ```
24
169
 
25
170
  ## Development
26
171
 
@@ -14,24 +14,31 @@ module USerializer
14
14
 
15
15
  raise HeterogeneousArray if clss.count > 1
16
16
 
17
- @root_key = opts[:root] || ActiveSupport::Inflector.pluralize(
17
+ @root_key = opts[:root]
18
+ @root_key ||= ActiveSupport::Inflector.pluralize(
18
19
  ActiveSupport::Inflector.underscore(obj_class.name).split('/').last
19
- ).to_sym
20
+ ).to_sym if obj_class
20
21
 
21
- @serializer = opts[:each_serializer] || USerializer.infered_serializer_class(
22
- obj_class
23
- )
22
+ serializer = opts[:each_serializer]
23
+
24
+ @serializer = if serializer&.is_a?(Proc)
25
+ serializer
26
+ elsif serializer
27
+ proc { serializer }
28
+ end
24
29
  end
25
30
 
26
31
  def merge_root(res, opts)
27
32
  @objs.each do |obj|
28
- @serializer.new(obj, @opts).merge_root(res, @root_key, false, opts)
33
+ serializer(obj, opts).merge_root(res, @root_key, false, opts)
29
34
  end
30
35
  end
31
36
 
32
37
  def to_hash
33
38
  res = {}
34
39
 
40
+ res[@root_key] = [] if @root_key
41
+
35
42
  merge_root(res, @opts)
36
43
  res[:meta] = @meta if @meta
37
44
 
@@ -43,5 +50,14 @@ module USerializer
43
50
  end
44
51
 
45
52
  def scope; @opts[:scope]; end
53
+
54
+ private
55
+
56
+ def serializer(obj, opts)
57
+ return @serializer.call(obj, opts).new(obj, @opts) if @serializer
58
+ return obj.serialize if obj.respond_to?(:serialize)
59
+
60
+ USerializer.infered_serializer_class(obj.class).new(obj, @opts)
61
+ end
46
62
  end
47
63
  end
@@ -8,8 +8,8 @@ module USerializer
8
8
  class BaseSerializer
9
9
  class << self
10
10
  def inherited(subclass)
11
- subclass.attrs = self.attrs || { id: Attribute.new(:id, {}, nil) }
12
- subclass.relations = self.relations || {}
11
+ subclass.attrs = self.attrs.dup || { id: Attribute.new(:id, {}, nil) }
12
+ subclass.relations = self.relations.dup || {}
13
13
  end
14
14
 
15
15
  def attributes(*attrs, &block)
@@ -8,21 +8,31 @@ module USerializer
8
8
  @opts = opts
9
9
  @id_key = "#{ActiveSupport::Inflector.singularize(key)}_ids".to_sym
10
10
 
11
+ @embed_key = opts[:embed_key] || :id
11
12
  @conditional_block = opts[:if] || proc { true }
12
13
  end
13
14
 
14
15
  def merge_attributes(res, ser, opts)
15
16
  return unless @conditional_block.call(ser.object, opts)
16
17
 
17
- res[@id_key] = (ser.send(@key) || []).compact.map(&:id).compact
18
+ res[@id_key] = (entities(ser) || []).compact.map do |obj|
19
+ obj.nil? ? nil : obj.send(@embed_key)
20
+ end.compact
18
21
  end
19
22
 
20
23
  def merge_root(res, ser, opts)
21
- objs = ser.send(@key) || []
24
+ objs = entities(ser) || []
22
25
 
23
26
  return if objs.empty? || !@conditional_block.call(ser.object, opts)
24
27
 
25
28
  ArraySerializer.new(objs, @opts).merge_root(res, opts)
26
29
  end
30
+
31
+ def entities(ser)
32
+ obj = ser.send(@key) || []
33
+ return obj unless @opts[:scope]
34
+
35
+ obj.send(@opts[:scope])
36
+ end
27
37
  end
28
38
  end
@@ -6,8 +6,15 @@ module USerializer
6
6
  @id_key = "#{key}_id".to_sym
7
7
  @root_key = opts[:root]&.to_sym
8
8
 
9
- @serializer = opts[:serializer]
9
+ serializer = opts[:serializer]
10
10
 
11
+ @serializer = if serializer&.is_a?(Proc)
12
+ @serializer = serializer
13
+ elsif serializer
14
+ proc { serializer }
15
+ end
16
+
17
+ @embed_key = opts[:embed_key] || :id
11
18
  @conditional_block = opts[:if] || proc { true }
12
19
  end
13
20
 
@@ -17,7 +24,7 @@ module USerializer
17
24
  return unless @conditional_block.call(ser.object, opts)
18
25
 
19
26
  obj = ser.send(@key)
20
- res[@id_key] = obj&.id
27
+ res[@id_key] = obj.nil? ? nil : obj.send(@embed_key)
21
28
  end
22
29
 
23
30
  def merge_root(res, ser, opts)
@@ -25,13 +32,13 @@ module USerializer
25
32
 
26
33
  return if obj.nil? || !@conditional_block.call(ser.object, opts)
27
34
 
28
- serializer(obj).merge_root(res, root_key(obj), false, opts)
35
+ serializer(obj, opts).merge_root(res, root_key(obj), false, opts)
29
36
  end
30
37
 
31
38
  private
32
39
 
33
- def serializer(obj)
34
- return @serializer.new(obj, @opts) if @serializer
40
+ def serializer(obj, opts)
41
+ return @serializer.call(obj, opts).new(obj, @opts) if @serializer
35
42
  return obj.serialize if obj.respond_to?(:serialize)
36
43
 
37
44
  USerializer.infered_serializer_class(obj.class).new(obj, @opts)
@@ -1,3 +1,3 @@
1
1
  module USerializer
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
data/userializer.gemspec CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.16"
24
- spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "bundler", "~> 2.0"
24
+ spec.add_development_dependency "rake", "~> 13.0"
25
25
  spec.add_development_dependency "rspec", "~> 3.0"
26
26
  spec.add_dependency "oj"
27
27
  spec.add_dependency "activesupport"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: userializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Montagne
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-10 00:00:00.000000000 Z
11
+ date: 2021-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.16'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -87,11 +87,11 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".circleci/config.yml"
90
91
  - ".gitignore"
91
92
  - ".rspec"
92
93
  - ".travis.yml"
93
94
  - Gemfile
94
- - Gemfile.lock
95
95
  - README.md
96
96
  - Rakefile
97
97
  - bin/console
@@ -107,7 +107,7 @@ files:
107
107
  homepage: https://github.com/upfluence/userializer
108
108
  licenses: []
109
109
  metadata: {}
110
- post_install_message:
110
+ post_install_message:
111
111
  rdoc_options: []
112
112
  require_paths:
113
113
  - lib
@@ -122,9 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
124
  requirements: []
125
- rubyforge_project:
126
- rubygems_version: 2.7.3
127
- signing_key:
125
+ rubygems_version: 3.0.3
126
+ signing_key:
128
127
  specification_version: 4
129
128
  summary: Write a short summary, because RubyGems requires one.
130
129
  test_files: []
data/Gemfile.lock DELETED
@@ -1,50 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- userializer (0.1.2)
5
- activesupport
6
- oj
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- activesupport (5.2.3)
12
- concurrent-ruby (~> 1.0, >= 1.0.2)
13
- i18n (>= 0.7, < 2)
14
- minitest (~> 5.1)
15
- tzinfo (~> 1.1)
16
- concurrent-ruby (1.1.5)
17
- diff-lcs (1.3)
18
- i18n (1.6.0)
19
- concurrent-ruby (~> 1.0)
20
- minitest (5.11.3)
21
- oj (3.7.12)
22
- rake (10.4.2)
23
- rspec (3.8.0)
24
- rspec-core (~> 3.8.0)
25
- rspec-expectations (~> 3.8.0)
26
- rspec-mocks (~> 3.8.0)
27
- rspec-core (3.8.0)
28
- rspec-support (~> 3.8.0)
29
- rspec-expectations (3.8.3)
30
- diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.8.0)
32
- rspec-mocks (3.8.0)
33
- diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.8.0)
35
- rspec-support (3.8.0)
36
- thread_safe (0.3.6)
37
- tzinfo (1.2.5)
38
- thread_safe (~> 0.1)
39
-
40
- PLATFORMS
41
- ruby
42
-
43
- DEPENDENCIES
44
- bundler (~> 1.16)
45
- rake (~> 10.0)
46
- rspec (~> 3.0)
47
- userializer!
48
-
49
- BUNDLED WITH
50
- 1.16.1