activesupport 3.1.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +798 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +13 -7
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +38 -34
  7. data/lib/active_support/benchmarkable.rb +17 -28
  8. data/lib/active_support/cache/file_store.rb +85 -70
  9. data/lib/active_support/cache/mem_cache_store.rb +75 -66
  10. data/lib/active_support/cache/memory_store.rb +31 -23
  11. data/lib/active_support/cache/null_store.rb +41 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +73 -70
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  14. data/lib/active_support/cache.rb +360 -294
  15. data/lib/active_support/callbacks.rb +563 -393
  16. data/lib/active_support/concern.rb +42 -34
  17. data/lib/active_support/concurrency/latch.rb +19 -0
  18. data/lib/active_support/concurrency/share_lock.rb +186 -0
  19. data/lib/active_support/configurable.rb +70 -12
  20. data/lib/active_support/core_ext/array/access.rb +53 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +109 -62
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +39 -32
  24. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  25. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  26. data/lib/active_support/core_ext/array/wrap.rb +16 -18
  27. data/lib/active_support/core_ext/array.rb +2 -2
  28. data/lib/active_support/core_ext/benchmark.rb +7 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
  30. data/lib/active_support/core_ext/class/attribute.rb +47 -34
  31. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
  32. data/lib/active_support/core_ext/class/subclasses.rb +12 -7
  33. data/lib/active_support/core_ext/class.rb +0 -3
  34. data/lib/active_support/core_ext/date/blank.rb +12 -0
  35. data/lib/active_support/core_ext/date/calculations.rb +57 -167
  36. data/lib/active_support/core_ext/date/conversions.rb +31 -42
  37. data/lib/active_support/core_ext/date/zones.rb +2 -10
  38. data/lib/active_support/core_ext/date.rb +5 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  42. data/lib/active_support/core_ext/date_time/acts_like.rb +1 -0
  43. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  44. data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
  45. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  46. data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
  47. data/lib/active_support/core_ext/date_time.rb +5 -0
  48. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  49. data/lib/active_support/core_ext/enumerable.rb +81 -74
  50. data/lib/active_support/core_ext/file/atomic.rb +53 -26
  51. data/lib/active_support/core_ext/file.rb +0 -1
  52. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  53. data/lib/active_support/core_ext/hash/conversions.rb +175 -70
  54. data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
  55. data/lib/active_support/core_ext/hash/except.rb +11 -12
  56. data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
  57. data/lib/active_support/core_ext/hash/keys.rb +147 -24
  58. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  59. data/lib/active_support/core_ext/hash/slice.rb +22 -14
  60. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  61. data/lib/active_support/core_ext/hash.rb +2 -2
  62. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  63. data/lib/active_support/core_ext/integer/multiple.rb +4 -0
  64. data/lib/active_support/core_ext/integer/time.rb +12 -22
  65. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  66. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  67. data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
  68. data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
  69. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  70. data/lib/active_support/core_ext/kernel.rb +2 -3
  71. data/lib/active_support/core_ext/load_error.rb +14 -7
  72. data/lib/active_support/core_ext/marshal.rb +22 -0
  73. data/lib/active_support/core_ext/module/aliasing.rb +16 -12
  74. data/lib/active_support/core_ext/module/anonymous.rb +12 -8
  75. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  76. data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
  77. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  78. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  79. data/lib/active_support/core_ext/module/delegation.rb +141 -68
  80. data/lib/active_support/core_ext/module/deprecation.rb +17 -3
  81. data/lib/active_support/core_ext/module/introspection.rb +9 -31
  82. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  83. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  84. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  85. data/lib/active_support/core_ext/module/remove_method.rb +24 -5
  86. data/lib/active_support/core_ext/module.rb +3 -3
  87. data/lib/active_support/core_ext/name_error.rb +15 -2
  88. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  89. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  90. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  91. data/lib/active_support/core_ext/numeric/time.rb +31 -36
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  94. data/lib/active_support/core_ext/object/blank.rb +52 -18
  95. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  96. data/lib/active_support/core_ext/object/duplicable.rb +12 -20
  97. data/lib/active_support/core_ext/object/inclusion.rb +13 -1
  98. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  99. data/lib/active_support/core_ext/object/json.rb +205 -0
  100. data/lib/active_support/core_ext/object/to_param.rb +1 -55
  101. data/lib/active_support/core_ext/object/to_query.rb +66 -9
  102. data/lib/active_support/core_ext/object/try.rb +124 -33
  103. data/lib/active_support/core_ext/object/with_options.rb +37 -11
  104. data/lib/active_support/core_ext/object.rb +2 -1
  105. data/lib/active_support/core_ext/range/conversions.rb +17 -7
  106. data/lib/active_support/core_ext/range/each.rb +21 -0
  107. data/lib/active_support/core_ext/range/include_range.rb +20 -18
  108. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  109. data/lib/active_support/core_ext/range.rb +1 -2
  110. data/lib/active_support/core_ext/securerandom.rb +23 -0
  111. data/lib/active_support/core_ext/string/access.rb +95 -90
  112. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  113. data/lib/active_support/core_ext/string/conversions.rb +41 -38
  114. data/lib/active_support/core_ext/string/exclude.rb +6 -1
  115. data/lib/active_support/core_ext/string/filters.rb +70 -17
  116. data/lib/active_support/core_ext/string/indent.rb +43 -0
  117. data/lib/active_support/core_ext/string/inflections.rb +139 -59
  118. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  119. data/lib/active_support/core_ext/string/multibyte.rb +46 -65
  120. data/lib/active_support/core_ext/string/output_safety.rb +153 -56
  121. data/lib/active_support/core_ext/string/strip.rb +3 -6
  122. data/lib/active_support/core_ext/string/zones.rb +14 -0
  123. data/lib/active_support/core_ext/string.rb +2 -3
  124. data/lib/active_support/core_ext/struct.rb +3 -0
  125. data/lib/active_support/core_ext/time/calculations.rb +173 -173
  126. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  127. data/lib/active_support/core_ext/time/conversions.rb +33 -29
  128. data/lib/active_support/core_ext/time/marshal.rb +2 -56
  129. data/lib/active_support/core_ext/time/zones.rb +57 -32
  130. data/lib/active_support/core_ext/time.rb +5 -0
  131. data/lib/active_support/core_ext/uri.rb +13 -19
  132. data/lib/active_support/core_ext.rb +3 -2
  133. data/lib/active_support/dependencies/autoload.rb +47 -20
  134. data/lib/active_support/dependencies/interlock.rb +51 -0
  135. data/lib/active_support/dependencies.rb +315 -265
  136. data/lib/active_support/deprecation/behaviors.rb +71 -30
  137. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  138. data/lib/active_support/deprecation/method_wrappers.rb +59 -18
  139. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
  140. data/lib/active_support/deprecation/reporting.rb +61 -14
  141. data/lib/active_support/deprecation.rb +38 -13
  142. data/lib/active_support/descendants_tracker.rb +34 -19
  143. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  144. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  145. data/lib/active_support/duration.rb +85 -14
  146. data/lib/active_support/evented_file_update_checker.rb +194 -0
  147. data/lib/active_support/execution_wrapper.rb +117 -0
  148. data/lib/active_support/executor.rb +6 -0
  149. data/lib/active_support/file_update_checker.rb +138 -17
  150. data/lib/active_support/gem_version.rb +15 -0
  151. data/lib/active_support/gzip.rb +11 -5
  152. data/lib/active_support/hash_with_indifferent_access.rb +199 -49
  153. data/lib/active_support/i18n.rb +6 -2
  154. data/lib/active_support/i18n_railtie.rb +40 -21
  155. data/lib/active_support/inflections.rb +22 -13
  156. data/lib/active_support/inflector/inflections.rb +175 -144
  157. data/lib/active_support/inflector/methods.rb +328 -91
  158. data/lib/active_support/inflector/transliterate.rb +51 -37
  159. data/lib/active_support/json/decoding.rb +31 -22
  160. data/lib/active_support/json/encoding.rb +88 -248
  161. data/lib/active_support/key_generator.rb +71 -0
  162. data/lib/active_support/lazy_load_hooks.rb +27 -25
  163. data/lib/active_support/locale/en.yml +102 -3
  164. data/lib/active_support/log_subscriber/test_helper.rb +24 -21
  165. data/lib/active_support/log_subscriber.rb +36 -49
  166. data/lib/active_support/logger.rb +106 -0
  167. data/lib/active_support/logger_silence.rb +28 -0
  168. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  169. data/lib/active_support/message_encryptor.rb +72 -36
  170. data/lib/active_support/message_verifier.rb +96 -24
  171. data/lib/active_support/multibyte/chars.rb +88 -333
  172. data/lib/active_support/multibyte/unicode.rb +156 -136
  173. data/lib/active_support/multibyte.rb +5 -28
  174. data/lib/active_support/notifications/fanout.rb +115 -19
  175. data/lib/active_support/notifications/instrumenter.rb +52 -15
  176. data/lib/active_support/notifications.rb +168 -33
  177. data/lib/active_support/number_helper/number_converter.rb +182 -0
  178. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  179. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  180. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  181. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  182. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  183. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  184. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  185. data/lib/active_support/number_helper.rb +368 -0
  186. data/lib/active_support/option_merger.rb +1 -1
  187. data/lib/active_support/ordered_hash.rb +18 -183
  188. data/lib/active_support/ordered_options.rb +44 -24
  189. data/lib/active_support/per_thread_registry.rb +58 -0
  190. data/lib/active_support/proxy_object.rb +13 -0
  191. data/lib/active_support/rails.rb +27 -0
  192. data/lib/active_support/railtie.rb +25 -34
  193. data/lib/active_support/reloader.rb +129 -0
  194. data/lib/active_support/rescuable.rb +98 -48
  195. data/lib/active_support/security_utils.rb +27 -0
  196. data/lib/active_support/string_inquirer.rb +14 -9
  197. data/lib/active_support/subscriber.rb +120 -0
  198. data/lib/active_support/tagged_logging.rb +78 -0
  199. data/lib/active_support/test_case.rb +69 -17
  200. data/lib/active_support/testing/assertions.rb +43 -41
  201. data/lib/active_support/testing/autorun.rb +12 -0
  202. data/lib/active_support/testing/constant_lookup.rb +50 -0
  203. data/lib/active_support/testing/declarative.rb +7 -21
  204. data/lib/active_support/testing/deprecation.rb +14 -33
  205. data/lib/active_support/testing/file_fixtures.rb +34 -0
  206. data/lib/active_support/testing/isolation.rb +53 -95
  207. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  208. data/lib/active_support/testing/setup_and_teardown.rb +21 -82
  209. data/lib/active_support/testing/stream.rb +42 -0
  210. data/lib/active_support/testing/tagged_logging.rb +25 -0
  211. data/lib/active_support/testing/time_helpers.rb +134 -0
  212. data/lib/active_support/time.rb +6 -23
  213. data/lib/active_support/time_with_zone.rb +239 -92
  214. data/lib/active_support/values/time_zone.rb +236 -160
  215. data/lib/active_support/values/unicode_tables.dat +0 -0
  216. data/lib/active_support/version.rb +5 -7
  217. data/lib/active_support/xml_mini/jdom.rb +19 -13
  218. data/lib/active_support/xml_mini/libxml.rb +3 -4
  219. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  220. data/lib/active_support/xml_mini/nokogiri.rb +3 -4
  221. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  222. data/lib/active_support/xml_mini/rexml.rb +8 -10
  223. data/lib/active_support/xml_mini.rb +66 -34
  224. data/lib/active_support.rb +40 -23
  225. metadata +185 -134
  226. data/CHANGELOG +0 -1534
  227. data/lib/active_support/base64.rb +0 -42
  228. data/lib/active_support/basic_object.rb +0 -21
  229. data/lib/active_support/buffered_logger.rb +0 -137
  230. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  231. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  232. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  233. data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
  234. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
  235. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  236. data/lib/active_support/core_ext/date/freeze.rb +0 -31
  237. data/lib/active_support/core_ext/date_time/zones.rb +0 -21
  238. data/lib/active_support/core_ext/exception.rb +0 -3
  239. data/lib/active_support/core_ext/file/path.rb +0 -5
  240. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  241. data/lib/active_support/core_ext/float.rb +0 -1
  242. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
  243. data/lib/active_support/core_ext/hash/diff.rb +0 -13
  244. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  245. data/lib/active_support/core_ext/logger.rb +0 -81
  246. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  247. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  248. data/lib/active_support/core_ext/module/synchronization.rb +0 -43
  249. data/lib/active_support/core_ext/object/to_json.rb +0 -19
  250. data/lib/active_support/core_ext/proc.rb +0 -14
  251. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  252. data/lib/active_support/core_ext/process.rb +0 -1
  253. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  254. data/lib/active_support/core_ext/range/cover.rb +0 -3
  255. data/lib/active_support/core_ext/rexml.rb +0 -46
  256. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  257. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  258. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  259. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  260. data/lib/active_support/file_watcher.rb +0 -36
  261. data/lib/active_support/json/variable.rb +0 -9
  262. data/lib/active_support/memoizable.rb +0 -105
  263. data/lib/active_support/multibyte/exceptions.rb +0 -8
  264. data/lib/active_support/multibyte/utils.rb +0 -60
  265. data/lib/active_support/ruby/shim.rb +0 -22
  266. data/lib/active_support/secure_random.rb +0 -6
  267. data/lib/active_support/testing/mochaing.rb +0 -7
  268. data/lib/active_support/testing/pending.rb +0 -52
  269. data/lib/active_support/testing/performance/jruby.rb +0 -115
  270. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  271. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  272. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  273. data/lib/active_support/testing/performance/ruby.rb +0 -152
  274. data/lib/active_support/testing/performance.rb +0 -317
  275. data/lib/active_support/time/autoload.rb +0 -5
  276. data/lib/active_support/whiny_nil.rb +0 -60
