Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Fast JSON API serialized 250 records in 3.01 ms
* [Object Serialization](#object-serialization)
* [Compound Document](#compound-document)
* [Key Transforms](#key-transforms)
* [Pluralize Type](#pluralize-type)
* [Collection Serialization](#collection-serialization)
* [Caching](#caching)
* [Params](#params)
Expand Down Expand Up @@ -170,6 +171,28 @@ set_key_transform :dash # "some_key" => "some-key"
set_key_transform :underscore # "some_key" => "some_key"
```

### Pluralize Type
By default fast_jsonapi does not pluralize type names. You can turn pluralization on using this syntax:

```ruby
class AwardSerializer
include FastJsonapi::ObjectSerializer
belongs_to :actor
pluralize_type true # "award" => "awards"
end
```

Relationship types are not automatically pluralized, even when their base types have `pluralize_type` set. Pluralization can be enabled in the relationship definition.

```ruby
class ActorSerializer
include FastJsonapi::ObjectSerializer
has_many :awards, pluralize_type: true # "award" => "awards"
end
```

The most common use case for this feature is to easily migrate from serialization engines that pluralize by default, such as AMS.

### Attributes
Attributes are defined in FastJsonapi using the `attributes` method. This method is also aliased as `attribute`, which is useful when defining a single attribute.

Expand Down
34 changes: 30 additions & 4 deletions lib/fast_jsonapi/object_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def inherited(subclass)
subclass.cachable_relationships_to_serialize = cachable_relationships_to_serialize.dup if cachable_relationships_to_serialize.present?
subclass.uncachable_relationships_to_serialize = uncachable_relationships_to_serialize.dup if uncachable_relationships_to_serialize.present?
subclass.transform_method = transform_method
subclass.pluralized_type = pluralized_type
subclass.cache_length = cache_length
subclass.race_condition_ttl = race_condition_ttl
subclass.data_links = data_links.dup if data_links.present?
Expand All @@ -126,6 +127,10 @@ def inherited(subclass)
subclass.meta_to_serialize = meta_to_serialize
end

def transformed_record_type
run_key_transform(run_key_pluralization(record_type))
end

def reflected_record_type
return @reflected_record_type if defined?(@reflected_record_type)

Expand Down Expand Up @@ -153,6 +158,17 @@ def set_key_transform(transform_name)
end
end

def pluralize_type(pluralize)
self.pluralized_type = pluralize

# ensure that the record type is correctly transformed
if record_type
set_type(record_type)
elsif reflected_record_type
set_type(reflected_record_type)
end
end

def run_key_transform(input)
if self.transform_method.present?
input.to_s.send(*@transform_method).to_sym
Expand All @@ -161,13 +177,21 @@ def run_key_transform(input)
end
end

def run_key_pluralization(input)
if self.pluralized_type
input.to_s.pluralize.to_sym
else
input.to_sym
end
end

def use_hyphen
warn('DEPRECATION WARNING: use_hyphen is deprecated and will be removed from fast_jsonapi 2.0 use (set_key_transform :dash) instead')
set_key_transform :dash
end

def set_type(type_name)
self.record_type = run_key_transform(type_name)
self.record_type = run_key_transform(run_key_pluralization(type_name))
end

def set_id(id_name = nil, &block)
Expand Down Expand Up @@ -243,16 +267,18 @@ def create_relationship(base_key, relationship_type, options, block)
end
Relationship.new(
key: options[:key] || run_key_transform(base_key),
base_key: run_key_transform(base_key_sym),
name: name,
id_method_name: compute_id_method_name(
options[:id_method_name],
"#{base_serialization_key}#{id_postfix}".to_sym,
block
),
record_type: options[:record_type] || run_key_transform(base_key_sym),
record_type: options[:record_type],
pluralize_type: options[:pluralize_type],
object_method_name: options[:object_method_name] || name,
object_block: block,
serializer: compute_serializer_name(options[:serializer] || base_key_sym),
serializer_name: compute_serializer_name(options[:serializer] || base_key_sym),
relationship_type: relationship_type,
cached: options[:cached],
polymorphic: fetch_polymorphic_option(options),
Expand Down Expand Up @@ -305,7 +331,7 @@ def validate_includes!(includes)
relationships_to_serialize = klass.relationships_to_serialize || {}
relationship_to_include = relationships_to_serialize[parsed_include]
raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
klass = relationship_to_include.serializer.to_s.constantize unless relationship_to_include.polymorphic.is_a?(Hash)
klass = relationship_to_include.serializer unless relationship_to_include.polymorphic.is_a?(Hash)
end
end
end
Expand Down
33 changes: 28 additions & 5 deletions lib/fast_jsonapi/relationship.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
module FastJsonapi
class Relationship
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method, :links, :lazy_load_data
attr_reader :key, :base_key, :name, :id_method_name, :pluralized_type, :object_method_name, :object_block, :serializer_name, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method, :links, :lazy_load_data

def initialize(
key:,
base_key:,
name:,
id_method_name:,
record_type:,
pluralize_type:,
object_method_name:,
object_block:,
serializer:,
serializer_name:,
relationship_type:,
cached: false,
polymorphic:,
Expand All @@ -19,12 +21,14 @@ def initialize(
lazy_load_data: false
)
@key = key
@base_key = base_key
@name = name
@id_method_name = id_method_name
@record_type = record_type
@pluralized_type = pluralize_type
@record_type = run_key_pluralization(record_type)
@object_method_name = object_method_name
@object_block = object_block
@serializer = serializer
@serializer_name = serializer_name
@relationship_type = relationship_type
@cached = cached
@polymorphic = polymorphic
Expand Down Expand Up @@ -59,6 +63,16 @@ def include_relationship?(record, serialization_params)
end
end

def serializer
@serializer ||= @serializer_name.to_s.constantize
end

def record_type
@record_type || serializer.transformed_record_type
rescue NameError
run_key_transform(run_key_pluralization(base_key))
end

private

def ids_hash_from_record_and_relationship(record, params = {})
Expand All @@ -78,8 +92,8 @@ def ids_hash_from_record_and_relationship(record, params = {})
def id_hash_from_record(record, record_types)
klass_name = record.class.respond_to?(:model_name) ? record.class.model_name.to_s : record.class.name
# memoize the record type within the record_types dictionary, then assigning to record_type:
associated_record_type = record_types[record.class] ||= run_key_transform(klass_name.demodulize.underscore)

associated_record_type = record_types[record.class] ||= run_key_transform(run_key_pluralization(klass_name.demodulize.underscore))
id_hash(record.id, associated_record_type)
end

Expand Down Expand Up @@ -118,5 +132,14 @@ def run_key_transform(input)
input.to_sym
end
end

def run_key_pluralization(input)
return unless input
if self.pluralized_type
input.to_s.pluralize.to_sym
else
input.to_sym
end
end
end
end
3 changes: 2 additions & 1 deletion lib/fast_jsonapi/serialization_core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class << self
:cachable_relationships_to_serialize,
:uncachable_relationships_to_serialize,
:transform_method,
:pluralized_type,
:record_type,
:record_id,
:cache_length,
Expand Down Expand Up @@ -122,7 +123,7 @@ def get_included_records(record, includes_list, known_included_objects, fieldset
next unless relationship_item.include_relationship?(record, params)
unless relationship_item.polymorphic.is_a?(Hash)
record_type = relationship_item.record_type
serializer = relationship_item.serializer.to_s.constantize
serializer = relationship_item.serializer
end
relationship_type = relationship_item.relationship_type

Expand Down
77 changes: 74 additions & 3 deletions spec/lib/object_serializer_class_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
end

after do
serializer.relationships_to_serialize = {}
MovieSerializer.relationships_to_serialize.delete(children.first)
end

context 'with namespace' do
Expand Down Expand Up @@ -120,7 +120,7 @@
end

after do
MovieSerializer.relationships_to_serialize = {}
MovieSerializer.relationships_to_serialize.delete(parent.first)
end

context 'with overrides' do
Expand Down Expand Up @@ -176,7 +176,7 @@
end

after do
MovieSerializer.relationships_to_serialize = {}
MovieSerializer.relationships_to_serialize.delete(partner.first)
end

context 'with overrides' do
Expand Down Expand Up @@ -486,4 +486,75 @@ def year_since_release_calculator(release_year)
end
end
end

describe '#pluralize_type' do
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }

before do
MovieSerializer.pluralize_type pluralize
end

after do
MovieSerializer.pluralize_type nil
MovieSerializer.set_type :movie
end

context 'when pluralize is true' do
let(:pluralize) { true }

it 'returns correct hash which type equals pluralized value' do
expect(serializable_hash[:data][:type]).to eq :movies
end
end

context 'when pluralize is false' do
let(:pluralize) { false }

it 'returns correct hash which type equals non-pluralized value' do
expect(serializable_hash[:data][:type]).to eq :movie
end
end
end

describe '#pluralize_type after #set_type' do
subject(:serializable_hash) { MovieSerializer.new(movie, include: [:actors]).serializable_hash }
let(:type_name) { :film }

before do
MovieSerializer.set_type type_name
MovieSerializer.pluralize_type true
end

after do
MovieSerializer.pluralize_type nil
MovieSerializer.set_type :movie
end

context 'when sets singular type name' do
it 'returns correct hash which type equals transformed set_type value' do
expect(serializable_hash[:data][:type]).to eq :films
end
end

context 'when sets plural type name' do
it 'returns correct hash which type equals transformed set_type value' do
expect(serializable_hash[:data][:type]).to eq :films
end
end

context 'when pluralizing a relationship type after #set_type' do
before do
ActorSerializer.pluralize_type true
end

after do
ActorSerializer.pluralize_type nil
end

it 'returns correct hash which relationship type equals transformed set_type value' do
expect(serializable_hash[:data][:relationships][:actors][:data][0][:type]).to eq(:actors)
expect(serializable_hash[:included][0][:type]).to eq(:actors)
end
end
end
end
21 changes: 17 additions & 4 deletions spec/lib/object_serializer_inheritance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,23 @@ class EmployeeSerializer < UserSerializer
has_one :account
end

class LegacyUserSerializer < UserSerializer
pluralize_type true
end

class LegacyEmployeeSerializer < LegacyUserSerializer
attributes :location
attributes :compensation

has_one :account
end

it 'sets the correct record type' do
expect(EmployeeSerializer.reflected_record_type).to eq :employee
expect(EmployeeSerializer.record_type).to eq :employee
end

context 'when testing inheritance of attributes' do

it 'includes parent attributes' do
subclass_attributes = EmployeeSerializer.attributes_to_serialize
superclass_attributes = UserSerializer.attributes_to_serialize
Expand Down Expand Up @@ -157,12 +167,15 @@ class EmployeeSerializer < UserSerializer
end
end

context 'when test inheritence of other attributes' do

it 'inherits the tranform method' do
context 'when testing inheritence of other attributes' do
it 'inherits the transform method' do
EmployeeSerializer
expect(UserSerializer.transform_method).to eq EmployeeSerializer.transform_method
end

it 'inherits pluralized_type' do
LegacyEmployeeSerializer
expect(LegacyUserSerializer.pluralized_type).to eq LegacyEmployeeSerializer.pluralized_type
end
end
end
Loading