Skip to content

Commit 54692ac

Browse files
author
Lee Richmond
committed
General refactor
Break up module into separate classes, so we don't pollute controllers and have better code organization.
1 parent a86b7e7 commit 54692ac

File tree

14 files changed

+318
-179
lines changed

14 files changed

+318
-179
lines changed

lib/jsonapi_compliable.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
require "jsonapi_compliable/version"
66
require "jsonapi_compliable/errors"
77
require "jsonapi_compliable/dsl"
8+
require "jsonapi_compliable/scope/base"
9+
require "jsonapi_compliable/scope/sort"
10+
require "jsonapi_compliable/scope/paginate"
11+
require "jsonapi_compliable/scope/sideload"
12+
require "jsonapi_compliable/scope/extra_fields"
13+
require "jsonapi_compliable/scope/filterable"
14+
require "jsonapi_compliable/scope/default_filter"
15+
require "jsonapi_compliable/scope/filter"
16+
require "jsonapi_compliable/util/include_params"
17+
require "jsonapi_compliable/util/field_params"
818

919
module JSONAPICompliable
1020
autoload :Base, 'jsonapi_compliable/base'

lib/jsonapi_compliable/base.rb

Lines changed: 12 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -7,115 +7,9 @@ module Base
77

88
included do
99
class_attribute :_jsonapi_compliable
10-
11-
def self.inherited(klass)
12-
klass._jsonapi_compliable = nil
13-
end
14-
1510
before_action :parse_fieldsets!
1611
end
1712

18-
# Private to module
19-
class CompareIncludes
20-
def self.call(includes, whitelist)
21-
{}.tap do |valid|
22-
includes.to_hash.each_pair do |key, sub_hash|
23-
if whitelist[key]
24-
valid[key] = CompareIncludes.call(sub_hash, whitelist[key])
25-
end
26-
end
27-
end
28-
end
29-
end
30-
31-
# Converts include params like foo.bar,baz
32-
# to AMS-compliant
33-
# {foo: {bar: {}}, baz: {}}
34-
#
35-
# Ensures they are part of the whitelist
36-
def scrub_includes
37-
return unless params[:include]
38-
39-
includes = _jsonapi_compliable.parse_includes(params[:include])
40-
whitelist = _jsonapi_compliable.sideloads[:whitelist][params[:action]]
41-
whitelist ? CompareIncludes.call(includes, whitelist) : {}
42-
end
43-
44-
def jsonapi_includes(scope)
45-
scrubbed = scrub_includes
46-
return scope unless scrubbed
47-
48-
scope = if custom_include = _jsonapi_compliable.sideloads[:custom_function]
49-
custom_include.call(scope, scrubbed)
50-
else
51-
scope.includes(scrubbed)
52-
end
53-
54-
scope
55-
end
56-
57-
def jsonapi_sort(scope)
58-
sort_param = params[:sort] || 'id'
59-
dir = sort_param.starts_with?('-') ? :desc : :asc
60-
att = sort_param.sub('-', '').to_sym
61-
62-
scope = if custom_sort = _jsonapi_compliable.sorting
63-
custom_sort.call(scope, att, dir)
64-
else
65-
scope.order(att => dir)
66-
end
67-
68-
scope
69-
end
70-
71-
def jsonapi_paginate(scope)
72-
page_param = params[:page] || {}
73-
number = (page_param[:number] || default_page_number).to_i
74-
size = (page_param[:size] || default_page_size).to_i
75-
76-
if size > MAX_PAGE_SIZE
77-
raise JSONAPICompliable::Errors::UnsupportedPageSize,"Requested page size #{size} is greater than max supported size #{MAX_PAGE_SIZE}"
78-
end
79-
80-
scope = if custom_pagination = _jsonapi_compliable.pagination
81-
custom_pagination.call(scope, number, size)
82-
else
83-
scope.page(number).per(size)
84-
end
85-
86-
scope
87-
end
88-
89-
def jsonapi_filter(scope)
90-
param_filters = params[:filter] || {}
91-
scope = _jsonapi_compliable.default_filter_scope(self, scope)
92-
param_filters.each_pair do |param_name, param_value|
93-
scope = _jsonapi_compliable.filter_scope(self, scope, param_name, param_value)
94-
end
95-
96-
scope
97-
end
98-
99-
def jsonapi_extra_fields(scope)
100-
_jsonapi_compliable.extra_fields.each_pair do |namespace, extra_fields|
101-
extra_fields.each do |extra_field|
102-
if requested_extra_field?(namespace, extra_field[:name])
103-
scope = extra_field[:proc].call(scope)
104-
end
105-
end
106-
end
107-
108-
scope
109-
end
110-
111-
def requested_extra_field?(namespace, field)
112-
if namespaced = params[:extra_fields].try(:[], namespace)
113-
namespaced.include?(field)
114-
else
115-
false
116-
end
117-
end
118-
11913
def default_page_number
12014
1
12115
end
@@ -130,44 +24,29 @@ def jsonapi_scope(scope,
13024
paginate: true,
13125
extra_fields: true,
13226
sort: true)
133-
scope = jsonapi_filter(scope) if filter
134-
scope = jsonapi_extra_fields(scope) if extra_fields
135-
scope = jsonapi_includes(scope) if includes
136-
scope = jsonapi_sort(scope) if sort
137-
scope = jsonapi_paginate(scope) if paginate
27+
scope = JSONAPICompliable::Scope::DefaultFilter.new(self, scope).apply
28+
scope = JSONAPICompliable::Scope::Filter.new(self, scope).apply if filter
29+
scope = JSONAPICompliable::Scope::ExtraFields.new(self, scope).apply if extra_fields
30+
scope = JSONAPICompliable::Scope::Sideload.new(self, scope).apply if includes
31+
scope = JSONAPICompliable::Scope::Sort.new(self, scope).apply if sort
32+
scope = JSONAPICompliable::Scope::Paginate.new(self, scope).apply if paginate
13833
scope
13934
end
14035

