factory_trace 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8eb259ef9d3572a820e5fddc7a65d484f29a8d25bfdfa5947a8306cf3c22435f
4
- data.tar.gz: 0b9eeaea9bf6355772994e4ca4d1dd7b62b82d43a00000c4453be067cad21376
3
+ metadata.gz: bd70e8ea6fe8ffb3e815147ea925478d36f4d298b9bbbd5b359b7d5e900d5265
4
+ data.tar.gz: e838d1f5525179c4fba7fa6daba3baf7ab8b23d27e981076069c3a4d33268472
5
5
  SHA512:
6
- metadata.gz: 21c794f7215824581bd33ad9f90235c7c929e71803ace8bb3dfd8d0fe27ab912c052633141c21d23180b5244228b4ba38ef180284c1327da41678e618887d6f5
7
- data.tar.gz: 3ef9526dfba81da0a0316aa4c45caa3b7a404c87e2573a320e719d03cc5f72d0bd041356d00d81a4dadb6fdaf8cacceb0c384a3c053ad096d6bfe3004f512807
6
+ metadata.gz: 0a0b4c5d0b471d570a4ab18567feed01559f861363779e4ba5010e8eb1b1a75a159d3affe54b0742da8c1909095df14cee0ec45eb7fbefcf3e594b8a58140037
7
+ data.tar.gz: 54b1804ed50d4ba0f327e9f18d100ad9c95c39c7ce6a373ed846aa5bbf8c6a9e7f5b42560db21b7922927aa135574a8f6d1acef6b39179b56bc911ba00dba151
@@ -2,7 +2,7 @@
2
2
 
3
3
  module FactoryTrace
4
4
  class Configuration
5
- attr_accessor :path, :enabled, :color, :mode, :trace_definition
5
+ attr_accessor :path, :enabled, :color, :mode, :trace_definition, :fixture_path
6
6
 
7
7
  def initialize
8
8
  @enabled = ENV.key?("FB_TRACE") || ENV.key?("FB_TRACE_FILE")
@@ -10,6 +10,7 @@ module FactoryTrace
10
10
  @color = path.nil?
11
11
  @mode = extract_mode(ENV["FB_TRACE"]) || :full
12
12
  @trace_definition = true
13
+ @fixture_path = "test/fixtures"
13
14
  end
14
15
 
15
16
  def trace_definition?
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FactoryTrace
4
+ class FixtureTracker
5
+ attr_reader :storage
6
+
7
+ def initialize
8
+ @storage = {}
9
+ end
10
+
11
+ def track!
12
+ tracker_storage = @storage
13
+
14
+ ::ActiveRecord::FixtureSet.prepend(Module.new do
15
+ define_method(:[]) do |fixture_name|
16
+ if fixture_name
17
+ tracker_storage[name] ||= Set.new
18
+ tracker_storage[name].add(fixture_name.to_s)
19
+ end
20
+ super(fixture_name)
21
+ end
22
+ end)
23
+ end
24
+ end
25
+ end
@@ -10,7 +10,7 @@ module FactoryTrace
10
10
  proxy = FactoryBot::DefinitionProxy.new(factory.definition)
11
11
  proxy.instance_eval(&block) if block
12
12
 
13
- REGISTER.register_factory(factory)
13
+ FactoryTrace.factory_bot_register.register_factory(factory)
14
14
 
15
15
  proxy.child_factories.each do |(child_name, child_caller_location, child_options, child_block)|
16
16
  parent_factory = child_options.delete(:parent) || name
@@ -19,7 +19,7 @@ module FactoryTrace
19
19
  end
20
20
 
21
21
  def trait(name, &block)
22
- REGISTER.register_trait(FactoryBot::Trait.new(name, Helpers::Caller.location, &block))
22
+ FactoryTrace.factory_bot_register.register_trait(FactoryBot::Trait.new(name, Helpers::Caller.location, &block))
23
23
  end
24
24
  end
25
25
  end
@@ -2,6 +2,5 @@
2
2
 
3
3
  module FactoryTrace
4
4
  module MonkeyPatches
5
- REGISTER = (FactoryBot::VERSION >= "5.1.0") ? FactoryBot::Internal : FactoryBot
6
5
  end
7
6
  end
@@ -7,7 +7,7 @@ module FactoryTrace
7
7
  def self.call
8
8
  collection = FactoryTrace::Structures::Collection.new
9
9
 
10
- MonkeyPatches::REGISTER.traits.each do |trait|
10
+ FactoryTrace.factory_bot_register.traits.each do |trait|
11
11
  collection.add(FactoryTrace::Helpers::Converter.trait(trait))
12
12
  end
