gorillib 0.1.11 → 0.4.0pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -2
  3. data/.yardopts +9 -0
  4. data/{CHANGELOG.textile → CHANGELOG.md} +35 -9
  5. data/Gemfile +21 -14
  6. data/Guardfile +19 -0
  7. data/{LICENSE.textile → LICENSE.md} +43 -29
  8. data/README.md +47 -52
  9. data/Rakefile +31 -30
  10. data/TODO.md +32 -0
  11. data/VERSION +1 -1
  12. data/examples/builder/ironfan.rb +133 -0
  13. data/examples/model/simple.rb +17 -0
  14. data/gorillib.gemspec +106 -86
  15. data/lib/alt/kernel/call_stack.rb +56 -0
  16. data/lib/gorillib/array/wrap.rb +53 -0
  17. data/lib/gorillib/base.rb +3 -3
  18. data/lib/gorillib/builder/field.rb +5 -0
  19. data/lib/gorillib/builder.rb +260 -0
  20. data/lib/gorillib/collection/has_collection.rb +31 -0
  21. data/lib/gorillib/collection.rb +129 -0
  22. data/lib/gorillib/configurable.rb +28 -0
  23. data/lib/gorillib/datetime/{flat.rb → to_flat.rb} +0 -0
  24. data/lib/gorillib/exception/confidence.rb +17 -0
  25. data/lib/gorillib/exception/raisers.rb +78 -0
  26. data/lib/gorillib/hash/mash.rb +202 -0
  27. data/lib/gorillib/hashlike/slice.rb +53 -19
  28. data/lib/gorillib/hashlike.rb +5 -3
  29. data/lib/gorillib/io/system_helpers.rb +30 -0
  30. data/lib/gorillib/logger/log.rb +18 -0
  31. data/lib/gorillib/metaprogramming/concern.rb +124 -0
  32. data/lib/gorillib/model/active_model_conversion.rb +68 -0
  33. data/lib/gorillib/model/active_model_naming.rb +87 -0
  34. data/lib/gorillib/model/active_model_shim.rb +33 -0
  35. data/lib/gorillib/model/base.rb +341 -0
  36. data/lib/gorillib/model/defaults.rb +71 -0
  37. data/lib/gorillib/model/errors.rb +14 -0
  38. data/lib/gorillib/model/factories.rb +372 -0
  39. data/lib/gorillib/model/field.rb +146 -0
  40. data/lib/gorillib/model/named_schema.rb +53 -0
  41. data/lib/gorillib/{struct/hashlike_iteration.rb → model/overlay.rb} +0 -0
  42. data/lib/gorillib/model/record_schema.rb +9 -0
  43. data/lib/gorillib/model/serialization.rb +23 -0
  44. data/lib/gorillib/model/validate.rb +22 -0
  45. data/lib/gorillib/model.rb +23 -0
  46. data/lib/gorillib/pathname.rb +78 -0
  47. data/lib/gorillib/{serialization.rb → serialization/to_wire.rb} +0 -0
  48. data/lib/gorillib/some.rb +11 -9
  49. data/lib/gorillib/string/constantize.rb +21 -14
  50. data/lib/gorillib/string/inflections.rb +6 -76
  51. data/lib/gorillib/string/inflector.rb +192 -0
  52. data/lib/gorillib/string/simple_inflector.rb +267 -0
  53. data/lib/gorillib/type/extended.rb +52 -0
  54. data/lib/gorillib/utils/capture_output.rb +28 -0
  55. data/lib/gorillib/utils/console.rb +131 -0
  56. data/lib/gorillib/utils/nuke_constants.rb +9 -0
  57. data/lib/gorillib/utils/stub_module.rb +33 -0
  58. data/spec/examples/builder/ironfan_spec.rb +37 -0
  59. data/spec/extlib/hash_spec.rb +64 -0
  60. data/spec/extlib/mash_spec.rb +312 -0
  61. data/spec/{array → gorillib/array}/compact_blank_spec.rb +2 -2
  62. data/spec/{array → gorillib/array}/extract_options_spec.rb +2 -2
  63. data/spec/gorillib/builder_spec.rb +187 -0
  64. data/spec/gorillib/collection_spec.rb +20 -0
  65. data/spec/gorillib/configurable_spec.rb +62 -0
  66. data/spec/{datetime → gorillib/datetime}/parse_spec.rb +3 -3
  67. data/spec/{datetime/flat_spec.rb → gorillib/datetime/to_flat_spec.rb} +4 -4
  68. data/spec/{enumerable → gorillib/enumerable}/sum_spec.rb +5 -5
  69. data/spec/gorillib/exception/raisers_spec.rb +60 -0
  70. data/spec/{hash → gorillib/hash}/compact_spec.rb +2 -2
  71. data/spec/{hash → gorillib/hash}/deep_compact_spec.rb +3 -3
  72. data/spec/{hash → gorillib/hash}/deep_merge_spec.rb +2 -2
  73. data/spec/{hash → gorillib/hash}/keys_spec.rb +2 -2
  74. data/spec/{hash → gorillib/hash}/reverse_merge_spec.rb +2 -2
  75. data/spec/{hash → gorillib/hash}/slice_spec.rb +2 -2
  76. data/spec/{hash → gorillib/hash}/zip_spec.rb +2 -2
  77. data/spec/{hashlike → gorillib/hashlike}/behave_same_as_hash_spec.rb +6 -3
  78. data/spec/{hashlike → gorillib/hashlike}/deep_hash_spec.rb +2 -2
  79. data/spec/{hashlike → gorillib/hashlike}/hashlike_behavior_spec.rb +32 -30
  80. data/spec/{hashlike → gorillib/hashlike}/hashlike_via_accessors_spec.rb +3 -3
  81. data/spec/{hashlike_spec.rb → gorillib/hashlike_spec.rb} +3 -3
  82. data/spec/{logger → gorillib/logger}/log_spec.rb +2 -2
  83. data/spec/{metaprogramming → gorillib/metaprogramming}/aliasing_spec.rb +3 -3
  84. data/spec/{metaprogramming → gorillib/metaprogramming}/class_attribute_spec.rb +3 -3
  85. data/spec/{metaprogramming → gorillib/metaprogramming}/delegation_spec.rb +3 -3
  86. data/spec/{metaprogramming → gorillib/metaprogramming}/singleton_class_spec.rb +3 -3
  87. data/spec/gorillib/model/record/defaults_spec.rb +108 -0
  88. data/spec/gorillib/model/record/factories_spec.rb +321 -0
  89. data/spec/gorillib/model/record/overlay_spec.rb +46 -0
  90. data/spec/gorillib/model/serialization_spec.rb +48 -0
  91. data/spec/gorillib/model_spec.rb +281 -0
  92. data/spec/{numeric → gorillib/numeric}/clamp_spec.rb +2 -2
  93. data/spec/{object → gorillib/object}/blank_spec.rb +2 -2
  94. data/spec/{object → gorillib/object}/try_dup_spec.rb +2 -2
  95. data/spec/{object → gorillib/object}/try_spec.rb +3 -2
  96. data/spec/gorillib/pathname_spec.rb +114 -0
  97. data/spec/{string → gorillib/string}/constantize_spec.rb +2 -2
  98. data/spec/{string → gorillib/string}/human_spec.rb +2 -2
  99. data/spec/{string → gorillib/string}/inflections_spec.rb +4 -3
  100. data/spec/{string → gorillib/string}/inflector_test_cases.rb +0 -0
  101. data/spec/{string → gorillib/string}/truncate_spec.rb +4 -10
  102. data/spec/gorillib/type/extended_spec.rb +120 -0
  103. data/spec/gorillib/utils/capture_output_spec.rb +71 -0
  104. data/spec/spec_helper.rb +8 -11
  105. data/spec/support/gorillib_test_helpers.rb +66 -0
  106. data/spec/support/hashlike_fuzzing_helper.rb +31 -33
  107. data/spec/support/hashlike_helper.rb +3 -3
  108. data/spec/support/model_test_helpers.rb +81 -0
  109. data/spec/support/shared_examples/included_module.rb +20 -0
  110. metadata +177 -158
  111. data/lib/gorillib/array/average.rb +0 -13
  112. data/lib/gorillib/array/sorted_median.rb +0 -11
  113. data/lib/gorillib/array/sorted_percentile.rb +0 -11
  114. data/lib/gorillib/array/sorted_sample.rb +0 -12
  115. data/lib/gorillib/dsl_object.rb +0 -64
  116. data/lib/gorillib/hash/indifferent_access.rb +0 -207
  117. data/lib/gorillib/hash/tree_merge.rb +0 -4
  118. data/lib/gorillib/hashlike/tree_merge.rb +0 -49
  119. data/lib/gorillib/metaprogramming/cattr_accessor.rb +0 -79
  120. data/lib/gorillib/metaprogramming/mattr_accessor.rb +0 -61
  121. data/lib/gorillib/receiver/active_model_shim.rb +0 -32
  122. data/lib/gorillib/receiver/acts_as_hash.rb +0 -195
  123. data/lib/gorillib/receiver/acts_as_loadable.rb +0 -42
  124. data/lib/gorillib/receiver/locale/en.yml +0 -27
  125. data/lib/gorillib/receiver/tree_diff.rb +0 -74
  126. data/lib/gorillib/receiver/validations.rb +0 -30
  127. data/lib/gorillib/receiver.rb +0 -402
  128. data/lib/gorillib/receiver_model.rb +0 -21
  129. data/lib/gorillib/struct/acts_as_hash.rb +0 -108
  130. data/notes/fancy_hashes_and_receivers.textile +0 -120
  131. data/notes/hash_rdocs.textile +0 -97
  132. data/spec/array/average_spec.rb +0 -24
  133. data/spec/array/sorted_median_spec.rb +0 -18
  134. data/spec/array/sorted_percentile_spec.rb +0 -24
  135. data/spec/array/sorted_sample_spec.rb +0 -28
  136. data/spec/dsl_object_spec.rb +0 -99
  137. data/spec/hash/indifferent_access_spec.rb +0 -391
  138. data/spec/metaprogramming/cattr_accessor_spec.rb +0 -43
  139. data/spec/metaprogramming/mattr_accessor_spec.rb +0 -45
  140. data/spec/receiver/acts_as_hash_spec.rb +0 -295
  141. data/spec/receiver_spec.rb +0 -551
  142. data/spec/struct/acts_as_hash_fuzz_spec.rb +0 -71
  143. data/spec/struct/acts_as_hash_spec.rb +0 -422
