motion-support 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +1 -1
  3. data/README.md +231 -2
  4. data/Rakefile +3 -3
  5. data/app/app_delegate.rb +0 -2
  6. data/examples/Inflector/.gitignore +16 -0
  7. data/examples/Inflector/Gemfile +5 -0
  8. data/examples/Inflector/Rakefile +13 -0
  9. data/examples/Inflector/app/app_delegate.rb +8 -0
  10. data/examples/Inflector/app/inflections.rb +5 -0
  11. data/examples/Inflector/app/inflector_view_controller.rb +120 -0
  12. data/examples/Inflector/app/words_view_controller.rb +101 -0
  13. data/examples/Inflector/resources/Default-568h@2x.png +0 -0
  14. data/examples/Inflector/spec/main_spec.rb +9 -0
  15. data/lib/motion-support/core_ext/array.rb +13 -0
  16. data/lib/motion-support/core_ext/class.rb +8 -0
  17. data/lib/motion-support/core_ext/hash.rb +16 -0
  18. data/lib/motion-support/core_ext/integer.rb +9 -0
  19. data/lib/motion-support/core_ext/module.rb +14 -0
  20. data/lib/motion-support/core_ext/numeric.rb +8 -0
  21. data/lib/motion-support/core_ext/object.rb +14 -0
  22. data/lib/motion-support/core_ext/range.rb +8 -0
  23. data/lib/motion-support/core_ext/string.rb +14 -0
  24. data/lib/motion-support/core_ext/time.rb +19 -0
  25. data/lib/motion-support/core_ext.rb +3 -0
  26. data/lib/motion-support/inflector.rb +12 -156
  27. data/lib/motion-support.rb +5 -5
  28. data/motion/_stdlib/cgi.rb +22 -0
  29. data/motion/_stdlib/date.rb +77 -0
  30. data/motion/_stdlib/time.rb +19 -0
  31. data/motion/core_ext/array/access.rb +28 -0
  32. data/motion/core_ext/array/conversions.rb +86 -0
  33. data/motion/core_ext/array/extract_options.rb +11 -0
  34. data/motion/core_ext/array/grouping.rb +99 -0
  35. data/motion/core_ext/array/prepend_and_append.rb +7 -0
  36. data/motion/core_ext/array/wrap.rb +45 -0
  37. data/{lib/motion-support → motion/core_ext}/array.rb +0 -4
  38. data/motion/core_ext/class/attribute.rb +119 -0
  39. data/motion/core_ext/class/attribute_accessors.rb +168 -0
  40. data/motion/core_ext/date/acts_like.rb +8 -0
  41. data/motion/core_ext/date/calculations.rb +117 -0
  42. data/motion/core_ext/date/conversions.rb +56 -0
  43. data/motion/core_ext/date_and_time/calculations.rb +232 -0
  44. data/motion/core_ext/enumerable.rb +90 -0
  45. data/motion/core_ext/hash/deep_merge.rb +27 -0
  46. data/motion/core_ext/hash/except.rb +15 -0
  47. data/motion/core_ext/hash/indifferent_access.rb +19 -0
  48. data/motion/core_ext/hash/keys.rb +138 -0
  49. data/motion/core_ext/hash/reverse_merge.rb +22 -0
  50. data/motion/core_ext/hash/slice.rb +40 -0
  51. data/motion/core_ext/integer/inflections.rb +27 -0
  52. data/motion/core_ext/integer/multiple.rb +10 -0
  53. data/motion/core_ext/integer/time.rb +41 -0
  54. data/{lib/motion-support → motion/core_ext}/metaclass.rb +0 -0
  55. data/motion/core_ext/module/aliasing.rb +69 -0
  56. data/motion/core_ext/module/anonymous.rb +19 -0
  57. data/motion/core_ext/module/attr_internal.rb +38 -0
  58. data/motion/core_ext/module/attribute_accessors.rb +64 -0
  59. data/motion/core_ext/module/delegation.rb +175 -0
  60. data/motion/core_ext/module/introspection.rb +60 -0
  61. data/motion/core_ext/module/reachable.rb +5 -0
  62. data/motion/core_ext/module/remove_method.rb +12 -0
  63. data/motion/core_ext/ns_dictionary.rb +11 -0
  64. data/motion/core_ext/numeric/bytes.rb +44 -0
  65. data/motion/core_ext/numeric/conversions.rb +7 -0
  66. data/motion/core_ext/numeric/time.rb +75 -0
  67. data/motion/core_ext/object/acts_like.rb +10 -0
  68. data/motion/core_ext/object/blank.rb +105 -0
  69. data/motion/core_ext/object/deep_dup.rb +44 -0
  70. data/motion/core_ext/object/duplicable.rb +83 -0
  71. data/motion/core_ext/object/instance_variables.rb +28 -0
  72. data/motion/core_ext/object/to_param.rb +58 -0
  73. data/motion/core_ext/object/to_query.rb +26 -0
  74. data/motion/core_ext/object/try.rb +78 -0
  75. data/motion/core_ext/range/include_range.rb +23 -0
  76. data/motion/core_ext/range/overlaps.rb +8 -0
  77. data/motion/core_ext/regexp.rb +5 -0
  78. data/motion/core_ext/string/access.rb +104 -0
  79. data/motion/core_ext/string/behavior.rb +6 -0
  80. data/motion/core_ext/string/exclude.rb +11 -0
  81. data/motion/core_ext/string/filters.rb +55 -0
  82. data/motion/core_ext/string/indent.rb +43 -0
  83. data/motion/core_ext/string/inflections.rb +195 -0
  84. data/motion/core_ext/string/starts_ends_with.rb +4 -0
  85. data/motion/core_ext/string/strip.rb +24 -0
  86. data/motion/core_ext/time/acts_like.rb +8 -0
  87. data/motion/core_ext/time/calculations.rb +215 -0
  88. data/motion/core_ext/time/conversions.rb +52 -0
  89. data/motion/duration.rb +104 -0
  90. data/motion/hash_with_indifferent_access.rb +251 -0
  91. data/motion/inflections.rb +67 -0
  92. data/motion/inflector/inflections.rb +203 -0
  93. data/motion/inflector/methods.rb +321 -0
  94. data/{lib/motion-support → motion}/logger.rb +0 -0
  95. data/{lib/motion-support → motion}/version.rb +1 -1
  96. data/motion-support.gemspec +2 -2
  97. data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
  98. data/spec/motion-support/_helpers/inflector_test_cases.rb +313 -0
  99. data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
  100. data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
  101. data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
  102. data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
  103. data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
  104. data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
  105. data/spec/motion-support/{array_spec.rb → core_ext/array_spec.rb} +0 -5
  106. data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
  107. data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
  108. data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
  109. data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
  110. data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
  111. data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
  112. data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
  113. data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
  114. data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
  115. data/spec/motion-support/core_ext/hash/key_spec.rb +230 -0
  116. data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
  117. data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
  118. data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
  119. data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
  120. data/spec/motion-support/{metaclass_spec.rb → core_ext/metaclass_spec.rb} +0 -0
  121. data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
  122. data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
  123. data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
  124. data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
  125. data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
  126. data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
  127. data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
  128. data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
  129. data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
  130. data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
  131. data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
  132. data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
  133. data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
  134. data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
  135. data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
  136. data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
  137. data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
  138. data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
  139. data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
  140. data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
  141. data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
  142. data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
  143. data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
  144. data/spec/motion-support/core_ext/string/filter_spec.rb +48 -0
  145. data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
  146. data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
  147. data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
  148. data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
  149. data/spec/motion-support/core_ext/string_spec.rb +88 -0
  150. data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
  151. data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
  152. data/spec/motion-support/core_ext/time/conversion_spec.rb +54 -0
  153. data/spec/motion-support/duration_spec.rb +107 -0
  154. data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
  155. data/spec/motion-support/inflector_spec.rb +474 -35
  156. data/spec/motion-support/ns_dictionary_spec.rb +29 -0
  157. metadata +212 -35
  158. data/lib/motion-support/cattr_accessor.rb +0 -19
  159. data/lib/motion-support/class_inheritable_accessor.rb +0 -23
  160. data/lib/motion-support/class_inheritable_array.rb +0 -29
  161. data/lib/motion-support/hash.rb +0 -31
  162. data/lib/motion-support/nilclass.rb +0 -5
  163. data/lib/motion-support/object.rb +0 -17
  164. data/lib/motion-support/string.rb +0 -71
  165. data/spec/motion-support/cattr_accessor_spec.rb +0 -49
  166. data/spec/motion-support/class_inheritable_accessor_spec.rb +0 -49
  167. data/spec/motion-support/class_inheritable_array_spec.rb +0 -61
  168. data/spec/motion-support/hash_spec.rb +0 -31
  169. data/spec/motion-support/nilclass_spec.rb +0 -5
  170. data/spec/motion-support/object_spec.rb +0 -43
  171. data/spec/motion-support/string_spec.rb +0 -145
