alba 1.5.0 → 2.0.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 +4 -4
 - data/.codeclimate.yml +11 -0
 - data/.github/dependabot.yml +4 -18
 - data/.github/workflows/codeql-analysis.yml +70 -0
 - data/.github/workflows/main.yml +4 -4
 - data/.github/workflows/perf.yml +2 -2
 - data/.rubocop.yml +5 -1
 - data/CHANGELOG.md +34 -0
 - data/CONTRIBUTING.md +30 -0
 - data/Gemfile +6 -2
 - data/HACKING.md +41 -0
 - data/README.md +599 -128
 - data/Rakefile +2 -2
 - data/alba.gemspec +8 -4
 - data/benchmark/README.md +81 -0
 - data/benchmark/collection.rb +60 -74
 - data/benchmark/single_resource.rb +33 -2
 - data/docs/migrate_from_jbuilder.md +18 -4
 - data/docs/rails.md +44 -0
 - data/lib/alba/association.rb +49 -9
 - data/lib/alba/conditional_attribute.rb +54 -0
 - data/lib/alba/default_inflector.rb +13 -24
 - data/lib/alba/errors.rb +10 -0
 - data/lib/alba/layout.rb +67 -0
 - data/lib/alba/nested_attribute.rb +18 -0
 - data/lib/alba/resource.rb +240 -156
 - data/lib/alba/typed_attribute.rb +1 -1
 - data/lib/alba/version.rb +1 -1
 - data/lib/alba.rb +43 -58
 - data/logo/alba-card.png +0 -0
 - data/logo/alba-sign.png +0 -0
 - data/logo/alba-typography.png +0 -0
 - metadata +21 -11
 - data/gemfiles/all.gemfile +0 -19
 - data/lib/alba/key_transform_factory.rb +0 -33
 - data/lib/alba/many.rb +0 -21
 - data/lib/alba/one.rb +0 -21
 - data/sider.yml +0 -60
 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,14 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            
         
     | 
| 
      
 2 
     | 
    
         
            +
            ----------
         
     | 
| 
       1 
