madmin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +102 -0
  4. data/Rakefile +32 -0
  5. data/app/assets/config/madmin_manifest.js +2 -0
  6. data/app/assets/javascripts/madmin/application.js +15 -0
  7. data/app/assets/javascripts/madmin/dashboard.js +2 -0
  8. data/app/assets/javascripts/madmin/resources.js +36 -0
  9. data/app/assets/stylesheets/madmin/application.css +22 -0
  10. data/app/assets/stylesheets/madmin/dashboard.css +4 -0
  11. data/app/assets/stylesheets/madmin/resources.css +4 -0
  12. data/app/controllers/madmin/application_controller.rb +16 -0
  13. data/app/controllers/madmin/base_controller.rb +16 -0
  14. data/app/controllers/madmin/dashboard_controller.rb +8 -0
  15. data/app/controllers/madmin/resources_controller.rb +97 -0
  16. data/app/decorators/madmin/resource_decorator.rb +16 -0
  17. data/app/helpers/madmin/application_helper.rb +15 -0
  18. data/app/helpers/madmin/fields/polymorphic_helper.rb +25 -0
  19. data/app/jobs/madmin/application_job.rb +4 -0
  20. data/app/mailers/madmin/application_mailer.rb +6 -0
  21. data/app/models/madmin/application_record.rb +5 -0
  22. data/app/views/application/_navigation.html.erb +17 -0
  23. data/app/views/layouts/madmin/application.html.erb +30 -0
  24. data/app/views/madmin/dashboard/index.html.erb +6 -0
  25. data/app/views/madmin/fields/belongs_to/_form.html.erb +14 -0
  26. data/app/views/madmin/fields/belongs_to/_index.html.erb +7 -0
  27. data/app/views/madmin/fields/belongs_to/_show.html.erb +8 -0
  28. data/app/views/madmin/fields/check_box/_form.html.erb +4 -0
  29. data/app/views/madmin/fields/check_box/_index.html.erb +1 -0
  30. data/app/views/madmin/fields/check_box/_show.html.erb +8 -0
  31. data/app/views/madmin/fields/email/_form.html.erb +4 -0
  32. data/app/views/madmin/fields/email/_index.html.erb +1 -0
  33. data/app/views/madmin/fields/email/_show.html.erb +8 -0
  34. data/app/views/madmin/fields/has_many/_form.html.erb +0 -0
  35. data/app/views/madmin/fields/has_many/_show.html.erb +15 -0
  36. data/app/views/madmin/fields/has_one/_form.html.erb +0 -0
  37. data/app/views/madmin/fields/has_one/_show.html.erb +12 -0
  38. data/app/views/madmin/fields/number/_form.html.erb +4 -0
  39. data/app/views/madmin/fields/number/_index.html.erb +1 -0
  40. data/app/views/madmin/fields/number/_show.html.erb +8 -0
  41. data/app/views/madmin/fields/password/_form.html.erb +4 -0
  42. data/app/views/madmin/fields/password/_index.html.erb +1 -0
  43. data/app/views/madmin/fields/password/_show.html.erb +8 -0
  44. data/app/views/madmin/fields/polymorphic/_form.html.erb +32 -0
  45. data/app/views/madmin/fields/polymorphic/_index.html.erb +1 -0
  46. data/app/views/madmin/fields/polymorphic/_show.html.erb +14 -0
  47. data/app/views/madmin/fields/select/_form.html.erb +4 -0
  48. data/app/views/madmin/fields/select/_index.html.erb +1 -0
  49. data/app/views/madmin/fields/select/_show.html.erb +8 -0
  50. data/app/views/madmin/fields/text/_form.html.erb +4 -0
  51. data/app/views/madmin/fields/text/_index.html.erb +1 -0
  52. data/app/views/madmin/fields/text/_show.html.erb +8 -0
  53. data/app/views/madmin/fields/text_area/_form.html.erb +4 -0
  54. data/app/views/madmin/fields/text_area/_index.html.erb +1 -0
  55. data/app/views/madmin/fields/text_area/_show.html.erb +8 -0
  56. data/app/views/madmin/resources/_form.html.erb +15 -0
  57. data/app/views/madmin/resources/_scopes.html.erb +10 -0
  58. data/app/views/madmin/resources/edit.html.erb +2 -0
  59. data/app/views/madmin/resources/index.html.erb +13 -0
  60. data/app/views/madmin/resources/index/_content.html.erb +33 -0
  61. data/app/views/madmin/resources/new.html.erb +2 -0
  62. data/app/views/madmin/resources/show.html.erb +10 -0
  63. data/config/routes.rb +11 -0
  64. data/lib/generators/madmin/controller/USAGE +8 -0
  65. data/lib/generators/madmin/controller/controller_generator.rb +10 -0
  66. data/lib/generators/madmin/install/install_generator.rb +31 -0
  67. data/lib/generators/madmin/page/USAGE +8 -0
  68. data/lib/generators/madmin/page/page_generator.rb +20 -0
  69. data/lib/generators/madmin/page/templates/template.html.erb +2 -0
  70. data/lib/generators/madmin/page/templates/template.rb.erb +10 -0
  71. data/lib/generators/madmin/resource/resource_generator.rb +76 -0
  72. data/lib/generators/madmin/resource/templates/resource.rb.erb +11 -0
  73. data/lib/generators/madmin/views/views_generator.rb +15 -0
  74. data/lib/madmin.rb +30 -0
  75. data/lib/madmin/engine.rb +9 -0
  76. data/lib/madmin/field.rb +64 -0
  77. data/lib/madmin/field/associatable.rb +58 -0
  78. data/lib/madmin/field/belongs_to.rb +9 -0
  79. data/lib/madmin/field/check_box.rb +8 -0
  80. data/lib/madmin/field/date_time.rb +8 -0
  81. data/lib/madmin/field/email.rb +8 -0
  82. data/lib/madmin/field/has_many.rb +9 -0
  83. data/lib/madmin/field/has_one.rb +9 -0
  84. data/lib/madmin/field/number.rb +8 -0
  85. data/lib/madmin/field/password.rb +8 -0
  86. data/lib/madmin/field/polymorphic.rb +57 -0
  87. data/lib/madmin/field/select.rb +13 -0
  88. data/lib/madmin/field/text.rb +8 -0
  89. data/lib/madmin/field/text_area.rb +8 -0
  90. data/lib/madmin/generator_helpers.rb +13 -0
  91. data/lib/madmin/resourceable.rb +72 -0
  92. data/lib/madmin/resourceable/class_methods.rb +152 -0
  93. data/lib/madmin/resources.rb +13 -0
  94. data/lib/madmin/version.rb +3 -0
  95. data/lib/tasks/madmin_tasks.rake +4 -0
  96. metadata +197 -0
