caruby-core 1.4.1

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 (86) hide show
  1. data/History.txt +4 -0
  2. data/LEGAL +5 -0
  3. data/LICENSE +22 -0
  4. data/README.md +51 -0
  5. data/doc/website/css/site.css +1 -5
  6. data/doc/website/images/avatar.png +0 -0
  7. data/doc/website/images/favicon.ico +0 -0
  8. data/doc/website/images/logo.png +0 -0
  9. data/doc/website/index.html +82 -0
  10. data/doc/website/install.html +87 -0
  11. data/doc/website/quick_start.html +87 -0
  12. data/doc/website/tissue.html +85 -0
  13. data/doc/website/uom.html +10 -0
  14. data/lib/caruby.rb +3 -0
  15. data/lib/caruby/active_support/README.txt +2 -0
  16. data/lib/caruby/active_support/core_ext/string.rb +7 -0
  17. data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
  18. data/lib/caruby/active_support/inflections.rb +55 -0
  19. data/lib/caruby/active_support/inflector.rb +398 -0
  20. data/lib/caruby/cli/application.rb +36 -0
  21. data/lib/caruby/cli/command.rb +169 -0
  22. data/lib/caruby/csv/csv_mapper.rb +157 -0
  23. data/lib/caruby/csv/csvio.rb +185 -0
  24. data/lib/caruby/database.rb +252 -0
  25. data/lib/caruby/database/fetched_matcher.rb +66 -0
  26. data/lib/caruby/database/persistable.rb +432 -0
  27. data/lib/caruby/database/persistence_service.rb +162 -0
  28. data/lib/caruby/database/reader.rb +599 -0
  29. data/lib/caruby/database/saved_merger.rb +131 -0
  30. data/lib/caruby/database/search_template_builder.rb +59 -0
  31. data/lib/caruby/database/sql_executor.rb +75 -0
  32. data/lib/caruby/database/store_template_builder.rb +200 -0
  33. data/lib/caruby/database/writer.rb +469 -0
  34. data/lib/caruby/domain/annotatable.rb +25 -0
  35. data/lib/caruby/domain/annotation.rb +23 -0
  36. data/lib/caruby/domain/attribute_metadata.rb +447 -0
  37. data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
  38. data/lib/caruby/domain/merge.rb +91 -0
  39. data/lib/caruby/domain/properties.rb +95 -0
  40. data/lib/caruby/domain/reference_visitor.rb +289 -0
  41. data/lib/caruby/domain/resource_attributes.rb +528 -0
  42. data/lib/caruby/domain/resource_dependency.rb +205 -0
  43. data/lib/caruby/domain/resource_introspection.rb +159 -0
  44. data/lib/caruby/domain/resource_metadata.rb +117 -0
  45. data/lib/caruby/domain/resource_module.rb +285 -0
  46. data/lib/caruby/domain/uniquify.rb +38 -0
  47. data/lib/caruby/import/annotatable_class.rb +28 -0
  48. data/lib/caruby/import/annotation_class.rb +27 -0
  49. data/lib/caruby/import/annotation_module.rb +67 -0
  50. data/lib/caruby/import/java.rb +338 -0
  51. data/lib/caruby/migration/migratable.rb +167 -0
  52. data/lib/caruby/migration/migrator.rb +533 -0
  53. data/lib/caruby/migration/resource.rb +8 -0
  54. data/lib/caruby/migration/resource_module.rb +11 -0
  55. data/lib/caruby/migration/uniquify.rb +20 -0
  56. data/lib/caruby/resource.rb +969 -0
  57. data/lib/caruby/util/attribute_path.rb +46 -0
  58. data/lib/caruby/util/cache.rb +53 -0
  59. data/lib/caruby/util/class.rb +99 -0
  60. data/lib/caruby/util/collection.rb +1053 -0
  61. data/lib/caruby/util/controlled_value.rb +35 -0
  62. data/lib/caruby/util/coordinate.rb +75 -0
  63. data/lib/caruby/util/domain_extent.rb +49 -0
  64. data/lib/caruby/util/file_separator.rb +65 -0
  65. data/lib/caruby/util/inflector.rb +20 -0
  66. data/lib/caruby/util/log.rb +95 -0
  67. data/lib/caruby/util/math.rb +12 -0
  68. data/lib/caruby/util/merge.rb +59 -0
  69. data/lib/caruby/util/module.rb +34 -0
  70. data/lib/caruby/util/options.rb +92 -0
  71. data/lib/caruby/util/partial_order.rb +36 -0
  72. data/lib/caruby/util/person.rb +119 -0
  73. data/lib/caruby/util/pretty_print.rb +184 -0
  74. data/lib/caruby/util/properties.rb +112 -0
  75. data/lib/caruby/util/stopwatch.rb +66 -0
  76. data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
  77. data/lib/caruby/util/transitive_closure.rb +45 -0
  78. data/lib/caruby/util/tree.rb +48 -0
  79. data/lib/caruby/util/trie.rb +37 -0
  80. data/lib/caruby/util/uniquifier.rb +30 -0
  81. data/lib/caruby/util/validation.rb +48 -0
  82. data/lib/caruby/util/version.rb +56 -0
  83. data/lib/caruby/util/visitor.rb +351 -0
  84. data/lib/caruby/util/weak_hash.rb +36 -0
  85. data/lib/caruby/version.rb +3 -0
  86. metadata +186 -0
