motion_blender-support 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|