motion_blender-support 0.2.7

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 (182) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +4 -0
  5. data/HACKS.md +7 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +359 -0
  8. data/Rakefile +14 -0
  9. data/app/app_delegate.rb +5 -0
  10. data/examples/Inflector/.gitignore +16 -0
  11. data/examples/Inflector/Gemfile +5 -0
  12. data/examples/Inflector/Rakefile +13 -0
  13. data/examples/Inflector/app/app_delegate.rb +8 -0
  14. data/examples/Inflector/app/inflections.rb +5 -0
  15. data/examples/Inflector/app/inflector_view_controller.rb +109 -0
  16. data/examples/Inflector/app/words_view_controller.rb +101 -0
  17. data/examples/Inflector/resources/Default-568h@2x.png +0 -0
  18. data/examples/Inflector/spec/main_spec.rb +9 -0
  19. data/lib/motion-support/callbacks.rb +8 -0
  20. data/lib/motion-support/concern.rb +4 -0
  21. data/lib/motion-support/core_ext/array.rb +10 -0
  22. data/lib/motion-support/core_ext/class.rb +5 -0
  23. data/lib/motion-support/core_ext/hash.rb +13 -0
  24. data/lib/motion-support/core_ext/integer.rb +6 -0
  25. data/lib/motion-support/core_ext/module.rb +11 -0
  26. data/lib/motion-support/core_ext/numeric.rb +6 -0
  27. data/lib/motion-support/core_ext/object.rb +12 -0
  28. data/lib/motion-support/core_ext/range.rb +5 -0
  29. data/lib/motion-support/core_ext/string.rb +13 -0
  30. data/lib/motion-support/core_ext/time.rb +16 -0
  31. data/lib/motion-support/core_ext.rb +13 -0
  32. data/lib/motion-support/inflector.rb +8 -0
  33. data/lib/motion-support.rb +81 -0
  34. data/motion/_stdlib/array.rb +13 -0
  35. data/motion/_stdlib/cgi.rb +22 -0
  36. data/motion/_stdlib/date.rb +81 -0
  37. data/motion/_stdlib/enumerable.rb +9 -0
  38. data/motion/_stdlib/time.rb +19 -0
  39. data/motion/callbacks.rb +511 -0
  40. data/motion/concern.rb +122 -0
  41. data/motion/core_ext/array/access.rb +28 -0
  42. data/motion/core_ext/array/conversions.rb +86 -0
  43. data/motion/core_ext/array/extract_options.rb +11 -0
  44. data/motion/core_ext/array/grouping.rb +99 -0
  45. data/motion/core_ext/array/prepend_and_append.rb +7 -0
  46. data/motion/core_ext/array/wrap.rb +45 -0
  47. data/motion/core_ext/array.rb +19 -0
  48. data/motion/core_ext/class/attribute.rb +119 -0
  49. data/motion/core_ext/class/attribute_accessors.rb +168 -0
  50. data/motion/core_ext/date/acts_like.rb +8 -0
  51. data/motion/core_ext/date/calculations.rb +117 -0
  52. data/motion/core_ext/date/conversions.rb +56 -0
  53. data/motion/core_ext/date_and_time/calculations.rb +232 -0
  54. data/motion/core_ext/enumerable.rb +90 -0
  55. data/motion/core_ext/hash/deep_delete_if.rb +23 -0
  56. data/motion/core_ext/hash/deep_merge.rb +27 -0
  57. data/motion/core_ext/hash/except.rb +15 -0
  58. data/motion/core_ext/hash/indifferent_access.rb +19 -0
  59. data/motion/core_ext/hash/keys.rb +150 -0
  60. data/motion/core_ext/hash/reverse_merge.rb +22 -0
  61. data/motion/core_ext/hash/slice.rb +40 -0
  62. data/motion/core_ext/integer/inflections.rb +27 -0
  63. data/motion/core_ext/integer/multiple.rb +10 -0
  64. data/motion/core_ext/integer/time.rb +41 -0
  65. data/motion/core_ext/kernel/singleton_class.rb +6 -0
  66. data/motion/core_ext/metaclass.rb +8 -0
  67. data/motion/core_ext/module/aliasing.rb +69 -0
  68. data/motion/core_ext/module/anonymous.rb +19 -0
  69. data/motion/core_ext/module/attr_internal.rb +38 -0
  70. data/motion/core_ext/module/attribute_accessors.rb +64 -0
  71. data/motion/core_ext/module/delegation.rb +175 -0
  72. data/motion/core_ext/module/introspection.rb +60 -0
  73. data/motion/core_ext/module/reachable.rb +5 -0
  74. data/motion/core_ext/module/remove_method.rb +12 -0
  75. data/motion/core_ext/ns_dictionary.rb +14 -0
  76. data/motion/core_ext/ns_string.rb +14 -0
  77. data/motion/core_ext/numeric/bytes.rb +44 -0
  78. data/motion/core_ext/numeric/conversions.rb +49 -0
  79. data/motion/core_ext/numeric/time.rb +75 -0
  80. data/motion/core_ext/object/acts_like.rb +10 -0
  81. data/motion/core_ext/object/blank.rb +105 -0
  82. data/motion/core_ext/object/deep_dup.rb +44 -0
  83. data/motion/core_ext/object/duplicable.rb +87 -0
  84. data/motion/core_ext/object/inclusion.rb +15 -0
  85. data/motion/core_ext/object/instance_variables.rb +28 -0
  86. data/motion/core_ext/object/to_param.rb +58 -0
  87. data/motion/core_ext/object/to_query.rb +26 -0
  88. data/motion/core_ext/object/try.rb +78 -0
  89. data/motion/core_ext/range/include_range.rb +23 -0
  90. data/motion/core_ext/range/overlaps.rb +8 -0
  91. data/motion/core_ext/regexp.rb +5 -0
  92. data/motion/core_ext/string/access.rb +104 -0
  93. data/motion/core_ext/string/behavior.rb +6 -0
  94. data/motion/core_ext/string/exclude.rb +11 -0
  95. data/motion/core_ext/string/filters.rb +55 -0
  96. data/motion/core_ext/string/indent.rb +43 -0
  97. data/motion/core_ext/string/inflections.rb +178 -0
  98. data/motion/core_ext/string/starts_ends_with.rb +4 -0
  99. data/motion/core_ext/string/strip.rb +24 -0
  100. data/motion/core_ext/time/acts_like.rb +8 -0
  101. data/motion/core_ext/time/calculations.rb +215 -0
  102. data/motion/core_ext/time/conversions.rb +52 -0
  103. data/motion/descendants_tracker.rb +50 -0
  104. data/motion/duration.rb +104 -0
  105. data/motion/hash_with_indifferent_access.rb +253 -0
  106. data/motion/inflections.rb +67 -0
  107. data/motion/inflector/inflections.rb +203 -0
  108. data/motion/inflector/methods.rb +321 -0
  109. data/motion/logger.rb +47 -0
  110. data/motion/number_helper.rb +54 -0
  111. data/motion/version.rb +3 -0
  112. data/motion_blender-support.gemspec +21 -0
  113. data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
  114. data/spec/motion-support/_helpers/inflector_test_cases.rb +270 -0
  115. data/spec/motion-support/callback_spec.rb +702 -0
  116. data/spec/motion-support/concern_spec.rb +93 -0
  117. data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
  118. data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
  119. data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
  120. data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
  121. data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
  122. data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
  123. data/spec/motion-support/core_ext/array_spec.rb +16 -0
  124. data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
  125. data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
  126. data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
  127. data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
  128. data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
  129. data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
  130. data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
  131. data/spec/motion-support/core_ext/hash/deep_delete_if_spec.rb +19 -0
  132. data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
  133. data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
  134. data/spec/motion-support/core_ext/hash/key_spec.rb +236 -0
  135. data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
  136. data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
  137. data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
  138. data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
  139. data/spec/motion-support/core_ext/kernel/singleton_class_spec.rb +9 -0
  140. data/spec/motion-support/core_ext/metaclass_spec.rb +9 -0
  141. data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
  142. data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
  143. data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
  144. data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
  145. data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
  146. data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
  147. data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
  148. data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
  149. data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
  150. data/spec/motion-support/core_ext/numeric/conversions_spec.rb +40 -0
  151. data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
  152. data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
  153. data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
  154. data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
  155. data/spec/motion-support/core_ext/object/inclusion_spec.rb +34 -0
  156. data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
  157. data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
  158. data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
  159. data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
  160. data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
  161. data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
  162. data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
  163. data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
  164. data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
  165. data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
  166. data/spec/motion-support/core_ext/string/filter_spec.rb +49 -0
  167. data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
  168. data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
  169. data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
  170. data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
  171. data/spec/motion-support/core_ext/string_spec.rb +88 -0
  172. data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
  173. data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
  174. data/spec/motion-support/core_ext/time/conversion_spec.rb +53 -0
  175. data/spec/motion-support/descendants_tracker_spec.rb +58 -0
  176. data/spec/motion-support/duration_spec.rb +107 -0
  177. data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
  178. data/spec/motion-support/inflector_spec.rb +504 -0
  179. data/spec/motion-support/ns_dictionary_spec.rb +89 -0
  180. data/spec/motion-support/ns_string_spec.rb +182 -0
  181. data/spec/motion-support/number_helper_spec.rb +55 -0
  182. metadata +352 -0