@@ -1,15 +1,25 @@
1
1
  require 'active_support/core_ext/module/attribute_accessors'
2
2
  require 'active_support/core_ext/module/delegation'
3
- require 'multi_json'
3
+ require 'json'
4
4
 
5
5
  module ActiveSupport
6
6
  # Look for and parse json strings that look like ISO 8601 times.
7
7
  mattr_accessor :parse_json_times
8
8
 
9
9
  module JSON
10
+ # matches YAML-formatted dates
11
+ DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/
12
+ DATETIME_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})?)?)$/
13
+
10
14
  class << self
11
- def decode(json, options ={})
12
- data = MultiJson.decode(json, options)
15
+ # Parses a JSON string (JavaScript Object Notation) into a hash.
16
+ # See http://www.json.org for more info.
17
+ #
18
+ # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
19
+ # => {"team" => "rails", "players" => "36"}
20
+ def decode(json)
21
+ data = ::JSON.parse(json, quirks_mode: true)
22
+
13
23
  if ActiveSupport.parse_json_times
14
24
  convert_dates_from(data)
15
25
  else
@@ -17,25 +27,18 @@ module ActiveSupport
17
27
  end
18
28
  end
19
29
 
20
- def engine
21
- MultiJson.engine
22
- end
23
- alias :backend :engine
24
-
25
- def engine=(name)
26
- MultiJson.engine = name
27
- end
28
- alias :backend= :engine=
29
-
30
- def with_backend(name)
31
- old_backend, self.backend = backend, name
32
- yield
33
- ensure
34
- self.backend = old_backend
35
- end
36
-
30
+ # Returns the class of the error that will be raised when there is an
31
+ # error in decoding JSON. Using this method means you won't directly
32
+ # depend on the ActiveSupport's JSON implementation, in case it changes
33
+ # in the future.
34
+ #
35
+ # begin
36
+ # obj = ActiveSupport::JSON.decode(some_string)
37
+ # rescue ActiveSupport::JSON.parse_error
38
+ # Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
39
+ # end
37
40
  def parse_error
