Skip to content

Commit

Permalink
Merge pull request #252 from okuramasafumi/to_json
Browse files Browse the repository at this point in the history
[Fix] let Rails implicitly call `to_json`
  • Loading branch information
okuramasafumi authored Oct 19, 2022
2 parents eecf634 + bdf9f7a commit 5a0ff3a
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,8 @@ Alba.backend = :active_support
Alba.backend = :oj_rails
```

To find out more details, please see https://github.com/okuramasafumi/alba/blob/main/docs/rails.md

## Why named "Alba"?

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.
Expand Down
44 changes: 44 additions & 0 deletions docs/rails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Alba for Rails
author: OKURA Masafumi
---

# Alba for Rails

While Alba is NOT designed for Rails specifically, you can definitely use Alba with Rails. This document describes in detail how to use Alba with Rails to be more productive.

## Initializer

You might want to add some configurations to initializer file such as `alba.rb` with something like below:

```ruby
# alba.rb
Alba.backend = :active_support
Alba.enable_inference!(:active_support)
```

You can also use `:oj_rails` for backend if you prefer using Oj.

## Rendering JSON

You can render JSON with Rails in two ways. One way is to pass JSON String.

```ruby
render json: FooResource.new(foo).serialize
```

But you can also render JSON passing `Alba::Resource` object. Rails automatically calls `to_json` on a resource.

```ruby
render json: FooResource.new(foo)
```

Note that almost all options given to this `render` are ignored. The only exceptions are `layout`, `prefixes`, `template` and `status`.

```ruby
# This `only` option is ignored
render json: FooResource.new(foo), only: [:id]

# This is OK
render json: FooResource.new(foo), status: 200
```
36 changes: 35 additions & 1 deletion lib/alba/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,27 @@ def initialize(object, params: {}, within: WITHIN_DEFAULT)
def serialize(root_key: nil, meta: {})
serialize_with(as_json(root_key: root_key, meta: meta))
end
alias to_json serialize

if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0')
# For Rails compatibility
# The first options is a dummy parameter but required
# You can pass empty Hash if you don't want to pass any arguments
#
# @see #serialize
# @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
def to_json(options, root_key: nil, meta: {})
_to_json(root_key, meta, options)
end
else
# For Rails compatibility
# The first options is a dummy parameter
#
# @see #serialize
# @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
def to_json(options = {}, root_key: nil, meta: {})
_to_json(root_key, meta, options)
end
end

# Returns a Hash correspondng {Resource#serialize}
#
Expand Down Expand Up @@ -84,6 +104,20 @@ def encode(hash)
Alba.encoder.call(hash)
end

def _to_json(root_key, meta, options)
options.reject! { |k, _| %i[layout prefixes template status].include?(k) } # Rails specific guard
# TODO: use `filter_map` after dropping support of Ruby 2.6
names = options.map { |k, v| k unless v.nil? }
names.compact!
unless names.empty?
names.sort!
names.map! { |s| "\"#{s}\"" }
message = "You passed #{names.join(', ')} options but ignored. Please refer to the document: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md"
Kernel.warn(message)
end
serialize(root_key: root_key, meta: meta)
end

def serialize_with(hash)
serialized_json = encode(hash)
return serialized_json unless @_layout
Expand Down
60 changes: 60 additions & 0 deletions test/resource_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,66 @@ def test_as_json
)
end

def test_to_json
# With Ruby 2 series it's difficult to define dummy options parameter
# For Ruby 2.x we make dummy options parameter required, which should be fine for Rails
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0')
assert_equal(
'{"foo":{"id":1,"bar_size":1,"bars":[{"id":1}]}}',
FooResource.new(@foo).to_json
)
end
assert_equal(
'{"foo":{"id":1,"bar_size":1,"bars":[{"id":1}]}}',
FooResource.new(@foo).to_json({})
)
end

def test_to_json_with_various_arguments # rubocop:disable Minitest/MultipleAssertions
result = nil
execute = -> { FooResource.new(@foo).to_json({only: :id}, root_key: :bar) }
assert_output('', "You passed \"only\" options but ignored. Please refer to the document: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md\n") { result = execute.call }
assert_equal(
'{"bar":{"id":1,"bar_size":1,"bars":[{"id":1}]}}',
result
)

execute2 = lambda do
FooResource.new(@foo).to_json(
{
only: :id,
include: :bar,
methods: [:baz],
except: :excepted,
root: :fooo
},
meta: {this: :meta}
)
end
message = "You passed \"except\", \"include\", \"methods\", \"only\", \"root\" options but ignored. Please refer to the document: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md\n"
assert_output('', message) { result = execute2.call }
assert_equal(
'{"foo":{"id":1,"bar_size":1,"bars":[{"id":1}]},"meta":{"this":"meta"}}',
result
)

execute3 = lambda do
FooResource.new(@foo).to_json(
{
layout: 'default',
prefixes: 'prefixes',
template: 'template',
status: 200
}
)
end
assert_silent { result = execute3.call }
assert_equal(
'{"foo":{"id":1,"bar_size":1,"bars":[{"id":1}]}}',
result
)
end

def test_serializable_hash
assert_equal(
{'id' => 1, 'bar_size' => 1, 'bars' => [{'id' => 1}]},
Expand Down

0 comments on commit 5a0ff3a

Please sign in to comment.