xumlidot 0.1.0
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 +7 -0
- data/bin/xumlidot +39 -0
- data/lib/xumlidot.rb +10 -0
- data/lib/xumlidot/diagram.rb +18 -0
- data/lib/xumlidot/diagram/dot.rb +55 -0
- data/lib/xumlidot/diagram/dot/klass.rb +75 -0
- data/lib/xumlidot/diagram/dot/module.rb +11 -0
- data/lib/xumlidot/diagram/shared/naming.rb +19 -0
- data/lib/xumlidot/diagram/xmi.rb +190 -0
- data/lib/xumlidot/diagram/xmi/argument.rb +34 -0
- data/lib/xumlidot/diagram/xmi/attribute.rb +24 -0
- data/lib/xumlidot/diagram/xmi/constant.rb +16 -0
- data/lib/xumlidot/diagram/xmi/id.rb +39 -0
- data/lib/xumlidot/diagram/xmi/klass.rb +133 -0
- data/lib/xumlidot/diagram/xmi/method.rb +38 -0
- data/lib/xumlidot/diagram/xmi/superklass.rb +18 -0
- data/lib/xumlidot/directory_tree.rb +24 -0
- data/lib/xumlidot/options.rb +104 -0
- data/lib/xumlidot/parsers.rb +14 -0
- data/lib/xumlidot/parsers/args.rb +159 -0
- data/lib/xumlidot/parsers/call.rb +92 -0
- data/lib/xumlidot/parsers/file.rb +15 -0
- data/lib/xumlidot/parsers/generic.rb +106 -0
- data/lib/xumlidot/parsers/klass_definition.rb +76 -0
- data/lib/xumlidot/parsers/method_signature.rb +95 -0
- data/lib/xumlidot/parsers/module_definition.rb +55 -0
- data/lib/xumlidot/parsers/scope.rb +54 -0
- data/lib/xumlidot/parsers/stack.rb +96 -0
- data/lib/xumlidot/types.rb +18 -0
- data/lib/xumlidot/types/argument.rb +61 -0
- data/lib/xumlidot/types/arguments.rb +14 -0
- data/lib/xumlidot/types/attribute.rb +34 -0
- data/lib/xumlidot/types/attributes.rb +11 -0
- data/lib/xumlidot/types/constant.rb +49 -0
- data/lib/xumlidot/types/constants.rb +57 -0
- data/lib/xumlidot/types/inherited_module.rb +24 -0
- data/lib/xumlidot/types/instance_methods.rb +10 -0
- data/lib/xumlidot/types/klass.rb +60 -0
- data/lib/xumlidot/types/klass_definition.rb +61 -0
- data/lib/xumlidot/types/klass_methods.rb +10 -0
- data/lib/xumlidot/types/method.rb +10 -0
- data/lib/xumlidot/types/method_signature.rb +64 -0
- data/lib/xumlidot/types/methods.rb +10 -0
- data/lib/xumlidot/types/module.rb +11 -0
- data/lib/xumlidot/types/module_definition.rb +14 -0
- data/lib/xumlidot/types/superklass.rb +32 -0
- data/spec/spec_helper.rb +2 -0
- metadata +175 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parsers'
|
4
|
+
require_relative '../types'
|
5
|
+
|
6
|
+
module Xumlidot
|
7
|
+
module Parsers
|
8
|
+
module Stack
|
9
|
+
class Constants
|
10
|
+
attr_reader :last_added
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
# This should be main or object
|
14
|
+
@nesting = Xumlidot::Types::Klass.new(nil)
|
15
|
+
@last_added = @nesting
|
16
|
+
end
|
17
|
+
|
18
|
+
def traverse(&block)
|
19
|
+
@nesting.constants.traverse(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# when we add a constant we might want to add to the top of the tree
|
23
|
+
# e.g.
|
24
|
+
# Module A
|
25
|
+
# Moudle B
|
26
|
+
#
|
27
|
+
# or we might want to add onto a given const
|
28
|
+
# e,g
|
29
|
+
# Module A
|
30
|
+
# Module B
|
31
|
+
#
|
32
|
+
# add(Module C, Module B)
|
33
|
+
#
|
34
|
+
def add(c)
|
35
|
+
return if @nesting.constants.find_first(c)
|
36
|
+
|
37
|
+
root = @nesting.constants.root_namespace_for(c)
|
38
|
+
(root.nil? ? @nesting.constants : root.constants) << c
|
39
|
+
@last_added = c
|
40
|
+
end
|
41
|
+
|
42
|
+
class ExternalKlassReferences < Array
|
43
|
+
def <<(external_klass)
|
44
|
+
return if find do |klass|
|
45
|
+
klass.definition == external_klass.definition
|
46
|
+
end
|
47
|
+
|
48
|
+
super(external_klass)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolve_inheritance(constant = nil)
|
53
|
+
external_klasses = ExternalKlassReferences.new
|
54
|
+
|
55
|
+
# The first traversal we are going through finding all
|
56
|
+
# classes with a superklass. The second traversal we are
|
57
|
+
# trying to find the klass which is the superklass of the superklass
|
58
|
+
# found in the first traversal.
|
59
|
+
#
|
60
|
+
# Note Im hacking through this so poor code
|
61
|
+
@nesting.constants.traverse do |klass|
|
62
|
+
next if klass.definition.superklass.empty?
|
63
|
+
|
64
|
+
# If we reach here we have a superklass
|
65
|
+
@nesting.constants.traverse do |other_klass|
|
66
|
+
if other_klass.definition.superklass_of?(klass.definition.superklass)
|
67
|
+
if ::Xumlidot::Options.debug == true
|
68
|
+
STDERR.puts "SETTING SUPERKLASS REFERENCE FOR #{klass} to #{other_klass}"
|
69
|
+
end
|
70
|
+
klass.superklass.reference = other_klass
|
71
|
+
break
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if klass.superklass.reference.nil?
|
76
|
+
# See if we have added it already to the list of external_klasses
|
77
|
+
found = external_klasses.find do |external_klass|
|
78
|
+
klass.definition == external_klass.definition
|
79
|
+
end
|
80
|
+
|
81
|
+
if found
|
82
|
+
klass.superklass.reference = found
|
83
|
+
else
|
84
|
+
new_klass = klass.definition.superklass.to_klass
|
85
|
+
klass.superklass.reference = new_klass
|
86
|
+
external_klasses << new_klass
|
87
|
+
|
88
|
+
add(new_klass)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'types/method'
|
2
|
+
require_relative 'types/methods'
|
3
|
+
require_relative 'types/instance_methods'
|
4
|
+
require_relative 'types/constant'
|
5
|
+
require_relative 'types/superklass'
|
6
|
+
require_relative 'types/inherited_module'
|
7
|
+
require_relative 'types/klass_definition'
|
8
|
+
require_relative 'types/klass'
|
9
|
+
require_relative 'types/klass_methods'
|
10
|
+
require_relative 'types/argument'
|
11
|
+
require_relative 'types/arguments'
|
12
|
+
require_relative 'types/method_signature'
|
13
|
+
require_relative 'types/constants'
|
14
|
+
require_relative 'types/module_definition'
|
15
|
+
require_relative 'types/module'
|
16
|
+
require_relative 'types/klass'
|
17
|
+
require_relative 'types/attribute'
|
18
|
+
require_relative 'types/attributes'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../types'
|
4
|
+
|
5
|
+
module Xumlidot
|
6
|
+
module Types
|
7
|
+
# Value object for an argument as specified within a methods definition.
|
8
|
+
#
|
9
|
+
# e.g. def foo(a, b)
|
10
|
+
#
|
11
|
+
# a and b are the arguments
|
12
|
+
#
|
13
|
+
# Depending on the argument; type (TODO), assign and default may be
|
14
|
+
# unpopulated e.g.
|
15
|
+
#
|
16
|
+
# def foo(a, b)
|
17
|
+
#
|
18
|
+
# will be parsed with an empty assign and default, whereas
|
19
|
+
#
|
20
|
+
# def bar(a = 1, b = nil)
|
21
|
+
#
|
22
|
+
# will both have an assign of '=' and defaults of 1 and :nil respectively.
|
23
|
+
#
|
24
|
+
# This is :nil rather than nill since an assignment to a variable of nil
|
25
|
+
# is parsed in Args and the default value set to the *symbol* :nil
|
26
|
+
class Argument
|
27
|
+
attr_accessor :assign,
|
28
|
+
:default
|
29
|
+
# :types # TODO: determine the type of the argument
|
30
|
+
|
31
|
+
attr_reader :name
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
# @types = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def name=(val)
|
38
|
+
@name = val.tr('&', '')
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
str_default = case default
|
43
|
+
when :nil
|
44
|
+
'nil'
|
45
|
+
when String
|
46
|
+
default
|
47
|
+
when NilClass
|
48
|
+
nil
|
49
|
+
when Symbol
|
50
|
+
":#{default}"
|
51
|
+
when Hash
|
52
|
+
'{}' # TODO: Some hashes were crashing the parser, why?
|
53
|
+
else
|
54
|
+
default.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
[@name, @assign, str_default || nil].compact.join(' ')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../types'
|
4
|
+
|
5
|
+
module Xumlidot
|
6
|
+
module Types
|
7
|
+
# Value object for an attribute, i.e. accessor defined
|
8
|
+
# via attr_reader, attr_writer or attribute
|
9
|
+
#
|
10
|
+
class Attribute
|
11
|
+
attr_accessor :read,
|
12
|
+
:write,
|
13
|
+
:name
|
14
|
+
|
15
|
+
def initialize(name, read, write)
|
16
|
+
@name = name.to_s
|
17
|
+
@read = read
|
18
|
+
@write = write
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"(#{accessibility}) #{@name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def accessibility
|
28
|
+
return 'r+w' if @read && @write
|
29
|
+
return 'ro' if @read && !@write
|
30
|
+
return 'wo' if !@read && @write
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../types'
|
4
|
+
|
5
|
+
module Xumlidot
|
6
|
+
module Types
|
7
|
+
# Our representation of a constant - although just
|
8
|
+
# a string we need to be able to be unambigous in
|
9
|
+
# the representation.
|
10
|
+
#
|
11
|
+
# name is a single constant, namespace is the remaining full path
|
12
|
+
#
|
13
|
+
# e.g
|
14
|
+
# For ::Xumlidot::Types::Constant tne name would be Constant and the
|
15
|
+
# namespace [Types, Xumlidot]
|
16
|
+
#
|
17
|
+
# Note I am REALLY not happy with this design ...
|
18
|
+
class Constant
|
19
|
+
attr_reader :name,
|
20
|
+
:namespace
|
21
|
+
|
22
|
+
attr_accessor :reference # TODO: What IS this? I think I may have thrown this
|
23
|
+
# in to fix a traversal issue if so...hack!
|
24
|
+
|
25
|
+
def initialize(name, namespace = nil)
|
26
|
+
@name = name
|
27
|
+
@namespace = namespace ? namespace.dup : []
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"#{@name} (#{formatted_namespace})"
|
32
|
+
end
|
33
|
+
|
34
|
+
def empty?
|
35
|
+
@name.nil? && @namespace.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def root
|
41
|
+
return '::' if @namespace.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def formatted_namespace
|
45
|
+
[@namespace].flatten.reverse.each(&:to_s).join('::')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../types'
|
4
|
+
|
5
|
+
module Xumlidot
|
6
|
+
module Types
|
7
|
+
# Container class
|
8
|
+
#
|
9
|
+
# I'm thinking a hash to make lookup quicker ...
|
10
|
+
class Constants < Array
|
11
|
+
# return exact matches
|
12
|
+
def find_first(constant)
|
13
|
+
found = find do |klass|
|
14
|
+
klass.definition == constant.definition
|
15
|
+
end
|
16
|
+
return found unless found.nil?
|
17
|
+
|
18
|
+
each do |k|
|
19
|
+
k.constants.each do |klass|
|
20
|
+
found = klass.constants.find_first(constant)
|
21
|
+
return found unless found.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# find any constant that is the root of the namespace for
|
28
|
+
# the supplied constant
|
29
|
+
def root_namespace_for(constant)
|
30
|
+
found = find do |klass|
|
31
|
+
klass.definition.root_namespace_for?(constant)
|
32
|
+
end
|
33
|
+
|
34
|
+
return found unless found.nil?
|
35
|
+
|
36
|
+
each do |k|
|
37
|
+
k.constants.each do |klass|
|
38
|
+
return klass if klass.definition.root_namespace_for?(constant)
|
39
|
+
found = klass.constants.root_namespace_for(constant)
|
40
|
+
return found unless found.nil?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def traverse(&block)
|
47
|
+
each do |k|
|
48
|
+
yield k if block_given?
|
49
|
+
k.constants.each do |klass|
|
50
|
+
yield klass if block_given?
|
51
|
+
klass.constants.traverse(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../types'
|
4
|
+
|
5
|
+
module Xumlidot
|
6
|
+
module Types
|
7
|
+
class InheritedModule < Superklass
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
def initialize(name, namespace = nil)
|
11
|
+
super
|
12
|
+
@has_root = false
|
13
|
+
@type = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def type=(value)
|
17
|
+
case value
|
18
|
+
when :extend, :prepend, :include
|
19
|
+
@type = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require_relative '../types'
|
6
|
+
|
7
|
+
module Xumlidot
|
8
|
+
module Types
|
9
|
+
# representation for a class
|
10
|
+
class Klass
|
11
|
+
# Definition currently holds the superclass ... may change these to nesting and
|
12
|
+
# ancestors yet...
|
13
|
+
attr_accessor :definition,
|
14
|
+
# we *could* just have a single methods type
|
15
|
+
# and construct the instance/class list dynamically...
|
16
|
+
:attributes,
|
17
|
+
:instance_methods,
|
18
|
+
:class_methods,
|
19
|
+
# New additions as I attempt to resolve ancestry
|
20
|
+
|
21
|
+
# Okay so lets say we keep in constants all constants,
|
22
|
+
# modules and clasess (to make it easy to iterate over everything)
|
23
|
+
# are there any drawbacks ... well, well have to test each ...
|
24
|
+
# lets go with it
|
25
|
+
#
|
26
|
+
# Technically (for our purposes) a class, a module, and a constant are
|
27
|
+
# the same thing apart from being semantically different so the constants
|
28
|
+
# within a module or class with hold actual constants such as FOO = 1,
|
29
|
+
# but also Modules and Klasses
|
30
|
+
:constants,
|
31
|
+
:calls # Not yet implemented
|
32
|
+
|
33
|
+
extend Forwardable
|
34
|
+
def_delegator :@definition, :superklass, :superklass
|
35
|
+
|
36
|
+
def initialize(definition)
|
37
|
+
@definition = definition
|
38
|
+
|
39
|
+
@instance_methods = InstanceMethods.new
|
40
|
+
@class_methods = KlassMethods.new
|
41
|
+
@attributes = Attributes.new
|
42
|
+
@constants = Constants.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"#{definition} "
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_method(m)
|
50
|
+
m = m.definition if m.respond_to?(:definition)
|
51
|
+
|
52
|
+
if m.superclass_method == true
|
53
|
+
@class_methods << m
|
54
|
+
else
|
55
|
+
@instance_methods << m
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|