GithubHelp home page GithubHelp logo

excid3 / madmin Goto Github PK

View Code? Open in Web Editor NEW
517.0 17.0 58.0 1.78 MB

A robust Admin Interface for Ruby on Rails apps

Home Page: https://github.com/excid3/madmin

License: MIT License

Ruby 4.95% JavaScript 93.19% SCSS 0.11% CSS 0.08% HTML 1.68%
ruby ruby-on-rails admin hacktoberfest rails admin-dashboard

madmin's Introduction

Madmin

πŸ›  A robust Admin Interface for Ruby on Rails apps

Build Status Gem Version

Why another Ruby on Rails admin? We wanted an admin that was:

  • Familiar and customizable like Rails scaffolds (less DSL)
  • Supports all the Rails features out of the box (ActionText, ActionMailbox, has_secure_password, etc)
  • Stimulus / Turbolinks / Hotwire ready

Madmin Screenshot We're still working on the design!

Installation

Add madmin to your application's Gemfile:

bundle add madmin

Then run the madmin generator:

rails g madmin:install

This will install Madmin and generate resources for each of the models it finds.

Resources

Madmin uses Resource classes to add models to the admin area.

Generate a Resource

To generate a resource for a model, you can run:

rails g madmin:resource ActionText::RichText

Configuring Views

The views packaged within the gem are a great starting point, but inevitably people will need to be able to customize those views.

You can use the included generator to create the appropriate view files, which can then be customized.

For example, running the following will copy over all of the views into your application that will be used for every resource:

rails generate madmin:views

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:

  • application.html.erb (layout file)
  • _javascript.html.erb (default JavaScript setup)
  • _navigation.html.erb (renders the navigation/sidebar menu)

As with the other views, you can specifically run the views generator for only the navigation or application layout views:

rails g madmin:views:navigation
 # -> app/views/madmin/_navigation.html.erb

rails g madmin:views:layout  # Note the layout generator includes the layout, javascript, and navigation files.
 # -> app/views/madmin/application.html.erb
 # -> app/views/madmin/_javascript.html.erb
 # -> app/views/madmin/_navigation.html.erb

If you only need to customize specific views, you can restrict which views are copied by the generator:

rails g madmin:views:index
 # -> app/views/madmin/application/index.html.erb

You might want to make some of your model's attributes visible in some views but invisible in others. The attribute method in model_resource.rb gives you that flexibility.

 # -> app/madmin/resources/book_resource.rb
class UserResource < Madmin::Resource
  attribute :id, form: false
  attribute :tile
  attribute :subtitle, index: false
  attribute :author
  attribute :genre
  attribute :pages, show: false
end

You can also scope the copied view(s) to a specific Resource/Model:

rails generate madmin:views:index Book
 # -> app/views/madmin/books/index.html.erb

Custom Fields

You can generate a custom field with:

rails g madmin:field Custom

This will create a CustomField class in app/madmin/fields/custom_field.rb And the related views:

# -> app/views/madmin/fields/custom_field/_form.html.erb
# -> app/views/madmin/fields/custom_field/_index.html.erb
# -> app/views/madmin/fields/custom_field/_show.html.erb

You can then use this field on our resource:

class PostResource < Madmin::Resource
  attribute :title, field: CustomField
end

Authentication

You can use a couple of strategies to authenticate users who are trying to access your madmin panel: Authentication Docs

πŸ™ Contributing

This project uses Standard for formatting Ruby code. Please make sure to run standardrb before submitting pull requests.

πŸ“ License

The gem is available as open source under the terms of the MIT License.

madmin's People

Contributors

0xrichardh avatar afomera avatar andersklenke avatar bramjetten avatar dependabot[bot] avatar dhurba87 avatar esmale avatar etagwerker avatar excid3 avatar fivenineplusr avatar galtdea avatar ilunglee avatar jacobdaddario avatar jespr avatar jitingcn avatar juanvqz avatar kashiftariq1997 avatar maxencelenoir avatar mikerogers0 avatar npverni avatar pawelbanach avatar pbcoronel avatar vitchell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

madmin's Issues

key not found: :Inet