141-
def fieldset(name)
142-
params[name].to_unsafe_hash.deep_symbolize_keys
143-
end
144-
145-
def fieldset?(name)
146-
params[name].present?
147-
end
148-
14936
def parse_fieldsets!
150-
parse_fieldset!(:fields)
151-
parse_fieldset!(:extra_fields)
152-
end
153-
154-
def parse_fieldset!(name)
155-
return unless params[name]
156-
157-
params[name].each_pair do |key, value|
158-
params[name][key] = value.split(',').map(&:to_sym)
159-
end
37+
Util::FieldParams.parse!(params, :fields)
38+
Util::FieldParams.parse!(params, :extra_fields)
16039
end
16140

16241
# * Eager loads whitelisted includes
16342
# * Merges opts and ams_default_options
16443
def render_ams(scope, opts = {})
16544
scope = jsonapi_scope(scope) if scope.is_a?(ActiveRecord::Relation)
16645
options = default_ams_options
167-
options[:include] = forced_includes || scrub_includes
46+
options[:include] = forced_includes || Util::IncludeParams.scrub(self)
16847
options[:json] = scope
169-
options[:fields] = fieldset(:fields) if fieldset?(:fields)
170-
options[:extra_fields] = fieldset(:extra_fields) if fieldset?(:extra_fields)
48+
options[:fields] = Util::FieldParams.fieldset(params, :fields) if params[:fields]
49+
options[:extra_fields] = Util::FieldParams.fieldset(params, :extra_fields) if params[:extra_fields]
17150

17251
options.merge!(opts)
17352
render(options)
@@ -211,5 +90,5 @@ def jsonapi(&blk)
21190
self._jsonapi_compliable = dsl
21291
end
21392
end
214-
end
93+
end
21594
end

lib/jsonapi_compliable/dsl.rb

Lines changed: 2 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ def initialize
1717
end
1818

1919
def includes(whitelist: nil, &blk)
20-
whitelist = parse_includes(whitelist) if whitelist
20+
whitelist = JSONAPI::IncludeDirective.new(whitelist) if whitelist
2121

2222
@sideloads = {
2323
whitelist: whitelist,
24-
custom_function: blk
24+
custom_scope: blk
2525
}
2626
end
2727

