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.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +4 -0
  5. data/HACKS.md +7 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +359 -0
  8. data/Rakefile +14 -0
  9. data/app/app_delegate.rb +5 -0
  10. data/examples/Inflector/.gitignore +16 -0
  11. data/examples/Inflector/Gemfile +5 -0
  12. data/examples/Inflector/Rakefile +13 -0
  13. data/examples/Inflector/app/app_delegate.rb +8 -0
  14. data/examples/Inflector/app/inflections.rb +5 -0
  15. data/examples/Inflector/app/inflector_view_controller.rb +109 -0
  16. data/examples/Inflector/app/words_view_controller.rb +101 -0
  17. data/examples/Inflector/resources/Default-568h@2x.png +0 -0
  18. data/examples/Inflector/spec/main_spec.rb +9 -0
  19. data/lib/motion-support/callbacks.rb +8 -0
  20. data/lib/motion-support/concern.rb +4 -0
  21. data/lib/motion-support/core_ext/array.rb +10 -0
  22. data/lib/motion-support/core_ext/class.rb +5 -0
  23. data/lib/motion-support/core_ext/hash.rb +13 -0
  24. data/lib/motion-support/core_ext/integer.rb +6 -0
  25. data/lib/motion-support/core_ext/module.rb +11 -0
  26. data/lib/motion-support/core_ext/numeric.rb +6 -0
  27. data/lib/motion-support/core_ext/object.rb +12 -0
  28. data/lib/motion-support/core_ext/range.rb +5 -0
  29. data/lib/motion-support/core_ext/string.rb +13 -0
  30. data/lib/motion-support/core_ext/time.rb +16 -0
  31. data/lib/motion-support/core_ext.rb +13 -0
  32. data/lib/motion-support/inflector.rb +8 -0
  33. data/lib/motion-support.rb +81 -0
  34. data/motion/_stdlib/array.rb +13 -0
  35. data/motion/_stdlib/cgi.rb +22 -0
  36. data/motion/_stdlib/date.rb +81 -0
  37. data/motion/_stdlib/enumerable.rb +9 -0
  38. data/motion/_stdlib/time.rb +19 -0
  39. data/motion/callbacks.rb +511 -0
  40. data/motion/concern.rb +122 -0
  41. data/motion/core_ext/array/access.rb +28 -0
  42. data/motion/core_ext/array/conversions.rb +86 -0
  43. data/motion/core_ext/array/extract_options.rb +11 -0
  44. data/motion/core_ext/array/grouping.rb +99 -0
  45. data/motion/core_ext/array/prepend_and_append.rb +7 -0
  46. data/motion/core_ext/array/wrap.rb +45 -0
  47. data/motion/core_ext/array.rb +19 -0
  48. data/motion/core_ext/class/attribute.rb +119 -0
  49. data/motion/core_ext/class/attribute_accessors.rb +168 -0
  50. data/motion/core_ext/date/acts_like.rb +8 -0
  51. data/motion/core_ext/date/calculations.rb +117 -0
  52. data/motion/core_ext/date/conversions.rb +56 -0
  53. data/motion/core_ext/date_and_time/calculations.rb +232 -0
  54. data/motion/core_ext/enumerable.rb +90 -0
  55. data/motion/core_ext/hash/deep_delete_if.rb +23 -0
  56. data/motion/core_ext/hash/deep_merge.rb +27 -0
  57. data/motion/core_ext/hash/except.rb +15 -0
  58. data/motion/core_ext/hash/indifferent_access.rb +19 -0
  59. data/motion/core_ext/hash/keys.rb +150 -0
  60. data/motion/core_ext/hash/reverse_merge.rb +22 -0
  61. data/motion/core_ext/hash/slice.rb +40 -0
  62. data/motion/core_ext/integer/inflections.rb +27 -0
  63. data/motion/core_ext/integer/multiple.rb +10 -0
  64. data/motion/core_ext/integer/time.rb +41 -0
  65. data/motion/core_ext/kernel/singleton_class.rb +6 -0
  66. data/motion/core_ext/metaclass.rb +8 -0
  67. data/motion/core_ext/module/aliasing.rb +69 -0
  68. data/motion/core_ext/module/anonymous.rb +19 -0
  69. data/motion/core_ext/module/attr_internal.rb +38 -0
  70. data/motion/core_ext/module/attribute_accessors.rb +64 -0
  71. data/motion/core_ext/module/delegation.rb +175 -0
  72. data/motion/core_ext/module/introspection.rb +60 -0
  73. data/motion/core_ext/module/reachable.rb +5 -0
  74. data/motion/core_ext/module/remove_method.rb +12 -0
  75. data/motion/core_ext/ns_dictionary.rb +14 -0
  76. data/motion/core_ext/ns_string.rb +14 -0
  77. data/motion/core_ext/numeric/bytes.rb +44 -0
  78. data/motion/core_ext/numeric/conversions.rb +49 -0
  79. data/motion/core_ext/numeric/time.rb +75 -0
  80. data/motion/core_ext/object/acts_like.rb +10 -0
  81. data/motion/core_ext/object/blank.rb +105 -0
  82. data/motion/core_ext/object/deep_dup.rb +44 -0
  83. data/motion/core_ext/object/duplicable.rb +87 -0
  84. data/motion/core_ext/object/inclusion.rb +15 -0
  85. data/motion/core_ext/object/instance_variables.rb +28 -0
  86. data/motion/core_ext/object/to_param.rb +58 -0
  87. data/motion/core_ext/object/to_query.rb +26 -0
  88. data/motion/core_ext/object/try.rb +78 -0
  89. data/motion/core_ext/range/include_range.rb +23 -0
  90. data/motion/core_ext/range/overlaps.rb +8 -0
  91. data/motion/core_ext/regexp.rb +5 -0
  92. data/motion/core_ext/string/access.rb +104 -0
  93. data/motion/core_ext/string/behavior.rb +6 -0
  94. data/motion/core_ext/string/exclude.rb +11 -0
  95. data/motion/core_ext/string/filters.rb +55 -0
  96. data/motion/core_ext/string/indent.rb +43 -0
  97. data/motion/core_ext/string/inflections.rb +178 -0
  98. data/motion/core_ext/string/starts_ends_with.rb +4 -0
  99. data/motion/core_ext/string/strip.rb +24 -0
  100. data/motion/core_ext/time/acts_like.rb +8 -0
  101. data/motion/core_ext/time/calculations.rb +215 -0
  102. data/motion/core_ext/time/conversions.rb +52 -0
  103. data/motion/descendants_tracker.rb +50 -0
  104. data/motion/duration.rb +104 -0
  105. data/motion/hash_with_indifferent_access.rb +253 -0
  106. data/motion/inflections.rb +67 -0
  107. data/motion/inflector/inflections.rb +203 -0
  108. data/motion/inflector/methods.rb +321 -0
  109. data/motion/logger.rb +47 -0
  110. data/motion/number_helper.rb +54 -0
  111. data/motion/version.rb +3 -0
  112. data/motion_blender-support.gemspec +21 -0
  113. data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
  114. data/spec/motion-support/_helpers/inflector_test_cases.rb +270 -0
  115. data/spec/motion-support/callback_spec.rb +702 -0
  116. data/spec/motion-support/concern_spec.rb +93 -0
  117. data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
  118. data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
  119. data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
  120. data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
  121. data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
  122. data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
  123. data/spec/motion-support/core_ext/array_spec.rb +16 -0
  124. data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
  125. data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
  126. data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
  127. data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
  128. data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
  129. data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
  130. data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
  131. data/spec/motion-support/core_ext/hash/deep_delete_if_spec.rb +19 -0
  132. data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
  133. data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
  134. data/spec/motion-support/core_ext/hash/key_spec.rb +236 -0
  135. data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
  136. data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
  137. data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
  138. data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
  139. data/spec/motion-support/core_ext/kernel/singleton_class_spec.rb +9 -0
  140. data/spec/motion-support/core_ext/metaclass_spec.rb +9 -0
  141. data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
  142. data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
  143. data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
  144. data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
  145. data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
  146. data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
  147. data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
  148. data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
  149. data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
  150. data/spec/motion-support/core_ext/numeric/conversions_spec.rb +40 -0
  151. data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
  152. data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
  153. data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
  154. data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
  155. data/spec/motion-support/core_ext/object/inclusion_spec.rb +34 -0
  156. data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
  157. data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
  158. data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
  159. data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
  160. data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
  161. data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
  162. data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
  163. data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
  164. data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
  165. data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
  166. data/spec/motion-support/core_ext/string/filter_spec.rb +49 -0
  167. data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
  168. data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
  169. data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
  170. data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
  171. data/spec/motion-support/core_ext/string_spec.rb +88 -0
  172. data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
  173. data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
  174. data/spec/motion-support/core_ext/time/conversion_spec.rb +53 -0
  175. data/spec/motion-support/descendants_tracker_spec.rb +58 -0
  176. data/spec/motion-support/duration_spec.rb +107 -0
  177. data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
  178. data/spec/motion-support/inflector_spec.rb +504 -0
  179. data/spec/motion-support/ns_dictionary_spec.rb +89 -0
  180. data/spec/motion-support/ns_string_spec.rb +182 -0
  181. data/spec/motion-support/number_helper_spec.rb +55 -0
  182. 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,11 @@
1
+ class Array
2
+ # Extracts options from a set of arguments. Removes and returns the last
3
+ # element in the array if it's a hash, otherwise returns a blank hash.
4
+ def extract_options!
5
+ if last.is_a?(Hash)
6
+ pop
7
+ else
8
+ {}
9
+ end
10
+ end
11
+ 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, '&nbsp;') {|group| p group}
12
+ # ["1", "2"]
13
+ # ["3", "4"]
14
+ # ["5", "&nbsp;"]
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, '&nbsp;') {|group| p group}
49
+ # ["1", "2", "3", "4"]
50
+ # ["5", "6", "7", "&nbsp;"]
51
+ # ["8", "9", "10", "&nbsp;"]
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,7 @@
1
+ class Array
2
+ # The human way of thinking about adding stuff to the end of a list is with append
3
+ alias_method :append, :<<
4
+
5
+ # The human way of thinking about adding stuff to the beginning of a list is with prepend
6
+ alias_method :prepend, :unshift
7
+ 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,8 @@
1
+ require_relative '../object/acts_like'
2
+
3
+ class Date
4
+ # Duck-types as a Date-like class. See Object#acts_like?.
5
+ def acts_like_date?
6
+ true
7
+ end
8
+ 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