Class: MOSAIK::Extractors::Evolution
- Inherits:
-
MOSAIK::Extractor
- Object
- MOSAIK::Extractor
- MOSAIK::Extractors::Evolution
- Defined in:
- lib/mosaik/extractors/evolution.rb
Overview
Evolutionary (logical and contributor) coupling extraction
Instance Attribute Summary
Attributes inherited from MOSAIK::Extractor
Instance Method Summary collapse
Methods inherited from MOSAIK::Extractor
Constructor Details
This class inherits a constructor from MOSAIK::Extractor
Instance Method Details
#call ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/mosaik/extractors/evolution.rb', line 15 def call # Open the git repository git = Git.open([:directory], log: ::Logger.new(File::NULL)) # Fetch commits, limited to the last N commits commits = git.log([:limit]) # Limit commits to the load paths commits = commits.path(MOSAIK.configuration.load_paths) # Limit commits to a specific date commits = commits.since([:since]) if [:since] info "Analyzing #{commits.size} commits" # Create a co-change matrix for each pair of constants (logical coupling) co_changes = Hash.new { |h, k| h[k] = Hash.new(0) } # Create a contributor matrix for each constant (contributor coupling) contributors = Hash.new { |h, k| h[k] = Set.new } # Iterate over each commit commits.each do |commit| # Get the files for the commit files = commit.diff_parent.stats[:files] # Select only included files files = files .map { |file, _| File.join([:directory], file) } .select { |file| file.in? MOSAIK.configuration.files } # Resolve file paths to class name constants = files.map { |file| resolver.resolve_file!(file) } debug "Commit #{commit.sha} (#{constants.size} constants: #{constants.join(', ')})" # Increment the co-change number between each pair of constants (logical coupling) constants .permutation(2) .each { |(a, b)| co_changes[a][b] += 1 } # Add the commit author to the contributors for each constant (contributor coupling) constants.each { |constant| contributors[constant] << commit..email } end if [:couplings].include?(:logical) info "Constructing logical coupling graph..." # For each non-zero pair of constants in the co-change matrix (logical coupling) co_changes.each do |a, row| row.each do |b, co_change| # Skip if there are no co-changes next if co_change.zero? graph.find_or_add_vertex(a) graph.find_or_add_vertex(b) # Add an edge from the constant to the receiver edge = graph.find_or_add_edge(a, b, type: "logical") # Set or increment weight on edge edge.attributes[:weight] = edge.attributes.fetch(:weight, 0) + co_change end end end return unless [:couplings].include?(:contributor) info "Constructing contributor coupling graph..." # For each non-empty pair of constants in the contributor matrix (contributor coupling) contributors.keys.permutation(2).each do |(a, b)| next if contributors[a].empty? || contributors[b].empty? # Calculate coupling (cardinality of the intersection of sets of developers) coupling = (contributors[a] & contributors[b]).size # Skip if the coupling is one (no shared contributors) next if coupling == 1 graph.find_or_add_vertex(a) graph.find_or_add_vertex(b) # Add an edge from the constant to the receiver edge = graph.find_or_add_edge(a, b, type: "contributor") # Set weight on edge edge.attributes[:weight] = edge.attributes.fetch(:weight, 0) + coupling end end |
#validate ⇒ Object
11 12 13 |
# File 'lib/mosaik/extractors/evolution.rb', line 11 def validate raise OptionError, "directory is not a git repository" unless File.directory?(File.join([:directory], ".git")) end |