13
13
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FactoryTrace
4
+ module Preprocessors
5
+ class ExtractDefinedFixtures
6
+ # Extracts all defined fixture sets and their entries from fixture YAML files
7
+ #
8
+ # @param [String, Array<String>] fixture_path path(s) to directories containing fixture YAML files
9
+ #
10
+ # @return [FactoryTrace::Structures::Collection]
11
+ def self.call(fixture_path)
12
+ collection = FactoryTrace::Structures::Collection.new
13
+
14
+ Array(fixture_path).each do |path|
15
+ Dir.glob(File.join(path, "**", "*.yml")).sort.each do |file|
16
+ fixture_set_name = File.basename(file, ".yml")
17
+ entries = parse_fixture_file(file)
18
+
19
+ traits = entries.map do |entry_name, definition_path|
20
+ FactoryTrace::Structures::Trait.new(entry_name, definition_path: definition_path)
21
+ end
22
+
23
+ factory = FactoryTrace::Structures::Factory.new(
24
+ [fixture_set_name],
25
+ traits,
26
+ definition_path: "#{file}:1"
27
+ )
28
+
29
+ collection.add(factory)
30
+ end
31
+ end
32
+
33
+ collection
34
+ end
35
+
36
+ # Parses a fixture YAML file and returns a hash of entry names to definition paths
37
+ #
38
+ # @param [String] file_path
39
+ #
40
+ # @return [Hash<String, String>]
41
+ def self.parse_fixture_file(file_path)
42
+ entries = {}
43
+ lineno = 1
44
+
45
+ File.foreach(file_path) do |line|
46
+ if (match = line.match(/\A(\w+):(?:\s|$)/))
47
+ entries[match[1]] = "#{file_path}:#{lineno}"
48
+ end
49
+ lineno += 1
50
+ end
51
+
52
+ entries
53
+ end
54
+
55
+ private_class_method :parse_fixture_file
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FactoryTrace
4
- VERSION = "2.0.0"
4
+ VERSION = "3.0.0"
5
5
  end
@@ -16,11 +16,14 @@ module FactoryTrace
16
16
  }.freeze
17
17
 
18
18
  # @param [Array<Hash>] results
19
- def write(results)
19
+ # @param [Symbol] kind - :factory (default) or :fixture
20
+ def write(results, kind:)
21
+ io.puts("")
22
+
20
23
  total_color = (results.any? { |result| result[:code] == :unused && !result.key?(:value) }) ? :red : :green
21
24
 
22
25
  results.each do |result|
23
- io.puts(convert(result, total_color: total_color))
26
+ io.puts(convert(result, total_color: total_color, kind: kind))
24
27
  end
25
28
  end
26
29
 
@@ -28,13 +31,23 @@ module FactoryTrace
28
31
 
29
32
  # @param [Hash<Symbol, Object>] result
30
33
  # @param [Symbol] total_color
31
- def convert(result, total_color:)
34
+ # @param [Symbol] kind - :factory_bot or :fixtures
35
+ def convert(result, total_color:, kind:)
32
36
  if result[:value]
33
- colorize(total_color, "total number of unique #{humanize_code(result[:code])} factories & traits: #{result[:value]}")
37
+ label = (kind == :fixtures) ? "fixture sets & entries" : "factories & traits"
38
+ colorize(total_color, "total number of unique #{humanize_code(result[:code])} #{label}: #{result[:value]}")
34
39
  elsif result[:factory_names] && result[:trait_name]
35
- append_definition_path(result) { "#{humanize_code(result[:code])} trait #{colorize(:blue, result[:trait_name])} of factory #{list(result[:factory_names])}" }
40
+ if kind == :fixtures
41
+ append_definition_path(result) { "#{humanize_code(result[:code])} fixture entry #{colorize(:blue, result[:trait_name])} of fixture set #{list(result[:factory_names])}" }
42
+ else
43
+ append_definition_path(result) { "#{humanize_code(result[:code])} trait #{colorize(:blue, result[:trait_name])} of factory #{list(result[:factory_names])}" }
44
+ end
36
45
  elsif result[:factory_names]
37
- append_definition_path(result) { "#{humanize_code(result[:code])} factory #{list(result[:factory_names])}" }
46
+ if kind == :fixtures
47
+ append_definition_path(result) { "#{humanize_code(result[:code])} fixture set #{list(result[:factory_names])}" }
48
+ else
49
+ append_definition_path(result) { "#{humanize_code(result[:code])} factory #{list(result[:factory_names])}" }
50
+ end
38
51
  else
39
52
  append_definition_path(result) { "#{humanize_code(result[:code])} global trait #{colorize(:blue, result[:trait_name])}" }
40
53
  end
@@ -5,7 +5,14 @@ module FactoryTrace
5
5
  class TraceWriter < Writer
6
6
  # @param [FactoryTrace::Structures::Collection] defined
7
7
  # @param [FactoryTrace::Structures::Collection] used
8
- def write(defined, used)
8
+ def write(defined, used, kind:)
9
+ io.puts("")
10
+ text =
11
+ case kind
12
+ when :factory_bot then "FactoryBot"
13
+ when :fixtures then "Fixtures"
14
+ end
15
+ io.puts("#{text} definitions and usages:")
9
16
  io.puts(JSON.pretty_generate(defined: defined.to_h, used: used.to_h))
10
17
  end
11
18
  end
data/lib/factory_trace.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  # External dependencies
4
4
  require "active_support"
5
- require "factory_bot"
6
5
  require "json"
7
6
  require "set"
8
7
  require "pathname"