38
- MultiJson::DecodeError
41
+ ::JSON::ParserError
39
42
  end
40
43
 
41
44
  private
@@ -46,7 +49,13 @@ module ActiveSupport
46
49
  nil
47
50
  when DATE_REGEX
48
51
  begin
49
- DateTime.parse(data)
52
+ Date.parse(data)
53
+ rescue ArgumentError
54
+ data
55
+ end
56
+ when DATETIME_REGEX
57
+ begin
58
+ Time.zone.parse(data)
50
59
  rescue ArgumentError
51
60
  data
52
61
  end
@@ -1,287 +1,127 @@
1
- require 'active_support/core_ext/object/to_json'
1
+ require 'active_support/core_ext/object/json'
2
2
  require 'active_support/core_ext/module/delegation'
3
- require 'active_support/deprecation'
4
- require 'active_support/json/variable'
5
- require 'active_support/ordered_hash'
6
-
7
- require 'bigdecimal'
8
- require 'active_support/core_ext/big_decimal/conversions' # for #to_s
9
- require 'active_support/core_ext/array/wrap'
10
- require 'active_support/core_ext/hash/except'
11
- require 'active_support/core_ext/hash/slice'
12
- require 'active_support/core_ext/object/instance_variables'
13
- require 'time'
14
- require 'active_support/core_ext/time/conversions'
15
- require 'active_support/core_ext/date_time/conversions'
16
- require 'active_support/core_ext/date/conversions'
17
3
 