@@ -0,0 +1,35 @@
1
+ require 'set'
2
+
3
+ module CaRuby
4
+ class ControlledValue
5
+ attr_accessor :value, :parent
6
+
7
+ attr_reader :children
8
+
9
+ # Creates a new ControlledValue with the given String value and ControlledValue parent.
10
+ # If parent is not nil, then the new CV is added to the parent's children.
11
+ def initialize(value=nil, parent=nil)
12
+ @value = value
13
+ self.parent = parent
14
+ @children = Set.new
15
+ end
16
+
17
+ def descendants
18
+ children + children.map { |child| child.descendants.to_a }.flatten
19
+ end
20
+
21
+ def to_s
22
+ value
23
+ end
24
+
25
+ private
26
+
27
+ # Sets this CV's parent and adds this CV to the parent's children if necessary.
28
+ def parent=(parent)
29
+ @parent.children.delete(self) if @parent
30
+ @parent = parent
31
+ parent.children << self if parent
32
+ parent
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,75 @@
1
+ require 'generator'
2
+
3
+ # A Coordinate is a convenience Array wrapper class with aliased #x, #y and {#z} dimensions.
4
+ class Coordinate < Array
5
+ include Comparable
6
+
7
+ # @param [{Integer}] scalars the dimension coordinate values
8
+ # @return a new Coordinate at the given scalars
9
+ def initialize(*scalars)
10
+ super(scalars)
11
+ end
12
+
13
+ # @return [Integer] the first dimension
14
+ def x
15
+ self[0]
16
+ end
17
+
18
+ # @param [Integer] the first dimension value
19
+ def x=(value)
20
+ self[0] = value
21
+ end
22
+
23
+ # @return [Integer, nil] the second dimension
24
+ def y
25
+ self[1]
26
+ end
27
+
28
+ # @param [Integer] the second dimension value
29
+ def y=(value)
30
+ self[1] = value
31
+ end
32
+
33
+ # @return [Integer, nil] the third dimension
34
+ def z
35
+ self[2]
36
+ end
37
+
38
+ # @param [Integer] the third dimension value
39
+ def z=(value)
40
+ self[2] = value
41
+ end
42
+
43
+ # @return [Boolean] whether other is a Coordinate and has the same content as this Coordinate
44
+ def ==(other)
45
+ super rescue false
46
+ end
47
+
48
+ # Returns the comparison of the highest dimension which differs from the other
49
+ # coordinate, or zero if all dimensions are the same. This comparator sorts
50
+ # coordinates in z-y-x order.
51
+ # @example
52
+ # Coordinate.new(2, 1) < Coordinate.new(1, 2) #=> true
53
+ # @return [Integer] the high-to-low dimension comparison
54
+ # @raise [ArgumentError] if this Coordinate dimension size Coordinate differs from that
55
+ # of the other Dimension or any of the dimension values are nil
56
+ # @raise [TypeError] if other is not a Coordinate
57
+ def <=>(other)
58
+ return true if equal?(other)
59
+ raise TypeError.new("Can't compare #{self} with #{other} since it is not a Coordinate") unless Coordinate === other
60
+ raise ArgumentError.new("Can't compare #{self} with #{other} since it has a different dimension count") unless size == other.size
61
+ SyncEnumerator.new(self.reverse, other.reverse).each_with_index do |pair, index|
62
+ dim = pair.first
63
+ odim = pair.last
64
+ raise ArgumentError.new("Can't compare #{self} with missing dimension #{index} to #{other}") unless dim
65
+ raise ArgumentError.new("Can't compare #{self} to #{other} with missing dimension #{index}") unless odim
66
+ cmp = dim <=> odim
67
+ return cmp unless cmp.zero?
68
+ end
69
+ 0
70
+ end
71
+
72
+ def to_s
73
+ inspect
74
+ end
75
+ end
@@ -0,0 +1,49 @@
1
+ require 'caruby/util/collection'
2
+ require 'caruby/util/validation'
3
+
4
+ # A DomainExtent contains class-specific key => object associations.
5
+ # The objects are created on demand and accessed by the get method.
6
+ #
7
+ # @example
8
+ # DomainExtent.new { |klass, key| key.to_s + klass.name }.get(String, 'a') #=> aString
9
+ class DomainExtent < LazyHash
10
+ include Validation
11
+
12
+ # Creates a new DomainExtent. The block passed to the constructor
13
+ # is a factory to create an object on demand, with arguments
14
+ # the target class and the target key. The default block is empty.
15
+ def initialize
16
+ return initialize {} unless block_given?
17
+ super { |klass| LazyHash.new { |key| yield klass, key } }
18
+ end
19
+
20
+ # Sets the factory used to create an instance of the specified class.
21
+ # The factory is called to create a new instance when a get operation
22
+ # does not yet have a key => instance association.
23
+ #
24
+ # The factory accepts a single argument, the instance key, e.g.
25
+ # set_factory(MyClass) { |key| MyClass.new(key) }
26
+ def set_factory(klass, &factory)
27
+ validate_type(klass => Class)
28
+ # the current instances, if any
29
+ instances = fetch(klass) if has_key?(klass)
30
+ # make the key => instance class extent map
31
+ # the instance creation factory is
32
+ class_extent = LazyHash.new { |key| yield key }
33
+ # copy existing instances if necessary
34
+ class_extent.merge!(instances) if instances
35
+ # add the class => class extent association
36
+ self[klass] = class_extent
37
+ end
38
+
39
+ # Returns the domain instance of the given class for the given key.
40
+ # If there is nois no entry for key and the factory is set for the class,
41
+ # then a new object is created on demand.
42
+ def get(klass, key)
43
+ raise RuntimeError.new("Invalid target class: #{klass}") unless klass.is_a?(Class)
44
+ raise RuntimeError.new("Missing target key value") if key.nil?
45
+ # the class extent hash is created on demand if necessary.
46
+ # the instance is created on demand if there is a factory for the class.
47
+ self[klass][key]
48
+ end
49
+ end
@@ -0,0 +1,65 @@
1
+ require 'caruby/util/class'
2
+
3
+ class File
4
+ [:gets, :readline, :readlines].each do |attr|
5
+ # Overrides the standard method to infer a line separator from the input
6
+ # if the separator argument is the default.
7
+ redefine_method(attr) do |original|
8
+ lambda do |*params|
9
+ sep = params.first || default_line_separator
10
+ send(original, sep)
11
+ end
12
+ end
13
+
14
+ # Overrides the standard {#each} method to infer a line separator from the input
15
+ # if the separator argument is not given.
16
+ def each(separator=nil)
17
+ while (line = gets(separator)) do
18
+ yield line
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Returns the default line separator. The logic is borrowed from the FasterCVS gem.
26
+ def default_line_separator
27
+ @def_line_sep ||= infer_line_separator
28
+ end
29
+
30
+ def infer_line_separator
31
+ type_line_separator or content_line_separator or $/
32
+ end
33
+
34
+ def type_line_separator
35
+ if [ARGF, STDIN, STDOUT, STDERR].include?(self) or
36
+ (defined?(Zlib) and self.class == Zlib::GzipWriter) then
37
+ return $/
38
+ end
39
+ end
40
+
41
+ def content_line_separator
42
+ begin
43
+ saved_pos = pos # remember where we were
44
+ # read a chunk until a separator is discovered
45
+ sep = discover_line_separator
46
+ # tricky seek() clone to work around GzipReader's lack of seek()
47
+ rewind
48
+ # reset back to the remembered position
49
+ chunks, residual = saved_pos.divmod(1024)
50
+ chunks.times { read(1024) }
51
+ read(residual)
52
+ rescue IOError # stream not opened for reading
53
+ end
54
+ sep
55
+ end
56
+
57
+ def discover_line_separator
58
+ # read a chunk until a separator is discovered
59
+ while (sample = read(1024)) do
60
+ sample += read(1) if sample[-1, 1] == "\r" and not eof?
61
+ # try to find a standard separator
62
+ return $& if sample =~ /\r\n?|\n/
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ require 'caruby/active_support/inflector'
2
+
3
+ class String
4
+ # @param [Numeric] quantity the amount qualifier
5
+ # @return this String qualified by a plural if the quantity is not 1
6
+ # @example
7
+ # "rose".quantify(3) #=> "roses"
8
+ # "rose".quantify(1 #=> "rose"
9
+ def quantify(quantity)
10
+ raise ArgumentError.new("Missing quantity argument") if quantity.nil?
11
+ "#{quantity} #{quantity == 1 ? self : pluralize}"
12
+ end
13
+
14
+ # @return this String with the first letter capitalized and other letters preserved.
15
+ # @example
16
+ # "rosesAreRed".capitalize_first #=> "RosesAreRed"
17
+ def capitalize_first
18
+ sub(/(?:^)(.)/) { $1.upcase }
19
+ end
20
+ end
@@ -0,0 +1,95 @@
1
+ require 'logger'
2
+ require 'singleton'
3
+ require 'ftools'
4
+ require 'caruby/util/collection'
5
+
6
+ # @return [CaRuby::Logger] the global logger
7
+ def logger
8
+ CaRuby::Log::instance.logger
9
+ end
10
+
11
+ module CaRuby
12
+ # Extends the standard Logger to format multi-line messages on separate lines.
13
+ class MultilineLogger < Logger
14
+ # @see Logger#initialize
15
+ def initialize(*args)
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ # Writes msg to the log device. Each line in msg is formatted separately.
22
+ #
23
+ # @param (see Logger#format_message)
24
+ # @return (see Logger#format_message)
25
+ def format_message(severity, datetime, progname, msg)
26
+ if String === msg then
27
+ msg.inject('') { |s, line| s << super(severity, datetime, progname, line.chomp) }
28
+ else
29
+ super
30
+ end
31
+ end
32
+ end
33
+
34
+ # Wraps a standard global Logger.
35
+ class Log
36
+ include Singleton
37
+
38
+ # Opens the log.
39
+ #
40
+ # @param [String, IO, nil] file_or_dev the log file or device (default STDOUT)
41
+ # @param [Hash, nil] the Logger::LogDevice options
42
+ # @return [Logger] the logger
43
+ def open(file_or_dev=nil, options=nil)
44
+ dev = file_or_dev || default_log_file
45
+ return @logger if same_file?(dev, @dev)
46
+ # close the previous log file, if necessary
47
+ @logger.close if @logger
48
+ if String === dev then File.makedirs(File.dirname(dev)) end
49
+ # default is 4-file rotation @ 16MB each
50
+ shift_age = Options.get(:shift_age, options, 4)
51
+ shift_size = Options.get(:shift_size, options, 16 * 1048576)
52
+ @logger = MultilineLogger.new(dev, shift_age, shift_size)
53
+ @logger.level = Options.get(:debug, options) ? Logger::DEBUG : Logger::INFO
54
+ @logger.info('============================================')
55
+ @logger.info('Logging started.')
56
+ @dev = dev
57
+ @logger
58
+ end
59
+
60
+ def close
61
+ @logger.close
62
+ @logger = nil
63
+ end
64
+
65
+ # Returns the logger.
66
+ def logger
67
+ @logger ||= open
68
+ end
69
+
70
+ # @return [String, nil] the log file, or nil if the log was opened on an IO rather
71
+ # than a String
72
+ def file
73
+ @dev if String === @dev
74
+ end
75
+
76
+ private
77
+
78
+ def same_file?(f1, f2)
79
+ f1 == f2 or (String === f2 and String === f1 and File.expand_path(f1) == File.expand_path(f2))
80
+ end
81
+
82
+ def default_log_file
83
+ log_ndx = ARGV.index("--log") || ARGV.index("-l")
84
+ if log_ndx then
85
+ ARGV[log_ndx + 1]
86
+ elsif ENV.has_key?("CA_LOG") then
87
+ ENV["CA_LOG"]
88
+ elsif defined?(DEF_LOG_FILE)
89
+ DEF_LOG_FILE
90
+ else
91
+ 'log/caruby.log'
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,12 @@
1
+ # Extends the Numeric class with max and min methods.
2
+ class Numeric
3
+ # Returns the minimum of this Numeric and the other Numerics.
4
+ def min(*others)
5
+ others.inject(self) { |min, other| other < min ? other : min }
6
+ end
7
+
8
+ # Returns the minimum of this Numeric and the other Numerics.
9
+ def max(*others)
10
+ others.inject(self) { |max, other| other > max ? other : max }
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ require 'caruby/util/options'
2
+ require 'caruby/util/collection'
3
+
4
+ class Hash
5
+ # Returns a new hash which merges the other hash with this hash.
6
+ #
7
+ # Supported options include the following:
8
+ # * :deep - merge values which match on the key.
9
+ # If the :deep option is set, and a key matches both this hash and the other hash
10
+ # on hash values, then the other hash's value is recursively merged into this Hash's
11
+ # value using the non-destructive {#merge} method with the deep option set.
12
+ # If a block is given to this method, then the block is passed to the value merge.
13
+ #
14
+ # @example
15
+ # {:a => [1], :b => [2]}.merge({:b => [3]}, :deep) #=> {:a => [1], :b => [2, 3]}
16
+ # {:a => {:b => [1]}}.merge({:a => {:b => [2]}, :c => 3}, :deep) #=> {:a => {:b => [1, 2]}, :c => 3}
17
+ def merge(other, options=nil, &block)
18
+ dup.merge!(other, options, &block)
19
+ end
20
+
21
+ alias :base__merge! :merge!
22
+ private :base__merge!
23
+
24
+ # Merges the other hash into this hash and returns this modified hash.
25
+ #
26
+ # @see #merge the options and block description
27
+ def merge!(other, options=nil, &block)
28
+ # use the standard Hash merge unless the :deep option is set
29
+ return base__merge!(other, &block) unless Options.get(:deep, options)
30
+ # merge the other entries:
31
+ # if the hash value is a hash, then call merge on that hash value.
32
+ # otherwise, if the hash value understands merge, then call that method.
33
+ # otherwise, if there is a block, then call the block.
34
+ # otherwise, set the the hash value to the other value.
35
+ base__merge!(other) do |key, oldval, newval|
36
+ if Hash === oldval then
37
+ oldval.merge(newval, options, &block)
38
+ elsif oldval.respond_to?(:merge)
39
+ oldval.merge(newval, &block)
40
+ elsif block_given? then
41
+ yield(key, oldval, newval)
42
+ else
43
+ newval
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ class Array
50
+ # Adds the elements in the other Enumerable which are not already included in this Array.
51
+ # Returns this modified Array.
52
+ def merge(other)
53
+ # incompatible merge argument is allowed but ignored
54
+ self unless Enumerable === other
55
+ # concatenate the members of other not in self
56
+ unique = other.to_a - self
57
+ concat(unique)
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ class Module
2
+ # Returns the class or module with name in the parent module.
3
+ # If parent is nil, then name is looked up in the global context.
4
+ # Otherwise, this method returns {#module_with_name}.
5
+ def self.module_with_name(parent, name)
6
+ return parent.module_with_name(name) if parent
7
+ begin
8
+ constant = eval(name)
9
+ rescue Exception
10
+ return
11
+ end
12
+ constant if constant.is_a?(Module)
13
+ end
14
+
15
+ # Returns the class or module with name in this module.
16
+ # name can qualified by parent modules, e.g. +MyApp::Person+.
17
+ # If name cannot be resolved as a Module, then this method returns nil.
18
+ def module_with_name(name)
19
+ begin
20
+ constant = name.split('::').inject(parent) { |parent, name| parent.const_get(name) }
21
+ rescue Exception
22
+ return
23
+ end
24
+ constant if constant.is_a?(Module)
25
+ end
26
+
27
+ # Returns the class with name in this module.
28
+ # name can qualified by parent modules, e.g. +MyApp::Person+.
29
+ # If name cannot be resolved as a Class, then this method returns nil.
30
+ def class_with_name
31
+ mod = module_with_name
32
+ mod if mod.is_a?(Class)
33
+ end
34
+ end