@@ -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,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,138 @@
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)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
86
+ end
87
+ result
88
+ end
89
+
90
+ # Destructively convert all keys by using the block operation.
91
+ # This includes the keys from the root hash and from all
92
+ # nested hashes.
93
+ def deep_transform_keys!(&block)
94
+ keys.each do |key|
95
+ value = delete(key)
96
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
97
+ end
98
+ self
99
+ end
100
+
101
+ # Return a new hash with all keys converted to strings.
102
+ # This includes the keys from the root hash and from all
103
+ # nested hashes.
104
+ #
105
+ # hash = { person: { name: 'Rob', age: '28' } }
106
+ #
107
+ # hash.deep_stringify_keys
108
+ # # => { "person" => { "name" => "Rob", "age" => "28" } }
109
+ def deep_stringify_keys
110
+ deep_transform_keys{ |key| key.to_s }
111
+ end
112
+
113
+ # Destructively convert all keys to strings.
114
+ # This includes the keys from the root hash and from all
115
+ # nested hashes.
116
+ def deep_stringify_keys!
117
+ deep_transform_keys!{ |key| key.to_s }
118
+ end
119
+
120
+ # Return a new hash with all keys converted to symbols, as long as
121
+ # they respond to +to_sym+. This includes the keys from the root hash
122
+ # and from all nested hashes.
123
+ #
124
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
125
+ #
126
+ # hash.deep_symbolize_keys
127
+ # # => { person: { name: "Rob", age: "28" } }
128
+ def deep_symbolize_keys
129
+ deep_transform_keys{ |key| key.to_sym rescue key }
130
+ end
131
+
132
+ # Destructively convert all keys to symbols, as long as they respond
133
+ # to +to_sym+. This includes the keys from the root hash and from all
134
+ # nested hashes.
135
+ def deep_symbolize_keys!
136
+ deep_transform_keys!{ |key| key.to_sym rescue key }
137
+ end
138
+ 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
@@ -0,0 +1,27 @@
1
+ class Integer
2
+ # Ordinalize turns a number into an ordinal string used to denote the
3
+ # position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
4
+ #
5
+ # 1.ordinalize # => "1st"
6
+ # 2.ordinalize # => "2nd"
7
+ # 1002.ordinalize # => "1002nd"
8
+ # 1003.ordinalize # => "1003rd"
9
+ # -11.ordinalize # => "-11th"
10
+ # -1001.ordinalize # => "-1001st"
11
+ def ordinalize
12
+ MotionSupport::Inflector.ordinalize(self)
13
+ end
14
+
15
+ # Ordinal returns the suffix used to denote the position
16
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
17
+ #
18
+ # 1.ordinal # => "st"
19
+ # 2.ordinal # => "nd"
20
+ # 1002.ordinal # => "nd"
21
+ # 1003.ordinal # => "rd"
22
+ # -11.ordinal # => "th"
23
+ # -1001.ordinal # => "st"
24
+ def ordinal
25
+ MotionSupport::Inflector.ordinal(self)
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ class Integer
2
+ # Check whether the integer is evenly divisible by the argument.
3
+ #
4
+ # 0.multiple_of?(0) #=> true
5
+ # 6.multiple_of?(5) #=> false
6
+ # 10.multiple_of?(2) #=> true
7
+ def multiple_of?(number)
8
+ number != 0 ? self % number == 0 : zero?
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ class Integer
2
+ # Enables the use of time calculations and declarations, like <tt>45.minutes +
3
+ # 2.hours + 4.years</tt>.
4
+ #
5
+ # These methods use Time#advance for precise date calculations when using
6
+ # <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
7
+ # results from a Time object.
8
+ #
9
+ # # equivalent to Time.now.advance(months: 1)
10
+ # 1.month.from_now
11
+ #
12
+ # # equivalent to Time.now.advance(years: 2)
13
+ # 2.years.from_now
14
+ #
15
+ # # equivalent to Time.now.advance(months: 4, years: 5)
16
+ # (4.months + 5.years).from_now
17
+ #
18
+ # While these methods provide precise calculation when used as in the examples
19
+ # above, care should be taken to note that this is not true if the result of
20
+ # +months+, +years+, etc is converted before use:
21
+ #
22
+ # # equivalent to 30.days.to_i.from_now
23
+ # 1.month.to_i.from_now
24
+ #
25
+ # # equivalent to 365.25.days.to_f.from_now
26
+ # 1.year.to_f.from_now
27
+ #
28
+ # In such cases, Ruby's core
29
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
30
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
31
+ # date and time arithmetic.
32
+ def months
33
+ MotionSupport::Duration.new(self * 30.days, [[:months, self]])
34
+ end
35
+ alias :month :months
36
+
37
+ def years
38
+ MotionSupport::Duration.new(self * 365.25.days, [[:years, self]])
39
+ end
40
+ alias :year :years
41
+ end
@@ -0,0 +1,69 @@
1
+ class Module
2
+ # Encapsulates the common pattern of:
3
+ #
4
+ # alias_method :foo_without_feature, :foo
5
+ # alias_method :foo, :foo_with_feature
6
+ #
7
+ # With this, you simply do:
8
+ #
9
+ # alias_method_chain :foo, :feature
10
+ #
11
+ # And both aliases are set up for you.
12
+ #
13
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
14
+ #
15
+ # alias_method_chain :foo?, :feature
16
+ #
17
+ # is equivalent to
18
+ #
19
+ # alias_method :foo_without_feature?, :foo?
20
+ # alias_method :foo?, :foo_with_feature?
21
+ #
22
+ # so you can safely chain foo, foo?, and foo! with the same feature.
23
+ def alias_method_chain(target, feature)
24
+ # Strip out punctuation on predicates or bang methods since
25
+ # e.g. target?_without_feature is not a valid method name.
26
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
27
+ yield(aliased_target, punctuation) if block_given?
28
+
29
+ with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
30
+ without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
31
+
32
+ alias_method without_method, target
33
+ alias_method target, with_method
34
+
35
+ case
36
+ when public_method_defined?(without_method)
37
+ public target
38
+ when protected_method_defined?(without_method)
39
+ protected target
40
+ when private_method_defined?(without_method)
41
+ private target
42
+ end
43
+ end
44
+
45
+ # Allows you to make aliases for attributes, which includes
46
+ # getter, setter, and query methods.
47
+ #
48
+ # class Content < ActiveRecord::Base
49
+ # # has a title attribute
50
+ # end
51
+ #
52
+ # class Email < Content
53
+ # alias_attribute :subject, :title
54
+ # end
55
+ #
56
+ # e = Email.find(1)
57
+ # e.title # => "Superstars"
58
+ # e.subject # => "Superstars"
59
+ # e.subject? # => true
60
+ # e.subject = "Megastars"
61
+ # e.title # => "Megastars"
62
+ def alias_attribute(new_name, old_name)
63
+ module_exec do
64
+ define_method(new_name) { self.send(old_name) } # def subject; self.title; end
65
+ define_method("#{new_name}?") { self.send("#{old_name}?") } # def subject?; self.title?; end
66
+ define_method("#{new_name}=") { |v| self.send("#{old_name}=", v) } # def subject=(v); self.title = v; end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ class Module
2
+ # A module may or may not have a name.
3
+ #
4
+ # module M; end
5
+ # M.name # => "M"
6
+ #
7
+ # m = Module.new
8
+ # m.name # => nil
9
+ #
10
+ # A module gets a name when it is first assigned to a constant. Either
11
+ # via the +module+ or +class+ keyword or by an explicit assignment:
12
+ #
13
+ # m = Module.new # creates an anonymous module
14
+ # M = m # => m gets a name here as a side-effect
15
+ # m.name # => "M"
16
+ def anonymous?
17
+ name.nil?
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ class Module
2
+ # Declares an attribute reader backed by an internally-named instance variable.
3
+ def attr_internal_reader(*attrs)
4
+ attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
5
+ end
6
+
7
+ # Declares an attribute writer backed by an internally-named instance variable.
8
+ def attr_internal_writer(*attrs)
9
+ attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
10
+ end
11
+
12
+ # Declares an attribute reader and writer backed by an internally-named instance
13
+ # variable.
14
+ def attr_internal_accessor(*attrs)
15
+ attr_internal_reader(*attrs)
16
+ attr_internal_writer(*attrs)
17
+ end
18
+ alias_method :attr_internal, :attr_internal_accessor
19
+
20
+ class << self; attr_accessor :attr_internal_naming_format end
21
+ self.attr_internal_naming_format = '@_%s'
22
+
23
+ private
24
+ def attr_internal_ivar_name(attr)
25
+ Module.attr_internal_naming_format % attr
26
+ end
27
+
28
+ def attr_internal_define(attr_name, type)
29
+ internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
30
+ class_eval do # class_eval is necessary on 1.9 or else the methods a made private
31
+ # use native attr_* methods as they are faster on some Ruby implementations
32
+ send("attr_#{type}", internal_name)
33
+ end
34
+ attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
35
+ alias_method attr_name, internal_name
36
+ remove_method internal_name
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ class Module
2
+ def mattr_reader(*syms)
3
+ receiver = self
4
+ options = syms.extract_options!
5
+ syms.each do |sym|
6
+ raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
7
+ class_exec do
8
+ unless class_variable_defined?("@@#{sym}")
9
+ class_variable_set("@@#{sym}", nil)
10
+ end
11
+
12
+ define_singleton_method sym do
13
+ class_variable_get("@@#{sym}")
14
+ end
15
+ end
16
+
17
+ unless options[:instance_reader] == false || options[:instance_accessor] == false
18
+ class_exec do
19
+ define_method sym do
20
+ receiver.class_variable_get("@@#{sym}")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def mattr_writer(*syms)
28
+ receiver = self
29
+ options = syms.extract_options!
30
+ syms.each do |sym|
31
+ raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
32
+ class_exec do
33
+ define_singleton_method "#{sym}=" do |obj|
34
+ class_variable_set("@@#{sym}", obj)
35
+ end
36
+ end
37
+
38
+ unless options[:instance_writer] == false || options[:instance_accessor] == false
39
+ class_exec do
40
+ define_method "#{sym}=" do |obj|
41
+ receiver.class_variable_set("@@#{sym}", obj)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # Extends the module object with module and instance accessors for class attributes,
49
+ # just like the native attr* accessors for instance attributes.
50
+ #
51
+ # module AppConfiguration
52
+ # mattr_accessor :google_api_key
53
+ #
54
+ # self.google_api_key = "123456789"
55
+ # end
56
+ #
57
+ # AppConfiguration.google_api_key # => "123456789"
58
+ # AppConfiguration.google_api_key = "overriding the api key!"
59
+ # AppConfiguration.google_api_key # => "overriding the api key!"
60
+ def mattr_accessor(*syms)
61
+ mattr_reader(*syms)
62
+ mattr_writer(*syms)
63
+ end
64
+ end