@@ -13,6 +12,7 @@ require "factory_trace/helpers/converter"
13
12
  require "factory_trace/helpers/statusable"
14
13
  require "factory_trace/helpers/caller"
15
14
  require "factory_trace/tracker"
15
+ require "factory_trace/fixture_tracker"
16
16
 
17
17
  require "factory_trace/structures/factory"
18
18
  require "factory_trace/structures/trait"
@@ -20,6 +20,7 @@ require "factory_trace/structures/collection"
20
20
 
21
21
  require "factory_trace/preprocessors/extract_defined"
22
22
  require "factory_trace/preprocessors/extract_used"
23
+ require "factory_trace/preprocessors/extract_defined_fixtures"
23
24
 
24
25
  require "factory_trace/processors/find_unused"
25
26
 
@@ -44,19 +45,24 @@ module FactoryTrace
44
45
  return unless configuration.enabled
45
46
  trace_definitions! if configuration.trace_definition?
46
47
 
47
- tracker.track!
48
+ tracker.track! if factory_bot?
49
+ fixture_tracker.track! if fixtures?
48
50
  end
49
51
 
50
52
  def stop
51
53
  return unless configuration.enabled
52
54
 
53
55
  # This is required to exclude parent traits from +defined_traits+
54
- FactoryBot.reload
56
+ FactoryBot.reload if factory_bot?
55
57
 
56
58
  if configuration.mode?(:full)
57
- Writers::ReportWriter.new(configuration.out, configuration).write(Processors::FindUnused.call(defined, used))
59
+ writer = Writers::ReportWriter.new(configuration.out, configuration)
60
+ writer.write(Processors::FindUnused.call(defined, used), kind: :factory_bot) if factory_bot?
61
+ writer.write(Processors::FindUnused.call(defined_fixtures, used_fixtures), kind: :fixtures) if fixtures?
58
62
  elsif configuration.mode?(:trace_only)
59
- Writers::TraceWriter.new(configuration.out, configuration).write(defined, used)
63
+ writer = Writers::TraceWriter.new(configuration.out, configuration)
64
+ writer.write(defined, used, kind: :factory_bot) if factory_bot?
65
+ writer.write(defined_fixtures, used_fixtures, kind: :fixtures) if fixtures?
60
66
  end
61
67
  end
62
68
 
@@ -68,6 +74,10 @@ module FactoryTrace
68
74
  @configuration ||= Configuration.new
69
75
  end
70
76
 
77
+ def factory_bot_register
78
+ @factory_bot_register ||= (FactoryBot::VERSION >= "5.1.0") ? FactoryBot::Internal : FactoryBot
79
+ end
80
+
71
81
  private
72
82
 
73
83
  def used
@@ -82,7 +92,29 @@ module FactoryTrace
82
92
  @tracker ||= Tracker.new
83
93
  end
84
94
 
95
+ def fixture_tracker
96
+ @fixture_tracker ||= FixtureTracker.new
97
+ end
98
+
99
+ def defined_fixtures
100
+ @defined_fixtures ||= Preprocessors::ExtractDefinedFixtures.call(configuration.fixture_path)
101
+ end
102
+
103
+ def used_fixtures
104
+ @used_fixtures ||= Preprocessors::ExtractUsed.call(fixture_tracker.storage)
105
+ end
106
+
107
+ def factory_bot?
108
+ defined?(FactoryBot)
109
+ end
110
+
111
+ def fixtures?
112
+ defined?(ActiveRecord::FixtureSet)
113
+ end
114
+
85
115
  def trace_definitions!
116
+ return unless factory_bot?
117
+
86
118
  FactoryBot::Factory.prepend(FactoryTrace::MonkeyPatches::Factory)
87
119
  FactoryBot::Trait.prepend(FactoryTrace::MonkeyPatches::Trait)
88
120
  FactoryBot::Syntax::Default::DSL.prepend(FactoryTrace::MonkeyPatches::Default::DSL)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_trace
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - djezzzl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-10 00:00:00.000000000 Z
11
+ date: 2026-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: factory_bot
@@ -17,7 +17,21 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
- type: :runtime
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
@@ -91,6 +105,7 @@ files:
91
105
  - exe/factory_trace
92
106
  - lib/factory_trace.rb
93
107
  - lib/factory_trace/configuration.rb
108
+ - lib/factory_trace/fixture_tracker.rb
94
109
  - lib/factory_trace/helpers/caller.rb
95
110
  - lib/factory_trace/helpers/converter.rb
96
111
  - lib/factory_trace/helpers/statusable.rb
@@ -100,6 +115,7 @@ files:
100
115
  - lib/factory_trace/monkey_patches/monkey_patches.rb
101
116
  - lib/factory_trace/monkey_patches/trait.rb
102
117
  - lib/factory_trace/preprocessors/extract_defined.rb
118
+ - lib/factory_trace/preprocessors/extract_defined_fixtures.rb
103
119
  - lib/factory_trace/preprocessors/extract_used.rb
104
120
  - lib/factory_trace/processors/find_unused.rb
105
121
  - lib/factory_trace/readers/trace_reader.rb