@@ -0,0 +1,78 @@
1
+ require 'pathname'
2
+ require 'gorillib/exception/raisers'
3
+
4
+ module Gorillib
5
+ module Pathref
6
+ ROOT_PATHS = Hash.new unless defined?(ROOT_PATHS)
7
+
8
+ extend self
9
+
10
+ def register_path(handle, *pathsegs)
11
+ ArgumentError.arity_at_least!(pathsegs, 1)
12
+ ROOT_PATHS[handle] = pathsegs
13
+ end
14
+
15
+ def register_paths(pairs = {})
16
+ pairs.each_pair{ |handle, pathsegs| register_path(handle, *pathsegs) }
17
+ end
18
+
19
+ def unregister_path handle
20
+ ROOT_PATHS.delete handle
21
+ end
22
+
23
+ # Expand a path with late-evaluated segments.
24
+ # Calls expand_path -- '~' becomes $HOME, '..' is expanded, etc.
25
+ #
26
+ # @example A symbol represents a segment to expand
27
+ # Pathname.register_path(:conf_dir, '/etc/delorean')
28
+ # Pathname.path_to(:conf_dir) # '/etc/delorean'
29
+ # Pathname.path_to(:conf_dir, modacity.conf) # '/etc/delorean/modacity.conf'
30
+ #
31
+ # @example References aren't expanded until they're read
32
+ # Pathname.register_path(:conf_dir, '/etc/delorean')
33
+ # Pathname.register_path(:modacity, :conf_dir, 'modacity.conf')
34
+ # Pathname.path_to(:modacity) # '/etc/delorean/modacity.conf'
35
+ # # if we change the conf_dir, everything under it changes as well
36
+ # Pathname.register_path(:conf_dir, '~/.delorean.d')
37
+ # Pathname.path_to(:modacity) # '/home/flip/delorean.d/modacity.conf'
38
+ #
39
+ # @example References can be relative, and can hold symbols themselves
40
+ # Pathname.register_path(:conf_dir, '/etc', :appname, :environment)
41
+ # Pathname.register_path(:appname, 'happy_app')
42
+ # Pathname.register_path(:environment, 'dev')
43
+ # Pathname.path_to(:conf_dir) # '/etc/happy_app/dev'
44
+ #
45
+ # @param [Array<[String,Symbol]>] pathsegs
46
+ # any mixture of strings (literal sub-paths) and symbols (interpreted as references)
47
+ # @return [Pathname] A single expanded Pathname
48
+ #
49
+ def path_to(*pathsegs)
50
+ relative_path_to(*pathsegs).expand_path
51
+ end
52
+
53
+ # Expand a path with late-evaluated segments
54
+ # @see `.path_to`
55
+ #
56
+ # Calls cleanpath (removing `//` double slashes and useless `..`s), but does
57
+ # not reference the filesystem or make paths absolute
58
+ #
59
+ def relative_path_to(*pathsegs)
60
+ ArgumentError.arity_at_least!(pathsegs, 1)
61
+ pathsegs = pathsegs.map{|ps| expand_pathseg(ps) }.flatten
62
+ new(File.join(*pathsegs)).cleanpath(true)
63
+ end
64
+
65
+ protected
66
+ # Recursively expand a path handle
67
+ # @return [Array<String>] an array of path segments, suitable for .join
68
+ def expand_pathseg(handle)
69
+ return handle unless handle.is_a?(Symbol)
70
+ pathsegs = ROOT_PATHS[handle] or raise ArgumentError, "Don't know how to expand path reference '#{handle.inspect}'."
71
+ pathsegs.map{|ps| expand_pathseg(ps) }.flatten
72
+ end
73
+ end
74
+ end
75
+
76
+ class Pathname
77
+ extend Gorillib::Pathref
78
+ end
data/lib/gorillib/some.rb CHANGED
@@ -1,12 +1,14 @@
1
- require 'gorillib/base'
2
1
  require 'set'
