jinx 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +14 -0
  2. data/.rspec +3 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +27 -0
  6. data/History.md +6 -0
  7. data/LEGAL +5 -0
  8. data/LICENSE +22 -0
  9. data/README.md +44 -0
  10. data/Rakefile +41 -0
  11. data/examples/family/README.md +10 -0
  12. data/examples/family/ext/build.xml +35 -0
  13. data/examples/family/ext/src/family/Address.java +68 -0
  14. data/examples/family/ext/src/family/Child.java +24 -0
  15. data/examples/family/ext/src/family/DomainObject.java +26 -0
  16. data/examples/family/ext/src/family/Household.java +36 -0
  17. data/examples/family/ext/src/family/Parent.java +48 -0
  18. data/examples/family/ext/src/family/Person.java +42 -0
  19. data/examples/family/lib/family.rb +15 -0
  20. data/examples/family/lib/family/address.rb +6 -0
  21. data/examples/family/lib/family/domain_object.rb +6 -0
  22. data/examples/family/lib/family/household.rb +6 -0
  23. data/examples/family/lib/family/parent.rb +16 -0
  24. data/examples/family/lib/family/person.rb +6 -0
  25. data/examples/model/README.md +25 -0
  26. data/examples/model/ext/build.xml +35 -0
  27. data/examples/model/ext/src/domain/Child.java +192 -0
  28. data/examples/model/ext/src/domain/Dependent.java +29 -0
  29. data/examples/model/ext/src/domain/DomainObject.java +26 -0
  30. data/examples/model/ext/src/domain/Independent.java +83 -0
  31. data/examples/model/ext/src/domain/Parent.java +129 -0
  32. data/examples/model/ext/src/domain/Person.java +14 -0
  33. data/examples/model/lib/model.rb +13 -0
  34. data/examples/model/lib/model/child.rb +13 -0
  35. data/examples/model/lib/model/domain_object.rb +6 -0
  36. data/examples/model/lib/model/independent.rb +11 -0
  37. data/examples/model/lib/model/parent.rb +17 -0
  38. data/jinx.gemspec +22 -0
  39. data/lib/jinx.rb +3 -0
  40. data/lib/jinx/active_support/README.txt +2 -0
  41. data/lib/jinx/active_support/core_ext/string.rb +7 -0
  42. data/lib/jinx/active_support/core_ext/string/inflections.rb +167 -0
  43. data/lib/jinx/active_support/inflections.rb +55 -0
  44. data/lib/jinx/active_support/inflector.rb +398 -0
  45. data/lib/jinx/cli/application.rb +36 -0
  46. data/lib/jinx/cli/command.rb +214 -0
  47. data/lib/jinx/helpers/array.rb +108 -0
  48. data/lib/jinx/helpers/boolean.rb +42 -0
  49. data/lib/jinx/helpers/case_insensitive_hash.rb +39 -0
  50. data/lib/jinx/helpers/class.rb +149 -0
  51. data/lib/jinx/helpers/collection.rb +33 -0
  52. data/lib/jinx/helpers/collections.rb +11 -0
  53. data/lib/jinx/helpers/collector.rb +20 -0
  54. data/lib/jinx/helpers/conditional_enumerator.rb +21 -0
  55. data/lib/jinx/helpers/enumerable.rb +242 -0
  56. data/lib/jinx/helpers/enumerate.rb +35 -0
  57. data/lib/jinx/helpers/error.rb +15 -0
  58. data/lib/jinx/helpers/file_separator.rb +65 -0
  59. data/lib/jinx/helpers/filter.rb +52 -0
  60. data/lib/jinx/helpers/flattener.rb +38 -0
  61. data/lib/jinx/helpers/hash.rb +12 -0
  62. data/lib/jinx/helpers/hashable.rb +502 -0
  63. data/lib/jinx/helpers/inflector.rb +36 -0
  64. data/lib/jinx/helpers/key_transformer_hash.rb +43 -0
  65. data/lib/jinx/helpers/lazy_hash.rb +44 -0
  66. data/lib/jinx/helpers/log.rb +106 -0
  67. data/lib/jinx/helpers/math.rb +12 -0
  68. data/lib/jinx/helpers/merge.rb +60 -0
  69. data/lib/jinx/helpers/module.rb +18 -0
  70. data/lib/jinx/helpers/multi_enumerator.rb +31 -0
  71. data/lib/jinx/helpers/options.rb +92 -0
  72. data/lib/jinx/helpers/os.rb +19 -0
  73. data/lib/jinx/helpers/partial_order.rb +37 -0
  74. data/lib/jinx/helpers/pretty_print.rb +207 -0
  75. data/lib/jinx/helpers/set.rb +8 -0
  76. data/lib/jinx/helpers/stopwatch.rb +76 -0
  77. data/lib/jinx/helpers/transformer.rb +24 -0
  78. data/lib/jinx/helpers/transitive_closure.rb +55 -0
  79. data/lib/jinx/helpers/uniquifier.rb +50 -0
  80. data/lib/jinx/helpers/validation.rb +33 -0
  81. data/lib/jinx/helpers/visitor.rb +370 -0
  82. data/lib/jinx/import/class_path_modifier.rb +77 -0
  83. data/lib/jinx/import/java.rb +337 -0
  84. data/lib/jinx/importer.rb +240 -0
  85. data/lib/jinx/metadata.rb +155 -0
  86. data/lib/jinx/metadata/attribute_enumerator.rb +73 -0
  87. data/lib/jinx/metadata/dependency.rb +244 -0
  88. data/lib/jinx/metadata/id_alias.rb +23 -0
  89. data/lib/jinx/metadata/introspector.rb +179 -0
  90. data/lib/jinx/metadata/inverse.rb +170 -0
  91. data/lib/jinx/metadata/java_property.rb +169 -0
  92. data/lib/jinx/metadata/propertied.rb +500 -0
  93. data/lib/jinx/metadata/property.rb +401 -0
  94. data/lib/jinx/metadata/property_characteristics.rb +114 -0
  95. data/lib/jinx/resource.rb +862 -0
  96. data/lib/jinx/resource/copy_visitor.rb +36 -0
  97. data/lib/jinx/resource/inversible.rb +90 -0
  98. data/lib/jinx/resource/match_visitor.rb +180 -0
  99. data/lib/jinx/resource/matcher.rb +20 -0
  100. data/lib/jinx/resource/merge_visitor.rb +73 -0
  101. data/lib/jinx/resource/mergeable.rb +185 -0
  102. data/lib/jinx/resource/reference_enumerator.rb +49 -0
  103. data/lib/jinx/resource/reference_path_visitor.rb +38 -0
  104. data/lib/jinx/resource/reference_visitor.rb +55 -0
  105. data/lib/jinx/resource/unique.rb +35 -0
  106. data/lib/jinx/version.rb +3 -0
  107. data/spec/defaults_spec.rb +30 -0
  108. data/spec/definitions/model/alias/child.rb +5 -0
  109. data/spec/definitions/model/base/child.rb +5 -0
  110. data/spec/definitions/model/base/domain_object.rb +5 -0
  111. data/spec/definitions/model/base/independent.rb +5 -0
  112. data/spec/definitions/model/defaults/child.rb +5 -0
  113. data/spec/definitions/model/dependency/child.rb +5 -0
  114. data/spec/definitions/model/dependency/parent.rb +6 -0
  115. data/spec/definitions/model/inverse/child.rb +5 -0
  116. data/spec/definitions/model/inverse/independent.rb +5 -0
  117. data/spec/definitions/model/inverse/parent.rb +5 -0
  118. data/spec/definitions/model/mandatory/child.rb +6 -0
  119. data/spec/dependency_spec.rb +47 -0
  120. data/spec/family_spec.rb +64 -0
  121. data/spec/inverse_spec.rb +53 -0
  122. data/spec/mandatory_spec.rb +43 -0
  123. data/spec/metadata_spec.rb +68 -0
  124. data/spec/resource_spec.rb +30 -0
  125. data/spec/spec_helper.rb +3 -0
  126. data/spec/support/model.rb +19 -0
  127. data/test/fixtures/line_separator/cr_line_sep.txt +1 -0
  128. data/test/fixtures/line_separator/crlf_line_sep.txt +3 -0
  129. data/test/fixtures/line_separator/lf_line_sep.txt +3 -0
  130. data/test/fixtures/mixed/ext/build.xml +35 -0
  131. data/test/fixtures/mixed/ext/src/mixed/Case/Example.java +5 -0
  132. data/test/helper.rb +7 -0
  133. data/test/lib/jinx/command_test.rb +41 -0
  134. data/test/lib/jinx/helpers/boolean_test.rb +27 -0
  135. data/test/lib/jinx/helpers/class_test.rb +60 -0
  136. data/test/lib/jinx/helpers/collections_test.rb +402 -0
  137. data/test/lib/jinx/helpers/file_separator_test.rb +29 -0
  138. data/test/lib/jinx/helpers/inflector_test.rb +11 -0
  139. data/test/lib/jinx/helpers/lazy_hash_test.rb +32 -0
  140. data/test/lib/jinx/helpers/module_test.rb +24 -0
  141. data/test/lib/jinx/helpers/options_test.rb +66 -0
  142. data/test/lib/jinx/helpers/partial_order_test.rb +41 -0
  143. data/test/lib/jinx/helpers/pretty_print_test.rb +83 -0
  144. data/test/lib/jinx/helpers/stopwatch_test.rb +16 -0
  145. data/test/lib/jinx/helpers/transitive_closure_test.rb +80 -0
  146. data/test/lib/jinx/helpers/visitor_test.rb +288 -0
  147. data/test/lib/jinx/import/java_test.rb +78 -0
  148. data/test/lib/jinx/import/mixed_case_test.rb +16 -0
  149. metadata +272 -0
