diff --git a/lib/rails_graph/commands/build_graph.rb b/lib/rails_graph/commands/build_graph.rb index 14f73e1..db1c1a9 100644 --- a/lib/rails_graph/commands/build_graph.rb +++ b/lib/rails_graph/commands/build_graph.rb @@ -7,6 +7,7 @@ require_relative "../graph/nodes/abstract_model" require_relative "../graph/nodes/column" require_relative "../graph/nodes/database" +require_relative "../graph/nodes/gem" require_relative "../graph/nodes/model" require_relative "../graph/nodes/pack" require_relative "../graph/nodes/table" @@ -25,6 +26,7 @@ require_relative "./builders/models" require_relative "./builders/packs" require_relative "./builders/databases" +require_relative "./builders/gems_builder" module RailsGraph module Commands @@ -36,10 +38,11 @@ def self.call(configuration:) def call setup_generic_nodes - RailsGraph::Commands::Builders::Databases.enrich(graph: graph) if configuration.databases? + RailsGraph::Commands::Builders::GemsBuilder.enrich(graph: graph, configuration: configuration) + RailsGraph::Commands::Builders::Databases.enrich(graph: graph, configuration: configuration) RailsGraph::Commands::Builders::Models.enrich(inspector: inspector, graph: graph, classes: classes, configuration: configuration) - RailsGraph::Commands::Builders::Packs.enrich(graph: graph) if configuration.include_packwerk? + RailsGraph::Commands::Builders::Packs.enrich(graph: graph, configuration: configuration) graph end diff --git a/lib/rails_graph/commands/builders/databases.rb b/lib/rails_graph/commands/builders/databases.rb index 80f83d8..43ff874 100644 --- a/lib/rails_graph/commands/builders/databases.rb +++ b/lib/rails_graph/commands/builders/databases.rb @@ -4,7 +4,9 @@ module RailsGraph module Commands module Builders class Databases - def self.enrich(graph:) + def self.enrich(graph:, configuration:) + return unless configuration.databases? + new(graph: graph).enrich graph diff --git a/lib/rails_graph/commands/builders/gems_builder.rb b/lib/rails_graph/commands/builders/gems_builder.rb new file mode 100644 index 0000000..57ada5d --- /dev/null +++ b/lib/rails_graph/commands/builders/gems_builder.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module RailsGraph + module Commands + module Builders + class GemsBuilder + def self.enrich(graph:, configuration:) + return unless configuration.gems? + + new(graph: graph).enrich + + graph + end + + def enrich + build_gems_nodes + + gems.each do |specifications| + source_node = graph.node("gem_#{specifications.name}") + + # build_authored_by_relationships(specifications, source_node) + build_depends_on_gem_relationships(specifications, source_node) + end + end + + private + + attr_reader :graph + + def initialize(graph:) + @graph = graph + end + + def build_gems_nodes + gems.each do |gem| + node = RailsGraph::Graph::Nodes::Gem.new(gem) + graph.add_node(node) + end + end + + # def build_authored_by_relationships(specifications); end + + def build_depends_on_gem_relationships(specifications, source_node) + specifications.dependencies.each do |dependency| + target_node = graph.node("gem_#{dependency.name}") + add_depends_on_gem_relationship(source_node, target_node, dependency) + end + end + + def add_depends_on_gem_relationship(source_node, target_node, dependency) + relationship = RailsGraph::Graph::Relationship.new( + source: source_node, + target: target_node, + label: "DependsOnGem", + name: "depends_on_gem", + properties: { type: dependency.type, requirement: dependency.requirement.to_s } + ) + + graph.add_relationship(relationship) + end + + def gems + @gems ||= Gem::Specification.all + end + end + end + end +end diff --git a/lib/rails_graph/commands/builders/packs.rb b/lib/rails_graph/commands/builders/packs.rb index 22be84c..9285a58 100644 --- a/lib/rails_graph/commands/builders/packs.rb +++ b/lib/rails_graph/commands/builders/packs.rb @@ -4,7 +4,9 @@ module RailsGraph module Commands module Builders class Packs - def self.enrich(graph:) + def self.enrich(graph:, configuration:) + return unless configuration.include_packwerk? + new(graph: graph).enrich graph diff --git a/lib/rails_graph/configuration.rb b/lib/rails_graph/configuration.rb index e8d71c0..ac0c9de 100644 --- a/lib/rails_graph/configuration.rb +++ b/lib/rails_graph/configuration.rb @@ -3,7 +3,7 @@ module RailsGraph class Configuration attr_reader :include_classes - attr_writer :columns, :inheritance, :include_packwerk, :databases + attr_writer :columns, :inheritance, :include_packwerk, :databases, :gems def initialize @include_classes = [] @@ -11,6 +11,7 @@ def initialize @databases = true @columns = false @inheritance = true + @gems = false end def include_classes=(include_classes) @@ -32,5 +33,9 @@ def include_packwerk? def databases? @databases end + + def gems? + @gems + end end end diff --git a/lib/rails_graph/exporters/cypher.rb b/lib/rails_graph/exporters/cypher.rb index 7b76c0e..31a214d 100644 --- a/lib/rails_graph/exporters/cypher.rb +++ b/lib/rails_graph/exporters/cypher.rb @@ -32,7 +32,7 @@ def self.format_properties(item) item.properties.each do |k, v| formatted_value = case v - when String, Symbol then "'#{v}'" + when String, Symbol then "'#{escape_quotes(v)}'" when nil then "null" else v.to_s end @@ -57,6 +57,12 @@ def self.create_relationship_cypher(relationship) "CREATE (#{source_ref})-[:#{relationship.label} {#{format_properties(relationship)}}]->(#{target_ref})" end + + def self.escape_quotes(value) + return value if value.is_a? Symbol + + value.gsub("'") { "\\'" } + end end end end diff --git a/lib/rails_graph/graph/nodes/gem.rb b/lib/rails_graph/graph/nodes/gem.rb new file mode 100644 index 0000000..fe5a56e --- /dev/null +++ b/lib/rails_graph/graph/nodes/gem.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require_relative "../node" + +module RailsGraph + module Graph + module Nodes + class Gem < Node + attr_reader :specifications + + def initialize(specifications) + @specifications = specifications + + super(labels: "Gem", name: specifications.name, properties: build_properties) + end + + def identifier + "gem_#{name}" + end + + private + + def build_properties + { + description: specifications.description, + summary: specifications.summary, + homepage: specifications.homepage, + version: specifications.version.to_s, + licenses: specifications.licenses + } + end + end + end + end +end