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,86 @@
|
|
|
1
|
+
class Array
|
|
2
|
+
# Converts the array to a comma-separated sentence where the last element is
|
|
3
|
+
# joined by the connector word.
|
|
4
|
+
#
|
|
5
|
+
# You can pass the following options to change the default behavior. If you
|
|
6
|
+
# pass an option key that doesn't exist in the list below, it will raise an
|
|
7
|
+
# <tt>ArgumentError</tt>.
|
|
8
|
+
#
|
|
9
|
+
# Options:
|
|
10
|
+
#
|
|
11
|
+
# * <tt>:words_connector</tt> - The sign or word used to join the elements
|
|
12
|
+
# in arrays with two or more elements (default: ", ").
|
|
13
|
+
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
|
|
14
|
+
# in arrays with two elements (default: " and ").
|
|
15
|
+
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
|
|
16
|
+
# in arrays with three or more elements (default: ", and ").
|
|
17
|
+
#
|
|
18
|
+
# [].to_sentence # => ""
|
|
19
|
+
# ['one'].to_sentence # => "one"
|
|
20
|
+
# ['one', 'two'].to_sentence # => "one and two"
|
|
21
|
+
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
|
|
22
|
+
#
|
|
23
|
+
# ['one', 'two'].to_sentence(passing: 'invalid option')
|
|
24
|
+
# # => ArgumentError: Unknown key :passing
|
|
25
|
+
#
|
|
26
|
+
# ['one', 'two'].to_sentence(two_words_connector: '-')
|
|
27
|
+
# # => "one-two"
|
|
28
|
+
#
|
|
29
|
+
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
|
|
30
|
+
# # => "one or two or at least three"
|
|
31
|
+
def to_sentence(options = {})
|
|
32
|
+
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector)
|
|
33
|
+
|
|
34
|
+
default_connectors = {
|
|
35
|
+
:words_connector => ', ',
|
|
36
|
+
:two_words_connector => ' and ',
|
|
37
|
+
:last_word_connector => ', and '
|
|
38
|
+
}
|
|
39
|
+
options = default_connectors.merge!(options)
|
|
40
|
+
|
|
41
|
+
case length
|
|
42
|
+
when 0
|
|
43
|
+
''
|
|
44
|
+
when 1
|
|
45
|
+
self[0].to_s.dup
|
|
46
|
+
when 2
|
|
47
|
+
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
|
|
48
|
+
else
|
|
49
|
+
"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Converts a collection of elements into a formatted string by calling
|
|
54
|
+
# <tt>to_s</tt> on all elements and joining them. Having this model:
|
|
55
|
+
#
|
|
56
|
+
# class Blog < ActiveRecord::Base
|
|
57
|
+
# def to_s
|
|
58
|
+
# title
|
|
59
|
+
# end
|
|
60
|
+
# end
|
|
61
|
+
#
|
|
62
|
+
# Blog.all.map(&:title) #=> ["First Post", "Second Post", "Third post"]
|
|
63
|
+
#
|
|
64
|
+
# <tt>to_formatted_s</tt> shows us:
|
|
65
|
+
#
|
|
66
|
+
# Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
|
|
67
|
+
#
|
|
68
|
+
# Adding in the <tt>:db</tt> argument as the format yields a comma separated
|
|
69
|
+
# id list:
|
|
70
|
+
#
|
|
71
|
+
# Blog.all.to_formatted_s(:db) # => "1,2,3"
|
|
72
|
+
def to_formatted_s(format = :default)
|
|
73
|
+
case format
|
|
74
|
+
when :db
|
|
75
|
+
if empty?
|
|
76
|
+
'null'
|
|
77
|
+
else
|
|
78
|
+
collect { |element| element.id }.join(',')
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
to_default_s
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
alias_method :to_default_s, :to_s
|
|
85
|
+
alias_method :to_s, :to_formatted_s
|
|
86
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
class Array
|
|
2
|
+
# Splits or iterates over the array in groups of size +number+,
|
|
3
|
+
# padding any remaining slots with +fill_with+ unless it is +false+.
|
|
4
|
+
#
|
|
5
|
+
# %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
|
|
6
|
+
# ["1", "2", "3"]
|
|
7
|
+
# ["4", "5", "6"]
|
|
8
|
+
# ["7", "8", "9"]
|
|
9
|
+
# ["10", nil, nil]
|
|
10
|
+
#
|
|
11
|
+
# %w(1 2 3 4 5).in_groups_of(2, ' ') {|group| p group}
|
|
12
|
+
# ["1", "2"]
|
|
13
|
+
# ["3", "4"]
|
|
14
|
+
# ["5", " "]
|
|
15
|
+
#
|
|
16
|
+
# %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
|
|
17
|
+
# ["1", "2"]
|
|
18
|
+
# ["3", "4"]
|
|
19
|
+
# ["5"]
|
|
20
|
+
def in_groups_of(number, fill_with = nil)
|
|
21
|
+
if fill_with == false
|
|
22
|
+
collection = self
|
|
23
|
+
else
|
|
24
|
+
# size % number gives how many extra we have;
|
|
25
|
+
# subtracting from number gives how many to add;
|
|
26
|
+
# modulo number ensures we don't add group of just fill.
|
|
27
|
+
padding = (number - size % number) % number
|
|
28
|
+
collection = dup.concat([fill_with] * padding)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if block_given?
|
|
32
|
+
collection.each_slice(number) { |slice| yield(slice) }
|
|
33
|
+
else
|
|
34
|
+
groups = []
|
|
35
|
+
collection.each_slice(number) { |group| groups << group }
|
|
36
|
+
groups
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Splits or iterates over the array in +number+ of groups, padding any
|
|
41
|
+
# remaining slots with +fill_with+ unless it is +false+.
|
|
42
|
+
#
|
|
43
|
+
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
|
|
44
|
+
# ["1", "2", "3", "4"]
|
|
45
|
+
# ["5", "6", "7", nil]
|
|
46
|
+
# ["8", "9", "10", nil]
|
|
47
|
+
#
|
|
48
|
+
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, ' ') {|group| p group}
|
|
49
|
+
# ["1", "2", "3", "4"]
|
|
50
|
+
# ["5", "6", "7", " "]
|
|
51
|
+
# ["8", "9", "10", " "]
|
|
52
|
+
#
|
|
53
|
+
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
|
|
54
|
+
# ["1", "2", "3"]
|
|
55
|
+
# ["4", "5"]
|
|
56
|
+
# ["6", "7"]
|
|
57
|
+
def in_groups(number, fill_with = nil)
|
|
58
|
+
# size / number gives minor group size;
|
|
59
|
+
# size % number gives how many objects need extra accommodation;
|
|
60
|
+
# each group hold either division or division + 1 items.
|
|
61
|
+
division = size.div number
|
|
62
|
+
modulo = size % number
|
|
63
|
+
|
|
64
|
+
# create a new array avoiding dup
|
|
65
|
+
groups = []
|
|
66
|
+
start = 0
|
|
67
|
+
|
|
68
|
+
number.times do |index|
|
|
69
|
+
length = division + (modulo > 0 && modulo > index ? 1 : 0)
|
|
70
|
+
groups << last_group = slice(start, length)
|
|
71
|
+
last_group << fill_with if fill_with != false &&
|
|
72
|
+
modulo > 0 && length == division
|
|
73
|
+
start += length
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if block_given?
|
|
77
|
+
groups.each { |g| yield(g) }
|
|
78
|
+
else
|
|
79
|
+
groups
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Divides the array into one or more subarrays based on a delimiting +value+
|
|
84
|
+
# or the result of an optional block.
|
|
85
|
+
#
|
|
86
|
+
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
|
|
87
|
+
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
|
|
88
|
+
def split(value = nil, &block)
|
|
89
|
+
inject([[]]) do |results, element|
|
|
90
|
+
if block && block.call(element) || value == element
|
|
91
|
+
results << []
|
|
92
|
+
else
|
|
93
|
+
results.last << element
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
results
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class Array
|
|
2
|
+
# Wraps its argument in an array unless it is already an array (or array-like).
|
|
3
|
+
#
|
|
4
|
+
# Specifically:
|
|
5
|
+
#
|
|
6
|
+
# * If the argument is +nil+ an empty list is returned.
|
|
7
|
+
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
|
|
8
|
+
# * Otherwise, returns an array with the argument as its single element.
|
|
9
|
+
#
|
|
10
|
+
# Array.wrap(nil) # => []
|
|
11
|
+
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
|
|
12
|
+
# Array.wrap(0) # => [0]
|
|
13
|
+
#
|
|
14
|
+
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
|
|
15
|
+
#
|
|
16
|
+
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
|
|
17
|
+
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
|
|
18
|
+
# such a +nil+ right away.
|
|
19
|
+
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
|
|
20
|
+
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
|
|
21
|
+
# * It does not call +to_a+ on the argument, though special-cases +nil+ to return an empty array.
|
|
22
|
+
#
|
|
23
|
+
# The last point is particularly worth comparing for some enumerables:
|
|
24
|
+
#
|
|
25
|
+
# Array(foo: :bar) # => [[:foo, :bar]]
|
|
26
|
+
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
|
|
27
|
+
#
|
|
28
|
+
# There's also a related idiom that uses the splat operator:
|
|
29
|
+
#
|
|
30
|
+
# [*object]
|
|
31
|
+
#
|
|
32
|
+
# which for +nil+ returns <tt>[]</tt>, and calls to <tt>Array(object)</tt> otherwise.
|
|
33
|
+
#
|
|
34
|
+
# Thus, in this case the behavior may be different for +nil+, and the differences with
|
|
35
|
+
# <tt>Kernel#Array</tt> explained above apply to the rest of <tt>object</tt>s.
|
|
36
|
+
def self.wrap(object)
|
|
37
|
+
if object.nil?
|
|
38
|
+
[]
|
|
39
|
+
elsif object.respond_to?(:to_ary)
|
|
40
|
+
object.to_ary || [object]
|
|
41
|
+
else
|
|
42
|
+
[object]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class Array
|
|
2
|
+
# If any item in the array has the key == `key` true, otherwise false.
|
|
3
|
+
# Of good use when writing specs.
|
|
4
|
+
def has_hash_key?(key)
|
|
5
|
+
self.each do |entity|
|
|
6
|
+
return true if entity.has_key? key
|
|
7
|
+
end
|
|
8
|
+
return false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# If any item in the array has the value == `key` true, otherwise false
|
|
12
|
+
# Of good use when writing specs.
|
|
13
|
+
def has_hash_value?(key)
|
|
14
|
+
self.each do |entity|
|
|
15
|
+
entity.each_pair{|hash_key, value| return true if value == key}
|
|
16
|
+
end
|
|
17
|
+
return false
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
class Class
|
|
2
|
+
# Declare a class-level attribute whose value is inheritable by subclasses.
|
|
3
|
+
# Subclasses can change their own value and it will not impact parent class.
|
|
4
|
+
#
|
|
5
|
+
# class Base
|
|
6
|
+
# class_attribute :setting
|
|
7
|
+
# end
|
|
8
|
+
#
|
|
9
|
+
# class Subclass < Base
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# Base.setting = true
|
|
13
|
+
# Subclass.setting # => true
|
|
14
|
+
# Subclass.setting = false
|
|
15
|
+
# Subclass.setting # => false
|
|
16
|
+
# Base.setting # => true
|
|
17
|
+
#
|
|
18
|
+
# In the above case as long as Subclass does not assign a value to setting
|
|
19
|
+
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
|
|
20
|
+
# would read value assigned to parent class. Once Subclass assigns a value then
|
|
21
|
+
# the value assigned by Subclass would be returned.
|
|
22
|
+
#
|
|
23
|
+
# This matches normal Ruby method inheritance: think of writing an attribute
|
|
24
|
+
# on a subclass as overriding the reader method. However, you need to be aware
|
|
25
|
+
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
|
|
26
|
+
# In such cases, you don't want to do changes in places but use setters:
|
|
27
|
+
#
|
|
28
|
+
# Base.setting = []
|
|
29
|
+
# Base.setting # => []
|
|
30
|
+
# Subclass.setting # => []
|
|
31
|
+
#
|
|
32
|
+
# # Appending in child changes both parent and child because it is the same object:
|
|
33
|
+
# Subclass.setting << :foo
|
|
34
|
+
# Base.setting # => [:foo]
|
|
35
|
+
# Subclass.setting # => [:foo]
|
|
36
|
+
#
|
|
37
|
+
# # Use setters to not propagate changes:
|
|
38
|
+
# Base.setting = []
|
|
39
|
+
# Subclass.setting += [:foo]
|
|
40
|
+
# Base.setting # => []
|
|
41
|
+
# Subclass.setting # => [:foo]
|
|
42
|
+
#
|
|
43
|
+
# For convenience, a query method is defined as well:
|
|
44
|
+
#
|
|
45
|
+
# Subclass.setting? # => false
|
|
46
|
+
#
|
|
47
|
+
# Instances may overwrite the class value in the same way:
|
|
48
|
+
#
|
|
49
|
+
# Base.setting = true
|
|
50
|
+
# object = Base.new
|
|
51
|
+
# object.setting # => true
|
|
52
|
+
# object.setting = false
|
|
53
|
+
# object.setting # => false
|
|
54
|
+
# Base.setting # => true
|
|
55
|
+
#
|
|
56
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
57
|
+
#
|
|
58
|
+
# object.setting # => NoMethodError
|
|
59
|
+
# object.setting? # => NoMethodError
|
|
60
|
+
#
|
|
61
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
62
|
+
#
|
|
63
|
+
# object.setting = false # => NoMethodError
|
|
64
|
+
#
|
|
65
|
+
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
|
|
66
|
+
def class_attribute(*attrs)
|
|
67
|
+
options = attrs.extract_options!
|
|
68
|
+
# double assignment is used to avoid "assigned but unused variable" warning
|
|
69
|
+
instance_reader = instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
|
70
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
|
71
|
+
|
|
72
|
+
attrs.each do |name|
|
|
73
|
+
define_singleton_method(name) { nil }
|
|
74
|
+
define_singleton_method("#{name}?") { !!public_send(name) }
|
|
75
|
+
|
|
76
|
+
ivar = "@#{name}"
|
|
77
|
+
|
|
78
|
+
define_singleton_method("#{name}=") do |val|
|
|
79
|
+
singleton_class.class_eval do
|
|
80
|
+
remove_possible_method(name)
|
|
81
|
+
define_method(name) { val }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if singleton_class?
|
|
85
|
+
class_eval do
|
|
86
|
+
remove_possible_method(name)
|
|
87
|
+
define_method(name) do
|
|
88
|
+
if instance_variable_defined? ivar
|
|
89
|
+
instance_variable_get ivar
|
|
90
|
+
else
|
|
91
|
+
singleton_class.send name
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
val
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if instance_reader
|
|
100
|
+
remove_possible_method name
|
|
101
|
+
define_method(name) do
|
|
102
|
+
if instance_variable_defined?(ivar)
|
|
103
|
+
instance_variable_get ivar
|
|
104
|
+
else
|
|
105
|
+
self.class.public_send name
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
define_method("#{name}?") { !!public_send(name) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
attr_writer name if instance_writer
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
def singleton_class?
|
|
117
|
+
ancestors.first != self
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Extends the class object with class and instance accessors for class attributes,
|
|
2
|
+
# just like the native attr* accessors for instance attributes.
|
|
3
|
+
class Class
|
|
4
|
+
# Defines a class attribute if it's not defined and creates a reader method that
|
|
5
|
+
# returns the attribute value.
|
|
6
|
+
#
|
|
7
|
+
# class Person
|
|
8
|
+
# cattr_reader :hair_colors
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# Person.class_variable_set("@@hair_colors", [:brown, :black])
|
|
12
|
+
# Person.hair_colors # => [:brown, :black]
|
|
13
|
+
# Person.new.hair_colors # => [:brown, :black]
|
|
14
|
+
#
|
|
15
|
+
# The attribute name must be a valid method name in Ruby.
|
|
16
|
+
#
|
|
17
|
+
# class Person
|
|
18
|
+
# cattr_reader :"1_Badname "
|
|
19
|
+
# end
|
|
20
|
+
# # => NameError: invalid attribute name
|
|
21
|
+
#
|
|
22
|
+
# If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
|
|
23
|
+
# or <tt>instance_accessor: false</tt>.
|
|
24
|
+
#
|
|
25
|
+
# class Person
|
|
26
|
+
# cattr_reader :hair_colors, instance_reader: false
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# Person.new.hair_colors # => NoMethodError
|
|
30
|
+
def cattr_reader(*syms)
|
|
31
|
+
options = syms.extract_options!
|
|
32
|
+
syms.each do |sym|
|
|
33
|
+
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
|
|
34
|
+
class_exec do
|
|
35
|
+
unless class_variable_defined?("@@#{sym}")
|
|
36
|
+
class_variable_set("@@#{sym}", nil)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
define_singleton_method sym do
|
|
40
|
+
class_variable_get("@@#{sym}")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
unless options[:instance_reader] == false || options[:instance_accessor] == false
|
|
45
|
+
class_exec do
|
|
46
|
+
define_method sym do
|
|
47
|
+
self.class.class_variable_get("@@#{sym}")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Defines a class attribute if it's not defined and creates a writer method to allow
|
|
55
|
+
# assignment to the attribute.
|
|
56
|
+
#
|
|
57
|
+
# class Person
|
|
58
|
+
# cattr_writer :hair_colors
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# Person.hair_colors = [:brown, :black]
|
|
62
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
|
|
63
|
+
# Person.new.hair_colors = [:blonde, :red]
|
|
64
|
+
# Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
|
65
|
+
#
|
|
66
|
+
# The attribute name must be a valid method name in Ruby.
|
|
67
|
+
#
|
|
68
|
+
# class Person
|
|
69
|
+
# cattr_writer :"1_Badname "
|
|
70
|
+
# end
|
|
71
|
+
# # => NameError: invalid attribute name
|
|
72
|
+
#
|
|
73
|
+
# If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
|
|
74
|
+
# or <tt>instance_accessor: false</tt>.
|
|
75
|
+
#
|
|
76
|
+
# class Person
|
|
77
|
+
# cattr_writer :hair_colors, instance_writer: false
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
|
|
81
|
+
#
|
|
82
|
+
# Also, you can pass a block to set up the attribute with a default value.
|
|
83
|
+
#
|
|
84
|
+
# class Person
|
|
85
|
+
# cattr_writer :hair_colors do
|
|
86
|
+
# [:brown, :black, :blonde, :red]
|
|
87
|
+
# end
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
|
91
|
+
def cattr_writer(*syms)
|
|
92
|
+
options = syms.extract_options!
|
|
93
|
+
syms.each do |sym|
|
|
94
|
+
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
|
|
95
|
+
class_exec do
|
|
96
|
+
unless class_variable_defined?("@@#{sym}")
|
|
97
|
+
class_variable_set("@@#{sym}", nil)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
define_singleton_method "#{sym}=" do |obj|
|
|
101
|
+
class_variable_set("@@#{sym}", obj)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
unless options[:instance_writer] == false || options[:instance_accessor] == false
|
|
106
|
+
class_exec do
|
|
107
|
+
define_method "#{sym}=" do |obj|
|
|
108
|
+
self.class.class_variable_set("@@#{sym}", obj)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
send("#{sym}=", yield) if block_given?
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Defines both class and instance accessors for class attributes.
|
|
117
|
+
#
|
|
118
|
+
# class Person
|
|
119
|
+
# cattr_accessor :hair_colors
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# Person.hair_colors = [:brown, :black, :blonde, :red]
|
|
123
|
+
# Person.hair_colors # => [:brown, :black, :blonde, :red]
|
|
124
|
+
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
|
125
|
+
#
|
|
126
|
+
# If a subclass changes the value then that would also change the value for
|
|
127
|
+
# parent class. Similarly if parent class changes the value then that would
|
|
128
|
+
# change the value of subclasses too.
|
|
129
|
+
#
|
|
130
|
+
# class Male < Person
|
|
131
|
+
# end
|
|
132
|
+
#
|
|
133
|
+
# Male.hair_colors << :blue
|
|
134
|
+
# Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
|
135
|
+
#
|
|
136
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
137
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
138
|
+
#
|
|
139
|
+
# class Person
|
|
140
|
+
# cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
|
141
|
+
# end
|
|
142
|
+
#
|
|
143
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
144
|
+
# Person.new.hair_colors # => NoMethodError
|
|
145
|
+
#
|
|
146
|
+
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
|
|
147
|
+
#
|
|
148
|
+
# class Person
|
|
149
|
+
# cattr_accessor :hair_colors, instance_accessor: false
|
|
150
|
+
# end
|
|
151
|
+
#
|
|
152
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
153
|
+
# Person.new.hair_colors # => NoMethodError
|
|
154
|
+
#
|
|
155
|
+
# Also you can pass a block to set up the attribute with a default value.
|
|
156
|
+
#
|
|
157
|
+
# class Person
|
|
158
|
+
# cattr_accessor :hair_colors do
|
|
159
|
+
# [:brown, :black, :blonde, :red]
|
|
160
|
+
# end
|
|
161
|
+
# end
|
|
162
|
+
#
|
|
163
|
+
# Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
|
|
164
|
+
def cattr_accessor(*syms, &blk)
|
|
165
|
+
cattr_reader(*syms)
|
|
166
|
+
cattr_writer(*syms, &blk)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require_relative '../date_and_time/calculations'
|
|
2
|
+
|
|
3
|
+
class Date
|
|
4
|
+
include DateAndTime::Calculations
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
attr_accessor :beginning_of_week_default
|
|
8
|
+
|
|
9
|
+
# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
|
|
10
|
+
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
|
|
11
|
+
# If no config.beginning_of_week was specified, returns :monday.
|
|
12
|
+
def beginning_of_week
|
|
13
|
+
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
|
|
17
|
+
#
|
|
18
|
+
# This method accepts any of the following day symbols:
|
|
19
|
+
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
|
|
20
|
+
def beginning_of_week=(week_start)
|
|
21
|
+
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
|
|
25
|
+
def find_beginning_of_week!(week_start)
|
|
26
|
+
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
|
|
27
|
+
week_start
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
|
|
31
|
+
def yesterday
|
|
32
|
+
::Date.current.yesterday
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
|
|
36
|
+
def tomorrow
|
|
37
|
+
::Date.current.tomorrow
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Alias for Date.today.
|
|
41
|
+
def current
|
|
42
|
+
::Date.today
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
|
|
47
|
+
# and then subtracts the specified number of seconds.
|
|
48
|
+
def ago(seconds)
|
|
49
|
+
to_time.since(-seconds)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
|
|
53
|
+
# and then adds the specified number of seconds
|
|
54
|
+
def since(seconds)
|
|
55
|
+
to_time.since(seconds)
|
|
56
|
+
end
|
|
57
|
+
alias :in :since
|
|
58
|
+
|
|
59
|
+
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
|
|
60
|
+
def beginning_of_day
|
|
61
|
+
to_time
|
|
62
|
+
end
|
|
63
|
+
alias :midnight :beginning_of_day
|
|
64
|
+
alias :at_midnight :beginning_of_day
|
|
65
|
+
alias :at_beginning_of_day :beginning_of_day
|
|
66
|
+
|
|
67
|
+
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
|
|
68
|
+
def end_of_day
|
|
69
|
+
to_time.end_of_day
|
|
70
|
+
end
|
|
71
|
+
alias :at_end_of_day :end_of_day
|
|
72
|
+
|
|
73
|
+
def plus_with_duration(other) #:nodoc:
|
|
74
|
+
if MotionSupport::Duration === other
|
|
75
|
+
other.since(self)
|
|
76
|
+
else
|
|
77
|
+
plus_without_duration(other)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
alias_method :plus_without_duration, :+
|
|
81
|
+
alias_method :+, :plus_with_duration
|
|
82
|
+
|
|
83
|
+
def minus_with_duration(other) #:nodoc:
|
|
84
|
+
if MotionSupport::Duration === other
|
|
85
|
+
plus_with_duration(-other)
|
|
86
|
+
else
|
|
87
|
+
minus_without_duration(other)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
alias_method :minus_without_duration, :-
|
|
91
|
+
alias_method :-, :minus_with_duration
|
|
92
|
+
|
|
93
|
+
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
|
|
94
|
+
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
|
|
95
|
+
def advance(options)
|
|
96
|
+
options = options.dup
|
|
97
|
+
d = self
|
|
98
|
+
d = d >> options.delete(:years) * 12 if options[:years]
|
|
99
|
+
d = d >> options.delete(:months) if options[:months]
|
|
100
|
+
d = d + options.delete(:weeks) * 7 if options[:weeks]
|
|
101
|
+
d = d + options.delete(:days) if options[:days]
|
|
102
|
+
d
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
|
|
106
|
+
# The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
|
|
107
|
+
#
|
|
108
|
+
# Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
|
|
109
|
+
# Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
|
|
110
|
+
def change(options)
|
|
111
|
+
::Date.new(
|
|
112
|
+
options.fetch(:year, year),
|
|
113
|
+
options.fetch(:month, month),
|
|
114
|
+
options.fetch(:day, day)
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
end
|