madmin 1.0.0.beta1 → 1.1.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/README.md +48 -1
- data/app/controllers/madmin/resource_controller.rb +1 -1
- data/app/views/madmin/application/_javascript.html.erb +40 -5
- data/app/views/madmin/fields/belongs_to/_index.html.erb +3 -2
- data/app/views/madmin/fields/belongs_to/_show.html.erb +3 -2
- data/app/views/madmin/fields/enum/_form.html.erb +1 -2
- data/app/views/madmin/fields/nested_has_many/_fields.html.erb +18 -0
- data/app/views/madmin/fields/nested_has_many/_form.html.erb +30 -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 +2 -0
- data/app/views/madmin/fields/password/_index.html.erb +1 -0
- data/app/views/madmin/fields/password/_show.html.erb +1 -0
- data/lib/generators/madmin/install/install_generator.rb +2 -1
- data/lib/generators/madmin/resource/resource_generator.rb +14 -3
- data/lib/generators/madmin/resource/templates/resource.rb.tt +5 -0
- data/lib/generators/madmin/views/edit_generator.rb +16 -0
- data/lib/generators/madmin/views/form_generator.rb +15 -0
- data/lib/generators/madmin/views/index_generator.rb +15 -0
- data/lib/generators/madmin/views/javascript_generator.rb +15 -0
- data/lib/generators/madmin/views/layout_generator.rb +21 -0
- data/lib/generators/madmin/views/navigation_generator.rb +15 -0
- data/lib/generators/madmin/views/new_generator.rb +16 -0
- data/lib/generators/madmin/views/show_generator.rb +15 -0
- data/lib/generators/madmin/views/views_generator.rb +16 -0
- data/lib/madmin.rb +11 -9
- data/lib/madmin/fields/belongs_to.rb +3 -2
- data/lib/madmin/fields/enum.rb +3 -0
- data/lib/madmin/fields/has_many.rb +3 -2
- data/lib/madmin/fields/nested_has_many.rb +40 -0
- data/lib/madmin/fields/password.rb +6 -0
- data/lib/madmin/namespace.rb +35 -0
- data/lib/madmin/resource.rb +37 -8
- data/lib/madmin/version.rb +1 -1
- data/lib/madmin/view_generator.rb +42 -0
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 223251f99312a051f3fd6a6d926c6583347e2c78b255343bc66b3fb063ea8a54
|
4
|
+
data.tar.gz: cfab141916431f52d137bd7704c33cc4cd3b1781735062e2a9d173f9d9d7bee4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b1480b7df30ec0bba0f1937a7336bb8532f51cd8dc9c8de78fd73b686c132dd14083eec88a303938bfb4fdfa50827db570a3d899e40952799869dfe4795e756
|
7
|
+
data.tar.gz: e362a3d85149b1f1074ea21eaf6a6659e6c8f002a929f668a21583a286fc1824a2f3ede02997eafe75feaa20e0172345c54210180b7c537b44d0de4bca1eb0b0
|
data/README.md
CHANGED
@@ -7,9 +7,12 @@
|
|
7
7
|
Why another Ruby on Rails admin? We wanted an admin that was:
|
8
8
|
|
9
9
|
* Familiar and customizable like Rails scaffolds (less DSL)
|
10
|
-
* Supports all the Rails features out of the box (ActionText, ActionMailbox, etc)
|
10
|
+
* Supports all the Rails features out of the box (ActionText, ActionMailbox, has_secure_password, etc)
|
11
11
|
* Stimulus / Turbolinks / Hotwire ready
|
12
12
|
|
13
|
+

