core_ext 0.0.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 (175) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3 -0
  3. data/lib/core_ext/array/access.rb +76 -0
  4. data/lib/core_ext/array/conversions.rb +211 -0
  5. data/lib/core_ext/array/extract_options.rb +29 -0
  6. data/lib/core_ext/array/grouping.rb +116 -0
  7. data/lib/core_ext/array/inquiry.rb +17 -0
  8. data/lib/core_ext/array/prepend_and_append.rb +7 -0
  9. data/lib/core_ext/array/wrap.rb +46 -0
  10. data/lib/core_ext/array.rb +7 -0
  11. data/lib/core_ext/array_inquirer.rb +44 -0
  12. data/lib/core_ext/benchmark.rb +14 -0
  13. data/lib/core_ext/benchmarkable.rb +49 -0
  14. data/lib/core_ext/big_decimal/conversions.rb +14 -0
  15. data/lib/core_ext/big_decimal.rb +1 -0
  16. data/lib/core_ext/builder.rb +6 -0
  17. data/lib/core_ext/callbacks.rb +770 -0
  18. data/lib/core_ext/class/attribute.rb +128 -0
  19. data/lib/core_ext/class/attribute_accessors.rb +4 -0
  20. data/lib/core_ext/class/subclasses.rb +42 -0
  21. data/lib/core_ext/class.rb +2 -0
  22. data/lib/core_ext/concern.rb +142 -0
  23. data/lib/core_ext/configurable.rb +148 -0
  24. data/lib/core_ext/date/acts_like.rb +8 -0
  25. data/lib/core_ext/date/blank.rb +12 -0
  26. data/lib/core_ext/date/calculations.rb +143 -0
  27. data/lib/core_ext/date/conversions.rb +93 -0
  28. data/lib/core_ext/date/zones.rb +6 -0
  29. data/lib/core_ext/date.rb +5 -0
  30. data/lib/core_ext/date_and_time/calculations.rb +328 -0
  31. data/lib/core_ext/date_and_time/zones.rb +40 -0
  32. data/lib/core_ext/date_time/acts_like.rb +14 -0
  33. data/lib/core_ext/date_time/blank.rb +12 -0
  34. data/lib/core_ext/date_time/calculations.rb +177 -0
  35. data/lib/core_ext/date_time/conversions.rb +104 -0
  36. data/lib/core_ext/date_time/zones.rb +6 -0
  37. data/lib/core_ext/date_time.rb +5 -0
  38. data/lib/core_ext/deprecation/behaviors.rb +86 -0
  39. data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
  40. data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
  41. data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
  42. data/lib/core_ext/deprecation/reporting.rb +105 -0
  43. data/lib/core_ext/deprecation.rb +43 -0
  44. data/lib/core_ext/digest/uuid.rb +51 -0
  45. data/lib/core_ext/duration.rb +157 -0
  46. data/lib/core_ext/enumerable.rb +106 -0
  47. data/lib/core_ext/file/atomic.rb +68 -0
  48. data/lib/core_ext/file.rb +1 -0
  49. data/lib/core_ext/hash/compact.rb +20 -0
  50. data/lib/core_ext/hash/conversions.rb +261 -0
  51. data/lib/core_ext/hash/deep_merge.rb +38 -0
  52. data/lib/core_ext/hash/except.rb +22 -0
  53. data/lib/core_ext/hash/indifferent_access.rb +23 -0
  54. data/lib/core_ext/hash/keys.rb +170 -0
  55. data/lib/core_ext/hash/reverse_merge.rb +22 -0
  56. data/lib/core_ext/hash/slice.rb +48 -0
  57. data/lib/core_ext/hash/transform_values.rb +29 -0
  58. data/lib/core_ext/hash.rb +9 -0
  59. data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
  60. data/lib/core_ext/inflections.rb +70 -0
  61. data/lib/core_ext/inflector/inflections.rb +244 -0
  62. data/lib/core_ext/inflector/methods.rb +381 -0
  63. data/lib/core_ext/inflector/transliterate.rb +112 -0
  64. data/lib/core_ext/inflector.rb +7 -0
  65. data/lib/core_ext/integer/inflections.rb +29 -0
  66. data/lib/core_ext/integer/multiple.rb +10 -0
  67. data/lib/core_ext/integer/time.rb +29 -0
  68. data/lib/core_ext/integer.rb +3 -0
  69. data/lib/core_ext/json/decoding.rb +67 -0
  70. data/lib/core_ext/json/encoding.rb +127 -0
  71. data/lib/core_ext/json.rb +2 -0
  72. data/lib/core_ext/kernel/agnostics.rb +11 -0
  73. data/lib/core_ext/kernel/concern.rb +10 -0
  74. data/lib/core_ext/kernel/reporting.rb +41 -0
  75. data/lib/core_ext/kernel/singleton_class.rb +6 -0
  76. data/lib/core_ext/kernel.rb +4 -0
  77. data/lib/core_ext/load_error.rb +30 -0
  78. data/lib/core_ext/logger.rb +57 -0
  79. data/lib/core_ext/logger_silence.rb +24 -0
  80. data/lib/core_ext/marshal.rb +19 -0
  81. data/lib/core_ext/module/aliasing.rb +74 -0
  82. data/lib/core_ext/module/anonymous.rb +28 -0
  83. data/lib/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/core_ext/module/concerning.rb +135 -0
  86. data/lib/core_ext/module/delegation.rb +218 -0
  87. data/lib/core_ext/module/deprecation.rb +23 -0
  88. data/lib/core_ext/module/introspection.rb +62 -0
  89. data/lib/core_ext/module/method_transplanting.rb +3 -0
  90. data/lib/core_ext/module/qualified_const.rb +52 -0
  91. data/lib/core_ext/module/reachable.rb +8 -0
  92. data/lib/core_ext/module/remove_method.rb +35 -0
  93. data/lib/core_ext/module.rb +11 -0
  94. data/lib/core_ext/multibyte/chars.rb +231 -0
  95. data/lib/core_ext/multibyte/unicode.rb +388 -0
  96. data/lib/core_ext/multibyte.rb +21 -0
  97. data/lib/core_ext/name_error.rb +31 -0
  98. data/lib/core_ext/numeric/bytes.rb +64 -0
  99. data/lib/core_ext/numeric/conversions.rb +132 -0
  100. data/lib/core_ext/numeric/inquiry.rb +26 -0
  101. data/lib/core_ext/numeric/time.rb +74 -0
  102. data/lib/core_ext/numeric.rb +4 -0
  103. data/lib/core_ext/object/acts_like.rb +10 -0
  104. data/lib/core_ext/object/blank.rb +140 -0
  105. data/lib/core_ext/object/conversions.rb +4 -0
  106. data/lib/core_ext/object/deep_dup.rb +53 -0
  107. data/lib/core_ext/object/duplicable.rb +98 -0
  108. data/lib/core_ext/object/inclusion.rb +27 -0
  109. data/lib/core_ext/object/instance_variables.rb +28 -0
  110. data/lib/core_ext/object/json.rb +199 -0
  111. data/lib/core_ext/object/to_param.rb +1 -0
  112. data/lib/core_ext/object/to_query.rb +84 -0
  113. data/lib/core_ext/object/try.rb +146 -0
  114. data/lib/core_ext/object/with_options.rb +69 -0
  115. data/lib/core_ext/object.rb +14 -0
  116. data/lib/core_ext/option_merger.rb +25 -0
  117. data/lib/core_ext/ordered_hash.rb +48 -0
  118. data/lib/core_ext/ordered_options.rb +81 -0
  119. data/lib/core_ext/range/conversions.rb +34 -0
  120. data/lib/core_ext/range/each.rb +21 -0
  121. data/lib/core_ext/range/include_range.rb +23 -0
  122. data/lib/core_ext/range/overlaps.rb +8 -0
  123. data/lib/core_ext/range.rb +4 -0
  124. data/lib/core_ext/regexp.rb +5 -0
  125. data/lib/core_ext/rescuable.rb +119 -0
  126. data/lib/core_ext/securerandom.rb +23 -0
  127. data/lib/core_ext/security_utils.rb +20 -0
  128. data/lib/core_ext/string/access.rb +104 -0
  129. data/lib/core_ext/string/behavior.rb +6 -0
  130. data/lib/core_ext/string/conversions.rb +56 -0
  131. data/lib/core_ext/string/exclude.rb +11 -0
  132. data/lib/core_ext/string/filters.rb +102 -0
  133. data/lib/core_ext/string/indent.rb +43 -0
  134. data/lib/core_ext/string/inflections.rb +235 -0
  135. data/lib/core_ext/string/inquiry.rb +13 -0
  136. data/lib/core_ext/string/multibyte.rb +53 -0
  137. data/lib/core_ext/string/output_safety.rb +261 -0
  138. data/lib/core_ext/string/starts_ends_with.rb +4 -0
  139. data/lib/core_ext/string/strip.rb +23 -0
  140. data/lib/core_ext/string/zones.rb +14 -0
  141. data/lib/core_ext/string.rb +13 -0
  142. data/lib/core_ext/string_inquirer.rb +26 -0
  143. data/lib/core_ext/tagged_logging.rb +78 -0
  144. data/lib/core_ext/test_case.rb +88 -0
  145. data/lib/core_ext/testing/assertions.rb +99 -0
  146. data/lib/core_ext/testing/autorun.rb +12 -0
  147. data/lib/core_ext/testing/composite_filter.rb +54 -0
  148. data/lib/core_ext/testing/constant_lookup.rb +50 -0
  149. data/lib/core_ext/testing/declarative.rb +26 -0
  150. data/lib/core_ext/testing/deprecation.rb +36 -0
  151. data/lib/core_ext/testing/file_fixtures.rb +34 -0
  152. data/lib/core_ext/testing/isolation.rb +115 -0
  153. data/lib/core_ext/testing/method_call_assertions.rb +41 -0
  154. data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
  155. data/lib/core_ext/testing/stream.rb +42 -0
  156. data/lib/core_ext/testing/tagged_logging.rb +25 -0
  157. data/lib/core_ext/testing/time_helpers.rb +134 -0
  158. data/lib/core_ext/time/acts_like.rb +8 -0
  159. data/lib/core_ext/time/calculations.rb +284 -0
  160. data/lib/core_ext/time/conversions.rb +66 -0
  161. data/lib/core_ext/time/zones.rb +95 -0
  162. data/lib/core_ext/time.rb +20 -0
  163. data/lib/core_ext/time_with_zone.rb +503 -0
  164. data/lib/core_ext/time_zone.rb +464 -0
  165. data/lib/core_ext/uri.rb +25 -0
  166. data/lib/core_ext/version.rb +3 -0
  167. data/lib/core_ext/xml_mini/jdom.rb +181 -0
  168. data/lib/core_ext/xml_mini/libxml.rb +79 -0
  169. data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
  170. data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
  171. data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
  172. data/lib/core_ext/xml_mini/rexml.rb +130 -0
  173. data/lib/core_ext/xml_mini.rb +194 -0
  174. data/lib/core_ext.rb +3 -0
  175. metadata +310 -0
