Class: Woods::Extractors::ServiceExtractor

Inherits:
Object
  • Object
show all
Includes:
SharedDependencyScanner, SharedUtilityMethods
Defined in:
lib/woods/extractors/service_extractor.rb

Overview

ServiceExtractor handles service object extraction.

Service objects often contain the most important business logic. Unlike models (which are discovered via ActiveRecord), services are discovered by scanning conventional directories.

We extract:

  • Public interface (call/perform/execute methods)

  • Dependencies (what models/services/jobs they use)

  • Error classes (custom exceptions defined)

  • Input/output patterns

Examples:

extractor = ServiceExtractor.new
units = extractor.extract_all
checkout = units.find { |u| u.identifier == "CheckoutService" }

Constant Summary collapse

SERVICE_DIRECTORIES =

Directories to scan for service objects

%w[
  app/services
  app/interactors
  app/operations
  app/commands
  app/use_cases
].freeze

Constants included from SharedDependencyScanner

Woods::Extractors::SharedDependencyScanner::FORM_ACTION_HELPER, Woods::Extractors::SharedDependencyScanner::ROUTE_HELPER_PATTERN

Instance Method Summary collapse

Methods included from SharedDependencyScanner

#extract_constantize_targets, #scan_common_dependencies, #scan_form_dependencies, #scan_job_dependencies, #scan_mailer_dependencies, #scan_model_dependencies, #scan_navigation_dependencies, #scan_service_dependencies

Methods included from SharedUtilityMethods

#app_source?, #condition_label, #count_loc, #detect_entry_points, #extract_action_filter_actions, #extract_callback_conditions, #extract_class_methods, #extract_class_name, #extract_custom_errors, #extract_initialize_params, #extract_namespace, #extract_parent_class, #extract_public_methods, #resolve_source_location, #skip_file?

Constructor Details

#initializeServiceExtractor

Returns a new instance of ServiceExtractor.



38
39
40
41
# File 'lib/woods/extractors/service_extractor.rb', line 38

def initialize
  @directories = SERVICE_DIRECTORIES.map { |d| Rails.root.join(d) }
                                    .select(&:directory?)
end

Instance Method Details

#extract_allArray<ExtractedUnit>

Extract all service objects

Returns:



46
47
48
49
50
51
52
# File 'lib/woods/extractors/service_extractor.rb', line 46

def extract_all
  @directories.flat_map do |dir|
    Dir[dir.join('**/*.rb')].filter_map do |file|
      extract_service_file(file)
    end
  end
end

#extract_service_file(file_path) ⇒ ExtractedUnit?

Extract a single service file

Parameters:

  • file_path (String)

    Path to the service file

Returns:

  • (ExtractedUnit, nil)

    The extracted unit or nil if not a service



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/woods/extractors/service_extractor.rb', line 58

def extract_service_file(file_path)
  source = File.read(file_path)
  class_name = extract_class_name(file_path, source, '(?:services|interactors|operations|commands|use_cases)')

  return nil unless class_name
  return nil if skip_file?(source)

  unit = ExtractedUnit.new(
    type: :service,
    identifier: class_name,
    file_path: file_path
  )

  unit.namespace = extract_namespace(class_name)
  unit.source_code = annotate_source(source, class_name)
  unit. = (source, class_name, file_path)
  unit.dependencies = extract_dependencies(source)

  unit
rescue StandardError => e
  Rails.logger.error("Failed to extract service #{file_path}: #{e.message}")
  nil
end