cadove 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ 0.0.0
2
+ =====
3
+ Original release.
data/README ADDED
@@ -0,0 +1,34 @@
1
+ caDOVe (Domain Object Verifier)
2
+ ===============================
3
+
4
+ About
5
+ -----
6
+ caDOVe helps with checking if a Java code base and a UML model
7
+ of the domain objects are in sync. This is especially useful for
8
+ verifying the silver level compatibility requirement,
9
+ "bla bla bla bla bla".
10
+
11
+ Currently...
12
+ - only Hibernate annotated Java classes.
13
+ - only XMI representations of UML are supported.
14
+
15
+ Installation
16
+ ------------
17
+ 1. Install the RJB rubygem (http://rjb.rubyforge.org/)
18
+ 2. Set JAVA_HOME in your environment
19
+
20
+ Usage Example
21
+ -------------
22
+ require 'cadove'
23
+
24
+ CaDOVe.configure do
25
+ left 'nbia.xmi'
26
+ right 'nbia/domain'
27
+
28
+ ignore_classes %w(Boolean Date Double Integer Long String)
29
+
30
+ matching_classes(
31
+ :Image => :CTImage
32
+ )
33
+ end.class_comparison.attribute_comparison
34
+
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/cadove/models'
2
+
3
+ module CaDOVe
4
+ class << self
5
+ attr_writer :left, :right, :ignore_classes, :matching_classes
6
+
7
+ def configure(&block)
8
+ Configurator.module_eval &block
9
+ self
10
+ end
11
+
12
+ ###### CONFIGURATION PROPERTY ACCESSORS
13
+ %w(left right).each do |attribute|
14
+ required_accessor = <<-EOS
15
+ def #{attribute}
16
+ raise ConfigurationError.new("Bcsec::#{attribute} not set") unless @#{attribute}
17
+ @#{attribute}
18
+ end
19
+ EOS
20
+ class_eval(required_accessor)
21
+ end
22
+
23
+ def ignore_classes
24
+ @ignore_classes ||= []
25
+ end
26
+
27
+ def matching_classes
28
+ @matching_clases ||= {}
29
+ end
30
+
31
+ def class_comparison
32
+ Reports::ClassReport.new(comparison, left_type, right_type).display
33
+ self
34
+ end
35
+
36
+ def attribute_comparison
37
+ Reports::AttributeReport.new(comparison, left_type, right_type).display
38
+ self
39
+ end
40
+
41
+ protected
42
+ def left_representation
43
+ @left_representation ||= xmi?(left) ? XMI::XMIDocument.new(left) : Java::JavaSourceTree.new(left)
44
+ end
45
+
46
+ def right_representation
47
+ @right_representation ||= xmi?(right) ? XMI::XMIDocument.new(right) : Java::JavaSourceTree.new(right)
48
+ end
49
+
50
+ def comparison
51
+ @comparison ||= Analytics::Comparison.new(
52
+ left_representation.hashify,
53
+ right_representation.hashify,
54
+ :ignore_classes => @ignore_classes,
55
+ :matching_classes => @matching_classes
56
+ )
57
+ end
58
+
59
+ def xmi?(path)
60
+ path =~ /.*\.xmi$/i
61
+ end
62
+
63
+ %w(left right).each do |side|
64
+ type_accessor = <<-EOS
65
+ def #{side}_type
66
+ xmi?(#{side}) ? :xmi : :java
67
+ end
68
+ EOS
69
+ class_eval(type_accessor)
70
+ end
71
+ end
72
+
73
+ module Configurator
74
+ class << self
75
+ #########
76
+ protected
77
+
78
+ SIMPLE_TERMS = %w(left right ignore_classes matching_classes)
79
+
80
+ # DSL terms for simple top-level attributes
81
+ SIMPLE_TERMS.each do |attribute|
82
+ simple_setter = <<-EOS
83
+ def #{attribute}(value)
84
+ CaDOVe.#{attribute} = value
85
+ end
86
+ EOS
87
+ class_eval(simple_setter)
88
+ end
89
+ end
90
+ end
91
+ class ConfigurationError < Exception; end
92
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+
3
+ Dir[File.dirname(__FILE__) + "/models/**/*.rb"].each do |model_file|
4
+ require model_file
5
+ end
@@ -0,0 +1,143 @@
1
+ require 'rexml/document'
2
+ require File.dirname(__FILE__) + '/../helpers'
3
+
4
+ module CaDOVe
5
+ module XMI
6
+ class XMIDocument
7
+ def initialize(input, opts = {})
8
+ opts[:input_type] ||= :file_path
9
+ val =
10
+ if opts[:input_type] == :text
11
+ input
12
+ elsif opts[:input_type] == :file_path
13
+ File.new(input)
14
+ end
15
+
16
+ @xml_doc = REXML::Document.new(val)
17
+ end
18
+
19
+ def classes
20
+ package = UMLPackage.logical_model(@xml_doc) || UMLPackage.all(@xml_doc).first
21
+ raise "No packages found in the XMI document" unless package
22
+ @classes ||= package.classes
23
+ end
24
+
25
+ def hashify
26
+ all = {}
27
+ classes.each do |clazz|
28
+ all.merge!(clazz.hashify)
29
+ end
30
+ all
31
+ end
32
+ end
33
+
34
+ class UMLPackage
35
+ def initialize(xml)
36
+ @xml = xml
37
+ @name = name
38
+ end
39
+
40
+ # def self.logical_model(xml)
41
+ # packages = all(xml)
42
+ # logical = packages.size == 1 ? packages : packages.select{|p| p.name =~ /Logical Model/i}
43
+ # logical.first
44
+ # end
45
+
46
+ def self.all(xml)
47
+ xml.elements.collect('.//UML:Package') do |p|
48
+ new(p)
49
+ end
50
+ end
51
+
52
+ def self.method_missing(name, *args)
53
+ package_name = name.to_s.gsub(/_/, ' ')
54
+ all(args[0]).select{|p| p.name =~ /#{package_name}/i}.first
55
+ end
56
+
57
+ def name
58
+ @name ||= @xml.attributes['name']
59
+ end
60
+
61
+ def classes
62
+ UMLClass.all(@xml)
63
+ end
64
+ end
65
+
66
+ class UMLClass
67
+ def initialize(xml)
68
+ @xml = xml
69
+ end
70
+
71
+ def self.all(xml)
72
+ xml.elements.collect('.//UML:Class') do |c|
73
+ new(c)
74
+ end
75
+ end
76
+
77
+ def name
78
+ @name ||= @xml.attributes['name']
79
+ end
80
+
81
+ def hashify
82
+ field_and_type = attributes.collect{|f| [f.name, f.type] }
83
+ {"#{name}" => field_and_type}
84
+ end
85
+
86
+ def attributes
87
+ @attributes ||= UMLAttribute.all(@xml)
88
+ end
89
+
90
+ def associations
91
+ @associations ||= UMLAssociation.all(@xml, name)
92
+ end
93
+ end
94
+
95
+ class UMLAttribute
96
+ include CaDOVe::Helpers
97
+
98
+ def initialize(xml)
99
+ @xml = xml
100
+ end
101
+
102
+ def self.all(class_element)
103
+ class_element.elements.collect('.//UML:Attribute') do |a|
104
+ new(a)
105
+ end
106
+ end
107
+
108
+ def name
109
+ @name ||= @xml.attributes['name']
110
+ end
111
+
112
+ def type
113
+ @type ||= remove_package_name(REXML::XPath.first(@xml, ".//UML:TaggedValue[@tag='type']").attributes['value'])
114
+ end
115
+
116
+ def key
117
+ "#{name} (#{type})"
118
+ end
119
+ end
120
+
121
+ class UMLAssociation
122
+ def initialize(xml)
123
+ @xml = xml
124
+ end
125
+
126
+ def self.all(class_element, class_name)
127
+ class_element.root.elements.collect("//UML:Association") do |a|
128
+ if REXML::XPath.first(a, ".//UML:TaggedValue[@tag='ea_sourceName']") && REXML::XPath.first(a, ".//UML:TaggedValue[@tag='ea_sourceName']").attributes['value'] == class_name
129
+ new(a)
130
+ end
131
+ end.compact
132
+ end
133
+
134
+ def name
135
+ REXML::XPath.first(@xml, ".//UML:TaggedValue[@tag='ea_targetName']").attributes['value']
136
+ end
137
+
138
+ def key
139
+ "#{name} (#{type})"
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,155 @@
1
+ module CaDOVe
2
+ module Analytics
3
+ class Comparison
4
+ def initialize(left, right, opts={})
5
+ @left = left
6
+ @right = right
7
+ @opts = opts
8
+ end
9
+
10
+ def classes
11
+ @classes ||= ClassMatch.all(@left, @right, @opts)
12
+ end
13
+
14
+ def intersecting_classes
15
+ @intersecting_classes ||= classes.select{|c| c.intersecting_class? }
16
+ end
17
+ end
18
+
19
+ class ClassMatch
20
+ def initialize(key, left_attributes, right_attributes)
21
+ @key = key
22
+ @left_attributes = left_attributes
23
+ @right_attributes = right_attributes
24
+ end
25
+ attr_reader :key
26
+
27
+ # TODO: Clean this mess up
28
+ def self.all(left, right, opts={})
29
+ opts ||= {}
30
+ ignore_classes = opts[:ignore_classes] || []
31
+ matching_classes = opts[:matching_classes] || {}
32
+
33
+ left_keys = left.keys.reject{|k| ignore_classes.include?(k)}
34
+ right_keys = right.keys.reject{|k| ignore_classes.include?(k)}
35
+ ClassMatchKey.align(left_keys, right_keys, matching_classes).collect do |key|
36
+ ClassMatch.new(
37
+ key,
38
+ left["#{key.left}"],
39
+ right["#{key.right}"]
40
+ )
41
+ end
42
+ end
43
+
44
+ def attributes
45
+ @attributes ||= AttributeMatch.all(@left_attributes, @right_attributes)
46
+ end
47
+
48
+ def left_match?
49
+ !@left_attributes.nil?
50
+ end
51
+
52
+ def right_match?
53
+ !@right_attributes.nil?
54
+ end
55
+
56
+ def intersecting_class?
57
+ left_match? && right_match?
58
+ end
59
+ end
60
+
61
+ class ClassMatchKey
62
+ def initialize(left, right)
63
+ @left = left
64
+ @right = right
65
+ end
66
+ attr_reader :left, :right
67
+
68
+ def self.align(left_keys, right_keys, override={})
69
+ override = override.with_indifferent_access
70
+
71
+ matches = []
72
+ left_keys.each do |left_key|
73
+ found =
74
+ if m = override_match(left_key, right_keys, override)
75
+ right_keys.delete(m)
76
+ elsif right_keys.include?(left_key)
77
+ right_keys.delete(left_key)
78
+ end
79
+
80
+ matches << new(left_key, found)
81
+ end
82
+
83
+ right_keys.each do |right_key|
84
+ matches << new(nil, right_key)
85
+ end
86
+
87
+ matches.sort
88
+ end
89
+
90
+ def self.override_match(left_key, right_keys, override={})
91
+ if override.has_key?(left_key) && right_keys.include?(override[left_key].to_s)
92
+ override[left_key].to_s
93
+ elsif override.invert.has_key?(left_key) && right_keys.include?(override.invert[left_key].to_s)
94
+ override.invert[left_key].to_s
95
+ end
96
+ end
97
+
98
+ def <=>(other)
99
+ display <=> other.display
100
+ end
101
+
102
+ def display
103
+ return if left.nil? && right.nil?
104
+
105
+ if left && right.nil?
106
+ left
107
+ elsif right && left.nil?
108
+ right
109
+ elsif left == right
110
+ left
111
+ elsif left != right
112
+ "#{left} => #{right}"
113
+ end
114
+ end
115
+ end
116
+
117
+ class AttributeMatch
118
+ def initialize(name, type, left_match, right_match)
119
+ @name = name
120
+ @type = type
121
+ @left_match = left_match
122
+ @right_match = right_match
123
+ end
124
+
125
+ def self.all(left_attributes, right_attributes)
126
+ combined = (left_attributes | right_attributes).sort{|a,b|a[0] <=> b[0]} # Sort by name
127
+
128
+ combined.collect do |name, type|
129
+ AttributeMatch.new(
130
+ name,
131
+ type,
132
+ left_attributes.include?( [name, type] ),
133
+ right_attributes.include?( [name, type] )
134
+ )
135
+ end
136
+ end
137
+
138
+ def key
139
+ [@name, @type]
140
+ end
141
+
142
+ def left_match?
143
+ @left_match
144
+ end
145
+
146
+ def right_match?
147
+ @right_match
148
+ end
149
+
150
+ def display
151
+ "#{@name} (#{@type})"
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,7 @@
1
+ module CaDOVe
2
+ module Helpers
3
+ def remove_package_name(fully_qualified)
4
+ fully_qualified.sub(/.*\./ , '')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,149 @@
1
+ # This class has dubious semantics and we only have it so that
2
+ # people can write params[:key] instead of params['key']
3
+ # and they get the same value for both keys.
4
+
5
+ class HashWithIndifferentAccess < Hash
6
+ def initialize(constructor = {})
7
+ if constructor.is_a?(Hash)
8
+ super()
9
+ update(constructor)
10
+ else
11
+ super(constructor)
12
+ end
13
+ end
14
+
15
+ def default(key = nil)
16
+ if key.is_a?(Symbol) && include?(key = key.to_s)
17
+ self[key]
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
24
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
25
+
26
+ # Assigns a new value to the hash:
27
+ #
28
+ # hash = HashWithIndifferentAccess.new
29
+ # hash[:key] = "value"
30
+ #
31
+ def []=(key, value)
32
+ regular_writer(convert_key(key), convert_value(value))
33
+ end
34
+
35
+ # Updates the instantized hash with values from the second:
36
+ #
37
+ # hash_1 = HashWithIndifferentAccess.new
38
+ # hash_1[:key] = "value"
39
+ #
40
+ # hash_2 = HashWithIndifferentAccess.new
41
+ # hash_2[:key] = "New Value!"
42
+ #
43
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
44
+ #
45
+ def update(other_hash)
46
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
47
+ self
48
+ end
49
+
50
+ alias_method :merge!, :update
51
+
52
+ # Checks the hash for a key matching the argument passed in:
53
+ #
54
+ # hash = HashWithIndifferentAccess.new
55
+ # hash["key"] = "value"
56
+ # hash.key? :key # => true
57
+ # hash.key? "key" # => true
58
+ #
59
+ def key?(key)
60
+ super(convert_key(key))
61
+ end
62
+
63
+ alias_method :include?, :key?
64
+ alias_method :has_key?, :key?
65
+ alias_method :member?, :key?
66
+
67
+ # Fetches the value for the specified key, same as doing hash[key]
68
+ def fetch(key, *extras)
69
+ super(convert_key(key), *extras)
70
+ end
71
+
72
+ # Returns an array of the values at the specified indices:
73
+ #
74
+ # hash = HashWithIndifferentAccess.new
75
+ # hash[:a] = "x"
76
+ # hash[:b] = "y"
77
+ # hash.values_at("a", "b") # => ["x", "y"]
78
+ #
79
+ def values_at(*indices)
80
+ indices.collect {|key| self[convert_key(key)]}
81
+ end
82
+
83
+ # Returns an exact copy of the hash.
84
+ def dup
85
+ HashWithIndifferentAccess.new(self)
86
+ end
87
+
88
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
89
+ # Does not overwrite the existing hash.
90
+ def merge(hash)
91
+ self.dup.update(hash)
92
+ end
93
+
94
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
95
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
96
+ def reverse_merge(other_hash)
97
+ super other_hash.with_indifferent_access
98
+ end
99
+
100
+ def invert
101
+ super.with_indifferent_access
102
+ end
103
+
104
+ # Removes a specified key from the hash.
105
+ def delete(key)
106
+ super(convert_key(key))
107
+ end
108
+
109
+ def stringify_keys!; self end
110
+ def symbolize_keys!; self end
111
+ def to_options!; self end
112
+
113
+ # Convert to a Hash with String keys.
114
+ def to_hash
115
+ Hash.new(default).merge(self)
116
+ end
117
+
118
+ protected
119
+ def convert_key(key)
120
+ key.kind_of?(Symbol) ? key.to_s : key
121
+ end
122
+
123
+ def convert_value(value)
124
+ case value
125
+ when Hash
126
+ value.with_indifferent_access
127
+ when Array
128
+ value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
129
+ else
130
+ value
131
+ end
132
+ end
133
+ end
134
+
135
+ module CaDOVe #:nodoc:
136
+ module CoreExtensions #:nodoc:
137
+ module Hash #:nodoc:
138
+ module IndifferentAccess #:nodoc:
139
+ def with_indifferent_access
140
+ hash = HashWithIndifferentAccess.new(self)
141
+ hash.default = self.default
142
+ hash
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ Hash.send(:include, CaDOVe::CoreExtensions::Hash::IndifferentAccess)
@@ -0,0 +1,108 @@
1
+ require 'rjb'
2
+ require File.dirname(__FILE__) + '/../helpers'
3
+
4
+ module CaDOVe
5
+ module Java
6
+ Rjb::load(File.dirname(__FILE__) + '/javalib/qdox-1.10.jar')
7
+
8
+ class JavaSourceTree
9
+ def initialize(directory)
10
+ file = Rjb::import('java.io.File').
11
+ new_with_sig('Ljava.lang.String;', directory)
12
+
13
+ builder = Rjb::import('com.thoughtworks.qdox.JavaDocBuilder').new
14
+ builder._invoke('addSourceTree', 'Ljava.io.File;', file)
15
+
16
+ @sources = builder.getSources
17
+ end
18
+
19
+ def classes
20
+ JavaClass.all(@sources)
21
+ end
22
+
23
+ def hashify
24
+ all = {}
25
+ classes.each do |clazz|
26
+ all.merge!(clazz.hashify)
27
+ end
28
+ all
29
+ end
30
+ end
31
+
32
+ class JavaClass
33
+ def initialize(clazz)
34
+ @clazz = clazz
35
+ end
36
+
37
+ def self.all(sources, including_abstract=false)
38
+ classes = sources.collect { |s| s.getClasses }.flatten
39
+ results = classes.collect { |c| new(c) }
40
+ end
41
+
42
+ def name
43
+ @name ||= @clazz.getName
44
+ end
45
+
46
+ def hashify
47
+ field_and_type = fields.collect{|f| [f.name, f.type] }
48
+ {"#{name}" => field_and_type}
49
+ end
50
+
51
+ def fields
52
+ JavaField.all_non_associated(@clazz)
53
+ end
54
+ alias_method :attributes, :fields
55
+ end
56
+
57
+
58
+ class JavaField
59
+ include CaDOVe::Helpers
60
+ def initialize(field)
61
+ @field = field
62
+ end
63
+
64
+ def self.all(clazz)
65
+ clazz.getFields.collect do |field|
66
+ new(field)
67
+ end.reject{|f| is_constant?(f.name) }
68
+ end
69
+
70
+ def self.all_non_associated(clazz)
71
+ all(clazz).select{|f| is_simple_type(f.type) }
72
+ end
73
+
74
+ def self.is_simple_type(class_name)
75
+ java_lang_and_other = %w(Boolean Byte Character Double Float Integer Long Math Number Short String Date Timestamp)
76
+ primitives = %w(boolean byte char double float integer long short)
77
+ result |= java_lang_and_other.include?(class_name)
78
+ result |= primitives.include?(class_name)
79
+ result
80
+ end
81
+
82
+ def self.is_constant?(name)
83
+ name =~ /^([A-Z]|_)+$/
84
+ end
85
+
86
+ def name
87
+ @name ||= @field.getName
88
+ end
89
+
90
+ def type
91
+ @type ||= remove_package_name(@field.getType.getValue)
92
+ end
93
+
94
+ def ==(other)
95
+ return true if self.equal?(other)
96
+ return false if self.class != Java::JavaMethod
97
+
98
+ if (self.name != other.name)
99
+ return false
100
+ elsif (self.type != other.type)
101
+ return false
102
+ end
103
+
104
+ true
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,60 @@
1
+ # TODO: Move these into a plain text file
2
+ module CaDOVe
3
+ module Reports
4
+ class ClassReport
5
+ def initialize(comparison, left_type, right_type)
6
+ @comparison = comparison
7
+ @left_type = left_type
8
+ @right_type = right_type
9
+ end
10
+
11
+ def display
12
+ puts Time.new.strftime('%b %d, %Y %H:%M:%S %Z')
13
+ puts "\n"
14
+ puts " #{header(@left_type)} | #{header(@right_type)} | Class Name"
15
+ puts "--------------------------------"
16
+ @comparison.classes.each do |clazz|
17
+ puts " #{clazz.left_match? ? 'X' : ' '} | #{clazz.right_match? ? 'X' : ' '} | #{clazz.key.display}"
18
+ end
19
+ end
20
+
21
+ def header(name)
22
+ name.to_s.upcase
23
+ end
24
+ end
25
+
26
+ class AttributeReport
27
+ def initialize(comparison, left_type, right_type)
28
+ @comparison = comparison
29
+ @left_type = left_type
30
+ @right_type = right_type
31
+ end
32
+
33
+ def display
34
+ puts Time.new.strftime('%b %d, %Y %H:%M:%S %Z')
35
+ puts "\n"
36
+
37
+ puts "########################"
38
+ @comparison.intersecting_classes.each do |clazz|
39
+ unless clazz.attributes.empty?
40
+ puts ""
41
+ puts "#{clazz.key.display}"
42
+ puts ""
43
+ puts " #{header(@left_type)} | #{header(@right_type)} | Attributes"
44
+ puts "--------------------------------------"
45
+
46
+ clazz.attributes.each do |a|
47
+ puts " #{a.left_match? ? 'X' : ' '} | #{a.right_match? ? 'X' : ' '} | #{a.display}"
48
+ end
49
+ puts ""
50
+ puts "########################"
51
+ end
52
+ end
53
+ end
54
+
55
+ def header(name)
56
+ name.to_s.upcase
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe CaDOVe do
4
+ it "should configure the left and right properties" do
5
+ CaDOVe.configure do
6
+ left 'hello'
7
+ right 'world'
8
+ end
9
+
10
+ CaDOVe.left.should == 'hello'
11
+ CaDOVe.right.should == 'world'
12
+ end
13
+
14
+ it "should throws an exception if a required configuration option is accessed without being set" do
15
+ CaDOVe.left = nil
16
+ lambda { CaDOVe.left }.should raise_error(CaDOVe::ConfigurationError)
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ package org.foo;
2
+
3
+ abstract class Animal {}
@@ -0,0 +1,3 @@
1
+ package org.foo;
2
+
3
+ class Cat { }
@@ -0,0 +1,18 @@
1
+ package org.foo;
2
+
3
+ class Dog {
4
+ String color;
5
+ boolean rabid;
6
+ Father father;
7
+ List<Enemies> enemies;
8
+
9
+ public String getColor(){}
10
+ public boolean isRabid(){}
11
+
12
+ public Father getFather() {}
13
+ public List<Enemies> getEnemies() {}
14
+
15
+ public String setIgnoreSetters(){}
16
+ private String getIgnorePrivateMethods(){}
17
+ protected String getIgnoreProtectedMethods(){}
18
+ }
@@ -0,0 +1,127 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module CaDOVe::XMI
4
+ describe UMLClass do
5
+ before(:each) do
6
+ @xmi =
7
+ xmi_document do
8
+ uml_package('Data Model') do
9
+ uml_class('DontFindMe')
10
+ end +
11
+ uml_package('Logical Model') do
12
+ uml_class('Dog') do
13
+ uml_attribute('color', 'String') +
14
+ uml_attribute('rabid', 'boolean')
15
+ end +
16
+ uml_class('Cat')
17
+ end +
18
+
19
+ uml_association('Dog', 'Friend') +
20
+ uml_association('Dog', 'Enemy')
21
+ end
22
+
23
+ @doc = XMIDocument.new(@xmi, :input_type => :text)
24
+ @dog = @doc.classes.select{ |c| c.name == 'Dog' }.first
25
+ end
26
+
27
+ it "should have two classes in the document" do
28
+ classes = @doc.classes
29
+ @doc.should have(2).classes
30
+ end
31
+
32
+ it "should include the class Cat and Dog" do
33
+ names = @doc.classes.collect { |c| c.name }
34
+ names.should include("Cat")
35
+ names.should include("Dog")
36
+ end
37
+
38
+ it "should have the correct attributes for Dog" do
39
+ @dog.should have(2).attributes
40
+ names = @dog.attributes.collect { |c| [c.name, c.type] }
41
+ names.should include( %w(color String) )
42
+ names.should include( %w(rabid boolean) )
43
+ end
44
+
45
+ it "should have the correct correct associations for Dog" do
46
+ @dog.should have(2).associations
47
+ names = @dog.associations.collect { |c| c.name }
48
+ names.should include('Friend')
49
+ names.should include('Enemy')
50
+ end
51
+
52
+ it "should return the only model available if only one exists" do
53
+ single_pkg_xmi =
54
+ xmi_document do
55
+ uml_package('My Package') do
56
+ uml_class('Foo')
57
+ end
58
+ end
59
+
60
+ single_pkg_doc = XMIDocument.new(single_pkg_xmi, :input_type => :text)
61
+ single_pkg_doc.should have(1).classes
62
+ end
63
+
64
+ it "should hashify dog" do
65
+ doggy_hash = @dog.hashify
66
+ doggy_hash['Dog'].should include( %w(color String) )
67
+ doggy_hash['Dog'].should include( %w(rabid boolean) )
68
+ end
69
+
70
+ it "should hashify the document tree" do
71
+ @doc.hashify.keys.should include('Cat')
72
+ @doc.hashify.keys.should include('Dog')
73
+
74
+ @doc.hashify['Dog'].should include( %w(color String) )
75
+ @doc.hashify['Dog'].should include( %w(rabid boolean) )
76
+ end
77
+
78
+ private
79
+ def xmi_document(&block)
80
+ <<-XMI
81
+ <xmi:XMI xmlns:UML="http://schema.omg.org/spec/UML/2.0" xmlns:xmi="http://schema.omg.org/spec/XMI/2.1">
82
+ #{yield if block_given?}
83
+ </xmi:XMI>
84
+ XMI
85
+ end
86
+
87
+ def uml_package(name, &block)
88
+ <<-XMI
89
+ <UML:Package name="#{name}">
90
+ #{yield if block_given?}
91
+ </UML:Package>
92
+ XMI
93
+ end
94
+
95
+ def uml_class(name, &block)
96
+ <<-XMI
97
+ <UML:Class name='#{name}'>
98
+ #{yield if block_given?}
99
+ </UML:Class>
100
+ XMI
101
+ end
102
+
103
+ def uml_attribute(name, type)
104
+ <<-XMI
105
+ <UML:Attribute name='#{name}' visibility='private'>
106
+ <UML:ModelElement.taggedValue>
107
+ <UML:TaggedValue tag='foo' value='bar'/>
108
+ <UML:TaggedValue tag='type' value='#{type}'/>
109
+ <UML:TaggedValue tag='derived' value='0'/>
110
+ </UML:ModelElement.taggedValue>
111
+ </UML:Attribute>
112
+ XMI
113
+ end
114
+
115
+ def uml_association(src, dst)
116
+ <<-XMI
117
+ <UML:Association visibility='public'>
118
+ <UML:ModelElement.taggedValue>
119
+ <UML:TaggedValue tag='style' value='3'/>
120
+ <UML:TaggedValue tag='ea_sourceName' value='#{src}'/>
121
+ <UML:TaggedValue tag='ea_targetName' value='#{dst}'/>
122
+ </UML:ModelElement.taggedValue>
123
+ </UML:Association>
124
+ XMI
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,105 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module CaDOVe::Analytics
4
+ describe Comparison do
5
+ before(:each) do
6
+ @left = {
7
+ 'Dog' => [
8
+ %w(name String),
9
+ %w(rabid boolean)
10
+ ]
11
+ }
12
+
13
+ @right = {
14
+ 'Dog' => [
15
+ %w(name String),
16
+ %w(color String)
17
+ ],
18
+ 'Rabbit' => [ %w(color String) ]
19
+ }
20
+
21
+ @comparison = Comparison.new(@left, @right)
22
+ @dog = @comparison.classes.select{|c| c.key.left == 'Dog' }.first
23
+ end
24
+
25
+ it "should have two class" do
26
+ @comparison.should have(2).classes
27
+ @comparison.classes.collect{|c| [c.key.left, c.key.right]}.should == [
28
+ ['Dog', 'Dog'],
29
+ [nil, 'Rabbit']
30
+ ]
31
+ end
32
+
33
+ it "should have a left and right match" do
34
+ @dog.should be_left_match
35
+ @dog.should be_right_match
36
+ end
37
+
38
+
39
+ it "should have one intersecting class" do
40
+ @comparison.should have(1).intersecting_classes
41
+ @comparison.intersecting_classes.collect{|c| [c.key.left, c.key.right]}.should == [['Dog', 'Dog']]
42
+ end
43
+
44
+ it "should have a dog match with 3 attributes" do
45
+ keys = @dog.attributes.collect{|a| a.key}
46
+ keys.should include( %w(name String) )
47
+ keys.should include( %w(rabid boolean))
48
+ keys.should include( %w(color String) )
49
+ end
50
+
51
+ it "should have a dog match having an 'name' attribute having a left and right match" do
52
+ name = @dog.attributes.select{|a| a.key[0] == 'name'}.first
53
+ name.should be_left_match
54
+ name.should be_right_match
55
+ end
56
+
57
+ it "should have a dog match having an 'rabid' attribute having only a left match" do
58
+ name = @dog.attributes.select{|a| a.key[0] == 'rabid'}.first
59
+ name.should be_left_match
60
+ name.should_not be_right_match
61
+ end
62
+
63
+ it "should ignore the Rabbit class" do
64
+ ignore_on = Comparison.new(@left, @right, :ignore_classes => %w(Rabbit) )
65
+ ignore_on.should have(1).classes
66
+ end
67
+
68
+ it "should manually match 'Rabbit' to 'Hare'" do
69
+ @left['Hare'] = [
70
+ %w(color String)
71
+ ]
72
+
73
+ manual_matching = Comparison.new(@left, @right, :matching_classes => {:Hare => :Rabbit})
74
+ manual_matching.should have(2).intersecting_classes
75
+ names = manual_matching.intersecting_classes.collect{|c| [c.key.left, c.key.right]}
76
+ names.should include(['Hare', 'Rabbit'])
77
+ names.should include(['Dog', 'Dog'])
78
+ end
79
+
80
+ it "should manually match 'Rabbit' to 'Hare' with inverse matching class" do
81
+ @left['Hare'] = [
82
+ %w(color String)
83
+ ]
84
+
85
+ manual_matching = Comparison.new(@left, @right, :matching_classes => {:Rabbit => :Hare})
86
+ manual_matching.should have(2).intersecting_classes
87
+ names = manual_matching.intersecting_classes.collect{|c| [c.key.left, c.key.right]}
88
+ names.should include(['Hare', 'Rabbit'])
89
+ names.should include(['Dog', 'Dog'])
90
+ end
91
+
92
+ it "should align keys" do
93
+ aligned = ClassMatchKey.align(['a', 'b'], ['b', 'c'], :a => :c).collect{|k| [k.left, k.right]}
94
+ aligned.size.should be(2)
95
+ aligned.should include(['b', 'b'])
96
+ aligned.should include(['a', 'c'])
97
+ end
98
+
99
+ it "should sort keys" do
100
+ keys = [ClassMatchKey.new('b', 'b'), ClassMatchKey.new('a', 'a')].sort
101
+ keys[0].left.should == 'a'
102
+ keys[1].left.should == 'b'
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ module CaDOVe::Java
4
+ describe JavaSourceTree do
5
+ before(:each) do
6
+ @code = JavaSourceTree.new(
7
+ File.dirname(__FILE__) + '/../../java_stubs'
8
+ )
9
+
10
+ @dog = @code.classes.select { |c| c.name == "Dog" }.first
11
+ end
12
+
13
+ it "should have three classes in the document" do
14
+ @code.should have(3).classes
15
+ end
16
+
17
+ it "should include only classes Animal, Cat and Dog" do
18
+ names = @code.classes.collect { |c| c.name }
19
+ names.should include("Animal")
20
+ names.should include("Cat")
21
+ names.should include("Dog")
22
+ end
23
+
24
+ it "should have the two fields" do
25
+ @dog.should have(2).fields
26
+ fields = @dog.fields.collect{ |f| [f.name, f.type] }
27
+ fields.should include( %w(color String) )
28
+ fields.should include( %w(rabid boolean) )
29
+ end
30
+
31
+
32
+ it "should hashify Dog" do
33
+ doggy_hash = @dog.hashify
34
+ doggy_hash['Dog'].should include( %w(color String) )
35
+ doggy_hash['Dog'].should include( %w(rabid boolean) )
36
+ end
37
+
38
+ it "should hashify the source tree" do
39
+ @code.hashify.keys.should include('Cat')
40
+ @code.hashify.keys.should include('Dog')
41
+
42
+ @code.hashify['Dog'].should include( %w(color String) )
43
+ @code.hashify['Dog'].should include( %w(rabid boolean) )
44
+ end
45
+
46
+ it "should have only associations"
47
+ end
48
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../lib/cadove'
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cadove
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - John Dzak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-11 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rjb
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description:
26
+ email:
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/cadove/models/analytics/comparison.rb
35
+ - lib/cadove/models/helpers.rb
36
+ - lib/cadove/models/indifferent_access.rb
37
+ - lib/cadove/models/java/code.rb
38
+ - lib/cadove/models/java/javalib/qdox-1.10.jar
39
+ - lib/cadove/models/reports/reports.rb
40
+ - lib/cadove/models/XMI/document.rb
41
+ - lib/cadove/models.rb
42
+ - lib/cadove.rb
43
+ - CHANGELOG
44
+ - README
45
+ - spec/cadove_spec.rb
46
+ - spec/java_stubs/org/foo/Animal.java
47
+ - spec/java_stubs/org/foo/Cat.java
48
+ - spec/java_stubs/org/foo/Dog.java
49
+ - spec/models/analytics/comparison_spec.rb
50
+ - spec/models/java/code_spec.rb
51
+ - spec/models/XMI/document_spec.rb
52
+ - spec/spec_helper.rb
53
+ has_rdoc: true
54
+ homepage:
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Domain Object Verifier compares Java domain objects to their corresponding XMI representations.
81
+ test_files: []
82
+