Issues (393)

lib/nose/model.rb (3 issues)

Severity
1
# frozen_string_literal: true
2
3 1
require_relative 'model/entity'
4 1
require_relative 'model/fields'
5
6 1
require 'graphviz'
7
8 1
module NoSE
9
  # A conceptual data model of a set of entities
10 1
  class Model
11
    # The subdirectory models are loaded from
12 1
    LOAD_PATH = 'models'
13 1
    include Loader
14
15 1
    attr_reader :entities
16
17 1
    def initialize(&block)
18 185
      @entities = {}
19
20
      # Apply the DSL
21 185
      WorkloadDSL.new(self).instance_eval(&block) if block_given?
22
    end
23
24
    # Compare all entities
25
    # @return [Boolean]
26 1
    def ==(other)
27
      other.is_a?(Model) && @entities = other.entities
28
    end
29 1
    alias eql? ==
30
31
    # Retrieve an entity by name
32
    # @return [Entity]
33 1
    def [](name)
34 3084
      return @entities[name] if @entities.key? name
0 ignored issues
show
Add empty line after guard clause.
Loading history...
35 1
      fail EntityNotFound
36
    end
37
38
    # Add an {Entity} to the workload
39
    # @return [Entity]
40 1
    def add_entity(entity)
41 540
      fail InvalidEntity, 'no primary key defined' if entity.id_field.nil?
0 ignored issues
show
Add empty line after guard clause.
Loading history...
42 540
      @entities[entity.name] = entity
43
    end
44
45
    # Find a field given an +Enumerable+ of identifiers
46
    # @return [Field]
47 1
    def find_field(field)
48 716
      if field.count > 2
49 109
        find_field_chain field
50
      else
51 607
        find_entity_field(*field)
52
      end
53
    end
54
55
    # Output a PNG representation of entities in the model
56 1
    def output(format, filename, include_fields = false)
0 ignored issues
show
Prefer keyword arguments for arguments with a boolean default value; use include_fields: false instead of include_fields = false.
Loading history...
57 1
      graph = GraphViz.new :G, type: :digraph
58 1
      nodes = add_graph_nodes graph, include_fields
59 1
      add_graph_edges graph, nodes
60
61 1
      graph.output(**{ format => filename })
62
    end
63
64 1
    private
65
66
    # Add the nodes (entities) to a GraphViz object
67 1
    def add_graph_nodes(graph, include_fields)
68 1
      Hash[@entities.each_value.map do |entity|
69 1
        label = "#{entity.name}\n"
70 1
        if include_fields
71
          label += entity.fields.each_value.map do |field|
72
            type = field.class.name.sub(/^NoSE::(.*?)(Field)?$/, '\1')
73
            "#{field.name}: #{type}"
74
          end.join("\n")
75
        end
76
77 1
        [entity.name, graph.add_nodes(label)]
78
      end]
79
    end
80
81
    # Add the edges (foreign keys) to a GraphViz object
82 1
    def add_graph_edges(graph, nodes)
83 1
      @entities.each_value do |entity|
84 1
        entity.foreign_keys.each_value do |key|
85
          graph.add_edges nodes[entity.name], nodes[key.entity.name]
86
        end
87
      end
88
    end
89
90
    # Find a field in an entity where the entity may be a string or an object
91 1
    def find_field_chain(field)
92
      # Do a foreign key lookup
93 109
      field = field.dup
94 109
      key_field = @entities[field[0]][field[1]]
95 109
      field[0..1] = key_field ? key_field.entity.name : field[1]
96 109
      find_field field
97
    end
98
99
    # Find a field in an entity where the entity may be a string or an object
100 1
    def find_entity_field(entity, field)
101 607
      entity = entities[entity] if entity.is_a?(String)
102 607
      entity[field]
103
    end
104
  end
105
106
  # Raised when looking up an entity in the workload which does not exist
107 1
  class EntityNotFound < StandardError
108
  end
109
110
  # Raised when attempting to add an invalid entity to a workload
111 1
  class InvalidEntity < StandardError
112
  end
113
end
114