### Install Dependencies Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Run this command to install the necessary Ruby gems for the benchmarks. ```bash bundle install ``` -------------------------------- ### Define Example Classes Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_jbuilder.md Defines User, Profile, and Article classes used in serialization examples. ```ruby class User attr_reader :id, :created_at, :updated_at attr_accessor :profile, :articles def initialize(id) @id = id @created_at = Time.now @updated_at = Time.now @articles = [] end end class Profile attr_reader :email def initialize(email) @email = email end end class Article attr_accessor :title, :body def initialize(title, body) @title = title @body = body end end ``` -------------------------------- ### Define Example Models Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Define example ActiveRecord models including User, Profile, and Article with their respective associations. ```ruby class User < ActiveRecord::Base # columns: id, created_at, updated_at has_one :profile has_many :articles end class Profile < ActiveRecord::Base # columns: id, user_id, email, created_at, updated_at belongs_to :user end class Article < ActiveRecord::Base # columns: id, user_id, title, body, created_at, updated_at belongs_to :user end ``` -------------------------------- ### Launch Interactive Console Source: https://github.com/okuramasafumi/alba/blob/main/AGENTS.md Start an interactive Ruby console with Alba preloaded for development and experimentation. ```bash bundle exec bin/console ``` -------------------------------- ### Define and Serialize a Resource Source: https://github.com/okuramasafumi/alba/blob/main/README.md Define a simple resource and serialize an object using it. This is a basic setup for further inspection. ```ruby class FooResource include Alba::Resource attributes :id end FooResource.new(foo).serialize ``` -------------------------------- ### Set Backend Configuration Source: https://github.com/okuramasafumi/alba/blob/main/README.md Configure the JSON serialization backend for Alba. This example sets the backend to use the 'oj' gem. ```ruby Alba.backend = :oj ``` -------------------------------- ### Install Alba Gem Source: https://github.com/okuramasafumi/alba/blob/main/README.md Add the Alba gem to your application's Gemfile for installation. ```ruby gem 'alba' ``` -------------------------------- ### Pass Options to Helpers Source: https://github.com/okuramasafumi/alba/blob/main/README.md Helpers can accept options to customize attribute serialization. This example shows how to pass options to a helper to format time attributes using `iso8601`. ```ruby module AlbaExtension def time_attributes(*attrs, **options) attrs.each do |attr| attribute(attr, **options) do |object| object.__send__(attr).iso8601 end end end end ``` -------------------------------- ### Install Type Checking Dependencies Source: https://github.com/okuramasafumi/alba/blob/main/README.md Install type checking dependencies for Alba using Bundler. This command is specific to CRuby. ```bash # Install type checking dependencies (CRuby only) bundle install --with type ``` -------------------------------- ### Set Encoder Configuration Source: https://github.com/okuramasafumi/alba/blob/main/README.md Directly set the JSON encoder using a Proc. This example uses the standard 'json' gem's generate method. ```ruby Alba.encoder = ->(object) { JSON.generate(object) } ``` -------------------------------- ### Ignored Render Option Example Source: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md Demonstrates that options like `only` are ignored when rendering an Alba resource directly. Use `status` for valid options. ```ruby # This `only` option is ignored render json: FooResource.new(foo), only: [:id] # This is OK render json: FooResource.new(foo), status: 200 ``` -------------------------------- ### Deeply Nested Attributes Example Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates deeply nested attributes using multiple `nested` calls to build a complex structure. ```ruby class FooResource include Alba::Resource root_key :foo nested :bar do nested :baz do attribute :deep do 42 end end end end FooResource.new(nil).serialize # => '{"foo":{"bar":{"baz":{"deep":42}}}}' ``` -------------------------------- ### Set Inflector Configuration Source: https://github.com/okuramasafumi/alba/blob/main/README.md Configure the inflector for Alba's inference feature. This example sets it to use ActiveSupport::Inflector. ```ruby Alba.inflector = :active_support ``` -------------------------------- ### Group Attributes with Traits Source: https://github.com/okuramasafumi/alba/blob/main/README.md Traits allow grouping attributes for reusable inclusion. This example defines a default set of attributes and an 'additional' trait for name and email. ```ruby class User attr_accessor :id, :name, :email def initialize(id, name, email) @id = id @name = name @email = email end end class UserResource include Alba::Resource attributes :id trait :additional do attributes :name, :email end end user = User.new(1, 'Foo', 'foo@example.org') UserResource.new(user).serialize # => '{"id":1}' UserResource.new(user, with_traits: :additional).serialize # => '{"id":1,"name":"Foo","email":"foo@example.com"}' ``` -------------------------------- ### Configuring a Custom Inflector Source: https://github.com/okuramasafumi/alba/blob/main/README.md Provides an example of how to set a custom inflector module for Alba. The `CustomInflector` module must implement methods like `camelize`, `camelize_lower`, `dasherize`, `underscore`, and `classify`. ```ruby module CustomInflector module_function def camelize(string); end def camelize_lower(string); end def dasherize(string); end def underscore(string); end def classify(string); end end Alba.inflector = CustomInflector ``` -------------------------------- ### Cascading Key Transformation with Associations Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how `transform_keys` automatically cascades to associated resources by default (cascade: true). The example demonstrates lower camel case transformation applied to both the main resource and its nested `bank_account` association. ```ruby class User attr_reader :id, :first_name, :last_name, :bank_account def initialize(id, first_name, last_name) @id = id @first_name = first_name @last_name = last_name @bank_account = BankAccount.new(1234) end end class BankAccount attr_reader :account_number def initialize(account_number) @account_number = account_number end end class UserResource include Alba::Resource attributes :id, :first_name, :last_name transform_keys :lower_camel # Default is cascade: true one :bank_account do attributes :account_number end end user = User.new(1, 'Masafumi', 'Okura') UserResource.new(user).serialize # => '{"id":1,"firstName":"Masafumi","lastName":"Okura","bankAccount":{"accountNumber":1234}}' ``` -------------------------------- ### Add Indexes to 'many' Associations in Alba Source: https://github.com/okuramasafumi/alba/blob/main/README.md Optimize serialization of 'many' associations by pre-fetching and providing indexes via `params`. This example demonstrates how to add an index attribute to each book in an author's collection. ```ruby Author = Data.define(:id, :books) Book = Data.define(:id, :name) book1 = Book.new(1, 'book1') book2 = Book.new(2, 'book2') book3 = Book.new(3, 'book3') author = Author.new(2, [book2, book3, book1]) class AuthorResource include Alba::Resource attributes :id many :books do attributes :id, :name attribute :index do |bar| params[:index][bar.id] end end end AuthorResource.new( author, params: { index: author.books.map.with_index { |book, index| [book.id, index] } .to_h } ).serialize # => {"id":2,"books":[{"id":2,"name":"book2","index":0},{"id":3,"name":"book3","index":1},{"id":1,"name":"book1","index":2}]} ``` -------------------------------- ### Run All Tests Source: https://github.com/okuramasafumi/alba/blob/main/CLAUDE.md Execute the full test suite using the default Gemfile. Prefix with BUNDLE_GEMFILE=gemfiles/.gemfile to test with alternate dependency sets. ```bash bundle exec rake test ``` ```bash rake test ``` ```bash BUNDLE_GEMFILE=gemfiles/.gemfile bundle exec rake test ``` -------------------------------- ### Run All Tests Source: https://github.com/okuramasafumi/alba/blob/main/AGENTS.md Execute the full test suite using the default Gemfile. Prefix with BUNDLE_GEMFILE=gemfiles/.gemfile to validate alternate dependency sets. ```bash bundle exec rake test ``` -------------------------------- ### Basic Resource Serialization in Ruby Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates how to define a resource and serialize a user object. Requires defining a User class and a UserResource class that inherits from Alba::Resource. ```ruby class User attr_accessor :id, :name, :email def initialize(id, name, email) @id = id @name = name @email = email end end class UserResource include Alba::Resource root_key :user attributes :id, :name attribute :name_with_email do |resource| "#{resource.name}: #{resource.email}" end end user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com') UserResource.new(user).serialize # => '{"user":{"id":1,"name":"Masafumi OKURA","name_with_email":"Masafumi OKURA: masafumi@example.com"}}' ``` -------------------------------- ### Simulate Multiple Root Keys with Alba.serialize Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates how to simulate multiple root keys in JSON output by using `Alba.serialize` with attributes that return hashes from resource classes. ```ruby # Define foo and bar local variables here Alba.serialize do attribute :key1 do FooResource.new(foo).to_h end attribute :key2 do BarResource.new(bar).to_h end end # => JSON containing "key1" and "key2" as root keys ``` -------------------------------- ### Define User Resource with Instance Method for Attribute Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to use an instance method within the resource class to define an attribute, achieving the same serialization result as the block-based approach. ```ruby # The serialization result is the same as above class UserResource include Alba::Resource root_key :user, :users # Later is for plural attributes :id, :name, :name_with_email # Attribute methods must accept one argument for each serialized object def name_with_email(user) "#{user.name}: #{user.email}" end end ``` -------------------------------- ### Equivalent Serialization and Hash Output Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates the equivalence between `serialize` and `to_json`, and between `serializable_hash` and `to_h` for resource objects. ```ruby # These are equivalent and will return serialized JSON UserResource.new(user).serialize UserResource.new(user).to_json # These are equivalent and will return a Hash UserResource.new(user).serializable_hash UserResource.new(user).to_h ``` -------------------------------- ### Benchmark: ips without YJIT and without Oj.optimize_rails Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Compares serialization speed (iterations per second) with both Oj.optimize_rails and YJIT disabled. This represents a baseline performance scenario. ```text Comparison: panko: 835.7 i/s barley_cache: 406.2 i/s - 2.06x slower props_template: 389.1 i/s - 2.15x slower alba: 381.7 i/s - 2.19x slower turbostreamer: 366.6 i/s - 2.28x slower jserializer: 363.3 i/s - 2.30x slower alba_with_transformation: 350.5 i/s - 2.38x slower barley: 316.5 i/s - 2.64x slower jbuilder: 232.6 i/s - 3.59x slower fast_serializer: 185.0 i/s - 4.52x slower rails: 163.2 i/s - 5.12x slower blueprinter: 144.2 i/s - 5.80x slower rabl: 113.3 i/s - 7.38x slower representable: 101.7 i/s - 8.22x slower alba_inline: 101.4 i/s - 8.24x slower simple_ams: 75.4 i/s - 11.08x slower ams: 27.7 i/s - 30.21x slower ``` -------------------------------- ### Transform Keys with transform_keys macro Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_jbuilder.md Use the `transform_keys` macro to migrate Jbuilder's `key_format!` behavior, for example, to convert keys to lower camel case. ```ruby class UserResource include Alba::Resource root_key :user attributes :id, :created_at, :updated_at transform_keys :lower_camel end ``` -------------------------------- ### Benchmark: ips with Oj.optimize_rails and YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Compares serialization speed (iterations per second) with Oj.optimize_rails enabled and YJIT active. Higher values indicate better performance. ```text Comparison: panko: 1267.6 i/s props_template: 966.2 i/s - 1.31x slower alba: 905.1 i/s - 1.40x slower turbostreamer: 881.3 i/s - 1.44x slower jserializer: 694.1 i/s - 1.83x slower alba_with_transformation: 632.2 i/s - 2.01x slower barley_cache: 450.4 i/s - 2.81x slower barley: 437.6 i/s - 2.90x slower jbuilder: 416.0 i/s - 3.05x slower fast_serializer: 384.0 i/s - 3.30x slower rails: 357.1 i/s - 3.55x slower rabl: 299.9 i/s - 4.23x slower blueprinter: 275.1 i/s - 4.61x slower representable: 177.1 i/s - 7.16x slower simple_ams: 114.2 i/s - 11.10x slower ams: 41.7 i/s - 30.37x slower alba_inline: 12.7 i/s - 99.68x slower ``` -------------------------------- ### Benchmark: ips with Oj.optimize_rails and without YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Compares serialization speed (iterations per second) with Oj.optimize_rails enabled but YJIT disabled. This highlights the impact of YJIT on performance. ```text Comparison: panko: 845.8 i/s props_template: 386.7 i/s - 2.19x slower jserializer: 384.9 i/s - 2.20x slower turbostreamer: 369.0 i/s - 2.29x slower alba: 367.1 i/s - 2.30x slower barley_cache: 347.4 i/s - 2.43x slower alba_with_transformation: 334.1 i/s - 2.53x slower barley: 278.3 i/s - 3.04x slower jbuilder: 224.0 i/s - 3.78x slower rails: 206.1 i/s - 4.10x slower fast_serializer: 189.6 i/s - 4.46x slower blueprinter: 150.3 i/s - 5.63x slower rabl: 114.0 i/s - 7.42x slower alba_inline: 102.1 i/s - 8.28x slower representable: 101.9 i/s - 8.30x slower simple_ams: 73.6 i/s - 11.49x slower ams: 28.7 i/s - 29.45x slower ``` -------------------------------- ### Filter Attributes Conditionally with `if` Proc Source: https://github.com/okuramasafumi/alba/blob/main/README.md Use the `if` option with a proc to conditionally include attributes based on the target object and attribute value. This example filters out nil attributes. ```ruby class User attr_accessor :id, :name, :email def initialize(id, name, email) @id = id @name = name @email = email end end class UserResource include Alba::Resource attributes :id, :name, :email, if: proc { |user, attribute| !attribute.nil? } end user = User.new(1, nil, nil) UserResource.new(user).serialize # => '{"id":1}' ``` -------------------------------- ### Resource Method Overriding Default Behavior Source: https://github.com/okuramasafumi/alba/blob/main/README.md Explains that by default, Alba calls methods on the Resource class, not the target object. This example shows a method conflict and its resolution. ```ruby class Foo def bar 'This is Foo' end end class FooResource include Alba::Resource attributes :bar def bar 'This is FooResource' end end FooResource.new(Foo.new).serialize ``` -------------------------------- ### Benchmark: ips without Oj.optimize_rails and with YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Compares serialization speed (iterations per second) with Oj.optimize_rails disabled but YJIT active. This shows performance without specific Oj optimizations. ```text Comparison: panko: 1281.1 i/s alba: 1058.7 i/s - 1.21x slower props_template: 969.6 i/s - 1.32x slower turbostreamer: 877.2 i/s - 1.46x slower alba_with_transformation: 705.5 i/s - 1.82x slower jserializer: 702.1 i/s - 1.82x slower barley_cache: 637.6 i/s - 2.01x slower barley: 629.1 i/s - 2.04x slower jbuilder: 532.6 i/s - 2.41x slower fast_serializer: 380.3 i/s - 3.37x slower rails: 324.9 i/s - 3.94x slower rabl: 306.4 i/s - 4.18x slower blueprinter: 274.8 i/s - 4.66x slower representable: 184.3 i/s - 6.95x slower simple_ams: 128.7 i/s - 9.96x slower ams: 42.0 i/s - 30.49x slower alba_inline: 13.2 i/s - 96.87x slower ``` -------------------------------- ### Run Benchmark with Oj.optimize_rails and with YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Execute the collection.rb script with Oj.optimize_rails enabled and YJIT enabled. ```bash bundle exec ruby collection.rb ``` -------------------------------- ### Serialize User Collection with Resource Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates serializing a collection of User objects using the UserResource, automatically handling pluralization with the root_key. ```ruby user1 = User.new(1, 'Masafumi OKURA', 'masafumi@example.com') user2 = User.new(2, 'Test User', 'test@example.com') UserResource.new([user1, user2]).serialize # => '{"users":[{"id":1,"name":"Masafumi OKURA","name_with_email":"Masafumi OKURA: masafumi@example.com"},{"id":2,"name":"Test User","name_with_email":"Test User: test@example.com"}]}' ``` -------------------------------- ### Experimental Key Transformation API Source: https://github.com/okuramasafumi/alba/blob/main/README.md Illustrates the experimental `transform_keys!` API for modifying resource classes without creating new ones, useful for variations like different key casing. ```ruby class FooResource include Alba::Resource transform_keys :camel attributes :id end # Rails app class FoosController < ApplicationController def index foos = Foo.where(some: :condition) key_transformation_type = params[:key_transformation_type] # Say it's "lower_camel" # When params is absent, do not use modification API since it's slower resource_class = key_transformation_type ? FooResource.transform_keys!(key_transformation_type) : FooResource render json: resource_class.new(foos).serialize # The keys are lower_camel end end ``` -------------------------------- ### Configure Alba Initializer Source: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md Set Alba's backend and inflector in a Rails initializer file. Use `:active_support` for both by default, or `:oj_rails` for the backend if Oj is preferred. ```ruby # alba.rb Alba.backend = :active_support Alba.inflector = :active_support ``` -------------------------------- ### Generate JSON with Multiple Root Keys using Alba.hashify Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to generate JSON with multiple root keys without defining resource classes, by combining `Alba.serialize` and `Alba.hashify`. ```ruby # Define foo and bar local variables here Alba.serialize do attribute :foo do Alba.hashify(foo) do attributes :id, :name # For example end end attribute :bar do Alba.hashify(bar) do attributes :id end end end # => JSON containing "foo" and "bar" as root keys ``` -------------------------------- ### Ruby: Define Resource with Inline String Layout Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates defining a resource with an inline layout specified as a Proc that returns a String. String interpolation is used to embed the serialized JSON, and percentage notation is shown for handling double quotes within the string. ```ruby class FooResource include Alba::Resource layout inline: proc { %( { "header": "my header", "body": #{serialized_json} } ) } end ``` -------------------------------- ### Use `meta` Option Without DSL Source: https://github.com/okuramasafumi/alba/blob/main/README.md Metadata can be provided solely through the `meta` option during serialization. This is useful for dynamic metadata or when a predefined `meta` block is not needed. ```ruby class UserResourceWithoutMeta include Alba::Resource root_key :user, :users attributes :id, :name end UserResourceWithoutMeta.new([user]).serialize(meta: {foo: :bar}) # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"foo":"bar"}}' ``` -------------------------------- ### Run Benchmark with Oj.optimize_rails and without YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Execute the collection.rb script with Oj.optimize_rails enabled and YJIT disabled. ```bash NO_YJIT=1 bundle exec ruby collection.rb ``` -------------------------------- ### Run Benchmark without Oj.optimize_rails and with YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Execute the collection.rb script with Oj.optimize_rails disabled and YJIT enabled. ```bash NO_OJ_OPTIMIZE_RAILS=1 bundle exec ruby collection.rb ``` -------------------------------- ### Define User Resource with Attributes and Calculated Field Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates defining a resource with simple attributes and a calculated attribute that combines name and email. Requires a User class with corresponding attributes. ```ruby class User attr_accessor :id, :name, :email, :created_at, :updated_at def initialize(id, name, email) @id = id @name = name @email = email @created_at = Time.now @updated_at = Time.now end end class UserResource include Alba::Resource root_key :user attributes :id, :name attribute :name_with_email do |resource| "#{resource.name}: #{resource.email}" end end user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com') UserResource.new(user).serialize # => '{"user":{"id":1,"name":"Masafumi OKURA","name_with_email":"Masafumi OKURA: masafumi@example.com"}}' ``` -------------------------------- ### Benchmark: memory with Oj.optimize_rails and YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Compares memory allocation during serialization with Oj.optimize_rails enabled and YJIT active. Lower values indicate more efficient memory usage. ```text Comparison: panko: 259178 allocated props_template: 457698 allocated - 1.77x more turbostreamer: 641720 allocated - 2.48x more jserializer: 822289 allocated - 3.17x more alba_with_transformation: 833869 allocated - 3.22x more alba: 833929 allocated - 3.22x more fast_serializer: 1470129 allocated - 5.67x more rabl: 1676235 allocated - 6.47x more barley: 2753439 allocated - 10.62x more ``` -------------------------------- ### Inheriting and Extending Alba Resources Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates how to inherit from an existing Alba Resource class to add new attributes and modify the root key. This allows for extending resource definitions using standard object-oriented principles. ```ruby class FooResource include Alba::Resource root_key :foo attributes :bar end class ExtendedFooResource < FooResource root_key :foofoo attributes :baz end Foo = Struct.new(:bar, :baz) foo = Foo.new(1, 2) FooResource.new(foo).serialize # => '{"foo":{"bar":1}}' ExtendedFooResource.new(foo).serialize # => '{"foofoo":{"bar":1,"baz":2}}' ``` -------------------------------- ### Ruby: Define Resource with Inline Hash Layout Source: https://github.com/okuramasafumi/alba/blob/main/README.md Illustrates defining a resource with an inline layout specified as a Proc that returns a Hash. The `serializable_hash` method can be used within the Proc to access the data to be serialized. ```ruby class FooResource include Alba::Resource layout inline: proc { { header: 'my header', body: serializable_hash } } end ``` -------------------------------- ### Create Custom DSL with AlbaExtension Source: https://github.com/okuramasafumi/alba/blob/main/README.md Define a custom DSL for serializing attributes by extending a module. This approach allows for more concise attribute definitions. Use `extend` to apply the extension. ```ruby module AlbaExtension # Here attrs are an Array of Symbol def formatted_time_attributes(*attrs) attrs.each do |attr| attribute(attr) do |object| time = object.__send__(attr) time.strftime('%m/%d/%Y') end end end end class FooResource include Alba::Resource extend AlbaExtension # other attributes formatted_time_attributes :created_at, :updated_at end class BarResource include Alba::Resource extend AlbaExtension # other attributes formatted_time_attributes :created_at, :updated_at end ``` -------------------------------- ### Alba Serialization with Root Key Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Demonstrates how to specify a root key for Alba serialization using the `root_key` method. Note that `serializable_hash` does not support root keys; use `serialize` and `JSON.parse` instead. ```ruby class UserResource include Alba::Resource root_key :user # Call root_key method like ActiveModel::Serializer#type attributes :id, :created_at, :updated_at end # serialize user = User.create! JSON.parse UserResource.new(user).serialize ``` -------------------------------- ### Basic Jbuilder Serialization Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_jbuilder.md Demonstrates basic JSON serialization using Jbuilder with a block or the `extract!` method. ```ruby # show.json.jbuilder # With block @user = User.new(id) json.user do |user| user.id @user.id user.created_at @user.created_at user.updated_at @user.updated_at end # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}' # or #extract! json.extract! @user, :id, :created_at, :updated_at # => '{"id":id, "created_at": created_at, "updated_at": updated_at}' ``` -------------------------------- ### ActiveModelSerializer Basic Serialization Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Demonstrates basic serialization of a User model using ActiveModel::Serializer, defining attributes and type. ```ruby class UserSerializer < ActiveModel::Serializer type :user attributes :id, :created_at, :updated_at end # serialize user = User.create! ActiveModelSerializers::SerializableResource.new( user ).serializable_hash ``` -------------------------------- ### Lint Code with RuboCop Source: https://github.com/okuramasafumi/alba/blob/main/AGENTS.md Apply configured Ruby, Rake, Markdown, Performance, and Minitest cops to maintain code quality. Autofixes should be applied before committing. ```bash bundle exec rubocop ``` -------------------------------- ### Ruby: Define Resource with File-Based Layout Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to define a resource that uses an external ERB file for its JSON layout. The `layout file:` option specifies the path to the layout file, which can include placeholders like `<%= serialized_json %>` and access `params` or `object`. ```ruby class FooResource include Alba::Resource layout file: 'my_layout.json.erb' end ``` -------------------------------- ### Generating JSON from Hash Output Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to generate a JSON string from the hash output of a resource object using `as_json` and `JSON.generate`. ```ruby # These are equivalent and will return the same result UserResource.new(user).serialize UserResource.new(user).to_json JSON.generate(UserResource.new(user).as_json) ``` -------------------------------- ### Run Benchmark without Oj.optimize_rails and without YJIT Source: https://github.com/okuramasafumi/alba/blob/main/benchmark/README.md Execute the collection.rb script with both Oj.optimize_rails and YJIT disabled. ```bash NO_YJIT=1 NO_OJ_OPTIMIZE_RAILS=1 bundle exec ruby collection.rb ``` -------------------------------- ### Serialize User with Associated Articles Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates serializing a User object that has associated Article objects, using the `many` macro to define the one-to-many relationship and specify the resource for articles. ```ruby class User attr_reader :id, :created_at, :updated_at attr_accessor :articles def initialize(id) @id = id @created_at = Time.now @updated_at = Time.now @articles = [] end end class Article attr_accessor :user_id, :title, :body def initialize(user_id, title, body) @user_id = user_id @title = title @body = body end end class ArticleResource include Alba::Resource attributes :title end class UserResource include Alba::Resource attributes :id many :articles, resource: ArticleResource end user = User.new(1) article1 = Article.new(1, 'Hello World!', 'Hello World!!!') user.articles << article1 article2 = Article.new(2, 'Super nice', 'Really nice!') user.articles << article2 UserResource.new(user).serialize # => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}' ``` -------------------------------- ### Prefer Object Method for Serialization Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to use `prefer_object_method!` to ensure Alba calls methods on the target object instead of the Resource class when a conflict exists. ```ruby class Foo def bar 'This is Foo' end end class FooResource include Alba::Resource prefer_object_method! # <- important attributes :bar # This is not called def bar 'This is FooResource' end end FooResource.new(Foo.new).serialize # => '{"bar":"This is Foo"}' ``` -------------------------------- ### Inline Serialization with Alba.serialize Source: https://github.com/okuramasafumi/alba/blob/main/README.md Uses `Alba.serialize` as a shortcut to define resource attributes and collections inline without a separate resource class. ```ruby Alba.serialize(user, root_key: :foo) do attributes :id many :articles do attributes :title, :body end end # => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}' ``` -------------------------------- ### Set Metadata with `meta` DSL Source: https://github.com/okuramasafumi/alba/blob/main/README.md Use the `meta` DSL to define metadata. It can conditionally include data based on the object type. Metadata is automatically included in the serialized output. ```ruby class UserResource include Alba::Resource root_key :user, :users attributes :id, :name meta do if object.is_a?(Enumerable) {size: object.size} else {foo: :bar} end end end user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com') UserResource.new([user]).serialize # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1}}' # You can merge metadata with `meta` option UserResource.new([user]).serialize(meta: {foo: :bar}) # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"foo":"bar"}}' ``` -------------------------------- ### Using Helper for Shared Behaviors in Inline Associations Source: https://github.com/okuramasafumi/alba/blob/main/README.md Shows how to use the `helper` block to define methods that can be shared across inline associations, resolving the inheritance issue. ```ruby class ApplicationResource include Alba::Resource helper do def with_id attributes(:id) end end end # Now `LibraryResource` works! ``` -------------------------------- ### Alba Serialization with Root Key and Symbolized Keys Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Illustrates how to serialize with a root key and then symbolize the keys using Rails' `deep_symbolize_keys` method. ```ruby user = User.create! JSON.parse(UserResource.new(user).serialize).deep_symbolize_keys ``` -------------------------------- ### Ruby: Use Custom Type in Resource Definition Source: https://github.com/okuramasafumi/alba/blob/main/README.md Illustrates using a previously registered custom type ':iso8601' for the 'created_at' attribute within a resource definition. This simplifies serialization by abstracting the conversion logic. ```ruby class UserResource include Alba::Resource attributes :id, created_at: :iso8601 end ``` -------------------------------- ### Ruby: Serialize Collection into Hash with Collection Key Source: https://github.com/okuramasafumi/alba/blob/main/README.md Demonstrates serializing a collection of objects into a hash where each object is keyed by a specified attribute. The `collection_key` method is crucial for this functionality and requires unique keys within the collection. ```ruby class User attr_reader :id, :name def initialize(id, name) @id = id @name = name end end class UserResource include Alba::Resource collection_key :id # This line is important attributes :id, :name end user1 = User.new(1, 'John') user2 = User.new(2, 'Masafumi') UserResource.new([user1, user2]).serialize # => '{"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Masafumi"}}' ``` -------------------------------- ### Ruby: Define User Resource with Type Conversions Source: https://github.com/okuramasafumi/alba/blob/main/README.md Defines a UserResource that serializes a User object, demonstrating automatic type conversions for attributes like id, age, and created_at. Ensure input types match expected types or enable auto_convert. ```ruby class User attr_reader :id, :name, :age, :bio, :admin, :created_at def initialize(id, name, age, bio = '', admin = false) @id = id @name = name @age = age @admin = admin @bio = bio @created_at = Time.new(2020, 10, 10) end end class UserResource include Alba::Resource attributes :name, id: [String, true], age: [Integer, true], bio: String, admin: [:Boolean, true], created_at: [String, ->(object) { object.strftime('%F') }] end user = User.new(1, 'Masafumi OKURA', '32', 'Ruby dev') UserResource.new(user).serialize # => '{"name":"Masafumi OKURA","id":"1","age":32,"bio":"Ruby dev","admin":false,"created_at":"2020-10-10"}' ``` -------------------------------- ### Use Symbol to Proc for Simple Attribute Renaming Source: https://github.com/okuramasafumi/alba/blob/main/README.md Illustrates a shortcut for renaming an attribute using Symbol to Proc, useful for simple attribute transformations. ```ruby class UserResource include Alba::Resource attribute :some_other_name, &:name end ``` -------------------------------- ### Change Metadata Key with `meta` DSL Source: https://github.com/okuramasafumi/alba/blob/main/README.md Specify a custom key for metadata using `meta :custom_key`. This custom key will be used for both DSL-defined metadata and metadata passed via the `meta` option. ```ruby class UserResourceWithDifferentMetaKey include Alba::Resource root_key :user, :users attributes :id, :name meta :my_meta do {foo: :bar} end end UserResourceWithDifferentMetaKey.new([user]).serialize # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"my_meta":{"foo":"bar"}}' UserResourceWithDifferentMetaKey.new([user]).serialize(meta: {extra: 42}) # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"my_meta":{"foo":"bar","extra":42}}' ``` -------------------------------- ### Determine Resource Dynamically with Proc Source: https://github.com/okuramasafumi/alba/blob/main/README.md Use a Proc for the `resource` option to dynamically determine which resource class to use for an association based on the associated object's properties. ```ruby class UserResource include Alba::Resource attributes :id many :articles, resource: ->(article) { article.with_comment? ? ArticleWithCommentResource : ArticleResource } end ``` -------------------------------- ### Serialize Heterogeneous Collection with Custom Resource Lambda Source: https://github.com/okuramasafumi/alba/blob/main/README.md Serializes a heterogeneous collection by providing a lambda to the `with` option of `Alba.serialize` to dynamically determine the resource class for each object. ```ruby Foo = Data.define(:id, :name) Bar = Data.define(:id, :address) class FooResource include Alba::Resource attributes :id, :name end class BarResource include Alba::Resource attributes :id, :address end class CustomFooResource include Alba::Resource attributes :id end foo1 = Foo.new(1, 'foo1') foo2 = Foo.new(2, 'foo2') bar1 = Bar.new(1, 'bar1') bar2 = Bar.new(2, 'bar2') Alba.serialize( [foo1, bar1, foo2, bar2], # `with` option takes a lambda to return resource class with: lambda do |obj| case obj when Foo CustomFooResource when Bar BarResource else raise # Impossible in this case end end ) # => '[{"id":1},{"id":1,"address":"bar1"},{"id":2},{"id":2,"address":"bar2"}] # Note `CustomFooResource` is used here ``` -------------------------------- ### Alba Basic Serialization Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Shows basic serialization of a User model using Alba::Resource, defining attributes. ```ruby class UserResource include Alba::Resource attributes :id, :created_at, :updated_at end # serialize user = User.create! UserResource.new(user).serializable_hash ``` -------------------------------- ### ActiveModelSerializer Nested Serialization Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_active_model_serializers.md Demonstrates nested serialization of User, Profile, and Article resources using ActiveModel::Serializer, specifying serializers for `has_one` and `has_many` associations. ```ruby class ProfileSerializer < ActiveModel::Serializer type :profile attributes :email end class ArticleSerializer < ActiveModel::Serializer type :article attributes :title, :body end class UserSerializer < ActiveModel::Serializer type :user attributes :id, :created_at, :updated_at has_one :profile, serializer: ProfileSerializer # For has_one relation has_many :articles, serializer: ArticleSerializer # For has_many relation end user = User.create! user.create_profile! email: email user.articles.create! title: title, body: body ActiveModelSerializers::SerializableResource.new( user ).serializable_hash ``` -------------------------------- ### Basic Alba Serialization Source: https://github.com/okuramasafumi/alba/blob/main/docs/migrate_from_jbuilder.md Shows basic JSON serialization with Alba using a block or a Resource class. ```ruby # With block user = User.new(id) Alba.serialize(user, root_key: :user) do attributes :id, :created_at, :updated_at end # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}' # or with resourceClass. # Infer and use by "#{MODEL_NAME}Resource" class UserResource include Alba::Resource root_key :user attributes :id, :created_at, :updated_at end UserResource.new(user).serialize # => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}' ``` -------------------------------- ### Transforming Root Key to Dash Case Source: https://github.com/okuramasafumi/alba/blob/main/README.md Illustrates how to transform the root key to dash case when the inflector is set and `root_key!` is used, combined with `transform_keys :dash, root: true`. This applies the transformation to the root element of the serialized output. ```ruby Alba.inflector = :active_support class BankAccount attr_reader :account_number def initialize(account_number) @account_number = account_number end end class BankAccountResource include Alba::Resource root_key! attributes :account_number transform_keys :dash, root: true end bank_account = BankAccount.new(123_456_789) BankAccountResource.new(bank_account).serialize # => '{"bank-account":{"account-number":123456789}}' ``` -------------------------------- ### Serialize Heterogeneous Collection with Inference Source: https://github.com/okuramasafumi/alba/blob/main/README.md Serializes a collection of objects of different types using `Alba.serialize` with the `with: :inference` option. ```ruby Foo = Data.define(:id, :name) Bar = Data.define(:id, :address) class FooResource include Alba::Resource attributes :id, :name end class BarResource include Alba::Resource attributes :id, :address end foo1 = Foo.new(1, 'foo1') foo2 = Foo.new(2, 'foo2') bar1 = Bar.new(1, 'bar1') bar2 = Bar.new(2, 'bar2') # This works only when inflector is set Alba.serialize([foo1, bar1, foo2, bar2], with: :inference) # => '[{"id":1,"name":"foo1"},{"id":1,"address":"bar1"},{"id":2,"name":"foo2"},{"id":2,"address":"bar2"}]' ``` -------------------------------- ### Specify Custom Association Source with Proc Source: https://github.com/okuramasafumi/alba/blob/main/README.md Define a custom data source for an association using a Proc with the `source` option. This allows fetching association data from methods or instance variables other than the association name. ```ruby class User attr_accessor :id, :name, :metadata def custom_profile {profile: {email: "#{name.downcase}@example.com"}} end end class UserResource include Alba::Resource attributes :id, :name # Use a custom method as source one :profile, source: proc { custom_profile[:profile] } # Access instance variables one :user_metadata, source: proc { @metadata } end ``` -------------------------------- ### Add Custom Logging to Alba Serialization Source: https://github.com/okuramasafumi/alba/blob/main/README.md Prepend a module to your resource to override the `serialize` method and log the intermediate `serializable_hash`. This allows you to see the data before it's fully serialized. ```ruby module Logging # `...` was added in Ruby 2.7 def serialize(...) puts serializable_hash super end end FooResource.prepend(Logging) FooResource.new(foo).serialize # => "{:id=>1}" is printed ``` -------------------------------- ### Perform Static Type Checking Source: https://github.com/okuramasafumi/alba/blob/main/AGENTS.md Run both RBS validation and Steep checks to ensure type safety. Individual commands 'rake rbs' or 'rake steep' can be used for faster feedback loops. ```bash bundle exec rake typecheck ``` -------------------------------- ### Convert Nil to Empty String with on_nil Source: https://github.com/okuramasafumi/alba/blob/main/README.md Globally set a default value, such as an empty string, for any `nil` attributes within a resource. This simplifies handling of missing data. ```ruby class User attr_reader :id, :name, :age def initialize(id, name = nil, age = nil) @id = id @name = name @age = age end end class UserResource include Alba::Resource on_nil { '' } root_key :user, :users attributes :id, :name, :age end UserResource.new(User.new(1)).serialize # => '{"user":{"id":1,"name":"","age":""}}' ```