3
2
  require 'time'
4
3
  require 'date'
5
- require 'gorillib/array/compact_blank'
6
- require 'gorillib/hash/compact'
7
- require 'gorillib/enumerable/sum'
8
- require 'gorillib/datetime/flat'
9
- require 'gorillib/datetime/parse'
10
- require 'gorillib/hash/zip'
11
- require 'gorillib/hash/slice'
12
- require 'gorillib/hash/keys'
4
+ require 'pathname'
5
+
6
+ require 'gorillib/base'
7
+
8
+ require 'gorillib/pathname'
9
+ require 'gorillib/string/simple_inflector'
10
+ require 'gorillib/string/inflections'
11
+ require 'gorillib/string/constantize'
12
+ require 'gorillib/hash/mash'
13
+ require 'gorillib/metaprogramming/delegation'
14
+ require 'gorillib/metaprogramming/concern'
@@ -1,21 +1,28 @@
1
+ require 'gorillib/string/inflector'
1
2
  class String
2
3
 
3
- # Constantize tries to find a declared constant with the name specified
4
+ # +constantize+ tries to find a declared constant with the name specified
4
5
  # in the string. It raises a NameError when the name is not in CamelCase
5
- # or is not initialized.
6
- #
7
- # @example
8
- # "Module".constantize #=> Module
9
- # "Class".constantize #=> Class
10
- #
11
- # This is the extlib version of String#constantize, which has different
12
- # behavior wrt using lexical context: see active_support/inflector/methods.rb
6
+ # or is not initialized. See Gorillib::Inflector.constantize
13
7
  #
