wearefine/fae

View on GitHub
lib/generators/fae/base_generator.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module Fae
  class BaseGenerator < Rails::Generators::NamedBase
    source_root ::File.expand_path('../templates', __FILE__)
    argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
    class_option :namespace, type: :string, default: 'admin', desc: 'Sets the namespace of the generator'
    class_option :template, type: :string, default: 'slim', desc: 'Sets the template engine of the generator'
    class_option :polymorphic, type: :boolean, default: false, desc: 'Makes the model and scaffolding polymorphic. parent-model is ignored if passed.'

    Rails::Generators::GeneratedAttribute::DEFAULT_TYPES += ['image', 'file', 'seo_set']

    @@attributes_flat = []
    @@attribute_names = []
    @@association_names = []
    @@attachments = []
    @@graphql_attributes = []
    @@has_position = false
    @@display_field = ''

    def check_template_support
      supported_templates = ['slim']
      raise "Fae::UnsupportedTemplate: the template engine you defined isn't supported" unless supported_templates.include?(options.template)
    end

    def set_globals
      if attributes.present?
        attributes.each do |arg|
          # prevent these from being in attributes_flat or attribute_names as they are not real model generator field options
          if is_attachment(arg)
            @@attachments << arg
          else
            @@attributes_flat << "#{arg.name}:#{arg.type}" + (arg.has_index? ? ":index" : "")
          end

          if options.polymorphic
            @@attributes_flat << "#{polymorphic_name}:references{polymorphic}"
          end

          if is_association(arg)
            @@association_names << arg.name.gsub(/_id$/, '')
          elsif !is_attachment(arg)
            @@attribute_names << arg.name
          end
          @@has_position = true if arg.name === 'position'

          @@graphql_attributes << graphql_object(arg)
        end

        @@attributes_flat = @@attributes_flat.uniq.join(' ')
        @@association_names.uniq!
        @@attribute_names.uniq!
        @@attachments.uniq!
        @@graphql_attributes.uniq!
      end
    end

  private

    ## Generator Methods

    def generate_model
      generate "model #{file_name} #{@@attributes_flat}"
      inject_concern
      inject_display_field_to_model
      inject_model_attachments
      inject_position_scope
    end

    def generate_controller_file
      @attachments = @@attachments
      template "controllers/scaffold_controller.rb", "app/controllers/#{options.namespace}/#{file_name.pluralize}_controller.rb"
    end

    def generate_view_files
      @toggle_attrs = set_toggle_attrs
      @form_attrs = set_form_attrs
      @association_names = @@association_names
      @attachments = @@attachments
      @has_position = @@has_position
      @display_field = @@display_field
      @polymorphic_name = polymorphic_name
      template "views/index.html.#{options.template}", "app/views/#{options.namespace}/#{plural_file_name}/index.html.#{options.template}"
      template "views/_form.html.#{options.template}", "app/views/#{options.namespace}/#{plural_file_name}/_form.html.#{options.template}"
      copy_file "views/new.html.#{options.template}", "app/views/#{options.namespace}/#{plural_file_name}/new.html.#{options.template}"
      copy_file "views/edit.html.#{options.template}", "app/views/#{options.namespace}/#{plural_file_name}/edit.html.#{options.template}"
    end

    def add_route
      inject_into_file "config/routes.rb", after: "namespace :#{options.namespace} do\n", force: true do <<-RUBY
    resources :#{plural_file_name}
RUBY
      end
    end

    ## Helper Methods

    def set_toggle_attrs
      ['active', 'on_stage', 'on_prod'] & @@attribute_names
    end

    def set_form_attrs
      rejected_attrs = ['position', 'on_stage', 'on_prod']
      @@attribute_names.delete_if { |i| rejected_attrs.include?(i)}
    end

    def inject_concern
      inject_into_file "app/models/#{file_name}.rb", after: /(ActiveRecord::Base|ApplicationRecord)\n/ do <<-RUBY
  include Fae::BaseModelConcern\n
RUBY
      end
    end

    def inject_display_field_to_model
      if @@attribute_names.include? 'name'
        @@display_field = 'name'
      elsif @@attribute_names.include? 'title'
        @@display_field = 'title'
      end

      inject_into_file "app/models/#{file_name}.rb", after: "include Fae::BaseModelConcern\n" do <<-RUBY
  def fae_display_field
    #{@@display_field}
  end
RUBY
      end

    end

    def inject_position_scope
      if @@has_position
        inject_into_file "app/models/#{file_name}.rb", after: "include Fae::BaseModelConcern\n" do <<-RUBY
\n  acts_as_list add_new_at: :top
  default_scope { order(:position) }\n
RUBY
        end
      end
    end

    def inject_model_attachments
      return if @@attachments.blank?
      @@attachments.each do |attachment|
        if attachment.type == :image
          inject_into_file "app/models/#{file_name}.rb", after: "include Fae::BaseModelConcern\n" do
            <<-RUBY
  has_fae_image :#{attachment.name}\n
RUBY
          end
        elsif attachment.type == :seo_set
            inject_into_file "app/models/#{file_name}.rb", after: "include Fae::BaseModelConcern\n" do
              <<-RUBY
    has_fae_seo_set :#{attachment.name}\n
  RUBY
            end
        elsif attachment.type == :file
          inject_into_file "app/models/#{file_name}.rb", after: "include Fae::BaseModelConcern\n" do
            <<-RUBY
  has_fae_file :#{attachment.name}\n
RUBY
          end
        end
      end
    end

    def inject_nav_item
      line = "item('#{plural_file_name.humanize.titlecase}', path: #{options.namespace}_#{plural_file_name}_path),\n\s\s\s\s\s\s\s\s"
      inject_into_file 'app/models/concerns/fae/navigation_concern.rb', line, before: '# scaffold inject marker'
    end

    def graphql_object(arg)
      if is_association(arg)
        assoc_name = arg.name.gsub(/_id$/, '')
        assoc_type = "Types::#{assoc_name.classify}Type"
        { attr: assoc_name.to_sym, type: assoc_type }
      else
        { attr: arg.name.to_sym, type: graphql_type(arg.type) }
      end
    end

    def graphql_type(type)
      case type.to_s
      when 'integer'
        'Integer'
      when 'boolean'
        'Boolean'
      when 'image'
        'Types::FaeImageType'
      when 'file'
        'Types::FaeFileType'
      when 'seo_set'
        'Types::FaeSeoSetType'
      else
        'String'
      end
    end

    def generate_graphql_type
      return unless uses_graphql
      @graphql_attributes = @@graphql_attributes
      template "graphql/graphql_type.rb", "app/graphql/types/#{file_name}_type.rb"
    end

    def uses_graphql
      defined?(GraphQL)
    end

    def is_association(arg)
      arg.name.end_with?('_id') || arg.type.to_s == 'references'
    end

    def is_attachment(arg)
      [:image, :file, :seo_set].include?(arg.type)
    end

    def polymorphic_name
      "#{file_name.underscore}able"
    end

    def polymorphic_name
      "#{file_name.underscore}able"
    end

  end
end