rails-surrender 0.1.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +103 -0
- data/LICENSE +674 -0
- data/README.md +240 -0
- data/lib/rails/surrender/config/locales/en.yml +3 -0
- data/lib/rails/surrender/controller_additions.rb +13 -73
- data/lib/rails/surrender/fields/asks.rb +50 -0
- data/lib/rails/surrender/fields/attribute.rb +37 -0
- data/lib/rails/surrender/fields/expand.rb +65 -0
- data/lib/rails/surrender/fields/field_tree.rb +71 -0
- data/lib/rails/surrender/fields/id_list.rb +38 -0
- data/lib/rails/surrender/fields/node.rb +86 -0
- data/lib/rails/surrender/menu/field.rb +15 -0
- data/lib/rails/surrender/menu/filter.rb +11 -0
- data/lib/rails/surrender/menu/menu.rb +55 -0
- data/lib/rails/surrender/model_additions.rb +47 -32
- data/lib/rails/surrender/params/list_param.rb +23 -0
- data/lib/rails/surrender/railtie.rb +23 -9
- data/lib/rails/surrender/render/resource.rb +11 -30
- data/lib/rails/surrender/selection/filtering.rb +144 -0
- data/lib/rails/surrender/selection/page_size.rb +33 -0
- data/lib/rails/surrender/selection/selected.rb +50 -0
- data/lib/rails/surrender/selection/selection.rb +70 -0
- data/lib/rails/surrender/selection/sorting.rb +61 -0
- data/lib/rails/surrender/version.rb +1 -1
- data/lib/rails/surrender.rb +13 -12
- metadata +97 -35
- data/lib/rails/surrender/config/initializers/active_record_associations_patch.rb +0 -17
- data/lib/rails/surrender/config/initializers/active_record_preloader_patch.rb +0 -20
- data/lib/rails/surrender/helpers/filter_builder.rb +0 -50
- data/lib/rails/surrender/helpers/pagination_builder.rb +0 -25
- data/lib/rails/surrender/helpers/query_param_parser.rb +0 -98
- data/lib/rails/surrender/helpers/sort_builder.rb +0 -56
- data/lib/rails/surrender/model_filter_scopes.rb +0 -56
- data/lib/rails/surrender/render/configuration/inclusion_mapper_logic.rb +0 -69
- data/lib/rails/surrender/render/configuration/instance_logic.rb +0 -88
- data/lib/rails/surrender/render/configuration.rb +0 -93
- data/lib/rails/surrender/render/count.rb +0 -21
- data/lib/rails/surrender/render/ids.rb +0 -21
- data/lib/rails/surrender/render/resource/collection.rb +0 -26
- data/lib/rails/surrender/render/resource/inclusion_mapper.rb +0 -48
- data/lib/rails/surrender/render/resource/instance.rb +0 -75
- data/test/surrender_test.rb +0 -9
- data/test/test_helper.rb +0 -21
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Add filter_by_date_(to/from/before/after) methods for all *_at columns
|
|
4
|
-
module Rails
|
|
5
|
-
module Surrender
|
|
6
|
-
# apply filter scopes to the model
|
|
7
|
-
module ModelFilterScopes
|
|
8
|
-
def self.included(base)
|
|
9
|
-
base.extend(ClassMethods)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
module ClassMethods
|
|
13
|
-
def inherited(child)
|
|
14
|
-
super
|
|
15
|
-
|
|
16
|
-
# Bad things happen if you ask table_exists? to ApplicationRecord while it's loading!
|
|
17
|
-
# TODO: Add configuration in case ApplicationRecord is _not_ the primary abstract class
|
|
18
|
-
return if child.name == 'ApplicationRecord'
|
|
19
|
-
|
|
20
|
-
return unless child.table_exists?
|
|
21
|
-
|
|
22
|
-
child.instance_eval do
|
|
23
|
-
# scope to filter by every column name
|
|
24
|
-
apply_surrender_column_name_scopes(child)
|
|
25
|
-
|
|
26
|
-
# scope to filter by date or time column names
|
|
27
|
-
apply_surrender_column_datetime_scopes(child)
|
|
28
|
-
rescue StandardError
|
|
29
|
-
# TODO: why are tests failing here!!!
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def apply_surrender_column_name_scopes(child)
|
|
34
|
-
child.column_names.each do |column|
|
|
35
|
-
scope "filter_by_#{column}".to_sym, ->(val) { where({ column.to_sym => val }) }
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def apply_surrender_column_datetime_scopes(child)
|
|
40
|
-
with_surrender_datetime_columns(child) do |column|
|
|
41
|
-
base = column.split(/_at$/).first
|
|
42
|
-
|
|
43
|
-
scope "filter_by_#{base}_to".to_sym, ->(time) { where("#{child.table_name}.#{column} <= ?", time) }
|
|
44
|
-
scope "filter_by_#{base}_from".to_sym, ->(time) { where("#{child.table_name}.#{column} >= ?", time) }
|
|
45
|
-
scope "filter_by_#{base}_before".to_sym, ->(time) { where("#{child.table_name}.#{column} < ?", time) }
|
|
46
|
-
scope "filter_by_#{base}_after".to_sym, ->(time) { where("#{child.table_name}.#{column} > ?", time) }
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def with_surrender_datetime_columns(child, &block)
|
|
51
|
-
child.columns.select { |c| c.type.in? %i[date datetime] }.map(&:name).each(&block)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
# Container for config structure when rendering or generating the inclusion object.
|
|
7
|
-
class Configuration
|
|
8
|
-
module InclusionMapperLogic
|
|
9
|
-
def expanding_elements
|
|
10
|
-
list = resource_class_surrender_attributes_that_expand +
|
|
11
|
-
resource_class_surrender_expands +
|
|
12
|
-
resource_class_subclass_surrender_attributes_that_expand +
|
|
13
|
-
resource_class_subclass_surrender_expands +
|
|
14
|
-
user_included_joins_required +
|
|
15
|
-
ctrl_included_joins_required
|
|
16
|
-
.flatten.uniq
|
|
17
|
-
list
|
|
18
|
-
.map { |e| element_from(e) }
|
|
19
|
-
.reject do |element|
|
|
20
|
-
element.klass.in?(history) ||
|
|
21
|
-
element.name.in?(local_user_excludes) ||
|
|
22
|
-
(element.name.in?(local_ctrl_excludes) && !element.name.in?(local_user_includes))
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
def resource_class_surrender_attributes_that_expand
|
|
29
|
-
resource_class.surrender_attributes
|
|
30
|
-
.select { |attr| attr.match /_ids$/ }
|
|
31
|
-
.map { |attr| attr.to_s.sub('_ids', '').pluralize }
|
|
32
|
-
.select { |include| attribute_type(include).in? %i[expand associate] }
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def resource_class_surrender_expands
|
|
36
|
-
resource_class.surrender_expands
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def resource_class_subclass_surrender_attributes_that_expand
|
|
40
|
-
resource_class.subclasses.map do |subclass|
|
|
41
|
-
subclass.surrender_attributes
|
|
42
|
-
.select { |attr| attr.match /_ids$/ }
|
|
43
|
-
.map { |attr| attr.to_s.sub('_ids', '').pluralize }
|
|
44
|
-
.select { |include| attribute_type(include, resource_class: sc).in? %i[expand associate] }
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def resource_class_subclass_surrender_expands
|
|
49
|
-
resource_class.subclasses.map(&:surrender_expands)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def user_included_joins_required
|
|
53
|
-
top_level_keys_from(user_include).select { |include| attribute_type(include).in? %i[expand associate] }
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def ctrl_included_joins_required
|
|
57
|
-
top_level_keys_from(ctrl_include).select { |include| attribute_type(include).in? %i[expand associate] }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def element_from(item)
|
|
61
|
-
item_name = resource_class.reflections.key?(item.to_s) ? item.to_s : item.to_s.sub('_ids', '').pluralize
|
|
62
|
-
item_klass = resource_class.reflections[item_name].klass
|
|
63
|
-
Element.new name: item_name, klass: item_klass, continue: (item.to_s == item_name)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
# Container for config structure when rendering or generating the inclusion object.
|
|
7
|
-
class Configuration
|
|
8
|
-
module InstanceLogic
|
|
9
|
-
def nested_user_includes
|
|
10
|
-
next_level_asks_from(user_include)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def nested_ctrl_includes
|
|
14
|
-
next_level_asks_from(ctrl_include)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def nested_user_excludes
|
|
18
|
-
next_level_asks_from(user_exclude)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def nested_ctrl_excludes
|
|
22
|
-
next_level_asks_from(ctrl_exclude)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def locally_included_attributes
|
|
26
|
-
[].push(resource_class.surrender_attributes)
|
|
27
|
-
.push(ctrl_included_attributes)
|
|
28
|
-
.push(user_included_attributes_to_render)
|
|
29
|
-
.flatten.uniq
|
|
30
|
-
.reject { |attr| exclude_locally?(attr) }
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def locally_included_expands
|
|
34
|
-
[].push(user_included_local_expansions_to_render)
|
|
35
|
-
.push(ctrl_included_expansions)
|
|
36
|
-
.push(resource_class.surrender_expands)
|
|
37
|
-
.flatten.uniq
|
|
38
|
-
.each_with_object({}) { |key, result| result[key.to_sym] = [] }
|
|
39
|
-
.deep_merge(nested_user_includes)
|
|
40
|
-
.deep_merge(nested_ctrl_includes)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def exclude_locally?(key)
|
|
44
|
-
local_excludes.include?(key) && !local_user_includes.include?(key)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
def local_excludes
|
|
50
|
-
local_ctrl_excludes.dup.push(local_user_excludes).flatten.uniq
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def user_included_attributes
|
|
54
|
-
top_level_keys_from(user_include).select { |include| attribute_type(include).in? %i[include associate] }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def user_included_attributes_to_render
|
|
58
|
-
attrs = user_included_attributes
|
|
59
|
-
unavailable_attrs = (attrs - resource_class.surrender_callable_attributes)
|
|
60
|
-
return attrs if unavailable_attrs.empty?
|
|
61
|
-
|
|
62
|
-
raise Error, I18n.t('surrender.error.query_string.include.not_available', param: unavailable_attrs)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def user_included_expansions
|
|
66
|
-
local_user_includes.select { |i| attribute_type(i) == :expand }
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def user_included_local_expansions_to_render
|
|
70
|
-
expansions = user_included_expansions
|
|
71
|
-
unavailable_expansions = (expansions - resource_class.surrender_callable_expands)
|
|
72
|
-
return expansions if unavailable_expansions.empty?
|
|
73
|
-
|
|
74
|
-
raise Error, I18n.t('surrender.error.query_string.include.not_available', param: unavailable_expansions)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def ctrl_included_attributes
|
|
78
|
-
top_level_keys_from(ctrl_include).select { |include| attribute_type(include).in? %i[include associate] }
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def ctrl_included_expansions
|
|
82
|
-
local_ctrl_includes.select { |i| attribute_type(i) == :expand }
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'configuration/inclusion_mapper_logic'
|
|
4
|
-
require_relative 'configuration/instance_logic'
|
|
5
|
-
|
|
6
|
-
module Rails
|
|
7
|
-
module Surrender
|
|
8
|
-
module Render
|
|
9
|
-
# Container for config structure when rendering or generating the inclusion object.
|
|
10
|
-
class Configuration
|
|
11
|
-
include InclusionMapperLogic
|
|
12
|
-
include InstanceLogic
|
|
13
|
-
|
|
14
|
-
attr_accessor :resource_class,
|
|
15
|
-
:reload_resource,
|
|
16
|
-
:user_exclude,
|
|
17
|
-
:user_include,
|
|
18
|
-
:ctrl_exclude,
|
|
19
|
-
:ctrl_include,
|
|
20
|
-
:history
|
|
21
|
-
|
|
22
|
-
alias reload_resource? reload_resource
|
|
23
|
-
|
|
24
|
-
Element = Struct.new(:name, :klass, :continue, keyword_init: true)
|
|
25
|
-
|
|
26
|
-
def initialize(
|
|
27
|
-
resource_class: nil,
|
|
28
|
-
reload_resource: false,
|
|
29
|
-
user_exclude: [],
|
|
30
|
-
user_include: [],
|
|
31
|
-
ctrl_exclude: [],
|
|
32
|
-
ctrl_include: [],
|
|
33
|
-
history: []
|
|
34
|
-
)
|
|
35
|
-
@resource_class = resource_class
|
|
36
|
-
@reload_resource = reload_resource
|
|
37
|
-
@user_exclude = user_exclude.compact
|
|
38
|
-
@user_include = user_include
|
|
39
|
-
@ctrl_exclude = ctrl_exclude.compact
|
|
40
|
-
@ctrl_include = ctrl_include
|
|
41
|
-
@history = history
|
|
42
|
-
|
|
43
|
-
validate_user_includes!
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def validate_user_includes!
|
|
49
|
-
return if invalid_local_user_includes.empty?
|
|
50
|
-
|
|
51
|
-
raise Error, I18n.t('surrender.error.query_string.include.not_available', param: invalid_local_user_includes)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def invalid_local_user_includes
|
|
55
|
-
local_user_includes.select { |include| attribute_type(include) == :none }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def local_ctrl_excludes
|
|
59
|
-
top_level_keys_from(ctrl_exclude)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def local_ctrl_includes
|
|
63
|
-
top_level_keys_from(ctrl_include)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def local_user_excludes
|
|
67
|
-
top_level_keys_from(user_exclude)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def local_user_includes
|
|
71
|
-
top_level_keys_from(user_include)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def attribute_type(attr, klass: resource_class)
|
|
75
|
-
return :expand if klass.reflections.keys.include?(attr.to_s)
|
|
76
|
-
return :associate if klass.reflections.keys.include?(attr.to_s.sub('_ids', '').pluralize)
|
|
77
|
-
return :include if klass.attribute_names.include?(attr.to_s)
|
|
78
|
-
return :include if klass.instance_methods.include?(attr)
|
|
79
|
-
|
|
80
|
-
:none
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def top_level_keys_from(list)
|
|
84
|
-
list.map { |x| x.is_a?(Hash) ? x.keys : x }.flatten.map(&:to_sym).uniq
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def next_level_asks_from(list)
|
|
88
|
-
list.select { |x| x.is_a? Hash }.reduce({}, :merge).symbolize_keys
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
# Rendering a count of resources.
|
|
7
|
-
class Count
|
|
8
|
-
attr_reader :resource
|
|
9
|
-
|
|
10
|
-
def initialize(resource)
|
|
11
|
-
@resource = resource
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def parse
|
|
15
|
-
count = resource.respond_to?(:count) ? resource.count : 1
|
|
16
|
-
Response.new(data: { count: count })
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
# Rendering the IDs of the requested resources.
|
|
7
|
-
class Ids
|
|
8
|
-
attr_reader :resource
|
|
9
|
-
|
|
10
|
-
def initialize(resource)
|
|
11
|
-
@resource = resource
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def parse
|
|
15
|
-
ids = resource.respond_to?(:ids) ? resource.ids : [resource.id]
|
|
16
|
-
Response.new(data: { ids: ids })
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
class Resource
|
|
7
|
-
# Renders a collection resource
|
|
8
|
-
class Collection
|
|
9
|
-
attr_reader :resource, :config, :ability
|
|
10
|
-
|
|
11
|
-
def initialize(resource:, config:, ability:)
|
|
12
|
-
@resource = resource
|
|
13
|
-
@config = config
|
|
14
|
-
@ability = ability
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def render
|
|
18
|
-
return nil if resource.nil?
|
|
19
|
-
|
|
20
|
-
resource.map { |data| Instance.new(resource: data, config: config, ability: ability).render }
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
class Resource
|
|
7
|
-
# Builds a complete map of the resources needed to fulfill the request, for supplying to ActiveRecord.includes
|
|
8
|
-
class InclusionMapper
|
|
9
|
-
attr_reader :resource_class, :config
|
|
10
|
-
|
|
11
|
-
Element = Struct.new(:name, :klass, keyword_init: true)
|
|
12
|
-
# InclusionMapper is designed to recursively crawl through the model rendering structure and build a hash
|
|
13
|
-
# that ActiveRecord can use to eager load ALL of the data we're going to render, to prevent N+1 queries
|
|
14
|
-
def initialize(resource_class:, config:)
|
|
15
|
-
@resource_class = resource_class
|
|
16
|
-
@config = config
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def parse
|
|
20
|
-
config.history.push resource_class
|
|
21
|
-
includes = []
|
|
22
|
-
|
|
23
|
-
config.expanding_elements.each do |element|
|
|
24
|
-
item_config = Configuration.new(
|
|
25
|
-
resource_class: element.klass,
|
|
26
|
-
user_include: config.nested_user_includes[element.name] || [],
|
|
27
|
-
ctrl_include: config.nested_ctrl_includes[element.name] || [],
|
|
28
|
-
user_exclude: config.nested_user_excludes[element.name] || [],
|
|
29
|
-
ctrl_exclude: config.nested_ctrl_excludes[element.name] || [],
|
|
30
|
-
history: config.history.dup.push(element.klass)
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
nested = if element.continue
|
|
34
|
-
InclusionMapper.new(resource_class: element.klass, config: item_config).parse
|
|
35
|
-
else
|
|
36
|
-
[]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
includes << (nested.size.zero? ? element.name : { element.name => nested })
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
includes.sort_by { |x| x.is_a?(Symbol) ? 0 : 1 }
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module Surrender
|
|
5
|
-
module Render
|
|
6
|
-
class Resource
|
|
7
|
-
# Renders an instance resource
|
|
8
|
-
class Instance
|
|
9
|
-
attr_reader :resource, :config, :ability
|
|
10
|
-
|
|
11
|
-
def initialize(resource:, config:, ability:)
|
|
12
|
-
@resource = resource
|
|
13
|
-
@config = config
|
|
14
|
-
@ability = ability
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def render
|
|
18
|
-
config.history.push history_class
|
|
19
|
-
|
|
20
|
-
result = {}
|
|
21
|
-
config.locally_included_attributes.each { |attr| result[attr.to_sym] = resource.send(attr) }
|
|
22
|
-
|
|
23
|
-
config.locally_included_expands.each_key do |key|
|
|
24
|
-
next if config.exclude_locally?(key)
|
|
25
|
-
|
|
26
|
-
nested_resource_class = nested_class_for(resource, key)
|
|
27
|
-
next if config.history.include? nested_resource_class
|
|
28
|
-
|
|
29
|
-
nested_config = nested_config_for(nested_resource_class, key)
|
|
30
|
-
|
|
31
|
-
if resource.class.reflections[key.to_s].try(:collection?)
|
|
32
|
-
collection = resource.send(key.to_sym).select { |i| ability.can? :read, i }
|
|
33
|
-
result[key] = Collection.new(resource: collection, config: nested_config, ability: ability).render
|
|
34
|
-
else
|
|
35
|
-
instance = resource.send(key)
|
|
36
|
-
next if config.history.include? instance.class
|
|
37
|
-
|
|
38
|
-
if ability.can?(:read, instance)
|
|
39
|
-
result[key] = Instance.new(resource: instance, config: nested_config, ability: ability).render
|
|
40
|
-
elsif instance.nil?
|
|
41
|
-
result[key] = nil # represent an associated element as null if it's missing
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
result
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
def nested_class_for(resource, key)
|
|
51
|
-
resource.class.reflections[key.to_s].klass
|
|
52
|
-
rescue NoMethodError
|
|
53
|
-
resource.send(key).class
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def nested_config_for(nested_resource_class, key)
|
|
57
|
-
Configuration.new(
|
|
58
|
-
resource_class: nested_resource_class,
|
|
59
|
-
user_include: config.nested_user_includes[key] || [],
|
|
60
|
-
ctrl_include: config.nested_ctrl_includes[key] || [],
|
|
61
|
-
user_exclude: config.nested_user_excludes[key] || [],
|
|
62
|
-
ctrl_exclude: config.nested_ctrl_excludes[key] || [],
|
|
63
|
-
history: config.history
|
|
64
|
-
)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# get to the root subclass for sti models and store that as history
|
|
68
|
-
def history_class
|
|
69
|
-
resource.class.superclass until resource.class.superclass.in? [ActiveRecord::Base, ApplicationRecord]
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
data/test/surrender_test.rb
DELETED
data/test/test_helper.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Configure Rails Environment
|
|
4
|
-
ENV['RAILS_ENV'] = 'test'
|
|
5
|
-
|
|
6
|
-
require File.expand_path('../test/dummy/config/environment.rb', __dir__)
|
|
7
|
-
ActiveRecord::Migrator.migrations_paths = [File.expand_path('../test/dummy/db/migrate', __dir__)]
|
|
8
|
-
require 'rails/test_help'
|
|
9
|
-
|
|
10
|
-
# Filter out Minitest backtrace while allowing backtrace from other libraries
|
|
11
|
-
# to be shown.
|
|
12
|
-
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
|
13
|
-
|
|
14
|
-
# Load support files
|
|
15
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
16
|
-
|
|
17
|
-
# Load fixtures from the engine
|
|
18
|
-
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
|
|
19
|
-
ActiveSupport::TestCase.fixture_path = File.expand_path('fixtures', __dir__)
|
|
20
|
-
ActiveSupport::TestCase.fixtures :all
|
|
21
|
-
end
|