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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/bin/xumlidot +39 -0
  3. data/lib/xumlidot.rb +10 -0
  4. data/lib/xumlidot/diagram.rb +18 -0
  5. data/lib/xumlidot/diagram/dot.rb +55 -0
  6. data/lib/xumlidot/diagram/dot/klass.rb +75 -0
  7. data/lib/xumlidot/diagram/dot/module.rb +11 -0
  8. data/lib/xumlidot/diagram/shared/naming.rb +19 -0
  9. data/lib/xumlidot/diagram/xmi.rb +190 -0
  10. data/lib/xumlidot/diagram/xmi/argument.rb +34 -0
  11. data/lib/xumlidot/diagram/xmi/attribute.rb +24 -0
  12. data/lib/xumlidot/diagram/xmi/constant.rb +16 -0
  13. data/lib/xumlidot/diagram/xmi/id.rb +39 -0
  14. data/lib/xumlidot/diagram/xmi/klass.rb +133 -0
  15. data/lib/xumlidot/diagram/xmi/method.rb +38 -0
  16. data/lib/xumlidot/diagram/xmi/superklass.rb +18 -0
  17. data/lib/xumlidot/directory_tree.rb +24 -0
  18. data/lib/xumlidot/options.rb +104 -0
  19. data/lib/xumlidot/parsers.rb +14 -0
  20. data/lib/xumlidot/parsers/args.rb +159 -0
  21. data/lib/xumlidot/parsers/call.rb +92 -0
  22. data/lib/xumlidot/parsers/file.rb +15 -0
  23. data/lib/xumlidot/parsers/generic.rb +106 -0
  24. data/lib/xumlidot/parsers/klass_definition.rb +76 -0
  25. data/lib/xumlidot/parsers/method_signature.rb +95 -0
  26. data/lib/xumlidot/parsers/module_definition.rb +55 -0
  27. data/lib/xumlidot/parsers/scope.rb +54 -0
  28. data/lib/xumlidot/parsers/stack.rb +96 -0
  29. data/lib/xumlidot/types.rb +18 -0
  30. data/lib/xumlidot/types/argument.rb +61 -0
  31. data/lib/xumlidot/types/arguments.rb +14 -0
  32. data/lib/xumlidot/types/attribute.rb +34 -0
  33. data/lib/xumlidot/types/attributes.rb +11 -0
  34. data/lib/xumlidot/types/constant.rb +49 -0
  35. data/lib/xumlidot/types/constants.rb +57 -0
  36. data/lib/xumlidot/types/inherited_module.rb +24 -0
  37. data/lib/xumlidot/types/instance_methods.rb +10 -0
  38. data/lib/xumlidot/types/klass.rb +60 -0
  39. data/lib/xumlidot/types/klass_definition.rb +61 -0
  40. data/lib/xumlidot/types/klass_methods.rb +10 -0
  41. data/lib/xumlidot/types/method.rb +10 -0
  42. data/lib/xumlidot/types/method_signature.rb +64 -0
  43. data/lib/xumlidot/types/methods.rb +10 -0
  44. data/lib/xumlidot/types/module.rb +11 -0
  45. data/lib/xumlidot/types/module_definition.rb +14 -0
  46. data/lib/xumlidot/types/superklass.rb +32 -0
  47. data/spec/spec_helper.rb +2 -0
  48. 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types'
4
+
5
+ module Xumlidot
6
+ module Types
7
+ # Container class
8
+ class Arguments < Array
9
+ def to_s
10
+ each.map(&:to_s).join(', ')
11
+ end
12
+ end
13
+ end
14
+ 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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types'
4
+
5
+ module Xumlidot
6
+ module Types
7
+ # Container class
8
+ class Attributes < Array
9
+ end
10
+ end
11
+ 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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types'
4
+
5
+ module Xumlidot
6
+ module Types
7
+ class InstanceMethods < Methods
8
+ end
9
+ end
10
+ 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