diff --git a/lib/rails_graph/commands/build_graph.rb b/lib/rails_graph/commands/build_graph.rb index 14f73e1..014aada 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,6 +38,7 @@ def self.call(configuration:) def call setup_generic_nodes + RailsGraph::Commands::Builders::GemsBuilder.enrich(graph: graph) RailsGraph::Commands::Builders::Databases.enrich(graph: graph) if configuration.databases? RailsGraph::Commands::Builders::Models.enrich(inspector: inspector, graph: graph, classes: classes, configuration: configuration) 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..b0ce0c5 --- /dev/null +++ b/lib/rails_graph/commands/builders/gems_builder.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module RailsGraph + module Commands + module Builders + class GemsBuilder + def self.enrich(graph:) + 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/exporters/cypher.rb b/lib/rails_graph/exporters/cypher.rb index 7b76c0e..c835bb9 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