porridge 0.1.0 → 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: f513e5f2d179544d20b2e0d181066402edd82f03ed491f7ef0c196516a193192
4
- data.tar.gz: 864b15e6a5049bc21e4734c184b686604fecef2598597d4287ddc70a7bdeb3de
3
+ metadata.gz: 1c767e7225acd4284055aa8379a67e9f8f8527d8946ff4df782e3c377d67bec8
4
+ data.tar.gz: b2bad4d91dc0feb01d990aaa1226345e49ce9baf0900578de6f720d47f86d5c3
5
5
  SHA512:
6
- metadata.gz: b8104a38ee8512d49eab94d6c5014158f5dac26d30a47410cfed46d92c5830c32817bc22be2c7274802de8464596a5c00e722992c826a6765b58c75eab0a3b1b
7
- data.tar.gz: f72647f1966d161194f6e239d2da082481a0a84733a0b8d30b99f893d65ab4475bd4f6c61ecf6a3c522b480a139932f75e0081f6eb6c306e1b53f29c7c1b990e
6
+ metadata.gz: 835d2f5b110f0de84a242416791b49b28847de02cab5324f87ea6ee6e6209236f9fbc9e288e3caa158056f8e060e9ff254bf48a73c3b925366ea4772fe11be78
7
+ data.tar.gz: bee59698bf34b8f763c36f39955ffa15ae60976cd151675bbfe2e049f6eb8ba4205d99db74d069b181c64636584d3dbc5816097ceafb9d9f081e7a6ac084d98a
data/.gitignore CHANGED
@@ -9,3 +9,6 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # built gems
14
+ *.gem
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+
@@ -0,0 +1,5 @@
1
+ #parse("Ruby File Header.rb")
2
+ require 'spec_helper'
3
+
4
+ describe Porridge::${NAME} do
5
+ end
data/.idea/porridge.iml CHANGED
@@ -11,12 +11,16 @@
11
11
  </content>
12
12
  <orderEntry type="inheritedJdk" />
13
13
  <orderEntry type="sourceFolder" forTests="false" />
14
+ <orderEntry type="library" scope="PROVIDED" name="activesupport (v5.2.5, rbenv: 3.0.0) [gem]" level="application" />
14
15
  <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, rbenv: 3.0.0) [gem]" level="application" />
15
16
  <orderEntry type="library" scope="PROVIDED" name="bundler (v2.2.25, rbenv: 3.0.0) [gem]" level="application" />
16
17
  <orderEntry type="library" scope="PROVIDED" name="byebug (v11.1.3, rbenv: 3.0.0) [gem]" level="application" />
17
18
  <orderEntry type="library" scope="PROVIDED" name="codecov (v0.6.0, rbenv: 3.0.0) [gem]" level="application" />
19
+ <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.9, rbenv: 3.0.0) [gem]" level="application" />
18
20
  <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, rbenv: 3.0.0) [gem]" level="application" />
19
21
  <orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, rbenv: 3.0.0) [gem]" level="application" />
22
+ <orderEntry type="library" scope="PROVIDED" name="i18n (v1.8.11, rbenv: 3.0.0) [gem]" level="application" />
23
+ <orderEntry type="library" scope="PROVIDED" name="minitest (v5.15.0, rbenv: 3.0.0) [gem]" level="application" />
20
24
  <orderEntry type="library" scope="PROVIDED" name="parallel (v1.21.0, rbenv: 3.0.0) [gem]" level="application" />
21
25
  <orderEntry type="library" scope="PROVIDED" name="parser (v3.1.0.0, rbenv: 3.0.0) [gem]" level="application" />
22
26
  <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.0.0, rbenv: 3.0.0) [gem]" level="application" />
@@ -36,6 +40,8 @@
36
40
  <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.21.2, rbenv: 3.0.0) [gem]" level="application" />
37
41
  <orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.12.3, rbenv: 3.0.0) [gem]" level="application" />
38
42
  <orderEntry type="library" scope="PROVIDED" name="simplecov_json_formatter (v0.1.3, rbenv: 3.0.0) [gem]" level="application" />
43
+ <orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, rbenv: 3.0.0) [gem]" level="application" />
44
+ <orderEntry type="library" scope="PROVIDED" name="tzinfo (v1.2.9, rbenv: 3.0.0) [gem]" level="application" />
39
45
  <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.1.0, rbenv: 3.0.0) [gem]" level="application" />
40
46
  </component>
41
47
  <component name="RakeTasksCache">
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --private --protected lib/**/*.rb - README.md
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2022-01-19
4
+
5
+ This is the initial functional release of the gem. Extractors, serializers, fields, field policies, and an elegant DSL over top were implemented added.
6
+
3
7
  ## [0.1.0] - 2022-01-16
4
8
 
5
9
  - Initial release. This version of the gem has no functionality whatsoever and is intended solely as a deployment test.
data/Gemfile.lock CHANGED
@@ -1,17 +1,27 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- porridge (0.1.0)
4
+ porridge (0.2.0)
5
+ activesupport (~> 5.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ activesupport (5.2.5)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
9
15
  ast (2.4.2)
10
16
  byebug (11.1.3)
11
17
  codecov (0.6.0)
12
18
  simplecov (>= 0.15, < 0.22)
19
+ concurrent-ruby (1.1.9)
13
20
  diff-lcs (1.5.0)
14
21
  docile (1.4.0)
22
+ i18n (1.8.11)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.15.0)
15
25
  parallel (1.21.0)
16
26
  parser (3.1.0.0)
17
27
  ast (~> 2.4.1)
@@ -54,6 +64,9 @@ GEM
54
64
  simplecov_json_formatter (~> 0.1)
55
65
  simplecov-html (0.12.3)
56
66
  simplecov_json_formatter (0.1.3)
