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,112 @@
|
|
|
1
|
+
require 'core_ext/string/multibyte'
|
|
2
|
+
require 'i18n'
|
|
3
|
+
|
|
4
|
+
module CoreExt
|
|
5
|
+
module Inflector
|
|
6
|
+
|
|
7
|
+
# Replaces non-ASCII characters with an ASCII approximation, or if none
|
|
8
|
+
# exists, a replacement character which defaults to "?".
|
|
9
|
+
#
|
|
10
|
+
# transliterate('Ærøskøbing')
|
|
11
|
+
# # => "AEroskobing"
|
|
12
|
+
#
|
|
13
|
+
# Default approximations are provided for Western/Latin characters,
|
|
14
|
+
# e.g, "ø", "ñ", "é", "ß", etc.
|
|
15
|
+
#
|
|
16
|
+
# This method is I18n aware, so you can set up custom approximations for a
|
|
17
|
+
# locale. This can be useful, for example, to transliterate German's "ü"
|
|
18
|
+
# and "ö" to "ue" and "oe", or to add support for transliterating Russian
|
|
19
|
+
# to ASCII.
|
|
20
|
+
#
|
|
21
|
+
# In order to make your custom transliterations available, you must set
|
|
22
|
+
# them as the <tt>i18n.transliterate.rule</tt> i18n key:
|
|
23
|
+
#
|
|
24
|
+
# # Store the transliterations in locales/de.yml
|
|
25
|
+
# i18n:
|
|
26
|
+
# transliterate:
|
|
27
|
+
# rule:
|
|
28
|
+
# ü: "ue"
|
|
29
|
+
# ö: "oe"
|
|
30
|
+
#
|
|
31
|
+
# # Or set them using Ruby
|
|
32
|
+
# I18n.backend.store_translations(:de, i18n: {
|
|
33
|
+
# transliterate: {
|
|
34
|
+
# rule: {
|
|
35
|
+
# 'ü' => 'ue',
|
|
36
|
+
# 'ö' => 'oe'
|
|
37
|
+
# }
|
|
38
|
+
# }
|
|
39
|
+
# })
|
|
40
|
+
#
|
|
41
|
+
# The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
|
|
42
|
+
# maps characters to ASCII approximations as shown above, or, for more
|
|
43
|
+
# complex requirements, a Proc:
|
|
44
|
+
#
|
|
45
|
+
# I18n.backend.store_translations(:de, i18n: {
|
|
46
|
+
# transliterate: {
|
|
47
|
+
# rule: ->(string) { MyTransliterator.transliterate(string) }
|
|
48
|
+
# }
|
|
49
|
+
# })
|
|
50
|
+
#
|
|
51
|
+
# Now you can have different transliterations for each locale:
|
|
52
|
+
#
|
|
53
|
+
# I18n.locale = :en
|
|
54
|
+
# transliterate('Jürgen')
|
|
55
|
+
# # => "Jurgen"
|
|
56
|
+
#
|
|
57
|
+
# I18n.locale = :de
|
|
58
|
+
# transliterate('Jürgen')
|
|
59
|
+
# # => "Juergen"
|
|
60
|
+
def transliterate(string, replacement = "?".freeze)
|
|
61
|
+
I18n.transliterate(CoreExt::Multibyte::Unicode.normalize(
|
|
62
|
+
CoreExt::Multibyte::Unicode.tidy_bytes(string), :c),
|
|
63
|
+
:replacement => replacement)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Replaces special characters in a string so that it may be used as part of
|
|
67
|
+
# a 'pretty' URL.
|
|
68
|
+
#
|
|
69
|
+
# parameterize("Donald E. Knuth") # => "donald-e-knuth"
|
|
70
|
+
# parameterize("^trés|Jolie-- ") # => "tres-jolie"
|
|
71
|
+
#
|
|
72
|
+
# To use a custom separator, override the `separator` argument.
|
|
73
|
+
#
|
|
74
|
+
# parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
|
|
75
|
+
# parameterize("^trés|Jolie-- ", separator: '_') # => "tres_jolie"
|
|
76
|
+
#
|
|
77
|
+
# To preserve the case of the characters in a string, use the `preserve_case` argument.
|
|
78
|
+
#
|
|
79
|
+
# parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
|
|
80
|
+
# parameterize("^trés|Jolie-- ", preserve_case: true) # => "tres-Jolie"
|
|
81
|
+
#
|
|
82
|
+
def parameterize(string, sep = :unused, separator: '-', preserve_case: false)
|
|
83
|
+
unless sep == :unused
|
|
84
|
+
CoreExt::Deprecation.warn("Passing the separator argument as a positional parameter is deprecated and will soon be removed. Use `separator: '#{sep}'` instead.")
|
|
85
|
+
separator = sep
|
|
86
|
+
end
|
|
87
|
+
# Replace accented chars with their ASCII equivalents.
|
|
88
|
+
parameterized_string = transliterate(string)
|
|
89
|
+
|
|
90
|
+
# Turn unwanted chars into the separator.
|
|
91
|
+
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
|
|
92
|
+
|
|
93
|
+
unless separator.nil? || separator.empty?
|
|
94
|
+
if separator == "-".freeze
|
|
95
|
+
re_duplicate_separator = /-{2,}/
|
|
96
|
+
re_leading_trailing_separator = /^-|-$/i
|
|
97
|
+
else
|
|
98
|
+
re_sep = Regexp.escape(separator)
|
|
99
|
+
re_duplicate_separator = /#{re_sep}{2,}/
|
|
100
|
+
re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
|
|
101
|
+
end
|
|
102
|
+
# No more than one of the separator in a row.
|
|
103
|
+
parameterized_string.gsub!(re_duplicate_separator, separator)
|
|
104
|
+
# Remove leading/trailing separator.
|
|
105
|
+
parameterized_string.gsub!(re_leading_trailing_separator, ''.freeze)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
parameterized_string.downcase! unless preserve_case
|
|
109
|
+
parameterized_string
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# in case active_support/inflector is required without the rest of active_support
|
|
2
|
+
require 'core_ext/inflector/inflections'
|
|
3
|
+
require 'core_ext/inflector/transliterate'
|
|
4
|
+
require 'core_ext/inflector/methods'
|
|
5
|
+
|
|
6
|
+
require 'core_ext/inflections'
|
|
7
|
+
require 'core_ext/string/inflections'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'core_ext/inflector'
|
|
2
|
+
|
|
3
|
+
class Integer
|
|
4
|
+
# Ordinalize turns a number into an ordinal string used to denote the
|
|
5
|
+
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
|
6
|
+
#
|
|
7
|
+
# 1.ordinalize # => "1st"
|
|
8
|
+
# 2.ordinalize # => "2nd"
|
|
9
|
+
# 1002.ordinalize # => "1002nd"
|
|
10
|
+
# 1003.ordinalize # => "1003rd"
|
|
11
|
+
# -11.ordinalize # => "-11th"
|
|
12
|
+
# -1001.ordinalize # => "-1001st"
|
|
13
|
+
def ordinalize
|
|
14
|
+
CoreExt::Inflector.ordinalize(self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Ordinal returns the suffix used to denote the position
|
|
18
|
+
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
|
19
|
+
#
|
|
20
|
+
# 1.ordinal # => "st"
|
|
21
|
+
# 2.ordinal # => "nd"
|
|
22
|
+
# 1002.ordinal # => "nd"
|
|
23
|
+
# 1003.ordinal # => "rd"
|
|
24
|
+
# -11.ordinal # => "th"
|
|
25
|
+
# -1001.ordinal # => "st"
|
|
26
|
+
def ordinal
|
|
27
|
+
CoreExt::Inflector.ordinal(self)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class Integer
|
|
2
|
+
# Check whether the integer is evenly divisible by the argument.
|
|
3
|
+
#
|
|
4
|
+
# 0.multiple_of?(0) # => true
|
|
5
|
+
# 6.multiple_of?(5) # => false
|
|
6
|
+
# 10.multiple_of?(2) # => true
|
|
7
|
+
def multiple_of?(number)
|
|
8
|
+
number != 0 ? self % number == 0 : zero?
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'core_ext/duration'
|
|
2
|
+
require 'core_ext/numeric/time'
|
|
3
|
+
|
|
4
|
+
class Integer
|
|
5
|
+
# Enables the use of time calculations and declarations, like <tt>45.minutes +
|
|
6
|
+
# 2.hours + 4.years</tt>.
|
|
7
|
+
#
|
|
8
|
+
# These methods use Time#advance for precise date calculations when using
|
|
9
|
+
# <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
|
|
10
|
+
# results from a Time object.
|
|
11
|
+
#
|
|
12
|
+
# # equivalent to Time.now.advance(months: 1)
|
|
13
|
+
# 1.month.from_now
|
|
14
|
+
#
|
|
15
|
+
# # equivalent to Time.now.advance(years: 2)
|
|
16
|
+
# 2.years.from_now
|
|
17
|
+
#
|
|
18
|
+
# # equivalent to Time.now.advance(months: 4, years: 5)
|
|
19
|
+
# (4.months + 5.years).from_now
|
|
20
|
+
def months
|
|
21
|
+
CoreExt::Duration.new(self * 30.days, [[:months, self]])
|
|
22
|
+
end
|
|
23
|
+
alias :month :months
|
|
24
|
+
|
|
25
|
+
def years
|
|
26
|
+
CoreExt::Duration.new(self * 365.25.days.to_i, [[:years, self]])
|
|
27
|
+
end
|
|
28
|
+
alias :year :years
|
|
29
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'core_ext/module/attribute_accessors'
|
|
2
|
+
require 'core_ext/module/delegation'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module CoreExt
|
|
6
|
+
# Look for and parse json strings that look like ISO 8601 times.
|
|
7
|
+
mattr_accessor :parse_json_times
|
|
8
|
+
|
|
9
|
+
module JSON
|
|
10
|
+
# matches YAML-formatted dates
|
|
11
|
+
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
# Parses a JSON string (JavaScript Object Notation) into a hash.
|
|
15
|
+
# See http://www.json.org for more info.
|
|
16
|
+
#
|
|
17
|
+
# CoreExt::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
|
|
18
|
+
# => {"team" => "rails", "players" => "36"}
|
|
19
|
+
def decode(json)
|
|
20
|
+
data = ::JSON.parse(json, quirks_mode: true)
|
|
21
|
+
|
|
22
|
+
if CoreExt.parse_json_times
|
|
23
|
+
convert_dates_from(data)
|
|
24
|
+
else
|
|
25
|
+
data
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the class of the error that will be raised when there is an
|
|
30
|
+
# error in decoding JSON. Using this method means you won't directly
|
|
31
|
+
# depend on the CoreExt's JSON implementation, in case it changes
|
|
32
|
+
# in the future.
|
|
33
|
+
#
|
|
34
|
+
# begin
|
|
35
|
+
# obj = CoreExt::JSON.decode(some_string)
|
|
36
|
+
# rescue CoreExt::JSON.parse_error
|
|
37
|
+
# Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
|
|
38
|
+
# end
|
|
39
|
+
def parse_error
|
|
40
|
+
::JSON::ParserError
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def convert_dates_from(data)
|
|
46
|
+
case data
|
|
47
|
+
when nil
|
|
48
|
+
nil
|
|
49
|
+
when DATE_REGEX
|
|
50
|
+
begin
|
|
51
|
+
DateTime.parse(data)
|
|
52
|
+
rescue ArgumentError
|
|
53
|
+
data
|
|
54
|
+
end
|
|
55
|
+
when Array
|
|
56
|
+
data.map! { |d| convert_dates_from(d) }
|
|
57
|
+
when Hash
|
|
58
|
+
data.each do |key, value|
|
|
59
|
+
data[key] = convert_dates_from(value)
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
data
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'core_ext/object/json'
|
|
2
|
+
require 'core_ext/module/delegation'
|
|
3
|
+
|
|
4
|
+
module CoreExt
|
|
5
|
+
class << self
|
|
6
|
+
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
|
|
7
|
+
:time_precision, :time_precision=,
|
|
8
|
+
:escape_html_entities_in_json, :escape_html_entities_in_json=,
|
|
9
|
+
:json_encoder, :json_encoder=,
|
|
10
|
+
:to => :'CoreExt::JSON::Encoding'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module JSON
|
|
14
|
+
# Dumps objects in JSON (JavaScript Object Notation).
|
|
15
|
+
# See http://www.json.org for more info.
|
|
16
|
+
#
|
|
17
|
+
# CoreExt::JSON.encode({ team: 'rails', players: '36' })
|
|
18
|
+
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
|
|
19
|
+
def self.encode(value, options = nil)
|
|
20
|
+
Encoding.json_encoder.new(options).encode(value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Encoding #:nodoc:
|
|
24
|
+
class JSONGemEncoder #:nodoc:
|
|
25
|
+
attr_reader :options
|
|
26
|
+
|
|
27
|
+
def initialize(options = nil)
|
|
28
|
+
@options = options || {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Encode the given object into a JSON string
|
|
32
|
+
def encode(value)
|
|
33
|
+
stringify jsonify value.as_json(options.dup)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
# Rails does more escaping than the JSON gem natively does (we
|
|
38
|
+
# escape \u2028 and \u2029 and optionally >, <, & to work around
|
|
39
|
+
# certain browser problems).
|
|
40
|
+
ESCAPED_CHARS = {
|
|
41
|
+
"\u2028" => '\u2028',
|
|
42
|
+
"\u2029" => '\u2029',
|
|
43
|
+
'>' => '\u003e',
|
|
44
|
+
'<' => '\u003c',
|
|
45
|
+
'&' => '\u0026',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
|
|
49
|
+
ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
|
|
50
|
+
|
|
51
|
+
# This class wraps all the strings we see and does the extra escaping
|
|
52
|
+
class EscapedString < String #:nodoc:
|
|
53
|
+
def to_json(*)
|
|
54
|
+
if Encoding.escape_html_entities_in_json
|
|
55
|
+
super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
|
|
56
|
+
else
|
|
57
|
+
super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_s
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Mark these as private so we don't leak encoding-specific constructs
|
|
67
|
+
private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
|
|
68
|
+
:ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
|
|
69
|
+
|
|
70
|
+
# Convert an object into a "JSON-ready" representation composed of
|
|
71
|
+
# primitives like Hash, Array, String, Numeric, and true/false/nil.
|
|
72
|
+
# Recursively calls #as_json to the object to recursively build a
|
|
73
|
+
# fully JSON-ready object.
|
|
74
|
+
#
|
|
75
|
+
# This allows developers to implement #as_json without having to
|
|
76
|
+
# worry about what base types of objects they are allowed to return
|
|
77
|
+
# or having to remember to call #as_json recursively.
|
|
78
|
+
#
|
|
79
|
+
# Note: the +options+ hash passed to +object.to_json+ is only passed
|
|
80
|
+
# to +object.as_json+, not any of this method's recursive +#as_json+
|
|
81
|
+
# calls.
|
|
82
|
+
def jsonify(value)
|
|
83
|
+
case value
|
|
84
|
+
when String
|
|
85
|
+
EscapedString.new(value)
|
|
86
|
+
when Numeric, NilClass, TrueClass, FalseClass
|
|
87
|
+
value
|
|
88
|
+
when Hash
|
|
89
|
+
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
|
|
90
|
+
when Array
|
|
91
|
+
value.map { |v| jsonify(v) }
|
|
92
|
+
else
|
|
93
|
+
jsonify value.as_json
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Encode a "jsonified" Ruby data structure using the JSON gem
|
|
98
|
+
def stringify(jsonified)
|
|
99
|
+
::JSON.generate(jsonified, quirks_mode: true, max_nesting: false)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class << self
|
|
104
|
+
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
|
|
105
|
+
# to the Active Support legacy format.
|
|
106
|
+
attr_accessor :use_standard_json_time_format
|
|
107
|
+
|
|
108
|
+
# If true, encode >, <, & as escaped unicode sequences (e.g. > as \u003e)
|
|
109
|
+
# as a safety measure.
|
|
110
|
+
attr_accessor :escape_html_entities_in_json
|
|
111
|
+
|
|
112
|
+
# Sets the precision of encoded time values.
|
|
113
|
+
# Defaults to 3 (equivalent to millisecond precision)
|
|
114
|
+
attr_accessor :time_precision
|
|
115
|
+
|
|
116
|
+
# Sets the encoder used by Rails to encode Ruby objects into JSON strings
|
|
117
|
+
# in +Object#to_json+ and +CoreExt::JSON.encode+.
|
|
118
|
+
attr_accessor :json_encoder
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
self.use_standard_json_time_format = true
|
|
122
|
+
self.escape_html_entities_in_json = true
|
|
123
|
+
self.json_encoder = JSONGemEncoder
|
|
124
|
+
self.time_precision = 3
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
# Makes backticks behave (somewhat more) similarly on all platforms.
|
|
3
|
+
# On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
|
|
4
|
+
# spawned shell prints a message to stderr and sets $?. We emulate
|
|
5
|
+
# Unix on the former but not the latter.
|
|
6
|
+
def `(command) #:nodoc:
|
|
7
|
+
super
|
|
8
|
+
rescue Errno::ENOENT => e
|
|
9
|
+
STDERR.puts "#$0: #{e}"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
# Sets $VERBOSE to nil for the duration of the block and back to its original
|
|
3
|
+
# value afterwards.
|
|
4
|
+
#
|
|
5
|
+
# silence_warnings do
|
|
6
|
+
# value = noisy_call # no warning voiced
|
|
7
|
+
# end
|
|
8
|
+
#
|
|
9
|
+
# noisy_call # warning voiced
|
|
10
|
+
def silence_warnings
|
|
11
|
+
with_warnings(nil) { yield }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Sets $VERBOSE to +true+ for the duration of the block and back to its
|
|
15
|
+
# original value afterwards.
|
|
16
|
+
def enable_warnings
|
|
17
|
+
with_warnings(true) { yield }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Sets $VERBOSE for the duration of the block and back to its original
|
|
21
|
+
# value afterwards.
|
|
22
|
+
def with_warnings(flag)
|
|
23
|
+
old_verbose, $VERBOSE = $VERBOSE, flag
|
|
24
|
+
yield
|
|
25
|
+
ensure
|
|
26
|
+
$VERBOSE = old_verbose
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Blocks and ignores any exception passed as argument if raised within the block.
|
|
30
|
+
#
|
|
31
|
+
# suppress(ZeroDivisionError) do
|
|
32
|
+
# 1/0
|
|
33
|
+
# puts 'This code is NOT reached'
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# puts 'This code gets executed and nothing related to ZeroDivisionError was seen'
|
|
37
|
+
def suppress(*exception_classes)
|
|
38
|
+
yield
|
|
39
|
+
rescue *exception_classes
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'core_ext/deprecation/proxy_wrappers'
|
|
2
|
+
|
|
3
|
+
class LoadError
|
|
4
|
+
REGEXPS = [
|
|
5
|
+
/^no such file to load -- (.+)$/i,
|
|
6
|
+
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
|
|
7
|
+
/^Missing API definition file in (.+)$/i,
|
|
8
|
+
/^cannot load such file -- (.+)$/i,
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
unless method_defined?(:path)
|
|
12
|
+
# Returns the path which was unable to be loaded.
|
|
13
|
+
def path
|
|
14
|
+
@path ||= begin
|
|
15
|
+
REGEXPS.find do |regex|
|
|
16
|
+
message =~ regex
|
|
17
|
+
end
|
|
18
|
+
$1
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns true if the given path name (except perhaps for the ".rb"
|
|
24
|
+
# extension) is the missing file which caused the exception to be raised.
|
|
25
|
+
def is_missing?(location)
|
|
26
|
+
location.sub(/\.rb$/, ''.freeze) == path.sub(/\.rb$/, ''.freeze)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
MissingSourceFile = CoreExt::Deprecation::DeprecatedConstantProxy.new('MissingSourceFile', 'LoadError')
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'core_ext/module/attribute_accessors'
|
|
2
|
+
require 'core_ext/logger_silence'
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module CoreExt
|
|
6
|
+
class Logger < ::Logger
|
|
7
|
+
include LoggerSilence
|
|
8
|
+
|
|
9
|
+
# Broadcasts logs to multiple loggers.
|
|
10
|
+
def self.broadcast(logger) # :nodoc:
|
|
11
|
+
Module.new do
|
|
12
|
+
define_method(:add) do |*args, &block|
|
|
13
|
+
logger.add(*args, &block)
|
|
14
|
+
super(*args, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
define_method(:<<) do |x|
|
|
18
|
+
logger << x
|
|
19
|
+
super(x)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
define_method(:close) do
|
|
23
|
+
logger.close
|
|
24
|
+
super()
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
define_method(:progname=) do |name|
|
|
28
|
+
logger.progname = name
|
|
29
|
+
super(name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
define_method(:formatter=) do |formatter|
|
|
33
|
+
logger.formatter = formatter
|
|
34
|
+
super(formatter)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
define_method(:level=) do |level|
|
|
38
|
+
logger.level = level
|
|
39
|
+
super(level)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def initialize(*args)
|
|
45
|
+
super
|
|
46
|
+
@formatter = SimpleFormatter.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Simple formatter which only displays the message.
|
|
50
|
+
class SimpleFormatter < ::Logger::Formatter
|
|
51
|
+
# This method is invoked when a log event occurs
|
|
52
|
+
def call(severity, timestamp, progname, msg)
|
|
53
|
+
"#{String === msg ? msg : msg.inspect}\n"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'core_ext/concern'
|
|
2
|
+
|
|
3
|
+
module CoreExt::LoggerSilence
|
|
4
|
+
extend CoreExt::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
cattr_accessor :silencer
|
|
8
|
+
self.silencer = true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Silences the logger for the duration of the block.
|
|
12
|
+
def silence(temporary_level = Logger::ERROR)
|
|
13
|
+
if silencer
|
|
14
|
+
begin
|
|
15
|
+
old_logger_level, self.level = level, temporary_level
|
|
16
|
+
yield self
|
|
17
|
+
ensure
|
|
18
|
+
self.level = old_logger_level
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
yield self
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module CoreExt
|
|
2
|
+
module MarshalWithAutoloading # :nodoc:
|
|
3
|
+
def load(source)
|
|
4
|
+
super(source)
|
|
5
|
+
rescue ArgumentError, NameError => exc
|
|
6
|
+
if exc.message.match(%r|undefined class/module (.+)|)
|
|
7
|
+
# try loading the class/module
|
|
8
|
+
$1.constantize
|
|
9
|
+
# if it is an IO we need to go back to read the object
|
|
10
|
+
source.rewind if source.respond_to?(:rewind)
|
|
11
|
+
retry
|
|
12
|
+
else
|
|
13
|
+
raise exc
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Marshal.singleton_class.prepend(CoreExt::MarshalWithAutoloading)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
class Module
|
|
2
|
+
# NOTE: This method is deprecated. Please use <tt>Module#prepend</tt> that
|
|
3
|
+
# comes with Ruby 2.0 or newer instead.
|
|
4
|
+
#
|
|
5
|
+
# Encapsulates the common pattern of:
|
|
6
|
+
#
|
|
7
|
+
# alias_method :foo_without_feature, :foo
|
|
8
|
+
# alias_method :foo, :foo_with_feature
|
|
9
|
+
#
|
|
10
|
+
# With this, you simply do:
|
|
11
|
+
#
|
|
12
|
+
# alias_method_chain :foo, :feature
|
|
13
|
+
#
|
|
14
|
+
# And both aliases are set up for you.
|
|
15
|
+
#
|
|
16
|
+
# Query and bang methods (foo?, foo!) keep the same punctuation:
|
|
17
|
+
#
|
|
18
|
+
# alias_method_chain :foo?, :feature
|
|
19
|
+
#
|
|
20
|
+
# is equivalent to
|
|
21
|
+
#
|
|
22
|
+
# alias_method :foo_without_feature?, :foo?
|
|
23
|
+
# alias_method :foo?, :foo_with_feature?
|
|
24
|
+
#
|
|
25
|
+
# so you can safely chain foo, foo?, foo! and/or foo= with the same feature.
|
|
26
|
+
def alias_method_chain(target, feature)
|
|
27
|
+
CoreExt::Deprecation.warn("alias_method_chain is deprecated. Please, use Module#prepend instead. From module, you can access the original method using super.")
|
|
28
|
+
|
|
29
|
+
# Strip out punctuation on predicates, bang or writer methods since
|
|
30
|
+
# e.g. target?_without_feature is not a valid method name.
|
|
31
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
|
32
|
+
yield(aliased_target, punctuation) if block_given?
|
|
33
|
+
|
|
34
|
+
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
|
|
35
|
+
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
|
|
36
|
+
|
|
37
|
+
alias_method without_method, target
|
|
38
|
+
alias_method target, with_method
|
|
39
|
+
|
|
40
|
+
case
|
|
41
|
+
when public_method_defined?(without_method)
|
|
42
|
+
public target
|
|
43
|
+
when protected_method_defined?(without_method)
|
|
44
|
+
protected target
|
|
45
|
+
when private_method_defined?(without_method)
|
|
46
|
+
private target
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Allows you to make aliases for attributes, which includes
|
|
51
|
+
# getter, setter, and a predicate.
|
|
52
|
+
#
|
|
53
|
+
# class Content < ActiveRecord::Base
|
|
54
|
+
# # has a title attribute
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# class Email < Content
|
|
58
|
+
# alias_attribute :subject, :title
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# e = Email.find(1)
|
|
62
|
+
# e.title # => "Superstars"
|
|
63
|
+
# e.subject # => "Superstars"
|
|
64
|
+
# e.subject? # => true
|
|
65
|
+
# e.subject = "Megastars"
|
|
66
|
+
# e.title # => "Megastars"
|
|
67
|
+
def alias_attribute(new_name, old_name)
|
|
68
|
+
module_eval <<-STR, __FILE__, __LINE__ + 1
|
|
69
|
+
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
|
|
70
|
+
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
|
|
71
|
+
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
|
|
72
|
+
STR
|
|
73
|
+
end
|
|
74
|
+
end
|