You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: rails6/en/chapter06-improve-json.adoc
+19-19Lines changed: 19 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
[#chapter06-improve-json]
2
2
= JSON with Active Model Serializers
3
3
4
-
In the previous chapter, we added the products to the application and built all the necessary roads. We have also associated a product with a user and restricted some of the actions of `products_controller`.
4
+
In the previous chapter, we added products to the application and built all the necessary roads. We have also associated a product with an user and restricted some of the actions of `products_controller`.
5
5
6
6
Now, you should be satisfied with all this work. But we still have a lot of work to do. Currently, we have a JSON output that is not perfect. The JSON output looks like this:
The content of the `data` key is also quite strict:
56
56
57
-
* it must have a `type' key that describes the type of the JSON model (if it is an article, a user, etc...)
58
-
* the properties of the objects must be placed in an `attributes` key and the _undescore_ (`_`) are replaced by dashes (`-`)
59
-
* the links of the objects must be placed in a `relationships` key
57
+
* it must have a `type' key describing the type of the JSON model (if it is an article, a user, etc...)
58
+
* properties of the objects must be placed in an `attributes` key and the _undescore_ (`_`) are replaced by dashes (`-`)
59
+
* links of the objects must be placed in a `relationships` key
60
60
61
61
In this chapter, we will customize the JSON output using the Netflix gem https://github.com/Netflix/fast_jsonapi_jsonapi[fast_jsonapi] who implements all https://jsonapi.org/[JSON:API] normes.
62
62
@@ -93,7 +93,7 @@ class UserSerializer
93
93
end
94
94
----
95
95
96
-
This _serializer_ will allow us to convert our `User` object to JSON which correctly implements the JSON:API standard. We have specified the `email` attribute so that it is present in the `data` array.
96
+
This _serializer_ will allow us to convert our `User` object to JSON which correctly implements the JSON:API standard. We have specified the `email` attribute so it is present in the `data` array.
97
97
98
98
Let's try all this in the Rails with `rails console` console:
For some reason the answer is not quite what we expect. This is because the gem modifies the response we had previously defined. So to pass the tests, you just have to update our test:
150
+
For some reason the answer is not quite what we expect. This is because the gem modifies the response we had previously defined. So to pass these tests, you just have to update our test:
We have worked with serializers and you may notice that it is very simple. In some cases, the difficult decision is how to name your routes or how to structure the JSON output so that your solution is sustainable. When working with associations between models on an API, there are many approaches you can take.
269
+
We have worked with serializers and you may notice that it is very simple. In some cases, the difficult decision is naming your routes or structuring the JSON output so that your solution is sustainable. When working with associations between models on an API, there are many approaches you can take.
270
270
271
271
We don't have to worry about this problem in our case, the JSON:API standard did it for us!
272
272
@@ -296,13 +296,13 @@ It is a good idea to integrate users into the JSON outputs of products. This wil
296
296
297
297
Imagine a scenario where you go to the API to get the products, but in this case you have to display some of the user information.
298
298
299
-
One possible solution would be to add the attribute `user_id` to the `product_serializer` so that we can get the corresponding user later. This may sound like a good idea, but if you are concerned about performance, or if your database transactions are not fast enough, you should reconsider this approach. You must understand that for each product you retrieve, you will have to retrieve its corresponding user.
299
+
One possible solution would be adding the attribute `user_id` to the `product_serializer` so that we can get the corresponding user later. This may sound like a good idea, but if you are concerned about performance, or if your database transactions are not fast enough, you should reconsider this approach. You must understand that for each product you retrieve, you will have to retrieve its corresponding user.
300
300
301
-
Faced with this problem, there are several possible alternatives.
301
+
Faced with this problem, there are several alternatives.
302
302
303
303
=== Integrate into a meta attribute
304
304
305
-
The first solution A good solution in my opinion is to integrate the identifiers of users linked to products in a meta attribute, so we have a JSON output like:
305
+
The first solution, a good solution in my opinion, is to integrate the identifiers of users linked to products in a meta attribute, so we have a JSON output like:
306
306
307
307
[source,json]
308
308
----
@@ -347,7 +347,7 @@ Another solution is to incorporate the `user` object into the `product` object.
347
347
}
348
348
----
349
349
350
-
The problem with this approach is that we have to duplicate the `User' objects for each product that belong to the same user:
350
+
The problem with this approach is we have to duplicate the `User' objects for each product that belong to the same user:
351
351
352
352
[source,json]
353
353
----
@@ -490,7 +490,7 @@ We are now checking three things on the JSON that has been returned:
490
490
. it contains the user ID of the user linked to the product
491
491
. the user data is included in the `include` key
492
492
493
-
NOTE: You may have noticed that I have chosen to use the method https://ruby-doc.org/core-2.6.3/Hash.html#method-i-dig[`Hash#dig`]. It is a Ruby method that allows you to retrieve elements in an nested _Hash_ by avoiding errors if an element is not present.
493
+
NOTE: You may have noticed that I have chosen to use the method https://ruby-doc.org/core-2.6.3/Hash.html#method-i-dig[`Hash#dig`]. It is a Ruby method allowing you to retrieve elements in an nested _Hash_ by avoiding errors if an element is not present.
494
494
495
495
To pass this test we will start by including the relationship in the _serializer_:
496
496
@@ -504,7 +504,7 @@ class ProductSerializer
504
504
end
505
505
----
506
506
507
-
Cette ajout aura pour effet de rajouter un clé `relatioship` contenant l’identifiant de l'utilisateur:
507
+
This addition will add a `relatioship` key containing the user's identifier:
508
508
509
509
[source,json]
510
510
----
@@ -529,7 +529,7 @@ Cette ajout aura pour effet de rajouter un clé `relatioship` contenant l’iden
529
529
}
530
530
----
531
531
532
-
This allows us to correct our first two assertions. We now want to include the attributes of the user who owns the product. To do this, we simply need to pass an option `:include` to the _serializer_ instantiated in the _controller_. Then let's do it:
532
+
This allows us correcting our first two assertions. We now want to include attributes of the user who owns the product. To do this, we simply need to pass an option `:include` to the _serializer_ instantiated in the _controller_. Then let's do it:
In this last section, we will continue to strengthen the `Products#index` action by setting up a very simple search mechanism to allow any customer to filter the results. This section is optional as it will have no impact on the application modules. But if you want to practice more with the TDD, I recommend that you complete this last step.
704
+
In this last section, we will continue to strengthen the `Products#index` action by setting up a very simple search mechanism allowing any customer to filter the results. This section is optional as it will have no impact on the application modules. But if you want to practice more with the TDD, I recommend that you complete this last step.
705
705
706
-
I use https://github.com/activerecord-hackery/ransack[Ransack] or https://github.com/casecommons/pg_search_search[pg_search] to build advanced search forms extremely quickly. But here, since the goal is to learn and the search we are going to do is very simple, I think we can build a search engine from scratch. We simply have to consider the criteria by which we will filter the attributes. Hang on to your seats, it's going to be a tough trip.
706
+
I use https://github.com/activerecord-hackery/ransack[Ransack] or https://github.com/casecommons/pg_search_search[pg_search] to build advanced search forms extremely quickly. But here, since the goal is learning and searching we are going to do is very simple, I think we can build a search engine from scratch. We simply have to consider the criteria by which we will filter the attributes. Hang on to your seats, it's going to be a tough trip.
707
707
708
708
We will therefore filter the products according to the following criteria:
709
709
@@ -772,7 +772,7 @@ class Product < ApplicationRecord
772
772
end
773
773
----
774
774
775
-
NOTE: _scoping_ allows you to specify commonly used queries that can be referenced as method calls on models. With these __scopes__ you can also link with Active Record methods like `where`, `joins` and `includes` because a _scope_ always returns an object https://api.rubyonrails.org/classes/ActiveRecord/Relation.html[`ActiveRecord::Relation`]. I invite you to take a look at https://guides.rubyonrails.org/active_record_querying.html#scopes_record_querying.html#scopes[Rails documentation]
775
+
NOTE: _scoping_ allows you to specify commonly used queries that can be referenced as method calls on models. With these __scopes__ you can also link with Active Record methods like `where`, `joins` and `includes` because a _scope_ always returns an object https://api.rubyonrails.org/classes/ActiveRecord/Relation.html[`ActiveRecord::Relation`]. I invite you taking a look at https://guides.rubyonrails.org/active_record_querying.html#scopes_record_querying.html#scopes[Rails documentation]
776
776
777
777
Implementation is sufficient for our tests to pass:
778
778
@@ -909,7 +909,7 @@ $ git commit -am "Adds search scopes on the product model"
909
909
910
910
==== Search engine
911
911
912
-
Now that we have the basis for the search engine we will use in the application, it is time to implement a simple but powerful search method. She will manage all the logic to retrieve the product records.
912
+
Now that we have the basis for the search engine we will use in the application, it is time to implement a simple but powerful search method. It will manage all the logic to retrieve the product records.
913
913
914
914
The method will consist in linking all the `scope` that we have previously built and returning the result. Let's start by adding some tests:
0 commit comments