alba 3.5.0 → 3.7.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/README.md +96 -40
  4. data/lib/alba/association.rb +4 -1
  5. data/lib/alba/conditional_attribute.rb +11 -14
  6. data/lib/alba/layout.rb +8 -6
  7. data/lib/alba/railtie.rb +2 -2
  8. data/lib/alba/resource.rb +90 -17
  9. data/lib/alba/typed_attribute.rb +2 -0
  10. data/lib/alba/version.rb +1 -1
  11. data/lib/alba.rb +51 -32
  12. metadata +5 -53
  13. data/.codeclimate.yml +0 -12
  14. data/.editorconfig +0 -10
  15. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -26
  16. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  17. data/.github/dependabot.yml +0 -12
  18. data/.github/workflows/codeql-analysis.yml +0 -70
  19. data/.github/workflows/lint.yml +0 -17
  20. data/.github/workflows/main.yml +0 -41
  21. data/.gitignore +0 -11
  22. data/.rubocop.yml +0 -156
  23. data/.yardopts +0 -4
  24. data/CODE_OF_CONDUCT.md +0 -132
  25. data/CONTRIBUTING.md +0 -30
  26. data/Gemfile +0 -39
  27. data/HACKING.md +0 -42
  28. data/Rakefile +0 -17
  29. data/SECURITY.md +0 -12
  30. data/alba.gemspec +0 -33
  31. data/benchmark/Gemfile +0 -26
  32. data/benchmark/README.md +0 -137
  33. data/benchmark/collection.rb +0 -297
  34. data/benchmark/prep.rb +0 -56
  35. data/benchmark/single_resource.rb +0 -300
  36. data/bin/console +0 -15
  37. data/bin/setup +0 -8
  38. data/codecov.yml +0 -8
  39. data/docs/migrate_from_active_model_serializers.md +0 -359
  40. data/docs/migrate_from_jbuilder.md +0 -237
  41. data/docs/rails.md +0 -56
  42. data/gemfiles/without_active_support.gemfile +0 -19
  43. data/gemfiles/without_oj.gemfile +0 -19
  44. data/logo/alba-card.png +0 -0
  45. data/logo/alba-sign.png +0 -0
  46. data/logo/alba-typography.png +0 -0
