patternrubyMinor
Classes to serialize departments and their employees
Viewed 0 times
employeesserializedepartmentsclassesandtheir
Problem
I have many small but similar classes like these:
As you can see they're all just variations on a theme. There is a potential here for a combinatorial explosion of classes if there are enough use cases. Should I do something like
class DepartmentSerializer < Serializer
self.root = false
attributes :id, :name
has_many :employees, serializer: EmployeeSerializer
has_one :location, serializer: LocationNameSerializer
end
class DepartmentWithManagersSerializer < Serializer
self.root = false
attributes :id, :name
has_many :primary_managers, serializer: ManagerSerializer
has_many :secondary_managers, serializer: ManagerSerializer
end
class DepartmentNameSerializer < Serializer
self.root = false
attributes :id, :name
end
class DepartmentWithEmployeesSerializer < Serializer
self.root = false
attributes :id, :name
has_many :employees, serializer: EmployeeNameSerializer
endAs you can see they're all just variations on a theme. There is a potential here for a combinatorial explosion of classes if there are enough use cases. Should I do something like
ActiveRecord's find methods which allow you to create methods like .find_by_name_and_manager_and_location?Solution
Using inheritance, first you can clean up your structure as following:
There is little you can do to really enhance your code any further in terms of readability and reusability if you want to separate all serializer contexts into separate classes like this.
If you are using
And your controllers can call the
class DepartmentNameSerializer < Serializer
self.root = false
attributes :id, :name
end
class DepartmentSerializer < DepartmentNameSerializer
has_many :employees, serializer: EmployeeSerializer
has_one :location, serializer: LocationNameSerializer
end
class DepartmentWithManagersSerializer < DepartmentNameSerializer
has_many :primary_managers, serializer: ManagerSerializer
has_many :secondary_managers, serializer: ManagerSerializer
end
class DepartmentWithEmployeesSerializer < DepartmentNameSerializer
has_many :employees, serializer: EmployeeNameSerializer
endThere is little you can do to really enhance your code any further in terms of readability and reusability if you want to separate all serializer contexts into separate classes like this.
If you are using
ActiveModel::Serializers which I think you do, since 0.8.0 you can use ActiveRecord-Serialization-like :except and :only to add some serialization context, so you could refactor your structure to work like the following:class DepartmentSerializer < Serializer
self.root = false
attributes :id, :name, :employee_names, :location_names
# it is afaik not possible to pass serialization context to the other serializers
# via association
# has_many :employee_names, serializer: EmployeeSerializer, only: :name
# has_one :location_names, serializer: LocationSerializer, only: :name
has_many :employees, serializer: EmployeeSerializer
has_many :primary_managers, serializer: ManagerSerializer
has_many :secondary_managers, serializer: ManagerSerializer
def employee_names
EmployeeSerializer.new(object.employees, only: :name) # + root: false if EmployeeSerializer uses a root
end
def location_names
LocationSerializer.new(object.locations, only: :name) # + root: false if LocationSerializer uses a root
end
endAnd your controllers can call the
DepartmentSerializer like this:class DepartmentController < ApplicationController # or other
def some_context_with_managers
render json: @department, except: %i(employees employee_names location_names)
end
# ... and so on
endCode Snippets
class DepartmentNameSerializer < Serializer
self.root = false
attributes :id, :name
end
class DepartmentSerializer < DepartmentNameSerializer
has_many :employees, serializer: EmployeeSerializer
has_one :location, serializer: LocationNameSerializer
end
class DepartmentWithManagersSerializer < DepartmentNameSerializer
has_many :primary_managers, serializer: ManagerSerializer
has_many :secondary_managers, serializer: ManagerSerializer
end
class DepartmentWithEmployeesSerializer < DepartmentNameSerializer
has_many :employees, serializer: EmployeeNameSerializer
endclass DepartmentSerializer < Serializer
self.root = false
attributes :id, :name, :employee_names, :location_names
# it is afaik not possible to pass serialization context to the other serializers
# via association
# has_many :employee_names, serializer: EmployeeSerializer, only: :name
# has_one :location_names, serializer: LocationSerializer, only: :name
has_many :employees, serializer: EmployeeSerializer
has_many :primary_managers, serializer: ManagerSerializer
has_many :secondary_managers, serializer: ManagerSerializer
def employee_names
EmployeeSerializer.new(object.employees, only: :name) # + root: false if EmployeeSerializer uses a root
end
def location_names
LocationSerializer.new(object.locations, only: :name) # + root: false if LocationSerializer uses a root
end
endclass DepartmentController < ApplicationController # or other
def some_context_with_managers
render json: @department, except: %i(employees employee_names location_names)
end
# ... and so on
endContext
StackExchange Code Review Q#37305, answer score: 2
Revisions (0)
No revisions yet.