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.
- data/.gitignore +3 -0
- data/Gemfile +1 -1
- data/README.md +231 -2
- data/Rakefile +3 -3
- data/app/app_delegate.rb +0 -2
- data/examples/Inflector/.gitignore +16 -0
- data/examples/Inflector/Gemfile +5 -0
- data/examples/Inflector/Rakefile +13 -0
- data/examples/Inflector/app/app_delegate.rb +8 -0
- data/examples/Inflector/app/inflections.rb +5 -0
- data/examples/Inflector/app/inflector_view_controller.rb +120 -0
- data/examples/Inflector/app/words_view_controller.rb +101 -0
- data/examples/Inflector/resources/Default-568h@2x.png +0 -0
- data/examples/Inflector/spec/main_spec.rb +9 -0
- data/lib/motion-support/core_ext/array.rb +13 -0
- data/lib/motion-support/core_ext/class.rb +8 -0
- data/lib/motion-support/core_ext/hash.rb +16 -0
- data/lib/motion-support/core_ext/integer.rb +9 -0
- data/lib/motion-support/core_ext/module.rb +14 -0
- data/lib/motion-support/core_ext/numeric.rb +8 -0
- data/lib/motion-support/core_ext/object.rb +14 -0
- data/lib/motion-support/core_ext/range.rb +8 -0
- data/lib/motion-support/core_ext/string.rb +14 -0
- data/lib/motion-support/core_ext/time.rb +19 -0
- data/lib/motion-support/core_ext.rb +3 -0
- data/lib/motion-support/inflector.rb +12 -156
- data/lib/motion-support.rb +5 -5
- data/motion/_stdlib/cgi.rb +22 -0
- data/motion/_stdlib/date.rb +77 -0
- data/motion/_stdlib/time.rb +19 -0
- data/motion/core_ext/array/access.rb +28 -0
- data/motion/core_ext/array/conversions.rb +86 -0
- data/motion/core_ext/array/extract_options.rb +11 -0
- data/motion/core_ext/array/grouping.rb +99 -0
- data/motion/core_ext/array/prepend_and_append.rb +7 -0
- data/motion/core_ext/array/wrap.rb +45 -0
- data/{lib/motion-support → motion/core_ext}/array.rb +0 -4
- data/motion/core_ext/class/attribute.rb +119 -0
- data/motion/core_ext/class/attribute_accessors.rb +168 -0
- data/motion/core_ext/date/acts_like.rb +8 -0
- data/motion/core_ext/date/calculations.rb +117 -0
- data/motion/core_ext/date/conversions.rb +56 -0
- data/motion/core_ext/date_and_time/calculations.rb +232 -0
- data/motion/core_ext/enumerable.rb +90 -0
- data/motion/core_ext/hash/deep_merge.rb +27 -0
- data/motion/core_ext/hash/except.rb +15 -0
- data/motion/core_ext/hash/indifferent_access.rb +19 -0
- data/motion/core_ext/hash/keys.rb +138 -0
- data/motion/core_ext/hash/reverse_merge.rb +22 -0
- data/motion/core_ext/hash/slice.rb +40 -0
- data/motion/core_ext/integer/inflections.rb +27 -0
- data/motion/core_ext/integer/multiple.rb +10 -0
- data/motion/core_ext/integer/time.rb +41 -0
- data/{lib/motion-support → motion/core_ext}/metaclass.rb +0 -0
- data/motion/core_ext/module/aliasing.rb +69 -0
- data/motion/core_ext/module/anonymous.rb +19 -0
- data/motion/core_ext/module/attr_internal.rb +38 -0
- data/motion/core_ext/module/attribute_accessors.rb +64 -0
- data/motion/core_ext/module/delegation.rb +175 -0
- data/motion/core_ext/module/introspection.rb +60 -0
- data/motion/core_ext/module/reachable.rb +5 -0
- data/motion/core_ext/module/remove_method.rb +12 -0
- data/motion/core_ext/ns_dictionary.rb +11 -0
- data/motion/core_ext/numeric/bytes.rb +44 -0
- data/motion/core_ext/numeric/conversions.rb +7 -0
- data/motion/core_ext/numeric/time.rb +75 -0
- data/motion/core_ext/object/acts_like.rb +10 -0
- data/motion/core_ext/object/blank.rb +105 -0
- data/motion/core_ext/object/deep_dup.rb +44 -0
- data/motion/core_ext/object/duplicable.rb +83 -0
- data/motion/core_ext/object/instance_variables.rb +28 -0
- data/motion/core_ext/object/to_param.rb +58 -0
- data/motion/core_ext/object/to_query.rb +26 -0
- data/motion/core_ext/object/try.rb +78 -0
- data/motion/core_ext/range/include_range.rb +23 -0
- data/motion/core_ext/range/overlaps.rb +8 -0
- data/motion/core_ext/regexp.rb +5 -0
- data/motion/core_ext/string/access.rb +104 -0
- data/motion/core_ext/string/behavior.rb +6 -0
- data/motion/core_ext/string/exclude.rb +11 -0
- data/motion/core_ext/string/filters.rb +55 -0
- data/motion/core_ext/string/indent.rb +43 -0
- data/motion/core_ext/string/inflections.rb +195 -0
- data/motion/core_ext/string/starts_ends_with.rb +4 -0
- data/motion/core_ext/string/strip.rb +24 -0
- data/motion/core_ext/time/acts_like.rb +8 -0
- data/motion/core_ext/time/calculations.rb +215 -0
- data/motion/core_ext/time/conversions.rb +52 -0
- data/motion/duration.rb +104 -0
- data/motion/hash_with_indifferent_access.rb +251 -0
- data/motion/inflections.rb +67 -0
- data/motion/inflector/inflections.rb +203 -0
- data/motion/inflector/methods.rb +321 -0
- data/{lib/motion-support → motion}/logger.rb +0 -0
- data/{lib/motion-support → motion}/version.rb +1 -1
- data/motion-support.gemspec +2 -2
- data/spec/motion-support/_helpers/constantize_test_cases.rb +75 -0
- data/spec/motion-support/_helpers/inflector_test_cases.rb +313 -0
- data/spec/motion-support/core_ext/array/access_spec.rb +29 -0
- data/spec/motion-support/core_ext/array/conversion_spec.rb +60 -0
- data/spec/motion-support/core_ext/array/extract_options_spec.rb +15 -0
- data/spec/motion-support/core_ext/array/grouping_spec.rb +85 -0
- data/spec/motion-support/core_ext/array/prepend_and_append_spec.rb +25 -0
- data/spec/motion-support/core_ext/array/wrap_spec.rb +19 -0
- data/spec/motion-support/{array_spec.rb → core_ext/array_spec.rb} +0 -5
- data/spec/motion-support/core_ext/class/attribute_accessor_spec.rb +127 -0
- data/spec/motion-support/core_ext/class/attribute_spec.rb +92 -0
- data/spec/motion-support/core_ext/date/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/date/calculation_spec.rb +186 -0
- data/spec/motion-support/core_ext/date/conversion_spec.rb +18 -0
- data/spec/motion-support/core_ext/date_and_time/calculation_spec.rb +336 -0
- data/spec/motion-support/core_ext/enumerable_spec.rb +130 -0
- data/spec/motion-support/core_ext/hash/deep_merge_spec.rb +32 -0
- data/spec/motion-support/core_ext/hash/except_spec.rb +43 -0
- data/spec/motion-support/core_ext/hash/key_spec.rb +230 -0
- data/spec/motion-support/core_ext/hash/reverse_merge_spec.rb +26 -0
- data/spec/motion-support/core_ext/hash/slice_spec.rb +61 -0
- data/spec/motion-support/core_ext/integer/inflection_spec.rb +23 -0
- data/spec/motion-support/core_ext/integer/multiple_spec.rb +19 -0
- data/spec/motion-support/{metaclass_spec.rb → core_ext/metaclass_spec.rb} +0 -0
- data/spec/motion-support/core_ext/module/aliasing_spec.rb +143 -0
- data/spec/motion-support/core_ext/module/anonymous_spec.rb +29 -0
- data/spec/motion-support/core_ext/module/attr_internal_spec.rb +104 -0
- data/spec/motion-support/core_ext/module/attribute_accessor_spec.rb +86 -0
- data/spec/motion-support/core_ext/module/delegation_spec.rb +136 -0
- data/spec/motion-support/core_ext/module/introspection_spec.rb +70 -0
- data/spec/motion-support/core_ext/module/reachable_spec.rb +61 -0
- data/spec/motion-support/core_ext/module/remove_method_spec.rb +25 -0
- data/spec/motion-support/core_ext/numeric/bytes_spec.rb +43 -0
- data/spec/motion-support/core_ext/object/acts_like_spec.rb +21 -0
- data/spec/motion-support/core_ext/object/blank_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/deep_dup_spec.rb +54 -0
- data/spec/motion-support/core_ext/object/duplicable_spec.rb +31 -0
- data/spec/motion-support/core_ext/object/instance_variable_spec.rb +19 -0
- data/spec/motion-support/core_ext/object/to_param_spec.rb +75 -0
- data/spec/motion-support/core_ext/object/to_query_spec.rb +37 -0
- data/spec/motion-support/core_ext/object/try_spec.rb +92 -0
- data/spec/motion-support/core_ext/range/include_range_spec.rb +31 -0
- data/spec/motion-support/core_ext/range/overlap_spec.rb +43 -0
- data/spec/motion-support/core_ext/regexp_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/access_spec.rb +53 -0
- data/spec/motion-support/core_ext/string/behavior_spec.rb +7 -0
- data/spec/motion-support/core_ext/string/exclude_spec.rb +8 -0
- data/spec/motion-support/core_ext/string/filter_spec.rb +48 -0
- data/spec/motion-support/core_ext/string/indent_spec.rb +56 -0
- data/spec/motion-support/core_ext/string/inflection_spec.rb +142 -0
- data/spec/motion-support/core_ext/string/starts_end_with_spec.rb +14 -0
- data/spec/motion-support/core_ext/string/strip_spec.rb +34 -0
- data/spec/motion-support/core_ext/string_spec.rb +88 -0
- data/spec/motion-support/core_ext/time/acts_like_spec.rb +11 -0
- data/spec/motion-support/core_ext/time/calculation_spec.rb +201 -0
- data/spec/motion-support/core_ext/time/conversion_spec.rb +54 -0
- data/spec/motion-support/duration_spec.rb +107 -0
- data/spec/motion-support/hash_with_indifferent_access_spec.rb +605 -0
- data/spec/motion-support/inflector_spec.rb +474 -35
- data/spec/motion-support/ns_dictionary_spec.rb +29 -0
- metadata +212 -35
- data/lib/motion-support/cattr_accessor.rb +0 -19
- data/lib/motion-support/class_inheritable_accessor.rb +0 -23
- data/lib/motion-support/class_inheritable_array.rb +0 -29
- data/lib/motion-support/hash.rb +0 -31
- data/lib/motion-support/nilclass.rb +0 -5
- data/lib/motion-support/object.rb +0 -17
- data/lib/motion-support/string.rb +0 -71
- data/spec/motion-support/cattr_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_accessor_spec.rb +0 -49
- data/spec/motion-support/class_inheritable_array_spec.rb +0 -61
- data/spec/motion-support/hash_spec.rb +0 -31
- data/spec/motion-support/nilclass_spec.rb +0 -5
- data/spec/motion-support/object_spec.rb +0 -43
- 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,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,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,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
|