plutonium 0.18.4 → 0.18.6
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/app/assets/plutonium.js +1 -1
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +1 -1
- data/app/assets/plutonium.min.js.map +2 -2
- data/app/views/resource/interactive_bulk_action.html.erb +1 -1
- data/app/views/resource/interactive_resource_action.html.erb +1 -1
- data/lib/generators/pu/eject/layout/layout_generator.rb +1 -2
- data/lib/generators/pu/eject/shell/shell_generator.rb +1 -3
- data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +4 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +80 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/resource_selector.rb +48 -0
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +2 -42
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +1 -4
- data/lib/generators/pu/res/conn/conn_generator.rb +9 -21
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +10 -5
- data/lib/plutonium/core/controller.rb +2 -1
- data/lib/plutonium/resource/controller.rb +21 -7
- data/lib/plutonium/resource/controllers/authorizable.rb +8 -2
- data/lib/plutonium/resource/controllers/crud_actions.rb +17 -17
- data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
- data/lib/plutonium/resource/controllers/presentable.rb +2 -2
- data/lib/plutonium/resource/record/associated_with.rb +83 -0
- data/lib/plutonium/resource/record/associations.rb +92 -0
- data/lib/plutonium/resource/record/field_names.rb +83 -0
- data/lib/plutonium/resource/record/labeling.rb +19 -0
- data/lib/plutonium/resource/record/routes.rb +66 -0
- data/lib/plutonium/resource/record.rb +6 -258
- data/lib/plutonium/routing/mapper_extensions.rb +2 -2
- data/lib/plutonium/routing/route_set_extensions.rb +7 -4
- data/lib/plutonium/ui/breadcrumbs.rb +4 -4
- data/lib/plutonium/ui/component/methods.rb +4 -12
- data/lib/plutonium/ui/form/base.rb +22 -10
- data/lib/plutonium/ui/form/components/secure_association.rb +112 -0
- data/lib/plutonium/ui/form/components/secure_polymorphic_association.rb +54 -0
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +0 -1
- data/lib/plutonium/ui/form/theme.rb +12 -1
- data/lib/plutonium/ui/page/show.rb +1 -1
- data/lib/plutonium/ui/page_header.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/src/js/controllers/slim_select_controller.js +3 -1
- metadata +11 -4
- data/lib/plutonium/ui/form/components/belongs_to.rb +0 -66
- data/lib/plutonium/ui/form/components/has_many.rb +0 -66
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/plutonium/resource/associations.rb
|
4
|
+
module Plutonium
|
5
|
+
module Resource
|
6
|
+
module Record
|
7
|
+
module AssociatedWith
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
scope :associated_with, ->(record) do
|
12
|
+
named_scope = :"associated_with_#{record.model_name.singular}"
|
13
|
+
return send(named_scope, record) if respond_to?(named_scope)
|
14
|
+
|
15
|
+
own_association = klass.find_association_from_self_to_record(record)
|
16
|
+
if own_association
|
17
|
+
return klass.query_based_on_association(own_association, record)
|
18
|
+
end
|
19
|
+
|
20
|
+
record_association = klass.find_association_to_self_from_record(record)
|
21
|
+
if record_association
|
22
|
+
Plutonium.logger.warn do
|
23
|
+
[
|
24
|
+
"Using indirect association from #{record.class} to #{klass.name}",
|
25
|
+
"via '#{record_association.name}'.",
|
26
|
+
"This may result in poor query performance for large datasets",
|
27
|
+
"as it requires loading records to perform the association.",
|
28
|
+
"",
|
29
|
+
"Consider defining a direct association or implementing",
|
30
|
+
"a custom scope '#{named_scope}' for better performance."
|
31
|
+
].join("\n")
|
32
|
+
end
|
33
|
+
return where(id: record.public_send(record_association.name))
|
34
|
+
end
|
35
|
+
|
36
|
+
klass.raise_unresolvable_association_error(record, named_scope)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class_methods do
|
41
|
+
def find_association_from_self_to_record(record)
|
42
|
+
reflect_on_all_associations.find do |assoc|
|
43
|
+
assoc.klass.name == record.class.name unless assoc.polymorphic?
|
44
|
+
rescue
|
45
|
+
assoc.check_validity!
|
46
|
+
raise
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_association_to_self_from_record(record)
|
51
|
+
record.class.reflect_on_all_associations.find do |assoc|
|
52
|
+
assoc.klass.name == name
|
53
|
+
rescue
|
54
|
+
assoc.check_validity!
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def query_based_on_association(assoc, record)
|
60
|
+
case assoc.macro
|
61
|
+
when :has_one
|
62
|
+
joins(assoc.name).where(assoc.name => {record.class.primary_key => record.id})
|
63
|
+
when :belongs_to
|
64
|
+
where(assoc.name => record)
|
65
|
+
when :has_many
|
66
|
+
joins(assoc.name).where(assoc.klass.table_name => record)
|
67
|
+
else
|
68
|
+
raise NotImplementedError, "associated_with->##{assoc.macro}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def raise_unresolvable_association_error(record, named_scope)
|
73
|
+
raise "Could not resolve the association between '#{name}' and '#{record.class.name}'\n\n" \
|
74
|
+
"Define\n" \
|
75
|
+
" 1. the associations between the models\n" \
|
76
|
+
" 2. a named scope on #{name} e.g.\n\n" \
|
77
|
+
"scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/plutonium/resource/associations.rb
|
4
|
+
module Plutonium
|
5
|
+
module Resource
|
6
|
+
module Record
|
7
|
+
module Associations
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def belongs_to(name, scope = nil, **options)
|
12
|
+
super
|
13
|
+
include_secure_association_methods(:belongs_to, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_one(name, scope = nil, **options)
|
17
|
+
super
|
18
|
+
include_secure_association_methods(:has_one, name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_many(name, scope = nil, **options)
|
22
|
+
super
|
23
|
+
include_secure_association_methods(:has_many, name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_and_belongs_to_many(name, scope = nil, **options)
|
27
|
+
super
|
28
|
+
include_secure_association_methods(:has_and_belongs_to_many, name)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def include_secure_association_methods(association_type, name)
|
34
|
+
mod = Module.new
|
35
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
36
|
+
extend ActiveSupport::Concern
|
37
|
+
|
38
|
+
#{generate_sgid_methods(association_type, name)}
|
39
|
+
|
40
|
+
define_singleton_method(:to_s) { "Plutonium::Resource::Record::Associations::#{association_type.to_s.camelize}(:#{name})" }
|
41
|
+
define_singleton_method(:inspect) { "Plutonium::Resource::Record::Associations::#{association_type.to_s.camelize}(:#{name})" }
|
42
|
+
RUBY
|
43
|
+
include mod
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_sgid_methods(association_type, name)
|
47
|
+
case association_type
|
48
|
+
when :belongs_to, :has_one
|
49
|
+
generate_singular_sgid_methods(name)
|
50
|
+
when :has_many, :has_and_belongs_to_many
|
51
|
+
generate_collection_sgid_methods(name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_singular_sgid_methods(name)
|
56
|
+
<<-RUBY
|
57
|
+
def #{name}_sgid
|
58
|
+
#{name}&.to_signed_global_id
|
59
|
+
end
|
60
|
+
|
61
|
+
def #{name}_sgid=(sgid)
|
62
|
+
self.#{name} = GlobalID::Locator.locate_signed(sgid)
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_collection_sgid_methods(name)
|
68
|
+
<<-RUBY
|
69
|
+
def #{name.to_s.singularize}_sgids
|
70
|
+
#{name}.map(&:to_signed_global_id)
|
71
|
+
end
|
72
|
+
|
73
|
+
def #{name.to_s.singularize}_sgids=(sgids)
|
74
|
+
self.#{name} = GlobalID::Locator.locate_many_signed(sgids)
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_#{name.to_s.singularize}_sgid(sgid)
|
78
|
+
record = GlobalID::Locator.locate_signed(sgid)
|
79
|
+
#{name} << record if record
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_#{name.to_s.singularize}_sgid(sgid)
|
83
|
+
record = GlobalID::Locator.locate_signed(sgid)
|
84
|
+
#{name}.delete(record) if record
|
85
|
+
end
|
86
|
+
RUBY
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/plutonium/resource/associations.rb
|
4
|
+
module Plutonium
|
5
|
+
module Resource
|
6
|
+
module Record
|
7
|
+
module FieldNames
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def resource_field_names
|
12
|
+
return @resource_field_names if defined?(@resource_field_names) && !Rails.env.local?
|
13
|
+
|
14
|
+
@resource_field_names = gather_resource_field_names
|
15
|
+
end
|
16
|
+
|
17
|
+
def belongs_to_association_field_names
|
18
|
+
return @belongs_to_association_field_names if defined?(@belongs_to_association_field_names) && !Rails.env.local?
|
19
|
+
|
20
|
+
@belongs_to_association_field_names = reflect_on_all_associations(:belongs_to).map(&:name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_one_association_field_names
|
24
|
+
return @has_one_association_field_names if defined?(@has_one_association_field_names) && !Rails.env.local?
|
25
|
+
|
26
|
+
@has_one_association_field_names = reflect_on_all_associations(:has_one)
|
27
|
+
.map { |assoc| (!/_attachment$|_blob$/.match?(assoc.name)) ? assoc.name : nil }
|
28
|
+
.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_many_association_field_names
|
32
|
+
return @has_many_association_field_names if defined?(@has_many_association_field_names) && !Rails.env.local?
|
33
|
+
|
34
|
+
@has_many_association_field_names = reflect_on_all_associations(:has_many)
|
35
|
+
.map { |assoc| (!/_attachments$|_blobs$/.match?(assoc.name)) ? assoc.name : nil }
|
36
|
+
.compact
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_one_attached_field_names
|
40
|
+
return @has_one_attached_field_names if defined?(@has_one_attached_field_names) && !Rails.env.local?
|
41
|
+
|
42
|
+
@has_one_attached_field_names = if respond_to?(:reflect_on_all_attachments)
|
43
|
+
reflect_on_all_attachments
|
44
|
+
.select { |a| a.macro == :has_one_attached }
|
45
|
+
.map(&:name)
|
46
|
+
else
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_many_attached_field_names
|
52
|
+
return @has_many_attached_field_names if defined?(@has_many_attached_field_names) && !Rails.env.local?
|
53
|
+
|
54
|
+
@has_many_attached_field_names = if respond_to?(:reflect_on_all_attachments)
|
55
|
+
reflect_on_all_attachments
|
56
|
+
.select { |a| a.macro == :has_many_attached }
|
57
|
+
.map(&:name)
|
58
|
+
else
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def content_column_field_names
|
64
|
+
return @content_column_field_names if defined?(@content_column_field_names) && !Rails.env.local?
|
65
|
+
|
66
|
+
@content_column_field_names = content_columns.map { |col| col.name.to_sym }
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def gather_resource_field_names
|
72
|
+
belongs_to_association_field_names +
|
73
|
+
has_one_attached_field_names +
|
74
|
+
has_one_association_field_names +
|
75
|
+
has_many_attached_field_names +
|
76
|
+
has_many_association_field_names +
|
77
|
+
content_column_field_names
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/plutonium/resource/associations.rb
|
4
|
+
module Plutonium
|
5
|
+
module Resource
|
6
|
+
module Record
|
7
|
+
module Labeling
|
8
|
+
def to_label
|
9
|
+
%i[name title].each do |method|
|
10
|
+
name = send(method) if respond_to?(method)
|
11
|
+
return name if name.present?
|
12
|
+
end
|
13
|
+
|
14
|
+
"#{model_name.human} ##{to_param}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# lib/plutonium/resource/associations.rb
|
4
|
+
module Plutonium
|
5
|
+
module Resource
|
6
|
+
module Record
|
7
|
+
module Routes
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
scope :from_path_param, ->(param) { where(id: param) }
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
def has_many_association_routes
|
16
|
+
return @has_many_association_routes if defined?(@has_many_association_routes) && !Rails.env.local?
|
17
|
+
|
18
|
+
@has_many_association_routes = reflect_on_all_associations(:has_many).map { |assoc| assoc.klass.model_name.plural }
|
19
|
+
end
|
20
|
+
|
21
|
+
def all_nested_attributes_options
|
22
|
+
unless Rails.env.local?
|
23
|
+
return @all_nested_attributes_options if defined?(@all_nested_attributes_options)
|
24
|
+
end
|
25
|
+
|
26
|
+
@all_nested_attributes_options = reflect_on_all_associations.map do |association|
|
27
|
+
setter_method = "#{association.name}_attributes="
|
28
|
+
if method_defined?(setter_method)
|
29
|
+
[association.name, nested_attributes_options[association.name].merge(
|
30
|
+
macro: association.macro,
|
31
|
+
class: association.polymorphic? ? nil : association.klass
|
32
|
+
)]
|
33
|
+
end
|
34
|
+
end.compact.to_h
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def path_parameter(param_name)
|
40
|
+
param_name = param_name.to_sym
|
41
|
+
|
42
|
+
scope :from_path_param, ->(param) { where(param_name => param) }
|
43
|
+
|
44
|
+
define_method :to_param do
|
45
|
+
return nil unless persisted?
|
46
|
+
|
47
|
+
send(param_name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def dynamic_path_parameter(param_name)
|
52
|
+
param_name = param_name.to_sym
|
53
|
+
|
54
|
+
scope :from_path_param, ->(param) { where(id: param.split("-").first) }
|
55
|
+
|
56
|
+
define_method :to_param do
|
57
|
+
return nil unless persisted?
|
58
|
+
|
59
|
+
"#{id}-#{send(param_name)}".parameterize
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -2,265 +2,13 @@ module Plutonium
|
|
2
2
|
module Resource
|
3
3
|
module Record
|
4
4
|
extend ActiveSupport::Concern
|
5
|
-
include Plutonium::Models::HasCents
|
6
|
-
|
7
|
-
included do
|
8
|
-
scope :from_path_param, ->(param) { where(id: param) }
|
9
|
-
|
10
|
-
scope :associated_with, ->(record) do
|
11
|
-
named_scope = :"associated_with_#{record.model_name.singular}"
|
12
|
-
return send(named_scope, record) if respond_to?(named_scope)
|
13
|
-
|
14
|
-
# TODO: add support for polymorphic associations
|
15
|
-
# TODO: add logging
|
16
|
-
# TODO: memoize this
|
17
|
-
|
18
|
-
own_association = klass.find_association_from_self_to_record(record)
|
19
|
-
if own_association
|
20
|
-
return klass.query_based_on_association(own_association, record)
|
21
|
-
end
|
22
|
-
|
23
|
-
record_association = klass.find_association_to_self_from_record(record)
|
24
|
-
if record_association
|
25
|
-
# TODO: add a warning here about a potentially poor performing query
|
26
|
-
return where(id: record.public_send(record_association.name))
|
27
|
-
end
|
28
|
-
|
29
|
-
klass.raise_association_error(record, named_scope)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class_methods do
|
34
|
-
# Returns the resource field names
|
35
|
-
# @return [Array<Symbol>]
|
36
|
-
def resource_field_names
|
37
|
-
return @resource_field_names if defined?(@resource_field_names) && !Rails.env.local?
|
38
|
-
|
39
|
-
@resource_field_names = gather_resource_field_names
|
40
|
-
end
|
41
|
-
|
42
|
-
# Overrides belongs_to to add support for polymorphic associations
|
43
|
-
# @param [Symbol] name The name of the association
|
44
|
-
# @param [Proc] scope The scope for the association
|
45
|
-
# @param [Hash] options The options for the association
|
46
|
-
def belongs_to(name, scope = nil, **options)
|
47
|
-
super
|
48
|
-
|
49
|
-
return unless options[:polymorphic]
|
50
|
-
|
51
|
-
mod = Module.new
|
52
|
-
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
53
|
-
extend ActiveSupport::Concern
|
54
|
-
|
55
|
-
def #{name}_sgid
|
56
|
-
#{name}&.to_signed_global_id
|
57
|
-
end
|
58
|
-
|
59
|
-
def #{name}_sgid=(sgid)
|
60
|
-
self.#{name} = GlobalID::Locator.locate_signed(sgid)
|
61
|
-
end
|
62
|
-
|
63
|
-
define_singleton_method(:to_s) { "Plutonium::Polymorphic::BelongsTo(:#{name})" }
|
64
|
-
define_singleton_method(:inspect) { "Plutonium::Polymorphic::BelongsTo(:#{name})" }
|
65
|
-
RUBY
|
66
|
-
include mod
|
67
|
-
end
|
68
|
-
|
69
|
-
# Returns the names of belongs_to associations
|
70
|
-
# @return [Array<Symbol>]
|
71
|
-
def belongs_to_association_field_names
|
72
|
-
return @belongs_to_association_field_names if defined?(@belongs_to_association_field_names) && !Rails.env.local?
|
73
|
-
|
74
|
-
@belongs_to_association_field_names = reflect_on_all_associations(:belongs_to).map(&:name)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns the names of has_one associations
|
78
|
-
# @return [Array<Symbol>]
|
79
|
-
def has_one_association_field_names
|
80
|
-
return @has_one_association_field_names if defined?(@has_one_association_field_names) && !Rails.env.local?
|
81
|
-
|
82
|
-
@has_one_association_field_names = reflect_on_all_associations(:has_one)
|
83
|
-
.map { |assoc| (!/_attachment$|_blob$/.match?(assoc.name)) ? assoc.name : nil }
|
84
|
-
.compact
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns the names of has_many associations
|
88
|
-
# @return [Array<Symbol>]
|
89
|
-
def has_many_association_field_names
|
90
|
-
return @has_many_association_field_names if defined?(@has_many_association_field_names) && !Rails.env.local?
|
91
|
-
|
92
|
-
@has_many_association_field_names = reflect_on_all_associations(:has_many)
|
93
|
-
.map { |assoc| (!/_attachments$|_blobs$/.match?(assoc.name)) ? assoc.name : nil }
|
94
|
-
.compact
|
95
|
-
end
|
96
|
-
|
97
|
-
# Returns the names of has_one attached associations
|
98
|
-
# @return [Array<Symbol>]
|
99
|
-
def has_one_attached_field_names
|
100
|
-
return @has_one_attached_field_names if defined?(@has_one_attached_field_names) && !Rails.env.local?
|
101
|
-
|
102
|
-
@has_one_attached_field_names = if respond_to?(:reflect_on_all_attachments)
|
103
|
-
reflect_on_all_attachments
|
104
|
-
.select { |a| a.macro == :has_one_attached }
|
105
|
-
.map(&:name)
|
106
|
-
else
|
107
|
-
[]
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Returns the names of has_many attached associations
|
112
|
-
# @return [Array<Symbol>]
|
113
|
-
def has_many_attached_field_names
|
114
|
-
return @has_many_attached_field_names if defined?(@has_many_attached_field_names) && !Rails.env.local?
|
115
5
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
# Returns the names of content columns
|
126
|
-
# @return [Array<Symbol>]
|
127
|
-
def content_column_field_names
|
128
|
-
return @content_column_field_names if defined?(@content_column_field_names) && !Rails.env.local?
|
129
|
-
|
130
|
-
@content_column_field_names = content_columns.map { |col| col.name.to_sym }
|
131
|
-
end
|
132
|
-
|
133
|
-
# Returns the routes for has_many associations
|
134
|
-
# @return [Array<Symbol>]
|
135
|
-
def has_many_association_routes
|
136
|
-
return @has_many_association_routes if defined?(@has_many_association_routes) && !Rails.env.local?
|
137
|
-
|
138
|
-
@has_many_association_routes = reflect_on_all_associations(:has_many).map { |assoc| assoc.klass.model_name.plural }
|
139
|
-
end
|
140
|
-
|
141
|
-
# Returns all nested attributes options
|
142
|
-
# @return [Hash]
|
143
|
-
def all_nested_attributes_options
|
144
|
-
unless Rails.env.local?
|
145
|
-
return @all_nested_attributes_options if defined?(@all_nested_attributes_options)
|
146
|
-
end
|
147
|
-
|
148
|
-
@all_nested_attributes_options = reflect_on_all_associations.map do |association|
|
149
|
-
setter_method = "#{association.name}_attributes="
|
150
|
-
if method_defined?(setter_method)
|
151
|
-
[association.name, nested_attributes_options[association.name].merge(
|
152
|
-
macro: association.macro,
|
153
|
-
class: association.polymorphic? ? nil : association.klass
|
154
|
-
)]
|
155
|
-
end
|
156
|
-
end.compact.to_h
|
157
|
-
end
|
158
|
-
|
159
|
-
# Finds the association to the given record
|
160
|
-
# @param [ActiveRecord::Base] record The record to find the association with
|
161
|
-
# @return [ActiveRecord::Reflection::AssociationReflection, nil]
|
162
|
-
def find_association_from_self_to_record(record)
|
163
|
-
reflect_on_all_associations.find do |assoc|
|
164
|
-
assoc.klass.name == record.class.name unless assoc.polymorphic?
|
165
|
-
rescue
|
166
|
-
assoc.check_validity!
|
167
|
-
raise
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# Finds the association to self in the given record
|
172
|
-
# @param [ActiveRecord::Base] record The record to find the association with
|
173
|
-
# @return [ActiveRecord::Reflection::AssociationReflection, nil]
|
174
|
-
def find_association_to_self_from_record(record)
|
175
|
-
record.class.reflect_on_all_associations.find do |assoc|
|
176
|
-
assoc.klass.name == name
|
177
|
-
rescue
|
178
|
-
assoc.check_validity!
|
179
|
-
raise
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# Queries based on the association type
|
184
|
-
# @param [ActiveRecord::Reflection::AssociationReflection] assoc The association
|
185
|
-
# @param [ActiveRecord::Base] record The record to query with
|
186
|
-
# @return [ActiveRecord::Relation]
|
187
|
-
def query_based_on_association(assoc, record)
|
188
|
-
case assoc.macro
|
189
|
-
when :has_one
|
190
|
-
joins(assoc.name).where(assoc.name => {record.class.primary_key => record.id})
|
191
|
-
when :belongs_to
|
192
|
-
where(assoc.name => record)
|
193
|
-
when :has_many
|
194
|
-
joins(assoc.name).where(assoc.klass.table_name => record)
|
195
|
-
else
|
196
|
-
raise NotImplementedError, "associated_with->##{assoc.macro}"
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Raises an error for unresolved associations
|
201
|
-
# @param [ActiveRecord::Base] record The record with unresolved association
|
202
|
-
# @param [Symbol] named_scope The named scope
|
203
|
-
# @raise [RuntimeError]
|
204
|
-
def raise_association_error(record, named_scope)
|
205
|
-
raise "Could not resolve the association between '#{name}' and '#{record.class.name}'\n\n" \
|
206
|
-
"Define\n" \
|
207
|
-
" 1. the associations between the models\n" \
|
208
|
-
" 2. a named scope on #{name} e.g.\n\n" \
|
209
|
-
"scope :#{named_scope}, ->(#{record.model_name.singular}) { do_something_here }"
|
210
|
-
end
|
211
|
-
|
212
|
-
private
|
213
|
-
|
214
|
-
# Defines a scope and method for path parameters
|
215
|
-
# @param [Symbol] param_name The name of the parameter
|
216
|
-
def path_parameter(param_name)
|
217
|
-
param_name = param_name.to_sym
|
218
|
-
|
219
|
-
scope :from_path_param, ->(param) { where(param_name => param) }
|
220
|
-
|
221
|
-
define_method :to_param do
|
222
|
-
return nil unless persisted?
|
223
|
-
|
224
|
-
send(param_name)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
# Defines a scope and method for dynamic path parameters
|
229
|
-
# @param [Symbol] param_name The name of the parameter
|
230
|
-
def dynamic_path_parameter(param_name)
|
231
|
-
param_name = param_name.to_sym
|
232
|
-
|
233
|
-
scope :from_path_param, ->(param) { where(id: param.split("-").first) }
|
234
|
-
|
235
|
-
define_method :to_param do
|
236
|
-
return nil unless persisted?
|
237
|
-
|
238
|
-
"#{id}-#{send(param_name)}".parameterize
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# Gathers all resource field names
|
243
|
-
# @return [Array<Symbol>]
|
244
|
-
def gather_resource_field_names
|
245
|
-
belongs_to_association_field_names +
|
246
|
-
has_one_attached_field_names +
|
247
|
-
has_one_association_field_names +
|
248
|
-
has_many_attached_field_names +
|
249
|
-
has_many_association_field_names +
|
250
|
-
content_column_field_names
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
# Returns a label for the record
|
255
|
-
# @return [String]
|
256
|
-
def to_label
|
257
|
-
%i[name title].each do |method|
|
258
|
-
name = send(method) if respond_to?(method)
|
259
|
-
return name if name.present?
|
260
|
-
end
|
261
|
-
|
262
|
-
"#{model_name.human} ##{to_param}"
|
263
|
-
end
|
6
|
+
include Plutonium::Models::HasCents
|
7
|
+
include Plutonium::Resource::Record::Routes
|
8
|
+
include Plutonium::Resource::Record::Labeling
|
9
|
+
include Plutonium::Resource::Record::FieldNames
|
10
|
+
include Plutonium::Resource::Record::Associations
|
11
|
+
include Plutonium::Resource::Record::AssociatedWith
|
264
12
|
end
|
265
13
|
end
|
266
14
|
end
|
@@ -19,7 +19,7 @@ module Plutonium
|
|
19
19
|
# @yield An optional block for additional resource configuration.
|
20
20
|
# @return [void]
|
21
21
|
def register_resource(resource, options = {}, &)
|
22
|
-
route_config = route_set.register_resource(resource, &)
|
22
|
+
route_config = route_set.register_resource(resource, options, &)
|
23
23
|
define_resource_routes(route_config, resource)
|
24
24
|
resource_route_concern_names << route_config[:concern_name]
|
25
25
|
end
|
@@ -65,7 +65,7 @@ module Plutonium
|
|
65
65
|
# @return [void]
|
66
66
|
def define_resource_routes(route_config, resource)
|
67
67
|
concern route_config[:concern_name] do
|
68
|
-
|
68
|
+
send route_config[:route_type], route_config[:route_name], **route_config[:route_options] do
|
69
69
|
instance_exec(&route_config[:block]) if route_config[:block]
|
70
70
|
define_nested_resource_routes(resource)
|
71
71
|
end
|
@@ -49,17 +49,18 @@ module Plutonium
|
|
49
49
|
# Registers a resource for routing.
|
50
50
|
#
|
51
51
|
# @param resource [Class] The resource class to be registered.
|
52
|
+
# @param options [Hash] Additional options for resource registration.
|
52
53
|
# @yield An optional block for additional resource configuration.
|
53
54
|
# @return [Hash] The configuration for the registered resource.
|
54
55
|
# @raise [ArgumentError] If the engine is not supported.
|
55
|
-
def register_resource(resource, &)
|
56
|
+
def register_resource(resource, options = {}, &)
|
56
57
|
self.class.validate_engine! engine
|
57
58
|
engine.resource_register.register(resource)
|
58
59
|
|
59
60
|
route_name = resource.model_name.plural
|
60
61
|
concern_name = :"#{route_name}_routes"
|
61
62
|
|
62
|
-
config = create_resource_config(resource, route_name, concern_name, &)
|
63
|
+
config = create_resource_config(resource, route_name, concern_name, options, &)
|
63
64
|
resource_route_config_lookup[route_name] = config
|
64
65
|
|
65
66
|
config
|
@@ -101,15 +102,17 @@ module Plutonium
|
|
101
102
|
# @param resource_name [String] The name of the resource.
|
102
103
|
# @param route_name [String] The pluralized name for routes.
|
103
104
|
# @param concern_name [Symbol] The name of the concern for this resource.
|
105
|
+
# @param options [Hash] Additional options for resource registration.
|
104
106
|
# @yield An optional block for additional resource configuration.
|
105
107
|
# @return [Hash] The complete resource configuration.
|
106
|
-
def create_resource_config(resource, route_name, concern_name, &block)
|
108
|
+
def create_resource_config(resource, route_name, concern_name, options = {}, &block)
|
107
109
|
{
|
110
|
+
route_type: options[:singular] ? :resource : :resources,
|
108
111
|
route_name: route_name,
|
109
112
|
concern_name: concern_name,
|
110
113
|
route_options: {
|
111
114
|
controller: resource.to_s.pluralize.underscore,
|
112
|
-
path: resource.model_name.collection,
|
115
|
+
path: options[:singular] ? resource.model_name.singular : resource.model_name.collection,
|
113
116
|
concerns: %i[interactive_resource_actions]
|
114
117
|
},
|
115
118
|
block: block
|