Class: Woods::Extractors::SerializerExtractor

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

Overview

SerializerExtractor handles extraction of serializers, blueprinters, and decorators.

Serializers define the API contract — what data is exposed and how it’s shaped. They often wrap models, select attributes, and define associations that map directly to JSON responses. Understanding these is critical for API-aware code analysis.

Supports:

  • ActiveModel::Serializer (AMS)

  • Blueprinter::Base

  • Draper::Decorator

Examples:

extractor = SerializerExtractor.new
units = extractor.extract_all
user_serializer = units.find { |u| u.identifier == "UserSerializer" }

Constant Summary collapse

SERIALIZER_DIRECTORIES =

Directories to scan for serializer-like files

%w[
  app/serializers
  app/blueprinters
  app/decorators
].freeze
BASE_CLASSES =

Known base classes for runtime discovery

{
  'ActiveModel::Serializer' => :ams,
  'Blueprinter::Base' => :blueprinter,
  'Draper::Decorator' => :draper
}.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_custom_errors, #extract_initialize_params, #extract_namespace, #extract_parent_class, #extract_public_methods, #resolve_source_location, #skip_file?

Constructor Details

#initializeSerializerExtractor

Returns a new instance of SerializerExtractor.



43
44
45
46
# File 'lib/woods/extractors/serializer_extractor.rb', line 43

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

Instance Method Details

#extract_allArray<ExtractedUnit>

Extract all serializers, blueprinters, and decorators in the application

Returns:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/woods/extractors/serializer_extractor.rb', line 51

def extract_all
  units = []

  # File-based discovery (catches everything in known directories)
  @directories.each do |dir|
    Dir[dir.join('**/*.rb')].each do |file|
      unit = extract_serializer_file(file)
      units << unit if unit
    end
  end

  # Class-based discovery for loaded gems
  seen = units.to_set(&:identifier)
  BASE_CLASSES.each_key do |base_class_name|
    base_class = begin
      base_class_name.constantize
    rescue NameError
      nil
    end
    next unless base_class

    base_class.descendants.each do |klass|
      next if klass.name.nil?
      next if seen.include?(klass.name)

      unit = extract_serializer_class(klass, base_class_name)
      if unit
        units << unit
        seen << unit.identifier
      end
    end
  end

  units.compact
end

#extract_serializer_file(file_path) ⇒ ExtractedUnit?

Extract a serializer from its file

Parameters:

  • file_path (String)

    Path to the serializer file

Returns:

  • (ExtractedUnit, nil)

    The extracted unit, or nil if not a serializer



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/woods/extractors/serializer_extractor.rb', line 91

def extract_serializer_file(file_path)
  source = File.read(file_path)
  class_name = extract_class_name(file_path, source)

  return nil unless class_name
  return nil unless serializer_file?(source)

  unit = ExtractedUnit.new(
    type: :serializer,
    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)
  unit.dependencies = extract_dependencies(source)

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