madmin 0.1.1 → 1.0.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +68 -6
  3. data/Rakefile +4 -0
  4. data/app/controllers/madmin/{application_controller.rb → base_controller.rb} +1 -1
  5. data/app/controllers/madmin/resource_controller.rb +12 -3
  6. data/app/helpers/madmin/application_helper.rb +12 -0
  7. data/app/views/layouts/madmin/application.html.erb +2 -3
  8. data/app/views/madmin/application/_javascript.html.erb +24 -0
  9. data/app/views/madmin/application/_navigation.html.erb +1 -1
  10. data/app/views/madmin/application/index.html.erb +14 -2
  11. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -1
  12. data/app/views/madmin/fields/belongs_to/_index.html.erb +3 -2
  13. data/app/views/madmin/fields/belongs_to/_show.html.erb +3 -2
  14. data/app/views/madmin/fields/date/_form.html.erb +1 -1
  15. data/app/views/madmin/fields/date_time/_form.html.erb +1 -1
  16. data/app/views/madmin/fields/decimal/_form.html.erb +2 -0
  17. data/app/views/madmin/fields/decimal/_index.html.erb +1 -0
  18. data/app/views/madmin/fields/decimal/_show.html.erb +1 -0
  19. data/app/views/madmin/fields/enum/_form.html.erb +1 -2
  20. data/app/views/madmin/fields/float/_form.html.erb +1 -1
  21. data/app/views/madmin/fields/has_many/_form.html.erb +1 -1
  22. data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -1
  23. data/app/views/madmin/fields/rich_text/_form.html.erb +2 -2
  24. data/lib/generators/madmin/install/install_generator.rb +8 -1
  25. data/lib/generators/madmin/install/templates/controller.rb.tt +22 -0
  26. data/lib/generators/madmin/views/edit_generator.rb +16 -0
  27. data/lib/generators/madmin/views/form_generator.rb +15 -0
  28. data/lib/generators/madmin/views/index_generator.rb +15 -0
  29. data/lib/generators/madmin/views/layout_generator.rb +21 -0
  30. data/lib/generators/madmin/views/navigation_generator.rb +15 -0
  31. data/lib/generators/madmin/views/new_generator.rb +16 -0
  32. data/lib/generators/madmin/views/show_generator.rb +15 -0
  33. data/lib/generators/madmin/views/views_generator.rb +15 -0
  34. data/lib/madmin.rb +1 -0
  35. data/lib/madmin/field.rb +7 -2
  36. data/lib/madmin/fields/decimal.rb +6 -0
  37. data/lib/madmin/fields/enum.rb +3 -0
  38. data/lib/madmin/namespace.rb +35 -0
  39. data/lib/madmin/resource.rb +56 -5
  40. data/lib/madmin/version.rb +1 -1
  41. data/lib/madmin/view_generator.rb +42 -0
  42. metadata +23 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ae907c78952efd333b7151ef50818dc6b171017239a11152c129c17bb45af10
4
- data.tar.gz: 923a8e6ecb3e84bffd6973a511845cc16cc599e36adc5116525ab01e46bc1c16
3
+ metadata.gz: 19d2f645bfcae060a5a925b945892a2a196790a2d6c3b296ec0fe5118aeaf186
4
+ data.tar.gz: 4310e5b0fb00744f6098032799d6df7368890b736382a9bad466d328662f4da4
5
5
  SHA512:
6
- metadata.gz: c696a1782666fdcc4a113d7709775c41e245a9f92ef298c93a6fb98a1c1f2db4f5a34f36d59a1c3da476a6db44bfcc864f5b37c8136ef8f8a155415344b74660
7
- data.tar.gz: d7eef669f0b0366ccaf73f3cd68f85525067172ade70c55397b9cd27e7132c3008a067d68d5bc3d073e94441f1cbf8a9e893a11a2b91fbfd0dbdd6a695487230
6
+ metadata.gz: c4d157858da05695020064ef9aa636079298cdc7bb1937bc3a4cb13faf71087d31ac0df3d1f3027414112aae61e97f9bfd099a1cf8bbf6bf8380532e654fe844
7
+ data.tar.gz: 2969bdf7c7e419290cff1db31f1d22befdb2eb7b0591cc60a00a328dd7e024991825547cae8404a98abdfd309c3308bc7c46332bf154fd6765f48372785baddd
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # Madmin
2
2
 