18
4
  module ActiveSupport
19
5
  class << self
20
6
  delegate :use_standard_json_time_format, :use_standard_json_time_format=,
7
+ :time_precision, :time_precision=,
21
8
  :escape_html_entities_in_json, :escape_html_entities_in_json=,
9
+ :json_encoder, :json_encoder=,
22
10
  :to => :'ActiveSupport::JSON::Encoding'
23
11
  end
24
12
 
25
13
  module JSON
26
- # matches YAML-formatted dates
27
- 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})?))$/
28
-
29
- # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
14
+ # Dumps objects in JSON (JavaScript Object Notation).
15
+ # See http://www.json.org for more info.
16
+ #
17
+ # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
18
+ # # => "{\"team\":\"rails\",\"players\":\"36\"}"
30
19
  def self.encode(value, options = nil)
31
- Encoding::Encoder.new(options).encode(value)
20
+ Encoding.json_encoder.new(options).encode(value)
32
21
  end
33
22
 
34
23
  module Encoding #:nodoc:
35
- class CircularReferenceError < StandardError; end
36
-
37
- class Encoder
24
+ class JSONGemEncoder #:nodoc:
38
25
  attr_reader :options
39
26
 
40
27
  def initialize(options = nil)
41
- @options = options
42
- @seen = []
43
- end
44
-
45
- def encode(value, use_options = true)
46
- check_for_circular_references(value) do
47
- jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
48
- jsonified.encode_json(self)
49
- end
50
- end
51
-
52
- # like encode, but only calls as_json, without encoding to string
53
- def as_json(value)
54
- check_for_circular_references(value) do
55
- value.as_json(options_for(value))
56
- end
57
- end
58
-
59
- def options_for(value)
60
- if value.is_a?(Array) || value.is_a?(Hash)
61
- # hashes and arrays need to get encoder in the options, so that they can detect circular references
62
- (options || {}).merge(:encoder => self)
63
- else
64
- options
65
- end
28
+ @options = options || {}
66
29
  end
