core_ext 0.0.1
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.
- checksums.yaml +7 -0
- data/README.md +3 -0
- data/lib/core_ext/array/access.rb +76 -0
- data/lib/core_ext/array/conversions.rb +211 -0
- data/lib/core_ext/array/extract_options.rb +29 -0
- data/lib/core_ext/array/grouping.rb +116 -0
- data/lib/core_ext/array/inquiry.rb +17 -0
- data/lib/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/core_ext/array/wrap.rb +46 -0
- data/lib/core_ext/array.rb +7 -0
- data/lib/core_ext/array_inquirer.rb +44 -0
- data/lib/core_ext/benchmark.rb +14 -0
- data/lib/core_ext/benchmarkable.rb +49 -0
- data/lib/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/core_ext/big_decimal.rb +1 -0
- data/lib/core_ext/builder.rb +6 -0
- data/lib/core_ext/callbacks.rb +770 -0
- data/lib/core_ext/class/attribute.rb +128 -0
- data/lib/core_ext/class/attribute_accessors.rb +4 -0
- data/lib/core_ext/class/subclasses.rb +42 -0
- data/lib/core_ext/class.rb +2 -0
- data/lib/core_ext/concern.rb +142 -0
- data/lib/core_ext/configurable.rb +148 -0
- data/lib/core_ext/date/acts_like.rb +8 -0
- data/lib/core_ext/date/blank.rb +12 -0
- data/lib/core_ext/date/calculations.rb +143 -0
- data/lib/core_ext/date/conversions.rb +93 -0
- data/lib/core_ext/date/zones.rb +6 -0
- data/lib/core_ext/date.rb +5 -0
- data/lib/core_ext/date_and_time/calculations.rb +328 -0
- data/lib/core_ext/date_and_time/zones.rb +40 -0
- data/lib/core_ext/date_time/acts_like.rb +14 -0
- data/lib/core_ext/date_time/blank.rb +12 -0
- data/lib/core_ext/date_time/calculations.rb +177 -0
- data/lib/core_ext/date_time/conversions.rb +104 -0
- data/lib/core_ext/date_time/zones.rb +6 -0
- data/lib/core_ext/date_time.rb +5 -0
- data/lib/core_ext/deprecation/behaviors.rb +86 -0
- data/lib/core_ext/deprecation/instance_delegator.rb +24 -0
- data/lib/core_ext/deprecation/method_wrappers.rb +70 -0
- data/lib/core_ext/deprecation/proxy_wrappers.rb +149 -0
- data/lib/core_ext/deprecation/reporting.rb +105 -0
- data/lib/core_ext/deprecation.rb +43 -0
- data/lib/core_ext/digest/uuid.rb +51 -0
- data/lib/core_ext/duration.rb +157 -0
- data/lib/core_ext/enumerable.rb +106 -0
- data/lib/core_ext/file/atomic.rb +68 -0
- data/lib/core_ext/file.rb +1 -0
- data/lib/core_ext/hash/compact.rb +20 -0
- data/lib/core_ext/hash/conversions.rb +261 -0
- data/lib/core_ext/hash/deep_merge.rb +38 -0
- data/lib/core_ext/hash/except.rb +22 -0
- data/lib/core_ext/hash/indifferent_access.rb +23 -0
- data/lib/core_ext/hash/keys.rb +170 -0
- data/lib/core_ext/hash/reverse_merge.rb +22 -0
- data/lib/core_ext/hash/slice.rb +48 -0
- data/lib/core_ext/hash/transform_values.rb +29 -0
- data/lib/core_ext/hash.rb +9 -0
- data/lib/core_ext/hash_with_indifferent_access.rb +298 -0
- data/lib/core_ext/inflections.rb +70 -0
- data/lib/core_ext/inflector/inflections.rb +244 -0
- data/lib/core_ext/inflector/methods.rb +381 -0
- data/lib/core_ext/inflector/transliterate.rb +112 -0
- data/lib/core_ext/inflector.rb +7 -0
- data/lib/core_ext/integer/inflections.rb +29 -0
- data/lib/core_ext/integer/multiple.rb +10 -0
- data/lib/core_ext/integer/time.rb +29 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/json/decoding.rb +67 -0
- data/lib/core_ext/json/encoding.rb +127 -0
- data/lib/core_ext/json.rb +2 -0
- data/lib/core_ext/kernel/agnostics.rb +11 -0
- data/lib/core_ext/kernel/concern.rb +10 -0
- data/lib/core_ext/kernel/reporting.rb +41 -0
- data/lib/core_ext/kernel/singleton_class.rb +6 -0
- data/lib/core_ext/kernel.rb +4 -0
- data/lib/core_ext/load_error.rb +30 -0
- data/lib/core_ext/logger.rb +57 -0
- data/lib/core_ext/logger_silence.rb +24 -0
- data/lib/core_ext/marshal.rb +19 -0
- data/lib/core_ext/module/aliasing.rb +74 -0
- data/lib/core_ext/module/anonymous.rb +28 -0
- data/lib/core_ext/module/attr_internal.rb +36 -0
- data/lib/core_ext/module/attribute_accessors.rb +212 -0
- data/lib/core_ext/module/concerning.rb +135 -0
- data/lib/core_ext/module/delegation.rb +218 -0
- data/lib/core_ext/module/deprecation.rb +23 -0
- data/lib/core_ext/module/introspection.rb +62 -0
- data/lib/core_ext/module/method_transplanting.rb +3 -0
- data/lib/core_ext/module/qualified_const.rb +52 -0
- data/lib/core_ext/module/reachable.rb +8 -0
- data/lib/core_ext/module/remove_method.rb +35 -0
- data/lib/core_ext/module.rb +11 -0
- data/lib/core_ext/multibyte/chars.rb +231 -0
- data/lib/core_ext/multibyte/unicode.rb +388 -0
- data/lib/core_ext/multibyte.rb +21 -0
- data/lib/core_ext/name_error.rb +31 -0
- data/lib/core_ext/numeric/bytes.rb +64 -0
- data/lib/core_ext/numeric/conversions.rb +132 -0
- data/lib/core_ext/numeric/inquiry.rb +26 -0
- data/lib/core_ext/numeric/time.rb +74 -0
- data/lib/core_ext/numeric.rb +4 -0
- data/lib/core_ext/object/acts_like.rb +10 -0
- data/lib/core_ext/object/blank.rb +140 -0
- data/lib/core_ext/object/conversions.rb +4 -0
- data/lib/core_ext/object/deep_dup.rb +53 -0
- data/lib/core_ext/object/duplicable.rb +98 -0
- data/lib/core_ext/object/inclusion.rb +27 -0
- data/lib/core_ext/object/instance_variables.rb +28 -0
- data/lib/core_ext/object/json.rb +199 -0
- data/lib/core_ext/object/to_param.rb +1 -0
- data/lib/core_ext/object/to_query.rb +84 -0
- data/lib/core_ext/object/try.rb +146 -0
- data/lib/core_ext/object/with_options.rb +69 -0
- data/lib/core_ext/object.rb +14 -0
- data/lib/core_ext/option_merger.rb +25 -0
- data/lib/core_ext/ordered_hash.rb +48 -0
- data/lib/core_ext/ordered_options.rb +81 -0
- data/lib/core_ext/range/conversions.rb +34 -0
- data/lib/core_ext/range/each.rb +21 -0
- data/lib/core_ext/range/include_range.rb +23 -0
- data/lib/core_ext/range/overlaps.rb +8 -0
- data/lib/core_ext/range.rb +4 -0
- data/lib/core_ext/regexp.rb +5 -0
- data/lib/core_ext/rescuable.rb +119 -0
- data/lib/core_ext/securerandom.rb +23 -0
- data/lib/core_ext/security_utils.rb +20 -0
- data/lib/core_ext/string/access.rb +104 -0
- data/lib/core_ext/string/behavior.rb +6 -0
- data/lib/core_ext/string/conversions.rb +56 -0
- data/lib/core_ext/string/exclude.rb +11 -0
- data/lib/core_ext/string/filters.rb +102 -0
- data/lib/core_ext/string/indent.rb +43 -0
- data/lib/core_ext/string/inflections.rb +235 -0
- data/lib/core_ext/string/inquiry.rb +13 -0
- data/lib/core_ext/string/multibyte.rb +53 -0
- data/lib/core_ext/string/output_safety.rb +261 -0
- data/lib/core_ext/string/starts_ends_with.rb +4 -0
- data/lib/core_ext/string/strip.rb +23 -0
- data/lib/core_ext/string/zones.rb +14 -0
- data/lib/core_ext/string.rb +13 -0
- data/lib/core_ext/string_inquirer.rb +26 -0
- data/lib/core_ext/tagged_logging.rb +78 -0
- data/lib/core_ext/test_case.rb +88 -0
- data/lib/core_ext/testing/assertions.rb +99 -0
- data/lib/core_ext/testing/autorun.rb +12 -0
- data/lib/core_ext/testing/composite_filter.rb +54 -0
- data/lib/core_ext/testing/constant_lookup.rb +50 -0
- data/lib/core_ext/testing/declarative.rb +26 -0
- data/lib/core_ext/testing/deprecation.rb +36 -0
- data/lib/core_ext/testing/file_fixtures.rb +34 -0
- data/lib/core_ext/testing/isolation.rb +115 -0
- data/lib/core_ext/testing/method_call_assertions.rb +41 -0
- data/lib/core_ext/testing/setup_and_teardown.rb +50 -0
- data/lib/core_ext/testing/stream.rb +42 -0
- data/lib/core_ext/testing/tagged_logging.rb +25 -0
- data/lib/core_ext/testing/time_helpers.rb +134 -0
- data/lib/core_ext/time/acts_like.rb +8 -0
- data/lib/core_ext/time/calculations.rb +284 -0
- data/lib/core_ext/time/conversions.rb +66 -0
- data/lib/core_ext/time/zones.rb +95 -0
- data/lib/core_ext/time.rb +20 -0
- data/lib/core_ext/time_with_zone.rb +503 -0
- data/lib/core_ext/time_zone.rb +464 -0
- data/lib/core_ext/uri.rb +25 -0
- data/lib/core_ext/version.rb +3 -0
- data/lib/core_ext/xml_mini/jdom.rb +181 -0
- data/lib/core_ext/xml_mini/libxml.rb +79 -0
- data/lib/core_ext/xml_mini/libxmlsax.rb +85 -0
- data/lib/core_ext/xml_mini/nokogiri.rb +83 -0
- data/lib/core_ext/xml_mini/nokogirisax.rb +87 -0
- data/lib/core_ext/xml_mini/rexml.rb +130 -0
- data/lib/core_ext/xml_mini.rb +194 -0
- data/lib/core_ext.rb +3 -0
- metadata +310 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'core_ext/duration'
|
|
2
|
+
require 'core_ext/time/calculations'
|
|
3
|
+
require 'core_ext/time/acts_like'
|
|
4
|
+
require 'core_ext/date/calculations'
|
|
5
|
+
require 'core_ext/date/acts_like'
|
|
6
|
+
|
|
7
|
+
class Numeric
|
|
8
|
+
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
|
|
9
|
+
#
|
|
10
|
+
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
|
|
11
|
+
# as well as adding or subtracting their results from a Time object. For example:
|
|
12
|
+
#
|
|
13
|
+
# # equivalent to Time.current.advance(months: 1)
|
|
14
|
+
# 1.month.from_now
|
|
15
|
+
#
|
|
16
|
+
# # equivalent to Time.current.advance(years: 2)
|
|
17
|
+
# 2.years.from_now
|
|
18
|
+
#
|
|
19
|
+
# # equivalent to Time.current.advance(months: 4, years: 5)
|
|
20
|
+
# (4.months + 5.years).from_now
|
|
21
|
+
def seconds
|
|
22
|
+
CoreExt::Duration.new(self, [[:seconds, self]])
|
|
23
|
+
end
|
|
24
|
+
alias :second :seconds
|
|
25
|
+
|
|
26
|
+
# Returns a Duration instance matching the number of minutes provided.
|
|
27
|
+
#
|
|
28
|
+
# 2.minutes # => 120 seconds
|
|
29
|
+
def minutes
|
|
30
|
+
CoreExt::Duration.new(self * 60, [[:seconds, self * 60]])
|
|
31
|
+
end
|
|
32
|
+
alias :minute :minutes
|
|
33
|
+
|
|
34
|
+
# Returns a Duration instance matching the number of hours provided.
|
|
35
|
+
#
|
|
36
|
+
# 2.hours # => 7_200 seconds
|
|
37
|
+
def hours
|
|
38
|
+
CoreExt::Duration.new(self * 3600, [[:seconds, self * 3600]])
|
|
39
|
+
end
|
|
40
|
+
alias :hour :hours
|
|
41
|
+
|
|
42
|
+
# Returns a Duration instance matching the number of days provided.
|
|
43
|
+
#
|
|
44
|
+
# 2.days # => 2 days
|
|
45
|
+
def days
|
|
46
|
+
CoreExt::Duration.new(self * 24.hours, [[:days, self]])
|
|
47
|
+
end
|
|
48
|
+
alias :day :days
|
|
49
|
+
|
|
50
|
+
# Returns a Duration instance matching the number of weeks provided.
|
|
51
|
+
#
|
|
52
|
+
# 2.weeks # => 14 days
|
|
53
|
+
def weeks
|
|
54
|
+
CoreExt::Duration.new(self * 7.days, [[:days, self * 7]])
|
|
55
|
+
end
|
|
56
|
+
alias :week :weeks
|
|
57
|
+
|
|
58
|
+
# Returns a Duration instance matching the number of fortnights provided.
|
|
59
|
+
#
|
|
60
|
+
# 2.fortnights # => 28 days
|
|
61
|
+
def fortnights
|
|
62
|
+
CoreExt::Duration.new(self * 2.weeks, [[:days, self * 14]])
|
|
63
|
+
end
|
|
64
|
+
alias :fortnight :fortnights
|
|
65
|
+
|
|
66
|
+
# Returns the number of milliseconds equivalent to the seconds provided.
|
|
67
|
+
# Used with the standard time durations, like 1.hour.in_milliseconds --
|
|
68
|
+
# so we can feed them to JavaScript functions like getTime().
|
|
69
|
+
#
|
|
70
|
+
# 2.in_milliseconds # => 2_000
|
|
71
|
+
def in_milliseconds
|
|
72
|
+
self * 1000
|
|
73
|
+
end
|
|
74
|
+
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,140 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
|
3
|
+
# For example, +false+, '', ' ', +nil+, [], and {} are all blank.
|
|
4
|
+
#
|
|
5
|
+
# This simplifies
|
|
6
|
+
#
|
|
7
|
+
# !address || address.empty?
|
|
8
|
+
#
|
|
9
|
+
# to
|
|
10
|
+
#
|
|
11
|
+
# address.blank?
|
|
12
|
+
#
|
|
13
|
+
# @return [true, false]
|
|
14
|
+
def blank?
|
|
15
|
+
respond_to?(:empty?) ? !!empty? : !self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# An object is present if it's not blank.
|
|
19
|
+
#
|
|
20
|
+
# @return [true, false]
|
|
21
|
+
def present?
|
|
22
|
+
!blank?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the receiver if it's present otherwise returns +nil+.
|
|
26
|
+
# <tt>object.presence</tt> is equivalent to
|
|
27
|
+
#
|
|
28
|
+
# object.present? ? object : nil
|
|
29
|
+
#
|
|
30
|
+
# For example, something like
|
|
31
|
+
#
|
|
32
|
+
# state = params[:state] if params[:state].present?
|
|
33
|
+
# country = params[:country] if params[:country].present?
|
|
34
|
+
# region = state || country || 'US'
|
|
35
|
+
#
|
|
36
|
+
# becomes
|
|
37
|
+
#
|
|
38
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
|
39
|
+
#
|
|
40
|
+
# @return [Object]
|
|
41
|
+
def presence
|
|
42
|
+
self if present?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class NilClass
|
|
47
|
+
# +nil+ is blank:
|
|
48
|
+
#
|
|
49
|
+
# nil.blank? # => true
|
|
50
|
+
#
|
|
51
|
+
# @return [true]
|
|
52
|
+
def blank?
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class FalseClass
|
|
58
|
+
# +false+ is blank:
|
|
59
|
+
#
|
|
60
|
+
# false.blank? # => true
|
|
61
|
+
#
|
|
62
|
+
# @return [true]
|
|
63
|
+
def blank?
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class TrueClass
|
|
69
|
+
# +true+ is not blank:
|
|
70
|
+
#
|
|
71
|
+
# true.blank? # => false
|
|
72
|
+
#
|
|
73
|
+
# @return [false]
|
|
74
|
+
def blank?
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Array
|
|
80
|
+
# An array is blank if it's empty:
|
|
81
|
+
#
|
|
82
|
+
# [].blank? # => true
|
|
83
|
+
# [1,2,3].blank? # => false
|
|
84
|
+
#
|
|
85
|
+
# @return [true, false]
|
|
86
|
+
alias_method :blank?, :empty?
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Hash
|
|
90
|
+
# A hash is blank if it's empty:
|
|
91
|
+
#
|
|
92
|
+
# {}.blank? # => true
|
|
93
|
+
# { key: 'value' }.blank? # => false
|
|
94
|
+
#
|
|
95
|
+
# @return [true, false]
|
|
96
|
+
alias_method :blank?, :empty?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class String
|
|
100
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
|
101
|
+
|
|
102
|
+
# A string is blank if it's empty or contains whitespaces only:
|
|
103
|
+
#
|
|
104
|
+
# ''.blank? # => true
|
|
105
|
+
# ' '.blank? # => true
|
|
106
|
+
# "\t\n\r".blank? # => true
|
|
107
|
+
# ' blah '.blank? # => false
|
|
108
|
+
#
|
|
109
|
+
# Unicode whitespace is supported:
|
|
110
|
+
#
|
|
111
|
+
# "\u00a0".blank? # => true
|
|
112
|
+
#
|
|
113
|
+
# @return [true, false]
|
|
114
|
+
def blank?
|
|
115
|
+
BLANK_RE === self
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Numeric #:nodoc:
|
|
120
|
+
# No number is blank:
|
|
121
|
+
#
|
|
122
|
+
# 1.blank? # => false
|
|
123
|
+
# 0.blank? # => false
|
|
124
|
+
#
|
|
125
|
+
# @return [false]
|
|
126
|
+
def blank?
|
|
127
|
+
false
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class Time #:nodoc:
|
|
132
|
+
# No Time is blank:
|
|
133
|
+
#
|
|
134
|
+
# Time.now.blank? # => false
|
|
135
|
+
#
|
|
136
|
+
# @return [false]
|
|
137
|
+
def blank?
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'core_ext/object/duplicable'
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
# Returns a deep copy of object if it's duplicable. If it's
|
|
5
|
+
# not duplicable, returns +self+.
|
|
6
|
+
#
|
|
7
|
+
# object = Object.new
|
|
8
|
+
# dup = object.deep_dup
|
|
9
|
+
# dup.instance_variable_set(:@a, 1)
|
|
10
|
+
#
|
|
11
|
+
# object.instance_variable_defined?(:@a) # => false
|
|
12
|
+
# dup.instance_variable_defined?(:@a) # => true
|
|
13
|
+
def deep_dup
|
|
14
|
+
duplicable? ? dup : self
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Array
|
|
19
|
+
# Returns a deep copy of array.
|
|
20
|
+
#
|
|
21
|
+
# array = [1, [2, 3]]
|
|
22
|
+
# dup = array.deep_dup
|
|
23
|
+
# dup[1][2] = 4
|
|
24
|
+
#
|
|
25
|
+
# array[1][2] # => nil
|
|
26
|
+
# dup[1][2] # => 4
|
|
27
|
+
def deep_dup
|
|
28
|
+
map(&:deep_dup)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Hash
|
|
33
|
+
# Returns a deep copy of hash.
|
|
34
|
+
#
|
|
35
|
+
# hash = { a: { b: 'b' } }
|
|
36
|
+
# dup = hash.deep_dup
|
|
37
|
+
# dup[:a][:c] = 'c'
|
|
38
|
+
#
|
|
39
|
+
# hash[:a][:c] # => nil
|
|
40
|
+
# dup[:a][:c] # => "c"
|
|
41
|
+
def deep_dup
|
|
42
|
+
hash = dup
|
|
43
|
+
each_pair do |key, value|
|
|
44
|
+
if key.frozen? && ::String === key
|
|
45
|
+
hash[key] = value.deep_dup
|
|
46
|
+
else
|
|
47
|
+
hash.delete(key)
|
|
48
|
+
hash[key.deep_dup] = value.deep_dup
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
hash
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
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, number, method 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
|
+
require 'bigdecimal'
|
|
80
|
+
class BigDecimal
|
|
81
|
+
# BigDecimals are duplicable:
|
|
82
|
+
#
|
|
83
|
+
# BigDecimal.new("1.2").duplicable? # => true
|
|
84
|
+
# BigDecimal.new("1.2").dup # => #<BigDecimal:...,'0.12E1',18(18)>
|
|
85
|
+
def duplicable?
|
|
86
|
+
true
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class Method
|
|
91
|
+
# Methods are not duplicable:
|
|
92
|
+
#
|
|
93
|
+
# method(:puts).duplicable? # => false
|
|
94
|
+
# method(:puts).dup # => TypeError: allocator undefined for Method
|
|
95
|
+
def duplicable?
|
|
96
|
+
false
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# Returns true if this object is included in the argument. Argument must be
|
|
3
|
+
# any object which responds to +#include?+. Usage:
|
|
4
|
+
#
|
|
5
|
+
# characters = ["Konata", "Kagami", "Tsukasa"]
|
|
6
|
+
# "Konata".in?(characters) # => true
|
|
7
|
+
#
|
|
8
|
+
# This will throw an +ArgumentError+ if the argument doesn't respond
|
|
9
|
+
# to +#include?+.
|
|
10
|
+
def in?(another_object)
|
|
11
|
+
another_object.include?(self)
|
|
12
|
+
rescue NoMethodError
|
|
13
|
+
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns the receiver if it's included in the argument otherwise returns +nil+.
|
|
17
|
+
# Argument must be any object which responds to +#include?+. Usage:
|
|
18
|
+
#
|
|
19
|
+
# params[:bucket_type].presence_in %w( project calendar )
|
|
20
|
+
#
|
|
21
|
+
# This will throw an +ArgumentError+ if the argument doesn't respond to +#include?+.
|
|
22
|
+
#
|
|
23
|
+
# @return [Object]
|
|
24
|
+
def presence_in(another_object)
|
|
25
|
+
self.in?(another_object) ? self : nil
|
|
26
|
+
end
|
|
27
|
+
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(&:to_s)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Hack to load json gem first so we can overwrite its to_json.
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'bigdecimal'
|
|
5
|
+
require 'core_ext/big_decimal/conversions' # for #to_s
|
|
6
|
+
require 'core_ext/hash/except'
|
|
7
|
+
require 'core_ext/hash/slice'
|
|
8
|
+
require 'core_ext/object/instance_variables'
|
|
9
|
+
require 'core_ext/time/conversions'
|
|
10
|
+
require 'core_ext/date_time/conversions'
|
|
11
|
+
require 'core_ext/date/conversions'
|
|
12
|
+
|
|
13
|
+
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
|
|
14
|
+
# their default behavior. That said, we need to define the basic to_json method in all of them,
|
|
15
|
+
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
|
16
|
+
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
|
17
|
+
# and consequently classes as CoreExt::OrderedHash cannot be serialized to json.
|
|
18
|
+
#
|
|
19
|
+
# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
|
|
20
|
+
# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
|
|
21
|
+
# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
|
|
22
|
+
# calls to the original to_json method.
|
|
23
|
+
#
|
|
24
|
+
# It should be noted that when using ::JSON.{generate,dump} directly, CoreExt's encoder is
|
|
25
|
+
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
|
|
26
|
+
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
|
|
27
|
+
# should give exactly the same results with or without active support.
|
|
28
|
+
|
|
29
|
+
module CoreExt
|
|
30
|
+
module ToJsonWithCoreExtEncoder # :nodoc:
|
|
31
|
+
def to_json(options = nil)
|
|
32
|
+
if options.is_a?(::JSON::State)
|
|
33
|
+
# Called from JSON.{generate,dump}, forward it to JSON gem's to_json
|
|
34
|
+
super(options)
|
|
35
|
+
else
|
|
36
|
+
# to_json is being invoked directly, use CoreExt's encoder
|
|
37
|
+
CoreExt::JSON.encode(self, options)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].reverse_each do |klass|
|
|
44
|
+
klass.prepend(CoreExt::ToJsonWithCoreExtEncoder)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class Object
|
|
48
|
+
def as_json(options = nil) #:nodoc:
|
|
49
|
+
if respond_to?(:to_hash)
|
|
50
|
+
to_hash.as_json(options)
|
|
51
|
+
else
|
|
52
|
+
instance_values.as_json(options)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Struct #:nodoc:
|
|
58
|
+
def as_json(options = nil)
|
|
59
|
+
Hash[members.zip(values)].as_json(options)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class TrueClass
|
|
64
|
+
def as_json(options = nil) #:nodoc:
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class FalseClass
|
|
70
|
+
def as_json(options = nil) #:nodoc:
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class NilClass
|
|
76
|
+
def as_json(options = nil) #:nodoc:
|
|
77
|
+
self
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class String
|
|
82
|
+
def as_json(options = nil) #:nodoc:
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class Symbol
|
|
88
|
+
def as_json(options = nil) #:nodoc:
|
|
89
|
+
to_s
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class Numeric
|
|
94
|
+
def as_json(options = nil) #:nodoc:
|
|
95
|
+
self
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class Float
|
|
100
|
+
# Encoding Infinity or NaN to JSON should return "null". The default returns
|
|
101
|
+
# "Infinity" or "NaN" which are not valid JSON.
|
|
102
|
+
def as_json(options = nil) #:nodoc:
|
|
103
|
+
finite? ? self : nil
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class BigDecimal
|
|
108
|
+
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
|
|
109
|
+
# however, parse non-integer JSON numbers directly as floats. Clients using
|
|
110
|
+
# those libraries would get in general a wrong number and no way to recover
|
|
111
|
+
# other than manually inspecting the string with the JSON code itself.
|
|
112
|
+
#
|
|
113
|
+
# That's why a JSON string is returned. The JSON literal is not numeric, but
|
|
114
|
+
# if the other end knows by contract that the data is supposed to be a
|
|
115
|
+
# BigDecimal, it still has the chance to post-process the string and get the
|
|
116
|
+
# real value.
|
|
117
|
+
def as_json(options = nil) #:nodoc:
|
|
118
|
+
finite? ? to_s : nil
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class Regexp
|
|
123
|
+
def as_json(options = nil) #:nodoc:
|
|
124
|
+
to_s
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
module Enumerable
|
|
129
|
+
def as_json(options = nil) #:nodoc:
|
|
130
|
+
to_a.as_json(options)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class Range
|
|
135
|
+
def as_json(options = nil) #:nodoc:
|
|
136
|
+
to_s
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class Array
|
|
141
|
+
def as_json(options = nil) #:nodoc:
|
|
142
|
+
map { |v| options ? v.as_json(options.dup) : v.as_json }
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class Hash
|
|
147
|
+
def as_json(options = nil) #:nodoc:
|
|
148
|
+
# create a subset of the hash by applying :only or :except
|
|
149
|
+
subset = if options
|
|
150
|
+
if attrs = options[:only]
|
|
151
|
+
slice(*Array(attrs))
|
|
152
|
+
elsif attrs = options[:except]
|
|
153
|
+
except(*Array(attrs))
|
|
154
|
+
else
|
|
155
|
+
self
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
self
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class Time
|
|
166
|
+
def as_json(options = nil) #:nodoc:
|
|
167
|
+
if CoreExt::JSON::Encoding.use_standard_json_time_format
|
|
168
|
+
xmlschema(CoreExt::JSON::Encoding.time_precision)
|
|
169
|
+
else
|
|
170
|
+
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class Date
|
|
176
|
+
def as_json(options = nil) #:nodoc:
|
|
177
|
+
if CoreExt::JSON::Encoding.use_standard_json_time_format
|
|
178
|
+
strftime("%Y-%m-%d")
|
|
179
|
+
else
|
|
180
|
+
strftime("%Y/%m/%d")
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
class DateTime
|
|
186
|
+
def as_json(options = nil) #:nodoc:
|
|
187
|
+
if CoreExt::JSON::Encoding.use_standard_json_time_format
|
|
188
|
+
xmlschema(CoreExt::JSON::Encoding.time_precision)
|
|
189
|
+
else
|
|
190
|
+
strftime('%Y/%m/%d %H:%M:%S %z')
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class Process::Status #:nodoc:
|
|
196
|
+
def as_json(options = nil)
|
|
197
|
+
{ :exitstatus => exitstatus, :pid => pid }
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'core_ext/object/to_query'
|