@@ -56,48 +56,5 @@ def extra_field(field, &blk)
5656
proc: blk
5757
}
5858
end
59-
60-
def parse_includes(includes)
61-
JSONAPI::IncludeDirective.new(includes)
62-
end
63-
64-
def filter_scope(controller, scope, name, value)
65-
name = name.to_sym
66-
filter = find_filter!(controller, name)
67-
value = value.split(',') if value.include?(',')
68-
69-
if custom_scope = filter.values.first[:filter]
70-
custom_scope.call(scope, value)
71-
else
72-
scope.where(filter.keys.first => value)
73-
end
74-
end
75-
76-
def default_filter_scope(controller, scope)
77-
@default_filters.each_pair do |name, opts|
78-
next if find_filter(controller, name)
79-
scope = opts[:filter].call(scope)
80-
end
81-
82-
scope
83-
end
84-
85-
private
86-
87-
def find_filter(controller, name)
88-
find_filter!(controller, name)
89-
rescue JSONAPICompliable::Errors::BadFilter
90-
nil
91-
end
92-
93-
def find_filter!(controller, name)
94-
filter_name, filter_value = \
95-
@filters.find { |_name, opts| opts[:aliases].include?(name.to_sym) }
96-
raise JSONAPICompliable::Errors::BadFilter unless filter_name
97-
if guard = filter_value[:if]
98-
raise JSONAPICompliable::Errors::BadFilter if controller.send(guard) == false
99-
end
100-
{ filter_name => filter_value }
101-
end
10259
end
10360
end

lib/jsonapi_compliable/errors.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
module JSONAPICompliable
22
module Errors
33
class BadFilter < StandardError; end
4-
class UnsupportedPageSize < StandardError; end
4+
5+
class UnsupportedPageSize < StandardError
6+
def initialize(size, max)
7+
@size, @max = size, max
8+
end
9+
10+
def message
11+
"Requested page size #{@size} is greater than max supported size #{@max}"
12+
end
13+
end
514
end
615
end

lib/jsonapi_compliable/scope/base.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module JSONAPICompliable
2+
module Scope
3+
class Base
4+
attr_reader :controller, :dsl, :params, :scope
5+
6+
def initialize(controller, scope)
7+
@controller = controller
8+
@dsl = controller._jsonapi_compliable
9+
@params = controller.params
10+
@scope = scope
11+
end
12+
13+
def apply
14+
apply_standard_or_override
15+
end
16+
17+
def apply_standard_or_override
18+
if apply_standard_scope?
19+
@scope = apply_standard_scope
20+
else
21+
@scope = apply_custom_scope
22+
end
23+
24+
@scope
25+
end
26+
27+
def apply_standard_scope?
28+
custom_scope.nil?
29+
end
30+
31+
def apply_standard_scope
32+
raise 'override in subclass'
33+
end
34+
35+
def apply_custom_scope
36+
raise 'override in subclass'
37+
end
38+
end
39+
end
40+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module JSONAPICompliable
2+
class Scope::DefaultFilter < Scope::Base
3+
include Scope::Filterable
4+
5+
def apply
6+
dsl.default_filters.each_pair do |name, opts|
7+
next if find_filter(name)
8+
@scope = opts[:filter].call(@scope)
9+
end
10+
11+
@scope
12+
end
13+
end
14+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module JSONAPICompliable
2+
class Scope::ExtraFields < Scope::Base
3+
4+
def apply
5+
each_extra_field do |extra_field|
6+
@scope = extra_field[:proc].call(@scope)
7+
end
8+
9+
@scope
10+
end
11+
12+
private
13+
14+
def each_extra_field
15+
dsl.extra_fields.each_pair do |namespace, extra_fields|
16+
extra_fields.each do |extra_field|
17+
if requested_extra_field?(namespace, extra_field[:name])
18+
yield extra_field
19+
end
20+
end
21+
end
22+
end
23+
24+
def extra_fields
25+
params[:extra_fields] || {}
26+
end
27+
28+
def requested_extra_field?(namespace, field)
29+
if namespaced = extra_fields[namespace]
30+
namespaced.include?(field)
31+
else
32+
false
33+
end
34+
end
35+
end
36+
end

0 commit comments

Comments
 (0)