xumlidot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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