67
+ thread_safe (0.3.6)
68
+ tzinfo (1.2.9)
69
+ thread_safe (~> 0.1)
57
70
  unicode-display_width (2.1.0)
58
71
 
59
72
  PLATFORMS
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Porridge
2
-
2
+ [![Gem Version](https://badge.fury.io/rb/porridge.svg)](https://badge.fury.io/rb/porridge)
3
+ ![Build](https://github.com/jacoblockard99/porridge/actions/workflows/build.yml/badge.svg)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/9c3a8a230097bac612e3/maintainability)](https://codeclimate.com/github/jacoblockard99/porridge/maintainability)
4
5
  [![codecov](https://codecov.io/gh/jacoblockard99/porridge/branch/master/graph/badge.svg?token=V9GxyepasN)](https://codecov.io/gh/jacoblockard99/porridge)
5
6
 
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {ArraySerializer} is a serializer that wraps another serializer, calling it for every element of the input array,
5
+ # if an array was given, or simply passing it the input if not.
6
+ class ArraySerializer < Serializer
7
+ # Creates a new instance of {ArraySerializer} with the given base serializer.
8
+ # @param base [Serializer, #call] the base serializer to call for any input, or all elements of that input if the
9
+ # input is an array.
10
+ # @raise [InvalidSerializerError] if the given base serializer is not a valid serializer.
11
+ def initialize(base)
12
+ Serializer.ensure_valid!(base)
13
+ @base = base
14
+ super()
15
+ end
16
+
17
+ # Serializes the given object, which may be an array, for the given input with the given options. If the object
18
+ # is an array (according to {#array?}), the base serializer {#base} will be called for each element, and an array
19
+ # with each result will be returned. If the object is not an array, will simply delegate to {#base}.
20
+ #
21
+ # The given object and options will be given to the base serializer for every element. Note that the options are
22
+ # *not* cloned or duplicated. Therefore <b>the base serializer must not mutate the options object</b> or else
23
+ # the other invocations will also receive the mutated version.
24
+ #
25
+ # @param object_or_objects [Object, Array<Object>] the object or array of objects for which to transform the input.
26
+ # @param input the object being transformed, typically either a hash or an array.
27
+ # @param options [Hash] a hash of "options," which may be application specific.
28
+ # @return [Object, Array<Object>] the transformed output if the object was not an array, or an array of all
29
+ # transformed outputs if the object was an array.
30
+ def call(object_or_objects, input, options)
31
+ if array?(object_or_objects)
32
+ object_or_objects.map { |object| base.call(object, input, options) }
33
+ else
34
+ base.call(object_or_objects, input, options)
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ # Determines whether the given object is an array for the purposes of this {ArraySerializer} instance. The default
41
+ # implementation simple checks to see if the object implements the +map+ method. You may override this method to
42
+ # change the default behavior, if, for example, you have a non-array that implements +map+.
43
+ # @param object the object to check.
44
+ # @return [Boolean] +true+ if the given object functions like an array; +false+ if otherwise.
45
+ def array?(object)
46
+ object.respond_to? :map
47
+ end
48
+
49
+ private
50
+
51
+ # The base serializer, which will be called for the object, or each object, if an array is given.
52
+ # @return [Serializer, #call]
53
+ attr_reader :base
54
+ end
55
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {ChainSerializer} is a serializer that chains multiple other serializers together by passing the output of the
5
+ # first one as the input of the second, and the output of the second as the input of the third, and so on.
6
+ class ChainSerializer < Serializer
7
+ # Creates a new instance of {ChainSerializer} with the given serializers to chain.
8
+ # @param serializers [Array<Serializer,#call>] the splatted array of serializers to chain.
9
+ # @raise [InvalidSerializerError] if any of the given serializers are not valid serializers.
10
+ def initialize(*serializers)
11
+ super()
12
+ Serializer.ensure_valid!(*serializers)
13
+ @serializers = serializers
14
+ end
15
+
16
+ # Transforms the given input for the given object with the given options by chaining each serializer (contained in
17
+ # {#serializers}). The provided input will be given to the first serializer, whose output will be given to the next
18
+ # serializer, and so on for each serializer.
19
+ #
20
+ # The given object and options will be given to all the provided serializers. Note that the options are *not*
21
+ # cloned or duplicated. Therefore <b>none of the serializers should mutate the options object</b> or else
22
+ # all the other serializers will also receive the mutated version.
23
+ #
24
+ # @param object the object for which to transform the input.
25
+ # @param input the object being transformed, typically either a hash or an array.
26
+ # @param options [Hash] a hash of "options," which may be application specific.
27
+ # @return the transformed output, typically either a hash or an array, as returned from the final chained
28
+ # serializer.
29
+ def call(object, input, options)
30
+ output = input
31
+ serializers.each { |serializer| output = serializer.call(object, output, options) }
32
+ output
33
+ end
34
+
35
+ private
36
+
37
+ # The array of chained serializers.
38
+ # @return [Array<Serializer, #call>]
39
+ attr_reader :serializers
40
+ end
41
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {Error} is the base class for all porridge-related errors. You may thus catch {Error} to catch all porridge-specific
5
+ # errors.
6
+ class Error < StandardError
7
+ end
8
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {Extractor} is the nominal base class for all porridge value extractors.
5
+ #
6
+ # A (value) extractor is an object that is capable of retrieving a value from an object, given a set of "options",
7
+ # which may be application-specific. You are encouraged, but not required, to have your extractors derive from this
8
+ # class. Currently, any object that implements a +#call+ method is a valid extractor.
9
+ class Extractor
10
+ # Determines whether the given object is a valid porridge extractor. Currently, any object that responds to the
11
+ # +#call+ method is valid.
12
+ # @param object the object to check.
13
+ # @return [Boolean] +true+ if the object is a valid extractor; +false+ otherwise.
14
+ def self.valid?(object)
15
+ object.respond_to? :call
16
+ end
17
+
18
+ # Ensures that all the provided objects are valid extractors, raising {InvalidExtractorError} if not.
19
+ # @param objects [Array] the splatted array of objects to validate.
20
+ # @return [Boolean] +true+ if all the objects were valid; raises an error otherwise.
21
+ # @raise [InvalidExtractorError] if any of the provided objects are not valid extractors.
22
+ def self.ensure_valid!(*objects)
23
+ objects.each { |object| raise InvalidExtractorError unless valid?(object) }
24
+ true
25
+ end
26
+
27
+ # Should extract a value from the given object with the given options. Subclasses should override this method.
28
+ # @param object the object from which to retrieve the value.
29
+ # @param options [Hash] a hash of "options," which may be application-specific.
30
+ # @return the extracted value.
31
+ def call(object, options); end
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {Factory} is a class that is capable of instantiating various porridge serializers and extractors. All extractor-
5
+ # creation methods are suffixed with +_extractor+, all serializer-creation methods are suffixed with +_serializer+,
6
+ # and all field serializer-creation methods are suffixed with +_field_serializer+.
7
+ #
8
+ # You may subclass this class if you wish to change its behavior. For example, if you wished to substitute your
9
+ # own "from name" extractor, that accesses a hash value rather than sends a method call, for the default one:
10
+ #
11
+ # class CustomPorridgeFactory < Porridge::Factory
12
+ # def from_name_extractor(name)
13
+ # extractor HashValueExtractor.new(name)
14
+ # end
15
+ # end
16
+ #
17
+ # {#from_name_field_serializer}, {#attribute_extractor}, and {#attribute_field_serializer} would then be automatically
18
+ # updated in the process, along with any other methods that depend on the aforementioned ones.
19
+ #
20
+ # This method is rarely used directly. Typically, it is used in conjunction with a {SerializerDefiner} and/or
21
+ # {SerializerDefinition} instance.
22
+ class Factory
23
+ def extractor(base)
24
+ return nil if base.nil?
25
+
26
+ Extractor.ensure_valid!(base)
27
+ base
28
+ end
29
+
30
+ def from_name_extractor(name)
31
+ extractor SendExtractor.new(name)
32
+ end
33
+
34
+ def custom_extractor(callback)
35
+ extractor callback
36
+ end
37
+
38
+ def association_extractor(serializer:, extractor: nil, extraction_name: nil, callback: nil, &block)
39
+ extractor SerializingExtractor.new(
40
+ extractor || custom_extractor(callback) || custom_extractor(block) || from_name_extractor(extraction_name),
41
+ serializer
42
+ )
43
+ end
44
+ alias belongs_to_extractor association_extractor
45
+ alias has_many_extractor association_extractor
46
+
47
+ def serializer(base)
48
+ return nil if base.nil?
49
+
50
+ Serializer.ensure_valid!(base)
51
+ base
52
+ end
53
+
54
+ def chain_serializer(*bases)
55
+ serializer ChainSerializer.new(*bases)
56
+ end
57
+ alias serializers chain_serializer
58
+
59
+ def for_extracted_serializer(serializer, extractor)
60
+ serializer SerializerForExtracted.new(serializer, extractor)
61
+ end
62
+ alias serializer_for_extracted for_extracted_serializer
63
+
64
+ def field_serializer(name, extractor)
65
+ serializer FieldSerializer.new(name, extractor)
66
+ end
67
+
68
+ def attribute_field_serializer(name, callback = nil, extraction_name: nil, &block)
69
+ extractor = custom_extractor(callback || block) || from_name_extractor(extraction_name || name)
70
+ field_serializer(name, extractor)
71
+ end
72
+
73
+ def association_field_serializer(name, options = {}, &block)
74
+ options[:extraction_name] ||= name
75
+ field_serializer(name, association_extractor(**options, &block))
76
+ end
77
+ alias belongs_to_field_serializer association_field_serializer
78
+ alias has_many_field_serializer association_field_serializer
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {FieldPolicy} is the nominal base class for all field policy classes.
5
+ #
6
+ # A field policy is an object that is capable of determining whether a certain "field" is allowed in a given
7
+ # context. Currently, it is primarily used in {FieldSerializer} as the default method of determining whether a field
8
+ # is valid. You are encouraged, but not required, to have your own custom field policies derive from this class.
9
+ # Currently, any object that implements the +#allowed?+ method is a valid field policy.
10
+ class FieldPolicy
11
+ # Determines whether the given object is a valid porridge field policy. Currently, any object that responds to the
12
+ # +#allowed?+ method is valid.
13
+ # @param object the object to check.
14
+ # @return [Boolean] +true+ if the object is a valid field policy; +false+ otherwise.
15
+ def self.valid?(object)
16
+ object.respond_to? :allowed?
17
+ end
18
+
19
+ # Ensures that all the provided objects are valid field policies, raising {InvalidFieldPolicyError} if not.
20
+ # @param objects [Array] the splatted array of objects to validate.
21
+ # @return [Boolean] +true+ if all the objects were valid; raises an error otherwise.
22
+ # @raise [InvalidFieldPolicyError] if any of the provided objects are not valid field policies.
23
+ def self.ensure_valid!(*objects)
24
+ objects.each { |object| raise InvalidFieldPolicyError unless valid?(object) }
25
+ true
26
+ end
27
+
28
+ # Determiners whether the field with the given name for the given object with the given options is currently
29
+ # allowed.
30
+ # @param _name the name of the field being validated.
31
+ # @param _object the object for which the field being validated is being generated.
32
+ # @param _options [Hash] the options with which the field being validated is being generated.
33
+ # @return [Boolean] +true+ if the indicated field is allowed; +false+ otherwise.
34
+ def allowed?(_name, _object, _options)
35
+ true
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {FieldSerializer} is a serializer that adds a "field" to a hash. It does so by using a predefined "field name" as
5
+ # the key and evaluates an {Extractor} for the object for the value.
6
+ #
7
+ # {FieldSerializer} is the most opinionated piece of the porridge framework. In particular, it adds to a
8
+ # +:field_hierarchy+ array in the options hash to keep track of nested fields. It also requires the use of a
9
+ # {FieldPolicy} object given in the options to determine whether a given field is allowed. Do not be afraid to
10
+ # subclass {FieldSerializer} or even create a new "field serializer" class altogether if you want to substantially
11
+ # change the way fields are implemented.
12
+ class FieldSerializer < Serializer
13
+ # Creates a new instance of {FieldSerializer} with the given field name and value extractor.
14
+ # @param name the name of the field; will be used as the key for the field in the hash.
15
+ # @param extractor [Extractor, #call] the value extractor to use to retrieve a value from the object, which will be
16
+ # used as the value for the field in the hash.
17
+ # @raise [InvalidExtractorError] if the provided extractor is not a valid extractor.
18
+ def initialize(name, extractor)
19
+ @name = name
20
+ @extractor = extractor
21
+ Extractor.ensure_valid!(extractor)
22
+ super()
23
+ end
24
+
25
+ # Serializes the given input hash for the given object with the given options by adding an element to the hash with
26
+ # a key that is equal to the field name ({#name}) and a value extracted from the object using the field extractor
27
+ # ({#extractor}).
28
+ # @param object the object for which to transform the input. The field value will be retrieved from this object
29
+ # using the extractor.
30
+ # @param hash [Hash] the input hash being transformed. A key-value pair will be added for the field.
31
+ # @param options [Hash] a hash of "options," which may be application specific.
32
+ # @option options [FieldPolicy, #allowed?] :field_policy the field policy to use to determine whether the field
33
+ # is currently allowed. This option *must* be provided.
34
+ # @return [Hash] the transformed hash.
35
+ # @raise [InvalidFieldPolicyError] if no field policy was provided or if the field policy was not a valid field
36
+ # policy object.
37
+ def call(object, hash, options)
38
+ if allowed?(object, options)
39
+ options = options.dup
40
+ add_field_to_hierarchy!(options)
41
+ hash_with_field(object, hash, options)
42
+ else
43
+ hash
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # Determines whether the given object/options, along with the current {#name} constitutes a valid field. Currently,
50
+ # this is done by simply delegating to the field policy which should have been provided as an option.
51
+ # @param object the object for which the field being validated is being implemented.
52
+ # @param options [Hash] the options for which the field being validated is being implemented.
53
+ # @return [Boolean] +true+ if the indicated field is valid; +false+ otherwise.
54
+ # @raise [InvalidFieldPolicyError] if no field policy was provided or if the field policy was not a valid field
55
+ # policy object.
56
+ def allowed?(object, options)
57
+ FieldPolicy.ensure_valid!(options[:field_policy])
58
+ options[:field_policy].allowed?(name, object, options.except(:field_policy))
59
+ end
60
+
61
+ # Safely adds the current {#name} to the +:field_hierarchy+ in the given options hash. While the options hash itself
62
+ # is mutated, the field hierarchy array is first duplicated, meaning the options hash must have first been
63
+ # duplicated for this to be safe.
64
+ # @param options_hash [Hash] the options hash to which the field should be added.
65
+ # @return [void]
66
+ def add_field_to_hierarchy!(options_hash)
67
+ options_hash[:field_hierarchy] ||= []
68
+ options_hash[:field_hierarchy] = options_hash[:field_hierarchy].dup
69
+ options_hash[:field_hierarchy] << name
70
+ end
71
+
72
+ # Creates a new hash from the given one with a field for the given object injected.
73
+ # @param object the object for which to inject the field. Will be passed to the extractor.
74
+ # @param hash [Hash] the hash into which to inject the field.
75
+ # @param options [Hash] the options for which to inject the field. Will be passed to the extractor.
76
+ # @return [Hash] the transformed hash.
77
+ def hash_with_field(object, hash, options)
78
+ hash.merge(name => extractor.call(object, options))
79
+ end
80
+
81
+ # The name of the field being serialized by this {FieldSerializer}; used as the key for the field in the hash.
82
+ attr_reader :name
83
+
84
+ # The value extractor to use to retrieve a value from the object for which serialization is occurring.
85
+ # @return [Extractor, #call]
86
+ attr_reader :extractor
87
+ end
88
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {InvalidExtractorError} is the error that is thrown when a non-extractor object is used like an extractor.
5
+ class InvalidExtractorError < Error; end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {InvalidFieldPolicyError} is the error that is thrown when a non-field-policy object is used like a field policy.
5
+ class InvalidFieldPolicyError < Error; end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {InvalidSerializerError} is the error that is thrown when a non-serializer object is used like a serializer.
5
+ class InvalidSerializerError < Error; end
6
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/hash/keys'
5
+
6
+ module Porridge
7
+ # {KeyNormalizingSerializer} is a serializer that wraps another serializer and recursively normalizes the keys of the
8
+ # resulting hash to either strings or symbols.
9
+ class KeyNormalizingSerializer < Serializer
10
+ # Creates a new instance of {KeyNormalizingSerializer} with the given base serializer and key type.
11
+ # @param base [Serializer, #call] the base serializer to wrap. Note that the output of the base serializer *must*
12
+ # be a hash.
13
+ # @param key_type [Symbol] the type that the keys should be normalized to. Both +:string+ and +:symbol+ are
14
+ # supported.
15
+ # @raise [InvalidSerializerError] if the given base serializer is not a valid serializer.
16
+ def initialize(base, key_type: :string)
17
+ Serializer.ensure_valid!(base)
18
+ @base = base
19
+ @key_type = key_type
20
+ super()
21
+ end
22
+
23
+ # Serializes the given input for the given object with the given options by delegating to the base serializer
24
+ # ({#base}) and recursively transforming the keys of the resulting hash to the appropriate type ({#key_type}).
25
+ # Note that the output of the base serializer *must* be a hash.
26
+ # @param object the object for which to transform the input.
27
+ # @param input the object being transformed, typically either a hash or an array.
28
+ # @param options [Hash] a hash of "options," which may be application specific.
29
+ # @return [Hash] the hash returned from the base serializer, normalized.
30
+ def call(object, input, options)
31
+ normalize_keys(base.call(object, input, options))
32
+ end
33
+
34
+ private
35
+
36
+ # Normalizes the keys of the given hash according to the {#key_type}. Uses ActiveSupport methods to accomplish this.
37
+ # @param hash [Hash] the hash to normalize.
38
+ # @return [Hash] the normalized hash.
39
+ def normalize_keys(hash)
40
+ key_type == :symbol ? hash.deep_symbolize_keys : hash.deep_stringify_keys
41
+ end
42
+
43
+ # The base serializer whose output hash will be normalized
44
+ # @return [Serializer, #call]
45
+ attr_reader :base
46
+
47
+ # The key type that the hash should be normalized to.
48
+ # @return [Symbol] either +:string+ or +:symbol+.
49
+ attr_reader :key_type
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {SendExtractor} is an extractor that retrieves a value from an object by simply calling a predefined method on it.
5
+ class SendExtractor < Extractor
6
+ # Creates a new instance of {SendExtractor} with the given method name.
7
+ # @param method_name [String, Symbol] the name of the method to call when extracting the value.
8
+ def initialize(method_name)
9
+ @method_name = method_name.to_s
10
+ super()
11
+ end
12
+
13
+ # Extracts the value from the given object by sending the method name ({#method_name}) to it.
14
+ # @param object the object from which to retrieve the value.
15
+ # @param _options [Hash] a hash of "options," which may be application-specific. These options are ignored.
16
+ # @return the extracted value, as returned from the sent method.
17
+ def call(object, _options)
18
+ object.respond_to?(method_name) ? object.send(method_name) : nil
19
+ end
20
+
21
+ private
22
+
23
+ # The name of the method to call when extracting the value.
24
+ # @return [String]
25
+ attr_reader :method_name
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {Serializer} is the nominal base class for all porridge serializers.
5
+ #
6
+ # A serializer is an object that arbitrarily transforms a given input for a given object with a given set of options.
7
+ # The input may be anything, but is typically either a hash or an array. In Rails applications, the object is often
8
+ # an ActiveRecord model. The options may be application-specific.
9
+ #
10
+ # Serializers are the heart and soul of the porridge gem and are typically layered with composition into a final
11
+ # serializer that is used to actually serialize an object into (typically) a hash or array. Thus, a serializer often
12
+ # simply wraps another serializer, transforming the object or options in some way.
13
+ #
14
+ # You are encouraged, but not required, to have all your serializers derive from this class. Currently, any object
15
+ # that implements the +#call+ method is a valid serializer.
16
+ class Serializer
17
+ # Determines whether the given object is a valid porridge serializer. Currently, any object that responds to the
18
+ # '#call' method is valid.
19
+ # @param object the object to check.
20
+ # @return [Boolean] +true+ if the object is a valid serializer; +false+ otherwise.
21
+ def self.valid?(object)
22
+ object.respond_to? :call
23
+ end
24
+
25
+ # Ensures that all the provided objects are valid serializers, raising {InvalidSerializerError} if not.
26
+ # @param objects [Array] the splatted array of objects to validate.
27
+ # @return [Boolean] +true+ if all the objects were valid; raises an error otherwise.
28
+ # @raise [InvalidSerializerError] if any of the provided objects are not valid serializers.
29
+ def self.ensure_valid!(*objects)
30
+ objects.each { |object| raise InvalidSerializerError unless valid?(object) }
31
+ true
32
+ end
33
+
34
+ # Should transforms the given input for the given object with the given options and return the desired output.
35
+ # @param _object the object for which to transform the input.
36
+ # @param input the object being transformed, typically either a hash or an array.
37
+ # @param _options [Hash] a hash of "options," which may be application specific.
38
+ # @return the transformed output, typically either a hash or an array.
39
+ def call(_object, input, _options)
40
+ input
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {SerializerDefiner} is a class that wraps a {Factory} and allows serializes to be easily defined with an elegant
5
+ # DSL.
6
+ class SerializerDefiner
7
+ FACTORY_PREFIX = 'create_'
8
+ SERIALIZER_SUFFIX = '_serializer'
9
+ FIELD_SERIALIZER_SUFFIX = '_field_serializer'
10
+
11
+ delegate :call, to: :defined_serializer
12
+
13
+ def initialize(factory = Factory.new)
14
+ @factory = factory
15
+ @serializers = []
16
+ end
17
+
18
+ def method_missing(method_name, *args, &block)
19
+ method_name = method_name.to_s
20
+ return factory.send(method_name.delete_prefix(FACTORY_PREFIX), *args, &block) if create_method? method_name
21
+
22
+ if serializer_method? method_name
23
+ return add_serializer(factory.send(method_name + SERIALIZER_SUFFIX, *args, &block))
24
+ end
25
+
26
+ if field_serializer_method? method_name
27
+ return add_serializer(factory.send(method_name + FIELD_SERIALIZER_SUFFIX, *args, &block))
28
+ end
29
+
30
+ super(method_name.to_sym, *args, &block)
31
+ end
32
+
33
+ def respond_to_missing?(method_name, include_private = false)
34
+ method_name = method_name.to_s
35
+ super(method_name.to_sym, include_private) ||
36
+ create_method?(method_name) ||
37
+ serializer_method?(method_name) ||
38
+ field_serializer_method?(method_name)
39
+ end
40
+
41
+ def defined_serializer
42
+ factory.serializers(*added_serializers)
43
+ end
44
+
45
+ def added_serializers
46
+ @serializers
47
+ end
48
+
49
+ def serializer(...)
50
+ add_serializer(factory.serializer(...))
51
+ end
52
+
53
+ private
54
+
55
+ def create_method?(method_name)
56
+ method_name.start_with?(FACTORY_PREFIX) && factory.respond_to?(method_name.delete_prefix(FACTORY_PREFIX))
57
+ end
58
+
59
+ def serializer_method?(method_name)
60
+ factory.respond_to?(method_name + SERIALIZER_SUFFIX)
61
+ end
62
+
63
+ def field_serializer_method?(method_name)
64
+ factory.respond_to?(method_name + FIELD_SERIALIZER_SUFFIX)
65
+ end
66
+
67
+ def add_serializer(serializer)
68
+ @serializers << serializer
69
+ end
70
+
71
+ attr_reader :factory
72
+ end
73
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {SerializerDefinition} is a class that allows serializers to be defined as with a {SerializerDefiner}, but within
5
+ # a class. Simply subclass this class and use the same DSL within it.
6
+ class SerializerDefinition
7
+ class << self
8
+ attr_writer :definer
9
+
10
+ delegate_missing_to :definer
11
+
12
+ def inherited(subclass)
13
+ super
14
+ definer.added_serializers.each { |serializer| subclass.definer.serializer(serializer) }
15
+ end
16
+
17
+ def definer
18
+ @definer ||= create_definer
19
+ end
20
+
21
+ def create_definer
22
+ SerializerDefiner.new
23
+ end
24
+
25
+ def reset!
26
+ @definer = nil
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {SerializerForExtracted} is a serializer that wraps another serializer and passes it an object that is extracted
5
+ # from the initial object using an {Extractor}.
6
+ class SerializerForExtracted < Serializer
7
+ # Creates a new instance of {SerializerForExtracted} with the given base serializer and extractor.
8
+ # @param base [Serializer, #call] the base serializer to wrap.
9
+ # @param extractor [Extractor, #call] the extractor to use to extract a value from the object before passing it
10
+ # to the base serializer.
11
+ # @raise [InvalidSerializerError] if the provided base serializer is not a valid serializer.
12
+ # @raise [InvalidExtractorError] if the provided extractor is not a valid extractor.
13
+ def initialize(base, extractor)
14
+ Serializer.ensure_valid!(base)
15
+ Extractor.ensure_valid!(extractor)
16
+ @base = base
17
+ @extractor = extractor
18
+ super()
19
+ end
20
+
21
+ # Serializes the given input for the given object with the given options by first extracted a value from the given
22
+ # object, then passing that value, along with the given input and options, to the base serializer ({#base}).
23
+ # @param object the object for which to transform the input. A value will be extracted and that value will be passed
24
+ # to the base serializer.
25
+ # @param input the object being transformed, typically either a hash or an array.
26
+ # @param options [Hash] a hash of "options," which may be application specific.
27
+ # @return the transformed output, typically either a hash or an array, as returned from the base serializer.
28
+ def call(object, input, options)
29
+ extracted_value = extractor.call(object, options)
30
+ base.call(extracted_value, input, options)
31
+ end
32
+
33
+ private
34
+
35
+ # The base serializer to wrap.
36
+ # @return [Serializer, #call]
37
+ attr_reader :base
38
+
39
+ # The extractor to use to extract a value from the object before passing it to the base serializer.
40
+ # @return [Extractor, #call]
41
+ attr_reader :extractor
42
+ end
43
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/string/inflections'
5
+
6
+ module Porridge
7
+ # {SerializerWithRoot} is a serializer that wraps another serializer and adds a "root key" to the resulting hash.
8
+ class SerializerWithRoot < Serializer
9
+ # Creates a new instance of {SerializerWithRoot} with the given base serializer and, optionally, root key.
10
+ # @param base [Serializer, #call] the base serializer to wrap.
11
+ # @param root_key the "root" key to inject into the resulting hash. If +nil+, which is the default, the root key
12
+ # will be inferred from the object.
13
+ # @raise [InvalidSerializerError] if the provided base serializer is not a valid serializer.
14
+ def initialize(base, root_key: nil)
15
+ Serializer.ensure_valid!(base)
16
+ @base = base
17
+ @root_key = root_key
18
+ super()
19
+ end
20
+
21
+ # Serializes the given input for the given object with the given options by delegating to the base serializer
22
+ # ({#base}) and adding a root key to the resulting hash. Note that the output of the base serializer *must* be a
23
+ # hash.
24
+ #
25
+ # If the root key was not set manually, it will be inferred from the "underscored" class name of the object. If the
26
+ # object is an array (according to {#array?}), then the class name will be derived from the first object in the
27
+ # array, and will be pluralized. Be aware that retrieving the first element of the "array" may cause an SQL query to
28
+ # be performed if the "array" is a Rails relation.
29
+ #
30
+ # Note that an inferred root key is always a string. You may wish to use a {KeyNormalizingSerializer} if symbol
31
+ # keys are desired.
32
+ #
33
+ # @param object the object for which to transform the input. If no root key was set manually, it will be inferred
34
+ # from the object's class.
35
+ # @param input the object being transformed, typically either a hash or an array.
36
+ # @param options [Hash] a hash of "options," which may be application specific.
37
+ # @return [Hash] the hash returned from the base serializer, injected with a root key.
38
+ def call(object, input, options)
39
+ { evaluate_root_key(object) => base.call(object, input, options) }
40
+ end
41
+
42
+ protected
43
+
44
+ # Determines whether the given object functions like an array for the purposes of this {SerializerWithRoot}. The
45
+ # default implementation checks to see whether the object implements both +#map+ and +#first+. You may override the
46
+ # default behavior by overriding this method. Note that if you override {ArraySerializer#like_array?} you will
47
+ # likely wish to override this method as well.
48
+ # @param object the object to check.
49
+ # @return [Boolean] +true+ if the given object is like an array; +false+ otherwise.
50
+ def array?(object)
51
+ object.respond_to?(:map) && object.respond_to?(:first)
52
+ end
53
+
54
+ private
55
+
56
+ # Gets a root key for the given object by either returning {#root_key}, or returning a singular/plural version
57
+ # of the {#base_root_key}, depending on whether the object is an array.
58
+ # @param object the object for which to get a root key.
59
+ # @return the resolved string root key.
60
+ def evaluate_root_key(object)
61
+ return root_key if root_key
62
+
63
+ array?(object) ? base_root_key(object).pluralize : base_root_key(object).singularize
64
+ end
65
+
66
+ # Gets the inferred base root key, without singularization or pluralization for the given object.
67
+ # @param object the object for which to get the base root key.
68
+ # @return [String] the resolved base root key.
69
+ def base_root_key(object)
70
+ representative_sample(object).class.name.underscore.to_s
71
+ end
72
+
73
+ # Gets a "representative" sample from the given object. In practice, this means either returning the object itself,
74
+ # or, if the object is an array-like structure, returning the first element.
75
+ def representative_sample(object)
76
+ array?(object) ? object.first : object
77
+ end
78
+
79
+ # The base serializer to wrap.
80
+ # @return [Serializer, #call]
81
+ attr_reader :base
82
+
83
+ # The explicit root key; if +nil+, will be inferred.
84
+ attr_reader :root_key
85
+ end
86
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {SerializingExtractor} is an extractor that wraps another extractor and serializes for its output using a provided
5
+ # serializer. Note that {SerializingExtractor} passes the output of the base extractor as the *object* for the
6
+ # serializer, not the input.
7
+ class SerializingExtractor < Extractor
8
+ # Creates a new instance of {SerializingExtractor} with the given base extractor and serializer.
9
+ # @param base [Extractor, #call] the extractor to wrap, whose output will be serialized for before being returned.
10
+ # @param serializer [Serializer, #call] the serializer to use to transform the output of the extractor.
11
+ # @raise [InvalidExtractorError] if the provided base extractor was not a valid extractor.
12
+ # @raise [InvalidSerializerError] if the provided serializer was not a valid serializer.
13
+ def initialize(base, serializer)
14
+ Extractor.ensure_valid!(base)
15
+ Serializer.ensure_valid!(serializer)
16
+ @base = base
17
+ @serializer = serializer
18
+ super()
19
+ end
20
+
21
+ # Extracts a value from the given object for the given options by:
22
+ # 1. Using the base extractor ({#extractor}) to extract a value from the object with the given options; and
23
+ # 2. Passing that value as the object to {#serializer#call}. A blank hash is given as the input, and the
24
+ # given options are passed along.
25
+ # @param object the object from which to retrieve the value.
26
+ # @param options [Hash] a hash of "options," which may be application-specific.
27
+ # @return the extracted value.
28
+ def call(object, options)
29
+ serializer.call(base.call(object, options), {}, options)
30
+ end
31
+
32
+ private
33
+
34
+ # The base extractor.
35
+ # @return [Extractor, #call]
36
+ attr_reader :base
37
+
38
+ # The serializer to use to serialize for the output of the base extractor.
39
+ # @return [Serializer, #call]
40
+ attr_reader :serializer
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Porridge
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Porridge
4
+ # {WhitelistFieldPolicy} is a field policy that uses a nested whitelist of field names to determine which fields are
5
+ # valid.
6
+ class WhitelistFieldPolicy < FieldPolicy
7
+ # Creates a new instance of {WhitelistFieldPolicy} with the given whitelist.
8
+ # @param whitelist [Hash] the nested whitelist hash of allowed field names.
9
+ def initialize(whitelist)
10
+ @whitelist = whitelist
11
+ super()
12
+ end
13
+
14
+ # Determiners whether the field with the given name with the given options is currently allowed by checking the
15
+ # field hierarchy, which must be contained in +options[:field_hierarchy] against the whitelist.
16
+ # @param name the name of the field being validated.
17
+ # @param _object the object for which the field being validated is being generated.
18
+ # @param options [Hash] the options with which the field being validated is being generated.
19
+ # @return [Boolean] +true+ if the indicated field is allowed; +false+ otherwise.
20
+ def allowed?(name, _object, options)
21
+ field_hierarchy = options[:field_hierarchy] || []
22
+ _allowed?([*field_hierarchy, name], whitelist)
23
+ end
24
+
25
+ protected
26
+
27
+ # Determines whether the given object functions as a hash for the purposes of this {WhitelistFieldPolicy} instance.
28
+ # You may override this method if desired, but hashes must at least respond to +#[]+.
29
+ # @param input the input object to check.
30
+ # @return [Boolean] +true+ if the given object is like a hash; +false+ otherwise.
31
+ def hash?(input)
32
+ input.is_a? Hash
33
+ end
34
+
35
+ private
36
+
37
+ # @overload _allowed?(field_hierarchy, whitelist)
38
+ # Recursively traverses the given field hierarchy and determines whether the field indicated by the hierarchy is
39
+ # allowed for the given whitelist.
40
+ # @param field_hierarchy [Array] the field hierarchy to validate.
41
+ # @param whitelist [Hash] the nested whitelist hash of field names.
42
+ # @overload _allowed?(field_hierarchy, whitelist, level)
43
+ # Recursively traverses the given field hierarchy and determines whether the field indicated by the hierarchy is
44
+ # allowed for the given whitelist, starting from the specified level.
45
+ # @param field_hierarchy [Array] the field hierarchy to validate.
46
+ # @param whitelist [Hash] the nested whitelist hash of field names.
47
+ # @param level [Integer] the current level of the hierarchy being checked.
48
+ def _allowed?(field_hierarchy, whitelist, level = 0)
49
+ # If the level is equal to the field hierarchy length, then we've reached the end. Immediately return the
50
+ # truthiness of whitelist, which is now equal to the final resolved value referenced by the field hierarchy.
51
+ return !!whitelist if level >= field_hierarchy.count
52
+
53
+ # If the current whitelist is not a hash, then the field hierarchy is deeper than the whitelist.
54
+ # As an example, take this whitelist:
55
+ # { users: true }
56
+ # And this field hierarchy:
57
+ # [:user, :id]
58
+ # One interpretation of this is that since 'users' is true, all fields should be allowed. We take the opposite
59
+ # approach and say that no attributes have been explicitly defined.
60
+ # Therefore immediately return false.
61
+ return false unless hash?(whitelist)
62
+
63
+ _allowed?(field_hierarchy, whitelist[field_hierarchy[level]], level + 1)
64
+ end
65
+
66
+ # The nested whitelist hash of field names.
67
+ # @return [Hash]
68
+ attr_reader :whitelist
69
+ end
70
+ end
data/lib/porridge.rb CHANGED
@@ -1,8 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'porridge/version'
4
+ require_relative 'porridge/extractor'
5
+ require_relative 'porridge/send_extractor'
6
+ require_relative 'porridge/serializer'
7
+ require_relative 'porridge/chain_serializer'
8
+ require_relative 'porridge/array_serializer'
9
+ require_relative 'porridge/key_normalizing_serializer'
10
+ require_relative 'porridge/serializer_for_extracted'
11
+ require_relative 'porridge/serializer_with_root'
12
+ require_relative 'porridge/error'
13
+ require_relative 'porridge/invalid_serializer_error'
14
+ require_relative 'porridge/invalid_extractor_error'
15
+ require_relative 'porridge/invalid_field_policy_error'
16
+ require_relative 'porridge/field_policy'
17
+ require_relative 'porridge/field_serializer'
18
+ require_relative 'porridge/serializing_extractor'
19
+ require_relative 'porridge/whitelist_field_policy'
20
+ require_relative 'porridge/factory'
21
+ require_relative 'porridge/serializer_definer'
22
+ require_relative 'porridge/serializer_definition'
4
23
 
5
- module Porridge
6
- class Error < StandardError; end
7
- # Your code goes here...
8
- end
24
+ # {Porridge} is the root namespace for all classes in the +porridge+ gem.
25
+ module Porridge; end
data/porridge.gemspec CHANGED
@@ -42,6 +42,8 @@ Gem::Specification.new do |spec|
42
42
  # Uncomment to register a new dependency of your gem
43
43
  # spec.add_dependency "example-gem", "~> 1.0"
44
44
 
45
+ spec.add_dependency 'activesupport', '~> 5.0'
46
+
45
47
  # For more information and examples about making a new gem, checkout our
46
48
  # guide at: https://bundler.io/guides/creating_gem.html
47
49
  spec.metadata['rubygems_mfa_required'] = 'true'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: porridge
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
  - Jacob
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-16 00:00:00.000000000 Z
11
+ date: 2022-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.21'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activesupport
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '5.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '5.0'
111
125
  description: |
112
126
  `porridge` is a plain Ruby gem that takes a flexible, object-oriented approach to serialization. `porridge`
113
127
  transforms objects into ruby hashes and arrays, which can then be serialized with other libraries.
@@ -120,12 +134,15 @@ files:
120
134
  - ".github/workflows/build.yml"
121
135
  - ".gitignore"
122
136
  - ".idea/.gitignore"
137
+ - ".idea/fileTemplates/includes/Ruby File Header.rb"
138
+ - ".idea/fileTemplates/internal/RSpec.rb"
123
139
  - ".idea/misc.xml"
124
140
  - ".idea/modules.xml"
125
141
  - ".idea/porridge.iml"
126
142
  - ".idea/vcs.xml"
127
143
  - ".rspec"
128
144
  - ".rubocop.yml"
145
+ - ".yardopts"
129
146
  - CHANGELOG.md
130
147
  - Gemfile
131
148
  - Gemfile.lock
@@ -135,7 +152,26 @@ files:
135
152
  - bin/console
136
153
  - bin/setup
137
154
  - lib/porridge.rb
155
+ - lib/porridge/array_serializer.rb
156
+ - lib/porridge/chain_serializer.rb
157
+ - lib/porridge/error.rb
158
+ - lib/porridge/extractor.rb
159
+ - lib/porridge/factory.rb
160
+ - lib/porridge/field_policy.rb
161
+ - lib/porridge/field_serializer.rb
162
+ - lib/porridge/invalid_extractor_error.rb
163
+ - lib/porridge/invalid_field_policy_error.rb
164
+ - lib/porridge/invalid_serializer_error.rb
165
+ - lib/porridge/key_normalizing_serializer.rb
166
+ - lib/porridge/send_extractor.rb
167
+ - lib/porridge/serializer.rb
168
+ - lib/porridge/serializer_definer.rb
169
+ - lib/porridge/serializer_definition.rb
170
+ - lib/porridge/serializer_for_extracted.rb
171
+ - lib/porridge/serializer_with_root.rb
172
+ - lib/porridge/serializing_extractor.rb
138
173
  - lib/porridge/version.rb
174
+ - lib/porridge/whitelist_field_policy.rb
139
175
  - porridge.gemspec
140
176
  homepage: https://github.com/jacoblockard99/porridge
141
177
  licenses: