lib/generators/madmin/resource/resource_generator.rb in madmin-0.1.0 vs lib/generators/madmin/resource/resource_generator.rb in madmin-0.1.1

- old
+ new

@@ -1,76 +1,112 @@ -require "rails/generators/named_base" -require "madmin/generator_helpers" - module Madmin module Generators class ResourceGenerator < Rails::Generators::NamedBase - ATTRIBUTE_TYPE_MAPPING = { - boolean: "Field::CheckBox", - date: "Field::DateTime", - datetime: "Field::DateTime", - enum: "Field::Text", - float: "Field::Number", - integer: "Field::Number", - time: "Field::DateTime", - text: "Field::TextArea", - string: "Field::Text", - } + include Madmin::GeneratorHelpers source_root File.expand_path("../templates", __FILE__) - def create_resource_file - template( - "resource.rb.erb", - Rails.root.join("app/madmin/resources/#{file_name}.rb"), - ) + def eager_load + Rails.application.eager_load! end - private + def generate_resource + template "resource.rb", "app/madmin/resources/#{file_path}_resource.rb" + end - def attributes - klass.attribute_types.map { |name, attr| - # Skip attributes related to associations - next if ignored_attributes.include?(name) + def generate_controller + destination = Rails.root.join("app/controllers/madmin/#{file_path.pluralize}_controller.rb") + template("controller.rb", destination) + end - [name, madmin_type_for_column(attr.type)] - }.compact + def generate_route + if route_namespace_exists? + route "resources :#{plural_name}", namespace: class_path, indentation: 4, sentinel: /namespace :madmin do\s*\n/m + else + route "resources :#{plural_name}", namespace: [:madmin] + class_path + end end + private + def associations - klass.reflections.map do |name, association| - if association.polymorphic? - [name, "Field::Polymorphic"] - elsif association.belongs_to? - [name, "Field::BelongsTo"] - elsif association.has_one? - [name, "Field::HasOne"] - else - [name, "Field::HasMany"] + model.reflections.reject { |name, association| + # Hide these special associations + name.starts_with?("rich_text") || + name.ends_with?("_attachment") || + name.ends_with?("_attachments") || + name.ends_with?("_blob") || + name.ends_with?("_blobs") + }.keys + end + + def attributes + model.attribute_names + virtual_attributes - redundant_attributes + end + + def virtual_attributes + virtual = [] + + # Add virtual attributes for ActionText and ActiveStorage + model.reflections.each do |name, association| + if name.starts_with?("rich_text") + virtual << name.split("rich_text_").last + elsif name.ends_with?("_attachment") + virtual << name.split("_attachment").first + elsif name.ends_with?("_attachments") + virtual << name.split("_attachments").first end end + + virtual end - def ignored_attributes - attrs = [] + def redundant_attributes + redundant = [] - klass.reflections.map do |name, association| - if association.polymorphic? - attrs += [association.foreign_key, association.foreign_type] - elsif association.belongs_to? - attrs += [association.foreign_key] + model.reflections.each do |name, association| + if association.has_one? + next + elsif association.collection? + next + elsif association.polymorphic? + redundant << "#{name}_id" + redundant << "#{name}_type" + elsif name.starts_with?("rich_text") + redundant << name + else # belongs to + redundant << "#{name}_id" end end - attrs + redundant end - def klass - @klass ||= Object.const_get(class_name) + def model + @model ||= class_name.constantize end - def madmin_type_for_column(column_type) - ATTRIBUTE_TYPE_MAPPING[column_type] + def formatted_options_for_attribute(name) + options = options_for_attribute(name) + return if options.blank? + + ", " + options.map { |key, value| + "#{key}: #{value}" + }.join(", ") + end + + def options_for_attribute(name) + if %w[id created_at updated_at].include?(name) + {form: false} + + # Attributes without a database column + elsif !model.column_names.include?(name) + {index: false} + + # Counter cache columns are typically not editable + elsif name.ends_with?("_count") + {form: false} + end end end end end