|
14
|
+
_We're still working on the design!_
|
15
|
+
|
13
16
|
## Installation
|
14
17
|
Add `madmin` to your application's Gemfile:
|
15
18
|
|
@@ -37,6 +40,50 @@ To generate a resource for a model, you can run:
|
|
37
40
|
rails g madmin:resource ActionText::RichText
|
38
41
|
```
|
39
42
|
|
43
|
+
## Configuring Views
|
44
|
+
|
45
|
+
The views packaged within the gem are a great starting point, but inevitably people will need to be able to customize those views.
|
46
|
+
|
47
|
+
You can use the included generator to create the appropriate view files, which can then be customized.
|
48
|
+
|
49
|
+
For example, running the following will copy over all of the views into your application that will be used for every resource:
|
50
|
+
```bash
|
51
|
+
rails generate madmin:views
|
52
|
+
```
|
53
|
+
|
54
|
+
The view files that are copied over in this case includes all of the standard Rails action views (index, new, edit, show, and _form), as well as:
|
55
|
+
* `application.html.erb` (layout file)
|
56
|
+
* `_javascript.html.erb` (default JavaScript setup)
|
57
|
+
* `_navigation.html.erb` (renders the navigation/sidebar menu)
|
58
|
+
|
59
|
+
As with the other views, you can specifically run the views generator for only the navigation or application layout views:
|
60
|
+
```bash
|
61
|
+
rails g madmin:views:navigation
|
62
|
+
# -> app/views/madmin/_navigation.html.erb
|
63
|
+
|
64
|
+
rails g madmin:views:layout # Note the layout generator includes the layout, javascript, and navigation files.
|
65
|
+
# -> app/views/madmin/application.html.erb
|
66
|
+
# -> app/views/madmin/_javascript.html.erb
|
67
|
+
# -> app/views/madmin/_navigation.html.erb
|
68
|
+
```
|
69
|
+
|
70
|
+
If you only need to customize specific views, you can restrict which views are copied by the generator:
|
71
|
+
```bash
|
72
|
+
rails g madmin:views:index
|
73
|
+
# -> app/views/madmin/application/index.html.erb
|
74
|
+
```
|
75
|
+
|
76
|
+
You can also scope the copied view(s) to a specific Resource/Model:
|
77
|
+
```bash
|
78
|
+
rails generate madmin:views:index Book
|
79
|
+
# -> app/views/madmin/books/index.html.erb
|
80
|
+
```
|
81
|
+
|
82
|
+
## Authentication
|
83
|
+
|
84
|
+
You can use a couple of strategies to authenticate users who are trying to
|
85
|
+
access your madmin panel: [Authentication Docs](docs/authentication.md)
|
86
|
+
|
40
87
|
## 🙏 Contributing
|
41
88
|
|
42
89
|
This project uses Standard for formatting Ruby code. Please make sure to run standardrb before submitting pull requests.
|
@@ -1,9 +1,9 @@
|
|
1
|
-
<%= stylesheet_link_tag "https://
|
2
|
-
<%= stylesheet_link_tag "https://
|
3
|
-
<%= stylesheet_link_tag "https://
|
1
|
+
<%= stylesheet_link_tag "https://unpkg.com/flatpickr/dist/flatpickr.min.css", "data-turbo-track": "reload" %>
|
2
|
+
<%= stylesheet_link_tag "https://unpkg.com/trix/dist/trix.css", "data-turbo-track": "reload" %>
|
3
|
+
<%= stylesheet_link_tag "https://unpkg.com/slim-select@1.27.0/dist/slimselect.min.css", "data-turbo-track": "reload" %>
|
4
4
|
|
5
5
|
<script type="module">
|
6
|
-
import { Application } from 'https://cdn.skypack.dev/stimulus'
|
6
|
+
import { Application, Controller } from 'https://cdn.skypack.dev/stimulus'
|
7
7
|
const application = Application.start()
|
8
8
|
|
9
9
|
import stimulusFlatpickr from 'https://cdn.skypack.dev/stimulus-flatpickr'
|
@@ -17,8 +17,43 @@
|
|
17
17
|
import 'https://cdn.skypack.dev/trix'
|
18
18
|
import 'https://cdn.skypack.dev/@rails/actiontext@<%= npm_rails_version %>'
|
19
19
|
|
20
|
-
Rails.start()
|
20
|
+
if (!window._rails_loaded) { Rails.start() }
|
21
21
|
ActiveStorage.start()
|
22
22
|
|
23
23
|
import * as Turbo from "https://cdn.skypack.dev/@hotwired/turbo"
|
24
|
+
|
25
|
+
(() => {
|
26
|
+
application.register('nested-form', class extends Controller {
|
27
|
+
static get targets() {
|
28
|
+
return [ "links", "template" ]
|
29
|
+
}
|
30
|
+
|
31
|
+
connect() {
|
32
|
+
this.wrapperClass = this.data.get("wrapperClass") || "nested-fields"
|
33
|
+
}
|
34
|
+
|
35
|
+
add_association(event) {
|
36
|
+
event.preventDefault()
|
37
|
+
|
38
|
+
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
|
39
|
+
this.linksTarget.insertAdjacentHTML('beforebegin', content)
|
40
|
+
}
|
41
|
+
|
42
|
+
remove_association(event) {
|
43
|
+
event.preventDefault()
|
44
|
+
|
45
|
+
let wrapper = event.target.closest("." + this.wrapperClass)
|
46
|
+
|
47
|
+
// New records are simply removed from the page
|
48
|
+
if (wrapper.dataset.newRecord == "true") {
|
49
|
+
wrapper.remove()
|
50
|
+
|
51
|
+
// Existing records are hidden and flagged for deletion
|
52
|
+
} else {
|
53
|
+
wrapper.querySelector("input[name*='_destroy']").value = 1
|
54
|
+
wrapper.style.display = 'none'
|
55
|
+
}
|
56
|
+
}
|
57
|
+
})
|
58
|
+
})()
|
24
59
|
</script>
|
@@ -1,2 +1,3 @@
|
|
1
|
-
<% object = field.value(record) %>
|
2
|
-
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
1
|
+
<% if (object = field.value(record)) %>
|
2
|
+
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
3
|
+
<% end %>
|
@@ -1,2 +1,3 @@
|
|
1
|
-
<% object = field.value(record) %>
|
2
|
-
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
1
|
+
<% if (object = field.value(record)) %>
|
2
|
+
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
3
|
+
<% end %>
|
@@ -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,30 @@
|
|
1
|
+
<%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
|
2
|
+
|
3
|
+
<div class="container space-y-8" data-controller="nested-form">
|
4
|
+
<template data-target="nested-form.template">
|
5
|
+
|
6
|
+
<%= form.fields_for field.attribute_name, field.to_model.new, child_index: 'NEW_RECORD' do |nested_form| %>
|
7
|
+
<%= render(
|
8
|
+
partial: field.to_partial_path('fields'),
|
9
|
+
locals: {
|
10
|
+
f: nested_form,
|
11
|
+
field: field
|
12
|
+
}
|
13
|
+
) %>
|
14
|
+
<% end %>
|
15
|
+
</template>
|
16
|
+
|
17
|
+
<%= form.fields_for field.attribute_name do |nested_form| %>
|
18
|
+
<%= render(
|
19
|
+
partial: field.to_partial_path('fields'),
|
20
|
+
locals: {
|
21
|
+
f: nested_form,
|
22
|
+
field: field
|
23
|
+
}
|
24
|
+
) %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<%= content_tag :div, class: '', data: { target:"nested-form.links" } do %>
|
28
|
+
<%= link_to "+ Add new", "#", data: { action: "click->nested-form#add_association" } %>
|
29
|
+
<% end %>
|
30
|
+
</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) %>
|
@@ -35,9 +35,10 @@ module Madmin
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
+
# Skip Abstract classes, ActiveRecord::Base, and auto-generated HABTM models
|
38
39
|
def generateable_models
|
39
40
|
active_record_models.reject do |model|
|
40
|
-
model.abstract_class? || model == ActiveRecord::Base
|
41
|
+
model.abstract_class? || model == ActiveRecord::Base || model.name.start_with?("HABTM_")
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
@@ -46,6 +46,10 @@ module Madmin
|
|
46
46
|
def virtual_attributes
|
47
47
|
virtual = []
|
48
48
|
|
49
|
+
# has_secure_password columns
|
50
|
+
password_attributes = model.attribute_types.keys.select { |k| k.ends_with?("_digest") }.map { |k| k.delete_suffix("_digest") }
|
51
|
+
virtual += password_attributes.map { |attr| [attr, "#{attr}_confirmation"] }.flatten
|
52
|
+
|
49
53
|
# Add virtual attributes for ActionText and ActiveStorage
|
50
54
|
model.reflections.each do |name, association|
|
51
55
|
if name.starts_with?("rich_text")
|
@@ -63,6 +67,9 @@ module Madmin
|
|
63
67
|
def redundant_attributes
|
64
68
|
redundant = []
|
65
69
|
|
70
|
+
# has_secure_password columns
|
71
|
+
redundant += model.attribute_types.keys.select { |k| k.ends_with?("_digest") }
|
72
|
+
|
66
73
|
model.reflections.each do |name, association|
|
67
74
|
if association.has_one?
|
68
75
|
next
|
@@ -98,13 +105,17 @@ module Madmin
|
|
98
105
|
if %w[id created_at updated_at].include?(name)
|
99
106
|
{form: false}
|
100
107
|
|
101
|
-
#
|
102
|
-
elsif
|
103
|
-
{index: false}
|
108
|
+
# has_secure_passwords should only show on forms
|
109
|
+
elsif name.ends_with?("_confirmation") || virtual_attributes.include?("#{name}_confirmation")
|
110
|
+
{index: false, show: false}
|
104
111
|
|
105
112
|
# Counter cache columns are typically not editable
|
106
113
|
elsif name.ends_with?("_count")
|
107
114
|
{form: false}
|
115
|
+
|
116
|
+
# Attributes without a database column
|
117
|
+
elsif !model.column_names.include?(name)
|
118
|
+
{index: false}
|
108
119
|
end
|
109
120
|
end
|
110
121
|
end
|
@@ -8,4 +8,9 @@ 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
|
11
16
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class EditGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_edit
|
10
|
+
copy_resource_template("edit")
|
11
|
+
copy_resource_template("_form")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class FormGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_form
|
10
|
+
copy_resource_template("_form")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class IndexGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_template
|
10
|
+
copy_resource_template("index")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
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
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class LayoutGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_template
|
10
|
+
copy_file(
|
11
|
+
"../../layouts/madmin/application.html.erb",
|
12
|
+
"app/views/layouts/madmin/application.html.erb"
|
13
|
+
)
|
14
|
+
|
15
|
+
call_generator("madmin:views:navigation")
|
16
|
+
copy_resource_template("_javascript")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class NavigationGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_navigation
|
10
|
+
copy_resource_template("_navigation")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class NewGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_new
|
10
|
+
copy_resource_template("new")
|
11
|
+
copy_resource_template("_form")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
module Views
|
6
|
+
class ShowGenerator < Madmin::ViewGenerator
|
7
|
+
source_root template_source_path
|
8
|
+
|
9
|
+
def copy_template
|
10
|
+
copy_resource_template("show")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "madmin/view_generator"
|
2
|
+
|
3
|
+
module Madmin
|
4
|
+
module Generators
|
5
|
+
class ViewsGenerator < Madmin::ViewGenerator
|
6
|
+
def copy_templates
|
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)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/madmin.rb
CHANGED
@@ -8,24 +8,26 @@ module Madmin
|
|
8
8
|
autoload :Resource, "madmin/resource"
|
9
9
|
|
10
10
|
module Fields
|
11
|
+
autoload :Attachment, "madmin/fields/attachment"
|
12
|
+
autoload :Attachments, "madmin/fields/attachments"
|
13
|
+
autoload :BelongsTo, "madmin/fields/belongs_to"
|
11
14
|
autoload :Boolean, "madmin/fields/boolean"
|
12
|
-
autoload :Integer, "madmin/fields/integer"
|
13
|
-
autoload :String, "madmin/fields/string"
|
14
|
-
autoload :Text, "madmin/fields/text"
|
15
15
|
autoload :Date, "madmin/fields/date"
|
16
16
|
autoload :DateTime, "madmin/fields/date_time"
|
17
17
|
autoload :Decimal, "madmin/fields/decimal"
|
18
|
-
autoload :Json, "madmin/fields/json"
|
19
18
|
autoload :Enum, "madmin/fields/enum"
|
20
19
|
autoload :Float, "madmin/fields/float"
|
21
|
-
autoload :Time, "madmin/fields/time"
|
22
|
-
autoload :BelongsTo, "madmin/fields/belongs_to"
|
23
|
-
autoload :Polymorphic, "madmin/fields/polymorphic"
|
24
20
|
autoload :HasMany, "madmin/fields/has_many"
|
25
21
|
autoload :HasOne, "madmin/fields/has_one"
|
22
|
+
autoload :Integer, "madmin/fields/integer"
|
23
|
+
autoload :Json, "madmin/fields/json"
|
24
|
+
autoload :NestedHasMany, "madmin/fields/nested_has_many"
|
25
|
+
autoload :Password, "madmin/fields/password"
|
26
|
+
autoload :Polymorphic, "madmin/fields/polymorphic"
|
26
27
|
autoload :RichText, "madmin/fields/rich_text"
|
27
|
-
autoload :
|
28
|
-
autoload :
|
28
|
+
autoload :String, "madmin/fields/string"
|
29
|
+
autoload :Text, "madmin/fields/text"
|
30
|
+
autoload :Time, "madmin/fields/time"
|
29
31
|
end
|
30
32
|
|
31
33
|
mattr_accessor :resources, default: []
|
@@ -3,10 +3,11 @@ module Madmin
|
|
3
3
|
class BelongsTo < Field
|
4
4
|
def options_for_select(record)
|
5
5
|
association = record.class.reflect_on_association(attribute_name)
|
6
|
-
|
7
6
|
klass = association.klass
|
7
|
+
resource = nil
|
8
8
|
klass.all.map do |r|
|
9
|
-
|
9
|
+
resource ||= Madmin.resource_for(r)
|
10
|
+
[resource.display_name(r), r.id]
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
data/lib/madmin/fields/enum.rb
CHANGED
@@ -3,10 +3,11 @@ module Madmin
|
|
3
3
|
class HasMany < Field
|
4
4
|
def options_for_select(record)
|
5
5
|
association = record.class.reflect_on_association(attribute_name)
|
6
|
-
|
7
6
|
klass = association.klass
|
7
|
+
resource = nil
|
8
8
|
klass.all.map do |r|
|
9
|
-
|
9
|
+
resource ||= Madmin.resource_for(r)
|
10
|
+
[resource.display_name(r), r.id]
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
@@ -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
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Madmin
|
2
|
+
class Namespace
|
3
|
+
def initialize(namespace)
|
4
|
+
@namespace = namespace
|
5
|
+
end
|
6
|
+
|
7
|
+
def resources
|
8
|
+
@resources ||= routes.map(&:first).uniq.map { |path|
|
9
|
+
Resource.new(namespace, path)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def routes
|
14
|
+
@routes ||= all_routes.select { |controller, _action|
|
15
|
+
controller.starts_with?("#{namespace}/")
|
16
|
+
}.map { |controller, action|
|
17
|
+
[controller.gsub(/^#{namespace}\//, ""), action]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def resources_with_index_route
|
22
|
+
routes.select { |_resource, route| route == "index" }.map(&:first).uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :namespace
|
28
|
+
|
29
|
+
def all_routes
|
30
|
+
Rails.application.routes.routes.map do |route|
|
31
|
+
route.defaults.values_at(:controller, :action).map(&:to_s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/madmin/resource.rb
CHANGED
@@ -18,6 +18,10 @@ module Madmin
|
|
18
18
|
model_name.constantize
|
19
19
|
end
|
20
20
|
|
21
|
+
def model_find(id)
|
22
|
+
friendly_model? ? model.friendly.find(id) : model.find(id)
|
23
|
+
end
|
24
|
+
|
21
25
|
def model_name
|
22
26
|
to_s.chomp("Resource").classify
|
23
27
|
end
|
@@ -38,21 +42,27 @@ module Madmin
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def index_path(options = {})
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
route_name = "madmin_#{model.table_name}_path"
|
46
|
+
|
47
|
+
url_helpers.send(route_name, options)
|
44
48
|
end
|
45
49
|
|
46
50
|
def new_path
|
47
|
-
"
|
51
|
+
route_name = "new_madmin_#{model.model_name.singular}_path"
|
52
|
+
|
53
|
+
url_helpers.send(route_name)
|
48
54
|
end
|
49
55
|
|
50
56
|
def show_path(record)
|
51
|
-
"
|
57
|
+
route_name = "madmin_#{model.model_name.singular}_path"
|
58
|
+
|
59
|
+
url_helpers.send(route_name, record.to_param)
|
52
60
|
end
|
53
61
|
|
54
62
|
def edit_path(record)
|
55
|
-
"
|
63
|
+
route_name = "edit_madmin_#{model.model_name.singular}_path"
|
64
|
+
|
65
|
+
url_helpers.send(route_name, record.to_param)
|
56
66
|
end
|
57
67
|
|
58
68
|
def param_key
|
@@ -67,6 +77,10 @@ module Madmin
|
|
67
77
|
"#{record.class} ##{record.id}"
|
68
78
|
end
|
69
79
|
|
80
|
+
def friendly_model?
|
81
|
+
model.respond_to? :friendly
|
82
|
+
end
|
83
|
+
|
70
84
|
private
|
71
85
|
|
72
86
|
def field_for_type(name, type)
|
@@ -90,6 +104,7 @@ module Madmin
|
|
90
104
|
text: Fields::Text,
|
91
105
|
time: Fields::Time,
|
92
106
|
timestamp: Fields::Time,
|
107
|
+
password: Fields::Password,
|
93
108
|
|
94
109
|
# Postgres specific types
|
95
110
|
bit: Fields::String,
|
@@ -126,7 +141,8 @@ module Madmin
|
|
126
141
|
polymorphic: Fields::Polymorphic,
|
127
142
|
has_many: Fields::HasMany,
|
128
143
|
has_one: Fields::HasOne,
|
129
|
-
rich_text: Fields::RichText
|
144
|
+
rich_text: Fields::RichText,
|
145
|
+
nested_has_many: Fields::NestedHasMany
|
130
146
|
}.fetch(type)
|
131
147
|
rescue
|
132
148
|
raise ArgumentError, <<~MESSAGE
|
@@ -143,7 +159,12 @@ module Madmin
|
|
143
159
|
name_string = name.to_s
|
144
160
|
|
145
161
|
if model.attribute_types.include?(name_string)
|
146
|
-
model.attribute_types[name_string]
|
162
|
+
column_type = model.attribute_types[name_string]
|
163
|
+
if column_type.is_a? ActiveRecord::Enum::EnumType
|
164
|
+
:enum
|
165
|
+
else
|
166
|
+
column_type.type || :string
|
167
|
+
end
|
147
168
|
elsif (association = model.reflect_on_association(name))
|
148
169
|
type_for_association(association)
|
149
170
|
elsif model.reflect_on_association(:"rich_text_#{name_string}")
|
@@ -152,6 +173,10 @@ module Madmin
|
|
152
173
|
:attachment
|
153
174
|
elsif model.reflect_on_association(:"#{name_string}_attachments")
|
154
175
|
:attachments
|
176
|
+
|
177
|
+
# has_secure_password
|
178
|
+
elsif model.attribute_types.include?("#{name_string}_digest") || name_string.ends_with?("_confirmation")
|
179
|
+
:password
|
155
180
|
end
|
156
181
|
end
|
157
182
|
|
@@ -166,6 +191,10 @@ module Madmin
|
|
166
191
|
:belongs_to
|
167
192
|
end
|
168
193
|
end
|
194
|
+
|
195
|
+
def url_helpers
|
196
|
+
@url_helpers ||= Rails.application.routes.url_helpers
|
197
|
+
end
|
169
198
|
end
|
170
199
|
end
|
171
200
|
end
|
data/lib/madmin/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
require "madmin/generator_helpers"
|
3
|
+
require "madmin/namespace"
|
4
|
+
|
5
|
+
module Madmin
|
6
|
+
class ViewGenerator < Rails::Generators::Base
|
7
|
+
include Madmin::GeneratorHelpers
|
8
|
+
class_option :namespace, type: :string, default: "madmin"
|
9
|
+
|
10
|
+
def self.template_source_path
|
11
|
+
File.expand_path(
|
12
|
+
"../../../app/views/madmin/application",
|
13
|
+
__FILE__
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def namespace
|
20
|
+
options[:namespace]
|
21
|
+
end
|
22
|
+
|
23
|
+
def copy_resource_template(template_name)
|
24
|
+
template_file = "#{template_name}.html.erb"
|
25
|
+
|
26
|
+
copy_file(
|
27
|
+
template_file,
|
28
|
+
"app/views/#{namespace}/#{resource_path}/#{template_file}"
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def resource_path
|
33
|
+
args.first.try(:underscore).try(:pluralize) || BaseResourcePath.new
|
34
|
+
end
|
35
|
+
|
36
|
+
class BaseResourcePath
|
37
|
+
def to_s
|
38
|
+
"application"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: madmin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Oliver
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-04-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -111,6 +111,13 @@ files:
|
|
111
111
|
- app/views/madmin/fields/json/_form.html.erb
|
112
112
|
- app/views/madmin/fields/json/_index.html.erb
|
113
113
|
- app/views/madmin/fields/json/_show.html.erb
|
114
|
+
- app/views/madmin/fields/nested_has_many/_fields.html.erb
|
115
|
+
- app/views/madmin/fields/nested_has_many/_form.html.erb
|
116
|
+
- app/views/madmin/fields/nested_has_many/_index.html.erb
|
117
|
+
- app/views/madmin/fields/nested_has_many/_show.html.erb
|
118
|
+
- app/views/madmin/fields/password/_form.html.erb
|
119
|
+
- app/views/madmin/fields/password/_index.html.erb
|
120
|
+
- app/views/madmin/fields/password/_show.html.erb
|
114
121
|
- app/views/madmin/fields/polymorphic/_form.html.erb
|
115
122
|
- app/views/madmin/fields/polymorphic/_index.html.erb
|
116
123
|
- app/views/madmin/fields/polymorphic/_show.html.erb
|
@@ -131,6 +138,15 @@ files:
|
|
131
138
|
- lib/generators/madmin/resource/resource_generator.rb
|
132
139
|
- lib/generators/madmin/resource/templates/controller.rb.tt
|
133
140
|
- lib/generators/madmin/resource/templates/resource.rb.tt
|
141
|
+
- lib/generators/madmin/views/edit_generator.rb
|
142
|
+
- lib/generators/madmin/views/form_generator.rb
|
143
|
+
- lib/generators/madmin/views/index_generator.rb
|
144
|
+
- lib/generators/madmin/views/javascript_generator.rb
|
145
|
+
- lib/generators/madmin/views/layout_generator.rb
|
146
|
+
- lib/generators/madmin/views/navigation_generator.rb
|
147
|
+
- lib/generators/madmin/views/new_generator.rb
|
148
|
+
- lib/generators/madmin/views/show_generator.rb
|
149
|
+
- lib/generators/madmin/views/views_generator.rb
|
134
150
|
- lib/madmin.rb
|
135
151
|
- lib/madmin/engine.rb
|
136
152
|
- lib/madmin/field.rb
|
@@ -147,14 +163,18 @@ files:
|
|
147
163
|
- lib/madmin/fields/has_one.rb
|
148
164
|
- lib/madmin/fields/integer.rb
|
149
165
|
- lib/madmin/fields/json.rb
|
166
|
+
- lib/madmin/fields/nested_has_many.rb
|
167
|
+
- lib/madmin/fields/password.rb
|
150
168
|
- lib/madmin/fields/polymorphic.rb
|
151
169
|
- lib/madmin/fields/rich_text.rb
|
152
170
|
- lib/madmin/fields/string.rb
|
153
171
|
- lib/madmin/fields/text.rb
|
154
172
|
- lib/madmin/fields/time.rb
|
155
173
|
- lib/madmin/generator_helpers.rb
|
174
|
+
- lib/madmin/namespace.rb
|
156
175
|
- lib/madmin/resource.rb
|
157
176
|
- lib/madmin/version.rb
|
177
|
+
- lib/madmin/view_generator.rb
|
158
178
|
- lib/tasks/madmin_tasks.rake
|
159
179
|
homepage: https://github.com/excid3/madmin
|
160
180
|
licenses:
|
@@ -171,9 +191,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
171
191
|
version: 2.5.0
|
172
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
193
|
requirements:
|
174
|
-
- - "
|
194
|
+
- - ">="
|
175
195
|
- !ruby/object:Gem::Version
|
176
|
-
version:
|
196
|
+
version: '0'
|
177
197
|
requirements: []
|
178
198
|
rubygems_version: 3.2.3
|
179
199
|
signing_key:
|