67
30
 
68
- def escape(string)
69
- Encoding.escape(string)
31
+ # Encode the given object into a JSON string
32
+ def encode(value)
33
+ stringify jsonify value.as_json(options.dup)
70
34
  end
71
35
 
72
36
  private
73
- def check_for_circular_references(value)
74
- if @seen.any? { |object| object.equal?(value) }
75
- raise CircularReferenceError, 'object references itself'
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
76
59
  end
77
- @seen.unshift value
78
- yield
79
- ensure
80
- @seen.shift
81
- end
82
- end
83
-
84
60
 
85
- ESCAPED_CHARS = {
86
- "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
87
- "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
88
- "\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
89
- "\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
90
- "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
91
- "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
92
- "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
93
- "\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
94
- "\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
95
- "\010" => '\b',
96
- "\f" => '\f',
97
- "\n" => '\n',
98
- "\r" => '\r',
99
- "\t" => '\t',
100
- '"' => '\"',
101
- '\\' => '\\\\',
102
- '>' => '\u003E',
103
- '<' => '\u003C',
104
- '&' => '\u0026' }
105
-
106
- class << self
107
- # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
108
- attr_accessor :use_standard_json_time_format
109
-
110
- attr_accessor :escape_regex
111
- attr_reader :escape_html_entities_in_json
61
+ def to_s
62
+ self
63
+ end
64
+ end
112
65
 