@@ -0,0 +1,23 @@
1
+ require 'securerandom'
2
+
3
+ module SecureRandom
4
+ BASE58_ALPHABET = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a - ['0', 'O', 'I', 'l']
5
+ # SecureRandom.base58 generates a random base58 string.
6
+ #
7
+ # The argument _n_ specifies the length, of the random string to be generated.
8
+ #
9
+ # If _n_ is not specified or is nil, 16 is assumed. It may be larger in the future.
10
+ #
11
+ # The result may contain alphanumeric characters except 0, O, I and l
12
+ #
13
+ # p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
14
+ # p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
15
+ #
16
+ def self.base58(n = 16)
17
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
18
+ idx = byte % 64
19
+ idx = SecureRandom.random_number(58) if idx >= 58
20
+ BASE58_ALPHABET[idx]
21
+ end.join
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ module CoreExt
2
+ module SecurityUtils
3
+ # Constant time string comparison.
4
+ #
5
+ # The values compared should be of fixed length, such as strings
6
+ # that have already been processed by HMAC. This should not be used
7
+ # on variable length plaintext strings because it could leak length info
8
+ # via timing attacks.
9
+ def secure_compare(a, b)
10
+ return false unless a.bytesize == b.bytesize
11
+
12
+ l = a.unpack "C#{a.bytesize}"
13
+
14
+ res = 0
15
+ b.each_byte { |byte| res |= byte ^ l.shift }
16
+ res == 0
17
+ end
18
+ module_function :secure_compare
19
+ end
20
+ end
@@ -0,0 +1,104 @@
1
+ class String
2
+ # If you pass a single Fixnum, returns a substring of one character at that
3
+ # position. The first character of the string is at position 0, the next at
4
+ # position 1, and so on. If a range is supplied, a substring containing
5
+ # characters at offsets given by the range is returned. In both cases, if an
6
+ # offset is negative, it is counted from the end of the string. Returns nil
7
+ # if the initial offset falls outside the string. Returns an empty string if
8
+ # the beginning of the range is greater than the end of the string.
9
+ #
10
+ # str = "hello"
11
+ # str.at(0) # => "h"
12
+ # str.at(1..3) # => "ell"
13
+ # str.at(-2) # => "l"
14
+ # str.at(-2..-1) # => "lo"
15
+ # str.at(5) # => nil
16
+ # str.at(5..-1) # => ""
17
+ #
18
+ # If a Regexp is given, the matching portion of the string is returned.
19
+ # If a String is given, that given string is returned if it occurs in
20
+ # the string. In both cases, nil is returned if there is no match.
21
+ #
22
+ # str = "hello"
23
+ # str.at(/lo/) # => "lo"
24
+ # str.at(/ol/) # => nil
25
+ # str.at("lo") # => "lo"
26
+ # str.at("ol") # => nil
27
+ def at(position)
28
+ self[position]
29
+ end
30
+
31
+ # Returns a substring from the given position to the end of the string.
32
+ # If the position is negative, it is counted from the end of the string.
33
+ #
34
+ # str = "hello"
35
+ # str.from(0) # => "hello"
36
+ # str.from(3) # => "lo"
37
+ # str.from(-2) # => "lo"
38
+ #
39
+ # You can mix it with +to+ method and do fun things like:
40
+ #
41
+ # str = "hello"
42
+ # str.from(0).to(-1) # => "hello"
43
+ # str.from(1).to(-2) # => "ell"
44
+ def from(position)
45
+ self[position..-1]
46
+ end
47
+
48
+ # Returns a substring from the beginning of the string to the given position.
49
+ # If the position is negative, it is counted from the end of the string.
50
+ #
51
+ # str = "hello"
52
+ # str.to(0) # => "h"
53
+ # str.to(3) # => "hell"
54
+ # str.to(-2) # => "hell"
55
+ #
56
+ # You can mix it with +from+ method and do fun things like:
57
+ #
58
+ # str = "hello"
59
+ # str.from(0).to(-1) # => "hello"
60
+ # str.from(1).to(-2) # => "ell"
61
+ def to(position)
62
+ self[0..position]
63
+ end
64
+
65
+ # Returns the first character. If a limit is supplied, returns a substring
66
+ # from the beginning of the string until it reaches the limit value. If the
67
+ # given limit is greater than or equal to the string length, returns a copy of self.
68
+ #
69
+ # str = "hello"
70
+ # str.first # => "h"
71
+ # str.first(1) # => "h"
72
+ # str.first(2) # => "he"
73
+ # str.first(0) # => ""
74
+ # str.first(6) # => "hello"
75
+ def first(limit = 1)
76
+ if limit == 0
77
+ ''
78
+ elsif limit >= size
79
+ self.dup
80
+ else
81
+ to(limit - 1)
82
+ end
83
+ end
84
+
85
+ # Returns the last character of the string. If a limit is supplied, returns a substring
86
+ # from the end of the string until it reaches the limit value (counting backwards). If
87
+ # the given limit is greater than or equal to the string length, returns a copy of self.
88
+ #
89
+ # str = "hello"
90
+ # str.last # => "o"
91
+ # str.last(1) # => "o"
92
+ # str.last(2) # => "lo"
93
+ # str.last(0) # => ""
94
+ # str.last(6) # => "hello"
95
+ def last(limit = 1)
96
+ if limit == 0
97
+ ''
98
+ elsif limit >= size
99
+ self.dup
100
+ else
101
+ from(-limit)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,6 @@
1
+ class String
2
+ # Enables more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
3
+ def acts_like_string?
4
+ true
5
+ end
6
+ end
@@ -0,0 +1,56 @@
1
+ require 'date'
2
+ require 'core_ext/time/calculations'
3
+
4
+ class String
5
+ # Converts a string to a Time value.
6
+ # The +form+ can be either :utc or :local (default :local).
7
+ #
8
+ # The time is parsed using Time.parse method.
9
+ # If +form+ is :local, then the time is in the system timezone.
10
+ # If the date part is missing then the current date is used and if
11
+ # the time part is missing then it is assumed to be 00:00:00.
12
+ #
13
+ # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
14
+ # "06:12".to_time # => 2012-12-13 06:12:00 +0100
15
+ # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
16
+ # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
17
+ # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
18
+ # "12/13/2012".to_time # => ArgumentError: argument out of range
19
+ def to_time(form = :local)
20
+ parts = Date._parse(self, false)
21
+ return if parts.empty?
22
+
23
+ now = Time.now
24
+ time = Time.new(
25
+ parts.fetch(:year, now.year),
26
+ parts.fetch(:mon, now.month),
27
+ parts.fetch(:mday, now.day),
28
+ parts.fetch(:hour, 0),
29
+ parts.fetch(:min, 0),
30
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
31
+ parts.fetch(:offset, form == :utc ? 0 : nil)
32
+ )
33
+
34
+ form == :utc ? time.utc : time.getlocal
35
+ end
36
+
37
+ # Converts a string to a Date value.
38
+ #
39
+ # "1-1-2012".to_date # => Sun, 01 Jan 2012
40
+ # "01/01/2012".to_date # => Sun, 01 Jan 2012
41
+ # "2012-12-13".to_date # => Thu, 13 Dec 2012
42
+ # "12/13/2012".to_date # => ArgumentError: invalid date
43
+ def to_date
44
+ ::Date.parse(self, false) unless blank?
45
+ end
46
+
47
+ # Converts a string to a DateTime value.
48
+ #
49
+ # "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
50
+ # "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
51
+ # "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
52
+ # "12/13/2012".to_datetime # => ArgumentError: invalid date
53
+ def to_datetime
54
+ ::DateTime.parse(self, false) unless blank?
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ class String
2
+ # The inverse of <tt>String#include?</tt>. Returns true if the string
3
+ # does not include the other string.
4
+ #
5
+ # "hello".exclude? "lo" # => false
6
+ # "hello".exclude? "ol" # => true
7
+ # "hello".exclude? ?h # => false
8
+ def exclude?(string)
9
+ !include?(string)
10
+ end
11
+ end
@@ -0,0 +1,102 @@
1
+ class String
2
+ # Returns the string, first removing all whitespace on both ends of
3
+ # the string, and then changing remaining consecutive whitespace
4
+ # groups into one space each.
5
+ #
6
+ # Note that it handles both ASCII and Unicode whitespace.
7
+ #
8
+ # %{ Multi-line
9
+ # string }.squish # => "Multi-line string"
10
+ # " foo bar \n \t boo".squish # => "foo bar boo"
11
+ def squish
12
+ dup.squish!
13
+ end
14
+
15
+ # Performs a destructive squish. See String#squish.
16
+ # str = " foo bar \n \t boo"
17
+ # str.squish! # => "foo bar boo"
18
+ # str # => "foo bar boo"
19
+ def squish!
20
+ gsub!(/[[:space:]]+/, ' ')
21
+ strip!
22
+ self
23
+ end
24
+
25
+ # Returns a new string with all occurrences of the patterns removed.
26
+ # str = "foo bar test"
27
+ # str.remove(" test") # => "foo bar"
28
+ # str.remove(" test", /bar/) # => "foo "
29
+ # str # => "foo bar test"
30
+ def remove(*patterns)
31
+ dup.remove!(*patterns)
32
+ end
33
+
34
+ # Alters the string by removing all occurrences of the patterns.
35
+ # str = "foo bar test"
36
+ # str.remove!(" test", /bar/) # => "foo "
37
+ # str # => "foo "
38
+ def remove!(*patterns)
39
+ patterns.each do |pattern|
40
+ gsub! pattern, ""
41
+ end
42
+
43
+ self
44
+ end
45
+
46
+ # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
47
+ #
48
+ # 'Once upon a time in a world far far away'.truncate(27)
49
+ # # => "Once upon a time in a wo..."
50
+ #
51
+ # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
52
+ #
53
+ # 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
54
+ # # => "Once upon a time in a..."
55
+ #
56
+ # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
57
+ # # => "Once upon a time in a..."
58
+ #
59
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
60
+ # for a total length not exceeding <tt>length</tt>:
61
+ #
62
+ # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
63
+ # # => "And they f... (continued)"
64
+ def truncate(truncate_at, options = {})
65
+ return dup unless length > truncate_at
66
+
67
+ omission = options[:omission] || '...'
68
+ length_with_room_for_omission = truncate_at - omission.length
69
+ stop = \
70
+ if options[:separator]
71
+ rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
72
+ else
73
+ length_with_room_for_omission
74
+ end
75
+
76
+ "#{self[0, stop]}#{omission}"
77
+ end
78
+
79
+ # Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
80
+ #
81
+ # 'Once upon a time in a world far far away'.truncate_words(4)
82
+ # # => "Once upon a time..."
83
+ #
84
+ # Pass a string or regexp <tt>:separator</tt> to specify a different separator of words:
85
+ #
86
+ # 'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')
87
+ # # => "Once<br>upon<br>a<br>time<br>in..."
88
+ #
89
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "..."):
90
+ #
91
+ # 'And they found that many people were sleeping better.'.truncate_words(5, omission: '... (continued)')
92
+ # # => "And they found that many... (continued)"
93
+ def truncate_words(words_count, options = {})
94
+ sep = options[:separator] || /\s+/
95
+ sep = Regexp.escape(sep.to_s) unless Regexp === sep
96
+ if self =~ /\A((?>.+?#{sep}){#{words_count - 1}}.+?)#{sep}.*/m
97
+ $1 + (options[:omission] || '...')
98
+ else
99
+ dup
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,43 @@
1
+ class String
2
+ # Same as +indent+, except it indents the receiver in-place.
3
+ #
4
+ # Returns the indented string, or +nil+ if there was nothing to indent.
5
+ def indent!(amount, indent_string=nil, indent_empty_lines=false)
6
+ indent_string = indent_string || self[/^[ \t]/] || ' '
7
+ re = indent_empty_lines ? /^/ : /^(?!$)/
8
+ gsub!(re, indent_string * amount)
9
+ end
10
+
11
+ # Indents the lines in the receiver:
12
+ #
13
+ # <<EOS.indent(2)
14
+ # def some_method
15
+ # some_code
16
+ # end
17
+ # EOS
18
+ # # =>
19
+ # def some_method
20
+ # some_code
21
+ # end
22
+ #
23
+ # The second argument, +indent_string+, specifies which indent string to
24
+ # use. The default is +nil+, which tells the method to make a guess by
25
+ # peeking at the first indented line, and fallback to a space if there is
26
+ # none.
27
+ #
28
+ # " foo".indent(2) # => " foo"
29
+ # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
30
+ # "foo".indent(2, "\t") # => "\t\tfoo"
31
+ #
32
+ # While +indent_string+ is typically one space or tab, it may be any string.
33
+ #
34
+ # The third argument, +indent_empty_lines+, is a flag that says whether
35
+ # empty lines should be indented. Default is false.
36
+ #
37
+ # "foo\n\nbar".indent(2) # => " foo\n\n bar"
38
+ # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
39
+ #
40
+ def indent(amount, indent_string=nil, indent_empty_lines=false)
41
+ dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
42
+ end
43
+ end
@@ -0,0 +1,235 @@
1
+ require 'core_ext/inflector/methods'
2
+ require 'core_ext/inflector/transliterate'
3
+
4
+ # String inflections define new methods on the String class to transform names for different purposes.
5
+ # For instance, you can figure out the name of a table from the name of a class.
6
+ #
7
+ # 'ScaleScore'.tableize # => "scale_scores"
8
+ #
9
+ class String
10
+ # Returns the plural form of the word in the string.
11
+ #
12
+ # If the optional parameter +count+ is specified,
13
+ # the singular form will be returned if <tt>count == 1</tt>.
14
+ # For any other value of +count+ the plural will be returned.
15
+ #
16
+ # If the optional parameter +locale+ is specified,
17
+ # the word will be pluralized as a word of that language.
18
+ # By default, this parameter is set to <tt>:en</tt>.
19
+ # You must define your own inflection rules for languages other than English.
20
+ #
21
+ # 'post'.pluralize # => "posts"
22
+ # 'octopus'.pluralize # => "octopi"
23
+ # 'sheep'.pluralize # => "sheep"
24
+ # 'words'.pluralize # => "words"
25
+ # 'the blue mailman'.pluralize # => "the blue mailmen"
26
+ # 'CamelOctopus'.pluralize # => "CamelOctopi"
27
+ # 'apple'.pluralize(1) # => "apple"
28
+ # 'apple'.pluralize(2) # => "apples"
29
+ # 'ley'.pluralize(:es) # => "leyes"
30
+ # 'ley'.pluralize(1, :es) # => "ley"
31
+ def pluralize(count = nil, locale = :en)
32
+ locale = count if count.is_a?(Symbol)
33
+ if count == 1
34
+ self.dup
35
+ else
36
+ CoreExt::Inflector.pluralize(self, locale)
37
+ end
38
+ end
39
+
40
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
41
+ #
42
+ # If the optional parameter +locale+ is specified,
43
+ # the word will be singularized as a word of that language.
44
+ # By default, this parameter is set to <tt>:en</tt>.
45
+ # You must define your own inflection rules for languages other than English.
46
+ #
47
+ # 'posts'.singularize # => "post"
48
+ # 'octopi'.singularize # => "octopus"
49
+ # 'sheep'.singularize # => "sheep"
50
+ # 'word'.singularize # => "word"
51
+ # 'the blue mailmen'.singularize # => "the blue mailman"
52
+ # 'CamelOctopi'.singularize # => "CamelOctopus"
53
+ # 'leyes'.singularize(:es) # => "ley"
54
+ def singularize(locale = :en)
55
+ CoreExt::Inflector.singularize(self, locale)
56
+ end
57
+
58
+ # +constantize+ tries to find a declared constant with the name specified
59
+ # in the string. It raises a NameError when the name is not in CamelCase
60
+ # or is not initialized. See CoreExt::Inflector.constantize
61
+ #
62
+ # 'Module'.constantize # => Module
63
+ # 'Class'.constantize # => Class
64
+ # 'blargle'.constantize # => NameError: wrong constant name blargle
65
+ def constantize
66
+ CoreExt::Inflector.constantize(self)
67
+ end
68
+
69
+ # +safe_constantize+ tries to find a declared constant with the name specified
70
+ # in the string. It returns nil when the name is not in CamelCase
71
+ # or is not initialized. See CoreExt::Inflector.safe_constantize
72
+ #
73
+ # 'Module'.safe_constantize # => Module
74
+ # 'Class'.safe_constantize # => Class
75
+ # 'blargle'.safe_constantize # => nil
76
+ def safe_constantize
77
+ CoreExt::Inflector.safe_constantize(self)
78
+ end
79
+
80
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
81
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
82
+ #
83
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
84
+ #
85
+ # 'active_record'.camelize # => "ActiveRecord"
86
+ # 'active_record'.camelize(:lower) # => "activeRecord"
87
+ # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
88
+ # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
89
+ def camelize(first_letter = :upper)
90
+ case first_letter
91
+ when :upper
92
+ CoreExt::Inflector.camelize(self, true)
93
+ when :lower
94
+ CoreExt::Inflector.camelize(self, false)
95
+ end
96
+ end
97
+ alias_method :camelcase, :camelize
98
+
99
+ # Capitalizes all the words and replaces some characters in the string to create
100
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
101
+ # used in the Rails internals.
102
+ #
103
+ # +titleize+ is also aliased as +titlecase+.
104
+ #
105
+ # 'man from the boondocks'.titleize # => "Man From The Boondocks"
106
+ # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
107
+ def titleize
108
+ CoreExt::Inflector.titleize(self)
109
+ end
110
+ alias_method :titlecase, :titleize
111
+
112
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
113
+ #
114
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
115
+ #
116
+ # 'ActiveModel'.underscore # => "active_model"
117
+ # 'ActiveModel::Errors'.underscore # => "active_model/errors"
118
+ def underscore
119
+ CoreExt::Inflector.underscore(self)
120
+ end
121
+
122
+ # Replaces underscores with dashes in the string.
123
+ #
124
+ # 'puni_puni'.dasherize # => "puni-puni"
125
+ def dasherize
126
+ CoreExt::Inflector.dasherize(self)
127
+ end
128
+
129
+ # Removes the module part from the constant expression in the string.
130
+ #
131
+ # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
132
+ # 'Inflections'.demodulize # => "Inflections"
133
+ # '::Inflections'.demodulize # => "Inflections"
134
+ # ''.demodulize # => ''
135
+ #
136
+ # See also +deconstantize+.
137
+ def demodulize
138
+ CoreExt::Inflector.demodulize(self)
139
+ end
140
+
141
+ # Removes the rightmost segment from the constant expression in the string.
142
+ #
143
+ # 'Net::HTTP'.deconstantize # => "Net"
144
+ # '::Net::HTTP'.deconstantize # => "::Net"
145
+ # 'String'.deconstantize # => ""
146
+ # '::String'.deconstantize # => ""
147
+ # ''.deconstantize # => ""
148
+ #
149
+ # See also +demodulize+.
150
+ def deconstantize
151
+ CoreExt::Inflector.deconstantize(self)
152
+ end
153
+
154
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
155
+ #
156
+ # class Person
157
+ # def to_param
158
+ # "#{id}-#{name.parameterize}"
159
+ # end
160
+ # end
161
+ #
162
+ # @person = Person.find(1)
163
+ # # => #<Person id: 1, name: "Donald E. Knuth">
164
+ #
165
+ # <%= link_to(@person.name, person_path) %>
166
+ # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
167
+ #
168
+ # To preserve the case of the characters in a string, use the `preserve_case` argument.
169
+ #
170
+ # class Person
171
+ # def to_param
172
+ # "#{id}-#{name.parameterize(preserve_case: true)}"
173
+ # end
174
+ # end
175
+ #
176
+ # @person = Person.find(1)
177
+ # # => #<Person id: 1, name: "Donald E. Knuth">
178
+ #
179
+ # <%= link_to(@person.name, person_path) %>
180
+ # # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
181
+ def parameterize(sep = :unused, separator: '-', preserve_case: false)
182
+ unless sep == :unused
183
+ CoreExt::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.")
184
+ separator = sep
185
+ end
186
+ CoreExt::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case)
187
+ end
188
+
189
+ # Creates the name of a table like Rails does for models to table names. This method
190
+ # uses the +pluralize+ method on the last word in the string.
191
+ #
192
+ # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
193
+ # 'ham_and_egg'.tableize # => "ham_and_eggs"
194
+ # 'fancyCategory'.tableize # => "fancy_categories"
195
+ def tableize
196
+ CoreExt::Inflector.tableize(self)
197
+ end
198
+
199
+ # Creates a class name from a plural table name like Rails does for table names to models.
200
+ # Note that this returns a string and not a class. (To convert to an actual class
201
+ # follow +classify+ with +constantize+.)
202
+ #
203
+ # 'ham_and_eggs'.classify # => "HamAndEgg"
204
+ # 'posts'.classify # => "Post"
205
+ def classify
206
+ CoreExt::Inflector.classify(self)
207
+ end
208
+
209
+ # Capitalizes the first word, turns underscores into spaces, and strips a
210
+ # trailing '_id' if present.
211
+ # Like +titleize+, this is meant for creating pretty output.
212
+ #
213
+ # The capitalization of the first word can be turned off by setting the
214
+ # optional parameter +capitalize+ to false.
215
+ # By default, this parameter is true.
216
+ #
217
+ # 'employee_salary'.humanize # => "Employee salary"
218
+ # 'author_id'.humanize # => "Author"
219
+ # 'author_id'.humanize(capitalize: false) # => "author"
220
+ # '_id'.humanize # => "Id"
221
+ def humanize(options = {})
222
+ CoreExt::Inflector.humanize(self, options)
223
+ end
224
+
225
+ # Creates a foreign key name from a class name.
226
+ # +separate_class_name_and_id_with_underscore+ sets whether
227
+ # the method should put '_' between the name and 'id'.
228
+ #
229
+ # 'Message'.foreign_key # => "message_id"
230
+ # 'Message'.foreign_key(false) # => "messageid"
231
+ # 'Admin::Post'.foreign_key # => "post_id"
232
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
233
+ CoreExt::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
234
+ end
235
+ end
@@ -0,0 +1,13 @@
1
+ require 'core_ext/string_inquirer'
2
+
3
+ class String
4
+ # Wraps the current string in the <tt>CoreExt::StringInquirer</tt> class,
5
+ # which gives you a prettier way to test for equality.
6
+ #
7
+ # env = 'production'.inquiry
8
+ # env.production? # => true
9
+ # env.development? # => false
10
+ def inquiry
11
+ CoreExt::StringInquirer.new(self)
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ require 'core_ext/multibyte'
2
+
3
+ class String
4
+ # == Multibyte proxy
5
+ #
6
+ # +mb_chars+ is a multibyte safe proxy for string methods.
7
+ #
8
+ # It creates and returns an instance of the CoreExt::Multibyte::Chars class which
9
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
10
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
11
+ #
12
+ # >> "lj".upcase
13
+ # => "lj"
14
+ # >> "lj".mb_chars.upcase.to_s
15
+ # => "LJ"
16
+ #
17
+ # == Method chaining
18
+ #
19
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
20
+ # method chaining on the result of any of these methods.
21
+ #
22
+ # name.mb_chars.reverse.length # => 12
23
+ #
24
+ # == Interoperability and configuration
25
+ #
26
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
27
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
28
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
29
+ #
30
+ # For more information about the methods defined on the Chars proxy see CoreExt::Multibyte::Chars. For
31
+ # information about how to change the default Multibyte behavior see CoreExt::Multibyte.
32
+ def mb_chars
33
+ CoreExt::Multibyte.proxy_class.new(self)
34
+ end
35
+
36
+ # Returns +true+ if string has utf_8 encoding.
37
+ #
38
+ # utf_8_str = "some string".encode "UTF-8"
39
+ # iso_str = "some string".encode "ISO-8859-1"
40
+ #
41
+ # utf_8_str.is_utf8? # => true
42
+ # iso_str.is_utf8? # => false
43
+ def is_utf8?
44
+ case encoding
45
+ when Encoding::UTF_8
46
+ valid_encoding?
47
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
48
+ dup.force_encoding(Encoding::UTF_8).valid_encoding?
49
+ else
50
+ false
51
+ end
52
+ end
53
+ end