motion-support 0.1.0 → 0.2.0

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 (171) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +1 -1
  3. data/README.md +231 -2
  4. data/Rakefile +3 -3
  5. data/app/app_delegate.rb +0 -2
  6. data/examples/Inflector/.gitignore +16 -0
  7. data/examples/Inflector/Gemfile +5 -0
  8. data/examples/Inflector/Rakefile +13 -0
  9. data/examples/Inflector/app/app_delegate.rb +8 -0
  10. data/examples/Inflector/app/inflections.rb +5 -0
  11. data/examples/Inflector/app/inflector_view_controller.rb +120 -0
  12. data/examples/Inflector/app/words_view_controller.rb +101 -0
  13. data/examples/Inflector/resources/Default-568h@2x.png +0 -0
  14. data/examples/Inflector/spec/main_spec.rb +9 -0
  15. data/lib/motion-support/core_ext/array.rb +13 -0
  16. data/lib/motion-support/core_ext/class.rb +8 -0
  17. data/lib/motion-support/core_ext/hash.rb +16 -0
  18. data/lib/motion-support/core_ext/integer.rb +9 -0
  19. data/lib/motion-support/core_ext/module.rb +14 -0
  20. data/lib/motion-support/core_ext/numeric.rb +8 -0
  21. data/lib/motion-support/core_ext/object.rb +14 -0
  22. data/lib/motion-support/core_ext/range.rb +8 -0
  23. data/lib/motion-support/core_ext/string.rb +14 -0
  24. data/lib/motion-support/core_ext/time.rb +19 -0
  25. data/lib/motion-support/core_ext.rb +3 -0
  26. data/lib/motion-support/inflector.rb +12 -156
  27. data/lib/motion-support.rb +5 -5
  28. data/motion/_stdlib/cgi.rb +22 -0
  29. data/motion/_stdlib/date.rb +77 -0
  30. data/motion/_stdlib/time.rb +19 -0
  31. data/motion/core_ext/array/access.rb +28 -0
  32. data/motion/core_ext/array/conversions.rb +86 -0
  33. data/motion/core_ext/array/extract_options.rb +11 -0
  34. data/motion/core_ext/array/grouping.rb +99 -0
  35. data/motion/core_ext/array/prepend_and_append.rb +7 -0
  36. data/motion/core_ext/array/wrap.rb +45 -0
  37. data/{lib/motion-support → motion/core_ext}/array.rb +0 -4
  38. data/motion/core_ext/class/attribute.rb +119 -0
  39. data/motion/core_ext/class/attribute_accessors.rb +168 -0
  40. data/motion/core_ext/date/acts_like.rb +8 -0
  41. data/motion/core_ext/date/calculations.rb +117 -0
  42. data/motion/core_ext/date/conversions.rb +56 -0
  43. data/motion/core_ext/date_and_time/calculations.rb +232 -0
  44. data/motion/core_ext/enumerable.rb +90 -0
  45. data/motion/core_ext/hash/deep_merge.rb +27 -0
  46. data/motion/core_ext/hash/except.rb +15 -0
  47. data/motion/core_ext/hash/indifferent_access.rb +19 -0
  48. data/motion/core_ext/hash/keys.rb +138 -0
  49. data/motion/core_ext/hash/reverse_merge.rb +22 -0
  50. data/motion/core_ext/hash/slice.rb +40 -0
  51. data/motion/core_ext/integer/inflections.rb +27 -0
  52. data/motion/core_ext/integer/multiple.rb +10 -0
  53. data/motion/core_ext/integer/time.rb +41 -0
  54. data/{lib/motion-support → motion/core_ext}/metaclass.rb +0 -0
  55. data/motion/core_ext/module/aliasing.rb +69 -0
  56. data/motion/core_ext/module/anonymous.rb +19 -0
  57. data/motion/core_ext/module/attr_internal.rb +38 -0
  58. data/motion/core_ext/module/attribute_accessors.rb +64 -0
  59. data/motion/core_ext/module/delegation.rb +175 -0
  60. data/motion/core_ext/module/introspection.rb +60 -0
  61. data/motion/core_ext/module/reachable.rb +5 -0
  62. data/motion/core_ext/module/remove_method.rb +12 -0
  63. data/motion/core_ext/ns_dictionary.rb +11 -0
  64. data/motion/core_ext/numeric/bytes.rb +44 -0
  65. data/motion/core_ext/numeric/conversions.rb +7 -0
  66. data/motion/core_ext/numeric/time.rb +75 -0
  67. data/motion/core_ext/object/acts_like.rb +10 -0
  68. data/motion/core_ext/object/blank.rb +105 -0
  69. data/motion/core_ext/object/deep_dup.rb +44 -0
  70. data/motion/core_ext/object/duplicable.rb +83 -0
  71. data/motion/core_ext/object/instance_variables.rb +28 -0
  72. data/motion/core_ext/object/to_param.rb +58 -0
  73. data/motion/core_ext/object/to_query.rb +26 -0
  74. data/motion/core_ext/object/try.rb +78 -0
  75. data/motion/core_ext/range/include_range.rb +23 -0
  76. data/motion/core_ext/range/overlaps.rb +8 -0
  77. data/motion/core_ext/regexp.rb +5 -0
  78. data/motion/core_ext/string/access.rb +104 -0
  79. data/motion/core_ext/string/behavior.rb +6 -0
  80. data/motion/core_ext/string/exclude.rb +11 -0
  81. data/motion/core_ext/string/filters.rb +55 -0
  82. data/motion/core_ext/string/indent.rb +43 -0
  83. data/motion/core_ext/string/inflections.rb +195 -0
  84. data/motion/core_ext/string/starts_ends_with.rb +4 -0
  85. data/motion/core_ext/string/strip.rb +24 -0
  86. data/motion/core_ext/time/acts_like.rb +8 -0
  87. data/motion/core_ext/time/calculations.rb +215 -0
  88. data/motion/core_ext/time/conversions.rb +52 -0
  89. data/motion/duration.rb +104 -0
  90. data/motion/hash_with_indifferent_access.rb +251 -0
  91. data/motion/inflections.rb +67 -0
  92. data/motion/inflector/inflections.rb +203 -0
  93. data/motion/inflector/methods.rb +321 -0
  94. data/{lib/motion-support → motion}/logger.rb +0 -0
  95. data/{lib/motion-support → motion}/version.rb +1 -1
  96. data/motion-support.gemspec +2 -2
  97. data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
  98. data/spec/motion-support/_helpers/inflector_test_cases.rb +313 -0
  99. data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
  100. data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
  101. data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
  102. data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
  103. data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
  104. data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
  105. data/spec/motion-support/{array_spec.rb → core_ext/array_spec.rb} +0 -5
  106. data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
  107. data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
  108. data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
  109. data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
  110. data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
  111. data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
  112. data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
  113. data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
  114. data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
  115. data/spec/motion-support/core_ext/hash/key_spec.rb +230 -0
  116. data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
  117. data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
  118. data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
  119. data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
  120. data/spec/motion-support/{metaclass_spec.rb → core_ext/metaclass_spec.rb} +0 -0
  121. data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
  122. data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
  123. data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
  124. data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
  125. data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
  126. data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
  127. data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
  128. data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
  129. data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
  130. data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
  131. data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
  132. data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
  133. data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
  134. data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
  135. data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
  136. data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
  137. data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
  138. data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
  139. data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
  140. data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
  141. data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
  142. data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
  143. data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
  144. data/spec/motion-support/core_ext/string/filter_spec.rb +48 -0
  145. data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
  146. data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
  147. data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
  148. data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
  149. data/spec/motion-support/core_ext/string_spec.rb +88 -0
  150. data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
  151. data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
  152. data/spec/motion-support/core_ext/time/conversion_spec.rb +54 -0
  153. data/spec/motion-support/duration_spec.rb +107 -0
  154. data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
  155. data/spec/motion-support/inflector_spec.rb +474 -35
  156. data/spec/motion-support/ns_dictionary_spec.rb +29 -0
  157. metadata +212 -35
  158. data/lib/motion-support/cattr_accessor.rb +0 -19
  159. data/lib/motion-support/class_inheritable_accessor.rb +0 -23
  160. data/lib/motion-support/class_inheritable_array.rb +0 -29
  161. data/lib/motion-support/hash.rb +0 -31
  162. data/lib/motion-support/nilclass.rb +0 -5
  163. data/lib/motion-support/object.rb +0 -17
  164. data/lib/motion-support/string.rb +0 -71
  165. data/spec/motion-support/cattr_accessor_spec.rb +0 -49
  166. data/spec/motion-support/class_inheritable_accessor_spec.rb +0 -49
  167. data/spec/motion-support/class_inheritable_array_spec.rb +0 -61
  168. data/spec/motion-support/hash_spec.rb +0 -31
  169. data/spec/motion-support/nilclass_spec.rb +0 -5
  170. data/spec/motion-support/object_spec.rb +0 -43
  171. data/spec/motion-support/string_spec.rb +0 -145