@@ -0,0 +1,10 @@
1
+ class Madmin::ControllerGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("../../../../../", __FILE__)
3
+
4
+ def copy_controller
5
+ copy_file(
6
+ "app/controllers/madmin/#{file_name}_controller.rb",
7
+ "app/controllers/madmin/#{file_name}_controller.rb"
8
+ )
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ require "rails/generators/base"
2
+ require "madmin/generator_helpers"
3
+
4
+ module Madmin
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Madmin::GeneratorHelpers
8
+
9
+ def generate_resources
10
+ resources.each do |model|
11
+ call_generator "madmin:resource", model.to_s
12
+ end
13
+ end
14
+
15
+ def install_route
16
+ inject_into_file(
17
+ Rails.root.join("config/routes.rb"),
18
+ after: "Rails.application.routes.draw do\n"
19
+ ) { " namespace :madmin do\n end\n mount Madmin::Engine => \"/madmin\"\n" }
20
+ end
21
+
22
+ private
23
+
24
+ def resources
25
+ Rails.application.eager_load! unless Rails.application.config.cache_classes
26
+
27
+ ActiveRecord::Base.descendants.reject(&:abstract_class?)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate custom_controller Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,20 @@
1
+ class Madmin::PageGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def copy_template
5
+ template(
6
+ "template.rb.erb",
7
+ Rails.root.join("app/controllers/madmin/#{file_name}_controller.rb"),
8
+ )
9
+
10
+ template(
11
+ "template.html.erb",
12
+ Rails.root.join("app/views/madmin/#{file_name}/index.html.erb"),
13
+ )
14
+
15
+ inject_into_file(
16
+ Rails.root.join("config/routes.rb"),
17
+ after: "namespace :madmin do\n"
18
+ ) { " get \"#{file_name}\", to: \"#{file_name}#index\"\n" }
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ <h1><%= name %></h1>
2
+ <p>Find me in apps/views/madmin/<%= file_name %>/index.erb</p>
@@ -0,0 +1,10 @@
1
+ class Madmin::<%= name %>Controller < Madmin::ApplicationController
2
+ def index
3
+ end
4
+
5
+ private
6
+
7
+ def in_menu?
8
+ true
9
+ end
10
+ end
@@ -0,0 +1,76 @@
1
+ require "rails/generators/named_base"
2
+ require "madmin/generator_helpers"
3
+
4
+ module Madmin
5
+ module Generators
6
+ class ResourceGenerator < Rails::Generators::NamedBase
7
+ ATTRIBUTE_TYPE_MAPPING = {
8
+ boolean: "Field::CheckBox",
9
+ date: "Field::DateTime",
10
+ datetime: "Field::DateTime",
11
+ enum: "Field::Text",
12
+ float: "Field::Number",
13
+ integer: "Field::Number",
14
+ time: "Field::DateTime",
15
+ text: "Field::TextArea",
16
+ string: "Field::Text",
17
+ }
18
+
19
+ source_root File.expand_path("../templates", __FILE__)
20
+
21
+ def create_resource_file
22
+ template(
23
+ "resource.rb.erb",
24
+ Rails.root.join("app/madmin/resources/#{file_name}.rb"),
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ def attributes
31
+ klass.attribute_types.map { |name, attr|
32
+ # Skip attributes related to associations
33
+ next if ignored_attributes.include?(name)
34
+
35
+ [name, madmin_type_for_column(attr.type)]
36
+ }.compact
37
+ end
38
+
39
+ def associations
40
+ klass.reflections.map do |name, association|
41
+ if association.polymorphic?
42
+ [name, "Field::Polymorphic"]
43
+ elsif association.belongs_to?
44
+ [name, "Field::BelongsTo"]
45
+ elsif association.has_one?
46
+ [name, "Field::HasOne"]
47
+ else
48
+ [name, "Field::HasMany"]
49
+ end
50
+ end
51
+ end
52
+
53
+ def ignored_attributes
54
+ attrs = []
55
+
56
+ klass.reflections.map do |name, association|
57
+ if association.polymorphic?
58
+ attrs += [association.foreign_key, association.foreign_type]
59
+ elsif association.belongs_to?
60
+ attrs += [association.foreign_key]
61
+ end
62
+ end
63
+
64
+ attrs
65
+ end
66
+
67
+ def klass
68
+ @klass ||= Object.const_get(class_name)
69
+ end
70
+
71
+ def madmin_type_for_column(column_type)
72
+ ATTRIBUTE_TYPE_MAPPING[column_type]
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ module Madmin
2
+ class Resources
3
+ class <%= class_name %>
4
+ include Madmin::Resourceable
5
+
6
+ <% (attributes + associations ).each do |name, type| -%>
7
+ field :<%= name %>, Madmin::<%= type %>
8
+ <% end -%>
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ require "rails/generators/base"
2
+
3
+ module Madmin
4
+ module Generators
5
+ class ViewsGenerator < Rails::Generators::Base
6
+ desc("Copies views for customizing Madmin's views")
7
+
8
+ source_root File.expand_path("../../../../..", __FILE__)
9
+
10
+ def copy_views
11
+ directory "app/views/madmin", "app/views/madmin"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require "madmin/engine"
2
+ require "madmin/resourceable"
3
+ require "madmin/resources"
4
+
5
+ module Madmin
6
+ class NoResourcesError < StandardError; end
7
+ class ResourceNotFoundError < StandardError; end
8
+ class WrongArgumentError < StandardError; end
9
+ class UndefinedScopeError < StandardError; end
10
+ class ScopeWithArgumentsError < StandardError; end
11
+
12
+ autoload :Field, "madmin/field"
13
+ autoload :Resourceable, "madmin/resourceable"
14
+
15
+ class Field
16
+ autoload :Associatable, "madmin/field/associatable"
17
+ autoload :BelongsTo, "madmin/field/belongs_to"
18
+ autoload :CheckBox, "madmin/field/check_box"
19
+ autoload :DateTime, "madmin/field/date_time"
20
+ autoload :Email, "madmin/field/email"
21
+ autoload :HasMany, "madmin/field/has_many"
22
+ autoload :HasOne, "madmin/field/has_one"
23
+ autoload :Number, "madmin/field/number"
24
+ autoload :Password, "madmin/field/password"
25
+ autoload :Polymorphic, "madmin/field/polymorphic"
26
+ autoload :Select, "madmin/field/select"
27
+ autoload :TextArea, "madmin/field/text_area"
28
+ autoload :Text, "madmin/field/text"
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Madmin
4
+
5
+ config.to_prepare do
6
+ Dir.glob("#{Rails.root}/app/madmin/**/*.rb").each { |r| require_dependency(r) }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ module Madmin
2
+ class Field
3
+ attr_reader :form
4
+ attr_reader :index
5
+ attr_reader :key
6
+ attr_reader :label
7
+ attr_reader :model
8
+ attr_reader :options
9
+ attr_reader :show
10
+ attr_reader :type
11
+
12
+ def initialize(args)
13
+ @key = args[:key]
14
+ @options = args
15
+ @model = args[:model]
16
+ @label = option_or_default(:label, label_from_attribute(key))
17
+ @form = option_or_default(:form, false)
18
+ @index = option_or_default(:index, false)
19
+ @show = option_or_default(:show, true)
20
+ end
21
+
22
+ ##
23
+ # Returns an array of key(s) to use in strong
24
+ # params for the resources controller.
25
+ def strong_params_keys
26
+ [key]
27
+ end
28
+
29
+ ##
30
+ # Returns the partial path for the field type
31
+ def to_partial_path
32
+ "madmin/fields/#{self.class.to_s.split("Madmin::Field::").last.underscore}"
33
+ end
34
+
35
+ ##
36
+ # Returns the value for a given resource
37
+ def value_for(resource)
38
+ resource.send(key)
39
+ end
40
+
41
+ private
42
+
43
+ ##
44
+ # This method takes an attribute and converts it
45
+ # into a human readable format for form labels.
46
+ def label_from_attribute(attribute)
47
+ attribute.to_s.humanize
48
+ end
49
+
50
+ ##
51
+ # This method verifies options are present
52
+ # (as a hash).
53
+ def options?
54
+ options.is_a?(Hash)
55
+ end
56
+
57
+ ##
58
+ # This method extracts the many option checks
59
+ # performed when assigning field options.
60
+ def option_or_default(key, default)
61
+ options? ? options.fetch(key, default) : default
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,58 @@
1
+ module Madmin
2
+ class Field
3
+ module Associatable
4
+ class << self
5
+ def included(_base)
6
+ attr_reader :association_class
7
+ attr_reader :association_display_value
8
+ attr_reader :association_foreign_key
9
+ attr_reader :association_scope
10
+ end
11
+ end
12
+
13
+ def initialize(args)
14
+ super(args)
15
+ implement_association!
16
+ end
17
+
18
+ def association_collection
19
+ association_class.send(association_scope)
20
+ end
21
+
22
+ def association_id_for(resource)
23
+ value_for(resource).id
24
+ end
25
+
26
+ def association_id_or_blank_for(resource)
27
+ value_for(resource).try(:id)
28
+ end
29
+
30
+ def association_param
31
+ ActiveModel::Naming.param_key(association_class)
32
+ end
33
+
34
+ def association_slug
35
+ Object.const_get("::Madmin::Resources::#{association_class}").new.slug
36
+ end
37
+
38
+ def association_value_for(resource)
39
+ value_for(resource).send(association_display_value)
40
+ end
41
+
42
+ def strong_params_keys
43
+ [association_foreign_key]
44
+ end
45
+
46
+ private
47
+
48
+ def implement_association!
49
+ association = model.reflect_on_all_associations.find { |assc| assc.name == key }
50
+
51
+ @association_class = association.klass
52
+ @association_display_value = option_or_default(:display_value, :name)
53
+ @association_foreign_key = association.foreign_key
54
+ @association_scope = :all
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a :belongs_to relationship within a Rails model.
5
+ class BelongsTo < Madmin::Field
6
+ include Madmin::Field::Associatable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a checkbox (boolean) value.
5
+ class CheckBox < Madmin::Field
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a datetime value.
5
+ class DateTime < Madmin::Field
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents an email value.
5
+ class Email < Madmin::Field
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a :has_many relationship within a Rails model.
5
+ class HasMany < Madmin::Field
6
+ include Madmin::Field::Associatable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a :has_one relationship within a Rails model.
5
+ class HasOne < Madmin::Field
6
+ include Madmin::Field::Associatable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a number value.
5
+ class Number < Madmin::Field
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Madmin
2
+ class Field
3
+ ##
4
+ # This field represents a password value.
5
+ class Password < Madmin::Field
6
+ end
7
+ end
8
+ end