@@ -1,297 +0,0 @@
1
- # Benchmark script to run varieties of JSON serializers
2
- # Fetch Alba from local, otherwise fetch latest from RubyGems
3
-
4
- require_relative 'prep'
5
-
6
- # --- Alba serializers ---
7
-
8
- require "alba"
9
-
10
- Alba.inflector = :active_support
11
-
12
- class AlbaCommentResource
13
- include ::Alba::Resource
14
- attributes :id, :body
15
- end
16
-
17
- class AlbaPostResource
18
- include ::Alba::Resource
19
- attributes :id, :body
20
- attribute :commenter_names do |post|
21
- post.commenters.pluck(:name)
22
- end
23
- many :comments, resource: AlbaCommentResource
24
- end
25
-
26
- class AlbaCommentWithTransformationResource < AlbaCommentResource
27
- transform_keys :lower_camel
28
- end
29
-
30
- class AlbaPostWithTransformationResource < AlbaPostResource
31
- many :comments, resource: AlbaCommentWithTransformationResource
32
-
33
- transform_keys :lower_camel
34
- end
35
-
36
- # --- ActiveModelSerializer serializers ---
37
-
38
- require "active_model_serializers"
39
-
40
- ActiveModelSerializers.logger = Logger.new(nil)
41
-
42
- class AMSCommentSerializer < ActiveModel::Serializer
43
- attributes :id, :body
44
- end
45
-
46
- class AMSPostSerializer < ActiveModel::Serializer
47
- attributes :id, :body
48
- attribute :commenter_names
49
- has_many :comments, serializer: AMSCommentSerializer
50
-
51
- def commenter_names
52
- object.commenters.pluck(:name)
53
- end
54
- end
55
-
56
- # --- Blueprint serializers ---
57
-
58
- require "blueprinter"
59
-
60
- class CommentBlueprint < Blueprinter::Base
61
- fields :id, :body
62
- end
63
-
64
- class PostBlueprint < Blueprinter::Base
65
- fields :id, :body, :commenter_names
66
- association :comments, blueprint: CommentBlueprint
67
-
68
- def commenter_names
69
- commenters.pluck(:name)
70
- end
71
- end
72
-
73
- # --- Fast Serializer Ruby
74
-
75
- require "fast_serializer"
76
-
77
- class FastSerializerCommentResource
78
- include ::FastSerializer::Schema::Mixin
79
- attributes :id, :body
80
- end
81
-
82
- class FastSerializerPostResource
83
- include ::FastSerializer::Schema::Mixin
84
-
85
- attributes :id, :body
86
-
87
- attribute :commenter_names do
88
- object.commenters.pluck(:name)
89
- end
90
-
91
- has_many :comments, serializer: FastSerializerCommentResource
92
- end
93
-
94
- # --- Jserializer serializers ---
95
-
96
- require 'jserializer'
97
-
98
- class JserializerCommentSerializer < Jserializer::Base
99
- attributes :id, :body
100
- end
101
-
102
- class JserializerPostSerializer < Jserializer::Base
103
- attributes :id, :body, :commenter_names
104
- has_many :comments, serializer: JserializerCommentSerializer
105
- def commenter_names
106
- object.commenters.pluck(:name)
107
- end
108
- end
109
-
110
- # --- Panko serializers ---
111
- #
112
-
113
- require "panko_serializer"
114
-
115
- class PankoCommentSerializer < Panko::Serializer
116
- attributes :id, :body
117
- end
118
-
119
-
120
- class PankoPostSerializer < Panko::Serializer
121
- attributes :id, :body, :commenter_names
122
-
123
- has_many :comments, serializer: PankoCommentSerializer
124
-
125
- def commenter_names
126
- object.commenters.pluck(:name)
127
- end
128
- end
129
-
130
- # --- Representable serializers ---
131
-
132
- require "representable"
133
-
134
- class CommentRepresenter < Representable::Decorator
135
- include Representable::JSON
136
-
137
- property :id
138
- property :body
139
- end
140
-
141
- class PostsRepresenter < Representable::Decorator
142
- include Representable::JSON::Collection
143
-
144
- items class: Post do
145
- property :id
146
- property :body
147
- property :commenter_names
148
- collection :comments, decorator: CommentRepresenter
149
- end
150
-
151
- def commenter_names
152
- commenters.pluck(:name)
153
- end
154
- end
155
-
156
- # --- SimpleAMS serializers ---
157
-
158
- require "simple_ams"
159
-
160
- class SimpleAMSCommentSerializer
161
- include SimpleAMS::DSL
162
-
163
- attributes :id, :body
164
- end
165
-
166
- class SimpleAMSPostSerializer
167
- include SimpleAMS::DSL
168
-
169
- attributes :id, :body
170
- attribute :commenter_names
171
- has_many :comments, serializer: SimpleAMSCommentSerializer
172
-
173
- def commenter_names
174
- object.commenters.pluck(:name)
175
- end
176
- end
177
-
178
- require 'turbostreamer'
179
- TurboStreamer.set_default_encoder(:json, :oj)
180
-
181
- class TurbostreamerSerializer
182
- def initialize(posts)
183
- @posts = posts
184
- end
185
-
186
- def to_json
187
- TurboStreamer.encode do |json|
188
- json.array! @posts do |post|
189
- json.object! do
190
- json.extract! post, :id, :body, :commenter_names
191
-
192
- json.comments post.comments do |comment|
193
- json.object! do
194
- json.extract! comment, :id, :body
195
- end
196
- end
197
- end
198
- end
199
- end
200
- end
201
- end
202
-
203
- # --- Test data creation ---
204
-
205
- 100.times do |i|
206
- post = Post.create!(body: "post#{i}")
207
- user1 = User.create!(name: "John#{i}")
208
- user2 = User.create!(name: "Jane#{i}")
209
- 10.times do |n|
210
- post.comments.create!(commenter: user1, body: "Comment1_#{i}_#{n}")
211
- post.comments.create!(commenter: user2, body: "Comment2_#{i}_#{n}")
212
- end
213
- end
214
-
215
- posts = Post.all.includes(:comments, :commenters)
216
-
217
- # --- Store the serializers in procs ---
218
-
219
- alba = Proc.new { AlbaPostResource.new(posts).serialize }
220
- alba_with_transformation = Proc.new { AlbaPostWithTransformationResource.new(posts).serialize }
221
- alba_inline = Proc.new do
222
- Alba.serialize(posts) do
223
- attributes :id, :body
224
- attribute :commenter_names do |post|
225
- post.commenters.pluck(:name)
226
- end
227
- many :comments do
228
- attributes :id, :body
229
- end
230
- end
231
- end
232
- ams = Proc.new { ActiveModelSerializers::SerializableResource.new(posts, {each_serializer: AMSPostSerializer}).to_json }
233
- blueprinter = Proc.new { PostBlueprint.render(posts) }
234
- fast_serializer = Proc.new { FastSerializerPostResource.new(posts).to_json }
235
- jserializer = Proc.new { JserializerPostSerializer.new(posts, is_collection: true).to_json }
236
- panko = proc { Panko::ArraySerializer.new(posts, each_serializer: PankoPostSerializer).to_json }
237
- rails = Proc.new do
238
- posts.to_json(include: {comments: {only: [:id, :body]}}, methods: [:commenter_names])
239
- end
240
- representable = Proc.new { PostsRepresenter.new(posts).to_json }
241
- simple_ams = Proc.new { SimpleAMS::Renderer::Collection.new(posts, serializer: SimpleAMSPostSerializer).to_json }
242
- turbostreamer = Proc.new { TurbostreamerSerializer.new(posts).to_json }
243
-
244
- # --- Execute the serializers to check their output ---
245
- GC.disable
246
- puts "Checking outputs..."
247
- correct = alba.call
248
- parsed_correct = JSON.parse(correct)
249
- {
250
- alba_inline: alba_inline,
251
- ams: ams,
252
- blueprinter: blueprinter,
253
- fast_serializer: fast_serializer,
254
- jserializer: jserializer,
255
- panko: panko,
256
- rails: rails,
257
- representable: representable,
258
- simple_ams: simple_ams,
259
- turbostreamer: turbostreamer
260
- }.each do |name, serializer|
261
- result = serializer.call
262
- parsed_result = JSON.parse(result)
263
- puts "#{name} yields wrong output: #{parsed_result}" unless parsed_result == parsed_correct
264
- end
265
-
266
- # --- Run the benchmarks ---
267
-
268
- benchmark_body = lambda do |x|
269
- x.report(:alba, &alba)
270
- x.report(:alba_with_transformation, &alba_with_transformation)
271
- x.report(:alba_inline, &alba_inline)
272
- x.report(:ams, &ams)
273
- x.report(:blueprinter, &blueprinter)
274
- x.report(:fast_serializer, &fast_serializer)
275
- x.report(:jserializer, &jserializer)
276
- x.report(:panko, &panko)
277
- x.report(:rails, &rails)
278
- x.report(:representable, &representable)
279
- x.report(:simple_ams, &simple_ams)
280
- x.report(:turbostreamer, &turbostreamer)
281
-
282
- x.compare!
283
- end
284
-
285
- require 'benchmark/ips'
286
- Benchmark.ips(&benchmark_body)
287
-
288
- require 'benchmark/memory'
289
- Benchmark.memory(&benchmark_body)
290
-
291
- # --- Show gem versions ---
292
-
293
- puts "Gem versions:"
294
- gems = %w[alba active_model_serializers blueprinter fast_serializer jserializer panko_serializer representable simple_ams turbostreamer]
295
- Bundler.load.specs.each do |spec|
296
- puts "#{spec.name}: #{spec.version}" if gems.include?(spec.name)
297
- end
data/benchmark/prep.rb DELETED
@@ -1,56 +0,0 @@
1
- # --- Test data model setup ---
2
-
3
- RubyVM::YJIT.enable if ENV["YJIT"]
4
- require "csv"
5
- require "pg"
6
- require "active_record"
7
- require "active_record/connection_adapters/postgresql_adapter"
8
- require "logger"
9
- require "oj"
10
- require "sqlite3"
11
- Oj.optimize_rails unless ENV['NO_OJ_OPTIMIZE_RAILS']
12
-
13
- ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
14
- # ActiveRecord::Base.logger = Logger.new($stdout)
15
-
16
- ActiveRecord::Schema.define do
17
- create_table :posts, force: true do |t|
18
- t.string :body
19
- end
20
-
21
- create_table :comments, force: true do |t|
22
- t.integer :post_id
23
- t.string :body
24
- t.integer :commenter_id
25
- end
26
-
27
- create_table :users, force: true do |t|
28
- t.string :name
29
- end
30
- end
31
-
32
- class Post < ActiveRecord::Base
33
- has_many :comments
34
- has_many :commenters, through: :comments, class_name: 'User', source: :commenter
35
-
36
- def attributes
37
- {id: nil, body: nil, commenter_names: commenter_names}
38
- end
39
-
40
- def commenter_names
41
- commenters.pluck(:name)
42
- end
43
- end
44
-
45
- class Comment < ActiveRecord::Base
46
- belongs_to :post
47
- belongs_to :commenter, class_name: 'User'
48
-
49
- def attributes
50
- {id: nil, body: nil}
51
- end
52
- end
53
-
54
- class User < ActiveRecord::Base
55
- has_many :comments
56
- end
@@ -1,300 +0,0 @@
1
- # Benchmark script to run varieties of JSON serializers
2
- # Fetch Alba from local, otherwise fetch latest from RubyGems
3
-
4
- require_relative 'prep'
5
-
6
- # --- Alba serializers ---
7
-
8
- require "alba"
9
-
10
- class AlbaCommentResource
11
- include ::Alba::Resource
12
- attributes :id, :body
13
- end
14
-
15
- class AlbaPostResource
16
- include ::Alba::Resource
17
- attributes :id, :body
18
- attribute :commenter_names do |post|
19
- post.commenters.pluck(:name)
20
- end
21
- many :comments, resource: AlbaCommentResource
22
- end
23
-
24
- # --- ActiveModelSerializer serializers ---
25
-
26
- require "active_model_serializers"
27
-
28
- class AMSCommentSerializer < ActiveModel::Serializer
29
- attributes :id, :body
30
- end
31
-
32
- class AMSPostSerializer < ActiveModel::Serializer
33
- attributes :id, :body
34
- attribute :commenter_names
35
- has_many :comments, serializer: AMSCommentSerializer
36
-
37
- def commenter_names
38
- object.commenters.pluck(:name)
39
- end
40
- end
41
-
42
- # --- Blueprint serializers ---
43
-
44
- require "blueprinter"
45
-
46
- class CommentBlueprint < Blueprinter::Base
47
- fields :id, :body
48
- end
49
-
50
- class PostBlueprint < Blueprinter::Base
51
- fields :id, :body, :commenter_names
52
- association :comments, blueprint: CommentBlueprint
53
-
54
- def commenter_names
55
- commenters.pluck(:name)
56
- end
57
- end
58
-
59
- # --- JBuilder serializers ---
60
-
61
- require "jbuilder"
62
-
63
- class Post
64
- def to_builder
65
- Jbuilder.new do |post|
66
- post.call(self, :id, :body, :commenter_names, :comments)
67
- end
68
- end
69
-
70
- def commenter_names
71
- commenters.pluck(:name)
72
- end
73
- end
74
-
75
- class Comment
76
- def to_builder
77
- Jbuilder.new do |comment|
78
- comment.call(self, :id, :body)
79
- end
80
- end
81
- end
82
-
83
- # --- Jserializer serializers ---
84
-
85
- require 'jserializer'
86
-
87
- class JserializerCommentSerializer < Jserializer::Base
88
- attributes :id, :body
89
- end
90
-
91
- class JserializerPostSerializer < Jserializer::Base
92
- attributes :id, :body, :commenter_names
93
- has_many :comments, serializer: JserializerCommentSerializer
94
- def commenter_names
95
- object.commenters.pluck(:name)
96
- end
97
- end
98
-
99
- # --- Panko serializers ---
100
-
101
- require "panko_serializer"
102
-
103
- class PankoCommentSerializer < Panko::Serializer
104
- attributes :id, :body
105
- end
106
-
107
-
108
- class PankoPostSerializer < Panko::Serializer
109
- attributes :id, :body, :commenter_names
110
-
111
- has_many :comments, serializer: PankoCommentSerializer
112
-
113
- def commenter_names
114
- object.commenters.pluck(:name)
115
- end
116
- end
117
-
118
- # --- Primalize serializers ---
119
-
120
- class PrimalizeCommentResource < Primalize::Single
121
- attributes id: integer, body: string
122
- end
123
-
124
- class PrimalizePostResource < Primalize::Single
125
- alias post object
126
-
127
- attributes(
128
- id: integer,
129
- body: string,
130
- comments: array(primalize(PrimalizeCommentResource)),
131
- commenter_names: array(string),
132
- )
133
-
134
- def commenter_names
135
- post.commenters.pluck(:name)
136
- end
137
- end
138
-
139
- # --- Representable serializers ---
140
-
141
- require "representable"
142
-
143
- class CommentRepresenter < Representable::Decorator
144
- include Representable::JSON
145
-
146
- property :id
147
- property :body
148
- end
149
-
150
- class PostRepresenter < Representable::Decorator
151
- include Representable::JSON
152
-
153
- property :id
154
- property :body
155
- property :commenter_names
156
- collection :comments
157
-
158
- def commenter_names
159
- commenters.pluck(:name)
160
- end
161
- end
162
-
163
- # --- SimpleAMS serializers ---
164
-
165
- require "simple_ams"
166
-
167
- class SimpleAMSCommentSerializer
168
- include SimpleAMS::DSL
169
-
170
- attributes :id, :body
171
- end
172
-
173
- class SimpleAMSPostSerializer
174
- include SimpleAMS::DSL
175
-
176
- attributes :id, :body
177
- attribute :commenter_names
178
- has_many :comments, serializer: SimpleAMSCommentSerializer
179
-
180
- def commenter_names
181
- object.commenters.pluck(:name)
182
- end
183
- end
184
-
185
- require 'turbostreamer'
186
- TurboStreamer.set_default_encoder(:json, :oj)
187
-
188
- class TurbostreamerSerializer
189
- def initialize(post)
190
- @post = post
191
- end
192
-
193
- def to_json
194
- TurboStreamer.encode do |json|
195
- json.object! do
196
- json.extract! @post, :id, :body, :commenter_names
197
-
198
- json.comments @post.comments do |comment|
199
- json.object! do
200
- json.extract! comment, :id, :body
201
- end
202
- end
203
- end
204
- end
205
- end
206
- end
207
-
208
- # --- Test data creation ---
209
-
210
- post = Post.create!(body: 'post')
211
- user1 = User.create!(name: 'John')
212
- user2 = User.create!(name: 'Jane')
213
- post.comments.create!(commenter: user1, body: 'Comment1')
214
- post.comments.create!(commenter: user2, body: 'Comment2')
215
- post.reload
216
-
217
- # --- Store the serializers in procs ---
218
-
219
- alba = Proc.new { AlbaPostResource.new(post).serialize }
220
- alba_inline = Proc.new do
221
- Alba.serialize(post) do
222
- attributes :id, :body
223
- attribute :commenter_names do |post|
224
- post.commenters.pluck(:name)
225
- end
226
- many :comments do
227
- attributes :id, :body
228
- end
229
- end
230
- end
231
-
232
- ams = Proc.new { AMSPostSerializer.new(post, {}).to_json }
233
- blueprinter = Proc.new { PostBlueprint.render(post) }
234
- jbuilder = Proc.new { post.to_builder.target! }
235
- jserializer = Proc.new { JserializerPostSerializer.new(post).to_json }
236
- panko = proc { PankoPostSerializer.new.serialize_to_json(post) }
237
- primalize = proc { PrimalizePostResource.new(post).to_json }
238
- rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
239
- representable = Proc.new { PostRepresenter.new(post).to_json }
240
- simple_ams = Proc.new { SimpleAMS::Renderer.new(post, serializer: SimpleAMSPostSerializer).to_json }
241
- turbostreamer = Proc.new { TurbostreamerSerializer.new(post).to_json }
242
-
243
- # --- Execute the serializers to check their output ---
244
-
245
- puts "Serializer outputs ----------------------------------"
246
- {
247
- alba: alba,
248
- alba_inline: alba_inline,
249
- ams: ams,
250
- blueprinter: blueprinter,
251
- jbuilder: jbuilder, # different order
252
- jserializer: jserializer,
253
- panko: panko,
254
- primalize: primalize,
255
- rails: rails,
256
- representable: representable,
257
- simple_ams: simple_ams,
258
- turbostreamer: turbostreamer
259
- }.each do |name, serializer|
260
- puts "#{name.to_s.ljust(24, ' ')} #{serializer.call}"
261
- end
262
-
263
- # --- Run the benchmarks ---
264
-
265
- require 'benchmark/ips'
266
- Benchmark.ips do |x|
267
- x.report(:alba, &alba)
268
- x.report(:alba_inline, &alba_inline)
269
- x.report(:ams, &ams)
270
- x.report(:blueprinter, &blueprinter)
271
- x.report(:jbuilder, &jbuilder)
272
- x.report(:jserializer, &jserializer)
273
- x.report(:panko, &panko)
274
- x.report(:primalize, &primalize)
275
- x.report(:rails, &rails)
276
- x.report(:representable, &representable)
277
- x.report(:simple_ams, &simple_ams)
278
- x.report(:turbostreamer, &turbostreamer)
279
-
280
- x.compare!
281
- end
282
-
283
-
284
- require 'benchmark/memory'
285
- Benchmark.memory do |x|
286
- x.report(:alba, &alba)
287
- x.report(:alba_inline, &alba_inline)
288
- x.report(:ams, &ams)
289
- x.report(:blueprinter, &blueprinter)
290
- x.report(:jbuilder, &jbuilder)
291
- x.report(:jserializer, &jserializer)
292
- x.report(:panko, &panko)
293
- x.report(:primalize, &primalize)
294
- x.report(:rails, &rails)
295
- x.report(:representable, &representable)
296
- x.report(:simple_ams, &simple_ams)
297
- x.report(:turbostreamer, &turbostreamer)
298
-
299
- x.compare!
300
- end
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'alba'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/codecov.yml DELETED
@@ -1,8 +0,0 @@
1
- coverage:
2
- status:
3
- project:
4
- default:
5
- informational: true
6
- patch:
7
- default:
8
- target: 90%