factory_trace 2.0.0 → 3.0.1
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 +4 -4
- data/exe/factory_trace +11 -4
- data/lib/factory_trace/configuration.rb +2 -1
- data/lib/factory_trace/fixture_tracker.rb +25 -0
- data/lib/factory_trace/monkey_patches/dsl.rb +2 -2
- data/lib/factory_trace/monkey_patches/monkey_patches.rb +0 -1
- data/lib/factory_trace/preprocessors/extract_defined.rb +1 -1
- data/lib/factory_trace/preprocessors/extract_defined_fixtures.rb +58 -0
- data/lib/factory_trace/readers/trace_reader.rb +13 -7
- data/lib/factory_trace/version.rb +1 -1
- data/lib/factory_trace/writers/report_writer.rb +19 -6
- data/lib/factory_trace/writers/trace_writer.rb +2 -2
- data/lib/factory_trace/writers/writer.rb +4 -0
- data/lib/factory_trace.rb +43 -5
- metadata +19 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9150973821d1c8196bf9dc7ba0ccbaa9c5e81cc397fed0613f2b0e0086104ec7
|
|
4
|
+
data.tar.gz: cbbf862a0a4aa1d3a436329dd4e2d2f94c2c700a1f5e0a07da5b08395f4fa89e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c12b5e6ee4de364e37c99a01c363931c35f6d5f32af1a9525e9ed990c11209c38473f1a9809eb2bc980237cb069cbffb017f3c44c50bb33f5b41b90645f46037
|
|
7
|
+
data.tar.gz: 245cc1ad516ff4a706f6f32fa6c96f2bf8d55b4ec032e9ebe10ca835f02bd06b501f94a5b5b9c742c2962d56c6c3e3eddfe55fa42e3e6d95376b545e2693ed6f
|
data/exe/factory_trace
CHANGED
|
@@ -7,9 +7,16 @@ require "factory_trace"
|
|
|
7
7
|
fail "You should pass at least one file with traced information.\nYou can generate it using only_trace mode." if ARGV.empty?
|
|
8
8
|
|
|
9
9
|
config = FactoryTrace.configuration
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
writer = FactoryTrace::Writers::ReportWriter.new(config.out, config)
|
|
11
|
+
code = 0
|
|
12
|
+
|
|
13
|
+
%i[factory_bot fixtures].each do |kind|
|
|
14
|
+
result = FactoryTrace::Readers::TraceReader.read_from_files(kind, *ARGV)
|
|
15
|
+
reports = FactoryTrace::Processors::FindUnused.call(result[:defined], result[:used])
|
|
16
|
+
if reports.any? { |report| report[:code] == :unused && !report.key?(:value) }
|
|
17
|
+
code = 1
|
|
18
|
+
end
|
|
19
|
+
writer.write(reports, kind: kind)
|
|
20
|
+
end
|
|
13
21
|
|
|
14
|
-
FactoryTrace::Writers::ReportWriter.new(config.out, config).write(reports)
|
|
15
22
|
exit(code)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
22
|
+
FactoryTrace.factory_bot_register.register_trait(FactoryBot::Trait.new(name, Helpers::Caller.location, &block))
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -7,7 +7,7 @@ module FactoryTrace
|
|
|
7
7
|
def self.call
|
|
8
8
|
collection = FactoryTrace::Structures::Collection.new
|
|
9
9
|
|
|
10
|
-
|
|
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
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
module FactoryTrace
|
|
4
4
|
module Readers
|
|
5
5
|
class TraceReader
|
|
6
|
-
attr_reader :io, :configuration
|
|
6
|
+
attr_reader :io, :configuration, :kind
|
|
7
7
|
|
|
8
8
|
# Read the data from files and merge it
|
|
9
9
|
#
|
|
10
10
|
# @return [Hash<Symbol, FactoryTrace::Structures::Collection>]
|
|
11
|
-
def self.read_from_files(*file_names, configuration: Configuration.new)
|
|
11
|
+
def self.read_from_files(kind, *file_names, configuration: Configuration.new)
|
|
12
12
|
result = {defined: FactoryTrace::Structures::Collection.new, used: FactoryTrace::Structures::Collection.new}
|
|
13
13
|
|
|
14
14
|
file_names.each do |file_name|
|
|
15
15
|
File.open(file_name, "r") do |file|
|
|
16
|
-
data = new(file, configuration: configuration).read
|
|
16
|
+
data = new(kind, file, configuration: configuration).read
|
|
17
17
|
|
|
18
18
|
[:defined, :used].each do |key|
|
|
19
|
-
result[key].merge!(data[key])
|
|
19
|
+
result[key].merge!(data[key]) unless data.empty?
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -24,7 +24,8 @@ module FactoryTrace
|
|
|
24
24
|
result
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def initialize(io, configuration: Configuration.new)
|
|
27
|
+
def initialize(kind, io, configuration: Configuration.new)
|
|
28
|
+
@kind = kind
|
|
28
29
|
@io = io
|
|
29
30
|
@configuration = configuration
|
|
30
31
|
end
|
|
@@ -35,9 +36,14 @@ module FactoryTrace
|
|
|
35
36
|
def read
|
|
36
37
|
hash = JSON.parse(io.read)
|
|
37
38
|
|
|
39
|
+
defined = hash["defined"][kind.to_s]
|
|
40
|
+
used = hash["used"][kind.to_s]
|
|
41
|
+
|
|
42
|
+
return {} if defined.nil? || used.nil?
|
|
43
|
+
|
|
38
44
|
{
|
|
39
|
-
defined: parse_collection(
|
|
40
|
-
used: parse_collection(
|
|
45
|
+
defined: parse_collection(defined),
|
|
46
|
+
used: parse_collection(used)
|
|
41
47
|
}
|
|
42
48
|
end
|
|
43
49
|
|
|
@@ -16,11 +16,14 @@ module FactoryTrace
|
|
|
16
16
|
}.freeze
|
|
17
17
|
|
|
18
18
|
# @param [Array<Hash>] results
|
|
19
|
-
|
|
19
|
+
# @param [Symbol] kind - :factory (default) or :fixture
|
|
20
|
+
def write(results, kind:)
|
|
21
|
+
io.puts("") unless file?
|
|
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
|
-
|
|
34
|
+
# @param [Symbol] kind - :factory_bot or :fixtures
|
|
35
|
+
def convert(result, total_color:, kind:)
|
|
32
36
|
if result[:value]
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module FactoryTrace
|
|
4
4
|
module Writers
|
|
5
5
|
class TraceWriter < Writer
|
|
6
|
-
# @param [
|
|
7
|
-
# @param [
|
|
6
|
+
# @param [Hash] defined
|
|
7
|
+
# @param [Hash] used
|
|
8
8
|
def write(defined, used)
|
|
9
9
|
io.puts(JSON.pretty_generate(defined: defined.to_h, used: used.to_h))
|
|
10
10
|
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,30 @@ 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)
|
|
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)
|
|
63
|
+
writer = Writers::TraceWriter.new(configuration.out, configuration)
|
|
64
|
+
writer.write(mix(defined_fixtures, defined), mix(used_fixtures, used))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def mix(fixtures, factory_bot)
|
|
69
|
+
{}.tap do |result|
|
|
70
|
+
result[:fixtures] = fixtures.to_h if fixtures?
|
|
71
|
+
result[:factory_bot] = factory_bot.to_h if factory_bot?
|
|
60
72
|
end
|
|
61
73
|
end
|
|
62
74
|
|
|
@@ -68,6 +80,10 @@ module FactoryTrace
|
|
|
68
80
|
@configuration ||= Configuration.new
|
|
69
81
|
end
|
|
70
82
|
|
|
83
|
+
def factory_bot_register
|
|
84
|
+
@factory_bot_register ||= (FactoryBot::VERSION >= "5.1.0") ? FactoryBot::Internal : FactoryBot
|
|
85
|
+
end
|
|
86
|
+
|
|
71
87
|
private
|
|
72
88
|
|
|
73
89
|
def used
|
|
@@ -82,7 +98,29 @@ module FactoryTrace
|
|
|
82
98
|
@tracker ||= Tracker.new
|
|
83
99
|
end
|
|
84
100
|
|
|
101
|
+
def fixture_tracker
|
|
102
|
+
@fixture_tracker ||= FixtureTracker.new
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def defined_fixtures
|
|
106
|
+
@defined_fixtures ||= Preprocessors::ExtractDefinedFixtures.call(configuration.fixture_path)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def used_fixtures
|
|
110
|
+
@used_fixtures ||= Preprocessors::ExtractUsed.call(fixture_tracker.storage)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def factory_bot?
|
|
114
|
+
defined?(FactoryBot)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def fixtures?
|
|
118
|
+
defined?(ActiveRecord::FixtureSet)
|
|
119
|
+
end
|
|
120
|
+
|
|
85
121
|
def trace_definitions!
|
|
122
|
+
return unless factory_bot?
|
|
123
|
+
|
|
86
124
|
FactoryBot::Factory.prepend(FactoryTrace::MonkeyPatches::Factory)
|
|
87
125
|
FactoryBot::Trait.prepend(FactoryTrace::MonkeyPatches::Trait)
|
|
88
126
|
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:
|
|
4
|
+
version: 3.0.1
|
|
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-
|
|
11
|
+
date: 2026-03-20 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: :
|
|
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
|