3 
     | 
    
         
             
            [](https://badge.fury.io/rb/alba)
         
     | 
| 
       2 
4 
     | 
    
         
             
            [](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
         
     | 
| 
       3 
     | 
    
         
            -
            [](https://codecov.io/gh/okuramasafumi/alba)
         
     | 
| 
       4 
6 
     | 
    
         
             
            [](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
         
     | 
| 
       5 
     | 
    
         
            -
            [](http://inch-ci.org/github/okuramasafumi/alba)
         
     | 
| 
       6 
7 
     | 
    
         
             
            
         
     | 
| 
       7 
8 
     | 
    
         
             
            
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
            # Alba
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            Alba is  
     | 
| 
      
 12 
     | 
    
         
            +
            Alba is a JSON serializer for Ruby, JRuby, and TruffleRuby.
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
            ## Discussions
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
         @@ -18,21 +19,25 @@ If you've already used Alba, please consider posting your thoughts and feelings 
     | 
|
| 
       18 
19 
     | 
    
         | 
| 
       19 
20 
     | 
    
         
             
            If you have feature requests or interesting ideas, join us with [Ideas](https://github.com/okuramasafumi/alba/discussions/categories/ideas). Let's make Alba even better, together!
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
            ## Resources
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            If you want to know more about Alba, there's a [screencast](https://hanamimastery.com/episodes/21-serialization-with-alba) created by Sebastian from [Hanami Mastery](https://hanamimastery.com/). It covers basic features of Alba and how to use it in Hanami.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       21 
26 
     | 
    
         
             
            ## Why Alba?
         
     | 
| 
       22 
27 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
            Because it's fast,  
     | 
| 
      
 28 
     | 
    
         
            +
            Because it's fast, easy and feature rich!
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
            ### Fast
         
     | 
| 
       26 
31 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
            Alba is faster than most of the alternatives. We have a [benchmark](https://github.com/okuramasafumi/alba/tree/ 
     | 
| 
      
 32 
     | 
    
         
            +
            Alba is faster than most of the alternatives. We have a [benchmark](https://github.com/okuramasafumi/alba/tree/main/benchmark).
         
     | 
| 
       28 
33 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
            ###  
     | 
| 
      
 34 
     | 
    
         
            +
            ### Easy
         
     | 
| 
       30 
35 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
            Alba  
     | 
| 
      
 36 
     | 
    
         
            +
            Alba is easy to use because there are only a few methods to remember. It's also easy to understand due to clean and short codebase. Finally it's easy to extend since it provides some methods for override to change default behavior of Alba.
         
     | 
| 
       32 
37 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
            ###  
     | 
| 
      
 38 
     | 
    
         
            +
            ### Feature rich
         
     | 
| 
       34 
39 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
            Alba is  
     | 
| 
      
 40 
     | 
    
         
            +
            While Alba's core is simple, it provides additional features when you need them, For example, Alba provides [a way to control circular associations](#circular-associations-control), [inferring resource classes, root key and associations](#inference) and [supports layouts](#layout).
         
     | 
| 
       36 
41 
     | 
    
         | 
| 
       37 
42 
     | 
    
         
             
            ## Installation
         
     | 
| 
       38 
43 
     | 
    
         | 
| 
         @@ -72,15 +77,6 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas 
     | 
|
| 
       72 
77 
     | 
    
         
             
            * Layout
         
     | 
| 
       73 
78 
     | 
    
         
             
            * No runtime dependencies
         
     | 
| 
       74 
79 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
            ## Anti features
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
            * Sorting keys
         
     | 
| 
       78 
     | 
    
         
            -
            * Class level support of parameters
         
     | 
| 
       79 
     | 
    
         
            -
            * Supporting all existing JSON encoder/decoder
         
     | 
| 
       80 
     | 
    
         
            -
            * Cache
         
     | 
| 
       81 
     | 
    
         
            -
            * [JSON:API](https://jsonapi.org) support
         
     | 
| 
       82 
     | 
    
         
            -
            * And many others
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
80 
     | 
    
         
             
            ## Usage
         
     | 
| 
       85 
81 
     | 
    
         | 
| 
       86 
82 
     | 
    
         
             
            ### Configuration
         
     | 
| 
         @@ -91,9 +87,13 @@ Alba's configuration is fairly simple. 
     | 
|
| 
       91 
87 
     | 
    
         | 
| 
       92 
88 
     | 
    
         
             
            Backend is the actual part serializing an object into JSON. Alba supports these backends.
         
     | 
| 
       93 
89 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 90 
     | 
    
         
            +
            |name|description|requires_external_gem|
         
     | 
| 
      
 91 
     | 
    
         
            +
            |--|--|--|
         
     | 
| 
      
 92 
     | 
    
         
            +
            |`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)|
         
     | 
| 
      
 93 
     | 
    
         
            +
            |`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)|
         
     | 
| 
      
 94 
     | 
    
         
            +
            |`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)|
         
     | 
| 
      
 95 
     | 
    
         
            +
            |`active_support`|For Rails compatibility|Yes|
         
     | 
| 
      
 96 
     | 
    
         
            +
            |`default`, `json`|Using `json` gem|No|
         
     | 
| 
       97 
97 
     | 
    
         | 
| 
       98 
98 
     | 
    
         
             
            You can set a backend like this:
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
         @@ -116,23 +116,21 @@ You can consider setting a backend with Symbol as a shortcut to set encoder. 
     | 
|
| 
       116 
116 
     | 
    
         
             
            You can enable inference feature using `enable_inference!` method.
         
     | 
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
            ```ruby
         
     | 
| 
       119 
     | 
    
         
            -
            Alba.enable_inference!
         
     | 
| 
      
 119 
     | 
    
         
            +
            Alba.enable_inference!(with: :active_support)
         
     | 
| 
       120 
120 
     | 
    
         
             
            ```
         
     | 
| 
       121 
121 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
            You  
     | 
| 
      
 122 
     | 
    
         
            +
            You can choose which inflector Alba uses for inference. Possible value for `with` option are:
         
     | 
| 
       123 
123 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       129 
     | 
    
         
            -
            Alba.on_error :ignore
         
     | 
| 
       130 
     | 
    
         
            -
            ```
         
     | 
| 
      
 124 
     | 
    
         
            +
            - `:active_support` for `ActiveSupport::Inflector`
         
     | 
| 
      
 125 
     | 
    
         
            +
            - `:dry` for `Dry::Inflector`
         
     | 
| 
      
 126 
     | 
    
         
            +
            - any object which responds to some methods (see [below](#custom-inflector))
         
     | 
| 
       131 
127 
     | 
    
         | 
| 
       132 
128 
     | 
    
         
             
            For the details, see [Error handling section](#error-handling)
         
     | 
| 
       133 
129 
     | 
    
         | 
| 
       134 
130 
     | 
    
         
             
            ### Simple serialization with root key
         
     | 
| 
       135 
131 
     | 
    
         | 
| 
      
 132 
     | 
    
         
            +
            You can define attributes with (yes) `attributes` macro with attribute names. If your attribute need some calculations, you can use `attribute` with block.
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
       136 
134 
     | 
    
         
             
            ```ruby
         
     | 
| 
       137 
135 
     | 
    
         
             
            class User
         
     | 
| 
       138 
136 
     | 
    
         
             
              attr_accessor :id, :name, :email, :created_at, :updated_at
         
     | 
| 
         @@ -159,11 +157,67 @@ end 
     | 
|
| 
       159 
157 
     | 
    
         | 
| 
       160 
158 
     | 
    
         
             
            user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
         
     | 
| 
       161 
159 
     | 
    
         
             
            UserResource.new(user).serialize
         
     | 
| 
       162 
     | 
    
         
            -
            # => "{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}"
         
     | 
| 
      
 160 
     | 
    
         
            +
            # => "{\"user\":{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}}"
         
     | 
| 
      
 161 
     | 
    
         
            +
            ```
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            You can define instance methods on resources so that you can use it as attribute name in `attributes`.
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 166 
     | 
    
         
            +
            # The serialization result is the same as above
         
     | 
| 
      
 167 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 168 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
              root_key :user, :users # Later is for plural
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
              attributes :id, :name, :name_with_email
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
              # Attribute methods must accept one argument for each serialized object
         
     | 
| 
      
 175 
     | 
    
         
            +
              def name_with_email(user)
         
     | 
| 
      
 176 
     | 
    
         
            +
                "#{user.name}: #{user.email}"
         
     | 
| 
      
 177 
     | 
    
         
            +
              end
         
     | 
| 
      
 178 
     | 
    
         
            +
            end
         
     | 
| 
      
 179 
     | 
    
         
            +
            ```
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
            This even works with users collection.
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 184 
     | 
    
         
            +
            user1 = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
         
     | 
| 
      
 185 
     | 
    
         
            +
            user2 = User.new(2, 'Test User', 'test@example.com')
         
     | 
| 
      
 186 
     | 
    
         
            +
            UserResource.new([user1, user2]).serialize
         
     | 
| 
      
 187 
     | 
    
         
            +
            # => "{\"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\"}]}"
         
     | 
| 
      
 188 
     | 
    
         
            +
            ```
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
            If you have a simple case where you want to change only the name, you can use the Symbol to Proc shortcut:
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 193 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 194 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
              attribute :some_other_name, &:name
         
     | 
| 
      
 197 
     | 
    
         
            +
            end
         
     | 
| 
      
 198 
     | 
    
         
            +
            ```
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
            #### Params
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
            You can pass a Hash to the resource for internal use. It can be used as "flags" to control attribute content.
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 205 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 206 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 207 
     | 
    
         
            +
              attribute :name do |user|
         
     | 
| 
      
 208 
     | 
    
         
            +
                params[:upcase] ? user.name.upcase : user.name
         
     | 
| 
      
 209 
     | 
    
         
            +
              end
         
     | 
| 
      
 210 
     | 
    
         
            +
            end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
            user = User.new(1, 'Masa', 'test@example.com')
         
     | 
| 
      
 213 
     | 
    
         
            +
            UserResource.new(user).serialize # => "{\"name\":\"foo\"}"
         
     | 
| 
      
 214 
     | 
    
         
            +
            UserResource.new(user, params: {upcase: true}).serialize # => "{\"name\":\"FOO\"}"
         
     | 
| 
       163 
215 
     | 
    
         
             
            ```
         
     | 
| 
       164 
216 
     | 
    
         | 
| 
       165 
217 
     | 
    
         
             
            ### Serialization with associations
         
     | 
| 
       166 
218 
     | 
    
         | 
| 
      
 219 
     | 
    
         
            +
            Associations can be defined using the `association` macro, which is also aliased as `one`, `many`, `has_one`, and `has_many` for convenience.
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
       167 
221 
     | 
    
         
             
            ```ruby
         
     | 
| 
       168 
222 
     | 
    
         
             
            class User
         
     | 
| 
       169 
223 
     | 
    
         
             
              attr_reader :id, :created_at, :updated_at
         
     | 
| 
         @@ -240,6 +294,208 @@ class AnotherUserResource 
     | 
|
| 
       240 
294 
     | 
    
         
             
            end
         
     | 
| 
       241 
295 
     | 
    
         
             
            ```
         
     | 
| 
       242 
296 
     | 
    
         | 
| 
      
 297 
     | 
    
         
            +
            You can "filter" association using second proc argument. This proc takes association object, `params` and initial object.
         
     | 
| 
      
 298 
     | 
    
         
            +
             
     | 
| 
      
 299 
     | 
    
         
            +
            This feature is useful when you want to modify association, such as adding `includes` or `order` to ActiveRecord relations.
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 302 
     | 
    
         
            +
            class User
         
     | 
| 
      
 303 
     | 
    
         
            +
              attr_reader :id, :banned
         
     | 
| 
      
 304 
     | 
    
         
            +
              attr_accessor :articles
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
              def initialize(id, banned = false)
         
     | 
| 
      
 307 
     | 
    
         
            +
                @id = id
         
     | 
| 
      
 308 
     | 
    
         
            +
                @banned = banned
         
     | 
| 
      
 309 
     | 
    
         
            +
                @articles = []
         
     | 
| 
      
 310 
     | 
    
         
            +
              end
         
     | 
| 
      
 311 
     | 
    
         
            +
            end
         
     | 
| 
      
 312 
     | 
    
         
            +
             
     | 
| 
      
 313 
     | 
    
         
            +
            class Article
         
     | 
| 
      
 314 
     | 
    
         
            +
              attr_accessor :id, :title, :body
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
              def initialize(id, title, body)
         
     | 
| 
      
 317 
     | 
    
         
            +
                @id = id
         
     | 
| 
      
 318 
     | 
    
         
            +
                @title = title
         
     | 
| 
      
 319 
     | 
    
         
            +
                @body = body
         
     | 
| 
      
 320 
     | 
    
         
            +
              end
         
     | 
| 
      
 321 
     | 
    
         
            +
            end
         
     | 
| 
      
 322 
     | 
    
         
            +
             
     | 
| 
      
 323 
     | 
    
         
            +
            class ArticleResource
         
     | 
| 
      
 324 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 325 
     | 
    
         
            +
             
     | 
| 
      
 326 
     | 
    
         
            +
              attributes :title
         
     | 
| 
      
 327 
     | 
    
         
            +
            end
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
      
 329 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 330 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 333 
     | 
    
         
            +
             
     | 
| 
      
 334 
     | 
    
         
            +
              # Second proc works as a filter
         
     | 
| 
      
 335 
     | 
    
         
            +
              many :articles,
         
     | 
| 
      
 336 
     | 
    
         
            +
                proc { |articles, params, user|
         
     | 
| 
      
 337 
     | 
    
         
            +
                  filter = params[:filter] || :odd?
         
     | 
| 
      
 338 
     | 
    
         
            +
                  articles.select {|a| a.id.send(filter) && !user.banned  }
         
     | 
| 
      
 339 
     | 
    
         
            +
                },
         
     | 
| 
      
 340 
     | 
    
         
            +
                resource: ArticleResource
         
     | 
| 
      
 341 
     | 
    
         
            +
            end
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
      
 343 
     | 
    
         
            +
            user = User.new(1)
         
     | 
| 
      
 344 
     | 
    
         
            +
            article1 = Article.new(1, 'Hello World!', 'Hello World!!!')
         
     | 
| 
      
 345 
     | 
    
         
            +
            user.articles << article1
         
     | 
| 
      
 346 
     | 
    
         
            +
            article2 = Article.new(2, 'Super nice', 'Really nice!')
         
     | 
| 
      
 347 
     | 
    
         
            +
            user.articles << article2
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 350 
     | 
    
         
            +
            # => '{"id":1,"articles":[{"title":"Hello World!"}]}'
         
     | 
| 
      
 351 
     | 
    
         
            +
            UserResource.new(user, params: {filter: :even?}).serialize
         
     | 
| 
      
 352 
     | 
    
         
            +
            # => '{"id":1,"articles":[{"title":"Super nice"}]}'
         
     | 
| 
      
 353 
     | 
    
         
            +
            ```
         
     | 
| 
      
 354 
     | 
    
         
            +
             
     | 
| 
      
 355 
     | 
    
         
            +
            You can change a key for association with `key` option.
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 358 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 359 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 360 
     | 
    
         
            +
             
     | 
| 
      
 361 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 362 
     | 
    
         
            +
             
     | 
| 
      
 363 
     | 
    
         
            +
              many :articles,
         
     | 
| 
      
 364 
     | 
    
         
            +
                key: 'my_articles', # Set key here
         
     | 
| 
      
 365 
     | 
    
         
            +
                resource: ArticleResource
         
     | 
| 
      
 366 
     | 
    
         
            +
            end
         
     | 
| 
      
 367 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 368 
     | 
    
         
            +
            # => '{"id":1,"my_articles":[{"title":"Hello World!"}]}'
         
     | 
| 
      
 369 
     | 
    
         
            +
            ```
         
     | 
| 
      
 370 
     | 
    
         
            +
             
     | 
| 
      
 371 
     | 
    
         
            +
            You can omit resource option if you enable Alba's inference feature.
         
     | 
| 
      
 372 
     | 
    
         
            +
             
     | 
| 
      
 373 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 374 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 375 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 376 
     | 
    
         
            +
             
     | 
| 
      
 377 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 378 
     | 
    
         
            +
             
     | 
| 
      
 379 
     | 
    
         
            +
              many :articles # Using `ArticleResource`
         
     | 
| 
      
 380 
     | 
    
         
            +
            end
         
     | 
| 
      
 381 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 382 
     | 
    
         
            +
            # => '{"id":1,"my_articles":[{"title":"Hello World!"}]}'
         
     | 
| 
      
 383 
     | 
    
         
            +
            ```
         
     | 
| 
      
 384 
     | 
    
         
            +
             
     | 
| 
      
 385 
     | 
    
         
            +
            If you need complex logic to determine what resource to use for association, you can use a Proc for resource option.
         
     | 
| 
      
 386 
     | 
    
         
            +
             
     | 
| 
      
 387 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 388 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 389 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
      
 391 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 392 
     | 
    
         
            +
             
     | 
| 
      
 393 
     | 
    
         
            +
              many :articles, ->(article) { article.with_comment? ? ArticleWithCommentResource : ArticleResource }
         
     | 
| 
      
 394 
     | 
    
         
            +
            end
         
     | 
| 
      
 395 
     | 
    
         
            +
            ```
         
     | 
| 
      
 396 
     | 
    
         
            +
             
     | 
| 
      
 397 
     | 
    
         
            +
            Note that using a Proc slows down serialization if there are too `many` associated objects.
         
     | 
| 
      
 398 
     | 
    
         
            +
             
     | 
| 
      
 399 
     | 
    
         
            +
            #### Params override
         
     | 
| 
      
 400 
     | 
    
         
            +
             
     | 
| 
      
 401 
     | 
    
         
            +
            Associations can override params. This is useful when associations are deeply nested.
         
     | 
| 
      
 402 
     | 
    
         
            +
             
     | 
| 
      
 403 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 404 
     | 
    
         
            +
            class BazResource
         
     | 
| 
      
 405 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 406 
     | 
    
         
            +
             
     | 
| 
      
 407 
     | 
    
         
            +
              attributes :data
         
     | 
| 
      
 408 
     | 
    
         
            +
              attributes :secret, if: proc { params[:expose_secret] }
         
     | 
| 
      
 409 
     | 
    
         
            +
            end
         
     | 
| 
      
 410 
     | 
    
         
            +
             
     | 
| 
      
 411 
     | 
    
         
            +
            class BarResource
         
     | 
| 
      
 412 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 413 
     | 
    
         
            +
             
     | 
| 
      
 414 
     | 
    
         
            +
              one :baz, resource: BazResource
         
     | 
| 
      
 415 
     | 
    
         
            +
            end
         
     | 
| 
      
 416 
     | 
    
         
            +
             
     | 
| 
      
 417 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 418 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 419 
     | 
    
         
            +
             
     | 
| 
      
 420 
     | 
    
         
            +
              root_key :foo
         
     | 
| 
      
 421 
     | 
    
         
            +
             
     | 
| 
      
 422 
     | 
    
         
            +
              one :bar, resource: BarResource
         
     | 
| 
      
 423 
     | 
    
         
            +
            end
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
            class FooResourceWithParamsOverride
         
     | 
| 
      
 426 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 427 
     | 
    
         
            +
             
     | 
| 
      
 428 
     | 
    
         
            +
              root_key :foo
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
      
 430 
     | 
    
         
            +
              one :bar, resource: BarResource, params: { expose_secret: false }
         
     | 
| 
      
 431 
     | 
    
         
            +
            end
         
     | 
| 
      
 432 
     | 
    
         
            +
             
     | 
| 
      
 433 
     | 
    
         
            +
            Baz = Struct.new(:data, :secret)
         
     | 
| 
      
 434 
     | 
    
         
            +
            Bar = Struct.new(:baz)
         
     | 
| 
      
 435 
     | 
    
         
            +
            Foo = Struct.new(:bar)
         
     | 
| 
      
 436 
     | 
    
         
            +
             
     | 
| 
      
 437 
     | 
    
         
            +
            foo = Foo.new(Bar.new(Baz.new(1, 'secret')))
         
     | 
| 
      
 438 
     | 
    
         
            +
            FooResource.new(foo, params: {expose_secret: true}).serialize # => '{"foo":{"bar":{"baz":{"data":1,"secret":"secret"}}}}'
         
     | 
| 
      
 439 
     | 
    
         
            +
            FooResourceWithParamsOverride.new(foo, params: {expose_secret: true}).serialize # => '{"foo":{"bar":{"baz":{"data":1}}}}'
         
     | 
| 
      
 440 
     | 
    
         
            +
            ```
         
     | 
| 
      
 441 
     | 
    
         
            +
             
     | 
| 
      
 442 
     | 
    
         
            +
            ### Nested Attribute
         
     | 
| 
      
 443 
     | 
    
         
            +
             
     | 
| 
      
 444 
     | 
    
         
            +
            Alba supports nested attributes that makes it easy to build complex data structure from single object.
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
            In order to define nested attributes, you can use `nested` or `nested_attribute` (alias of `nested`).
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 449 
     | 
    
         
            +
            class User
         
     | 
| 
      
 450 
     | 
    
         
            +
              attr_accessor :id, :name, :email, :city, :zipcode
         
     | 
| 
      
 451 
     | 
    
         
            +
             
     | 
| 
      
 452 
     | 
    
         
            +
              def initialize(id, name, email, city, zipcode)
         
     | 
| 
      
 453 
     | 
    
         
            +
                @id = id
         
     | 
| 
      
 454 
     | 
    
         
            +
                @name = name
         
     | 
| 
      
 455 
     | 
    
         
            +
                @email = email
         
     | 
| 
      
 456 
     | 
    
         
            +
                @city = city
         
     | 
| 
      
 457 
     | 
    
         
            +
                @zipcode = zipcode
         
     | 
| 
      
 458 
     | 
    
         
            +
              end
         
     | 
| 
      
 459 
     | 
    
         
            +
            end
         
     | 
| 
      
 460 
     | 
    
         
            +
             
     | 
| 
      
 461 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 462 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 463 
     | 
    
         
            +
             
     | 
| 
      
 464 
     | 
    
         
            +
              root_key :user
         
     | 
| 
      
 465 
     | 
    
         
            +
             
     | 
| 
      
 466 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 467 
     | 
    
         
            +
             
     | 
| 
      
 468 
     | 
    
         
            +
              nested_attribute :address do
         
     | 
| 
      
 469 
     | 
    
         
            +
                attributes :city, :zipcode
         
     | 
| 
      
 470 
     | 
    
         
            +
              end
         
     | 
| 
      
 471 
     | 
    
         
            +
            end
         
     | 
| 
      
 472 
     | 
    
         
            +
             
     | 
| 
      
 473 
     | 
    
         
            +
            user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com', 'Tokyo', '0000000')
         
     | 
| 
      
 474 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 475 
     | 
    
         
            +
            # => '{"user":{"id":1,"address":{"city":"Tokyo","zipcode":"0000000"}}}'
         
     | 
| 
      
 476 
     | 
    
         
            +
            ```
         
     | 
| 
      
 477 
     | 
    
         
            +
             
     | 
| 
      
 478 
     | 
    
         
            +
            Nested attributes can be nested deeply.
         
     | 
| 
      
 479 
     | 
    
         
            +
             
     | 
| 
      
 480 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 481 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 482 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 483 
     | 
    
         
            +
             
     | 
| 
      
 484 
     | 
    
         
            +
              root_key :foo
         
     | 
| 
      
 485 
     | 
    
         
            +
             
     | 
| 
      
 486 
     | 
    
         
            +
              nested :bar do
         
     | 
| 
      
 487 
     | 
    
         
            +
                nested :baz do
         
     | 
| 
      
 488 
     | 
    
         
            +
                  attribute :deep do
         
     | 
| 
      
 489 
     | 
    
         
            +
                    42
         
     | 
| 
      
 490 
     | 
    
         
            +
                  end
         
     | 
| 
      
 491 
     | 
    
         
            +
                end
         
     | 
| 
      
 492 
     | 
    
         
            +
              end
         
     | 
| 
      
 493 
     | 
    
         
            +
            end
         
     | 
| 
      
 494 
     | 
    
         
            +
             
     | 
| 
      
 495 
     | 
    
         
            +
            FooResource.new(nil).serialize
         
     | 
| 
      
 496 
     | 
    
         
            +
            # => '{"foo":{"bar":{"baz":{"deep":42}}}}'
         
     | 
| 
      
 497 
     | 
    
         
            +
            ```
         
     | 
| 
      
 498 
     | 
    
         
            +
             
     | 
| 
       243 
499 
     | 
    
         
             
            ### Inline definition with `Alba.serialize`
         
     | 
| 
       244 
500 
     | 
    
         | 
| 
       245 
501 
     | 
    
         
             
            `Alba.serialize` method is a shortcut to define everything inline.
         
     | 
| 
         @@ -263,9 +519,61 @@ Alba.serialize(something) 
     | 
|
| 
       263 
519 
     | 
    
         | 
| 
       264 
520 
     | 
    
         
             
            Although this might be useful sometimes, it's generally recommended to define a class for Resource.
         
     | 
| 
       265 
521 
     | 
    
         | 
| 
       266 
     | 
    
         
            -
            ###  
     | 
| 
      
 522 
     | 
    
         
            +
            ### Serializable Hash
         
     | 
| 
      
 523 
     | 
    
         
            +
             
     | 
| 
      
 524 
     | 
    
         
            +
            Instead of serializing to JSON, you can also output a Hash by calling `serializable_hash` or the `to_h` alias. Note also that the `serialize` method is aliased as `to_json`.
         
     | 
| 
      
 525 
     | 
    
         
            +
             
     | 
| 
      
 526 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 527 
     | 
    
         
            +
            # These are equivalent and will return serialized JSON
         
     | 
| 
      
 528 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 529 
     | 
    
         
            +
            UserResource.new(user).to_json
         
     | 
| 
      
 530 
     | 
    
         
            +
             
     | 
| 
      
 531 
     | 
    
         
            +
            # These are equivalent and will return a Hash
         
     | 
| 
      
 532 
     | 
    
         
            +
            UserResource.new(user).serializable_hash
         
     | 
| 
      
 533 
     | 
    
         
            +
            UserResource.new(user).to_h
         
     | 
| 
      
 534 
     | 
    
         
            +
            ```
         
     | 
| 
      
 535 
     | 
    
         
            +
             
     | 
| 
      
 536 
     | 
    
         
            +
            If you want a Hash that corresponds to a JSON String returned by `serialize` method, you can use `as_json`.
         
     | 
| 
      
 537 
     | 
    
         
            +
             
     | 
| 
      
 538 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 539 
     | 
    
         
            +
            # These are equivalent and will return the same result
         
     | 
| 
      
 540 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 541 
     | 
    
         
            +
            UserResource.new(user).to_json
         
     | 
| 
      
 542 
     | 
    
         
            +
            JSON.generate(UserResource.new(user).as_json)
         
     | 
| 
      
 543 
     | 
    
         
            +
            ```
         
     | 
| 
      
 544 
     | 
    
         
            +
             
     | 
| 
      
 545 
     | 
    
         
            +
            ### Inheritance
         
     | 
| 
      
 546 
     | 
    
         
            +
             
     | 
| 
      
 547 
     | 
    
         
            +
            When you include `Alba::Resource` in your class, it's just a class so you can define any class that inherits from it. You can add new attributes to inherited class like below:
         
     | 
| 
      
 548 
     | 
    
         
            +
             
     | 
| 
      
 549 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 550 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 551 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 552 
     | 
    
         
            +
             
     | 
| 
      
 553 
     | 
    
         
            +
              root_key :foo
         
     | 
| 
      
 554 
     | 
    
         
            +
             
     | 
| 
      
 555 
     | 
    
         
            +
              attributes :bar
         
     | 
| 
      
 556 
     | 
    
         
            +
            end
         
     | 
| 
      
 557 
     | 
    
         
            +
             
     | 
| 
      
 558 
     | 
    
         
            +
            class ExtendedFooResource < FooResource
         
     | 
| 
      
 559 
     | 
    
         
            +
              root_key :foofoo
         
     | 
| 
      
 560 
     | 
    
         
            +
             
     | 
| 
      
 561 
     | 
    
         
            +
              attributes :baz
         
     | 
| 
      
 562 
     | 
    
         
            +
            end
         
     | 
| 
      
 563 
     | 
    
         
            +
             
     | 
| 
      
 564 
     | 
    
         
            +
            Foo = Struct.new(:bar, :baz)
         
     | 
| 
      
 565 
     | 
    
         
            +
            foo = Foo.new(1, 2)
         
     | 
| 
      
 566 
     | 
    
         
            +
            FooResource.new(foo).serialize # => '{"foo":{"bar":1}}'
         
     | 
| 
      
 567 
     | 
    
         
            +
            ExtendedFooResource.new(foo).serialize # => '{"foo":{"bar":1,"baz":2}}'
         
     | 
| 
      
 568 
     | 
    
         
            +
            ```
         
     | 
| 
      
 569 
     | 
    
         
            +
             
     | 
| 
      
 570 
     | 
    
         
            +
            In this example we add `baz` attribute and change `root_key`. This way, you can extend existing resource classes just like normal OOP. Don't forget that when your inheritance structure is too deep it'll become difficult to modify existing classes.
         
     | 
| 
      
 571 
     | 
    
         
            +
             
     | 
| 
      
 572 
     | 
    
         
            +
            ### Filtering attributes
         
     | 
| 
       267 
573 
     | 
    
         | 
| 
       268 
     | 
    
         
            -
            You can  
     | 
| 
      
 574 
     | 
    
         
            +
            You can filter out certain attributes by overriding `attributes` instance method. This is useful when you want to customize existing resource with inheritance.
         
     | 
| 
      
 575 
     | 
    
         
            +
             
     | 
| 
      
 576 
     | 
    
         
            +
            You can access raw attributes via `super` call. It returns a Hash whose keys are the name of the attribute and whose values are the body. Usually you need only keys to filter out, like below.
         
     | 
| 
       269 
577 
     | 
    
         | 
| 
       270 
578 
     | 
    
         
             
            ```ruby
         
     | 
| 
       271 
579 
     | 
    
         
             
            class Foo
         
     | 
| 
         @@ -285,7 +593,9 @@ class GenericFooResource 
     | 
|
| 
       285 
593 
     | 
    
         
             
            end
         
     | 
| 
       286 
594 
     | 
    
         | 
| 
       287 
595 
     | 
    
         
             
            class RestrictedFooResource < GenericFooResource
         
     | 
| 
       288 
     | 
    
         
            -
               
     | 
| 
      
 596 
     | 
    
         
            +
              def attributes
         
     | 
| 
      
 597 
     | 
    
         
            +
                super.select { |key, _| key.to_sym == :name }
         
     | 
| 
      
 598 
     | 
    
         
            +
              end
         
     | 
| 
       289 
599 
     | 
    
         
             
            end
         
     | 
| 
       290 
600 
     | 
    
         | 
| 
       291 
601 
     | 
    
         
             
            RestrictedFooResource.new(foo).serialize
         
     | 
| 
         @@ -324,14 +634,24 @@ UserResourceCamel.new(user).serialize 
     | 
|
| 
       324 
634 
     | 
    
         
             
            # => '{"id":1,"firstName":"Masafumi","lastName":"Okura"}'
         
     | 
| 
       325 
635 
     | 
    
         
             
            ```
         
     | 
| 
       326 
636 
     | 
    
         | 
| 
      
 637 
     | 
    
         
            +
            Possible values for `transform_keys` argument are:
         
     | 
| 
      
 638 
     | 
    
         
            +
             
     | 
| 
      
 639 
     | 
    
         
            +
            * `:camel` for CamelCase
         
     | 
| 
      
 640 
     | 
    
         
            +
            * `:lower_camel` for lowerCamelCase
         
     | 
| 
      
 641 
     | 
    
         
            +
            * `:dash` for dash-case
         
     | 
| 
      
 642 
     | 
    
         
            +
            * `:snake` for snake_case
         
     | 
| 
      
 643 
     | 
    
         
            +
            * `:none` for not transforming keys
         
     | 
| 
      
 644 
     | 
    
         
            +
             
     | 
| 
      
 645 
     | 
    
         
            +
            #### Root key transformation
         
     | 
| 
      
 646 
     | 
    
         
            +
             
     | 
| 
       327 
647 
     | 
    
         
             
            You can also transform root key when:
         
     | 
| 
       328 
648 
     | 
    
         | 
| 
       329 
649 
     | 
    
         
             
            * `Alba.enable_inference!` is called
         
     | 
| 
       330 
650 
     | 
    
         
             
            * `root_key!` is called in Resource class
         
     | 
| 
       331 
     | 
    
         
            -
            * `root` option of `transform_keys` is set to true 
     | 
| 
      
 651 
     | 
    
         
            +
            * `root` option of `transform_keys` is set to true
         
     | 
| 
       332 
652 
     | 
    
         | 
| 
       333 
653 
     | 
    
         
             
            ```ruby
         
     | 
| 
       334 
     | 
    
         
            -
            Alba.enable_inference!
         
     | 
| 
      
 654 
     | 
    
         
            +
            Alba.enable_inference!(with: :active_support) # with :dry also works
         
     | 
| 
       335 
655 
     | 
    
         | 
| 
       336 
656 
     | 
    
         
             
            class BankAccount
         
     | 
| 
       337 
657 
     | 
    
         
             
              attr_reader :account_number
         
     | 
| 
         @@ -357,75 +677,81 @@ BankAccountResource.new(bank_account).serialize 
     | 
|
| 
       357 
677 
     | 
    
         | 
| 
       358 
678 
     | 
    
         
             
            This behavior to transform root key will become default at version 2.
         
     | 
| 
       359 
679 
     | 
    
         | 
| 
       360 
     | 
    
         
            -
             
     | 
| 
      
 680 
     | 
    
         
            +
            #### Key transformation cascading
         
     | 
| 
       361 
681 
     | 
    
         | 
| 
       362 
     | 
    
         
            -
             
     | 
| 
      
 682 
     | 
    
         
            +
            When you use `transform_keys` with inline association, it automatically applies the same transformation type to those inline association.
         
     | 
| 
      
 683 
     | 
    
         
            +
             
     | 
| 
      
 684 
     | 
    
         
            +
            This is the default behavior from version 2, but you can do the same thing with adding `transform_keys` to each association.
         
     | 
| 
      
 685 
     | 
    
         
            +
             
     | 
| 
      
 686 
     | 
    
         
            +
            You can also turn it off by setting `cascade: false` option to `transform_keys`.
         
     | 
| 
       363 
687 
     | 
    
         | 
| 
       364 
     | 
    
         
            -
            A custom inflector can be plugged in as follows...
         
     | 
| 
       365 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       366 
     | 
    
         
            -
            Alba.inflector = MyCustomInflector
         
     | 
| 
       367 
     | 
    
         
            -
            ```
         
     | 
| 
       368 
     | 
    
         
            -
            ...and has to implement following interface (the parameter `key` is of type `String`):
         
     | 
| 
       369 
688 
     | 
    
         
             
            ```ruby
         
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
     | 
    
         
            -
               
     | 
| 
       372 
     | 
    
         
            -
             
     | 
| 
      
 689 
     | 
    
         
            +
            class User
         
     | 
| 
      
 690 
     | 
    
         
            +
              attr_reader :id, :first_name, :last_name
         
     | 
| 
      
 691 
     | 
    
         
            +
             
     | 
| 
      
 692 
     | 
    
         
            +
              def initialize(id, first_name, last_name)
         
     | 
| 
      
 693 
     | 
    
         
            +
                @id = id
         
     | 
| 
      
 694 
     | 
    
         
            +
                @first_name = first_name
         
     | 
| 
      
 695 
     | 
    
         
            +
                @last_name = last_name
         
     | 
| 
      
 696 
     | 
    
         
            +
                @bank_account = BankAccount.new(1234)
         
     | 
| 
       373 
697 
     | 
    
         
             
              end
         
     | 
| 
      
 698 
     | 
    
         
            +
            end
         
     | 
| 
       374 
699 
     | 
    
         | 
| 
       375 
     | 
    
         
            -
             
     | 
| 
       376 
     | 
    
         
            -
             
     | 
| 
      
 700 
     | 
    
         
            +
            class BankAccount
         
     | 
| 
      
 701 
     | 
    
         
            +
              attr_reader :account_number
         
     | 
| 
      
 702 
     | 
    
         
            +
             
     | 
| 
      
 703 
     | 
    
         
            +
              def initialize(account_number)
         
     | 
| 
      
 704 
     | 
    
         
            +
                @account_number = account_number
         
     | 
| 
       377 
705 
     | 
    
         
             
              end
         
     | 
| 
      
 706 
     | 
    
         
            +
            end
         
     | 
| 
       378 
707 
     | 
    
         | 
| 
       379 
     | 
    
         
            -
             
     | 
| 
       380 
     | 
    
         
            -
             
     | 
| 
      
 708 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 709 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 710 
     | 
    
         
            +
             
     | 
| 
      
 711 
     | 
    
         
            +
              attributes :id, :first_name, :last_name
         
     | 
| 
      
 712 
     | 
    
         
            +
             
     | 
| 
      
 713 
     | 
    
         
            +
              transform_keys :lower_camel # Default is cascade: true
         
     | 
| 
      
 714 
     | 
    
         
            +
             
     | 
| 
      
 715 
     | 
    
         
            +
              one :bank_account do
         
     | 
| 
      
 716 
     | 
    
         
            +
                attributes :account_number
         
     | 
| 
       381 
717 
     | 
    
         
             
              end
         
     | 
| 
       382 
718 
     | 
    
         
             
            end
         
     | 
| 
       383 
719 
     | 
    
         | 
| 
       384 
     | 
    
         
            -
             
     | 
| 
       385 
     | 
    
         
            -
             
     | 
| 
       386 
     | 
    
         
            -
             
     | 
| 
       387 
     | 
    
         
            -
            Alba.inflector = Dry::Inflector.new
         
     | 
| 
      
 720 
     | 
    
         
            +
            user = User.new(1, 'Masafumi', 'Okura')
         
     | 
| 
      
 721 
     | 
    
         
            +
            UserResource.new(user).serialize
         
     | 
| 
      
 722 
     | 
    
         
            +
            # => '{"id":1,"firstName":"Masafumi","lastName":"Okura","bankAccount":{"accountNumber":1234}}'
         
     | 
| 
       388 
723 
     | 
    
         
             
            ```
         
     | 
| 
       389 
724 
     | 
    
         | 
| 
       390 
     | 
    
         
            -
             
     | 
| 
      
 725 
     | 
    
         
            +
            #### Custom inflector
         
     | 
| 
       391 
726 
     | 
    
         | 
| 
       392 
     | 
    
         
            -
             
     | 
| 
      
 727 
     | 
    
         
            +
            A custom inflector can be plugged in as follows.
         
     | 
| 
       393 
728 
     | 
    
         | 
| 
       394 
729 
     | 
    
         
             
            ```ruby
         
     | 
| 
       395 
     | 
    
         
            -
             
     | 
| 
       396 
     | 
    
         
            -
               
     | 
| 
      
 730 
     | 
    
         
            +
            module CustomInflector
         
     | 
| 
      
 731 
     | 
    
         
            +
              module_function
         
     | 
| 
       397 
732 
     | 
    
         | 
| 
       398 
     | 
    
         
            -
              def  
     | 
| 
       399 
     | 
    
         
            -
                @id = id
         
     | 
| 
       400 
     | 
    
         
            -
                @name = name
         
     | 
| 
       401 
     | 
    
         
            -
                @email = email
         
     | 
| 
      
 733 
     | 
    
         
            +
              def camelize(string)
         
     | 
| 
       402 
734 
     | 
    
         
             
              end
         
     | 
| 
       403 
     | 
    
         
            -
            end
         
     | 
| 
       404 
735 
     | 
    
         | 
| 
       405 
     | 
    
         
            -
             
     | 
| 
       406 
     | 
    
         
            -
               
     | 
| 
      
 736 
     | 
    
         
            +
              def camelize_lower(string)
         
     | 
| 
      
 737 
     | 
    
         
            +
              end
         
     | 
| 
       407 
738 
     | 
    
         | 
| 
       408 
     | 
    
         
            -
               
     | 
| 
      
 739 
     | 
    
         
            +
              def dasherize(string)
         
     | 
| 
      
 740 
     | 
    
         
            +
              end
         
     | 
| 
       409 
741 
     | 
    
         | 
| 
       410 
     | 
    
         
            -
               
     | 
| 
      
 742 
     | 
    
         
            +
              def underscore(string)
         
     | 
| 
      
 743 
     | 
    
         
            +
              end
         
     | 
| 
       411 
744 
     | 
    
         | 
| 
       412 
     | 
    
         
            -
               
     | 
| 
       413 
     | 
    
         
            -
              def converter
         
     | 
| 
       414 
     | 
    
         
            -
                super >> proc { |hash| hash.compact }
         
     | 
| 
      
 745 
     | 
    
         
            +
              def classify(string)
         
     | 
| 
       415 
746 
     | 
    
         
             
              end
         
     | 
| 
       416 
747 
     | 
    
         
             
            end
         
     | 
| 
       417 
748 
     | 
    
         | 
| 
       418 
     | 
    
         
            -
             
     | 
| 
       419 
     | 
    
         
            -
            UserResource.new(user).serialize # => '{"id":1}'
         
     | 
| 
      
 749 
     | 
    
         
            +
            Alba.enable_inference!(with: CustomInflector)
         
     | 
| 
       420 
750 
     | 
    
         
             
            ```
         
     | 
| 
       421 
751 
     | 
    
         | 
| 
       422 
     | 
    
         
            -
            The key part is the use of `Proc#>>` since `Alba::Resource#converter` returns a `Proc` which contains the basic logic and it's impossible to change its behavior by just overriding the method.
         
     | 
| 
       423 
     | 
    
         
            -
             
     | 
| 
       424 
     | 
    
         
            -
            It's not recommended to swap the whole conversion logic. It's recommended to always call `super` when you override `converter`.
         
     | 
| 
       425 
     | 
    
         
            -
             
     | 
| 
       426 
752 
     | 
    
         
             
            ### Conditional attributes
         
     | 
| 
       427 
753 
     | 
    
         | 
| 
       428 
     | 
    
         
            -
            Filtering attributes with overriding ` 
     | 
| 
      
 754 
     | 
    
         
            +
            Filtering attributes with overriding `attributes` works well for simple cases. However, It's cumbersome when we want to filter various attributes based on different conditions for keys.
         
     | 
| 
       429 
755 
     | 
    
         | 
| 
       430 
756 
     | 
    
         
             
            In these cases, conditional attributes works well. We can pass `if` option to `attributes`, `attribute`, `one` and `many`. Below is an example for the same effect as [filtering attributes section](#filtering-attributes).
         
     | 
| 
       431 
757 
     | 
    
         | 
| 
         @@ -469,7 +795,7 @@ We believe this is clearer than using some (not implemented yet) DSL such as `de 
     | 
|
| 
       469 
795 
     | 
    
         
             
            After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
         
     | 
| 
       470 
796 
     | 
    
         | 
| 
       471 
797 
     | 
    
         
             
            ```ruby
         
     | 
| 
       472 
     | 
    
         
            -
            Alba.enable_inference!
         
     | 
| 
      
 798 
     | 
    
         
            +
            Alba.enable_inference!(with: :active_support) # with :dry also works
         
     | 
| 
       473 
799 
     | 
    
         | 
| 
       474 
800 
     | 
    
         
             
            class User
         
     | 
| 
       475 
801 
     | 
    
         
             
              attr_reader :id
         
     | 
| 
         @@ -517,8 +843,6 @@ This resource automatically sets its root key to either "users" or "user", depen 
     | 
|
| 
       517 
843 
     | 
    
         | 
| 
       518 
844 
     | 
    
         
             
            Also, you don't have to specify which resource class to use with `many`. Alba infers it from association name.
         
     | 
| 
       519 
845 
     | 
    
         | 
| 
       520 
     | 
    
         
            -
            Note that to enable this feature you must install `ActiveSupport` gem.
         
     | 
| 
       521 
     | 
    
         
            -
             
     | 
| 
       522 
846 
     | 
    
         
             
            ### Error handling
         
     | 
| 
       523 
847 
     | 
    
         | 
| 
       524 
848 
     | 
    
         
             
            You can set error handler globally or per resource using `on_error`.
         
     | 
| 
         @@ -562,12 +886,14 @@ There are four possible arguments `on_error` method accepts. 
     | 
|
| 
       562 
886 
     | 
    
         
             
            The block receives five arguments, `error`, `object`, `key`, `attribute` and `resource class` and must return a two-element array. Below is an example.
         
     | 
| 
       563 
887 
     | 
    
         | 
| 
       564 
888 
     | 
    
         
             
            ```ruby
         
     | 
| 
       565 
     | 
    
         
            -
             
     | 
| 
       566 
     | 
    
         
            -
            Alba 
     | 
| 
       567 
     | 
    
         
            -
               
     | 
| 
       568 
     | 
    
         
            -
                 
     | 
| 
       569 
     | 
    
         
            -
             
     | 
| 
       570 
     | 
    
         
            -
                 
     | 
| 
      
 889 
     | 
    
         
            +
            class ExampleResource
         
     | 
| 
      
 890 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 891 
     | 
    
         
            +
              on_error do |error, object, key, attribute, resource_class|
         
     | 
| 
      
 892 
     | 
    
         
            +
                if resource_class == MyResource
         
     | 
| 
      
 893 
     | 
    
         
            +
                  ['error_fallback', object.error_fallback]
         
     | 
| 
      
 894 
     | 
    
         
            +
                else
         
     | 
| 
      
 895 
     | 
    
         
            +
                  [key, error.message]
         
     | 
| 
      
 896 
     | 
    
         
            +
                end
         
     | 
| 
       571 
897 
     | 
    
         
             
              end
         
     | 
| 
       572 
898 
     | 
    
         
             
            end
         
     | 
| 
       573 
899 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -624,43 +950,6 @@ UserResource.new(User.new(1)).serialize 
     | 
|
| 
       624 
950 
     | 
    
         
             
            # => '{"user":{"id":1,"name":"User1","age":20}}'
         
     | 
| 
       625 
951 
     | 
    
         
             
            ```
         
     | 
| 
       626 
952 
     | 
    
         | 
| 
       627 
     | 
    
         
            -
            You can also set global nil handler.
         
     | 
| 
       628 
     | 
    
         
            -
             
     | 
| 
       629 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       630 
     | 
    
         
            -
            Alba.on_nil { 'default name' }
         
     | 
| 
       631 
     | 
    
         
            -
             
     | 
| 
       632 
     | 
    
         
            -
            class Foo
         
     | 
| 
       633 
     | 
    
         
            -
              attr_reader :name
         
     | 
| 
       634 
     | 
    
         
            -
              def initialize(name)
         
     | 
| 
       635 
     | 
    
         
            -
                @name = name
         
     | 
| 
       636 
     | 
    
         
            -
              end
         
     | 
| 
       637 
     | 
    
         
            -
            end
         
     | 
| 
       638 
     | 
    
         
            -
             
     | 
| 
       639 
     | 
    
         
            -
            class FooResource
         
     | 
| 
       640 
     | 
    
         
            -
              include Alba::Resource
         
     | 
| 
       641 
     | 
    
         
            -
             
     | 
| 
       642 
     | 
    
         
            -
              key :foo
         
     | 
| 
       643 
     | 
    
         
            -
             
     | 
| 
       644 
     | 
    
         
            -
              attributes :name
         
     | 
| 
       645 
     | 
    
         
            -
            end
         
     | 
| 
       646 
     | 
    
         
            -
             
     | 
| 
       647 
     | 
    
         
            -
            FooResource.new(Foo.new).serialize
         
     | 
| 
       648 
     | 
    
         
            -
            # => '{"foo":{"name":"default name"}}'
         
     | 
| 
       649 
     | 
    
         
            -
             
     | 
| 
       650 
     | 
    
         
            -
            class FooResource2
         
     | 
| 
       651 
     | 
    
         
            -
              include Alba::Resource
         
     | 
| 
       652 
     | 
    
         
            -
             
     | 
| 
       653 
     | 
    
         
            -
              key :foo
         
     | 
| 
       654 
     | 
    
         
            -
             
     | 
| 
       655 
     | 
    
         
            -
              on_nil { '' } # This is applied instead of global handler
         
     | 
| 
       656 
     | 
    
         
            -
             
     | 
| 
       657 
     | 
    
         
            -
              attributes :name
         
     | 
| 
       658 
     | 
    
         
            -
            end
         
     | 
| 
       659 
     | 
    
         
            -
             
     | 
| 
       660 
     | 
    
         
            -
            FooResource2.new(Foo.new).serialize
         
     | 
| 
       661 
     | 
    
         
            -
            # => '{"foo":{"name":""}}'
         
     | 
| 
       662 
     | 
    
         
            -
            ```
         
     | 
| 
       663 
     | 
    
         
            -
             
     | 
| 
       664 
953 
     | 
    
         
             
            ### Metadata
         
     | 
| 
       665 
954 
     | 
    
         | 
| 
       666 
955 
     | 
    
         
             
            You can set a metadata with `meta` DSL or `meta` option.
         
     | 
| 
         @@ -715,7 +1004,7 @@ Note that setting root key is required when setting a metadata. 
     | 
|
| 
       715 
1004 
     | 
    
         | 
| 
       716 
1005 
     | 
    
         
             
            You can control circular associations with `within` option. `within` option is a nested Hash such as `{book: {authors: books}}`. In this example, Alba serializes a book's authors' books. This means you can reference `BookResource` from `AuthorResource` and vice versa. This is really powerful when you have a complex data structure and serialize certain parts of it.
         
     | 
| 
       717 
1006 
     | 
    
         | 
| 
       718 
     | 
    
         
            -
            For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/ 
     | 
| 
      
 1007 
     | 
    
         
            +
            For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/main/test/usecases/circular_association_test.rb)
         
     | 
| 
       719 
1008 
     | 
    
         | 
| 
       720 
1009 
     | 
    
         
             
            ### Experimental support of types
         
     | 
| 
       721 
1010 
     | 
    
         | 
| 
         @@ -758,6 +1047,37 @@ UserResource.new(user).serialize 
     | 
|
| 
       758 
1047 
     | 
    
         | 
| 
       759 
1048 
     | 
    
         
             
            Note that this feature is experimental and interfaces are subject to change.
         
     | 
| 
       760 
1049 
     | 
    
         | 
| 
      
 1050 
     | 
    
         
            +
            ### Collection serialization into Hash
         
     | 
| 
      
 1051 
     | 
    
         
            +
             
     | 
| 
      
 1052 
     | 
    
         
            +
            Sometimes we want to serialize a collection into a Hash, not an Array. It's possible with Alba.
         
     | 
| 
      
 1053 
     | 
    
         
            +
             
     | 
| 
      
 1054 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1055 
     | 
    
         
            +
            class User
         
     | 
| 
      
 1056 
     | 
    
         
            +
              attr_reader :id, :name
         
     | 
| 
      
 1057 
     | 
    
         
            +
              def initialize(id, name)
         
     | 
| 
      
 1058 
     | 
    
         
            +
                @id, @name = id, name
         
     | 
| 
      
 1059 
     | 
    
         
            +
              end
         
     | 
| 
      
 1060 
     | 
    
         
            +
            end
         
     | 
| 
      
 1061 
     | 
    
         
            +
             
     | 
| 
      
 1062 
     | 
    
         
            +
            class UserResource
         
     | 
| 
      
 1063 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1064 
     | 
    
         
            +
             
     | 
| 
      
 1065 
     | 
    
         
            +
              collection_key :id # This line is important
         
     | 
| 
      
 1066 
     | 
    
         
            +
             
     | 
| 
      
 1067 
     | 
    
         
            +
              attributes :id, :name
         
     | 
| 
      
 1068 
     | 
    
         
            +
            end
         
     | 
| 
      
 1069 
     | 
    
         
            +
             
     | 
| 
      
 1070 
     | 
    
         
            +
            user1 = User.new(1, 'John')
         
     | 
| 
      
 1071 
     | 
    
         
            +
            user2 = User.new(2, 'Masafumi')
         
     | 
| 
      
 1072 
     | 
    
         
            +
             
     | 
| 
      
 1073 
     | 
    
         
            +
            UserResource.new([user1, user2]).serialize
         
     | 
| 
      
 1074 
     | 
    
         
            +
            # => '{"1":{"id":1,"name":"John"},"2":{"id":2,"name":"Masafumi"}}'
         
     | 
| 
      
 1075 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1076 
     | 
    
         
            +
             
     | 
| 
      
 1077 
     | 
    
         
            +
            In the snippet above, `collection_key :id` specifies the key used for the key of the collection hash. In this example it's `:id`.
         
     | 
| 
      
 1078 
     | 
    
         
            +
             
     | 
| 
      
 1079 
     | 
    
         
            +
            Make sure that collection key is unique for the collection.
         
     | 
| 
      
 1080 
     | 
    
         
            +
             
     | 
| 
       761 
1081 
     | 
    
         
             
            ### Layout
         
     | 
| 
       762 
1082 
     | 
    
         | 
| 
       763 
1083 
     | 
    
         
             
            Sometimes we'd like to serialize JSON into a template. In other words, we need some structure OUTSIDE OF serialized JSON. IN HTML world, we call it a "layout".
         
     | 
| 
         @@ -824,6 +1144,156 @@ Also note that we use percentage notation here to use double quotes. Using singl 
     | 
|
| 
       824 
1144 
     | 
    
         | 
| 
       825 
1145 
     | 
    
         
             
            Currently, Alba doesn't support caching, primarily due to the behavior of `ActiveRecord::Relation`'s cache. See [the issue](https://github.com/rails/rails/issues/41784).
         
     | 
| 
       826 
1146 
     | 
    
         | 
| 
      
 1147 
     | 
    
         
            +
            ### Extend Alba
         
     | 
| 
      
 1148 
     | 
    
         
            +
             
     | 
| 
      
 1149 
     | 
    
         
            +
            Sometimes we have shared behaviors across resources. In such cases we can have a module for common logic.
         
     | 
| 
      
 1150 
     | 
    
         
            +
             
     | 
| 
      
 1151 
     | 
    
         
            +
            In `attribute` block we can call instance method so we can improve the code below:
         
     | 
| 
      
 1152 
     | 
    
         
            +
             
     | 
| 
      
 1153 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1154 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 1155 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1156 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1157 
     | 
    
         
            +
              attribute :created_at do |foo|
         
     | 
| 
      
 1158 
     | 
    
         
            +
                foo.created_at.strftime('%m/%d/%Y')
         
     | 
| 
      
 1159 
     | 
    
         
            +
              end
         
     | 
| 
      
 1160 
     | 
    
         
            +
             
     | 
| 
      
 1161 
     | 
    
         
            +
              attribute :updated_at do |foo|
         
     | 
| 
      
 1162 
     | 
    
         
            +
                foo.updated_at.strftime('%m/%d/%Y')
         
     | 
| 
      
 1163 
     | 
    
         
            +
              end
         
     | 
| 
      
 1164 
     | 
    
         
            +
            end
         
     | 
| 
      
 1165 
     | 
    
         
            +
             
     | 
| 
      
 1166 
     | 
    
         
            +
            class BarResource
         
     | 
| 
      
 1167 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1168 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1169 
     | 
    
         
            +
              attribute :created_at do |bar|
         
     | 
| 
      
 1170 
     | 
    
         
            +
                bar.created_at.strftime('%m/%d/%Y')
         
     | 
| 
      
 1171 
     | 
    
         
            +
              end
         
     | 
| 
      
 1172 
     | 
    
         
            +
             
     | 
| 
      
 1173 
     | 
    
         
            +
              attribute :updated_at do |bar|
         
     | 
| 
      
 1174 
     | 
    
         
            +
                bar.updated_at.strftime('%m/%d/%Y')
         
     | 
| 
      
 1175 
     | 
    
         
            +
              end
         
     | 
| 
      
 1176 
     | 
    
         
            +
            end
         
     | 
| 
      
 1177 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1178 
     | 
    
         
            +
             
     | 
| 
      
 1179 
     | 
    
         
            +
            to:
         
     | 
| 
      
 1180 
     | 
    
         
            +
             
     | 
| 
      
 1181 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1182 
     | 
    
         
            +
            module SharedLogic
         
     | 
| 
      
 1183 
     | 
    
         
            +
              def format_time(time)
         
     | 
| 
      
 1184 
     | 
    
         
            +
                time.strftime('%m/%d/%Y')
         
     | 
| 
      
 1185 
     | 
    
         
            +
              end
         
     | 
| 
      
 1186 
     | 
    
         
            +
            end
         
     | 
| 
      
 1187 
     | 
    
         
            +
             
     | 
| 
      
 1188 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 1189 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1190 
     | 
    
         
            +
              include SharedLogic
         
     | 
| 
      
 1191 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1192 
     | 
    
         
            +
              attribute :created_at do |foo|
         
     | 
| 
      
 1193 
     | 
    
         
            +
                format_time(foo.created_at)
         
     | 
| 
      
 1194 
     | 
    
         
            +
              end
         
     | 
| 
      
 1195 
     | 
    
         
            +
             
     | 
| 
      
 1196 
     | 
    
         
            +
              attribute :updated_at do |foo|
         
     | 
| 
      
 1197 
     | 
    
         
            +
                format_time(foo.updated_at)
         
     | 
| 
      
 1198 
     | 
    
         
            +
              end
         
     | 
| 
      
 1199 
     | 
    
         
            +
            end
         
     | 
| 
      
 1200 
     | 
    
         
            +
             
     | 
| 
      
 1201 
     | 
    
         
            +
            class BarResource
         
     | 
| 
      
 1202 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1203 
     | 
    
         
            +
              include SharedLogic
         
     | 
| 
      
 1204 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1205 
     | 
    
         
            +
              attribute :created_at do |bar|
         
     | 
| 
      
 1206 
     | 
    
         
            +
                format_time(bar.created_at)
         
     | 
| 
      
 1207 
     | 
    
         
            +
              end
         
     | 
| 
      
 1208 
     | 
    
         
            +
             
     | 
| 
      
 1209 
     | 
    
         
            +
              attribute :updated_at do |bar|
         
     | 
| 
      
 1210 
     | 
    
         
            +
                format_time(bar.updated_at)
         
     | 
| 
      
 1211 
     | 
    
         
            +
              end
         
     | 
| 
      
 1212 
     | 
    
         
            +
            end
         
     | 
| 
      
 1213 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1214 
     | 
    
         
            +
             
     | 
| 
      
 1215 
     | 
    
         
            +
            We can even add our own DSL to serialize attributes for readability and removing code duplications.
         
     | 
| 
      
 1216 
     | 
    
         
            +
             
     | 
| 
      
 1217 
     | 
    
         
            +
            To do so, we need to `extend` our module. Let's see how we can achieve the same goal with this approach.
         
     | 
| 
      
 1218 
     | 
    
         
            +
             
     | 
| 
      
 1219 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1220 
     | 
    
         
            +
            module AlbaExtension
         
     | 
| 
      
 1221 
     | 
    
         
            +
              # Here attrs are an Array of Symbol
         
     | 
| 
      
 1222 
     | 
    
         
            +
              def formatted_time_attributes(*attrs)
         
     | 
| 
      
 1223 
     | 
    
         
            +
                attrs.each do |attr|
         
     | 
| 
      
 1224 
     | 
    
         
            +
                  attribute attr do |object|
         
     | 
| 
      
 1225 
     | 
    
         
            +
                    time = object.send(attr)
         
     | 
| 
      
 1226 
     | 
    
         
            +
                    time.strftime('%m/%d/%Y')
         
     | 
| 
      
 1227 
     | 
    
         
            +
                  end
         
     | 
| 
      
 1228 
     | 
    
         
            +
                end
         
     | 
| 
      
 1229 
     | 
    
         
            +
              end
         
     | 
| 
      
 1230 
     | 
    
         
            +
            end
         
     | 
| 
      
 1231 
     | 
    
         
            +
             
     | 
| 
      
 1232 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 1233 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1234 
     | 
    
         
            +
              extend AlbaExtension
         
     | 
| 
      
 1235 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1236 
     | 
    
         
            +
              formatted_time_attributes :created_at, :updated_at
         
     | 
| 
      
 1237 
     | 
    
         
            +
            end
         
     | 
| 
      
 1238 
     | 
    
         
            +
             
     | 
| 
      
 1239 
     | 
    
         
            +
            class BarResource
         
     | 
| 
      
 1240 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1241 
     | 
    
         
            +
              extend AlbaExtension
         
     | 
| 
      
 1242 
     | 
    
         
            +
              # other attributes
         
     | 
| 
      
 1243 
     | 
    
         
            +
              formatted_time_attributes :created_at, :updated_at
         
     | 
| 
      
 1244 
     | 
    
         
            +
            end
         
     | 
| 
      
 1245 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1246 
     | 
    
         
            +
             
     | 
| 
      
 1247 
     | 
    
         
            +
            In this way we have shorter and cleaner code. Note that we need to use `send` or `public_send` in `attribute` block to get attribute data.
         
     | 
| 
      
 1248 
     | 
    
         
            +
             
     | 
| 
      
 1249 
     | 
    
         
            +
            ### Debugging
         
     | 
| 
      
 1250 
     | 
    
         
            +
             
     | 
| 
      
 1251 
     | 
    
         
            +
            Debugging is not easy. If you find Alba not working as you expect, there are a few things to do:
         
     | 
| 
      
 1252 
     | 
    
         
            +
             
     | 
| 
      
 1253 
     | 
    
         
            +
            1. Inspect
         
     | 
| 
      
 1254 
     | 
    
         
            +
             
     | 
| 
      
 1255 
     | 
    
         
            +
            The typical code looks like this:
         
     | 
| 
      
 1256 
     | 
    
         
            +
             
     | 
| 
      
 1257 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1258 
     | 
    
         
            +
            class FooResource
         
     | 
| 
      
 1259 
     | 
    
         
            +
              include Alba::Resource
         
     | 
| 
      
 1260 
     | 
    
         
            +
              attributes :id
         
     | 
| 
      
 1261 
     | 
    
         
            +
            end
         
     | 
| 
      
 1262 
     | 
    
         
            +
            FooResource.new(foo).serialize
         
     | 
| 
      
 1263 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1264 
     | 
    
         
            +
             
     | 
| 
      
 1265 
     | 
    
         
            +
            Notice that we instantiate `FooResource` and then call `serialize` method. We can get various information by calling `inspect` method on it.
         
     | 
| 
      
 1266 
     | 
    
         
            +
             
     | 
| 
      
 1267 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1268 
     | 
    
         
            +
            puts FooResource.new(foo).inspect # or: p class FooResource.new(foo)
         
     | 
| 
      
 1269 
     | 
    
         
            +
            # => "#<FooResource:0x000000010e21f408 @object=[#<Foo:0x000000010e3470d8 @id=1>], @params={}, @within=#<Object:0x000000010df2eac8>, @method_existence={}, @_attributes={:id=>:id}, @_key=nil, @_key_for_collection=nil, @_meta=nil, @_transform_type=:none, @_transforming_root_key=false, @_on_error=nil, @_on_nil=nil, @_layout=nil, @_collection_key=nil>"
         
     | 
| 
      
 1270 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1271 
     | 
    
         
            +
             
     | 
| 
      
 1272 
     | 
    
         
            +
            The output might be different depending on the version of Alba or the object you give, but the concepts are the same. `@object` represents the object you gave as an argument to `new` method, `@_attributes` represents the attributes you defined in `FooResource` class using `attributes` DSL.
         
     | 
| 
      
 1273 
     | 
    
         
            +
             
     | 
| 
      
 1274 
     | 
    
         
            +
            Other things are not so important, but you need to take care of corresponding part when you use additional features such as `root_key`, `transform_keys` and adding params.
         
     | 
| 
      
 1275 
     | 
    
         
            +
             
     | 
| 
      
 1276 
     | 
    
         
            +
            2. Logging
         
     | 
| 
      
 1277 
     | 
    
         
            +
             
     | 
| 
      
 1278 
     | 
    
         
            +
            Alba currently doesn't support logging directly, but you can add your own logging module to Alba easily.
         
     | 
| 
      
 1279 
     | 
    
         
            +
             
     | 
| 
      
 1280 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 1281 
     | 
    
         
            +
            module Logging
         
     | 
| 
      
 1282 
     | 
    
         
            +
              def serialize(...) # `...` was added in Ruby 2.7
         
     | 
| 
      
 1283 
     | 
    
         
            +
                puts serializable_hash
         
     | 
| 
      
 1284 
     | 
    
         
            +
                super(...)
         
     | 
| 
      
 1285 
     | 
    
         
            +
              end
         
     | 
| 
      
 1286 
     | 
    
         
            +
            end
         
     | 
| 
      
 1287 
     | 
    
         
            +
             
     | 
| 
      
 1288 
     | 
    
         
            +
            FooResource.prepend Logging
         
     | 
| 
      
 1289 
     | 
    
         
            +
            FooResource.new(foo).serialize
         
     | 
| 
      
 1290 
     | 
    
         
            +
            # => "{:id=>1}" is printed
         
     | 
| 
      
 1291 
     | 
    
         
            +
            ```
         
     | 
| 
      
 1292 
     | 
    
         
            +
             
     | 
| 
      
 1293 
     | 
    
         
            +
            Here, we override `serialize` method with `prepend`. In overridden method we print the result of `serializable_hash` that gives the basic hash for serialization to `serialize` method. Using `...` allows us to override without knowing method signiture of `serialize`.
         
     | 
| 
      
 1294 
     | 
    
         
            +
             
     | 
| 
      
 1295 
     | 
    
         
            +
            Don't forget calling `super` in this way.
         
     | 
| 
      
 1296 
     | 
    
         
            +
             
     | 
| 
       827 
1297 
     | 
    
         
             
            ## Rails
         
     | 
| 
       828 
1298 
     | 
    
         | 
| 
       829 
1299 
     | 
    
         
             
            When you use Alba in Rails, you can create an initializer file with the line below for compatibility with Rails JSON encoder.
         
     | 
| 
         @@ -834,6 +1304,8 @@ Alba.backend = :active_support 
     | 
|
| 
       834 
1304 
     | 
    
         
             
            Alba.backend = :oj_rails
         
     | 
| 
       835 
1305 
     | 
    
         
             
            ```
         
     | 
| 
       836 
1306 
     | 
    
         | 
| 
      
 1307 
     | 
    
         
            +
            To find out more details, please see https://github.com/okuramasafumi/alba/blob/main/docs/rails.md
         
     | 
| 
      
 1308 
     | 
    
         
            +
             
     | 
| 
       837 
1309 
     | 
    
         
             
            ## Why named "Alba"?
         
     | 
| 
       838 
1310 
     | 
    
         | 
| 
       839 
1311 
     | 
    
         
             
            The name "Alba" comes from "albatross", a kind of birds. In Japanese, this bird is called "Aho-dori", which means "stupid bird". I find it funny because in fact albatrosses fly really fast. I hope Alba looks stupid but in fact it does its job quick.
         
     | 
| 
         @@ -847,14 +1319,13 @@ There are great pioneers in Ruby's ecosystem which does basically the same thing 
     | 
|
| 
       847 
1319 
     | 
    
         | 
| 
       848 
1320 
     | 
    
         
             
            ## Development
         
     | 
| 
       849 
1321 
     | 
    
         | 
| 
       850 
     | 
    
         
            -
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
      
 1322 
     | 
    
         
            +
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
       851 
1323 
     | 
    
         | 
| 
       852 
1324 
     | 
    
         
             
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         
     | 
| 
       853 
1325 
     | 
    
         | 
| 
       854 
1326 
     | 
    
         
             
            ## Contributing
         
     | 
| 
       855 
1327 
     | 
    
         | 
| 
       856 
     | 
    
         
            -
             
     | 
| 
       857 
     | 
    
         
            -
             
     | 
| 
      
 1328 
     | 
    
         
            +
            Thank you for begin interested in contributing to Alba! Please see [contributors guide](https://github.com/okuramasafumi/alba/blob/main/CONTRIBUTING.md) before start contributing. If you have any questions, please feel free to ask in [Discussions](https://github.com/okuramasafumi/alba/discussions).
         
     | 
| 
       858 
1329 
     | 
    
         | 
| 
       859 
1330 
     | 
    
         
             
            ## License
         
     | 
| 
       860 
1331 
     | 
    
         | 
| 
         @@ -862,4 +1333,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ 
     | 
|
| 
       862 
1333 
     | 
    
         | 
| 
       863 
1334 
     | 
    
         
             
            ## Code of Conduct
         
     | 
| 
       864 
1335 
     | 
    
         | 
| 
       865 
     | 
    
         
            -
            Everyone interacting in the Alba project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/okuramasafumi/alba/blob/ 
     | 
| 
      
 1336 
     | 
    
         
            +
            Everyone interacting in the Alba project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/okuramasafumi/alba/blob/main/CODE_OF_CONDUCT.md).
         
     |