Class: JsonApiServer::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/json_api_server/builder.rb

Overview

This class integrates JSON API features – pagination, sorting, filters, inclusion of related resources, and sparse fieldsets – in one place. It collects data to be used by serializers.

  • It merges JSON API sub-queries (i.e, filters, pagination, sorting, etc.) into a query.

  • It provides convenience methods to requested sparse fields, includes and pagination.

Usage:

The Builder class takes two arguments, (1) the controller request and (2) an initial query (i.e., MyModel.all, MyModel.authorized_to(current_user), etc.).

Add JSON API features appropriate for the request. These methods are chainable. Except for sparse fields, JSON API features are configured; filter, include and sort configurations whitelist attributes/behavior while pagination sets defaults and limits on number of records to return.

  • add_pagination(pagination_options) - for collections

  • add_filter(filter_options) - for collections

  • add_include(include_options)

  • add_sort(sort_options) - for collections

  • add_fields

Once features are added, their corresponding values can be accessed:

  • query - memoized merged query (merges initial request with sort, pagination, filters, includes sub-queries if any)

  • paginator - Paginator class for pagination links.

  • includes - Array of requested (whitelisted) includes (i.e, ['comments', 'comment.author'])

  • sparse_fields - Hash of type/fields (i.e., => [’title’, ‘body’, ‘author’], ‘people’ => [‘name’])

Example
attr_accessor :pagination_options, :sort_options, :filter_options, :include_options

before_action do |c|
  c.pagination_options = { default_per_page: 10, max_per_page: 60 }
  c.sort_options = {
   permitted: [:character, :location, :published],
   default: { id: :desc }
  }
  c.filter_options = [
   { id: { type: 'Integer' } },
   { published: { type: 'Date' } },
   :location,
   { book: { wildcard: :both } }
  ]
  c.include_options = [
                        {'publisher': -> { includes(:publisher) }},
                        {'comments': -> { includes(:comments) }},
                       'comment.author'
                     ]
end

# A collection.
def index
  builder = JsonApiServer::Builder.new(request, Topic.current)
   .add_pagination(pagination_options)
   .add_filter(filter_options)
   .add_include(include_options)
   .add_sort(sort_options)
   .add_fields

 serializer = TopicsSerializer.from_builder(builder)
 render json: serializer.to_json, status: :ok
end

# A resource.
def show
  builder = JsonApiServer::Builder.new(request, Topic.find(params[:id]))
   .add_include(['publisher', 'comments', 'comments.includes'])
   .add_fields

  serializer = TopicSerializer.from_builder(builder)
  render json: serializer.to_json, status: :ok
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, query) ⇒ Builder

Arguments:

  • request - an ActionDispatch::Request

  • query (ActiveRecord::Relation) - Initial query.



102
103
104
105
106
# File 'lib/json_api_server/builder.rb', line 102

def initialize(request, query)
  @request = request
  @initial_query = query
  @model = model_from_query(@initial_query)
end

Instance Attribute Details

#fieldsObject (readonly)

JsonApiServer::Fields instance if #add_fields was called. nil otherwise.



85
86
87
# File 'lib/json_api_server/builder.rb', line 85

def fields
  @fields
end

#filterObject (readonly)

JsonApiServer::Filter instance if #add_filter was called. nil otherwise.



91
92
93
# File 'lib/json_api_server/builder.rb', line 91

def filter
  @filter
end

#includeObject (readonly)

JsonApiServer::Include instance if #add_include was called. nil otherwise.



94
95
96
# File 'lib/json_api_server/builder.rb', line 94

def include
  @include
end

#modelObject (readonly)

ActiveRecord::Base model extracted from initial query passed in constructor.



82
83
84
# File 'lib/json_api_server/builder.rb', line 82

def model
  @model
end

#paginationObject (readonly)

JsonApiServer::Pagination instance if #add_pagination was called. nil otherwise.



88
89
90
# File 'lib/json_api_server/builder.rb', line 88

def pagination
  @pagination
end

#requestObject (readonly)

ActionDispatch::Request passed in constructor.



79
80
81
# File 'lib/json_api_server/builder.rb', line 79

def request
  @request
end

#sortObject (readonly)

JsonApiServer::Sort instance if #add_sort was called. nil otherwise.



97
98
99
# File 'lib/json_api_server/builder.rb', line 97

def sort
  @sort
end

Instance Method Details

#add_fieldsObject

Creates JsonApiServer::Fields instance based on request. Instance is available through the #fields attribute.



165
166
167
168
# File 'lib/json_api_server/builder.rb', line 165

def add_fields
  @fields = JsonApiServer::Fields.new(request)
  self
end

#add_filter(permitted = []) ⇒ Object

Creates JsonApiServer::Filter instance based on request, initial query model and filter configs. Instance is available through the #filter attribute.

  • permitted - JsonApiServer::Filter configs.



138
139
140
141
# File 'lib/json_api_server/builder.rb', line 138

def add_filter(permitted = [])
  @filter = JsonApiServer::Filter.new(request, model, permitted)
  self
end

#add_include(permitted = []) ⇒ Object

Creates JsonApiServer::Include instance based on request, initial query model and include configs. Instance is available through the #include attribute.

  • permitted - JsonApiServer::Include configs.



148
149
150
151
# File 'lib/json_api_server/builder.rb', line 148

def add_include(permitted = [])
  @include = JsonApiServer::Include.new(request, model, permitted)
  self
end

#add_pagination(**options) ⇒ Object

Creates JsonApiServer::Pagination instance based on request, initial query model and pagination options. Instance is available through the #pagination attribute. For collections only.

  • options - JsonApiServer::Pagination options.



128
129
130
131
# File 'lib/json_api_server/builder.rb', line 128

def add_pagination(**options)
  @pagination = JsonApiServer::Pagination.new(request, model, options)
  self
end

#add_sort(**options) ⇒ Object

Creates JsonApiServer::Sort instance based on request, initial query model and sort options. Instance is available through the #sort attribute.

  • options - JsonApiServer::Sort options.



158
159
160
161
# File 'lib/json_api_server/builder.rb', line 158

def add_sort(**options)
  @sort = JsonApiServer::Sort.new(request, model, options)
  self
end

#includesObject

(Array or nil) Whitelisted includes. An array of relationships (strings) if #add_include was called previously, nil otherwise. i.e,

['comments', 'comments.author']


180
181
182
# File 'lib/json_api_server/builder.rb', line 180

def includes
  @include.try(:includes)
end

#paginatorObject

JsonApiServer::Paginator instance for collection if #add_pagination was called previously, nil otherwise.



172
173
174
# File 'lib/json_api_server/builder.rb', line 172

def paginator
  @pagination.try(:paginator_for, relation)
end

#relationObject Also known as: query

Merges pagination, filter, sort, and include sub-queries (if defined) into the initial query. Returns an ActiveRecord::Relation object.



110
111
112
113
114
115
116
117
118
119
# File 'lib/json_api_server/builder.rb', line 110

def relation
  @relation ||= begin
    return @initial_query unless @initial_query.respond_to?(:where)

    %i[pagination filter include sort].each_with_object(@initial_query) do |method, query|
      frag = send(method).try(:relation)
      query.merge!(frag) if frag
    end
  end
end

#sparse_fieldsObject

(Hash or nil) Sparse fields. Available if #add_fields was previously called. i.e.,

{'articles => ['title', 'body', 'author'], 'people' => ['name']}


187
188
189
# File 'lib/json_api_server/builder.rb', line 187

def sparse_fields
  @fields.try(:sparse_fields)
end