8
+ # Examples
9
+ # "Module".constantize # => Module
10
+ # "Class".constantize # => Class
11
+ # "blargle".constantize # => NameError: wrong constant name blargle
14
12
  def constantize
15
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
16
- raise NameError, "#{self.inspect} is not a valid constant name!"
17
- end
18
-
19
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
13
+ Gorillib::Inflector.constantize(self)
20
14
  end unless method_defined?(:constantize)
15
+
16
+ # +safe_constantize+ tries to find a declared constant with the name specified
17
+ # in the string. It returns nil when the name is not in CamelCase
18
+ # or is not initialized. See Gorillib::Model::Inflector.safe_constantize
19
+ #
20
+ # Examples
21
+ # "Module".safe_constantize # => Module
22
+ # "Class".safe_constantize # => Class
23
+ # "blargle".safe_constantize # => nil
24
+ def safe_constantize
25
+ Gorillib::Inflector.safe_constantize(self)
26
+ end unless method_defined?(:safe_constantize)
27
+
21
28
  end
@@ -1,78 +1,8 @@
1
- # String inflections define new methods on the String class to transform names for different purposes.
2
- #
3
- # "ScaleScore".underscore # => "scale_score"
4
- #
5
- # This doesn't define the full set of inflections -- only
6
- #
7
- # * camelize
8
- # * snakeize
9
- # * underscore
10
- # * demodulize
11
- #
12
- class String
13
-
14
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
15
- # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
16
- #
17
- # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
18
- #
19
- # @example:
20
- # "active_record".camelize # => "ActiveRecord"
21
- # "active_record".camelize(:lower) # => "activeRecord"
22
- # "active_record/errors".camelize # => "ActiveRecord::Errors"
23
- # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
24
- #
25
- # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
26
- # though there are cases where that does not hold:
27
- #
28
- # "SSLError".underscore.camelize # => "SslError"
29
- #
30
- def camelize(first_letter = :upper)
31
- camelized = self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
32
- (first_letter == :lower) ? (self[0..0].downcase + camelized[1..-1]) : camelized
33
- end unless method_defined?(:camelize)
34
-
35
- # Converts strings to snakeCase, also known as lowerCamelCase.
36
- #
37
- # +snakeize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
38
- #
39
- # @example:
40
- # "active_record".snakeize # => "activeRecord"
41
- # "active_record/errors".snakeize # => "activeRecord::Errors"
42
- #
43
- def snakeize
44
- camelize :lower
45
- end unless method_defined?(:snakeize)
46
-
47
- # Makes an underscored, lowercase form from the expression in the string.
48
- #
49
- # +underscore+ will also change '::' to '/' to convert namespaces to paths.
50
- #
51
- # Examples:
52
- # "ActiveRecord".underscore # => "active_record"
53
- # "ActiveRecord::Errors".underscore # => active_record/errors
54
- #
55
- # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
56
- # though there are cases where that does not hold:
57
- #
58
- # "SSLError".underscore.camelize # => "SslError"
59
- def underscore
60
- word = self.dup
61
- word.gsub!(/::/, '/')
62
- word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
63
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
64
- word.tr!("-", "_")
65
- word.downcase!
66
- word
67
- end unless method_defined?(:underscore)
68
-
69
- # Removes the module part from the expression in the string
70
- #
71
- # @example
72
- # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
73
- # "Inflections".demodulize #=> "Inflections"
74
- def demodulize
75
- self.gsub(/^.*::/, '')
76
- end unless method_defined?(:demodulize)
1
+ require 'gorillib/string/inflector'
77
2
 