ruby: 3.0.0
rails: 6.1.1
madmin: v0.1.1

definition:
t.citext :username, null: false, default: ''
t.inet :current_sign_in_ip

errors:
key not found: :inet
key not found: :citext
... and may be other postgresql types too(not tested)

Allow for custom path name

First of all thanks for this great gem!

Right now the admin interface lives at /madmin, and that is not customisable. I would like it at /admin instead. This is doable by doing:

namespace :madmin, path: "admin"

This introduces a problem with the navigation. The resource paths are hard coded to /madmin.

I can prepare a PR to generate the paths by the route name instead, if that's something you think is a good idea.

Support for Postgres Array type

I love postgres array type. I tend to reach for it for any tagging system that isn't too complex. Most of all, I find it super fun to write code like this:

module Taggable
  extend ActiveSupport::Concern

  class Parser
    def parse(tags)
      case tags
      when String
        tags.split(/ *, */)
      else
        tags
      end
    end
  end

  def self.parser
    @parser ||= Parser.new
  end

  module ClassMethods
    def has_tags(*tag_def)
      parser = Taggable.parser
      tag_def.each do |tag_name|
        scope :"with_any_#{tag_name}", ->(tags) { where("#{tag_name} && ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"with_all_#{tag_name}", ->(tags) { where("#{tag_name} @> ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"without_any_#{tag_name}", ->(tags) { where.not("#{tag_name} && ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"without_all_#{tag_name}", ->(tags) { where.not("#{tag_name} @> ARRAY[?]::varchar[]", parser.parse(tags)) }

        define_method :"#{tag_name}=" do |value|
          write_attribute tag_name,
            case value
            when String
              value.split(",").map(&:strip)
            else
              value
            end
        end

        define_method :"#{tag_name}_text" do
          (read_attribute(tag_name) || []).join ","
        end

        self.class.class_eval do
          define_method :"all_#{tag_name}" do |_options = {}, &block|
            subquery = unscoped.select("unnest(#{tag_name}) as tag")

            from(subquery).pluck(Arel.sql("distinct subquery.tag"))
          end

          define_method :"#{tag_name}_cloud" do |_options = {}, &block|
            subquery =
              unscoped
                .select("unnest(#{tag_name}) as tag, count(*) as count")
                .group(:tag)
                .order(count: :desc)

            from(subquery).pluck(Arel.sql("subquery.tag, subquery.count"))
          end
        end
      end
    end
  end
end

I just sloppy pasted this from the internet; it does get it done, even if it isn't pretty.

The idea is to tag, for example, blog posts.

When playing around with the possibilities of doing this in Administrate, I discovered a few limitations that would be absolutely amazing if they could be ironed out in madmin.

  1. I could use neither select nor create some multi-select because it still didn't allow me to add tags dynamically. In a back-office type of system like madmin, it doesn't need to be pretty. It would be to select tags but also have the ability to add new tags on the fly.
  2. While the code above takes both a comma-separated string and an array as input, it does display bizarre in administrate. I want better support for both editing and displaying array type built-in if possible.

If there is a way already, please let me know. Also, happy to contribute something if I can.

Application crashes on Heroku

Hi Chris,
App deploys successfully to heroku but crashes

$ heroku logs --tail reveals the following #deleted timestamps for clarity
heroku[worker.1]: Starting process with command sidekiq
heroku[worker.1]: State changed from starting to up
app[worker.1]: pid=4 tid=38s WARN: ArgumentError: Couldn't find attribute or association 'id' with type '' on Service model
app[worker.1]:
app[worker.1]: To fix this, either:
app[worker.1]: 1. Remove 'attribute id' from app/madmin/resources/service_resource.rb
app[worker.1]: 2. Or add the missing attribute or association to the Service model
app[worker.1]:
app[worker.1]: pid=4 tid=38s WARN: /app/vendor/bundle/ruby/2.7.0/gems/madmin-1.2.0/lib/madmin/resource.rb:152:in rescue in field_for_type' app[worker.1]: /app/vendor/bundle/ruby/2.7.0/gems/madmin-1.2.0/lib/madmin/resource.rb:36:in attribute'
app[worker.1]: /app/app/madmin/resources/service_resource.rb:3:in <class:ServiceResource>' app[worker.1]: /app/app/madmin/resources/service_resource.rb:2:in

'

Any thoughts?
Much appreciated
Archie

Relation fields in forms are not working

For models that have relations, the forms provide fields that purport to reference a select Stimulus controller: https://github.com/excid3/madmin/blob/master/app/views/madmin/fields/has_many/_form.html.erb#L4

These fields are not working for me. I believe they are supposed to be referencing a controller built into views/madmin/application/_javascript.html.erb.

In my browser console, I'm seeing the following:

SyntaxError: Importing binding name 'Controller' is not found.

Any idea how to fix this?

User resource management questions and `madmin:field` problem

Hi!

My issues are more questions about CRUD for users. I have users from devise that have password and password_confirmation. Unfortunately these are the fields that do not show up in the form for adding a new user. I understand that the only solution is to override the form view for users, and update action in users_controller?

Second question is about Resources. In a specific resource such as UserResource < Madmin::Resource we declare attribute :id, form: false. Is there somewhere a list of options that attribute can take, apart form: false/true?

Third thought πŸ˜„ I think rails g madmin:field does not work.

rails g madmin:field Custom

Running via Spring preloader in process 85728
Could not find generator 'madmin:field'. Maybe you meant "madmin:view"?
Run `bin/rails generate --help` for more options.

rails g --help

Madmin:
  madmin:install
  madmin:resource
  madmin:view
  madmin:views
  madmin:views:edit
  madmin:views:form
  madmin:views:index
  madmin:views:javascript
  madmin:views:layout
  madmin:views:navigation
  madmin:views:new
  madmin:views:show

Asset Pipeline & Webpacker Support

We need a couple tools like Flatpickr, slimselect, etc to make the admin more useful, plus CSS for styling the admin.

The goals here:

  • Good, clean defaults that work with Webpacker & the Asset Pipeline
  • Easily customizable to add your own JS & CSS
  • Everything Turbo + Turbolinks compatible

Javascript

It would be great if we could use something like Skypack CDN so that we didn't have to integrate with the Asset Pipeline or Webpacker. A user could then edit the layout to include their own JS.

If not, I think a good option is to actually provide two generators to install assets into your app (one for the asset pipeline, one for webpacker).

We could add app/javascript/packs/madmin.js and dependencies if webpacker is installed. And the same for app/assets/javascripts/madmin.js for the asset pipeline. We would have to install dependencies along with this approach though.

I'd like Madmin to fully support both Turbolinks & Hotwire, so Stimulus controllers are ideal here.

TailwindCSS

We're also currently using TailwindCSS for styling. This comes from a CDN, but it is a heck of a lot of CSS.

We could purge and ship only what we need as part of the gem and let users add their own CSS from the asset pipeline or webpacker.

key not found: :decimal

Exception on a new Rails app, models with decimal fields

  # Attributes
  attribute :id, form: false
  attribute :price
  attribute :complete
  attribute :created_at, form: false
  attribute :updated_at, form: false

Support store_accessor fields

We can find the store accessors easily with:

User.stored_attributes
#=> {:roles=>[:admin, :member]}

The values are the attributes we generate for Madmin resources.

The keys could probably be added to the list of ignored attributes.

Admin generation script fails due to has and belong to many association

Full stack trace below

Skipping ActionMailbox::InboundEmail because database table does not exist
/Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/inflector/methods.rb:288:in `const_get': uninitialized constant VideoCall::HabtmUsers (NameError)
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/inflector/methods.rb:288:in `block in constantize'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/inflector/methods.rb:284:in `each'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/inflector/methods.rb:284:in `inject'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/inflector/methods.rb:284:in `constantize'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/core_ext/string/inflections.rb:74:in `constantize'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/resource/resource_generator.rb:85:in `model'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/resource/resource_generator.rb:43:in `attributes'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/resource/templates/resource.rb.tt:3:in `template'
	from /Users/ankur/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/erb.rb:905:in `eval'
	from /Users/ankur/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/erb.rb:905:in `result'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/file_manipulation.rb:131:in `block in template'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:53:in `render'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:63:in `block (2 levels) in invoke!'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:63:in `open'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:63:in `block in invoke!'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/empty_directory.rb:117:in `invoke_with_conflict_check'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:60:in `invoke!'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions.rb:93:in `action'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/create_file.rb:25:in `create_file'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/actions/file_manipulation.rb:122:in `template'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/generators/named_base.rb:25:in `block in template'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/generators/named_base.rb:45:in `inside_template'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/generators/named_base.rb:24:in `template'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/resource/resource_generator.rb:13:in `generate_resource'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `block in invoke_all'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `each'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `map'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `invoke_all'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/group.rb:232:in `dispatch'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/base.rb:485:in `start'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/generators.rb:275:in `invoke'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/madmin/generator_helpers.rb:4:in `call_generator'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/install/install_generator.rb:23:in `block in generate_resources'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/install/install_generator.rb:21:in `each'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/madmin-0.1.1/lib/generators/madmin/install/install_generator.rb:21:in `generate_resources'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `block in invoke_all'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `each'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `map'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:134:in `invoke_all'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/group.rb:232:in `dispatch'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/base.rb:485:in `start'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/generators.rb:275:in `invoke'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/commands/generate/generate_command.rb:26:in `perform'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/command.rb:27:in `run'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/command/base.rb:69:in `perform'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/command.rb:50:in `invoke'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/railties-6.1.1/lib/rails/commands.rb:18:in `<main>'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
	from /Users/ankur/Projects/OnDemandVideoVisit/bin/rails:5:in `<main>'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:59:in `load'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/bootsnap-1.5.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:59:in `load'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/commands/rails.rb:6:in `call'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/command_wrapper.rb:38:in `call'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application.rb:220:in `block in serve'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:10:in `block in fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:10:in `block in fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:8:in `fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:8:in `fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:26:in `fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:8:in `fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/activesupport-6.1.1/lib/active_support/fork_tracker.rb:26:in `fork'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application.rb:180:in `serve'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application.rb:145:in `block in run'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application.rb:139:in `loop'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application.rb:139:in `run'
	from /Users/ankur/.rvm/gems/ruby-3.0.0/gems/spring-2.1.1/lib/spring/application/boot.rb:19:in `<top (required)>'
	from <internal:/Users/ankur/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from <internal:/Users/ankur/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from -e:1:in `<main>'

Bug: `rails generate madmin:views` does not generate navigation and layout views

Steps to repro:

  1. bundle add madmin
  2. rails g madmin:install
  3. rails generate madmin:views

Expected:

Generator creates:

app/views/layouts/madmin/application.html.erb
app/views/madmin/application/_form.html.erb
app/views/madmin/application/_javascript.html.erb
app/views/madmin/application/_navigation.html.erb
app/views/madmin/application/edit.html.erb
app/views/madmin/application/index.html.erb
app/views/madmin/application/new.html.erb
app/views/madmin/application/show.html.erb

Actual:

- app/views/layouts/madmin/application.html.erb
app/views/madmin/application/_form.html.erb
- app/views/madmin/application/_javascript.html.erb
- app/views/madmin/application/_navigation.html.erb
app/views/madmin/application/edit.html.erb
app/views/madmin/application/index.html.erb
app/views/madmin/application/new.html.erb
app/views/madmin/application/show.html.erb

Running:
rails g madmin:views:navigation and rails g madmin:views:layout explicitly, generates navigation and layout views respectively.

Running madmin:install error relation "blazer_uploads" does not exist

/usr/local/bundle/gems/activerecord-6.1.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:19:in `exec': PG::UndefinedTable: ERROR: relation "blazer_uploads" does not exist (ActiveRecord::StatementInvalid)
LINE 8: WHERE a.attrelid = '"blazer_uploads"'::regclass

Updating Zeitwerk to `2.5.0.beta2` breaks loading of resources

After updating Zeitwerk to 2.5.0.beta2, Rails expects Madmin resources to be namespaced.

For example, madmin/resources/customer_resource is expected to define Resources::CustomerResource.

The actual error on build:

Zeitwerk::NameError: expected file /app/app/madmin/resources/customer_resource.rb to define constant Resources::CustomerResource, but didn't

Show * for required fields

You can access the validators on a model by doing:

User._validators

We can use this to render an asterisk or note for required fields.

User._validators[:email].any? { |v| v.is_a? ActiveModel::Validations::PresenceValidator }

Todos:

  • required? method on Field
  • Add * Required label when rendering fields

Graceful error display on non-existent belongs_to association.

When adding a reference to a model, I personally make an initial migration allowing null values for the model_id, fill the ids and only then force the null: false on the id column.

However, this causes a NoMethodError on the display_name of a column. It is absolutely reasonable, but it makes the resource completely unavailable in that intermediate stage.

ActionView::Template::Error (undefined method `display_name' for nil:NilClass):
    1: <% object = field.value(record) %>
    2: <%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>

Perhaps it would make sense to somehow handle this use case?

Issue with rails g madmin:install

Have this issue when i run rails g madmin:install command on a fresh rails installation (with MySql DB).

rails g madmin:install
The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
Running via Spring preloader in process 15459
Traceback (most recent call last):
	42: from -e:1:in `<main>'
	41: from /home/dell/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	40: from /home/dell/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	39: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:59:in `load'
	38: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:59:in `load'
	37: from /home/dell/Bureau/madmin/bin/rails:9:in `<main>'
	36: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:34:in `require'
	35: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
	34: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
	33: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	32: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
	31: from /home/dell/.rvm/gems/ruby-2.6.5/gems/bootsnap-1.7.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
	30: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/commands.rb:18:in `<main>'
	29: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/command.rb:46:in `invoke'
	28: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/command/base.rb:69:in `perform'
	27: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor.rb:392:in `dispatch'
	26: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:127:in `invoke_command'
	25: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/command.rb:27:in `run'
	24: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/commands/generate/generate_command.rb:26:in `perform'
	23: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/generators.rb:276:in `invoke'
	22: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/base.rb:485:in `start'
	21: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/group.rb:232:in `dispatch'
	20: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:134:in `invoke_all'
	19: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:134:in `map'
	18: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:134:in `each'
	17: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:134:in `block in invoke_all'
	16: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/invocation.rb:127:in `invoke_command'
	15: from /home/dell/.rvm/gems/ruby-2.6.5/gems/thor-1.1.0/lib/thor/command.rb:27:in `run'
	14: from /home/dell/.rvm/gems/ruby-2.6.5/gems/madmin-1.0.0/lib/generators/madmin/install/install_generator.rb:11:in `eager_load'
	13: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/application.rb:507:in `eager_load!'
	12: from /home/dell/.rvm/gems/ruby-2.6.5/gems/railties-6.0.3.4/lib/rails/autoloaders.rb:30:in `each'
	11: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:393:in `eager_load'
	10: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:393:in `synchronize'
	 9: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:404:in `block in eager_load'
	 8: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:725:in `ls'
	 7: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:725:in `foreach'
	 6: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:733:in `block in ls'
	 5: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:409:in `block (2 levels) in eager_load'
	 4: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader.rb:409:in `const_get'
	 3: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:26:in `require'
	 2: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:26:in `tap'
	 1: from /home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/kernel.rb:27:in `block in require'
/home/dell/.rvm/gems/ruby-2.6.5/gems/zeitwerk-2.4.2/lib/zeitwerk/loader/callbacks.rb:18:in `on_file_autoloaded': expected file /home/dell/Bureau/madmin/app/madmin/resources/action_text/rich_text_resource.rb to define constant ActionText::RichTextResource, but didn't (Zeitwerk::NameError)

Solution:

To resolv this issue, you have to read this link about Zeitwerk
https://stackoverflow.com/questions/57277351/rails-6-zeitwerknameerror-doesnt-load-class-from-module

Native FriendlyID Support

I don't like friendly_id overriding the find method, so it would be awesome if Madmin could detect the FriendlyId module on the model and automatically use Resource.friendly.find instead of the normal Resource.find

This is purely a "nice to have", not something we should really prioritize.

Webpacker::Manifest::MissingEntryError in Madmin::Dashboard#show

Brand new Rails app w/ a few models generated

rails new {name} -T --database=postgresql

Ran through the docs for installation

rails s in one tab, ./bin/webpack-dev-server in another.

Unfortunately, I'm one of those old geezer Rails devs who's behind the times on Webpacker things.

Apologies if I'm missing something simple πŸ˜…

image
image

Generator for copying views

We really like how Administrate does this and it's a nice way to go:

rails g madmin:views should copy over app/views from madmin so you can override the templates and add features.

Specific views

rails generate madmin:views:show
 # -> app/views/admin/application/show.html.erb

Specific views for a resource

rails generate madmin:views:show User
 # -> app/views/admin/users/show.html.erb

rails generate madmin:views:edit User
 # -> app/views/admin/users/edit.html.erb
 # -> app/views/admin/users/_form.html.erb

rails generate madmin:views:new User
 # -> app/views/admin/users/new.html.erb
 # -> app/views/admin/users/_form.html.erb

Support ActiveRecord encryption

The encrypted attributes use a _digest column to store the encrypted values. We can ignore these columns, but add attributes to the Madmin resource for the attribute.

Dummy App New User Form Error

I'm receiving this error when trying to see the form for creating a new User.
Screen Shot 2021-09-24 at 12 43 16 AM
I'll dig into it when I have a moment, for now I'm just dropping this issue on the repo to document what was happening. Something being skipped is coming back as an integer so I'll have to investigate why that's the cause. Actually taking a look at the screenshot again, I think the method just needs to be a little more type aware.

about schema_migrations_* / data_schema_migrations_*

ruby: 3.0.0
rails: 6.1.1
madmin: v0.1.1

after rails g madmin:install, madmin generator created following files:

controllers
β”œβ”€β”€ madmin
β”‚Β Β  β”œβ”€β”€ active_record
β”‚Β Β  β”‚Β Β  └── schema_migrations_controller.rb
β”‚Β Β  β”œβ”€β”€ data_migrate
β”‚Β Β  β”‚Β Β  └── data_schema_migrations_controller.rb
β”‚Β Β  └── users_controller.rb

madmin
└── resources
β”œβ”€β”€ active_record
β”‚Β Β  └── schema_migration_resource.rb
β”œβ”€β”€ data_migrate
β”‚Β Β  └── data_schema_migration_resource.rb
└── user_resource.rb

what are the purpose of these schema_migrations_* ? is this on purpose or bug? thanks

Use custom FormBuilder

Hi Chris and team!

In my project I'm using a custom FormBuilder that automatically adds certain Stimulus data- properties around rich_text_areas tags in order to support media attachment:

  def rich_text_area(method, options = {})
    default_opts = {data: {'trix-youtube-plugin-target': 'editor'}}
    merged_opts = default_opts.merge(options)
    @template.content_tag :div, data: {controller: 'trix-youtube-plugin'} do
      @template.content_tag :div, data: {'trix-youtube-plugin-target': 'wrapper'} do
        super(method, merged_opts)
      end
    end
  end

Is there any easy way to reuse this form builder? Of course, I could just generate all the views and switch it myself but I didn't want to go that far (at least not yet).

Use `draw :madmin` for routes

Rails 6.1 includes draw for separating routes. It would be nice to use this for storing all the admin routes in a separate file.

For backwards compatibility, we could check the Rails version and fallback to inserting into the main routes file. Although I don't see much need to support Rails 6.0 or earlier.

ERROR: relation "action_mailbox_inbound_emails" does not exist

: ERROR: relation "action_mailbox_inbound_emails" does not exist (PG::UndefinedTable) LINE 8: WHERE a.attrelid = '"action_mailbox_inbound_emails

Does the gem assume action_mailbox installed by default? This gets fixed after I run

rails action_mailbox:install

Scope Filters

Registering scopes in a Resource can enable scope buttons for quick filtering on the index page.

Example

class BlogPostResource < Madmin::Resource
  scope :published
  scope :draft
end

This would add All, Published and Draft buttons to the top of the index.
Clicking on one would add ?scope=draft to the URL.
The index action would add the scope with matching name.

def index
  # Verify allowed scope name
  scope_name = BlogPostResource.scopes.include?(params[:scope]) ? params[:scope] : :all

  # Scope the resources
  @resources = @resources.send(scope_name)
end

Seeds quietly failing

The seeds file has been quietly failing for a bit I think. I'm drawing up a commit to fix this right now. Going to cherry-pick it out of my current branch and open a PR here.

Feature request: Add support for batch editing

There are times where I need to edit dozens or even hundreds of items at the same time. For example, updating a specific column to true for many instances at a time. It would be amazing if madmin supported this.

key not found :jsonb

From the GoRails slack:

trying to test Madmin on a new JSP template and getting this error after performing the madmin install + restart

image

Zeitwerk::NameError in Madmin::DashboardController#show

Hi team,

I ran the install command on a semi changed jump start pro app. It builds out all the resources and seems to get the routes right however i get the error 'Zeitwerk::NameError in Madmin::DashboardController#show' saying it expected 'to define constant Resources::AccountInvitationResource, but didn't' - account invitation is the example here. Adding 'Resources::' to the class name just throws another error.

Any clues? A little wary of running install again unless there are other options to pass in?

Screen Shot 2021-04-18 at 6 53 28 pm

Support virtual attributes for has_secure_password

My User model uses has_secure_password. This means it has a virtual attribute password and it should be editable in madmin.

Currently, madmin doesn't understand this:
Couldn't find attribute or association 'password' with type '' on User model

Document Authentication

πŸ‘‹

Something that would be good to document is how applications can implement a level of authentication for Madmin. Out of the box we produce an unauthenticated view, this is obviously not production ready.

Because authentication can be different from app to app, we'd prefer to not roll our own and to instead allow Developers to use their existing models instead.

What we'd like to see is:

  • Examples on how to use basic auth with Madmin
  • Examples of how to use it with Devise / Example of how to override the Madmin Controller to plugin any other system

Let us know if there's more information needed!

README enhancement

Hey guys, first contribution / issue here.
While being a newbie, I had some trouble setting up the app / foreman initially due to lack of knowledge Re redis / sidekiq

I would love to add the below notes to the bottom of ReadMe to help noobs like me carry on with your template which I absolutely love as well as GoRails content, that helps me immensely!


Enabling Admin Panel

App uses madmin gem, so you need to run the madmin generator:

rails g madmin:install

This will install Madmin and generate resources for each of the models it finds.

Redis set up

On OSX
brew update
brew install redis
brew services start redis
Ubuntu
sudo apt-get install redis-server

Build Error with Skypack

Looks like something is breaking with Skypack when trying to build stimulus-flatpickr.
Screen Shot 2021-10-03 at 11 14 57 AM
I originally encountered this error when trying to pull down updates to my fork so I could rebase #89 on top of it. I pulled down a fresh clone and confirmed that this is happening even on fresh installs.

It's breaking all Stimulus behavior, so I'll try and get this figured out so that Stimulus behavior isn't broken.

paper_trail version resource is built incorrectly

When running madmin with no changes the following resource is created for a paper_trail version resource:

class PaperTrail::VersionResource < Madmin::Resource
  # Attributes
  attribute :id, form: false
  attribute :{ :null=>false }
  attribute :event
  attribute :whodunnit
  attribute :object
  attribute :created_at, form: false
  attribute :transaction_id
  attribute :object_changes

  # Associations
  attribute :version_associations
  attribute :item
end

and gives the following errors when run:

Processing by Madmin::DashboardController#show as HTML
Completed 500 Internal Server Error in 167ms (ActiveRecord: 23.3ms | Allocations: 60500)



SyntaxError - syntax error, unexpected {, expecting literal content or terminator or tSTRING_DBEG or tSTRING_DVAR
  attribute :{:null=>false}
             ^:
  app/madmin/resources/paper_trail/version_resource.rb:4:in `'

field_for_type Argument Error Doesn’t Read as Expected

I’m opening this issue in relation to some of the trouble I ran into with #98. After more investigation I believe the error that can be raised in field_for_type isn’t functioning quite as expected. Particularly the names being inserted into the error string don’t make sense. I believe it’s catching an error not expected by the original implementation.

I’ll continue to investigate and document my findings here when I get #98 finished.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.