@@ -0,0 +1,55 @@
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 like mongolian vowel separator (U+180E).
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
+ def squish!
17
+ gsub!(/\A[[:space:]]+/, '')
18
+ gsub!(/[[:space:]]+\z/, '')
19
+ gsub!(/[[:space:]]+/, ' ')
20
+ self
21
+ end
22
+
23
+ # Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
24
+ #
25
+ # 'Once upon a time in a world far far away'.truncate(27)
26
+ # # => "Once upon a time in a wo..."
27
+ #
28
+ # Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
29
+ #
30
+ # 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
31
+ # # => "Once upon a time in a..."
32
+ #
33
+ # 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
34
+ # # => "Once upon a time in a..."
35
+ #
36
+ # The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
37
+ # for a total length not exceeding <tt>length</tt>:
38
+ #
39
+ # 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
40
+ # # => "And they f... (continued)"
41
+ def truncate(truncate_at, options = {})
42
+ return dup unless length > truncate_at
43
+
44
+ options[:omission] ||= '...'
45
+ length_with_room_for_omission = truncate_at - options[:omission].length
46
+ stop = \
47
+ if options[:separator]
48
+ rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
49
+ else
50
+ length_with_room_for_omission
51
+ end
52
+
53
+ self[0...stop] + options[:omission]
54
+ end
55
+ 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,178 @@
1
+ # String inflections define new methods on the String class to transform names for different purposes.
2
+ # For instance, you can figure out the name of a table from the name of a class.
3
+ #
4
+ # 'ScaleScore'.tableize # => "scale_scores"
5
+ #
6
+ class String
7
+ # Returns the plural form of the word in the string.
8
+ #
9
+ # If the optional parameter +count+ is specified,
10
+ # the singular form will be returned if <tt>count == 1</tt>.
11
+ # For any other value of +count+ the plural will be returned.
12
+ #
13
+ # 'post'.pluralize # => "posts"
14
+ # 'octopus'.pluralize # => "octopi"
15
+ # 'sheep'.pluralize # => "sheep"
16
+ # 'words'.pluralize # => "words"
17
+ # 'the blue mailman'.pluralize # => "the blue mailmen"
18
+ # 'CamelOctopus'.pluralize # => "CamelOctopi"
19
+ # 'apple'.pluralize(1) # => "apple"
20
+ # 'apple'.pluralize(2) # => "apples"
21
+ def pluralize(count = nil)
22
+ if count == 1
23
+ self
24
+ else
25
+ MotionSupport::Inflector.pluralize(self)
26
+ end
27
+ end
28
+
29
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
30
+ #
31
+ # 'posts'.singularize # => "post"
32
+ # 'octopi'.singularize # => "octopus"
33
+ # 'sheep'.singularize # => "sheep"
34
+ # 'word'.singularize # => "word"
35
+ # 'the blue mailmen'.singularize # => "the blue mailman"
36
+ # 'CamelOctopi'.singularize # => "CamelOctopus"
37
+ def singularize
38
+ MotionSupport::Inflector.singularize(self)
39
+ end
40
+
41
+ # +constantize+ tries to find a declared constant with the name specified
42
+ # in the string. It raises a NameError when the name is not in CamelCase
43
+ # or is not initialized. See MotionSupport::Inflector.constantize
44
+ #
45
+ # 'Module'.constantize # => Module
46
+ # 'Class'.constantize # => Class
47
+ # 'blargle'.constantize # => NameError: wrong constant name blargle
48
+ def constantize
49
+ MotionSupport::Inflector.constantize(self)
50
+ end
51
+
52
+ # +safe_constantize+ tries to find a declared constant with the name specified
53
+ # in the string. It returns nil when the name is not in CamelCase
54
+ # or is not initialized. See MotionSupport::Inflector.safe_constantize
55
+ #
56
+ # 'Module'.safe_constantize # => Module
57
+ # 'Class'.safe_constantize # => Class
58
+ # 'blargle'.safe_constantize # => nil
59
+ def safe_constantize
60
+ MotionSupport::Inflector.safe_constantize(self)
61
+ end
62
+
63
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
64
+ # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
65
+ #
66
+ # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
67
+ #
68
+ # 'active_record'.camelize # => "ActiveRecord"
69
+ # 'active_record'.camelize(:lower) # => "activeRecord"
70
+ # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
71
+ # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
72
+ def camelize(first_letter = :upper)
73
+ case first_letter
74
+ when :upper
75
+ MotionSupport::Inflector.camelize(self, true)
76
+ when :lower
77
+ MotionSupport::Inflector.camelize(self, false)
78
+ end
79
+ end
80
+ alias_method :camelcase, :camelize
81
+
82
+ # Capitalizes all the words and replaces some characters in the string to create
83
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
84
+ # used in the Rails internals.
85
+ #
86
+ # +titleize+ is also aliased as +titlecase+.
87
+ #
88
+ # 'man from the boondocks'.titleize # => "Man From The Boondocks"
89
+ # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
90
+ def titleize
91
+ MotionSupport::Inflector.titleize(self)
92
+ end
93
+ alias_method :titlecase, :titleize
94
+
95
+ # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
96
+ #
97
+ # +underscore+ will also change '::' to '/' to convert namespaces to paths.
98
+ #
99
+ # 'ActiveModel'.underscore # => "active_model"
100
+ # 'ActiveModel::Errors'.underscore # => "active_model/errors"
101
+ def underscore
102
+ MotionSupport::Inflector.underscore(self)
103
+ end
104
+
105
+ # Replaces underscores with dashes in the string.
106
+ #
107
+ # 'puni_puni'.dasherize # => "puni-puni"
108
+ def dasherize
109
+ MotionSupport::Inflector.dasherize(self)
110
+ end
111
+
112
+ # Removes the module part from the constant expression in the string.
113
+ #
114
+ # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
115
+ # 'Inflections'.demodulize # => "Inflections"
116
+ #
117
+ # See also +deconstantize+.
118
+ def demodulize
119
+ MotionSupport::Inflector.demodulize(self)
120
+ end
121
+
122
+ # Removes the rightmost segment from the constant expression in the string.
123
+ #
124
+ # 'Net::HTTP'.deconstantize # => "Net"
125
+ # '::Net::HTTP'.deconstantize # => "::Net"
126
+ # 'String'.deconstantize # => ""
127
+ # '::String'.deconstantize # => ""
128
+ # ''.deconstantize # => ""
129
+ #
130
+ # See also +demodulize+.
131
+ def deconstantize
132
+ MotionSupport::Inflector.deconstantize(self)
133
+ end
134
+
135
+ # Creates the name of a table like Rails does for models to table names. This method
136
+ # uses the +pluralize+ method on the last word in the string.
137
+ #
138
+ # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
139
+ # 'egg_and_ham'.tableize # => "egg_and_hams"
140
+ # 'fancyCategory'.tableize # => "fancy_categories"
141
+ def tableize
142
+ MotionSupport::Inflector.tableize(self)
143
+ end
144
+
145
+ # Create a class name from a plural table name like Rails does for table names to models.
146
+ # Note that this returns a string and not a class. (To convert to an actual class
147
+ # follow +classify+ with +constantize+.)
148
+ #
149
+ # 'egg_and_hams'.classify # => "EggAndHam"
150
+ # 'posts'.classify # => "Post"
151
+ #
152
+ # Singular names are not handled correctly.
153
+ #
154
+ # 'business'.classify # => "Busines"
155
+ def classify
156
+ MotionSupport::Inflector.classify(self)
157
+ end
158
+
159
+ # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
160
+ # Like +titleize+, this is meant for creating pretty output.
161
+ #
162
+ # 'employee_salary'.humanize # => "Employee salary"
163
+ # 'author_id'.humanize # => "Author"
164
+ def humanize
165
+ MotionSupport::Inflector.humanize(self)
166
+ end
167
+
168
+ # Creates a foreign key name from a class name.
169
+ # +separate_class_name_and_id_with_underscore+ sets whether
170
+ # the method should put '_' between the name and 'id'.
171
+ #
172
+ # 'Message'.foreign_key # => "message_id"
173
+ # 'Message'.foreign_key(false) # => "messageid"
174
+ # 'Admin::Post'.foreign_key # => "post_id"
175
+ def foreign_key(separate_class_name_and_id_with_underscore = true)
176
+ MotionSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
177
+ end
178
+ end
@@ -0,0 +1,4 @@
1
+ class String
2
+ alias_method :starts_with?, :start_with?
3
+ alias_method :ends_with?, :end_with?
4
+ end
@@ -0,0 +1,24 @@
1
+ class String
2
+ # Strips indentation in heredocs.
3
+ #
4
+ # For example in
5
+ #
6
+ # if options[:usage]
7
+ # puts <<-USAGE.strip_heredoc
8
+ # This command does such and such.
9
+ #
10
+ # Supported options are:
11
+ # -h This message
12
+ # ...
13
+ # USAGE
14
+ # end
15
+ #
16
+ # the user would see the usage message aligned against the left margin.
17
+ #
18
+ # Technically, it looks for the least indented line in the whole string, and removes
19
+ # that amount of leading whitespace.
20
+ def strip_heredoc
21
+ indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
22
+ gsub(/^[ \t]{#{indent}}/, '')
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../object/acts_like'
2
+
3
+ class Time
4
+ # Duck-types as a Time-like class. See Object#acts_like?.
5
+ def acts_like_time?
6
+ true
7
+ end
8
+ end
@@ -0,0 +1,215 @@
1
+ require_relative '../date_and_time/calculations'
2
+
3
+ class Time
4
+ include DateAndTime::Calculations
5
+
6
+ COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
7
+
8
+ class << self
9
+ # Return the number of days in the given month.
10
+ # If no year is specified, it will use the current year.
11
+ def days_in_month(month, year = now.year)
12
+ if month == 2 && ::Date.gregorian_leap?(year)
13
+ 29
14
+ else
15
+ COMMON_YEAR_DAYS_IN_MONTH[month]
16
+ end
17
+ end
18
+
19
+ # Alias for <tt>Time.now</tt>.
20
+ def current
21
+ ::Time.now
22
+ end
23
+ end
24
+
25
+ # Seconds since midnight: Time.now.seconds_since_midnight
26
+ def seconds_since_midnight
27
+ to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
28
+ end
29
+
30
+ # Returns the number of seconds until 23:59:59.
31
+ #
32
+ # Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
33
+ # Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
34
+ # Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
35
+ def seconds_until_end_of_day
36
+ end_of_day.to_i - to_i
37
+ end
38
+
39
+ # Returns a new Time where one or more of the elements have been changed according
40
+ # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
41
+ # <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
42
+ # then minute, sec, and usec is set to 0. If the hour and minute is passed, then
43
+ # sec and usec is set to 0. The +options+ parameter takes a hash with any of these
44
+ # keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
45
+ # <tt>:sec</tt>, <tt>:usec</tt>.
46
+ #
47
+ # Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
48
+ # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
49
+ # Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
50
+ def change(options)
51
+ new_year = options.fetch(:year, year)
52
+ new_month = options.fetch(:month, month)
53
+ new_day = options.fetch(:day, day)
54
+ new_hour = options.fetch(:hour, hour)
55
+ new_min = options.fetch(:min, options[:hour] ? 0 : min)
56
+ new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
57
+ new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
58
+
59
+ if utc?
60
+ ::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
61
+ elsif zone
62
+ ::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
63
+ else
64
+ ::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
65
+ end
66
+ end
67
+
68
+ # Uses Date to provide precise Time calculations for years, months, and days.
69
+ # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
70
+ # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
71
+ # <tt>:minutes</tt>, <tt>:seconds</tt>.
72
+ def advance(options)
73
+ unless options[:weeks].nil?
74
+ options[:weeks], partial_weeks = options[:weeks].divmod(1)
75
+ options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
76
+ end
77
+
78
+ unless options[:days].nil?
79
+ options[:days], partial_days = options[:days].divmod(1)
80
+ options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
81
+ end
82
+
83
+ d = to_date.advance(options)
84
+ time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
85
+ seconds_to_advance = \
86
+ options.fetch(:seconds, 0) +
87
+ options.fetch(:minutes, 0) * 60 +
88
+ options.fetch(:hours, 0) * 3600
89
+
90
+ if seconds_to_advance.zero?
91
+ time_advanced_by_date
92
+ else
93
+ time_advanced_by_date.since(seconds_to_advance)
94
+ end
95
+ end
96
+
97
+ # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
98
+ def ago(seconds)
99
+ since(-seconds)
100
+ end
101
+
102
+ # Returns a new Time representing the time a number of seconds since the instance time
103
+ def since(seconds)
104
+ self + seconds
105
+ rescue
106
+ to_datetime.since(seconds)
107
+ end
108
+ alias :in :since
109
+
110
+ # Returns a new Time representing the start of the day (0:00)
111
+ def beginning_of_day
112
+ #(self - seconds_since_midnight).change(usec: 0)
113
+ change(:hour => 0)
114
+ end
115
+ alias :midnight :beginning_of_day
116
+ alias :at_midnight :beginning_of_day
117
+ alias :at_beginning_of_day :beginning_of_day
118
+
119
+ # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
120
+ def end_of_day
121
+ change(
122
+ :hour => 23,
123
+ :min => 59,
124
+ :sec => 59,
125
+ :usec => Rational(999999999, 1000)
126
+ )
127
+ end
128
+ alias :at_end_of_day :end_of_day
129
+
130
+ # Returns a new Time representing the start of the hour (x:00)
131
+ def beginning_of_hour
132
+ change(:min => 0)
133
+ end
134
+ alias :at_beginning_of_hour :beginning_of_hour
135
+
136
+ # Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
137
+ def end_of_hour
138
+ change(
139
+ :min => 59,
140
+ :sec => 59,
141
+ :usec => Rational(999999999, 1000)
142
+ )
143
+ end
144
+ alias :at_end_of_hour :end_of_hour
145
+
146
+ # Returns a new Time representing the start of the minute (x:xx:00)
147
+ def beginning_of_minute
148
+ change(:sec => 0)
149
+ end
150
+ alias :at_beginning_of_minute :beginning_of_minute
151
+
152
+ # Returns a new Time representing the end of the minute, x:xx:59.999999 (.999999999 in ruby1.9)
153
+ def end_of_minute
154
+ change(
155
+ :sec => 59,
156
+ :usec => Rational(999999999, 1000)
157
+ )
158
+ end
159
+ alias :at_end_of_minute :end_of_minute
160
+
161
+ # Returns a Range representing the whole day of the current time.
162
+ def all_day
163
+ beginning_of_day..end_of_day
164
+ end
165
+
166
+ # Returns a Range representing the whole week of the current time.
167
+ # Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
168
+ def all_week(start_day = Date.beginning_of_week)
169
+ beginning_of_week(start_day)..end_of_week(start_day)
170
+ end
171
+
172
+ # Returns a Range representing the whole month of the current time.
173
+ def all_month
174
+ beginning_of_month..end_of_month
175
+ end
176
+
177
+ # Returns a Range representing the whole quarter of the current time.
178
+ def all_quarter
179
+ beginning_of_quarter..end_of_quarter
180
+ end
181
+
182
+ # Returns a Range representing the whole year of the current time.
183
+ def all_year
184
+ beginning_of_year..end_of_year
185
+ end
186
+
187
+ def plus_with_duration(other) #:nodoc:
188
+ if MotionSupport::Duration === other
189
+ other.since(self)
190
+ else
191
+ plus_without_duration(other)
192
+ end
193
+ end
194
+ alias_method :plus_without_duration, :+
195
+ alias_method :+, :plus_with_duration
196
+
197
+ def minus_with_duration(other) #:nodoc:
198
+ if MotionSupport::Duration === other
199
+ other.until(self)
200
+ else
201
+ minus_without_duration(other)
202
+ end
203
+ end
204
+ alias_method :minus_without_duration, :-
205
+ alias_method :-, :minus_with_duration
206
+
207
+ # Layers additional behavior on Time#<=> so that Date instances
208
+ # can be chronologically compared with a Time
209
+ def compare_with_coercion(other)
210
+ compare_without_coercion(other.to_time)
211
+ end
212
+ alias_method :compare_without_coercion, :<=>
213
+ alias_method :<=>, :compare_with_coercion
214
+
215
+ end
@@ -0,0 +1,52 @@
1
+ class Time
2
+ DATE_FORMATS = {
3
+ :db => '%Y-%m-%d %H:%M:%S',
4
+ :number => '%Y%m%d%H%M%S',
5
+ :nsec => '%Y%m%d%H%M%S%9N',
6
+ :time => '%H:%M',
7
+ :short => '%d %b %H:%M',
8
+ :long => '%B %d, %Y %H:%M',
9
+ :long_ordinal => lambda { |time|
10
+ day_format = MotionSupport::Inflector.ordinalize(time.day)
11
+ time.strftime("%B #{day_format}, %Y %H:%M")
12
+ },
13
+ :rfc822 => lambda { |time|
14
+ offset_format = time.formatted_offset(false)
15
+ time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
16
+ }
17
+ }
18
+
19
+ # Converts to a formatted string. See DATE_FORMATS for builtin formats.
20
+ #
21
+ # This method is aliased to <tt>to_s</tt>.
22
+ #
23
+ # time = Time.now # => Thu Jan 18 06:10:17 CST 2007
24
+ #
25
+ # time.to_formatted_s(:time) # => "06:10"
26
+ # time.to_s(:time) # => "06:10"
27
+ #
28
+ # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
29
+ # time.to_formatted_s(:number) # => "20070118061017"
30
+ # time.to_formatted_s(:short) # => "18 Jan 06:10"
31
+ # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
32
+ # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
33
+ # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
34
+ #
35
+ # == Adding your own time formats to +to_formatted_s+
36
+ # You can add your own formats to the Time::DATE_FORMATS hash.
37
+ # Use the format name as the hash key and either a strftime string
38
+ # or Proc instance that takes a time argument as the value.
39
+ #
40
+ # # config/initializers/time_formats.rb
41
+ # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
42
+ # Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
43
+ def to_formatted_s(format = :default)
44
+ if formatter = DATE_FORMATS[format]
45
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
46
+ else
47
+ to_default_s
48
+ end
49
+ end
50
+ alias_method :to_default_s, :to_s
51
+ alias_method :to_s, :to_formatted_s
52
+ end
@@ -0,0 +1,50 @@
1
+ module MotionSupport
2
+ # This module provides an internal implementation to track descendants
3
+ # which is faster than iterating through ObjectSpace.
4
+ module DescendantsTracker
5
+ @@direct_descendants = {}
6
+
7
+ class << self
8
+ def direct_descendants(klass)
9
+ @@direct_descendants[klass] || []
10
+ end
11
+
12
+ def descendants(klass)
13
+ arr = []
14
+ accumulate_descendants(klass, arr)
15
+ arr
16
+ end
17
+
18
+ def clear
19
+ @@direct_descendants.clear
20
+ end
21
+
22
+ # This is the only method that is not thread safe, but is only ever called
23
+ # during the eager loading phase.
24
+ def store_inherited(klass, descendant)
25
+ (@@direct_descendants[klass] ||= []) << descendant
26
+ end
27
+
28
+ private
29
+ def accumulate_descendants(klass, acc)
30
+ if direct_descendants = @@direct_descendants[klass]
31
+ acc.concat(direct_descendants)
32
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
33
+ end
34
+ end
35
+ end
36
+
37
+ def inherited(base)
38
+ DescendantsTracker.store_inherited(self, base)
39
+ super
40
+ end
41
+
42
+ def direct_descendants
43
+ DescendantsTracker.direct_descendants(self)
44
+ end
45
+
46
+ def descendants
47
+ DescendantsTracker.descendants(self)
48
+ end
49
+ end
50
+ end