motion-support 0.1.0 → 0.2.0

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