motion-support 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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