diff --git a/README.md b/README.md index afc06d180..262154f7e 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ backed by ActiveRecord models or by custom objects. * [Handling Exceptions] (#handling-exceptions) * [Action Callbacks] (#action-callbacks) * [Serializer] (#serializer) + * [Routing] (#routing) * [Configuration] (#configuration) * [Contributing] (#contributing) * [License] (#license) @@ -1368,7 +1369,158 @@ JSONAPI::ResourceSerializer.new(PostResource, include: include_resources, ).serialize_to_hash(PostResource.new(post, nil)) ``` -#### Routing +#### Formatting + +JR by default uses some simple rules to format (and unformat) an attribute for (de-)serialization. Strings and Integers are output to JSON +as is, and all other values have `.to_s` applied to them. This outputs something in all cases, but it is certainly not +correct for every situation. + +If you want to change the way an attribute is (de-)serialized you have a couple of ways. The simplest method is to create a +getter (and setter) method on the resource which overrides the attribute and apply the (un-)formatting there. For example: + +```ruby +class PersonResource < JSONAPI::Resource + attributes :name, :email, :last_login_time + + # Setter example + def email=(new_email) + @model.email = new_email.downcase + end + + # Getter example + def last_login_time + @model.last_login_time.in_time_zone(@context[:current_user].time_zone).to_s + end +end +``` + +This is simple to implement for a one off situation, but not for example if you want to apply the same formatting rules +to all DateTime fields in your system. Another issue is the attribute on the resource will always return a formatted +response, whether you want it or not. + +##### Value Formatters + +To overcome the above limitations JR uses Value Formatters. Value Formatters allow you to control the way values are +handled for an attribute. The `format` can be set per attribute as it is declared in the resource. For example: + +```ruby +class PersonResource < JSONAPI::Resource + attributes :name, :email, :spoken_languages + attribute :last_login_time, format: :date_with_utc_timezone + + # Getter/Setter for spoken_languages ... +end +``` + +A Value formatter has a `format` and an `unformat` method. Here's the base ValueFormatter and DefaultValueFormatter for +reference: + +```ruby +module JSONAPI + class ValueFormatter < Formatter + class << self + def format(raw_value) + super(raw_value) + end + + def unformat(value) + super(value) + end + ... + end + end +end + +class DefaultValueFormatter < JSONAPI::ValueFormatter + class << self + def format(raw_value) + case raw_value + when String, Integer + return raw_value + else + return raw_value.to_s + end + end + end +end +``` + +You can also create your own Value Formatter. Value Formatters must be named with the `format` name followed by +`ValueFormatter`, i.e. `DateWithUTCTimezoneValueFormatter` and derive from `JSONAPI::ValueFormatter`. It is +recommended that you create a directory for your formatters, called `formatters`. + +The `format` method is called by the `ResourceSerializer` as is serializing a resource. The format method takes the +`raw_value` parameter. `raw_value` is the value as read from the model. + +The `unformat` method is called when processing the request. Each incoming attribute (except `links`) are run through +the `unformat` method. The `unformat` method takes a `value`, which is the value as it comes in on the +request. This allows you process the incoming value to alter its state before it is stored in the model. + +###### Use a Different Default Value Formatter + +Another way to handle formatting is to set a different default value formatter. This will affect all attributes that do +not have a `format` set. You can do this by overriding the `default_attribute_options` method for a resource (or a base +resource for a system wide change). + +```ruby + def default_attribute_options + {format: :my_default} + end +``` + +and + +```ruby +class MyDefaultValueFormatter < JSONAPI::ValueFormatter + class << self + def format(raw_value) + case raw_value + when String, Integer + return raw_value + when DateTime + return raw_value.in_time_zone('UTC').to_s + else + return raw_value.to_s + end + end + end +end +``` + +This way all DateTime values will be formatted to display in the UTC timezone. + +#### Key Format + +By default JR uses dasherized keys as per the +[JSON API naming recommendations](http://jsonapi.org/recommendations/#naming). This can be changed by specifying a +different key formatter. + +For example, to use camel cased keys with an initial lowercase character (JSON's default) create an initializer and add +the following: + +```ruby +JSONAPI.configure do |config| + # built in key format options are :underscored_key, :camelized_key and :dasherized_key + config.json_key_format = :camelized_key +end +``` + +This will cause the serializer to use the `CamelizedKeyFormatter`. You can also create your own `KeyFormatter`, for +example: + +```ruby +class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter + class << self + def format(key) + super.camelize(:upper) + end + end +end +``` + +You would specify this in `JSONAPI.configure` as `:upper_camelized`. + +### Routing JR has a couple of helper methods available to assist you with setting up routes. @@ -1535,157 +1687,6 @@ phone_number_contact GET /phone-numbers/:phone_number_id/contact(.:format) co ``` -#### Formatting - -JR by default uses some simple rules to format (and unformat) an attribute for (de-)serialization. Strings and Integers are output to JSON -as is, and all other values have `.to_s` applied to them. This outputs something in all cases, but it is certainly not -correct for every situation. - -If you want to change the way an attribute is (de-)serialized you have a couple of ways. The simplest method is to create a -getter (and setter) method on the resource which overrides the attribute and apply the (un-)formatting there. For example: - -```ruby -class PersonResource < JSONAPI::Resource - attributes :name, :email, :last_login_time - - # Setter example - def email=(new_email) - @model.email = new_email.downcase - end - - # Getter example - def last_login_time - @model.last_login_time.in_time_zone(@context[:current_user].time_zone).to_s - end -end -``` - -This is simple to implement for a one off situation, but not for example if you want to apply the same formatting rules -to all DateTime fields in your system. Another issue is the attribute on the resource will always return a formatted -response, whether you want it or not. - -##### Value Formatters - -To overcome the above limitations JR uses Value Formatters. Value Formatters allow you to control the way values are -handled for an attribute. The `format` can be set per attribute as it is declared in the resource. For example: - -```ruby -class PersonResource < JSONAPI::Resource - attributes :name, :email, :spoken_languages - attribute :last_login_time, format: :date_with_utc_timezone - - # Getter/Setter for spoken_languages ... -end -``` - -A Value formatter has a `format` and an `unformat` method. Here's the base ValueFormatter and DefaultValueFormatter for -reference: - -```ruby -module JSONAPI - class ValueFormatter < Formatter - class << self - def format(raw_value) - super(raw_value) - end - - def unformat(value) - super(value) - end - ... - end - end -end - -class DefaultValueFormatter < JSONAPI::ValueFormatter - class << self - def format(raw_value) - case raw_value - when String, Integer - return raw_value - else - return raw_value.to_s - end - end - end -end -``` - -You can also create your own Value Formatter. Value Formatters must be named with the `format` name followed by -`ValueFormatter`, i.e. `DateWithUTCTimezoneValueFormatter` and derive from `JSONAPI::ValueFormatter`. It is -recommended that you create a directory for your formatters, called `formatters`. - -The `format` method is called by the `ResourceSerializer` as is serializing a resource. The format method takes the -`raw_value` parameter. `raw_value` is the value as read from the model. - -The `unformat` method is called when processing the request. Each incoming attribute (except `links`) are run through -the `unformat` method. The `unformat` method takes a `value`, which is the value as it comes in on the -request. This allows you process the incoming value to alter its state before it is stored in the model. - -###### Use a Different Default Value Formatter - -Another way to handle formatting is to set a different default value formatter. This will affect all attributes that do -not have a `format` set. You can do this by overriding the `default_attribute_options` method for a resource (or a base -resource for a system wide change). - -```ruby - def default_attribute_options - {format: :my_default} - end -``` - -and - -```ruby -class MyDefaultValueFormatter < JSONAPI::ValueFormatter - class << self - def format(raw_value) - case raw_value - when String, Integer - return raw_value - when DateTime - return raw_value.in_time_zone('UTC').to_s - else - return raw_value.to_s - end - end - end -end -``` - -This way all DateTime values will be formatted to display in the UTC timezone. - -#### Key Format - -By default JR uses dasherized keys as per the -[JSON API naming recommendations](http://jsonapi.org/recommendations/#naming). This can be changed by specifying a -different key formatter. - -For example, to use camel cased keys with an initial lowercase character (JSON's default) create an initializer and add -the following: - -```ruby -JSONAPI.configure do |config| - # built in key format options are :underscored_key, :camelized_key and :dasherized_key - config.json_key_format = :camelized_key -end -``` - -This will cause the serializer to use the `CamelizedKeyFormatter`. You can also create your own `KeyFormatter`, for -example: - -```ruby -class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter - class << self - def format(key) - super.camelize(:upper) - end - end -end -``` - -You would specify this in `JSONAPI.configure` as `:upper_camelized`. - ## Configuration JR has a few configuration options. Some have already been mentioned above. To set configuration options create an