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

#consolidate_dependencies, #extract_constantize_targets, #scan_common_dependencies, #scan_form_dependencies, #scan_job_dependencies, #scan_mailer_dependencies, #scan_model_dependencies, #scan_navigation_dependencies, #scan_service_dependencies, #strip_line_comment, #strip_ruby_line_comments

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, #find_files_in_directories, #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
# File 'lib/woods/extractors/service_extractor.rb', line 46

def extract_all
  find_files_in_directories(@directories).filter_map do |file|
    extract_service_file(file)
  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



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

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