@@ -0,0 +1,49 @@
1
+ require 'enumerator'
2
+ require 'generator'
3
+ require 'jinx/helpers/options'
4
+ require 'jinx/helpers/collections'
5
+
6
+ require 'jinx/helpers/validation'
7
+ require 'jinx/helpers/visitor'
8
+ require 'jinx/helpers/math'
9
+
10
+ module Jinx
11
+ # A ReferenceEnumerator iterates over domain property references.
12
+ class ReferenceEnumerator
13
+ include Enumerable
14
+
15
+ # @return [Resource] the domain object containing the references
16
+ attr_reader :subject
17
+
18
+ alias :on :subject
19
+
20
+ alias :from :subject
21
+
22
+ # @return [<Property>] the current property
23
+ attr_reader :property
24
+
25
+ # @param [Resource, nil] on the object containing the references
26
+ # @param [<Property>, Property, nil] properties the property or properties to dereference
27
+ def initialize(on=nil, properties=nil)
28
+ @subject = on
29
+ @properties = properties
30
+ end
31
+
32
+ # @param [Resource] obj the visiting domain object
33
+ # @return [(Resource, Resource, Property)] the (visited, visiting, property) tuples
34
+ # @yield [obj, from, property] operates on the visited domain object
35
+ # @yieldparam [Resource] obj the visited domain object
36
+ # @yieldparam [Resource] from the visiting domain object
37
+ # @yieldparam [Property] property the visiting property
38
+ def each
39
+ return if @subject.nil?
40
+ @properties.enumerate do |prop|
41
+ @property = prop
42
+ # the reference(s) to visit
43
+ refs = @subject.send(prop.attribute)
44
+ # associate each reference to visit with the current visited attribute
45
+ refs.enumerate { |ref| yield(ref) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ require 'jinx/helpers/collections'
2
+
3
+ require 'jinx/helpers/validation'
4
+ require 'jinx/helpers/visitor'
5
+ require 'jinx/helpers/math'
6
+
7
+ module Jinx
8
+ # A ReferencePathVisitor traverses an attribute path.
9
+ #
10
+ # For example, given the attributes:
11
+ # favorites : Person -> Book
12
+ # authors : Book -> Author
13
+ # publications : Author -> Book
14
+ # then a path visitor given by:
15
+ # ReferencePathVisitor.new(Person, [:favorites, :authors, :publications])
16
+ # visits the transitive closure of books published by the authors of a person's favorite books.
17
+ class ReferencePathVisitor < ReferenceVisitor
18
+ # @return [ReferenceVisitor] a visitor which traverses the given path attributes starting at
19
+ # an instance of the given type
20
+ #
21
+ # @param [Class] the type of object to begin the traversal
22
+ # @param [<Symbol>] the attributes to traverse
23
+ # @param opts (see ReferenceVisitor#initialize)
24
+ def initialize(klass, attributes, opts=nil)
25
+ # augment the attributes path as a [class, attribute] path
26
+ path = klass.property_path(*attributes)
27
+ # make the visitor
28
+ super(opts) do |ref|
29
+ # Collect the path attributes whose type is the ref type up to the
30
+ # next position in the path.
31
+ max = lineage.size.min(path.size)
32
+ pas = (0...max).map { |i| path[i].attribute if path[i].declarer === ref }
33
+ pas.compact!
34
+ ref.class.attribute_filter(pas)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ require 'enumerator'
2
+ require 'generator'
3
+ require 'jinx/helpers/options'
4
+ require 'jinx/helpers/collections'
5
+
6
+ require 'jinx/helpers/validation'
7
+ require 'jinx/helpers/visitor'
8
+ require 'jinx/helpers/math'
9
+
10
+ module Jinx
11
+ # A ReferenceVisitor traverses reference attributes.
12
+ class ReferenceVisitor < Visitor
13
+ # Creates a new ReferenceVisitor on domain reference attributes.
14
+ #
15
+ # The required selector block given to this initializer determines which attributes to
16
+ # visit. The references to visit next thus consist of the current domain object's selector
17
+ # attributes' values. If the :filter option is set, then the given filter block is applied
18
+ # to the selected attribute references to restrict which domain objects will be visited.
19
+ #
20
+ # @param opts (see Visitor#initialize)
21
+ # @option opts [Proc] :filter an optional filter on the references to visit
22
+ # @yield [obj] returns the {AttributeEnumerator} of attributes to visit next from the
23
+ # current domain object
24
+ # @yieldparam [Resource] obj the current domain object
25
+ def initialize(opts=nil, &selector)
26
+ Jinx.fail(ArgumentError, "Reference visitor missing domain reference selector") unless block_given?
27
+ # the property selector
28
+ @flt_sel = selector
29
+ # the reference filter
30
+ flt = Options.get(:filter, opts)
31
+ # Initialize the Visitor with a reference enumerator which selects the reference
32
+ # attributes and applies the optional filter if necessary.
33
+ @ref_enums = {}
34
+ super do |ref|
35
+ # the reference property filter
36
+ attrs = attributes_to_visit(ref)
37
+ if attrs then
38
+ logger.debug { "#{qp} visiting #{ref} attributes #{attrs.pp_s(:single_line)}..." } if @verbose
39
+ # an enumerator on the reference properties
40
+ enum = ReferenceEnumerator.new(ref, attrs.properties)
41
+ # If there is a reference filter, then apply it to the enum references.
42
+ flt ? enum.filter(&flt) : enum
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # @param [Resource] obj the visiting object
50
+ # @return [Propertied::Filter] the attributes to visit
51
+ def attributes_to_visit(obj)
52
+ @flt_sel.call(obj)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ require 'jinx/helpers/uniquifier'
2
+
3
+ module Jinx
4
+ # The Unique mix-in makes values unique within the scope of a Resource class.
5
+ module Unique
6
+ # Makes the given String value unique in the context of this object's class.
7
+ # @return nil if value is nil
8
+ # Raises TypeError if value is neither nil nor a String.
9
+ def uniquify_value(value)
10
+ unless String === value or value.nil? then
11
+ Jinx.fail(TypeError, "Cannot uniquify #{qp} non-String value #{value}")
12
+ end
13
+ Uniquifier.instance.uniquify(self, value)
14
+ end
15
+
16
+ # Makes the secondary key unique by replacing each String key attribute value
17
+ # with a unique value.
18
+ def uniquify
19
+ uniquify_attributes(self.class.secondary_key_attributes)
20
+ uniquify_attributes(self.class.alternate_key_attributes)
21
+ end
22
+
23
+ # Makes the given attribute values unique by replacing each String value
24
+ # with a unique value.
25
+ def uniquify_attributes(attributes)
26
+ attributes.each do |ka|
27
+ oldval = send(ka)
28
+ next unless String === oldval
29
+ newval = uniquify_value(oldval)
30
+ set_property_value(ka, newval)
31
+ logger.debug { "Reset #{qp} #{ka} from #{oldval} to unique value #{newval}." }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Jinx
2
+ VERSION = "2.1.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ module Model
4
+ describe 'Defaults' do
5
+ before(:all) do
6
+ Model.definitions BASE, DEFAULTS
7
+ end
8
+
9
+ it "should recognize the property default" do
10
+ Child.defaults[:cardinal].should be 1
11
+ end
12
+
13
+ it "should set the default property value" do
14
+ c = Child.new
15
+ c.add_defaults
16
+ c.cardinal.should be 1
17
+ end
18
+
19
+ it "should not reset a property value to the default" do
20
+ c = Child.new(:cardinal => 2)
21
+ c.add_defaults
22
+ c.cardinal.should be 2
23
+ end
24
+
25
+ private
26
+
27
+ # The defaults fixture model definitions.
28
+ DEFAULTS = File.dirname(__FILE__) + '/definitions/model/defaults'
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Child
3
+ property :friends, :alias => :pals
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Child
3
+ property :name, :secondary_key
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class DomainObject
3
+ property :identifier, :primary_key
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Independent
3
+ property :others, :type => self
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Child
3
+ property :cardinal, :default => 1
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Child
3
+ property :dependent, :dependent
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Model
2
+ class Parent
3
+ property :children, :dependent
4
+ property :dependent, :dependent
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Child
3
+ property :parent, :inverse => :children
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Independent
3
+ property :others, :inverse => :others
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Model
2
+ class Parent
3
+ property :spouse, :inverse => :spouse
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Model
2
+ class Child
3
+ property :flag, :mandatory
4
+ property :dependent, :autogenerated
5
+ end
6
+ end
@@ -0,0 +1,47 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ module Model
4
+ describe 'Dependency' do
5
+ before(:all) do
6
+ Model.definitions BASE, DEPENDENCY
7
+ end
8
+
9
+ context '1:1' do
10
+ it "should capture the dependents" do
11
+ d = Dependent.new
12
+ c = Child.new(:dependent => d)
13
+ c.dependents.should include d
14
+ end
15
+ end
16
+
17
+ context '1:N' do
18
+ it "should set the inverses" do
19
+ Child.property(:parent).inverse.should be :children
20
+ Parent.property(:children).inverse.should be :parent
21
+ end
22
+
23
+ it "should capture the dependents" do
24
+ p = Parent.new
25
+ c = Child.new(:parent => p)
26
+ p.dependents.should include c
27
+ end
28
+
29
+ it "should fail to validate a missing owner" do
30
+ c = Child.new
31
+ expect { c.validate }.to raise_error(Jinx::ValidationError)
32
+ end
33
+
34
+ it "should validate an existing owner" do
35
+ p = Parent.new
36
+ c = Child.new(:parent => p, :name => 'Sam')
37
+ expect { c.validate }.to_not raise_error
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # The dependency fixture model definitions.
44
+ # @private
45
+ DEPENDENCY = File.dirname(__FILE__) + '/definitions/model/dependency'
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+ require File.dirname(__FILE__) + '/../examples/family/lib/family'
3
+
4
+ module Family
5
+ describe DomainObject do
6
+ it "should set the primary key attribute" do
7
+ DomainObject.primary_key_attributes.should == [:identifier]
8
+ end
9
+ end
10
+
11
+ describe Address do
12
+ it "should have an attribute => value constructor" do
13
+ Address.new(:state => 'OR').state.should == 'OR'
14
+ end
15
+
16
+ it "should recognize an alias" do
17
+ a = Address.new(:postal_code => '95111')
18
+ a.zip.should == a.postal_code
19
+ end
20
+ end
21
+
22
+ describe Parent do
23
+ it "should inherit the primary key" do
24
+ Parent.primary_key_attributes.should be DomainObject.primary_key_attributes
25
+ end
26
+
27
+ it "should have a secondary key" do
28
+ Parent.secondary_key_attributes.should == [:ssn]
29
+ end
30
+
31
+ it "should have a name property" do
32
+ Parent.property_defined?(:name).should be true
33
+ end
34
+
35
+ it "should have a children dependent" do
36
+ Parent.property(:children).dependent?.should be true
37
+ end
38
+ end
39
+
40
+ describe Child do
41
+ it "should have a parents owner" do
42
+ Child.property(:parents).owner?.should be true
43
+ end
44
+
45
+ it "should add itself to the household inverse" do
46
+ h = Household.new
47
+ c = Child.new(:household => h)
48
+ h.members.should include c
49
+ end
50
+
51
+ it "should not add itself to the parents inverse" do
52
+ p = Parent.new
53
+ c = Child.new(:parents => [p])
54
+ p.children.should_not include c
55
+ end
56
+ end
57
+
58
+ describe Household do
59
+ it "should have a dependent address" do
60
+ a = Address.new
61
+ Household.new(:address => a).dependents.should include a
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ module Model
4
+ describe 'Inverse' do
5
+ before(:all) do
6
+ Model.definitions BASE, INVERSE
7
+ end
8
+
9
+ context '1:1' do
10
+ it "should set the inverse" do
11
+ Parent.property(:spouse).inverse.should == :spouse
12
+ end
13
+
14
+ it "should set the target inverse type back to self" do
15
+ Parent.property(:spouse).inverse.should == :spouse
16
+ end
17
+
18
+ it "should enforce inverse integrity" do
19
+ m = Parent.new
20
+ f = Parent.new(:spouse => m)
21
+ m.spouse.should be f
22
+ end
23
+ end
24
+
25
+ context '1:N' do
26
+ it "should set the inverse" do
27
+ Child.property(:parent).inverse.should == :children
28
+ end
29
+
30
+ it "should set the target inverse type back to self" do
31
+ Parent.property(:children).inverse.should == :parent
32
+ end
33
+
34
+ it "should enforce inverse integrity" do
35
+ p = Parent.new
36
+ c = Child.new(:parent => p)
37
+ p.children.should include c
38
+ end
39
+ end
40
+
41
+ context 'M:N' do
42
+ it "should set the inverse" do
43
+ Independent.property(:others).inverse.should == :others
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # The inverse fixture model definitions.
50
+ # @private
51
+ INVERSE = File.dirname(__FILE__) + '/definitions/model/inverse'
52
+ end
53
+ end