minato-rails-serializer 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e17119448417a63faa76e8a5096b0632d588ac6d08ff854535f1e8f2dded7135
4
+ data.tar.gz: cfc22d1e98188787a38f9db34c3538ba4b5dc38c3edc923225d2dde6c08df25b
5
+ SHA512:
6
+ metadata.gz: 0c8bd8c28eb59cfb4b875865836c2c496f1656aa110d3618ebc77fb1023ff2e1f0b28f62463fac35524e1e73deb5f5de623e43e861b384e3813ee27ca2548711
7
+ data.tar.gz: 26b23b31e4d1df071b6b316ee604b66253ca6b1cab8f59b4ff58c8b089a880821ac4fcd01d12ff650219d74a6f2d5d41335a7856b5d318910417172972784fdf
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ DisplayCopNames: true
6
+ DisplayStyleGuide: true
7
+ ExtraDetails: false
8
+ NewCops: enable
9
+ TargetRubyVersion: 3.1
10
+
11
+ Metrics/BlockLength:
12
+ IgnoredMethods: ['describe']
13
+ Max: 50
14
+
15
+ RSpec/EmptyExampleGroup:
16
+ Enabled: false
17
+
18
+ RSpec/ImplicitExpect:
19
+ Enabled: false
20
+
21
+ Style/Documentation:
22
+ Enabled: false
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # Minato Rails Serializer
2
+
3
+ A lightweight and high-performance JSON serialization layer for Rails, built on top of the Panko Serializer.
4
+
5
+ Minato::Rails::Serializer seamlessly integrates with Rails' render json:, offering automatic serializer detection, a simple DSL for root keys, and the flexibility to override behavior directly in the controller, all with a focus on convention over configuration.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```Ruby
11
+ gem 'minato-rails-serializer'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```Bash
17
+ $ bundle install
18
+ ```
19
+
20
+ ## Usage
21
+ The gem is designed to be as intuitive as possible, integrating with the standard Rails workflow.
22
+
23
+ ### 1. Creating a Serializer
24
+ Create your serializers by inheriting from the `Minato::Rails::Serializer::Base` base class.
25
+
26
+ ```Ruby
27
+ # app/serializers/post_serializer.rb
28
+ class PostSerializer < Minato::Rails::Serializer::Base
29
+ attributes :id, :title, :body
30
+ end
31
+ ```
32
+
33
+ ### 2. Basic Rendering in Controllers
34
+
35
+ Thanks to the included Railtie, the gem modifies render json: to automatically detect and use the corresponding serializer for your model.
36
+
37
+ In your controller, you can simply render the object:
38
+
39
+ ```Ruby
40
+ class PostsController < ApplicationController
41
+ def show
42
+ @post = Post.find(params[:id])
43
+ # The gem will automatically look for `PostSerializer`
44
+ render json: @post
45
+ end
46
+
47
+ def index
48
+ @posts = Post.all
49
+ # It also works for collections
50
+ render json: @posts
51
+ end
52
+ end
53
+ ```
54
+
55
+ ### 3. Using the root DSL
56
+ You can define a default root key directly in your serializer, which will be used to wrap the JSON output.
57
+
58
+ ```Ruby
59
+ # app/serializers/user_serializer.rb
60
+ class UserSerializer < Minato::Rails::Serializer::Base
61
+ # Sets 'user' as the root for single objects and infers 'users' for collections
62
+ root :user
63
+
64
+ attributes :id, :name, :email
65
+ end
66
+ ```
67
+
68
+ Now, when rendering:
69
+
70
+ ```Ruby
71
+ # In a `show` action
72
+ render json: @user
73
+ # Output:
74
+ # {
75
+ # "user": { "id": 1, "name": "...", "email": "..." }
76
+ # }
77
+
78
+ # In an `index` action
79
+ render json: @users
80
+ # Output:
81
+ # {
82
+ # "users": [
83
+ # { "id": 1, "name": "...", "email": "..." },
84
+ # { "id": 2, "name": "...", "email": "..." }
85
+ # ]
86
+ # }
87
+ ```
88
+
89
+ You can also specify a custom plural name:
90
+
91
+ ```Ruby
92
+ class ProductSerializer < Minato::Rails::Serializer::Base
93
+ root :product, :products_list
94
+
95
+ # ...
96
+ end
97
+ ```
98
+
99
+ ### 4. Overriding Options in the render Call
100
+
101
+ The gem offers full flexibility, allowing you to override the default behavior directly in the render call.
102
+
103
+ Overriding the Serializer
104
+ Use the :serializer option to force the use of a different serializer than the inferred default.
105
+
106
+ ```Ruby
107
+ # Assuming an AdminUserSerializer exists with more attributes
108
+ def show
109
+ @user = User.find(params[:id])
110
+ render json: @user, serializer: AdminUserSerializer
111
+ end
112
+ ```
113
+
114
+ Overriding the Root Key (:root)
115
+ Use the :root option to set a custom root key for a specific request, overriding the DSL.
116
+
117
+ ```Ruby
118
+ def profile
119
+ @user = current_user
120
+ # The output will be wrapped in "profile", ignoring the `root :user` from the DSL.
121
+ render json: @user, root: :profile
122
+ end
123
+ ```
124
+
125
+ Disabling the Root Key
126
+ To remove the root key (even if defined in the DSL), pass root: false.
127
+
128
+ ```Ruby
129
+ def show
130
+ @user = User.find(params[:id])
131
+ # Returns the JSON object without any root key
132
+ render json: @user, root: false
133
+ end
134
+ ```
135
+
136
+ ## Development
137
+
138
+ This project uses Docker and Docker Compose to ensure a consistent development and testing environment.
139
+
140
+ **Run the test suite:**
141
+
142
+ ```bash
143
+ $ docker compose run run minato-rails-serializer
144
+ ```
145
+
146
+ The entrypoint script defined in `compose.yml` will automatically create, migrate, and prepare the test database for the dummy application before the RSpec suite is executed. Manual database setup is no longer required.
147
+
148
+ To open an interactive shell inside the container for debugging or running other commands (like `rails c` from the dummy app), use:
149
+
150
+ ```bash
151
+ $ docker compose run minato-rails-serializer sh
152
+ ```
153
+
154
+ ## License
155
+ The gem is available as open source under the terms of the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'bundler/gem_tasks'
data/compose.yml ADDED
@@ -0,0 +1,12 @@
1
+ services:
2
+ minato-rails-serializer:
3
+ image: us-east1-docker.pkg.dev/minato-cloud/images/ruby-minato:1.0.0-dev
4
+ working_dir: /usr/src/app
5
+ volumes:
6
+ - .:/usr/src/app:cached
7
+ - bundle:/home/rails/.bundle
8
+ entrypoint: ./entrypoint.sh
9
+ command: rspec
10
+
11
+ volumes:
12
+ bundle:
data/entrypoint.sh ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ bundle install
6
+
7
+ exec "$@"
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'panko_serializer'
4
+
5
+ module Minato
6
+ module Rails
7
+ module Serializer
8
+ class Base < Panko::Serializer
9
+ class << self
10
+ def render(data, options = {})
11
+ rendered = render_data(data, options)
12
+ root = get_root(data, options)
13
+ return { root => rendered } if root.present?
14
+
15
+ rendered
16
+ end
17
+
18
+ protected
19
+
20
+ def root(name, plural_name = nil)
21
+ plural_root_name = plural_name || (name.pluralize if name.is_a?(String))
22
+ define_singleton_method(:root_name) { name }
23
+ define_singleton_method(:plural_root_name) { plural_root_name }
24
+ end
25
+
26
+ private
27
+
28
+ attr_accessor :root_name, :plural_root_name
29
+
30
+ def render_data(data, options = {})
31
+ if data.is_a?(Array) || data.is_a?(ActiveRecord::Relation) || data.respond_to?(:each)
32
+ return render_array(data, options)
33
+ end
34
+
35
+ render_object(data, options)
36
+ end
37
+
38
+ def render_array(data, options = {})
39
+ return [] if data.blank?
40
+
41
+ Panko::ArraySerializer.new(data, options.merge(each_serializer: self)).to_a
42
+ end
43
+
44
+ def render_object(data, options = {})
45
+ return nil if data.nil?
46
+
47
+ new(options[:options] || {}).serialize(data)
48
+ end
49
+
50
+ def get_root(data, options = {}) # rubocop:disable Metrics/CyclomaticComplexity
51
+ return nil if options[:root] == false
52
+ return options[:root] if options.key?(:root)
53
+ return nil if root_name.nil?
54
+
55
+ plural = plural_root_name || root_name&.to_s&.pluralize
56
+ data.is_a?(Enumerable) ? plural : root_name
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/minato/rails/serializer/railtie.rb
4
+
5
+ require 'rails/railtie'
6
+
7
+ module Minato
8
+ module Rails
9
+ module Serializer
10
+ class Railtie < ::Rails::Railtie
11
+ initializer 'minato.rails.serializer.add_json_renderer' do
12
+ ActionController::Renderers.remove(:json)
13
+
14
+ find_serializer_for_model = lambda do |obj|
15
+ klass = obj.is_a?(Enumerable) ? obj.first&.class : obj.class
16
+ return nil unless klass && defined?(ActiveRecord::Base) && klass < ActiveRecord::Base
17
+
18
+ "#{klass.name}Serializer".safe_constantize
19
+ end
20
+
21
+ ActionController::Renderers.add :json do |object, options|
22
+ self.content_type ||= Mime[:json]
23
+ serializer = options[:serializer].presence || find_serializer_for_model.call(object)
24
+
25
+ if serializer
26
+ serializer_options = options.slice(:root)
27
+ serializer.render(object, serializer_options).to_json
28
+ else
29
+ object = { options[:root] => object } if options[:root].present?
30
+ object.to_json(options.except(:root))
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minato
4
+ module Rails
5
+ module Serializer
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'serializer/version'
4
+ require_relative 'serializer/base'
5
+ require_relative 'serializer/railtie'
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/minato/rails/serializer/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'minato-rails-serializer'
7
+ spec.version = Minato::Rails::Serializer::VERSION
8
+ spec.authors = ['Ferreri']
9
+ spec.email = ['contato@ferreri.co']
10
+
11
+ spec.summary = 'Serializer for Minato Rails Apps.'
12
+ spec.homepage = 'https://gitlab.com/ferreri/minato/minato-rails-serializer'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = '>= 3.1.0'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/ferreri/minato/minato-rails-serializer'
18
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/ferreri/minato/minato-rails-serializer/-/blob/main/CHANGELOG.md?ref_type=heads'
19
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) ||
26
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
27
+ end
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'panko_serializer', '>= 0.8.3'
34
+ spec.add_dependency 'rails', '>= 7.0.0'
35
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minato-rails-serializer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ferreri
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: panko_serializer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 7.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 7.0.0
41
+ description:
42
+ email:
43
+ - contato@ferreri.co
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rspec"
49
+ - ".rubocop.yml"
50
+ - MIT-LICENSE
51
+ - README.md
52
+ - Rakefile
53
+ - compose.yml
54
+ - entrypoint.sh
55
+ - lib/minato/rails/serializer.rb
56
+ - lib/minato/rails/serializer/base.rb
57
+ - lib/minato/rails/serializer/railtie.rb
58
+ - lib/minato/rails/serializer/version.rb
59
+ - minato-rails-serializer.gemspec
60
+ homepage: https://gitlab.com/ferreri/minato/minato-rails-serializer
61
+ licenses:
62
+ - MIT
63
+ metadata:
64
+ homepage_uri: https://gitlab.com/ferreri/minato/minato-rails-serializer
65
+ source_code_uri: https://gitlab.com/ferreri/minato/minato-rails-serializer
66
+ changelog_uri: https://gitlab.com/ferreri/minato/minato-rails-serializer/-/blob/main/CHANGELOG.md?ref_type=heads
67
+ rubygems_mfa_required: 'true'
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 3.1.0
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.4.19
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Serializer for Minato Rails Apps.
87
+ test_files: []