@@ -0,0 +1,203 @@
1
+ motion_require '../core_ext/array/prepend_and_append'
2
+
3
+ module MotionSupport
4
+ module Inflector
5
+ extend self
6
+
7
+ # A singleton instance of this class is yielded by Inflector.inflections,
8
+ # which can then be used to specify additional inflection rules.
9
+ #
10
+ # MotionSupport::Inflector.inflections do |inflect|
11
+ # inflect.plural /^(ox)$/i, '\1\2en'
12
+ # inflect.singular /^(ox)en/i, '\1'
13
+ #
14
+ # inflect.irregular 'octopus', 'octopi'
15
+ #
16
+ # inflect.uncountable 'equipment'
17
+ # end
18
+ #
19
+ # New rules are added at the top. So in the example above, the irregular
20
+ # rule for octopus will now be the first of the pluralization and
21
+ # singularization rules that is runs. This guarantees that your rules run
22
+ # before any of the rules that may already have been loaded.
23
+ class Inflections
24
+ def self.instance
25
+ @__instance__ ||= new
26
+ end
27
+
28
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
29
+
30
+ def initialize
31
+ @plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
32
+ end
33
+
34
+ # Private, for the test suite.
35
+ def initialize_dup(orig) # :nodoc:
36
+ %w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
37
+ instance_variable_set("@#{scope}", orig.send(scope).dup)
38
+ end
39
+ end
40
+
41
+ # Specifies a new acronym. An acronym must be specified as it will appear
42
+ # in a camelized string. An underscore string that contains the acronym
43
+ # will retain the acronym when passed to +camelize+, +humanize+, or
44
+ # +titleize+. A camelized string that contains the acronym will maintain
45
+ # the acronym when titleized or humanized, and will convert the acronym
46
+ # into a non-delimited single lowercase word when passed to +underscore+.
47
+ #
48
+ # acronym 'HTML'
49
+ # titleize 'html' #=> 'HTML'
50
+ # camelize 'html' #=> 'HTML'
51
+ # underscore 'MyHTML' #=> 'my_html'
52
+ #
53
+ # The acronym, however, must occur as a delimited unit and not be part of
54
+ # another word for conversions to recognize it:
55
+ #
56
+ # acronym 'HTTP'
57
+ # camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
58
+ # camelize 'https' #=> 'Https', not 'HTTPs'
59
+ # underscore 'HTTPS' #=> 'http_s', not 'https'
60
+ #
61
+ # acronym 'HTTPS'
62
+ # camelize 'https' #=> 'HTTPS'
63
+ # underscore 'HTTPS' #=> 'https'
64
+ #
65
+ # Note: Acronyms that are passed to +pluralize+ will no longer be
66
+ # recognized, since the acronym will not occur as a delimited unit in the
67
+ # pluralized result. To work around this, you must specify the pluralized
68
+ # form as an acronym as well:
69
+ #
70
+ # acronym 'API'
71
+ # camelize(pluralize('api')) #=> 'Apis'
72
+ #
73
+ # acronym 'APIs'
74
+ # camelize(pluralize('api')) #=> 'APIs'
75
+ #
76
+ # +acronym+ may be used to specify any word that contains an acronym or
77
+ # otherwise needs to maintain a non-standard capitalization. The only
78
+ # restriction is that the word must begin with a capital letter.
79
+ #
80
+ # acronym 'RESTful'
81
+ # underscore 'RESTful' #=> 'restful'
82
+ # underscore 'RESTfulController' #=> 'restful_controller'
83
+ # titleize 'RESTfulController' #=> 'RESTful Controller'
84
+ # camelize 'restful' #=> 'RESTful'
85
+ # camelize 'restful_controller' #=> 'RESTfulController'
86
+ #
87
+ # acronym 'McDonald'
88
+ # underscore 'McDonald' #=> 'mcdonald'
89
+ # camelize 'mcdonald' #=> 'McDonald'
90
+ def acronym(word)
91
+ @acronyms[word.downcase] = word
92
+ @acronym_regex = /#{@acronyms.values.join("|")}/
93
+ end
94
+
95
+ # Specifies a new pluralization rule and its replacement. The rule can
96
+ # either be a string or a regular expression. The replacement should
97
+ # always be a string that may include references to the matched data from
98
+ # the rule.
99
+ def plural(rule, replacement)
100
+ @uncountables.delete(rule) if rule.is_a?(String)
101
+ @uncountables.delete(replacement)
102
+ @plurals.prepend([rule, replacement])
103
+ end
104
+
105
+ # Specifies a new singularization rule and its replacement. The rule can
106
+ # either be a string or a regular expression. The replacement should
107
+ # always be a string that may include references to the matched data from
108
+ # the rule.
109
+ def singular(rule, replacement)
110
+ @uncountables.delete(rule) if rule.is_a?(String)
111
+ @uncountables.delete(replacement)
112
+ @singulars.prepend([rule, replacement])
113
+ end
114
+
115
+ # Specifies a new irregular that applies to both pluralization and
116
+ # singularization at the same time. This can only be used for strings, not
117
+ # regular expressions. You simply pass the irregular in singular and
118
+ # plural form.
119
+ #
120
+ # irregular 'octopus', 'octopi'
121
+ # irregular 'person', 'people'
122
+ def irregular(singular, plural)
123
+ @uncountables.delete(singular)
124
+ @uncountables.delete(plural)
125
+
126
+ s0 = singular[0]
127
+ srest = singular[1..-1]
128
+
129
+ p0 = plural[0]
130
+ prest = plural[1..-1]
131
+
132
+ if s0.upcase == p0.upcase
133
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
134
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
135
+
136
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
137
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
138
+ else
139
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
140
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
141
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
142
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
143
+
144
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
145
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
146
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
147
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
148
+ end
149
+ end
150
+
151
+ # Add uncountable words that shouldn't be attempted inflected.
152
+ #
153
+ # uncountable 'money'
154
+ # uncountable 'money', 'information'
155
+ # uncountable %w( money information rice )
156
+ def uncountable(*words)
157
+ (@uncountables << words).flatten!
158
+ end
159
+
160
+ # Specifies a humanized form of a string by a regular expression rule or
161
+ # by a string mapping. When using a regular expression based replacement,
162
+ # the normal humanize formatting is called after the replacement. When a
163
+ # string is used, the human form should be specified as desired (example:
164
+ # 'The name', not 'the_name').
165
+ #
166
+ # human /_cnt$/i, '\1_count'
167
+ # human 'legacy_col_person_name', 'Name'
168
+ def human(rule, replacement)
169
+ @humans.prepend([rule, replacement])
170
+ end
171
+
172
+ # Clears the loaded inflections within a given scope (default is
173
+ # <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
174
+ # options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
175
+ # <tt>:humans</tt>.
176
+ #
177
+ # clear :all
178
+ # clear :plurals
179
+ def clear(scope = :all)
180
+ case scope
181
+ when :all
182
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
183
+ else
184
+ instance_variable_set "@#{scope}", []
185
+ end
186
+ end
187
+ end
188
+
189
+ # Yields a singleton instance of Inflector::Inflections so you can specify
190
+ # additional inflector rules.
191
+ #
192
+ # MotionSupport::Inflector.inflections do |inflect|
193
+ # inflect.uncountable 'rails'
194
+ # end
195
+ def inflections
196
+ if block_given?
197
+ yield Inflections.instance
198
+ else
199
+ Inflections.instance
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,321 @@
1
+ module MotionSupport
2
+ # The Inflector transforms words from singular to plural, class names to table
3
+ # names, modularized class names to ones without, and class names to foreign
4
+ # keys. The default inflections for pluralization, singularization, and
5
+ # uncountable words are kept in inflections.rb.
6
+ module Inflector
7
+ extend self
8
+
9
+ # Returns the plural form of the word in the string.
10
+ #
11
+ # 'post'.pluralize # => "posts"
12
+ # 'octopus'.pluralize # => "octopi"
13
+ # 'sheep'.pluralize # => "sheep"
14
+ # 'words'.pluralize # => "words"
15
+ # 'CamelOctopus'.pluralize # => "CamelOctopi"
16
+ def pluralize(word)
17
+ apply_inflections(word, inflections.plurals)
18
+ end
19
+
20
+ # The reverse of +pluralize+, returns the singular form of a word in a
21
+ # string.
22
+ #
23
+ # 'posts'.singularize # => "post"
24
+ # 'octopi'.singularize # => "octopus"
25
+ # 'sheep'.singularize # => "sheep"
26
+ # 'word'.singularize # => "word"
27
+ # 'CamelOctopi'.singularize # => "CamelOctopus"
28
+ def singularize(word)
29
+ apply_inflections(word, inflections.singulars)
30
+ end
31
+
32
+ # By default, +camelize+ converts strings to UpperCamelCase. If the argument
33
+ # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
34
+ # lowerCamelCase.
35
+ #
36
+ # +camelize+ will also convert '/' to '::' which is useful for converting
37
+ # paths to namespaces.
38
+ #
39
+ # 'active_model'.camelize # => "ActiveModel"
40
+ # 'active_model'.camelize(:lower) # => "activeModel"
41
+ # 'active_model/errors'.camelize # => "ActiveModel::Errors"
42
+ # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
43
+ #
44
+ # As a rule of thumb you can think of +camelize+ as the inverse of
45
+ # +underscore+, though there are cases where that does not hold:
46
+ #
47
+ # 'SSLError'.underscore.camelize # => "SslError"
48
+ def camelize(term, uppercase_first_letter = true)
49
+ string = term.to_s
50
+ if uppercase_first_letter
51
+ string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
52
+ else
53
+ string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
54
+ end
55
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
56
+ end
57
+
58
+ # Makes an underscored, lowercase form from the expression in the string.
59
+ #
60
+ # Changes '::' to '/' to convert namespaces to paths.
61
+ #
62
+ # 'ActiveModel'.underscore # => "active_model"
63
+ # 'ActiveModel::Errors'.underscore # => "active_model/errors"
64
+ #
65
+ # As a rule of thumb you can think of +underscore+ as the inverse of
66
+ # +camelize+, though there are cases where that does not hold:
67
+ #
68
+ # 'SSLError'.underscore.camelize # => "SslError"
69
+ def underscore(camel_cased_word)
70
+ word = camel_cased_word.to_s.dup
71
+ word.gsub!('::', '/')
72
+ word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
73
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
74
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
75
+ word.tr!("-", "_")
76
+ word.downcase!
77
+ word
78
+ end
79
+
80
+ # Capitalizes the first word and turns underscores into spaces and strips a
81
+ # trailing "_id", if any. Like +titleize+, this is meant for creating pretty
82
+ # output.
83
+ #
84
+ # 'employee_salary'.humanize # => "Employee salary"
85
+ # 'author_id'.humanize # => "Author"
86
+ def humanize(lower_case_and_underscored_word)
87
+ result = lower_case_and_underscored_word.to_s.dup
88
+ inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
89
+ result.gsub!(/_id$/, "")
90
+ result.tr!('_', ' ')
91
+ result.gsub(/([a-z\d]*)/i) { |match|
92
+ "#{inflections.acronyms[match] || match.downcase}"
93
+ }.gsub(/^\w/) { $&.upcase }
94
+ end
95
+
96
+ # Capitalizes all the words and replaces some characters in the string to
97
+ # create a nicer looking title. +titleize+ is meant for creating pretty
98
+ # output. It is not used in the Rails internals.
99
+ #
100
+ # +titleize+ is also aliased as +titlecase+.
101
+ #
102
+ # 'man from the boondocks'.titleize # => "Man From The Boondocks"
103
+ # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
104
+ # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
105
+ # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
106
+ def titleize(word)
107
+ humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
108
+ end
109
+
110
+ # Create the name of a table like Rails does for models to table names. This
111
+ # method uses the +pluralize+ method on the last word in the string.
112
+ #
113
+ # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
114
+ # 'egg_and_ham'.tableize # => "egg_and_hams"
115
+ # 'fancyCategory'.tableize # => "fancy_categories"
116
+ def tableize(class_name)
117
+ pluralize(underscore(class_name))
118
+ end
119
+
120
+ # Create a class name from a plural table name like Rails does for table
121
+ # names to models. Note that this returns a string and not a Class (To
122
+ # convert to an actual class follow +classify+ with +constantize+).
123
+ #
124
+ # 'egg_and_hams'.classify # => "EggAndHam"
125
+ # 'posts'.classify # => "Post"
126
+ #
127
+ # Singular names are not handled correctly:
128
+ #
129
+ # 'business'.classify # => "Busines"
130
+ def classify(table_name)
131
+ # strip out any leading schema name
132
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
133
+ end
134
+
135
+ # Replaces underscores with dashes in the string.
136
+ #
137
+ # 'puni_puni'.dasherize # => "puni-puni"
138
+ def dasherize(underscored_word)
139
+ underscored_word.tr('_', '-')
140
+ end
141
+
142
+ # Removes the module part from the expression in the string.
143
+ #
144
+ # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
145
+ # 'Inflections'.demodulize # => "Inflections"
146
+ #
147
+ # See also +deconstantize+.
148
+ def demodulize(path)
149
+ path = path.to_s
150
+ if i = path.rindex('::')
151
+ path[(i+2)..-1]
152
+ else
153
+ path
154
+ end
155
+ end
156
+
157
+ # Removes the rightmost segment from the constant expression in the string.
158
+ #
159
+ # 'Net::HTTP'.deconstantize # => "Net"
160
+ # '::Net::HTTP'.deconstantize # => "::Net"
161
+ # 'String'.deconstantize # => ""
162
+ # '::String'.deconstantize # => ""
163
+ # ''.deconstantize # => ""
164
+ #
165
+ # See also +demodulize+.
166
+ def deconstantize(path)
167
+ path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
168
+ end
169
+
170
+ # Creates a foreign key name from a class name.
171
+ # +separate_class_name_and_id_with_underscore+ sets whether
172
+ # the method should put '_' between the name and 'id'.
173
+ #
174
+ # 'Message'.foreign_key # => "message_id"
175
+ # 'Message'.foreign_key(false) # => "messageid"
176
+ # 'Admin::Post'.foreign_key # => "post_id"
177
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
178
+ underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
179
+ end
180
+
181
+ # Tries to find a constant with the name specified in the argument string.
182
+ #
183
+ # 'Module'.constantize # => Module
184
+ # 'Test::Unit'.constantize # => Test::Unit
185
+ #
186
+ # The name is assumed to be the one of a top-level constant, no matter
187
+ # whether it starts with "::" or not. No lexical context is taken into
188
+ # account:
189
+ #
190
+ # C = 'outside'
191
+ # module M
192
+ # C = 'inside'
193
+ # C # => 'inside'
194
+ # 'C'.constantize # => 'outside', same as ::C
195
+ # end
196
+ #
197
+ # NameError is raised when the name is not in CamelCase or the constant is
198
+ # unknown.
199
+ def constantize(camel_cased_word)
200
+ names = camel_cased_word.split('::')
201
+ names.shift if names.empty? || names.first.empty?
202
+
203
+ names.inject(Object) do |constant, name|
204
+ if constant == Object
205
+ constant.const_get(name)
206
+ else
207
+ candidate = constant.const_get(name)
208
+ next candidate if constant.const_defined?(name, false)
209
+ next candidate unless Object.const_defined?(name)
210
+
211
+ # Go down the ancestors to check it it's owned
212
+ # directly before we reach Object or the end of ancestors.
213
+ constant = constant.ancestors.inject do |const, ancestor|
214
+ break const if ancestor == Object
215
+ break ancestor if ancestor.const_defined?(name, false)
216
+ const
217
+ end
218
+
219
+ # owner is in Object, so raise
220
+ constant.const_get(name, false)
221
+ end
222
+ end
223
+ end
224
+
225
+ # Tries to find a constant with the name specified in the argument string.
226
+ #
227
+ # 'Module'.safe_constantize # => Module
228
+ # 'Test::Unit'.safe_constantize # => Test::Unit
229
+ #
230
+ # The name is assumed to be the one of a top-level constant, no matter
231
+ # whether it starts with "::" or not. No lexical context is taken into
232
+ # account:
233
+ #
234
+ # C = 'outside'
235
+ # module M
236
+ # C = 'inside'
237
+ # C # => 'inside'
238
+ # 'C'.safe_constantize # => 'outside', same as ::C
239
+ # end
240
+ #
241
+ # +nil+ is returned when the name is not in CamelCase or the constant (or
242
+ # part of it) is unknown.
243
+ #
244
+ # 'blargle'.safe_constantize # => nil
245
+ # 'UnknownModule'.safe_constantize # => nil
246
+ # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
247
+ def safe_constantize(camel_cased_word)
248
+ constantize(camel_cased_word)
249
+ rescue NameError => e
250
+ raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
251
+ e.name.to_s == camel_cased_word.to_s
252
+ rescue ArgumentError => e
253
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
254
+ end
255
+
256
+ # Returns the suffix that should be added to a number to denote the position
257
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
258
+ #
259
+ # ordinal(1) # => "st"
260
+ # ordinal(2) # => "nd"
261
+ # ordinal(1002) # => "nd"
262
+ # ordinal(1003) # => "rd"
263
+ # ordinal(-11) # => "th"
264
+ # ordinal(-1021) # => "st"
265
+ def ordinal(number)
266
+ abs_number = number.to_i.abs
267
+
268
+ if (11..13).include?(abs_number % 100)
269
+ "th"
270
+ else
271
+ case abs_number % 10
272
+ when 1; "st"
273
+ when 2; "nd"
274
+ when 3; "rd"
275
+ else "th"
276
+ end
277
+ end
278
+ end
279
+
280
+ # Turns a number into an ordinal string used to denote the position in an
281
+ # ordered sequence such as 1st, 2nd, 3rd, 4th.
282
+ #
283
+ # ordinalize(1) # => "1st"
284
+ # ordinalize(2) # => "2nd"
285
+ # ordinalize(1002) # => "1002nd"
286
+ # ordinalize(1003) # => "1003rd"
287
+ # ordinalize(-11) # => "-11th"
288
+ # ordinalize(-1021) # => "-1021st"
289
+ def ordinalize(number)
290
+ "#{number}#{ordinal(number)}"
291
+ end
292
+
293
+ private
294
+
295
+ # Mount a regular expression that will match part by part of the constant.
296
+ # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
297
+ def const_regexp(camel_cased_word) #:nodoc:
298
+ parts = camel_cased_word.split("::")
299
+ last = parts.pop
300
+
301
+ parts.reverse.inject(last) do |acc, part|
302
+ part.empty? ? acc : "#{part}(::#{acc})?"
303
+ end
304
+ end
305
+
306
+ # Applies inflection rules for +singularize+ and +pluralize+.
307
+ #
308
+ # apply_inflections('post', inflections.plurals) # => "posts"
309
+ # apply_inflections('posts', inflections.singulars) # => "post"
310
+ def apply_inflections(word, rules)
311
+ result = word.to_s.dup
312
+
313
+ if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
314
+ result
315
+ else
316
+ rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
317
+ result
318
+ end
319
+ end
320
+ end
321
+ end
File without changes
@@ -1,3 +1,3 @@
1
1
  module MotionSupport
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/motion-support/version', __FILE__)
2
+ require File.expand_path('../motion/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "motion-support"
@@ -14,6 +14,6 @@ Gem::Specification.new do |s|
14
14
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
15
15
  s.require_paths = ["lib"]
16
16
 
17
- s.add_dependency 'bubble-wrap'
17
+ s.add_dependency "motion-require", ">= 0.0.6"
18
18
  s.add_development_dependency 'rake'
19
19
  end
@@ -0,0 +1,75 @@
1
+ module Ace
2
+ module Base
3
+ class Case
4
+ class Dice
5
+ end
6
+ end
7
+ class Fase < Case
8
+ end
9
+ end
10
+ class Gas
11
+ include Base
12
+ end
13
+ end
14
+
15
+ class Object
16
+ module AddtlGlobalConstants
17
+ class Case
18
+ class Dice
19
+ end
20
+ end
21
+ end
22
+ include AddtlGlobalConstants
23
+ end
24
+
25
+ module ConstantizeTestCases
26
+ def run_constantize_tests_on(&block)
27
+ yield("Ace::Base::Case").should == Ace::Base::Case
28
+ yield("::Ace::Base::Case").should == Ace::Base::Case
29
+ yield("Ace::Base::Case::Dice").should == Ace::Base::Case::Dice
30
+ yield("Ace::Base::Fase::Dice").should == Ace::Base::Fase::Dice
31
+ yield("Ace::Gas::Case").should == Ace::Gas::Case
32
+ yield("Ace::Gas::Case::Dice").should == Ace::Gas::Case::Dice
33
+ yield("Case::Dice").should == Case::Dice
34
+ yield("Object::Case::Dice").should == Case::Dice
35
+ yield("ConstantizeTestCases").should == ConstantizeTestCases
36
+ yield("::ConstantizeTestCases").should == ConstantizeTestCases
37
+ yield("").should == Object
38
+ yield("::").should == Object
39
+ lambda { block.call("UnknownClass") }.should.raise NameError
40
+ lambda { block.call("UnknownClass::Ace") }.should.raise NameError
41
+ lambda { block.call("UnknownClass::Ace::Base") }.should.raise NameError
42
+ lambda { block.call("An invalid string") }.should.raise NameError
43
+ lambda { block.call("InvalidClass\n") }.should.raise NameError
44
+ lambda { block.call("Ace::ConstantizeTestCases") }.should.raise NameError
45
+ lambda { block.call("Ace::Base::ConstantizeTestCases") }.should.raise NameError
46
+ lambda { block.call("Ace::Gas::Base") }.should.raise NameError
47
+ lambda { block.call("Ace::Gas::ConstantizeTestCases") }.should.raise NameError
48
+ end
49
+
50
+ def run_safe_constantize_tests_on
51
+ yield("Ace::Base::Case").should == Ace::Base::Case
52
+ yield("::Ace::Base::Case").should == Ace::Base::Case
53
+ yield("Ace::Base::Case::Dice").should == Ace::Base::Case::Dice
54
+ yield("Ace::Base::Fase::Dice").should == Ace::Base::Fase::Dice
55
+ yield("Ace::Gas::Case").should == Ace::Gas::Case
56
+ yield("Ace::Gas::Case::Dice").should == Ace::Gas::Case::Dice
57
+ yield("Case::Dice").should == Case::Dice
58
+ yield("Object::Case::Dice").should == Case::Dice
59
+ yield("ConstantizeTestCases").should == ConstantizeTestCases
60
+ yield("::ConstantizeTestCases").should == ConstantizeTestCases
61
+ yield("").should == Object
62
+ yield("::").should == Object
63
+ yield("UnknownClass").should.be.nil
64
+ yield("UnknownClass::Ace").should.be.nil
65
+ yield("UnknownClass::Ace::Base").should.be.nil
66
+ yield("An invalid string").should.be.nil
67
+ yield("InvalidClass\n").should.be.nil
68
+ yield("blargle").should.be.nil
69
+ yield("Ace::ConstantizeTestCases").should.be.nil
70
+ yield("Ace::Base::ConstantizeTestCases").should.be.nil
71
+ yield("Ace::Gas::Base").should.be.nil
72
+ yield("Ace::Gas::ConstantizeTestCases").should.be.nil
73
+ yield("#<Class:0x7b8b718b>::Nested_1").should.be.nil
74
+ end
75
+ end