madmin 1.0.2 → 1.2.2
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/README.md +29 -1
- data/app/controllers/madmin/base_controller.rb +0 -5
- data/app/controllers/madmin/resource_controller.rb +23 -2
- data/app/helpers/madmin/application_helper.rb +4 -0
- data/app/helpers/madmin/nav_helper.rb +30 -0
- data/app/helpers/madmin/sort_helper.rb +32 -0
- data/app/views/layouts/madmin/application.html.erb +5 -5
- data/app/views/madmin/application/_form.html.erb +6 -8
- data/app/views/madmin/application/_javascript.html.erb +70 -4
- data/app/views/madmin/application/_navigation.html.erb +31 -5
- data/app/views/madmin/application/edit.html.erb +5 -1
- data/app/views/madmin/application/index.html.erb +34 -22
- data/app/views/madmin/application/new.html.erb +5 -1
- data/app/views/madmin/application/show.html.erb +24 -17
- data/app/views/madmin/fields/attachment/_form.html.erb +3 -1
- data/app/views/madmin/fields/attachment/_show.html.erb +7 -1
- data/app/views/madmin/fields/attachments/_form.html.erb +3 -1
- data/app/views/madmin/fields/attachments/_show.html.erb +7 -3
- data/app/views/madmin/fields/belongs_to/_form.html.erb +4 -2
- data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
- data/app/views/madmin/fields/boolean/_form.html.erb +3 -1
- data/app/views/madmin/fields/date/_form.html.erb +3 -1
- data/app/views/madmin/fields/date_time/_form.html.erb +3 -1
- data/app/views/madmin/fields/decimal/_form.html.erb +3 -1
- data/app/views/madmin/fields/enum/_form.html.erb +4 -2
- data/app/views/madmin/fields/float/_form.html.erb +3 -1
- data/app/views/madmin/fields/has_many/_form.html.erb +4 -2
- data/app/views/madmin/fields/has_many/_show.html.erb +1 -1
- data/app/views/madmin/fields/has_one/_form.html.erb +3 -2
- data/app/views/madmin/fields/integer/_form.html.erb +3 -1
- data/app/views/madmin/fields/json/_form.html.erb +3 -1
- data/app/views/madmin/fields/nested_has_many/_fields.html.erb +18 -0
- data/app/views/madmin/fields/nested_has_many/_form.html.erb +32 -0
- data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -0
- data/app/views/madmin/fields/nested_has_many/_show.html.erb +5 -0
- data/app/views/madmin/fields/password/_form.html.erb +4 -0
- data/app/views/madmin/fields/password/_index.html.erb +1 -0
- data/app/views/madmin/fields/password/_show.html.erb +1 -0
- data/app/views/madmin/fields/polymorphic/_form.html.erb +3 -1
- data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
- data/app/views/madmin/fields/rich_text/_form.html.erb +3 -1
- data/app/views/madmin/fields/string/_form.html.erb +3 -1
- data/app/views/madmin/fields/text/_form.html.erb +3 -1
- data/app/views/madmin/fields/time/_form.html.erb +3 -1
- data/app/views/madmin/shared/_label.html.erb +4 -0
- data/lib/generators/madmin/field/field_generator.rb +31 -0
- data/lib/generators/madmin/field/templates/_form.html.erb +2 -0
- data/lib/generators/madmin/field/templates/_index.html.erb +1 -0
- data/lib/generators/madmin/field/templates/_show.html.erb +1 -0
- data/lib/generators/madmin/field/templates/field.rb.tt +26 -0
- data/lib/generators/madmin/install/install_generator.rb +6 -1
- data/lib/generators/madmin/install/templates/routes.rb.tt +3 -0
- data/lib/generators/madmin/resource/resource_generator.rb +15 -54
- data/lib/generators/madmin/resource/templates/controller.rb.tt +6 -0
- data/lib/generators/madmin/resource/templates/resource.rb.tt +15 -1
- data/lib/generators/madmin/views/javascript_generator.rb +15 -0
- data/lib/generators/madmin/views/views_generator.rb +6 -5
- data/lib/madmin/engine.rb +4 -4
- data/lib/madmin/field.rb +4 -0
- data/lib/madmin/fields/belongs_to.rb +9 -5
- data/lib/madmin/fields/has_many.rb +10 -5
- data/lib/madmin/fields/nested_has_many.rb +40 -0
- data/lib/madmin/fields/password.rb +6 -0
- data/lib/madmin/fields/string.rb +3 -0
- data/lib/madmin/fields/text.rb +3 -0
- data/lib/madmin/generator_helpers.rb +22 -4
- data/lib/madmin/resource.rb +76 -28
- data/lib/madmin/resource_builder.rb +80 -0
- data/lib/madmin/search.rb +60 -0
- data/lib/madmin/version.rb +1 -1
- data/lib/madmin.rb +31 -14
- metadata +26 -5
@@ -1,2 +1,4 @@
|
|
1
|
-
|
2
|
-
<%=
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
4
|
+
<%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select", data: { controller: "select", select_url_value: field.index_path } } %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<% field.value(record).each do |object| %>
|
2
2
|
<div>
|
3
|
-
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
3
|
+
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object), class: "text-indigo-500 underline" %>
|
4
4
|
</div>
|
5
5
|
<% end %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<%= form.number_field field.attribute_name, class: "form-input" %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<%= form.text_area field.attribute_name, class: "form-input" %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= content_tag :div, class: "nested-fields bg-gray-100 rounded-t-xl p-5", data: { new_record: f.object.new_record? } do %>
|
2
|
+
<% field.nested_attributes.each do |nested_attribute| %>
|
3
|
+
<% next if nested_attribute[:field].nil? %>
|
4
|
+
<% next unless nested_attribute[:field].visible?(action_name) %>
|
5
|
+
<% next unless nested_attribute[:field].visible?(:form) %>
|
6
|
+
|
7
|
+
<% nested_field = nested_attribute[:field] %>
|
8
|
+
|
9
|
+
<div class="mb-4 flex">
|
10
|
+
<%= render partial: nested_field.to_partial_path("form"), locals: { field: nested_field, record: f.object, form: f, resource: field.resource } %>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<small><%= link_to "Remove", "#", data: { action: "click->nested-form#remove_association" } %></small>
|
15
|
+
|
16
|
+
<%= f.hidden_field :_destroy %>
|
17
|
+
|
18
|
+
<% end %>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div class="block md:inline-block w-32 flex-shrink-0">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<div class="container space-y-8" data-controller="nested-form">
|
6
|
+
<template data-target="nested-form.template">
|
7
|
+
|
8
|
+
<%= form.fields_for field.attribute_name, field.to_model.new, child_index: 'NEW_RECORD' do |nested_form| %>
|
9
|
+
<%= render(
|
10
|
+
partial: field.to_partial_path('fields'),
|
11
|
+
locals: {
|
12
|
+
f: nested_form,
|
13
|
+
field: field
|
14
|
+
}
|
15
|
+
) %>
|
16
|
+
<% end %>
|
17
|
+
</template>
|
18
|
+
|
19
|
+
<%= form.fields_for field.attribute_name do |nested_form| %>
|
20
|
+
<%= render(
|
21
|
+
partial: field.to_partial_path('fields'),
|
22
|
+
locals: {
|
23
|
+
f: nested_form,
|
24
|
+
field: field
|
25
|
+
}
|
26
|
+
) %>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<%= content_tag :div, class: '', data: { target:"nested-form.links" } do %>
|
30
|
+
<%= link_to "+ Add new", "#", data: { action: "click->nested-form#add_association" } %>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= pluralize field.value(record).count, field.attribute_name.to_s %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= field.value(record) %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= field.value(record) %>
|
@@ -1,5 +1,7 @@
|
|
1
1
|
<%= form.fields_for field.attribute_name do |pf| %>
|
2
|
-
|
2
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
3
|
+
<%= render "madmin/shared/label", form: pf, field: field %>
|
4
|
+
</div>
|
3
5
|
<%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, { class: "form-select", data: { controller: "slimselect" } } %>
|
4
6
|
<%= pf.hidden_field :type, value: "polymorphic" %>
|
5
7
|
<% end %>
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<% if (object = field.value(record)) %>
|
2
|
-
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
2
|
+
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object), class: "text-indigo-500 underline" %>
|
3
3
|
<% end %>
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<div class="flex-1">
|
3
5
|
<%= form.rich_text_area field.attribute_name, class: "form-input block" %>
|
4
6
|
</div>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<%= form.text_field field.attribute_name, class: "form-input" %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<%= form.text_area field.attribute_name, class: "form-input" %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
1
|
+
<div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
|
2
|
+
<%= render "madmin/shared/label", form: form, field: field %>
|
3
|
+
</div>
|
2
4
|
<%= form.time_select field.attribute_name, {}, { class: "form-select" } %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Madmin
|
2
|
+
module Generators
|
3
|
+
class FieldGenerator < Rails::Generators::NamedBase
|
4
|
+
include Madmin::GeneratorHelpers
|
5
|
+
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def eager_load
|
9
|
+
Rails.application.eager_load!
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_field
|
13
|
+
template "field.rb", "app/madmin/fields/#{file_path}_field.rb"
|
14
|
+
copy_resource_template "_form"
|
15
|
+
copy_resource_template "_index"
|
16
|
+
copy_resource_template "_show"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def copy_resource_template(template_name)
|
22
|
+
template_file = "#{template_name}.html.erb"
|
23
|
+
|
24
|
+
copy_file(
|
25
|
+
template_file,
|
26
|
+
"app/views/madmin/fields/#{file_path}_field/#{template_file}"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= field.value(record) %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= field.value(record) %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class <%= class_name %>Field < Madmin::Field
|
2
|
+
# def value(record)
|
3
|
+
# record.public_send(attribute_name)
|
4
|
+
# end
|
5
|
+
|
6
|
+
# def to_partial_path(name)
|
7
|
+
# unless %w[index show form].include? name
|
8
|
+
# raise ArgumentError, "`partial` must be 'index', 'show', or 'form'"
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# "/madmin/fields/#{self.class.field_type}/#{name}"
|
12
|
+
# end
|
13
|
+
|
14
|
+
# def to_param
|
15
|
+
# attribute_name
|
16
|
+
# end
|
17
|
+
|
18
|
+
# # Used for checking visibility of attribute on an view
|
19
|
+
# def visible?(action, default: true)
|
20
|
+
# options.fetch(action.to_sym, default)
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def required?
|
24
|
+
# model.validators_on(attribute_name).any? { |v| v.is_a? ActiveModel::Validations::PresenceValidator }
|
25
|
+
# end
|
26
|
+
end
|
@@ -16,8 +16,13 @@ module Madmin
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def generate_routes
|
19
|
+
if rails6_1_and_up?
|
20
|
+
route "draw :madmin", file: ROUTES_FILE[:default]
|
21
|
+
template("routes.rb.tt", "config/routes/madmin.rb")
|
22
|
+
end
|
23
|
+
|
19
24
|
if route_namespace_exists?
|
20
|
-
route "root to: \"dashboard#show\"", indentation: 4, sentinel: /namespace :madmin do\s*\n/m
|
25
|
+
route "root to: \"dashboard#show\"", indentation: separated_routes_file? ? 2 : 4, sentinel: /namespace :madmin do\s*\n/m
|
21
26
|
else
|
22
27
|
route "root to: \"dashboard#show\"", namespace: [:madmin]
|
23
28
|
end
|
@@ -20,7 +20,7 @@ module Madmin
|
|
20
20
|
|
21
21
|
def generate_route
|
22
22
|
if route_namespace_exists?
|
23
|
-
route "resources :#{plural_name}", namespace: class_path, indentation: 4, sentinel: /namespace :madmin do\s*\n/m
|
23
|
+
route "resources :#{plural_name}", namespace: class_path, indentation: separated_routes_file? ? 2 : 4, sentinel: /namespace :madmin do\s*\n/m
|
24
24
|
else
|
25
25
|
route "resources :#{plural_name}", namespace: [:madmin] + class_path
|
26
26
|
end
|
@@ -28,62 +28,19 @@ module Madmin
|
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
-
def
|
32
|
-
model
|
33
|
-
# Hide these special associations
|
34
|
-
name.starts_with?("rich_text") ||
|
35
|
-
name.ends_with?("_attachment") ||
|
36
|
-
name.ends_with?("_attachments") ||
|
37
|
-
name.ends_with?("_blob") ||
|
38
|
-
name.ends_with?("_blobs")
|
39
|
-
}.keys
|
40
|
-
end
|
41
|
-
|
42
|
-
def attributes
|
43
|
-
model.attribute_names + virtual_attributes - redundant_attributes
|
31
|
+
def model
|
32
|
+
@model ||= class_name.constantize
|
44
33
|
end
|
45
34
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
# Add virtual attributes for ActionText and ActiveStorage
|
50
|
-
model.reflections.each do |name, association|
|
51
|
-
if name.starts_with?("rich_text")
|
52
|
-
virtual << name.split("rich_text_").last
|
53
|
-
elsif name.ends_with?("_attachment")
|
54
|
-
virtual << name.split("_attachment").first
|
55
|
-
elsif name.ends_with?("_attachments")
|
56
|
-
virtual << name.split("_attachments").first
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
virtual
|
35
|
+
def resource_builder
|
36
|
+
@resource_builder ||= ResourceBuilder.new(model)
|
61
37
|
end
|
62
38
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
model.reflections.each do |name, association|
|
67
|
-
if association.has_one?
|
68
|
-
next
|
69
|
-
elsif association.collection?
|
70
|
-
next
|
71
|
-
elsif association.polymorphic?
|
72
|
-
redundant << "#{name}_id"
|
73
|
-
redundant << "#{name}_type"
|
74
|
-
elsif name.starts_with?("rich_text")
|
75
|
-
redundant << name
|
76
|
-
else # belongs to
|
77
|
-
redundant << "#{name}_id"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
redundant
|
39
|
+
def model_attributes
|
40
|
+
resource_builder.attributes
|
82
41
|
end
|
83
42
|
|
84
|
-
|
85
|
-
@model ||= class_name.constantize
|
86
|
-
end
|
43
|
+
delegate :associations, :virtual_attributes, :store_accessors, to: :resource_builder
|
87
44
|
|
88
45
|
def formatted_options_for_attribute(name)
|
89
46
|
options = options_for_attribute(name)
|
@@ -98,13 +55,17 @@ module Madmin
|
|
98
55
|
if %w[id created_at updated_at].include?(name)
|
99
56
|
{form: false}
|
100
57
|
|
101
|
-
#
|
102
|
-
elsif
|
103
|
-
{index: false}
|
58
|
+
# has_secure_passwords should only show on forms
|
59
|
+
elsif name.ends_with?("_confirmation") || virtual_attributes.include?("#{name}_confirmation")
|
60
|
+
{index: false, show: false}
|
104
61
|
|
105
62
|
# Counter cache columns are typically not editable
|
106
63
|
elsif name.ends_with?("_count")
|
107
64
|
{form: false}
|
65
|
+
|
66
|
+
# Attributes without a database column
|
67
|
+
elsif !model.column_names.include?(name) && !store_accessors.map(&:to_s).include?(name)
|
68
|
+
{index: false}
|
108
69
|
end
|
109
70
|
end
|
110
71
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= class_name %>Resource < Madmin::Resource
|
2
2
|
# Attributes
|
3
|
-
<%
|
3
|
+
<% model_attributes.each do |attribute_name| -%>
|
4
4
|
attribute :<%= attribute_name %><%= formatted_options_for_attribute(attribute_name) %>
|
5
5
|
<% end -%>
|
6
6
|
|
@@ -8,4 +8,18 @@ class <%= class_name %>Resource < Madmin::Resource
|
|
8
8
|
<% associations.each do |association_name| -%>
|
9
9
|
attribute :<%= association_name %>
|
10
10
|
<% end -%>
|
11
|
+
|
12
|
+
# Uncomment this to customize the display name of records in the admin area.
|
13
|
+
# def self.display_name(record)
|
14
|
+
# record.name
|
15
|
+
# end
|
16
|
+
|
17
|
+
# Uncomment this to customize the default sort column and direction.
|
18
|
+
# def self.default_sort_column
|
19
|
+
# "created_at"
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def self.default_sort_direction
|
23
|
+
# "desc"
|
24
|
+
# end
|
11
25
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class JavascriptGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_navigation
|
10
|
+
copy_resource_template("_javascript")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -4,11 +4,12 @@ module Madmin
|
|
4
4
|
module Generators
|
5
5
|
class ViewsGenerator < Madmin::ViewGenerator
|
6
6
|
def copy_templates
|
7
|
-
|
8
|
-
call_generator("
|
9
|
-
call_generator("
|
10
|
-
call_generator("
|
11
|
-
call_generator("
|
7
|
+
# Some generators duplicate templates, so not everything is present here
|
8
|
+
call_generator("madmin:views:edit", resource_path, "--namespace", namespace)
|
9
|
+
call_generator("madmin:views:index", resource_path, "--namespace", namespace)
|
10
|
+
call_generator("madmin:views:layout", resource_path, "--namespace", namespace)
|
11
|
+
call_generator("madmin:views:new", resource_path, "--namespace", namespace)
|
12
|
+
call_generator("madmin:views:show", resource_path, "--namespace", namespace)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
data/lib/madmin/engine.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Madmin
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
-
|
4
|
-
app.config.
|
5
|
-
app.config.
|
3
|
+
config.before_configuration do |app|
|
4
|
+
app.config.autoload_paths << File.expand_path("app/madmin/resources", Rails.root)
|
5
|
+
app.config.autoload_paths << File.expand_path("app/madmin/fields", Rails.root)
|
6
6
|
end
|
7
7
|
|
8
8
|
config.to_prepare do
|
9
|
-
Madmin.
|
9
|
+
Madmin.reset_resources!
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/madmin/field.rb
CHANGED
@@ -2,17 +2,21 @@ module Madmin
|
|
2
2
|
module Fields
|
3
3
|
class BelongsTo < Field
|
4
4
|
def options_for_select(record)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
[
|
5
|
+
if (record = record.send(attribute_name))
|
6
|
+
resource = Madmin.resource_for(record)
|
7
|
+
[[resource.display_name(record), record.id]]
|
8
|
+
else
|
9
|
+
[]
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_param
|
14
14
|
"#{attribute_name}_id"
|
15
15
|
end
|
16
|
+
|
17
|
+
def index_path
|
18
|
+
Madmin.resource_by_name(model.reflect_on_association(attribute_name).klass).index_path(format: :json)
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -2,17 +2,22 @@ module Madmin
|
|
2
2
|
module Fields
|
3
3
|
class HasMany < Field
|
4
4
|
def options_for_select(record)
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
if (records = record.send(attribute_name))
|
6
|
+
return [] unless records.first
|
7
|
+
resource = Madmin.resource_for(records.first)
|
8
|
+
records.map { |record| [resource.display_name(record), record.id] }
|
9
|
+
else
|
10
|
+
[]
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
def to_param
|
14
15
|
{"#{attribute_name.to_s.singularize}_ids".to_sym => []}
|
15
16
|
end
|
17
|
+
|
18
|
+
def index_path
|
19
|
+
Madmin.resource_by_name(model.reflect_on_association(attribute_name).klass).index_path(format: :json)
|
20
|
+
end
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Madmin
|
2
|
+
module Fields
|
3
|
+
class NestedHasMany < Field
|
4
|
+
DEFAULT_ATTRIBUTES = %w[_destroy id].freeze
|
5
|
+
def nested_attributes
|
6
|
+
resource.attributes.reject { |i| skipped_fields.include?(i[:name]) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def resource
|
10
|
+
"#{to_model.name}Resource".constantize
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_param
|
14
|
+
{"#{attribute_name}_attributes": permitted_fields}
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_partial_path(name)
|
18
|
+
unless %w[index show form fields].include? name
|
19
|
+
raise ArgumentError, "`partial` must be 'index', 'show', 'form' or 'fields'"
|
20
|
+
end
|
21
|
+
|
22
|
+
"/madmin/fields/#{self.class.field_type}/#{name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_model
|
26
|
+
attribute_name.to_s.singularize.classify.constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def permitted_fields
|
32
|
+
(resource.permitted_params - skipped_fields + DEFAULT_ATTRIBUTES).uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
def skipped_fields
|
36
|
+
options[:skip] || []
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/madmin/fields/string.rb
CHANGED
data/lib/madmin/fields/text.rb
CHANGED
@@ -1,24 +1,30 @@
|
|
1
1
|
module Madmin
|
2
2
|
module GeneratorHelpers
|
3
|
+
ROUTES_FILE = {default: "config/routes.rb", separated: "config/routes/madmin.rb"}.freeze
|
4
|
+
|
3
5
|
def call_generator(generator, *args)
|
4
6
|
Rails::Generators.invoke(generator, args, generator_options)
|
5
7
|
end
|
6
8
|
|
7
9
|
def route_namespace_exists?
|
8
|
-
File.readlines(Rails.root.join(
|
10
|
+
File.readlines(Rails.root.join(default_routes_file)).grep(/namespace :madmin/).size > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def rails6_1_and_up?
|
14
|
+
Gem.loaded_specs["rails"].version >= Gem::Version.new(6.1)
|
9
15
|
end
|
10
16
|
|
11
17
|
# Method copied from Rails 6.1 master
|
12
|
-
def route(routing_code, namespace: nil, sentinel: nil, indentation: 2)
|
18
|
+
def route(routing_code, namespace: nil, sentinel: nil, indentation: 2, file: default_routes_file)
|
13
19
|
routing_code = Array(namespace).reverse.reduce(routing_code) { |code, ns|
|
14
20
|
"namespace :#{ns} do\n#{indent(code, 2)}\nend"
|
15
21
|
}
|
16
22
|
|
17
23
|
log :route, routing_code
|
18
|
-
sentinel ||=
|
24
|
+
sentinel ||= default_sentinel(file)
|
19
25
|
|
20
26
|
in_root do
|
21
|
-
inject_into_file
|
27
|
+
inject_into_file file, optimize_indentation(routing_code, indentation), after: sentinel, verbose: false, force: false
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
@@ -30,6 +36,18 @@ module Madmin
|
|
30
36
|
|
31
37
|
private
|
32
38
|
|
39
|
+
def separated_routes_file?
|
40
|
+
default_routes_file.eql?(ROUTES_FILE[:separated])
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_sentinel(file)
|
44
|
+
file.eql?(ROUTES_FILE[:default]) ? /\.routes\.draw do\s*\n/m : /namespace :madmin do\s*\n/m
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_routes_file
|
48
|
+
rails6_1_and_up? ? ROUTES_FILE[:separated] : ROUTES_FILE[:default]
|
49
|
+
end
|
50
|
+
|
33
51
|
def generator_options
|
34
52
|
{behavior: behavior}
|
35
53
|
end
|