Repository: okuramasafumi/alba (Ruby)

Issue:
Cascade key transformation only cascades for one level
## Describe the bug
Apologies for not testing this myself sooner, but I think we have a bug left over from #220, where `transform_keys` only cascades for one level. 

## To Reproduce
Given the following code sample, note that the `articles` array has the `createdAt` key, but the `comments` array has the `created_at` key, which has not been transformed:

```ruby
class User
  attr_accessor :created_at, :articles

  def initialize
    @created_at = Time.now
  end
end

class Article
  attr_accessor :created_at, :comments

  def initialize
    @created_at = Time.now
  end
end

class Comment
  attr_accessor :created_at

  def initialize
    @created_at = Time.now
  end
end

class UserResource
  include Alba::Resource

  transform_keys :lower_camel

  attributes :created_at

  association :articles do
    attributes :created_at

    association :comments do
      attributes :created_at
    end
  end
end

user = User.new
comment = Comment.new
article = Article.new
article.comments = [comment]
user.articles = [article]

UserResource.new(user).to_h
# => 
{"createdAt"=>2022-10-24 15:23:10.190596 -0500,
 "articles"=>[{"createdAt"=>2022-10-24 15:23:10.203395 -0500, "comments"=>[{"created_at"=>2022-10-24 15:23:10.198995 -0500}]}]}
```


Discussion:
@trevorturk Thank you for reporting, I'll take a look.
@trevorturk #263 was a solution, but it was hard to debug. I described the problem and solution in the PR description so please check it if you're interested.
In short, there's an implicit requirement that `transform_keys` should be called before `attributes`. This might not be a problem for daily development, but in this case it's a trick issue.
Ack! Sorry the fix was so difficult, but I did test your PR/branch in my app and everything works as expected! Thank you so much! I have a lot of nested attributes, so this removes a lot of repeated `transform_keys` keys calls and my resources look much skinnier now :)



Patch:
diff --git a/lib/alba.rb b/lib/alba.rb
index 6f2f99e..461d628 100644
--- a/lib/alba.rb
+++ b/lib/alba.rb
@@ -69,7 +69,7 @@ def disable_inference!
     def resource_class(&block)
       klass = Class.new
       klass.include(Alba::Resource)
-      klass.class_eval(&block)
+      klass.class_eval(&block) if block
       klass
     end
 
diff --git a/lib/alba/association.rb b/lib/alba/association.rb
index cad6edb..d7ba46f 100644
--- a/lib/alba/association.rb
+++ b/lib/alba/association.rb
@@ -20,10 +20,9 @@ def initialize(name:, condition: nil, resource: nil, params: {}, nesting: nil, k
       @condition = condition
       @resource = resource
       @params = params
-      @key_transformation = key_transformation
       return if @resource
 
-      assign_resource(nesting, block)
+      assign_resource(nesting, key_transformation, block)
     end
 
     # Recursively converts an object into a Hash
@@ -58,9 +57,12 @@ def constantize(resource)
       end
     end
 
-    def assign_resource(nesting, block)
+    def assign_resource(nesting, key_transformation, block)
       @resource = if block
-                    Alba.resource_class(&block)
+                    klass = Alba.resource_class
+                    klass.transform_keys(key_transformation)
+                    klass.class_eval(&block)
+                    klass
                   elsif Alba.inferring
                     Alba.infer_resource_class(@name, nesting: nesting)
                   else
@@ -76,7 +78,6 @@ def to_h_with_each_resource(within, params)
 
     def to_h_with_constantize_resource(within, params)
       @resource = constantize(@resource)
-      @resource.transform_keys(@key_transformation)
       @resource.new(object, params: params, within: within).to_h
     end
   end
diff --git a/lib/alba/nested_attribute.rb b/lib/alba/nested_attribute.rb
index d4d5966..f209538 100644
--- a/lib/alba/nested_attribute.rb
+++ b/lib/alba/nested_attribute.rb
@@ -10,8 +10,9 @@ def initialize(key_transformation: :none, &block)
 
     # @return [Hash]
     def value(object)
-      resource_class = Alba.resource_class(&@block)
+      resource_class = Alba.resource_class
       resource_class.transform_keys(@key_transformation)
+      resource_class.class_eval(&@block)
       resource_class.new(object).serializable_hash
     end
   end


Test patch:
diff --git a/test/usecases/key_transform_test.rb b/test/usecases/key_transform_test.rb
index e2d49f4..22546eb 100644
--- a/test/usecases/key_transform_test.rb
+++ b/test/usecases/key_transform_test.rb
@@ -173,11 +173,19 @@ class UserResourceWithInlineAssociation < UserResource
     one :bank_account do
       attributes :account_number
     end
+
+    nested_attribute :fake_attribute do
+      nested_attribute :some_attribute do
+        attribute :real_attribute do
+          42
+        end
+      end
+    end
   end
 
   def test_key_transformation_cascades
     assert_equal(
-      '{"id":1,"firstName":"Masafumi","lastName":"Okura","bankAccount":{"accountNumber":123456789}}',
+      '{"id":1,"firstName":"Masafumi","lastName":"Okura","bankAccount":{"accountNumber":123456789},"fakeAttribute":{"someAttribute":{"realAttribute":42}}}',
       UserResourceWithInlineAssociation.new(@user).serialize
     )
   end