zeisler/active_mocker

View on GitHub
lib/active_mocker/mock_creator.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
require "active_mocker/inspectable"

module ActiveMocker
  class MockCreator
    using ActiveMocker::Inspectable
    ENABLED_PARTIALS_DEFAULT = [
      :mock_build_version,
      :modules_constants,
      :class_methods,
      :attributes,
      :recreate_class_method_calls,
      :defined_methods,
      :scopes,
      :associations,
    ].freeze

    def initialize(file:,
                   file_out:,
                   schema_scrapper:,
                   template_creator: nil,
                   class_introspector: nil,
                   enabled_partials: nil,
                   klasses_to_be_mocked:,
                   mock_append_name:,
                   active_record_model:,
                   active_record_base_klass: ActiveRecord::Base)
      @file                     = file
      @file_out                 = file_out
      @schema_scrapper          = schema_scrapper
      @template_creator         = template_creator || template_creator_default(file_out)
      @class_introspector       = class_introspector || class_introspector_default
      @enabled_partials         = enabled_partials || ENABLED_PARTIALS_DEFAULT
      @klasses_to_be_mocked     = klasses_to_be_mocked
      @active_record_base_klass = active_record_base_klass
      @mock_append_name         = mock_append_name
      @active_record_model      = active_record_model
      @errors                   = []
      @completed                = false
    end

    def create
      verify_class
      render { file_out.close } if errors.empty?
      self
    end

    def render
      template_creator.render
    rescue => e
      raise e unless error_already_collected?(e)
    ensure
      yield
      @completed = true
    end

    def completed?
      @completed
    end

    attr_reader :errors

    private

    attr_reader :file,
                :file_out,
                :schema_scrapper,
                :template_creator,
                :class_introspector,
                :enabled_partials,
                :klasses_to_be_mocked,
                :active_record_base_klass,
                :mock_append_name,
                :active_record_model

    def error_already_collected?(e)
      errors.any? { |eo| eo.original_error == e }
    end

    # -- Defaults -- #
    def template_creator_default(file_out)
      TemplateCreator.new(file_out:     file_out,
                          erb_template: File.new(File.join(File.dirname(__FILE__), "mock_template.erb"), "r"),
                          binding:      binding,
                          post_process: lambda do |str|
                            ruby_code = DissociatedIntrospection::RubyCode.build_from_source(str,
                                                                                             parse_with_comments: true)
                            DissociatedIntrospection::WrapInModules.new(ruby_code: ruby_code)
                              .call(modules: nested_modules)
                              .source_from_ast.gsub(/end\n/, "end\n\n")
                          end)
    end

    def class_introspector_default
      DissociatedIntrospection::Inspection.new(file: file)
    end

    # -- END defaults -- #

    def parent_class_inspector
      @parent_class_inspector ||= ParentClass.new(parsed_source:        class_introspector.parsed_source,
                                                  klasses_to_be_mocked: klasses_to_be_mocked,
                                                  mock_append_name:     mock_append_name).call
    end

    def verify_parent_class
      errors << parent_class_inspector.error if parent_class_inspector.error
    end

    def verify_class
      verify_parent_class
    end

    public

    def partials
      OpenStruct.new(enabled_partials.each_with_object({}) do |p, hash|
        begin
          file = File.new(File.join(File.dirname(__FILE__), "mock_template/_#{p}.erb"))
          extend(ActiveMocker::MockCreator.const_get(p.to_s.camelize))
          hash[p] = ERB.new(file.read, nil, "-", "_sub#{p}").result(binding)
        rescue => e
          errors << ErrorObject.new(class_name:     class_name,
                                    original_error: e, type: :generation,
                                    level:          :error,
                                    message:        e.message)
          errors << ErrorObject.new(class_name:     class_name,
                                    original_error: e,
                                    type:           :erb,
                                    level:          :debug,
                                    message:        "Erb template: #{p} failed.\n#{file.path}")
          raise e
        end
      end)
    end

    def class_name
      @class_name ||= class_introspector.parsed_source.class_name.split("::").last
    end

    def nested_modules
      @nested_modules ||= begin
        class_introspector.parsed_source.module_nesting.join("::")
      end
    end

    def primary_key
      @primary_key ||= ActiveRecordSchemaScrapper::Attribute.new(name: "id", type: :integer)
    end
  end
end