113
- def escape_html_entities_in_json=(value)
114
- self.escape_regex = \
115
- if @escape_html_entities_in_json = value
116
- /[\x00-\x1F"\\><&]/
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) }
117
92
  else
118
- /[\x00-\x1F"\\]/
93
+ jsonify value.as_json
119
94
  end
120
- end
95
+ end
121
96
 
122
- def escape(string)
123
- if string.respond_to?(:force_encoding)
124
- string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
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)
125
100
  end
126
- json = string.
127
- gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
128
- gsub(/([\xC0-\xDF][\x80-\xBF]|
129
- [\xE0-\xEF][\x80-\xBF]{2}|
130
- [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
131
- s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
132
- }
133
- json = %("#{json}")
134
- json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
135
- json
136
- end
137
101
  end
138
102
 
139
- self.use_standard_json_time_format = true
140
- self.escape_html_entities_in_json = false
141
- end
142
-
143
- CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
144
- end
145
- end
146
-
147
- class Object
148
- def as_json(options = nil) #:nodoc:
149
- if respond_to?(:to_hash)
150
- to_hash
151
- else
152
- instance_values
153
- end
154
- end
155
- end
156
-
157
- class Struct
158
- def as_json(options = nil) #:nodoc:
159
- Hash[members.zip(values)]
160
- end
161
- end
162
-
163
- class TrueClass
164
- AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
165
- def as_json(options = nil) AS_JSON end #:nodoc:
166
- end
167
-
168
- class FalseClass
169
- AS_JSON = ActiveSupport::JSON::Variable.new('false').freeze
170
- def as_json(options = nil) AS_JSON end #:nodoc:
171
- end
172
-
173
- class NilClass
174
- AS_JSON = ActiveSupport::JSON::Variable.new('null').freeze
175
- def as_json(options = nil) AS_JSON end #:nodoc:
176
- end
177
-
178
- class String
179
- def as_json(options = nil) self end #:nodoc:
180
- def encode_json(encoder) encoder.escape(self) end #:nodoc:
181
- end
182
-
183
- class Symbol
184
- def as_json(options = nil) to_s end #:nodoc:
185
- end
186
-
187
- class Numeric
188
- def as_json(options = nil) self end #:nodoc:
189
- def encode_json(encoder) to_s end #:nodoc:
190
- end
191
-
192
- class BigDecimal
193
- # A BigDecimal would be naturally represented as a JSON number. Most libraries,
194
- # however, parse non-integer JSON numbers directly as floats. Clients using
195
- # those libraries would get in general a wrong number and no way to recover
196
- # other than manually inspecting the string with the JSON code itself.
197
- #
198
- # That's why a JSON string is returned. The JSON literal is not numeric, but if
199
- # the other end knows by contract that the data is supposed to be a BigDecimal,
200
- # it still has the chance to post-process the string and get the real value.
201
- def as_json(options = nil) to_s end #:nodoc:
202
- end
203
-
204
- class Regexp
205
- def as_json(options = nil) to_s end #:nodoc:
206
- end
207
-
208
- module Enumerable
209
- def as_json(options = nil) #:nodoc:
210
- to_a.as_json(options)
211
- end
212
- end
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
213
107
 
214
- class Array
215
- def as_json(options = nil) #:nodoc:
216
- # use encoder as a proxy to call as_json on all elements, to protect from circular references
217
- encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
218
- map { |v| encoder.as_json(v) }
219
- end
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
220
111
 
221
- def encode_json(encoder) #:nodoc:
222
- # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
223
- "[#{map { |v| v.encode_json(encoder) } * ','}]"
224
- end
225
- end
112
+ # Sets the precision of encoded time values.
113
+ # Defaults to 3 (equivalent to millisecond precision)
114
+ attr_accessor :time_precision
226
115
 
227
- class Hash
228
- def as_json(options = nil) #:nodoc:
229
- # create a subset of the hash by applying :only or :except
230
- subset = if options
231
- if attrs = options[:only]
232
- slice(*Array.wrap(attrs))
233
- elsif attrs = options[:except]
234
- except(*Array.wrap(attrs))
235
- else
236
- self
116
+ # Sets the encoder used by Rails to encode Ruby objects into JSON strings
117
+ # in +Object#to_json+ and +ActiveSupport::JSON.encode+.
118
+ attr_accessor :json_encoder
237
119
  end
238
- else
239
- self
240
- end
241
-
242
- # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
243
- encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
244
- result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash
245
- result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }]
246
- end
247
-
248
- def encode_json(encoder)
249
- # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
250
- # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
251
-
252
- # on the other hand, we need to run as_json on the elements, because the model representation may contain fields
253
- # like Time/Date in their original (not jsonified) form, etc.
254
-
255
- "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
256
- end
257
- end
258
120
 
