motion_blender-support 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/HACKS.md +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +359 -0
- data/Rakefile +14 -0
- data/app/app_delegate.rb +5 -0
- 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 +109 -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/callbacks.rb +8 -0
- data/lib/motion-support/concern.rb +4 -0
- data/lib/motion-support/core_ext/array.rb +10 -0
- data/lib/motion-support/core_ext/class.rb +5 -0
- data/lib/motion-support/core_ext/hash.rb +13 -0
- data/lib/motion-support/core_ext/integer.rb +6 -0
- data/lib/motion-support/core_ext/module.rb +11 -0
- data/lib/motion-support/core_ext/numeric.rb +6 -0
- data/lib/motion-support/core_ext/object.rb +12 -0
- data/lib/motion-support/core_ext/range.rb +5 -0
- data/lib/motion-support/core_ext/string.rb +13 -0
- data/lib/motion-support/core_ext/time.rb +16 -0
- data/lib/motion-support/core_ext.rb +13 -0
- data/lib/motion-support/inflector.rb +8 -0
- data/lib/motion-support.rb +81 -0
- data/motion/_stdlib/array.rb +13 -0
- data/motion/_stdlib/cgi.rb +22 -0
- data/motion/_stdlib/date.rb +81 -0
- data/motion/_stdlib/enumerable.rb +9 -0
- data/motion/_stdlib/time.rb +19 -0
- data/motion/callbacks.rb +511 -0
- data/motion/concern.rb +122 -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/motion/core_ext/array.rb +19 -0
- 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_delete_if.rb +23 -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 +150 -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/motion/core_ext/kernel/singleton_class.rb +6 -0
- data/motion/core_ext/metaclass.rb +8 -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 +14 -0
- data/motion/core_ext/ns_string.rb +14 -0
- data/motion/core_ext/numeric/bytes.rb +44 -0
- data/motion/core_ext/numeric/conversions.rb +49 -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 +87 -0
- data/motion/core_ext/object/inclusion.rb +15 -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 +178 -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/descendants_tracker.rb +50 -0
- data/motion/duration.rb +104 -0
- data/motion/hash_with_indifferent_access.rb +253 -0
- data/motion/inflections.rb +67 -0
- data/motion/inflector/inflections.rb +203 -0
- data/motion/inflector/methods.rb +321 -0
- data/motion/logger.rb +47 -0
- data/motion/number_helper.rb +54 -0
- data/motion/version.rb +3 -0
- data/motion_blender-support.gemspec +21 -0
- data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
- data/spec/motion-support/_helpers/inflector_test_cases.rb +270 -0
- data/spec/motion-support/callback_spec.rb +702 -0
- data/spec/motion-support/concern_spec.rb +93 -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/core_ext/array_spec.rb +16 -0
- 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_delete_if_spec.rb +19 -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 +236 -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/core_ext/kernel/singleton_class_spec.rb +9 -0
- data/spec/motion-support/core_ext/metaclass_spec.rb +9 -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/numeric/conversions_spec.rb +40 -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/inclusion_spec.rb +34 -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 +49 -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 +53 -0
- data/spec/motion-support/descendants_tracker_spec.rb +58 -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 +504 -0
- data/spec/motion-support/ns_dictionary_spec.rb +89 -0
- data/spec/motion-support/ns_string_spec.rb +182 -0
- data/spec/motion-support/number_helper_spec.rb +55 -0
- metadata +352 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
class Date
|
2
|
+
DATE_FORMATS = {
|
3
|
+
:short => '%e %b',
|
4
|
+
:long => '%B %e, %Y',
|
5
|
+
:db => '%Y-%m-%d',
|
6
|
+
:number => '%Y%m%d',
|
7
|
+
:long_ordinal => lambda { |date|
|
8
|
+
day_format = MotionSupport::Inflector.ordinalize(date.day)
|
9
|
+
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
|
10
|
+
},
|
11
|
+
:rfc822 => '%e %b %Y'
|
12
|
+
}
|
13
|
+
|
14
|
+
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
|
15
|
+
#
|
16
|
+
# This method is aliased to <tt>to_s</tt>.
|
17
|
+
#
|
18
|
+
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
|
19
|
+
#
|
20
|
+
# date.to_formatted_s(:db) # => "2007-11-10"
|
21
|
+
# date.to_s(:db) # => "2007-11-10"
|
22
|
+
#
|
23
|
+
# date.to_formatted_s(:short) # => "10 Nov"
|
24
|
+
# date.to_formatted_s(:long) # => "November 10, 2007"
|
25
|
+
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
|
26
|
+
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
|
27
|
+
#
|
28
|
+
# == Adding your own time formats to to_formatted_s
|
29
|
+
# You can add your own formats to the Date::DATE_FORMATS hash.
|
30
|
+
# Use the format name as the hash key and either a strftime string
|
31
|
+
# or Proc instance that takes a date argument as the value.
|
32
|
+
#
|
33
|
+
# # config/initializers/time_formats.rb
|
34
|
+
# Date::DATE_FORMATS[:month_and_year] = '%B %Y'
|
35
|
+
# Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
|
36
|
+
def to_formatted_s(format = :default)
|
37
|
+
if formatter = DATE_FORMATS[format]
|
38
|
+
if formatter.respond_to?(:call)
|
39
|
+
formatter.call(self).to_s
|
40
|
+
else
|
41
|
+
strftime(formatter)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
to_default_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
alias_method :to_default_s, :to_s
|
48
|
+
alias_method :to_s, :to_formatted_s
|
49
|
+
|
50
|
+
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
|
51
|
+
def readable_inspect
|
52
|
+
strftime('%a, %d %b %Y')
|
53
|
+
end
|
54
|
+
alias_method :default_inspect, :inspect
|
55
|
+
alias_method :inspect, :readable_inspect
|
56
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module DateAndTime
|
2
|
+
module Calculations
|
3
|
+
DAYS_INTO_WEEK = {
|
4
|
+
:monday => 0,
|
5
|
+
:tuesday => 1,
|
6
|
+
:wednesday => 2,
|
7
|
+
:thursday => 3,
|
8
|
+
:friday => 4,
|
9
|
+
:saturday => 5,
|
10
|
+
:sunday => 6
|
11
|
+
}
|
12
|
+
|
13
|
+
# Returns a new date/time representing yesterday.
|
14
|
+
def yesterday
|
15
|
+
advance(:days => -1)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a new date/time representing tomorrow.
|
19
|
+
def tomorrow
|
20
|
+
advance(:days => 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns true if the date/time is today.
|
24
|
+
def today?
|
25
|
+
to_date == ::Date.current
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the date/time is in the past.
|
29
|
+
def past?
|
30
|
+
self < self.class.current
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns true if the date/time is in the future.
|
34
|
+
def future?
|
35
|
+
self > self.class.current
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a new date/time the specified number of days ago.
|
39
|
+
def days_ago(days)
|
40
|
+
advance(:days => -days)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a new date/time the specified number of days in the future.
|
44
|
+
def days_since(days)
|
45
|
+
advance(:days => days)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a new date/time the specified number of weeks ago.
|
49
|
+
def weeks_ago(weeks)
|
50
|
+
advance(:weeks => -weeks)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a new date/time the specified number of weeks in the future.
|
54
|
+
def weeks_since(weeks)
|
55
|
+
advance(:weeks => weeks)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a new date/time the specified number of months ago.
|
59
|
+
def months_ago(months)
|
60
|
+
advance(:months => -months)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a new date/time the specified number of months in the future.
|
64
|
+
def months_since(months)
|
65
|
+
advance(:months => months)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a new date/time the specified number of years ago.
|
69
|
+
def years_ago(years)
|
70
|
+
advance(:years => -years)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns a new date/time the specified number of years in the future.
|
74
|
+
def years_since(years)
|
75
|
+
advance(:years => years)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a new date/time at the start of the month.
|
79
|
+
# DateTime objects will have a time set to 0:00.
|
80
|
+
def beginning_of_month
|
81
|
+
first_hour{ change(:day => 1) }
|
82
|
+
end
|
83
|
+
alias :at_beginning_of_month :beginning_of_month
|
84
|
+
|
85
|
+
# Returns a new date/time at the start of the quarter.
|
86
|
+
# Example: 1st January, 1st July, 1st October.
|
87
|
+
# DateTime objects will have a time set to 0:00.
|
88
|
+
def beginning_of_quarter
|
89
|
+
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
|
90
|
+
beginning_of_month.change(:month => first_quarter_month)
|
91
|
+
end
|
92
|
+
alias :at_beginning_of_quarter :beginning_of_quarter
|
93
|
+
|
94
|
+
# Returns a new date/time at the end of the quarter.
|
95
|
+
# Example: 31st March, 30th June, 30th September.
|
96
|
+
# DateTime objects will have a time set to 23:59:59.
|
97
|
+
def end_of_quarter
|
98
|
+
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
|
99
|
+
beginning_of_month.change(:month => last_quarter_month).end_of_month
|
100
|
+
end
|
101
|
+
alias :at_end_of_quarter :end_of_quarter
|
102
|
+
|
103
|
+
# Return a new date/time at the beginning of the year.
|
104
|
+
# Example: 1st January.
|
105
|
+
# DateTime objects will have a time set to 0:00.
|
106
|
+
def beginning_of_year
|
107
|
+
change(:month => 1).beginning_of_month
|
108
|
+
end
|
109
|
+
alias :at_beginning_of_year :beginning_of_year
|
110
|
+
|
111
|
+
# Returns a new date/time representing the given day in the next week.
|
112
|
+
# Week is assumed to start on +start_day+, default is
|
113
|
+
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
114
|
+
# DateTime objects have their time set to 0:00.
|
115
|
+
def next_week(start_day = Date.beginning_of_week)
|
116
|
+
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Short-hand for months_since(1).
|
120
|
+
def next_month
|
121
|
+
months_since(1)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Short-hand for months_since(3)
|
125
|
+
def next_quarter
|
126
|
+
months_since(3)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Short-hand for years_since(1).
|
130
|
+
def next_year
|
131
|
+
years_since(1)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a new date/time representing the given day in the previous week.
|
135
|
+
# Week is assumed to start on +start_day+, default is
|
136
|
+
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
137
|
+
# DateTime objects have their time set to 0:00.
|
138
|
+
def prev_week(start_day = Date.beginning_of_week)
|
139
|
+
first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
|
140
|
+
end
|
141
|
+
alias_method :last_week, :prev_week
|
142
|
+
|
143
|
+
# Short-hand for months_ago(1).
|
144
|
+
def prev_month
|
145
|
+
months_ago(1)
|
146
|
+
end
|
147
|
+
alias_method :last_month, :prev_month
|
148
|
+
|
149
|
+
# Short-hand for months_ago(3).
|
150
|
+
def prev_quarter
|
151
|
+
months_ago(3)
|
152
|
+
end
|
153
|
+
alias_method :last_quarter, :prev_quarter
|
154
|
+
|
155
|
+
# Short-hand for years_ago(1).
|
156
|
+
def prev_year
|
157
|
+
years_ago(1)
|
158
|
+
end
|
159
|
+
alias_method :last_year, :prev_year
|
160
|
+
|
161
|
+
# Returns the number of days to the start of the week on the given day.
|
162
|
+
# Week is assumed to start on +start_day+, default is
|
163
|
+
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
164
|
+
def days_to_week_start(start_day = Date.beginning_of_week)
|
165
|
+
start_day_number = DAYS_INTO_WEEK[start_day]
|
166
|
+
current_day_number = wday != 0 ? wday - 1 : 6
|
167
|
+
(current_day_number - start_day_number) % 7
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns a new date/time representing the start of this week on the given day.
|
171
|
+
# Week is assumed to start on +start_day+, default is
|
172
|
+
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
173
|
+
# +DateTime+ objects have their time set to 0:00.
|
174
|
+
def beginning_of_week(start_day = Date.beginning_of_week)
|
175
|
+
result = days_ago(days_to_week_start(start_day))
|
176
|
+
acts_like?(:time) ? result.midnight : result
|
177
|
+
end
|
178
|
+
alias :at_beginning_of_week :beginning_of_week
|
179
|
+
|
180
|
+
# Returns Monday of this week assuming that week starts on Monday.
|
181
|
+
# +DateTime+ objects have their time set to 0:00.
|
182
|
+
def monday
|
183
|
+
beginning_of_week(:monday)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns a new date/time representing the end of this week on the given day.
|
187
|
+
# Week is assumed to start on +start_day+, default is
|
188
|
+
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
|
189
|
+
# DateTime objects have their time set to 23:59:59.
|
190
|
+
def end_of_week(start_day = Date.beginning_of_week)
|
191
|
+
last_hour{ days_since(6 - days_to_week_start(start_day)) }
|
192
|
+
end
|
193
|
+
alias :at_end_of_week :end_of_week
|
194
|
+
|
195
|
+
# Returns Sunday of this week assuming that week starts on Monday.
|
196
|
+
# +DateTime+ objects have their time set to 23:59:59.
|
197
|
+
def sunday
|
198
|
+
end_of_week(:monday)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns a new date/time representing the end of the month.
|
202
|
+
# DateTime objects will have a time set to 23:59:59.
|
203
|
+
def end_of_month
|
204
|
+
last_day = ::Time.days_in_month(month, year)
|
205
|
+
last_hour{ days_since(last_day - day) }
|
206
|
+
end
|
207
|
+
alias :at_end_of_month :end_of_month
|
208
|
+
|
209
|
+
# Returns a new date/time representing the end of the year.
|
210
|
+
# DateTime objects will have a time set to 23:59:59.
|
211
|
+
def end_of_year
|
212
|
+
change(:month => 12).end_of_month
|
213
|
+
end
|
214
|
+
alias :at_end_of_year :end_of_year
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
def first_hour
|
219
|
+
result = yield
|
220
|
+
acts_like?(:time) ? result.change(:hour => 0) : result
|
221
|
+
end
|
222
|
+
|
223
|
+
def last_hour
|
224
|
+
result = yield
|
225
|
+
acts_like?(:time) ? result.end_of_day : result
|
226
|
+
end
|
227
|
+
|
228
|
+
def days_span(day)
|
229
|
+
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Enumerable
|
2
|
+
# Iterates through a container, yielding each element and its index to the given block.
|
3
|
+
def collect_with_index(&block)
|
4
|
+
index = 0
|
5
|
+
collect do |value|
|
6
|
+
block.call(value, index).tap do
|
7
|
+
index += 1
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Calculates a sum from the elements.
|
13
|
+
#
|
14
|
+
# payments.sum { |p| p.price * p.tax_rate }
|
15
|
+
# payments.sum(&:price)
|
16
|
+
#
|
17
|
+
# The latter is a shortcut for:
|
18
|
+
#
|
19
|
+
# payments.inject(0) { |sum, p| sum + p.price }
|
20
|
+
#
|
21
|
+
# It can also calculate the sum without the use of a block.
|
22
|
+
#
|
23
|
+
# [5, 15, 10].sum # => 30
|
24
|
+
# ['foo', 'bar'].sum # => "foobar"
|
25
|
+
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
|
26
|
+
#
|
27
|
+
# The default sum of an empty list is zero. You can override this default:
|
28
|
+
#
|
29
|
+
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
30
|
+
def sum(identity = 0, &block)
|
31
|
+
if block_given?
|
32
|
+
map(&block).sum(identity)
|
33
|
+
else
|
34
|
+
inject { |sum, element| sum + element } || identity
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Convert an enumerable to a hash.
|
39
|
+
#
|
40
|
+
# people.index_by(&:login)
|
41
|
+
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
42
|
+
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
43
|
+
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
44
|
+
def index_by
|
45
|
+
if block_given?
|
46
|
+
Hash[map { |elem| [yield(elem), elem] }]
|
47
|
+
else
|
48
|
+
to_enum :index_by
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
53
|
+
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
54
|
+
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
55
|
+
# if more than one person is over 26.
|
56
|
+
def many?
|
57
|
+
cnt = 0
|
58
|
+
if block_given?
|
59
|
+
any? do |element|
|
60
|
+
cnt += 1 if yield element
|
61
|
+
cnt > 1
|
62
|
+
end
|
63
|
+
else
|
64
|
+
any? { (cnt += 1) > 1 }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
69
|
+
# collection does not include the object.
|
70
|
+
def exclude?(object)
|
71
|
+
!include?(object)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Range #:nodoc:
|
76
|
+
# Optimize range sum to use arithmetic progression if a block is not given and
|
77
|
+
# we have a range of numeric values.
|
78
|
+
def sum(identity = 0)
|
79
|
+
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
80
|
+
super
|
81
|
+
else
|
82
|
+
actual_last = exclude_end? ? (last - 1) : last
|
83
|
+
if actual_last >= first
|
84
|
+
(actual_last - first + 1) * (actual_last + first) / 2
|
85
|
+
else
|
86
|
+
identity
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a new hash with keys deleted if they match a criteria
|
3
|
+
# h1 = { x: { y: [ { z: 4, y: 1 }, 5, 6] }, a: { b: 2 } }
|
4
|
+
#
|
5
|
+
# h1.deep_delete { |k,v| k == :z } #=> { x: { y: [ { y: 1 }, 5, 6] }, a: { b: 2 } }
|
6
|
+
# h1.deep_delete { |k,v| k == :y } #=> { x: {}, a: { b: 2 } }
|
7
|
+
def deep_delete_if(&block)
|
8
|
+
result = {}
|
9
|
+
each do |key, value|
|
10
|
+
next if block.call(key, value)
|
11
|
+
|
12
|
+
result[key] = if value.is_a?(Hash)
|
13
|
+
value.deep_delete_if(&block)
|
14
|
+
elsif value.is_a?(Array)
|
15
|
+
value.map { |v| v.is_a?(Hash) ? v.deep_delete_if(&block) : v }
|
16
|
+
else
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
3
|
+
#
|
4
|
+
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
5
|
+
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
|
6
|
+
#
|
7
|
+
# h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
|
8
|
+
# h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
|
9
|
+
# h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
|
10
|
+
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
|
11
|
+
def deep_merge(other_hash, &block)
|
12
|
+
dup.deep_merge!(other_hash, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Same as +deep_merge+, but modifies +self+.
|
16
|
+
def deep_merge!(other_hash, &block)
|
17
|
+
other_hash.each_pair do |k,v|
|
18
|
+
tv = self[k]
|
19
|
+
if tv.is_a?(Hash) && v.is_a?(Hash)
|
20
|
+
self[k] = tv.deep_merge(v, &block)
|
21
|
+
else
|
22
|
+
self[k] = block && tv ? block.call(k, tv, v) : v
|
23
|
+
end
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Hash
|
2
|
+
# Return a hash that includes everything but the given keys. This is useful for
|
3
|
+
# limiting a set of parameters to everything but a few known toggles:
|
4
|
+
#
|
5
|
+
# @person.update(params[:person].except(:admin))
|
6
|
+
def except(*keys)
|
7
|
+
dup.except!(*keys)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Replaces the hash without the given keys.
|
11
|
+
def except!(*keys)
|
12
|
+
keys.each { |key| delete(key) }
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns an <tt>MotionSupport::HashWithIndifferentAccess</tt> out of its receiver:
|
3
|
+
#
|
4
|
+
# { a: 1 }.with_indifferent_access['a'] # => 1
|
5
|
+
def with_indifferent_access
|
6
|
+
MotionSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Called when object is nested under an object that receives
|
10
|
+
# #with_indifferent_access. This method will be called on the current object
|
11
|
+
# by the enclosing object and is aliased to #with_indifferent_access by
|
12
|
+
# default. Subclasses of Hash may overwrite this method to return +self+ if
|
13
|
+
# converting to an <tt>MotionSupport::HashWithIndifferentAccess</tt> would not be
|
14
|
+
# desirable.
|
15
|
+
#
|
16
|
+
# b = { b: 1 }
|
17
|
+
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
|
18
|
+
alias nested_under_indifferent_access with_indifferent_access
|
19
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
class Hash
|
2
|
+
# Return a new hash with all keys converted using the block operation.
|
3
|
+
#
|
4
|
+
# hash = { name: 'Rob', age: '28' }
|
5
|
+
#
|
6
|
+
# hash.transform_keys{ |key| key.to_s.upcase }
|
7
|
+
# # => { "NAME" => "Rob", "AGE" => "28" }
|
8
|
+
def transform_keys
|
9
|
+
result = {}
|
10
|
+
each_key do |key|
|
11
|
+
result[yield(key)] = self[key]
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
|
16
|
+
# Destructively convert all keys using the block operations.
|
17
|
+
# Same as transform_keys but modifies +self+.
|
18
|
+
def transform_keys!
|
19
|
+
keys.each do |key|
|
20
|
+
self[yield(key)] = delete(key)
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return a new hash with all keys converted to strings.
|
26
|
+
#
|
27
|
+
# hash = { name: 'Rob', age: '28' }
|
28
|
+
#
|
29
|
+
# hash.stringify_keys
|
30
|
+
# #=> { "name" => "Rob", "age" => "28" }
|
31
|
+
def stringify_keys
|
32
|
+
transform_keys{ |key| key.to_s }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Destructively convert all keys to strings. Same as
|
36
|
+
# +stringify_keys+, but modifies +self+.
|
37
|
+
def stringify_keys!
|
38
|
+
transform_keys!{ |key| key.to_s }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return a new hash with all keys converted to symbols, as long as
|
42
|
+
# they respond to +to_sym+.
|
43
|
+
#
|
44
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
45
|
+
#
|
46
|
+
# hash.symbolize_keys
|
47
|
+
# #=> { name: "Rob", age: "28" }
|
48
|
+
def symbolize_keys
|
49
|
+
transform_keys{ |key| key.to_sym rescue key }
|
50
|
+
end
|
51
|
+
alias_method :to_options, :symbolize_keys
|
52
|
+
|
53
|
+
# Destructively convert all keys to symbols, as long as they respond
|
54
|
+
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
|
55
|
+
def symbolize_keys!
|
56
|
+
transform_keys!{ |key| key.to_sym rescue key }
|
57
|
+
end
|
58
|
+
alias_method :to_options!, :symbolize_keys!
|
59
|
+
|
60
|
+
# Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
|
61
|
+
# on a mismatch. Note that keys are NOT treated indifferently, meaning if you
|
62
|
+
# use strings for keys but assert symbols as keys, this will fail.
|
63
|
+
#
|
64
|
+
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
|
65
|
+
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
|
66
|
+
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
67
|
+
def assert_valid_keys(*valid_keys)
|
68
|
+
valid_keys.flatten!
|
69
|
+
each_key do |k|
|
70
|
+
raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return a new hash with all keys converted by the block operation.
|
75
|
+
# This includes the keys from the root hash and from all
|
76
|
+
# nested hashes.
|
77
|
+
#
|
78
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
79
|
+
#
|
80
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
81
|
+
# # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
|
82
|
+
def deep_transform_keys(&block)
|
83
|
+
result = {}
|
84
|
+
each do |key, value|
|
85
|
+
result[yield(key)] = if value.is_a?(Hash)
|
86
|
+
value.deep_transform_keys(&block)
|
87
|
+
elsif value.is_a?(Array)
|
88
|
+
value.map { |v| v.is_a?(Hash) ? v.deep_transform_keys(&block) : v }
|
89
|
+
else
|
90
|
+
value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# Destructively convert all keys by using the block operation.
|
97
|
+
# This includes the keys from the root hash and from all
|
98
|
+
# nested hashes.
|
99
|
+
def deep_transform_keys!(&block)
|
100
|
+
keys.each do |key|
|
101
|
+
value = delete(key)
|
102
|
+
self[yield(key)] = if value.is_a?(Hash)
|
103
|
+
value.deep_transform_keys(&block)
|
104
|
+
elsif value.is_a?(Array)
|
105
|
+
value.map { |v| v.is_a?(Hash) ? v.deep_transform_keys(&block) : v }
|
106
|
+
else
|
107
|
+
value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return a new hash with all keys converted to strings.
|
114
|
+
# This includes the keys from the root hash and from all
|
115
|
+
# nested hashes.
|
116
|
+
#
|
117
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
118
|
+
#
|
119
|
+
# hash.deep_stringify_keys
|
120
|
+
# # => { "person" => { "name" => "Rob", "age" => "28" } }
|
121
|
+
def deep_stringify_keys
|
122
|
+
deep_transform_keys{ |key| key.to_s }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Destructively convert all keys to strings.
|
126
|
+
# This includes the keys from the root hash and from all
|
127
|
+
# nested hashes.
|
128
|
+
def deep_stringify_keys!
|
129
|
+
deep_transform_keys!{ |key| key.to_s }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Return a new hash with all keys converted to symbols, as long as
|
133
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
134
|
+
# and from all nested hashes.
|
135
|
+
#
|
136
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
137
|
+
#
|
138
|
+
# hash.deep_symbolize_keys
|
139
|
+
# # => { person: { name: "Rob", age: "28" } }
|
140
|
+
def deep_symbolize_keys
|
141
|
+
deep_transform_keys{ |key| key.to_sym rescue key }
|
142
|
+
end
|
143
|
+
|
144
|
+
# Destructively convert all keys to symbols, as long as they respond
|
145
|
+
# to +to_sym+. This includes the keys from the root hash and from all
|
146
|
+
# nested hashes.
|
147
|
+
def deep_symbolize_keys!
|
148
|
+
deep_transform_keys!{ |key| key.to_sym rescue key }
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Hash
|
2
|
+
# Merges the caller into +other_hash+. For example,
|
3
|
+
#
|
4
|
+
# options = options.reverse_merge(size: 25, velocity: 10)
|
5
|
+
#
|
6
|
+
# is equivalent to
|
7
|
+
#
|
8
|
+
# options = { size: 25, velocity: 10 }.merge(options)
|
9
|
+
#
|
10
|
+
# This is particularly useful for initializing an options hash
|
11
|
+
# with default values.
|
12
|
+
def reverse_merge(other_hash)
|
13
|
+
other_hash.merge(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Destructive +reverse_merge+.
|
17
|
+
def reverse_merge!(other_hash)
|
18
|
+
# right wins if there is no left
|
19
|
+
merge!( other_hash ){|key,left,right| left }
|
20
|
+
end
|
21
|
+
alias_method :reverse_update, :reverse_merge!
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Hash
|
2
|
+
# Slice a hash to include only the given keys. This is useful for
|
3
|
+
# limiting an options hash to valid keys before passing to a method:
|
4
|
+
#
|
5
|
+
# def search(criteria = {})
|
6
|
+
# criteria.assert_valid_keys(:mass, :velocity, :time)
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# search(options.slice(:mass, :velocity, :time))
|
10
|
+
#
|
11
|
+
# If you have an array of keys you want to limit to, you should splat them:
|
12
|
+
#
|
13
|
+
# valid_keys = [:mass, :velocity, :time]
|
14
|
+
# search(options.slice(*valid_keys))
|
15
|
+
def slice(*keys)
|
16
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
17
|
+
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Replaces the hash with only the given keys.
|
21
|
+
# Returns a hash containing the removed key/value pairs.
|
22
|
+
#
|
23
|
+
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
|
24
|
+
# # => {:c=>3, :d=>4}
|
25
|
+
def slice!(*keys)
|
26
|
+
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
|
27
|
+
omit = slice(*self.keys - keys)
|
28
|
+
hash = slice(*keys)
|
29
|
+
replace(hash)
|
30
|
+
omit
|
31
|
+
end
|
32
|
+
|
33
|
+
# Removes and returns the key/value pairs matching the given keys.
|
34
|
+
#
|
35
|
+
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
|
36
|
+
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
|
37
|
+
def extract!(*keys)
|
38
|
+
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
|
39
|
+
end
|
40
|
+
end
|