class CmAdmin::Model

def actions(only: [], except: [])

Insert into actions according to config block
def actions(only: [], except: [])
  acts = CmAdmin::DEFAULT_ACTIONS.keys
  acts = acts & (Array.new << only).flatten if only.present?
  acts = acts - (Array.new << except).flatten if except.present?
  acts.each do |act|
    action_defaults = CmAdmin::DEFAULT_ACTIONS[act]
    @available_actions << CmAdmin::Models::Action.new(name: act.to_s, verb: action_defaults[:verb], path: action_defaults[:path])
  end
  @actions_set = true
end

def all_actions

def all_actions
  @all_actions || []
end

def custom_controller_action(action_name, params)

def custom_controller_action(action_name, params)
  current_action = CmAdmin::Models::Action.find_by(self, name: action_name.to_s)
  if current_action
    @current_action = current_action
    @ar_object = @ar_model.name.classify.constantize.find(params[:id])
    if @current_action.child_records
      child_records = @ar_object.send(@current_action.child_records)
      @associated_model = CmAdmin::Model.find_by(name: @ar_model.reflect_on_association(@current_action.child_records).klass.name)
      if child_records.is_a? ActiveRecord::Relation
        @associated_ar_object = filter_by(params, child_records)
      else
        @associated_ar_object = child_records
      end
      return @ar_object, @associated_model, @associated_ar_object
    end
    return @ar_object
  end
end

def define_controller

If model is User, controller will be UsersController
Controller defined for each model
def define_controller
  klass = Class.new(CmAdmin::ApplicationController) do
    include Pundit::Authorization
    rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
    $available_actions.each do |action|
      define_method action.name.to_sym do
        # controller_name & action_name from ActionController
        @model = CmAdmin::Model.find_by(name: controller_name.classify)
        @model.params = params
        @action = CmAdmin::Models::Action.find_by(@model, name: action_name)
        @model.current_action = @action
        @ar_object = @model.try(@action.parent || action_name, params)
        @ar_object, @associated_model, @associated_ar_object = @model.custom_controller_action(action_name, params.permit!) if !@ar_object.present? && params[:id].present?
        authorize controller_name.classify.constantize, policy_class: "CmAdmin::#{controller_name.classify}Policy".constantize if defined? "CmAdmin::#{controller_name.classify}Policy".constantize
        aar_model = request.url.split('/')[-2].classify.constantize  if params[:aar_id]
        @associated_ar_object = aar_model.find(params[:aar_id]) if params[:aar_id]
        nested_tables = @model.available_fields[:new].except(:fields).keys
        nested_tables += @model.available_fields[:edit].except(:fields).keys
        @reflections = @model.ar_model.reflect_on_all_associations
        nested_tables.each do |table_name|
          reflection = @reflections.select {|x| x if x.name == table_name}.first
          if reflection.macro == :has_many
            @ar_object.send(table_name).build if action_name == "new" || action_name == "edit"
          else
            @ar_object.send(('build_' + table_name.to_s).to_sym) if action_name == "new"
          end
        end
        respond_to do |format|
          if %w(show index new edit).include?(action_name)
            if request.xhr? && action_name.eql?('index')
              format.html { render partial: '/cm_admin/main/table' }
            else
              format.html { render '/cm_admin/main/'+action_name }
            end
          elsif %w(create update destroy).include?(action_name)
            if %w(create update).include?(action_name)
              redirect_url = CmAdmin::Engine.mount_path + "/#{@model.name.underscore.pluralize}/#{@ar_object.id}"
            else
              redirect_url = CmAdmin::Engine.mount_path + "/#{@model.name.underscore.pluralize}"
            end
            if @ar_object.save
              format.html { redirect_to  redirect_url, notice: "#{action_name.titleize} #{@ar_object.class.name.downcase} is successful" }
            else
              format.html { render '/cm_admin/main/new', notice: "#{action_name.titleize} #{@ar_object.class.name.downcase} is unsuccessful" }
            end
          elsif action.action_type == :custom
            if action.child_records
              format.html { render action.layout }
            elsif action.display_type == :page
              data = @action.parent == "index" ? @ar_object.data : @ar_object
              format.html { render action.partial }
            else
              ar_object = @action.code_block.call(@ar_object)
              if ar_object.errors.empty?
                redirect_url = @model.current_action.redirection_url || @action.redirection_url || request.referrer || "/cm_admin/#{@model.ar_model.table_name}/#{@ar_object.id}"
                format.html { redirect_to redirect_url, notice: "#{@action.name.titleize} is successful" }
              else
                error_messages = ar_object.errors.full_messages.map{|error_message| "<li>#{error_message}</li>"}.join
                format.html { redirect_to request.referrer, alert: "<b>#{@action.name.titleize} is unsuccessful</b><br /><ul>#{error_messages}</ul>" }
              end
            end
          elsif action.layout.present?
            if request.xhr? && action.partial.present?
              format.html { render partial: action.partial }
            else
              format.html { render action.layout }
            end
          end
        end
      end
    end
    private
    def user_not_authorized
      flash[:alert] = 'You are not authorized to perform this action.'
      redirect_to CmAdmin::Engine.mount_path + '/access-denied'
    end
  end if $available_actions.present?
  CmAdmin.const_set "#{@name}Controller", klass
end

def filter_params(params)

def filter_params(params)
  # OPTIMIZE: Need to check if we can permit the filter_params in a better way
  date_columns = @filters.select{|x| x.filter_type.eql?(:date)}.map(&:db_column_name)
  range_columns = @filters.select{|x| x.filter_type.eql?(:range)}.map(&:db_column_name)
  single_select_columns = @filters.select{|x| x.filter_type.eql?(:single_select)}.map(&:db_column_name)
  multi_select_columns = @filters.select{|x| x.filter_type.eql?(:multi_select)}.map{|x| Hash["#{x.db_column_name}", []]}
  params.require(:filters).permit(:search, date: date_columns, range: range_columns, single_select: single_select_columns, multi_select: multi_select_columns) if params[:filters]
end

def find_by(search_hash)

def find_by(search_hash)
  CmAdmin.config.cm_admin_models.find { |x| x.name == search_hash[:name] }
end

def initialize(entity, &block)

def initialize(entity, &block)
  @name = entity.name
  @ar_model = entity
  @is_visible_on_sidebar = true
  @icon_name = 'fa fa-th-large'
  @available_actions ||= []
  @current_action = nil
  @available_tabs ||= []
  @available_fields ||= {index: [], show: [], edit: {fields: []}, new: {fields: []}}
  @params = nil
  @filters ||= []
  instance_eval(&block) if block_given?
  actions unless @actions_set
  $available_actions = @available_actions.dup
  self.class.all_actions.push(@available_actions)
  define_controller
end

def set_icon(name)

def set_icon(name)
  @icon_name = name
end

def user_not_authorized

def user_not_authorized
  flash[:alert] = 'You are not authorized to perform this action.'
  redirect_to CmAdmin::Engine.mount_path + '/access-denied'
end

def visible_on_sidebar(visible_option)

def visible_on_sidebar(visible_option)
  @is_visible_on_sidebar = visible_option
end