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.
- data/.gitignore +3 -0
- data/Gemfile +1 -1
- data/README.md +231 -2
- data/Rakefile +3 -3
- data/app/app_delegate.rb +0 -2
- data/examples/Inflector/.gitignore +16 -0
- data/examples/Inflector/Gemfile +5 -0
- data/examples/Inflector/Rakefile +13 -0
- data/examples/Inflector/app/app_delegate.rb +8 -0
- data/examples/Inflector/app/inflections.rb +5 -0
- data/examples/Inflector/app/inflector_view_controller.rb +120 -0
- data/examples/Inflector/app/words_view_controller.rb +101 -0
- data/examples/Inflector/resources/Default-568h@2x.png +0 -0
- data/examples/Inflector/spec/main_spec.rb +9 -0
- data/lib/motion-support/core_ext/array.rb +13 -0
- data/lib/motion-support/core_ext/class.rb +8 -0
- data/lib/motion-support/core_ext/hash.rb +16 -0
- data/lib/motion-support/core_ext/integer.rb +9 -0
- data/lib/motion-support/core_ext/module.rb +14 -0
- data/lib/motion-support/core_ext/numeric.rb +8 -0
- data/lib/motion-support/core_ext/object.rb +14 -0
- data/lib/motion-support/core_ext/range.rb +8 -0
- data/lib/motion-support/core_ext/string.rb +14 -0
- data/lib/motion-support/core_ext/time.rb +19 -0
- data/lib/motion-support/core_ext.rb +3 -0
- data/lib/motion-support/inflector.rb +12 -156
- data/lib/motion-support.rb +5 -5
- data/motion/_stdlib/cgi.rb +22 -0
- data/motion/_stdlib/date.rb +77 -0
- data/motion/_stdlib/time.rb +19 -0
- data/motion/core_ext/array/access.rb +28 -0
- data/motion/core_ext/array/conversions.rb +86 -0
- data/motion/core_ext/array/extract_options.rb +11 -0
- data/motion/core_ext/array/grouping.rb +99 -0
- data/motion/core_ext/array/prepend_and_append.rb +7 -0
- data/motion/core_ext/array/wrap.rb +45 -0
- data/{lib/motion-support → motion/core_ext}/array.rb +0 -4
- data/motion/core_ext/class/attribute.rb +119 -0
- data/motion/core_ext/class/attribute_accessors.rb +168 -0
- data/motion/core_ext/date/acts_like.rb +8 -0
- data/motion/core_ext/date/calculations.rb +117 -0
- data/motion/core_ext/date/conversions.rb +56 -0
- data/motion/core_ext/date_and_time/calculations.rb +232 -0
- data/motion/core_ext/enumerable.rb +90 -0
- data/motion/core_ext/hash/deep_merge.rb +27 -0
- data/motion/core_ext/hash/except.rb +15 -0
- data/motion/core_ext/hash/indifferent_access.rb +19 -0
- data/motion/core_ext/hash/keys.rb +138 -0
- data/motion/core_ext/hash/reverse_merge.rb +22 -0
- data/motion/core_ext/hash/slice.rb +40 -0
- data/motion/core_ext/integer/inflections.rb +27 -0
- data/motion/core_ext/integer/multiple.rb +10 -0
- data/motion/core_ext/integer/time.rb +41 -0
- data/{lib/motion-support → motion/core_ext}/metaclass.rb +0 -0
- data/motion/core_ext/module/aliasing.rb +69 -0
- data/motion/core_ext/module/anonymous.rb +19 -0
- data/motion/core_ext/module/attr_internal.rb +38 -0
- data/motion/core_ext/module/attribute_accessors.rb +64 -0
- data/motion/core_ext/module/delegation.rb +175 -0
- data/motion/core_ext/module/introspection.rb +60 -0
- data/motion/core_ext/module/reachable.rb +5 -0
- data/motion/core_ext/module/remove_method.rb +12 -0
- data/motion/core_ext/ns_dictionary.rb +11 -0
- data/motion/core_ext/numeric/bytes.rb +44 -0
- data/motion/core_ext/numeric/conversions.rb +7 -0
- data/motion/core_ext/numeric/time.rb +75 -0
- data/motion/core_ext/object/acts_like.rb +10 -0
- data/motion/core_ext/object/blank.rb +105 -0
- data/motion/core_ext/object/deep_dup.rb +44 -0
- data/motion/core_ext/object/duplicable.rb +83 -0
- data/motion/core_ext/object/instance_variables.rb +28 -0
- data/motion/core_ext/object/to_param.rb +58 -0
- data/motion/core_ext/object/to_query.rb +26 -0
- data/motion/core_ext/object/try.rb +78 -0
- data/motion/core_ext/range/include_range.rb +23 -0
- data/motion/core_ext/range/overlaps.rb +8 -0
- data/motion/core_ext/regexp.rb +5 -0
- data/motion/core_ext/string/access.rb +104 -0
- data/motion/core_ext/string/behavior.rb +6 -0
- data/motion/core_ext/string/exclude.rb +11 -0
- data/motion/core_ext/string/filters.rb +55 -0
- data/motion/core_ext/string/indent.rb +43 -0
- data/motion/core_ext/string/inflections.rb +195 -0
- data/motion/core_ext/string/starts_ends_with.rb +4 -0
- data/motion/core_ext/string/strip.rb +24 -0
- data/motion/core_ext/time/acts_like.rb +8 -0
- data/motion/core_ext/time/calculations.rb +215 -0
- data/motion/core_ext/time/conversions.rb +52 -0
- data/motion/duration.rb +104 -0
- data/motion/hash_with_indifferent_access.rb +251 -0
- data/motion/inflections.rb +67 -0
- data/motion/inflector/inflections.rb +203 -0
- data/motion/inflector/methods.rb +321 -0
- data/{lib/motion-support → motion}/logger.rb +0 -0
- data/{lib/motion-support → motion}/version.rb +1 -1
- data/motion-support.gemspec +2 -2
- data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
- data/spec/motion-support/_helpers/inflector_test_cases.rb +313 -0
- data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
- data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
- data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
- data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
- data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
- data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
- data/spec/motion-support/{array_spec.rb → core_ext/array_spec.rb} +0 -5
- data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
- data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
- data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
- data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
- data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
- data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
- data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
- data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
- data/spec/motion-support/core_ext/hash/key_spec.rb +230 -0
- data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
- data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
- data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
- data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
- data/spec/motion-support/{metaclass_spec.rb → core_ext/metaclass_spec.rb} +0 -0
- data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
- data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
- data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
- data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
- data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
- data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
- data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
- data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
- data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
- data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
- data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
- data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
- data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
- data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
- data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
- data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
- data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
- data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
- data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
- data/spec/motion-support/core_ext/string/filter_spec.rb +48 -0
- data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
- data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
- data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
- data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
- data/spec/motion-support/core_ext/string_spec.rb +88 -0
- data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
- data/spec/motion-support/core_ext/time/conversion_spec.rb +54 -0
- data/spec/motion-support/duration_spec.rb +107 -0
- data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
- data/spec/motion-support/inflector_spec.rb +474 -35
- data/spec/motion-support/ns_dictionary_spec.rb +29 -0
- metadata +212 -35
- data/lib/motion-support/cattr_accessor.rb +0 -19
- data/lib/motion-support/class_inheritable_accessor.rb +0 -23
- data/lib/motion-support/class_inheritable_array.rb +0 -29
- data/lib/motion-support/hash.rb +0 -31
- data/lib/motion-support/nilclass.rb +0 -5
- data/lib/motion-support/object.rb +0 -17
- data/lib/motion-support/string.rb +0 -71
- data/spec/motion-support/cattr_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_array_spec.rb +0 -61
- data/spec/motion-support/hash_spec.rb +0 -31
- data/spec/motion-support/nilclass_spec.rb +0 -5
- data/spec/motion-support/object_spec.rb +0 -43
- data/spec/motion-support/string_spec.rb +0 -145
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
motion_require "../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
|
data/motion/duration.rb
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module MotionSupport
|
|
2
|
+
# Provides accurate date and time measurements using Date#advance and
|
|
3
|
+
# Time#advance, respectively. It mainly supports the methods on Numeric.
|
|
4
|
+
#
|
|
5
|
+
# 1.month.ago # equivalent to Time.now.advance(months: -1)
|
|
6
|
+
class Duration < BasicObject
|
|
7
|
+
attr_accessor :value, :parts
|
|
8
|
+
|
|
9
|
+
def initialize(value, parts) #:nodoc:
|
|
10
|
+
@value, @parts = value, parts
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Adds another Duration or a Numeric to this Duration. Numeric values
|
|
14
|
+
# are treated as seconds.
|
|
15
|
+
def +(other)
|
|
16
|
+
if Duration === other
|
|
17
|
+
Duration.new(value + other.value, @parts + other.parts)
|
|
18
|
+
else
|
|
19
|
+
Duration.new(value + other, @parts + [[:seconds, other]])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Subtracts another Duration or a Numeric from this Duration. Numeric
|
|
24
|
+
# values are treated as seconds.
|
|
25
|
+
def -(other)
|
|
26
|
+
self + (-other)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def -@ #:nodoc:
|
|
30
|
+
Duration.new(-value, parts.map { |type,number| [type, -number] })
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def is_a?(klass) #:nodoc:
|
|
34
|
+
Duration == klass || value.is_a?(klass)
|
|
35
|
+
end
|
|
36
|
+
alias :kind_of? :is_a?
|
|
37
|
+
|
|
38
|
+
# Returns +true+ if +other+ is also a Duration instance with the
|
|
39
|
+
# same +value+, or if <tt>other == value</tt>.
|
|
40
|
+
def ==(other)
|
|
41
|
+
if Duration === other
|
|
42
|
+
other.value == value
|
|
43
|
+
else
|
|
44
|
+
other == value
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.===(other) #:nodoc:
|
|
49
|
+
other.is_a?(Duration)
|
|
50
|
+
rescue ::NoMethodError
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Calculates a new Time or Date that is as far in the future
|
|
55
|
+
# as this Duration represents.
|
|
56
|
+
def since(time = ::Time.now)
|
|
57
|
+
sum(1, time)
|
|
58
|
+
end
|
|
59
|
+
alias :from_now :since
|
|
60
|
+
|
|
61
|
+
# Calculates a new Time or Date that is as far in the past
|
|
62
|
+
# as this Duration represents.
|
|
63
|
+
def ago(time = ::Time.now)
|
|
64
|
+
sum(-1, time)
|
|
65
|
+
end
|
|
66
|
+
alias :until :ago
|
|
67
|
+
|
|
68
|
+
def inspect #:nodoc:
|
|
69
|
+
consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
|
|
70
|
+
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
|
|
71
|
+
n = consolidated[length]
|
|
72
|
+
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
|
|
73
|
+
end.compact
|
|
74
|
+
parts = ["0 seconds"] if parts.empty?
|
|
75
|
+
parts.to_sentence
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def as_json(options = nil) #:nodoc:
|
|
79
|
+
to_i
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
protected
|
|
83
|
+
|
|
84
|
+
def sum(sign, time = ::Time.now) #:nodoc:
|
|
85
|
+
parts.inject(time) do |t,(type,number)|
|
|
86
|
+
if t.acts_like?(:time) || t.acts_like?(:date)
|
|
87
|
+
if type == :seconds
|
|
88
|
+
t.since(sign * number)
|
|
89
|
+
else
|
|
90
|
+
t.advance(type => sign * number)
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def method_missing(method, *args, &block) #:nodoc:
|
|
101
|
+
value.send(method, *args, &block)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
module MotionSupport
|
|
2
|
+
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
|
|
3
|
+
# to be the same.
|
|
4
|
+
#
|
|
5
|
+
# rgb = MotionSupport::HashWithIndifferentAccess.new
|
|
6
|
+
#
|
|
7
|
+
# rgb[:black] = '#000000'
|
|
8
|
+
# rgb[:black] # => '#000000'
|
|
9
|
+
# rgb['black'] # => '#000000'
|
|
10
|
+
#
|
|
11
|
+
# rgb['white'] = '#FFFFFF'
|
|
12
|
+
# rgb[:white] # => '#FFFFFF'
|
|
13
|
+
# rgb['white'] # => '#FFFFFF'
|
|
14
|
+
#
|
|
15
|
+
# Internally symbols are mapped to strings when used as keys in the entire
|
|
16
|
+
# writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
|
|
17
|
+
# mapping belongs to the public interface. For example, given:
|
|
18
|
+
#
|
|
19
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new(a: 1)
|
|
20
|
+
#
|
|
21
|
+
# You are guaranteed that the key is returned as a string:
|
|
22
|
+
#
|
|
23
|
+
# hash.keys # => ["a"]
|
|
24
|
+
#
|
|
25
|
+
# Technically other types of keys are accepted:
|
|
26
|
+
#
|
|
27
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new(a: 1)
|
|
28
|
+
# hash[0] = 0
|
|
29
|
+
# hash # => {"a"=>1, 0=>0}
|
|
30
|
+
#
|
|
31
|
+
# but this class is intended for use cases where strings or symbols are the
|
|
32
|
+
# expected keys and it is convenient to understand both as the same. For
|
|
33
|
+
# example the +params+ hash in Ruby on Rails.
|
|
34
|
+
#
|
|
35
|
+
# Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
|
|
36
|
+
#
|
|
37
|
+
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
|
|
38
|
+
#
|
|
39
|
+
# which may be handy.
|
|
40
|
+
class HashWithIndifferentAccess < Hash
|
|
41
|
+
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
|
|
42
|
+
# this class.
|
|
43
|
+
def extractable_options?
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def with_indifferent_access
|
|
48
|
+
dup
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def nested_under_indifferent_access
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def initialize(constructor = {})
|
|
56
|
+
if constructor.is_a?(Hash)
|
|
57
|
+
super()
|
|
58
|
+
update(constructor)
|
|
59
|
+
else
|
|
60
|
+
super(constructor)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def default(key = nil)
|
|
65
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
|
66
|
+
self[key]
|
|
67
|
+
else
|
|
68
|
+
super
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.new_from_hash_copying_default(hash)
|
|
73
|
+
new(hash).tap do |new_hash|
|
|
74
|
+
new_hash.default = hash.default
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.[](*args)
|
|
79
|
+
new.merge!(Hash[*args])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
|
83
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
|
84
|
+
|
|
85
|
+
# Assigns a new value to the hash:
|
|
86
|
+
#
|
|
87
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new
|
|
88
|
+
# hash[:key] = 'value'
|
|
89
|
+
#
|
|
90
|
+
# This value can be later fetched using either +:key+ or +'key'+.
|
|
91
|
+
def []=(key, value)
|
|
92
|
+
regular_writer(convert_key(key), convert_value(value))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
alias_method :store, :[]=
|
|
96
|
+
|
|
97
|
+
# Updates the receiver in-place, merging in the hash passed as argument:
|
|
98
|
+
#
|
|
99
|
+
# hash_1 = MotionSupport::HashWithIndifferentAccess.new
|
|
100
|
+
# hash_1[:key] = 'value'
|
|
101
|
+
#
|
|
102
|
+
# hash_2 = MotionSupport::HashWithIndifferentAccess.new
|
|
103
|
+
# hash_2[:key] = 'New Value!'
|
|
104
|
+
#
|
|
105
|
+
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
|
106
|
+
#
|
|
107
|
+
# The argument can be either an
|
|
108
|
+
# <tt>MotionSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
|
|
109
|
+
# In either case the merge respects the semantics of indifferent access.
|
|
110
|
+
#
|
|
111
|
+
# If the argument is a regular hash with keys +:key+ and +"key"+ only one
|
|
112
|
+
# of the values end up in the receiver, but which one is unspecified.
|
|
113
|
+
#
|
|
114
|
+
# When given a block, the value for duplicated keys will be determined
|
|
115
|
+
# by the result of invoking the block with the duplicated key, the value
|
|
116
|
+
# in the receiver, and the value in +other_hash+. The rules for duplicated
|
|
117
|
+
# keys follow the semantics of indifferent access:
|
|
118
|
+
#
|
|
119
|
+
# hash_1[:key] = 10
|
|
120
|
+
# hash_2['key'] = 12
|
|
121
|
+
# hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
|
|
122
|
+
def update(other_hash)
|
|
123
|
+
if other_hash.is_a? HashWithIndifferentAccess
|
|
124
|
+
super(other_hash)
|
|
125
|
+
else
|
|
126
|
+
other_hash.each_pair do |key, value|
|
|
127
|
+
if block_given? && key?(key)
|
|
128
|
+
value = yield(convert_key(key), self[key], value)
|
|
129
|
+
end
|
|
130
|
+
regular_writer(convert_key(key), convert_value(value))
|
|
131
|
+
end
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
alias_method :merge!, :update
|
|
137
|
+
|
|
138
|
+
# Checks the hash for a key matching the argument passed in:
|
|
139
|
+
#
|
|
140
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new
|
|
141
|
+
# hash['key'] = 'value'
|
|
142
|
+
# hash.key?(:key) # => true
|
|
143
|
+
# hash.key?('key') # => true
|
|
144
|
+
def key?(key)
|
|
145
|
+
super(convert_key(key))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
alias_method :include?, :key?
|
|
149
|
+
alias_method :has_key?, :key?
|
|
150
|
+
alias_method :member?, :key?
|
|
151
|
+
|
|
152
|
+
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
|
|
153
|
+
# either a string or a symbol:
|
|
154
|
+
#
|
|
155
|
+
# counters = MotionSupport::HashWithIndifferentAccess.new
|
|
156
|
+
# counters[:foo] = 1
|
|
157
|
+
#
|
|
158
|
+
# counters.fetch('foo') # => 1
|
|
159
|
+
# counters.fetch(:bar, 0) # => 0
|
|
160
|
+
# counters.fetch(:bar) {|key| 0} # => 0
|
|
161
|
+
# counters.fetch(:zoo) # => KeyError: key not found: "zoo"
|
|
162
|
+
def fetch(key, *extras)
|
|
163
|
+
super(convert_key(key), *extras)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Returns an array of the values at the specified indices:
|
|
167
|
+
#
|
|
168
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new
|
|
169
|
+
# hash[:a] = 'x'
|
|
170
|
+
# hash[:b] = 'y'
|
|
171
|
+
# hash.values_at('a', 'b') # => ["x", "y"]
|
|
172
|
+
def values_at(*indices)
|
|
173
|
+
indices.collect {|key| self[convert_key(key)]}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Returns an exact copy of the hash.
|
|
177
|
+
def dup
|
|
178
|
+
self.class.new(self).tap do |new_hash|
|
|
179
|
+
new_hash.default = default
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# This method has the same semantics of +update+, except it does not
|
|
184
|
+
# modify the receiver but rather returns a new hash with indifferent
|
|
185
|
+
# access with the result of the merge.
|
|
186
|
+
def merge(hash, &block)
|
|
187
|
+
self.dup.update(hash, &block)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Like +merge+ but the other way around: Merges the receiver into the
|
|
191
|
+
# argument and returns a new hash with indifferent access as result:
|
|
192
|
+
#
|
|
193
|
+
# hash = MotionSupport::HashWithIndifferentAccess.new
|
|
194
|
+
# hash['a'] = nil
|
|
195
|
+
# hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
|
|
196
|
+
def reverse_merge(other_hash)
|
|
197
|
+
super(self.class.new_from_hash_copying_default(other_hash))
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
|
|
201
|
+
def reverse_merge!(other_hash)
|
|
202
|
+
replace(reverse_merge( other_hash ))
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Replaces the contents of this hash with other_hash.
|
|
206
|
+
#
|
|
207
|
+
# h = { "a" => 100, "b" => 200 }
|
|
208
|
+
# h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
|
|
209
|
+
def replace(other_hash)
|
|
210
|
+
super(self.class.new_from_hash_copying_default(other_hash))
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Removes the specified key from the hash.
|
|
214
|
+
def delete(key)
|
|
215
|
+
super(convert_key(key))
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def stringify_keys!; self end
|
|
219
|
+
def deep_stringify_keys!; self end
|
|
220
|
+
def stringify_keys; dup end
|
|
221
|
+
def deep_stringify_keys; dup end
|
|
222
|
+
undef :symbolize_keys!
|
|
223
|
+
undef :deep_symbolize_keys!
|
|
224
|
+
def symbolize_keys; to_hash.symbolize_keys end
|
|
225
|
+
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
|
|
226
|
+
def to_options!; self end
|
|
227
|
+
|
|
228
|
+
# Convert to a regular hash with string keys.
|
|
229
|
+
def to_hash
|
|
230
|
+
Hash.new(default).merge!(self)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
protected
|
|
234
|
+
def convert_key(key)
|
|
235
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def convert_value(value)
|
|
239
|
+
if value.is_a? Hash
|
|
240
|
+
value.nested_under_indifferent_access
|
|
241
|
+
elsif value.is_a?(Array)
|
|
242
|
+
value = value.dup if value.frozen?
|
|
243
|
+
value.map! { |e| convert_value(e) }
|
|
244
|
+
else
|
|
245
|
+
value
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
HashWithIndifferentAccess = MotionSupport::HashWithIndifferentAccess
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
motion_require 'inflector/inflections'
|
|
2
|
+
|
|
3
|
+
module MotionSupport
|
|
4
|
+
Inflector.inflections do |inflect|
|
|
5
|
+
inflect.plural(/$/, 's')
|
|
6
|
+
inflect.plural(/s$/i, 's')
|
|
7
|
+
inflect.plural(/^(ax|test)is$/i, '\1es')
|
|
8
|
+
inflect.plural(/(octop|vir)us$/i, '\1i')
|
|
9
|
+
inflect.plural(/(octop|vir)i$/i, '\1i')
|
|
10
|
+
inflect.plural(/(alias|status)$/i, '\1es')
|
|
11
|
+
inflect.plural(/(bu)s$/i, '\1ses')
|
|
12
|
+
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
|
|
13
|
+
inflect.plural(/([ti])um$/i, '\1a')
|
|
14
|
+
inflect.plural(/([ti])a$/i, '\1a')
|
|
15
|
+
inflect.plural(/sis$/i, 'ses')
|
|
16
|
+
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
|
17
|
+
inflect.plural(/(hive)$/i, '\1s')
|
|
18
|
+
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
|
19
|
+
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
|
|
20
|
+
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
|
21
|
+
inflect.plural(/^(m|l)ouse$/i, '\1ice')
|
|
22
|
+
inflect.plural(/^(m|l)ice$/i, '\1ice')
|
|
23
|
+
inflect.plural(/^(ox)$/i, '\1en')
|
|
24
|
+
inflect.plural(/^(oxen)$/i, '\1')
|
|
25
|
+
inflect.plural(/(quiz)$/i, '\1zes')
|
|
26
|
+
|
|
27
|
+
inflect.singular(/s$/i, '')
|
|
28
|
+
inflect.singular(/(ss)$/i, '\1')
|
|
29
|
+
inflect.singular(/(n)ews$/i, '\1ews')
|
|
30
|
+
inflect.singular(/([ti])a$/i, '\1um')
|
|
31
|
+
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
|
|
32
|
+
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
|
|
33
|
+
inflect.singular(/([^f])ves$/i, '\1fe')
|
|
34
|
+
inflect.singular(/(hive)s$/i, '\1')
|
|
35
|
+
inflect.singular(/(tive)s$/i, '\1')
|
|
36
|
+
inflect.singular(/([lr])ves$/i, '\1f')
|
|
37
|
+
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
|
38
|
+
inflect.singular(/(s)eries$/i, '\1eries')
|
|
39
|
+
inflect.singular(/(m)ovies$/i, '\1ovie')
|
|
40
|
+
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
|
|
41
|
+
inflect.singular(/^(m|l)ice$/i, '\1ouse')
|
|
42
|
+
inflect.singular(/(bus)(es)?$/i, '\1')
|
|
43
|
+
inflect.singular(/(o)es$/i, '\1')
|
|
44
|
+
inflect.singular(/(shoe)s$/i, '\1')
|
|
45
|
+
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
|
|
46
|
+
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
|
|
47
|
+
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
|
|
48
|
+
inflect.singular(/(alias|status)(es)?$/i, '\1')
|
|
49
|
+
inflect.singular(/^(ox)en/i, '\1')
|
|
50
|
+
inflect.singular(/(vert|ind)ices$/i, '\1ex')
|
|
51
|
+
inflect.singular(/(matr)ices$/i, '\1ix')
|
|
52
|
+
inflect.singular(/(quiz)zes$/i, '\1')
|
|
53
|
+
inflect.singular(/(database)s$/i, '\1')
|
|
54
|
+
|
|
55
|
+
inflect.irregular('person', 'people')
|
|
56
|
+
inflect.irregular('man', 'men')
|
|
57
|
+
inflect.irregular('child', 'children')
|
|
58
|
+
inflect.irregular('sex', 'sexes')
|
|
59
|
+
inflect.irregular('move', 'moves')
|
|
60
|
+
inflect.irregular('cow', 'kine')
|
|
61
|
+
inflect.irregular('zombie', 'zombies')
|
|
62
|
+
|
|
63
|
+
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
|
|
64
|
+
|
|
65
|
+
inflect.acronym('UI')
|
|
66
|
+
end
|
|
67
|
+
end
|