madmin 0.1.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []