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