3
+ class String
4
+ def camelize(*args) Gorillib::Inflector.camelize(self, *args) ; end
5
+ def snakeize(*args) Gorillib::Inflector.snakeize(self, *args) ; end
6
+ def underscore(*args) Gorillib::Inflector.underscore(self, *args) ; end
7
+ def demodulize(*args) Gorillib::Inflector.demodulize(self, *args) ; end
78
8
  end
@@ -0,0 +1,192 @@
1
+ module Gorillib ; end
2
+
3
+ # String inflections define new methods on the String class to transform names for different purposes.
4
+ #
5
+ # "ScaleScore".underscore # => "scale_score"
6
+ #
7
+ # This doesn't define the full set of inflections -- only
8
+ #
9
+ # * camelize
10
+ # * snakeize
11
+ # * underscore
12
+ # * demodulize
13
+ #
14
+ module Gorillib::Inflector
15
+ extend self
16
+
17
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
18
+ # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
19
+ #
20
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
21
+ #
22
+ # @example:
23
+ # "active_record".camelize # => "ActiveRecord"
24
+ # "active_record".camelize(:lower) # => "activeRecord"
25
+ # "active_record/errors".camelize # => "ActiveRecord::Errors"
26
+ # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
27
+ #
28
+ # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
29
+ # though there are cases where that does not hold:
30
+ #
31
+ # "SSLError".underscore.camelize # => "SslError"
32
+ #
33
+ def camelize(str, first_letter = :upper)
34
+ camelized = str.gsub(/\/(.?)/){ "::#{ $1.upcase }" }.gsub(/(?:^|_)(.)/){ $1.upcase }
35
+ (first_letter == :lower) ? (str[0..0].downcase + camelized[1..-1]) : camelized
36
+ end
37
+
38
+ # Converts strings to snakeCase, also known as lowerCamelCase.
39
+ #
40
+ # +snakeize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
41
+ #
42
+ # @example:
43
+ # "active_record".snakeize # => "activeRecord"
44
+ # "active_record/errors".snakeize # => "activeRecord::Errors"
45
+ #
46
+ def snakeize(str)
47
+ camelize(str, :lower)
48
+ end
49
+
50
+ # Makes an underscored, lowercase form from the expression in the string.
51
+ #
52
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
53
+ #
54
+ # Examples:
55
+ # "ActiveRecord".underscore # => "active_record"
56
+ # "ActiveRecord::Errors".underscore # => active_record/errors
57
+ #
58
+ # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
59
+ # though there are cases where that does not hold:
60
+ #
61
+ # "SSLError".underscore.camelize # => "SslError"
62
+ def underscore(str)
63
+ word = str.dup
64
+ word.gsub!(/::/, '/')
65
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
66
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
67
+ word.tr!("-", "_")
68
+ word.downcase!
69
+ word
70
+ end
71
+
72
+ # Replaces underscores with dashes in the string.
73
+ #
74
+ # Example:
75
+ # "puni_puni" # => "puni-puni"
76
+ def dasherize(underscored_word)
77
+ underscored_word.gsub(/_/, '-')
78
+ end
79
+
80
+ # Removes the module part from the expression in the string:
81
+ #
82
+ # @example
83
+ # "Gorillib::Inflections".demodulize #=> "Inflections"
84
+ # "Inflections".demodulize #=> "Inflections"
85
+ #
86
+ # See also +deconstantize+.
87
+ def demodulize(str)
88
+ str.gsub(/^.*::/, '')
89
+ end
90
+
91
+ # Removes the rightmost segment from the constant expression in the string:
92
+ #
93
+ # "Net::HTTP".deconstantize # => "Net"
94
+ # "::Net::HTTP".deconstantize # => "::Net"
95
+ # "String".deconstantize # => ""
96
+ # "::String".deconstantize # => ""
97
+ # "".deconstantize # => ""
98
+ #
99
+ # See also +demodulize+.
100
+ def deconstantize(path)
101
+ path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
102
+ end
103
+
104
+ # Constantize tries to find a declared constant with the name specified
105
+ # in the string. It raises a NameError when the name is not in CamelCase
106
+ # or is not initialized.
107
+ #
108
+ # @example
109
+ # "Module".constantize #=> Module
110
+ # "Class".constantize #=> Class
111
+ #
112
+ # This is the extlib version of String#constantize, which has different
113
+ # behavior wrt using lexical context: see active_support/inflector/methods.rb
114
+ #
115
+ def constantize(str)
116
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ str
117
+ raise NameError, "#{self.inspect} is not a valid constant name!"
118
+ end
119
+
120
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
121
+ end
122
+
123
+ # Tries to find a constant with the name specified in the argument string:
124
+ #
125
+ # "Module".safe_constantize # => Module
126
+ # "Test::Unit".safe_constantize # => Test::Unit
127
+ #
128
+ # The name is assumed to be the one of a top-level constant, no matter whether
129
+ # it starts with "::" or not. No lexical context is taken into account:
130
+ #
131
+ # C = 'outside'
132
+ # module M
133
+ # C = 'inside'
134
+ # C # => 'inside'
135
+ # "C".safe_constantize # => 'outside', same as ::C
136
+ # end
137
+ #
138
+ # nil is returned when the name is not in CamelCase or the constant (or part of it) is
139
+ # unknown.
140
+ #
141
+ # "blargle".safe_constantize # => nil
142
+ # "UnknownModule".safe_constantize # => nil
143
+ # "UnknownModule::Foo::Bar".safe_constantize # => nil
144
+ #
145
+ def safe_constantize(camel_cased_word)
146
+ begin
147
+ constantize(camel_cased_word)
148
+ rescue NameError => e
149
+ raise unless e.message =~ /uninitialized constant #{const_regexp(camel_cased_word)}$/ ||
150
+ e.name.to_s == camel_cased_word.to_s
151
+ rescue ArgumentError => e
152
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
153
+ end
154
+ end
155
+
156
+ # Turns a number into an ordinal string used to denote the position in an
157
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
158
+ #
159
+ # Examples:
160
+ # ordinalize(1) # => "1st"
161
+ # ordinalize(2) # => "2nd"
162
+ # ordinalize(1002) # => "1002nd"
163
+ # ordinalize(1003) # => "1003rd"
164
+ # ordinalize(-11) # => "-11th"
165
+ # ordinalize(-1021) # => "-1021st"
166
+ def ordinalize(number)
167
+ if (11..13).include?(number.to_i.abs % 100)
168
+ "#{number}th"
169
+ else
170
+ case number.to_i.abs % 10
171
+ when 1; "#{number}st"
172
+ when 2; "#{number}nd"
173
+ when 3; "#{number}rd"
174
+ else "#{number}th"
175
+ end
176
+ end
177
+ end
178
+
179
+ private
180
+
181
+ # Mount a regular expression that will match part by part of the constant.
182
+ # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
183
+ def const_regexp(camel_cased_word) #:nodoc:
184
+ parts = camel_cased_word.split("::")
185
+ last = parts.pop
186
+
187
+ parts.reverse.inject(last) do |acc, part|
188
+ part.empty? ? acc : "#{part}(::#{acc})?"
189
+ end
190
+ end
191
+
192
+ end