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,175 @@
1
+ class Module
2
+ # Provides a delegate class method to easily expose contained objects' public methods
3
+ # as your own. Pass one or more methods (specified as symbols or strings)
4
+ # and the name of the target object via the <tt>:to</tt> option (also a symbol
5
+ # or string). At least one method and the <tt>:to</tt> option are required.
6
+ #
7
+ # Delegation is particularly useful with Active Record associations:
8
+ #
9
+ # class Greeter < ActiveRecord::Base
10
+ # def hello
11
+ # 'hello'
12
+ # end
13
+ #
14
+ # def goodbye
15
+ # 'goodbye'
16
+ # end
17
+ # end
18
+ #
19
+ # class Foo < ActiveRecord::Base
20
+ # belongs_to :greeter
21
+ # delegate :hello, to: :greeter
22
+ # end
23
+ #
24
+ # Foo.new.hello # => "hello"
25
+ # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
26
+ #
27
+ # Multiple delegates to the same target are allowed:
28
+ #
29
+ # class Foo < ActiveRecord::Base
30
+ # belongs_to :greeter
31
+ # delegate :hello, :goodbye, to: :greeter
32
+ # end
33
+ #
34
+ # Foo.new.goodbye # => "goodbye"
35
+ #
36
+ # Methods can be delegated to instance variables, class variables, or constants
37
+ # by providing them as a symbols:
38
+ #
39
+ # class Foo
40
+ # CONSTANT_ARRAY = [0,1,2,3]
41
+ # @@class_array = [4,5,6,7]
42
+ #
43
+ # def initialize
44
+ # @instance_array = [8,9,10,11]
45
+ # end
46
+ # delegate :sum, to: :CONSTANT_ARRAY
47
+ # delegate :min, to: :@@class_array
48
+ # delegate :max, to: :@instance_array
49
+ # end
50
+ #
51
+ # Foo.new.sum # => 6
52
+ # Foo.new.min # => 4
53
+ # Foo.new.max # => 11
54
+ #
55
+ # It's also possible to delegate a method to the class by using +:class+:
56
+ #
57
+ # class Foo
58
+ # def self.hello
59
+ # "world"
60
+ # end
61
+ #
62
+ # delegate :hello, to: :class
63
+ # end
64
+ #
65
+ # Foo.new.hello # => "world"
66
+ #
67
+ # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
68
+ # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
69
+ # delegated to.
70
+ #
71
+ # Person = Struct.new(:name, :address)
72
+ #
73
+ # class Invoice < Struct.new(:client)
74
+ # delegate :name, :address, to: :client, prefix: true
75
+ # end
76
+ #
77
+ # john_doe = Person.new('John Doe', 'Vimmersvej 13')
78
+ # invoice = Invoice.new(john_doe)
79
+ # invoice.client_name # => "John Doe"
80
+ # invoice.client_address # => "Vimmersvej 13"
81
+ #
82
+ # It is also possible to supply a custom prefix.
83
+ #
84
+ # class Invoice < Struct.new(:client)
85
+ # delegate :name, :address, to: :client, prefix: :customer
86
+ # end
87
+ #
88
+ # invoice = Invoice.new(john_doe)
89
+ # invoice.customer_name # => 'John Doe'
90
+ # invoice.customer_address # => 'Vimmersvej 13'
91
+ #
92
+ # If the delegate object is +nil+ an exception is raised, and that happens
93
+ # no matter whether +nil+ responds to the delegated method. You can get a
94
+ # +nil+ instead with the +:allow_nil+ option.
95
+ #
96
+ # class Foo
97
+ # attr_accessor :bar
98
+ # def initialize(bar = nil)
99
+ # @bar = bar
100
+ # end
101
+ # delegate :zoo, to: :bar
102
+ # end
103
+ #
104
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
105
+ #
106
+ # class Foo
107
+ # attr_accessor :bar
108
+ # def initialize(bar = nil)
109
+ # @bar = bar
110
+ # end
111
+ # delegate :zoo, to: :bar, allow_nil: true
112
+ # end
113
+ #
114
+ # Foo.new.zoo # returns nil
115
+ def delegate(*methods)
116
+ options = methods.pop
117
+ unless options.is_a?(Hash) && to = options[:to]
118
+ raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
119
+ end
120
+
121
+ prefix, allow_nil = options.values_at(:prefix, :allow_nil)
122
+ unguarded = !allow_nil
123
+
124
+ if prefix == true && to =~ /^[^a-z_]/
125
+ raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
126
+ end
127
+
128
+ method_prefix = \
129
+ if prefix
130
+ "#{prefix == true ? to : prefix}_"
131
+ else
132
+ ''
133
+ end
134
+
135
+ reference, *hierarchy = to.to_s.split('.')
136
+ entry = resolver =
137
+ case reference
138
+ when 'self'
139
+ ->(_self) { _self }
140
+ when /^@@/
141
+ ->(_self) { _self.class.class_variable_get(reference) }
142
+ when /^@/
143
+ ->(_self) { _self.instance_variable_get(reference) }
144
+ when /^[A-Z]/
145
+ ->(_self) { if reference.to_s =~ /::/ then reference.constantize else _self.class.const_get(reference) end }
146
+ else
147
+ ->(_self) { _self.send(reference) }
148
+ end
149
+ resolver = ->(_self) { hierarchy.reduce(entry.call(_self)) { |obj, method| obj.public_send(method) } } unless hierarchy.empty?
150
+
151
+ methods.each do |method|
152
+ module_exec do
153
+ # def customer_name(*args, &block)
154
+ # begin
155
+ # if unguarded || client || client.respond_to?(:name)
156
+ # client.name(*args, &block)
157
+ # end
158
+ # rescue client.nil? && NoMethodError
159
+ # raise "..."
160
+ # end
161
+ # end
162
+ define_method("#{method_prefix}#{method}") do |*args, &block|
163
+ target = resolver.call(self)
164
+ if unguarded || target || target.respond_to?(method)
165
+ begin
166
+ target.public_send(method, *args, &block)
167
+ rescue target.nil? && NoMethodError # only rescue NoMethodError when target is nil
168
+ raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: #{self.inspect}"
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,60 @@
1
+ class Module
2
+ # Returns the name of the module containing this one.
3
+ #
4
+ # M::N.parent_name # => "M"
5
+ def parent_name
6
+ if defined? @parent_name
7
+ @parent_name
8
+ else
9
+ @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
10
+ end
11
+ end
12
+
13
+ # Returns the module which contains this one according to its name.
14
+ #
15
+ # module M
16
+ # module N
17
+ # end
18
+ # end
19
+ # X = M::N
20
+ #
21
+ # M::N.parent # => M
22
+ # X.parent # => M
23
+ #
24
+ # The parent of top-level and anonymous modules is Object.
25
+ #
26
+ # M.parent # => Object
27
+ # Module.new.parent # => Object
28
+ def parent
29
+ parent_name ? parent_name.constantize : Object
30
+ end
31
+
32
+ # Returns all the parents of this module according to its name, ordered from
33
+ # nested outwards. The receiver is not contained within the result.
34
+ #
35
+ # module M
36
+ # module N
37
+ # end
38
+ # end
39
+ # X = M::N
40
+ #
41
+ # M.parents # => [Object]
42
+ # M::N.parents # => [M, Object]
43
+ # X.parents # => [M, Object]
44
+ def parents
45
+ parents = []
46
+ if parent_name
47
+ parts = parent_name.split('::')
48
+ until parts.empty?
49
+ parents << (parts * '::').constantize
50
+ parts.pop
51
+ end
52
+ end
53
+ parents << Object unless parents.include? Object
54
+ parents
55
+ end
56
+
57
+ def local_constants #:nodoc:
58
+ constants(false)
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ class Module
2
+ def reachable? #:nodoc:
3
+ !anonymous? && name.safe_constantize.equal?(self)
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ class Module
2
+ def remove_possible_method(method)
3
+ if method_defined?(method) || private_method_defined?(method)
4
+ undef_method(method)
5
+ end
6
+ end
7
+
8
+ def redefine_method(method, &block)
9
+ remove_possible_method(method)
10
+ define_method(method, &block)
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ motion_require "module/delegation"
2
+
3
+ class NSDictionary
4
+ def to_hash
5
+ Hash.new.tap do |h|
6
+ h.replace self
7
+ end
8
+ end
9
+
10
+ delegate :symbolize_keys, :to => :to_hash
11
+ end
@@ -0,0 +1,44 @@
1
+ class Numeric
2
+ KILOBYTE = 1024
3
+ MEGABYTE = KILOBYTE * 1024
4
+ GIGABYTE = MEGABYTE * 1024
5
+ TERABYTE = GIGABYTE * 1024
6
+ PETABYTE = TERABYTE * 1024
7
+ EXABYTE = PETABYTE * 1024
8
+
9
+ # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
10
+ def bytes
11
+ self
12
+ end
13
+ alias :byte :bytes
14
+
15
+ def kilobytes
16
+ self * KILOBYTE
17
+ end
18
+ alias :kilobyte :kilobytes
19
+
20
+ def megabytes
21
+ self * MEGABYTE
22
+ end
23
+ alias :megabyte :megabytes
24
+
25
+ def gigabytes
26
+ self * GIGABYTE
27
+ end
28
+ alias :gigabyte :gigabytes
29
+
30
+ def terabytes
31
+ self * TERABYTE
32
+ end
33
+ alias :terabyte :terabytes
34
+
35
+ def petabytes
36
+ self * PETABYTE
37
+ end
38
+ alias :petabyte :petabytes
39
+
40
+ def exabytes
41
+ self * EXABYTE
42
+ end
43
+ alias :exabyte :exabytes
44
+ end
@@ -0,0 +1,7 @@
1
+ class Numeric
2
+ # Stub method to return a pseudo-JSON value from a number. It just returns a string by calling to_s.
3
+ # This should work most of the time.
4
+ def to_json
5
+ to_s
6
+ end
7
+ end
@@ -0,0 +1,75 @@
1
+ class Numeric
2
+ # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
3
+ #
4
+ # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
5
+ # as well as adding or subtracting their results from a Time object. For example:
6
+ #
7
+ # # equivalent to Time.now.advance(months: 1)
8
+ # 1.month.from_now
9
+ #
10
+ # # equivalent to Time.now.advance(years: 2)
11
+ # 2.years.from_now
12
+ #
13
+ # # equivalent to Time.now.advance(months: 4, years: 5)
14
+ # (4.months + 5.years).from_now
15
+ #
16
+ # While these methods provide precise calculation when used as in the examples above, care
17
+ # should be taken to note that this is not true if the result of `months', `years', etc is
18
+ # converted before use:
19
+ #
20
+ # # equivalent to 30.days.to_i.from_now
21
+ # 1.month.to_i.from_now
22
+ #
23
+ # # equivalent to 365.25.days.to_f.from_now
24
+ # 1.year.to_f.from_now
25
+ #
26
+ # In such cases, Ruby's core
27
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
28
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
29
+ # date and time arithmetic.
30
+ def seconds
31
+ MotionSupport::Duration.new(self, [[:seconds, self]])
32
+ end
33
+ alias :second :seconds
34
+
35
+ def minutes
36
+ MotionSupport::Duration.new(self * 60, [[:seconds, self * 60]])
37
+ end
38
+ alias :minute :minutes
39
+
40
+ def hours
41
+ MotionSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
42
+ end
43
+ alias :hour :hours
44
+
45
+ def days
46
+ MotionSupport::Duration.new(self * 24.hours, [[:days, self]])
47
+ end
48
+ alias :day :days
49
+
50
+ def weeks
51
+ MotionSupport::Duration.new(self * 7.days, [[:days, self * 7]])
52
+ end
53
+ alias :week :weeks
54
+
55
+ def fortnights
56
+ MotionSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
57
+ end
58
+ alias :fortnight :fortnights
59
+
60
+ # Reads best without arguments: 10.minutes.ago
61
+ def ago(time = ::Time.now)
62
+ time - self
63
+ end
64
+
65
+ # Reads best with argument: 10.minutes.until(time)
66
+ alias :until :ago
67
+
68
+ # Reads best with argument: 10.minutes.since(time)
69
+ def since(time = ::Time.now)
70
+ time + self
71
+ end
72
+
73
+ # Reads best without arguments: 10.minutes.from_now
74
+ alias :from_now :since
75
+ end
@@ -0,0 +1,10 @@
1
+ class Object
2
+ # A duck-type assistant method. For example, Active Support extends Date
3
+ # to define an <tt>acts_like_date?</tt> method, and extends Time to define
4
+ # <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
5
+ # <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
6
+ # we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
7
+ def acts_like?(duck)
8
+ respond_to? :"acts_like_#{duck}?"
9
+ end
10
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+
3
+ class Object
4
+ # An object is blank if it's false, empty, or a whitespace string.
5
+ # For example, '', ' ', +nil+, [], and {} are all blank.
6
+ #
7
+ # This simplifies:
8
+ #
9
+ # if address.nil? || address.empty?
10
+ #
11
+ # ...to:
12
+ #
13
+ # if address.blank?
14
+ def blank?
15
+ respond_to?(:empty?) ? empty? : !self
16
+ end
17
+
18
+ # An object is present if it's not <tt>blank?</tt>.
19
+ def present?
20
+ !blank?
21
+ end
22
+
23
+ # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
24
+ # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
25
+ #
26
+ # This is handy for any representation of objects where blank is the same
27
+ # as not present at all. For example, this simplifies a common check for
28
+ # HTTP POST/query parameters:
29
+ #
30
+ # state = params[:state] if params[:state].present?
31
+ # country = params[:country] if params[:country].present?
32
+ # region = state || country || 'US'
33
+ #
34
+ # ...becomes:
35
+ #
36
+ # region = params[:state].presence || params[:country].presence || 'US'
37
+ def presence
38
+ self if present?
39
+ end
40
+ end
41
+
42
+ class NilClass
43
+ # +nil+ is blank:
44
+ #
45
+ # nil.blank? # => true
46
+ def blank?
47
+ true
48
+ end
49
+ end
50
+
51
+ class FalseClass
52
+ # +false+ is blank:
53
+ #
54
+ # false.blank? # => true
55
+ def blank?
56
+ true
57
+ end
58
+ end
59
+
60
+ class TrueClass
61
+ # +true+ is not blank:
62
+ #
63
+ # true.blank? # => false
64
+ def blank?
65
+ false
66
+ end
67
+ end
68
+
69
+ class Array
70
+ # An array is blank if it's empty:
71
+ #
72
+ # [].blank? # => true
73
+ # [1,2,3].blank? # => false
74
+ alias_method :blank?, :empty?
75
+ end
76
+
77
+ class Hash
78
+ # A hash is blank if it's empty:
79
+ #
80
+ # {}.blank? # => true
81
+ # { key: 'value' }.blank? # => false
82
+ alias_method :blank?, :empty?
83
+ end
84
+
85
+ class String
86
+ # A string is blank if it's empty or contains whitespaces only:
87
+ #
88
+ # ''.blank? # => true
89
+ # ' '.blank? # => true
90
+ # ' '.blank? # => true
91
+ # ' something here '.blank? # => false
92
+ def blank?
93
+ self !~ /[^[:space:]]/
94
+ end
95
+ end
96
+
97
+ class Numeric #:nodoc:
98
+ # No number is blank:
99
+ #
100
+ # 1.blank? # => false
101
+ # 0.blank? # => false
102
+ def blank?
103
+ false
104
+ end
105
+ end
@@ -0,0 +1,44 @@
1
+ class Object
2
+ # Returns a deep copy of object if it's duplicable. If it's
3
+ # not duplicable, returns +self+.
4
+ #
5
+ # object = Object.new
6
+ # dup = object.deep_dup
7
+ # dup.instance_variable_set(:@a, 1)
8
+ #
9
+ # object.instance_variable_defined?(:@a) #=> false
10
+ # dup.instance_variable_defined?(:@a) #=> true
11
+ def deep_dup
12
+ duplicable? ? dup : self
13
+ end
14
+ end
15
+
16
+ class Array
17
+ # Returns a deep copy of array.
18
+ #
19
+ # array = [1, [2, 3]]
20
+ # dup = array.deep_dup
21
+ # dup[1][2] = 4
22
+ #
23
+ # array[1][2] #=> nil
24
+ # dup[1][2] #=> 4
25
+ def deep_dup
26
+ map { |it| it.deep_dup }
27
+ end
28
+ end
29
+
30
+ class Hash
31
+ # Returns a deep copy of hash.
32
+ #
33
+ # hash = { a: { b: 'b' } }
34
+ # dup = hash.deep_dup
35
+ # dup[:a][:c] = 'c'
36
+ #
37
+ # hash[:a][:c] #=> nil
38
+ # dup[:a][:c] #=> "c"
39
+ def deep_dup
40
+ each_with_object(dup) do |(key, value), hash|
41
+ hash[key.deep_dup] = value.deep_dup
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,83 @@
1
+ #--
2
+ # Most objects are cloneable, but not all. For example you can't dup +nil+:
3
+ #
4
+ # nil.dup # => TypeError: can't dup NilClass
5
+ #
6
+ # Classes may signal their instances are not duplicable removing +dup+/+clone+
7
+ # or raising exceptions from them. So, to dup an arbitrary object you normally
8
+ # use an optimistic approach and are ready to catch an exception, say:
9
+ #
10
+ # arbitrary_object.dup rescue object
11
+ #
12
+ # Rails dups objects in a few critical spots where they are not that arbitrary.
13
+ # That rescue is very expensive (like 40 times slower than a predicate), and it
14
+ # is often triggered.
15
+ #
16
+ # That's why we hardcode the following cases and check duplicable? instead of
17
+ # using that rescue idiom.
18
+ #++
19
+ class Object
20
+ # Can you safely dup this object?
21
+ #
22
+ # False for +nil+, +false+, +true+, symbol, and number objects;
23
+ # true otherwise.
24
+ def duplicable?
25
+ true
26
+ end
27
+ end
28
+
29
+ class NilClass
30
+ # +nil+ is not duplicable:
31
+ #
32
+ # nil.duplicable? # => false
33
+ # nil.dup # => TypeError: can't dup NilClass
34
+ def duplicable?
35
+ false
36
+ end
37
+ end
38
+
39
+ class FalseClass
40
+ # +false+ is not duplicable:
41
+ #
42
+ # false.duplicable? # => false
43
+ # false.dup # => TypeError: can't dup FalseClass
44
+ def duplicable?
45
+ false
46
+ end
47
+ end
48
+
49
+ class TrueClass
50
+ # +true+ is not duplicable:
51
+ #
52
+ # true.duplicable? # => false
53
+ # true.dup # => TypeError: can't dup TrueClass
54
+ def duplicable?
55
+ false
56
+ end
57
+ end
58
+
59
+ class Symbol
60
+ # Symbols are not duplicable:
61
+ #
62
+ # :my_symbol.duplicable? # => false
63
+ # :my_symbol.dup # => TypeError: can't dup Symbol
64
+ def duplicable?
65
+ false
66
+ end
67
+ end
68
+
69
+ class Numeric
70
+ # Numbers are not duplicable:
71
+ #
72
+ # 3.duplicable? # => false
73
+ # 3.dup # => TypeError: can't dup Fixnum
74
+ def duplicable?
75
+ false
76
+ end
77
+ end
78
+
79
+ class BigDecimal
80
+ def duplicable?
81
+ true
82
+ end
83
+ end
@@ -0,0 +1,28 @@
1
+ class Object
2
+ # Returns a hash with string keys that maps instance variable names without "@" to their
3
+ # corresponding values.
4
+ #
5
+ # class C
6
+ # def initialize(x, y)
7
+ # @x, @y = x, y
8
+ # end
9
+ # end
10
+ #
11
+ # C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
12
+ def instance_values
13
+ Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
14
+ end
15
+
16
+ # Returns an array of instance variable names as strings including "@".
17
+ #
18
+ # class C
19
+ # def initialize(x, y)
20
+ # @x, @y = x, y
21
+ # end
22
+ # end
23
+ #
24
+ # C.new(0, 1).instance_variable_names # => ["@y", "@x"]
25
+ def instance_variable_names
26
+ instance_variables.map { |var| var.to_s }
27
+ end
28
+ end