caruby-core 1.4.1

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