259
- class Time
260
- def as_json(options = nil) #:nodoc:
261
- if ActiveSupport.use_standard_json_time_format
262
- xmlschema
263
- else
264
- %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
265
- end
266
- end
267
- end
268
-
269
- class Date
270
- def as_json(options = nil) #:nodoc:
271
- if ActiveSupport.use_standard_json_time_format
272
- strftime("%Y-%m-%d")
273
- else
274
- strftime("%Y/%m/%d")
275
- end
276
- end
277
- end
278
-
279
- class DateTime
280
- def as_json(options = nil) #:nodoc:
281
- if ActiveSupport.use_standard_json_time_format
282
- xmlschema
283
- else
284
- strftime('%Y/%m/%d %H:%M:%S %z')
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
285
125
  end
286
126
  end
287
127
  end
@@ -0,0 +1,71 @@
1
+ require 'concurrent/map'
2
+ require 'openssl'
3
+
4
+ module ActiveSupport
5
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2.
6
+ # It can be used to derive a number of keys for various purposes from a given secret.
7
+ # This lets Rails applications have a single secure secret, but avoid reusing that
8
+ # key in multiple incompatible contexts.
9
+ class KeyGenerator
10
+ def initialize(secret, options = {})
11
+ @secret = secret
12
+ # The default iterations are higher than required for our key derivation uses
13
+ # on the off chance someone uses this for password storage
14
+ @iterations = options[:iterations] || 2**16
15
+ end
16
+
17
+ # Returns a derived key suitable for use. The default key_size is chosen
18
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
19
+ # i.e. OpenSSL::Digest::SHA1#block_length
20
+ def generate_key(salt, key_size=64)
21
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
22
+ end
23
+ end
24
+
25
+ # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
26
+ # re-executing the key generation process when it's called using the same salt and
27
+ # key_size.
28
+ class CachingKeyGenerator
29
+ def initialize(key_generator)
30
+ @key_generator = key_generator
31
+ @cache_keys = Concurrent::Map.new
32
+ end
33
+
34
+ # Returns a derived key suitable for use.
35
+ def generate_key(*args)
36
+ @cache_keys[args.join] ||= @key_generator.generate_key(*args)
37
+ end
38
+ end
39
+
40
+ class LegacyKeyGenerator # :nodoc:
41
+ SECRET_MIN_LENGTH = 30 # Characters
42
+
43
+ def initialize(secret)
44
+ ensure_secret_secure(secret)
45
+ @secret = secret
46
+ end
47
+
48
+ def generate_key(salt)
49
+ @secret
50
+ end
51
+
52
+ private
53
+
54
+ # To prevent users from using something insecure like "Password" we make sure that the
55
+ # secret they've provided is at least 30 characters in length.
56
+ def ensure_secret_secure(secret)
57
+ if secret.blank?
58
+ raise ArgumentError, "A secret is required to generate an integrity hash " \
59
+ "for cookie session data. Set a secret_key_base of at least " \
60
+ "#{SECRET_MIN_LENGTH} characters in config/secrets.yml."
61
+ end
62
+
63
+ if secret.length < SECRET_MIN_LENGTH
64
+ raise ArgumentError, "Secret should be something secure, " \
65
+ "like \"#{SecureRandom.hex(16)}\". The value you " \
66
+ "provided, \"#{secret}\", is shorter than the minimum length " \
67
+ "of #{SECRET_MIN_LENGTH} characters."
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,32 +1,34 @@
1
- # lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of
2
- # this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead
3
- # a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used
4
- # as example but this feature can be applied elsewhere too.
5
- #
6
- # Here is an example where +on_load+ method is called to register a hook.
7
- #
8
- # initializer "active_record.initialize_timezone" do
9
- # ActiveSupport.on_load(:active_record) do
10
- # self.time_zone_aware_attributes = true
11
- # self.default_timezone = :utc
12
- # end
13
- # end
14
- #
15
- # When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked.
16
- # The very last line of +activerecord/lib/active_record/base.rb+ is:
17
- #
18
- # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
19
- #
20
1
  module ActiveSupport
