Skip to content

Commit

Permalink
Merge pull request #323 from okuramasafumi/prefer-resource-method
Browse files Browse the repository at this point in the history
Prefer resource method
  • Loading branch information
okuramasafumi authored Jul 24, 2023
2 parents 27ac98f + ec58245 commit 24f080a
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Style/InlineComment:
Enabled: false

Style/MethodCallWithArgsParentheses:
AllowedMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send']
AllowedMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send', 'alias_method']
Exclude:
# There are so many `attributes` call without parenthese and that's absolutely fine
- 'test/**/*.rb'
Expand Down
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,72 @@ class UserResource
end
```

#### Prefer methods on resource

By default, Alba prefers methods on the object to methods on the resource. This means if you have a following situation:

```ruby
class User
attr_accessor :id, :name, :email

def initialize(id, name, email)
@id = id
@name = name
@email = email
end

def name_with_email
"dummy!"
end
end

class UserResource
include Alba::Resource

root_key :user, :users # Later is for plural

attributes :id, :name, :name_with_email

# Same method exists in `User` class!
# This is not called
def name_with_email(user)
"#{user.name}: #{user.email}"
end
end

user = User.new(1, 'Masafumi OKURA', '[email protected]')
UserResource.new(user).serialize
# => '{"user":{"id":1,"name":"Masafumi OKURA","name_with_email":"dummy!"}}'
```

You can see that `name_with_email` is now `dummy!` from `User#name_with_email`. You cna change this behavior by using `prefer_resource_method!` DSL in a resource class:

```ruby
# With the same `User` class

class UserResource
include Alba::Resource

prefer_resource_method! # This line is important

root_key :user, :users # Later is for plural

attributes :id, :name, :name_with_email

# Same method exists in `User` class!
# But now this is called!
def name_with_email(user)
"#{user.name}: #{user.email}"
end
end

user = User.new(1, 'Masafumi OKURA', '[email protected]')
UserResource.new(user).serialize
# => '{"user":{"id":1,"name":"Masafumi OKURA","name_with_email":"Masafumi OKURA: [email protected]"}}'
```

The next major version of Alba will change this default behavior to prefer resource methods. In case you want to preserve current behavior, there's `prefer_object_method!` DSL, which does that.

#### Params

You can pass a Hash to the resource for internal use. It can be used as "flags" to control attribute content.
Expand Down
21 changes: 21 additions & 0 deletions lib/alba/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,23 @@ def fetch_attribute(obj, key, attribute) # rubocop:disable Metrics/CyclomaticCom
value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value
end

# TODO: from version 3, `_fetch_attribute_from_resource_first` is default
def fetch_attribute_from_object_and_resource(obj, attribute)
_fetch_attribute_from_object_first(obj, attribute)
end

def _fetch_attribute_from_object_first(obj, attribute)
obj.__send__(attribute)
rescue NoMethodError
__send__(attribute, obj)
end

def _fetch_attribute_from_resource_first(obj, attribute)
__send__(attribute, obj)
rescue NoMethodError
obj.__send__(attribute)
end

def nil_handler
@_on_nil
end
Expand Down Expand Up @@ -510,6 +521,16 @@ def helper(mod = @_helper || Module.new, &block)
extend mod
@_helper = mod
end

# DSL for alias, purely for readability
def prefer_resource_method!
alias_method :fetch_attribute_from_object_and_resource, :_fetch_attribute_from_resource_first
end

# DSL for alias, purely for readability
def prefer_object_method!
alias_method :fetch_attribute_from_object_and_resource, :_fetch_attribute_from_object_first
end
end
end
end
73 changes: 73 additions & 0 deletions test/usecases/object_method_and_resource_method_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require_relative '../test_helper'

class ObjectMethodAndResourceMethodTest < Minitest::Test
class Foo
attr_reader :id
def initialize(id)
@id = id
end
end

def setup
@foo = Foo.new(1)
end

class FooResource
include Alba::Resource

prefer_resource_method!

attributes :id

def id(_)
42
end
end

def test_prefer_resource_method
assert_equal '{"id":42}', FooResource.new(@foo).serialize
end

class FooResource2
include Alba::Resource

prefer_object_method!

attributes :id

def id(_)
42
end
end

def test_prefer_object_method
assert_equal '{"id":1}', FooResource2.new(@foo).serialize
end

class FooResource3
include Alba::Resource

attributes :id

def id(_)
42
end
end

# TODO: perfer resource method by default from version 3
def test_default_behavior
assert_equal '{"id":1}', FooResource3.new(@foo).serialize
end

class FooResource4
include Alba::Resource

prefer_resource_method!

attributes :id
end

def test_prefer_resource_method_but_it_is_not_there
assert_equal '{"id":1}', FooResource4.new(@foo).serialize
end
end

0 comments on commit 24f080a

Please sign in to comment.