3
- ### A robust admin interface for Ruby on Rails apps
3
+ ### 🛠 A robust Admin Interface for Ruby on Rails apps
4
4
 
5
5
  [![Build Status](https://github.com/excid3/madmin/workflows/Tests/badge.svg)](https://github.com/excid3/madmin/actions) [![Gem Version](https://badge.fury.io/rb/madmin.svg)](https://badge.fury.io/rb/madmin)
6
6
 
7
- ## Usage
8
- How to use my plugin.
7
+ Why another Ruby on Rails admin? We wanted an admin that was:
8
+
9
+ * Familiar and customizable like Rails scaffolds (less DSL)
10
+ * Supports all the Rails features out of the box (ActionText, ActionMailbox, etc)
11
+ * Stimulus / Turbolinks / Hotwire ready
9
12
 
10
13
  ## Installation
11
14
  Add `madmin` to your application's Gemfile:
@@ -20,8 +23,67 @@ Then run the madmin generator:
20
23
  rails g madmin:install
21
24
  ```
22
25
 
23
- ## Contributing
24
- Contribution directions go here.
26
+ This will install Madmin and generate resources for each of the models it finds.
27
+
28
+ ## Resources
29
+
30
+ Madmin uses `Resource` classes to add models to the admin area.
31
+
32
+ ### Generate a Resource
33
+
34
+ To generate a resource for a model, you can run:
35
+
36
+ ```bash
37
+ rails g madmin:resource ActionText::RichText
38
+ ```
39
+
40
+ ## Configuring Views
41
+
42
+ The views packaged within the gem are a great starting point, but inevitably people will need to be able to customize those views.
43
+
44
+ You can use the included generator to create the appropriate view files, which can then be customized.
45
+
46
+ For example, running the following will copy over all of the views into your application that will be used for every resource:
47
+ ```bash
48
+ rails generate madmin:views
49
+ ```
50
+
51
+ 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:
52
+ * `application.html.erb` (layout file)
53
+ * `_javascript.html.erb` (default JavaScript setup)
54
+ * `_navigation.html.erb` (renders the navigation/sidebar menu)
55
+
56
+ As with the other views, you can specifically run the views generator for only the navigation or application layout views:
57
+ ```bash
58
+ rails g madmin:views:navigation
59
+ # -> app/views/madmin/_navigation.html.erb
60
+
61
+ rails g madmin:views:layout # Note the layout generator includes the layout, javascript, and navigation files.
62
+ # -> app/views/madmin/application.html.erb
63
+ # -> app/views/madmin/_javascript.html.erb
64
+ # -> app/views/madmin/_navigation.html.erb
65
+ ```
66
+
67
+ If you only need to customize specific views, you can restrict which views are copied by the generator:
68
+ ```bash
69
+ rails g madmin:views:index
70
+ # -> app/views/madmin/application/index.html.erb
71
+ ```
72
+
73
+ You can also scope the copied view(s) to a specific Resource/Model:
74
+ ```bash
75
+ rails generate madmin:views:index Book
76
+ # -> app/views/madmin/books/index.html.erb
77
+ ```
78
+
79
+ ## Authentication
80
+
81
+ You can use a couple of strategies to authenticate users who are trying to
82
+ access your madmin panel: [Authentication Docs](docs/authentication.md)
83
+
84
+ ## 🙏 Contributing
85
+
86
+ This project uses Standard for formatting Ruby code. Please make sure to run standardrb before submitting pull requests.
25
87
 
26
- ## License
88
+ ## 📝 License
27
89
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -18,6 +18,10 @@ require "bundler/gem_tasks"
18
18
 
19
19
  require "rake/testtask"
20
20
 
21
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
22
+ load "rails/tasks/engine.rake"
23
+ load "rails/tasks/statistics.rake"
24
+
21
25
  Rake::TestTask.new(:test) do |t|
22
26
  t.libs << "test"
23
27
  t.pattern = "test/**/*_test.rb"
@@ -1,5 +1,5 @@
1
1
  module Madmin
2
- class ApplicationController < ActionController::Base
2
+ class BaseController < ActionController::Base
3
3
  include Pagy::Backend
4
4
 
5
5
  protect_from_forgery with: :exception
@@ -3,7 +3,7 @@ module Madmin
3
3
  before_action :set_record, except: [:index, :new, :create]
4
4
 
5
5
  def index
6
- @pagy, @records = pagy(resource.model.all)
6
+ @pagy, @records = pagy(scoped_resources)
7
7
  end
8
8
 
9
9
  def show
@@ -18,7 +18,7 @@ module Madmin
18
18
  if @record.save
19
19
  redirect_to resource.show_path(@record)
20
20
  else
21
- render :new
21
+ render :new, status: :unprocessable_entity
22
22
  end
23
23
  end
24
24
 
@@ -29,7 +29,7 @@ module Madmin
29
29
  if @record.update(resource_params)
30
30
  redirect_to resource.show_path(@record)
31
31
  else
32
- render :edit
32
+ render :edit, status: :unprocessable_entity
33
33
  end
34
34
  end
35
35
 
@@ -53,6 +53,15 @@ module Madmin
53
53
  "#{controller_path.singularize}_resource".delete_prefix("madmin/").classify
54
54
  end
55
55
 
56
+ def scoped_resources
57
+ resource.model.send(valid_scope)
58
+ end
59
+
60
+ def valid_scope
61
+ scope = params.fetch(:scope, "all")
62
+ resource.scopes.include?(scope.to_sym) ? scope : :all
63
+ end
64
+
56
65
  def resource_params
57
66
  params.require(resource.param_key)
58
67
  .permit(*resource.permitted_params)
@@ -1,5 +1,17 @@
1
1
  module Madmin
2
2
  module ApplicationHelper
3
3
  include Pagy::Frontend
4
+
5
+ # Converts a Rails version to a NPM version
6
+ def npm_rails_version
7
+ version = [
8
+ Rails::VERSION::MAJOR,
9
+ Rails::VERSION::MINOR,
10
+ Rails::VERSION::TINY
11
+ ].join(".")
12
+
13
+ version += "-#{Rails::VERSION::PRE}" if Rails::VERSION::PRE
14
+ version
15
+ end
4
16
  end
5
17
  end
@@ -10,9 +10,9 @@
10
10
  <link href="https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" rel="stylesheet" />
11
11
  <link href="https://unpkg.com/@tailwindcss/forms/dist/forms.min.css" rel="stylesheet" />
12
12
  <link href="https://unpkg.com/@tailwindcss/typography/dist/typography.min.css" rel="stylesheet" />
13
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
14
- <%= javascript_pack_tag "madmin", "data-turbo-track": "reload" %>
15
13
  <%= csrf_meta_tags %>
14
+
15
+ <%= render "javascript" %>
16
16
  </head>
17
17
  <body class="prose" style="max-width:none">
18
18
  <div class="flex w-full p-4">
@@ -24,6 +24,5 @@
24
24
  <%= yield %>
25
25
  </main>
26
26
  </div>
27
- <%#= render "javascript" %>
28
27
  </body>
29
28
  </html>
@@ -0,0 +1,24 @@
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
+
5
+ <script type="module">
6
+ import { Application } from 'https://cdn.skypack.dev/stimulus'
7
+ const application = Application.start()
8
+
9
+ import stimulusFlatpickr from 'https://cdn.skypack.dev/stimulus-flatpickr'
10
+ application.register("flatpickr", stimulusFlatpickr)
11
+
12
+ import stimulusSlimselect from 'https://cdn.skypack.dev/stimulus-slimselect'
13
+ application.register("slimselect", stimulusSlimselect)
14
+
15
+ import Rails from 'https://cdn.skypack.dev/@rails/ujs@<%= npm_rails_version %>'
16
+ import * as ActiveStorage from 'https://cdn.skypack.dev/@rails/activestorage@<%= npm_rails_version %>'
17
+ import 'https://cdn.skypack.dev/trix'
18
+ import 'https://cdn.skypack.dev/@rails/actiontext@<%= npm_rails_version %>'
19
+
20
+ if (!window._rails_loaded) { Rails.start() }
21
+ ActiveStorage.start()
22
+
23
+ import * as Turbo from "https://cdn.skypack.dev/@hotwired/turbo"
24
+ </script>
@@ -1,5 +1,5 @@
1
1
  <div class="text-sm">
2
- <%= link_to "← Back to App", main_app.root_url, class: "block p-1" if main_app.respond_to?(:root_url) %>
2
+ <%= link_to "← Back to App", main_app.root_url, class: "block p-1", data: { turbo: false } if main_app.respond_to?(:root_url) %>
3
3
  <% Madmin.resources.each do |resource| %>
4
4
  <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "block p-1" %>
5
5
  <% end %>
@@ -1,5 +1,17 @@
1
- <h1><%= resource.friendly_name.pluralize %></h1>
2
- <%= link_to "New #{resource.friendly_name}", resource.new_path %>
1
+ <div class="flex justify-between">
2
+ <h1><%= resource.friendly_name.pluralize %></h1>
3
+ <%= link_to "New #{resource.friendly_name}", resource.new_path %>
4
+ </div>
5
+
6
+ <div>
7
+ <% if resource.scopes.any? %>
8
+ <%= link_to "All", resource.index_path %>
9
+ <% end %>
10
+
11
+ <% resource.scopes.each do |scope| %>
12
+ <%= link_to scope.to_s.humanize, resource.index_path(scope: scope) %>
13
+ <% end %>
14
+ </div>
3
15
 
4
16
  <table class="table-auto">
5
17
  <thead>
@@ -1,2 +1,2 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-select" } %>
2
+ <%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "slimselect", data: { controller: "slimselect" } } } %>
@@ -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 %>
@@ -1,2 +1,2 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.date_select field.attribute_name, {}, { class: "form-select" } %>
2
+ <%= form.text_field field.attribute_name, { class: "form-select", data: { controller: "flatpickr" } } %>
@@ -1,2 +1,2 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.datetime_select field.attribute_name, {}, { class: "form-select" } %>
2
+ <%= form.text_field field.attribute_name, data: { controller: "flatpickr", flatpickr_enable_time: true } %>
@@ -0,0 +1,2 @@
1
+ <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
+ <%= form.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -1,3 +1,2 @@
1
- ENUM
2
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
3
- <%= field.value(record) %>
2
+ <%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-select" } %>
@@ -1,2 +1,2 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.number_field field.attribute_name, class: "form-input" %>
2
+ <%= form.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -1,2 +1,2 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select" } %>
2
+ <%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select", data: { controller: "slimselect" } } %>
@@ -1,5 +1,5 @@
1
1
  <%= form.fields_for field.attribute_name do |pf| %>
2
2
  <%= pf.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
3
- <%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, { class: "form-select" } %>
3
+ <%= 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
4
  <%= pf.hidden_field :type, value: "polymorphic" %>
5
5
  <% end %>
@@ -1,4 +1,4 @@
1
1
  <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <div>
3
- <%= form.rich_text_area field.attribute_name, class: "form-input" %>
2
+ <div class="flex-1">
3
+ <%= form.rich_text_area field.attribute_name, class: "form-input block" %>
4
4
  </div>
@@ -5,10 +5,16 @@ module Madmin
5
5
  class InstallGenerator < Rails::Generators::Base
6
6
  include Madmin::GeneratorHelpers
7
7
 
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
8
10
  def eager_load
9
11
  Rails.application.eager_load!
10
12
  end
11
13
 
14
+ def copy_controller
15
+ template("controller.rb.tt", "app/controllers/madmin/application_controller.rb")
16
+ end
17
+
12
18
  def generate_routes
13
19
  if route_namespace_exists?
14
20
  route "root to: \"dashboard#show\"", indentation: 4, sentinel: /namespace :madmin do\s*\n/m
@@ -29,9 +35,10 @@ module Madmin
29
35
 
30
36
  private
31
37
 
38
+ # Skip Abstract classes, ActiveRecord::Base, and auto-generated HABTM models
32
39
  def generateable_models
33
40
  active_record_models.reject do |model|
34
- model.abstract_class? || model == ActiveRecord::Base
41
+ model.abstract_class? || model == ActiveRecord::Base || model.name.start_with?("HABTM_")
35
42
  end
36
43
  end
37
44
 
@@ -0,0 +1,22 @@
1
+ module Madmin
2
+ class ApplicationController < Madmin::BaseController
3
+ before_action :authenticate_admin_user
4
+
5
+ def authenticate_admin_user
6
+ # TODO: Add your authentication logic here
7
+
8
+ # For example, we could redirect if the user isn't an admin
9
+ # redirect_to "/", alert: "Not authorized." unless user_signed_in? && current_user.admin?
10
+ end
11
+
12
+ # Authenticate with Clearance
13
+ # include Clearance::Controller
14
+ # before_action :require_login
15
+
16
+ # Authenticate with Devise
17
+ # before_action :authenticate_user!
18
+
19
+ # Authenticate with Basic Auth
20
+ # http_basic_authenticate_with(name: Rails.application.credentials.admin_username, password: Rails.application.credentials.admin_password)
21
+ end
22
+ 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,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,15 @@
1
+ require "madmin/view_generator"
2
+
3
+ module Madmin
4
+ module Generators
5
+ class ViewsGenerator < Madmin::ViewGenerator
6
+ def copy_templates
7
+ view = "madmin:views:"
8
+ call_generator("#{view}index", resource_path, "--namespace", namespace)
9
+ call_generator("#{view}show", resource_path, "--namespace", namespace)
10
+ call_generator("#{view}new", resource_path, "--namespace", namespace)
11
+ call_generator("#{view}edit", resource_path, "--namespace", namespace)
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/madmin.rb CHANGED
@@ -14,6 +14,7 @@ module Madmin
14
14
  autoload :Text, "madmin/fields/text"
15
15
  autoload :Date, "madmin/fields/date"
16
16
  autoload :DateTime, "madmin/fields/date_time"
17
+ autoload :Decimal, "madmin/fields/decimal"
17
18
  autoload :Json, "madmin/fields/json"
18
19
  autoload :Enum, "madmin/fields/enum"
19
20
  autoload :Float, "madmin/fields/float"
data/lib/madmin/field.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  module Madmin
2
2
  class Field
3
- attr_reader :attribute_name, :options
3
+ attr_reader :attribute_name, :model, :options
4
4
 
5
5
  def self.field_type
6
6
  to_s.split("::").last.underscore
7
7
  end
8
8
 
9
- def initialize(attribute_name:, **options)
9
+ def initialize(attribute_name:, model:, **options)
10
10
  @attribute_name = attribute_name
11
+ @model = model
11
12
  @options = options
12
13
  end
13
14
 
@@ -31,5 +32,9 @@ module Madmin
31
32
  def visible?(action, default: true)
32
33
  options.fetch(action.to_sym, default)
33
34
  end
35
+
36
+ def required?
37
+ model.validators_on(attribute_name).any? { |v| v.is_a? ActiveModel::Validations::PresenceValidator }
38
+ end
34
39
  end
35
40
  end
@@ -0,0 +1,6 @@
1
+ module Madmin
2
+ module Fields
3
+ class Decimal < Field
4
+ end
5
+ end
6
+ end
@@ -1,6 +1,9 @@
1
1
  module Madmin
2
2
  module Fields
3
3
  class Enum < Field
4
+ def options_for_select(record)
5
+ model.defined_enums[attribute_name.to_s].keys
6
+ end
4
7
  end
5
8
  end
6
9
  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
@@ -29,7 +29,7 @@ module Madmin
29
29
  def attribute(name, type = nil, **options)
30
30
  attributes << {
31
31
  name: name,
32
- field: field_for_type(name, type).new(**options.merge(attribute_name: name))
32
+ field: field_for_type(name, type).new(**options.merge(attribute_name: name, model: model))
33
33
  }
34
34
  end
35
35
 
@@ -37,8 +37,10 @@ module Madmin
37
37
  model_name.gsub("::", " / ")
38
38
  end
39
39
 
40
- def index_path
41
- "/madmin/#{model.model_name.collection}"
40
+ def index_path(options = {})
41
+ path = "/madmin/#{model.model_name.collection}"
42
+ path += "?#{options.to_param}" if options.any?
43
+ path
42
44
  end
43
45
 
44
46
  def new_path
@@ -71,16 +73,51 @@ module Madmin
71
73
  type ||= infer_type(name)
72
74
 
73
75
  {
76
+ binary: Fields::String,
77
+ blob: Fields::Text,
78
+ boolean: Fields::Boolean,
74
79
  date: Fields::Date,
75
80
  datetime: Fields::DateTime,
81
+ decimal: Fields::Decimal,
76
82
  enum: Fields::Enum,
77
83
  float: Fields::Float,
84
+ hstore: Fields::Json,
78
85
  integer: Fields::Integer,
79
86
  json: Fields::Json,
87
+ jsonb: Fields::Json,
88
+ primary_key: Fields::String,
80
89
  string: Fields::String,
81
90
  text: Fields::Text,
82
91
  time: Fields::Time,
83
- boolean: Fields::Boolean,
92
+ timestamp: Fields::Time,
93
+
94
+ # Postgres specific types
95
+ bit: Fields::String,
96
+ bit_varying: Fields::String,
97
+ box: Fields::String,
98
+ cidr: Fields::String,
99
+ circle: Fields::String,
100
+ citext: Fields::Text,
101
+ daterange: Fields::String,
102
+ inet: Fields::String,
103
+ int4range: Fields::String,
104
+ int8range: Fields::String,
105
+ interval: Fields::String,
106
+ line: Fields::String,
107
+ lseg: Fields::String,
108
+ ltree: Fields::String,
109
+ macaddr: Fields::String,
110
+ money: Fields::String,
111
+ numrange: Fields::String,
112
+ oid: Fields::String,
113
+ path: Fields::String,
114
+ point: Fields::String,
115
+ polygon: Fields::String,
116
+ tsrange: Fields::String,
117
+ tstzrange: Fields::String,
118
+ tsvector: Fields::String,
119
+ uuid: Fields::String,
120
+ xml: Fields::Text,
84
121
 
85
122
  # Associations
86
123
  attachment: Fields::Attachment,
@@ -91,13 +128,27 @@ module Madmin
91
128
  has_one: Fields::HasOne,
92
129
  rich_text: Fields::RichText
93
130
  }.fetch(type)
131
+ rescue
132
+ raise ArgumentError, <<~MESSAGE
133
+ Couldn't find attribute or association '#{name}' with type '#{type}' on #{model} model
134
+
135
+ To fix this, either:
136
+
137
+ 1. Remove 'attribute #{name}' from app/madmin/resources/#{model.to_s.underscore}_resource.rb
138
+ 2. Or add the missing attribute or association to the #{model} model
139
+ MESSAGE
94
140
  end
95
141
 
96
142
  def infer_type(name)
97
143
  name_string = name.to_s
98
144
 
99
145
  if model.attribute_types.include?(name_string)
100
- model.attribute_types[name_string].type || :string
146
+ column_type = model.attribute_types[name_string]
147
+ if column_type.is_a? ActiveRecord::Enum::EnumType
148
+ :enum
149
+ else
150
+ column_type.type || :string
151
+ end
101
152
  elsif (association = model.reflect_on_association(name))
102
153
  type_for_association(association)
103
154
  elsif model.reflect_on_association(:"rich_text_#{name_string}")
@@ -1,3 +1,3 @@
1
1
  module Madmin
2
- VERSION = "0.1.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -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,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: madmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
8
8
  - Andrea Fomera
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-01-15 00:00:00.000000000 Z
12
+ date: 2021-03-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -45,20 +45,6 @@ dependencies:
45
45
  - - "<"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '4.0'
48
- - !ruby/object:Gem::Dependency
49
- name: sqlite3
50
- requirement: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
48
  description: It's an admin, obviously.
63
49
  email:
64
50
  - excid3@gmail.com
@@ -73,12 +59,13 @@ files:
73
59
  - app/assets/config/manifest.js
74
60
  - app/assets/stylesheets/actiontext.scss
75
61
  - app/assets/stylesheets/application.css
76
- - app/controllers/madmin/application_controller.rb
62
+ - app/controllers/madmin/base_controller.rb
77
63
  - app/controllers/madmin/dashboard_controller.rb
78
64
  - app/controllers/madmin/resource_controller.rb
79
65
  - app/helpers/madmin/application_helper.rb
80
66
  - app/views/layouts/madmin/application.html.erb
81
67
  - app/views/madmin/application/_form.html.erb
68
+ - app/views/madmin/application/_javascript.html.erb
82
69
  - app/views/madmin/application/_navigation.html.erb
83
70
  - app/views/madmin/application/edit.html.erb
84
71
  - app/views/madmin/application/index.html.erb
@@ -103,6 +90,9 @@ files:
103
90
  - app/views/madmin/fields/date_time/_form.html.erb
104
91
  - app/views/madmin/fields/date_time/_index.html.erb
105
92
  - app/views/madmin/fields/date_time/_show.html.erb
93
+ - app/views/madmin/fields/decimal/_form.html.erb
94
+ - app/views/madmin/fields/decimal/_index.html.erb
95
+ - app/views/madmin/fields/decimal/_show.html.erb
106
96
  - app/views/madmin/fields/enum/_form.html.erb
107
97
  - app/views/madmin/fields/enum/_index.html.erb
108
98
  - app/views/madmin/fields/enum/_show.html.erb
@@ -137,9 +127,18 @@ files:
137
127
  - app/views/madmin/fields/time/_index.html.erb
138
128
  - app/views/madmin/fields/time/_show.html.erb
139
129
  - lib/generators/madmin/install/install_generator.rb
130
+ - lib/generators/madmin/install/templates/controller.rb.tt
140
131
  - lib/generators/madmin/resource/resource_generator.rb
141
132
  - lib/generators/madmin/resource/templates/controller.rb.tt
142
133
  - lib/generators/madmin/resource/templates/resource.rb.tt
134
+ - lib/generators/madmin/views/edit_generator.rb
135
+ - lib/generators/madmin/views/form_generator.rb
136
+ - lib/generators/madmin/views/index_generator.rb
137
+ - lib/generators/madmin/views/layout_generator.rb
138
+ - lib/generators/madmin/views/navigation_generator.rb
139
+ - lib/generators/madmin/views/new_generator.rb
140
+ - lib/generators/madmin/views/show_generator.rb
141
+ - lib/generators/madmin/views/views_generator.rb
143
142
  - lib/madmin.rb
144
143
  - lib/madmin/engine.rb
145
144
  - lib/madmin/field.rb
@@ -149,6 +148,7 @@ files:
149
148
  - lib/madmin/fields/boolean.rb
150
149
  - lib/madmin/fields/date.rb
151
150
  - lib/madmin/fields/date_time.rb
151
+ - lib/madmin/fields/decimal.rb
152
152
  - lib/madmin/fields/enum.rb
153
153
  - lib/madmin/fields/float.rb
154
154
  - lib/madmin/fields/has_many.rb
@@ -161,14 +161,16 @@ files:
161
161
  - lib/madmin/fields/text.rb
162
162
  - lib/madmin/fields/time.rb
163
163
  - lib/madmin/generator_helpers.rb
164
+ - lib/madmin/namespace.rb
164
165
  - lib/madmin/resource.rb
165
166
  - lib/madmin/version.rb
167
+ - lib/madmin/view_generator.rb
166
168
  - lib/tasks/madmin_tasks.rake
167
169
  homepage: https://github.com/excid3/madmin
168
170
  licenses:
169
171
  - MIT
170
172
  metadata: {}
171
- post_install_message:
173
+ post_install_message:
172
174
  rdoc_options: []
173
175
  require_paths:
174
176
  - lib
@@ -183,8 +185,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
185
  - !ruby/object:Gem::Version
184
186
  version: '0'
185
187
  requirements: []
186
- rubygems_version: 3.1.4
187
- signing_key:
188
+ rubygems_version: 3.2.3
189
+ signing_key:
188
190
  specification_version: 4
189
191
  summary: A modern admin for Ruby on Rails apps
190
192
  test_files: []