21
- @load_hooks = Hash.new {|h,k| h[k] = [] }
22
- @loaded = {}
2
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
3
+ # making the app boot faster. Because of this feature now there is no need to
4
+ # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
5
+ # configuration. Instead a hook is registered that applies configuration once
6
+ # <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
7
+ # used as example but this feature can be applied elsewhere too.
8
+ #
9
+ # Here is an example where +on_load+ method is called to register a hook.
10
+ #
11
+ # initializer 'active_record.initialize_timezone' do
12
+ # ActiveSupport.on_load(:active_record) do
13
+ # self.time_zone_aware_attributes = true
14
+ # self.default_timezone = :utc
15
+ # end
16
+ # end
17
+ #
18
+ # When the entirety of +activerecord/lib/active_record/base.rb+ has been
19
+ # evaluated then +run_load_hooks+ is invoked. The very last line of
20
+ # +activerecord/lib/active_record/base.rb+ is:
21
+ #
22
+ # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
23
+ @load_hooks = Hash.new { |h,k| h[k] = [] }
24
+ @loaded = Hash.new { |h,k| h[k] = [] }
23
25
 
24
26
  def self.on_load(name, options = {}, &block)
25
- if base = @loaded[name]
27
+ @loaded[name].each do |base|
26
28
  execute_hook(base, options, block)
27
- else
28
- @load_hooks[name] << [block, options]
29
29
  end
30
+
31
+ @load_hooks[name] << [block, options]
30
32
  end
31
33
 
32
34
  def self.execute_hook(base, options, block)
@@ -38,7 +40,7 @@ module ActiveSupport
38
40
  end
39
41
 
40
42
  def self.run_load_hooks(name, base = Object)
41
- @loaded[name] = base
43
+ @loaded[name] << base
42
44
  @load_hooks[name].each do |hook, options|
43
45
  execute_hook(base, options, hook)
44
46
  end