activesupport 3.0.20 → 3.1.0.beta1

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 (110) hide show
  1. data/CHANGELOG +14 -66
  2. data/README.rdoc +1 -1
  3. data/lib/active_support/backtrace_cleaner.rb +1 -1
  4. data/lib/active_support/buffered_logger.rb +4 -11
  5. data/lib/active_support/cache.rb +12 -15
  6. data/lib/active_support/cache/file_store.rb +3 -2
  7. data/lib/active_support/cache/mem_cache_store.rb +11 -3
  8. data/lib/active_support/cache/strategy/local_cache.rb +28 -23
  9. data/lib/active_support/callbacks.rb +195 -175
  10. data/lib/active_support/concern.rb +105 -35
  11. data/lib/active_support/configurable.rb +41 -2
  12. data/lib/active_support/core_ext/array/access.rb +2 -2
  13. data/lib/active_support/core_ext/array/random_access.rb +10 -7
  14. data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -2
  15. data/lib/active_support/core_ext/class/attribute.rb +27 -17
  16. data/lib/active_support/core_ext/class/inheritable_attributes.rb +12 -86
  17. data/lib/active_support/core_ext/class/subclasses.rb +20 -34
  18. data/lib/active_support/core_ext/date/calculations.rb +14 -2
  19. data/lib/active_support/core_ext/date/conversions.rb +6 -0
  20. data/lib/active_support/core_ext/date_time/calculations.rb +26 -9
  21. data/lib/active_support/core_ext/date_time/conversions.rb +1 -1
  22. data/lib/active_support/core_ext/date_time/zones.rb +1 -1
  23. data/lib/active_support/core_ext/enumerable.rb +2 -5
  24. data/lib/active_support/core_ext/float/rounding.rb +1 -1
  25. data/lib/active_support/core_ext/hash.rb +1 -0
  26. data/lib/active_support/core_ext/hash/conversions.rb +23 -36
  27. data/lib/active_support/core_ext/hash/deep_dup.rb +11 -0
  28. data/lib/active_support/core_ext/hash/keys.rb +6 -4
  29. data/lib/active_support/core_ext/hash/reverse_merge.rb +9 -14
  30. data/lib/active_support/core_ext/kernel/reporting.rb +19 -0
  31. data/lib/active_support/core_ext/logger.rb +11 -38
  32. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +11 -12
  33. data/lib/active_support/core_ext/module/attr_internal.rb +13 -6
  34. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  35. data/lib/active_support/core_ext/object.rb +1 -1
  36. data/lib/active_support/core_ext/object/blank.rb +42 -9
  37. data/lib/active_support/core_ext/object/duplicable.rb +48 -9
  38. data/lib/active_support/core_ext/object/inclusion.rb +15 -0
  39. data/lib/active_support/core_ext/object/instance_variables.rb +1 -35
  40. data/lib/active_support/core_ext/object/to_param.rb +13 -8
  41. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  42. data/lib/active_support/core_ext/object/try.rb +27 -10
  43. data/lib/active_support/core_ext/object/with_options.rb +19 -5
  44. data/lib/active_support/core_ext/range.rb +1 -0
  45. data/lib/active_support/core_ext/range/cover.rb +3 -0
  46. data/lib/active_support/core_ext/string.rb +1 -0
  47. data/lib/active_support/core_ext/string/filters.rb +3 -3
  48. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  49. data/lib/active_support/core_ext/string/inquiry.rb +13 -0
  50. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  51. data/lib/active_support/core_ext/string/output_safety.rb +33 -89
  52. data/lib/active_support/core_ext/time/calculations.rb +14 -13
  53. data/lib/active_support/core_ext/time/conversions.rb +2 -24
  54. data/lib/active_support/core_ext/time/marshal.rb +0 -1
  55. data/lib/active_support/core_ext/time/zones.rb +32 -21
  56. data/lib/active_support/core_ext/uri.rb +8 -0
  57. data/lib/active_support/dependencies.rb +93 -37
  58. data/lib/active_support/deprecation.rb +2 -2
  59. data/lib/active_support/deprecation/behaviors.rb +7 -0
  60. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  61. data/lib/active_support/deprecation/reporting.rb +4 -0
  62. data/lib/active_support/duration.rb +4 -0
  63. data/lib/active_support/file_update_checker.rb +1 -1
  64. data/lib/active_support/file_watcher.rb +36 -0
  65. data/lib/active_support/gzip.rb +0 -1
  66. data/lib/active_support/hash_with_indifferent_access.rb +6 -4
  67. data/lib/active_support/i18n.rb +0 -1
  68. data/lib/active_support/i18n_railtie.rb +2 -2
  69. data/lib/active_support/inflector/inflections.rb +1 -1
  70. data/lib/active_support/json/decoding.rb +37 -23
  71. data/lib/active_support/json/encoding.rb +8 -3
  72. data/lib/active_support/lazy_load_hooks.rb +7 -7
  73. data/lib/active_support/log_subscriber.rb +3 -3
  74. data/lib/active_support/log_subscriber/test_helper.rb +4 -3
  75. data/lib/active_support/message_encryptor.rb +1 -1
  76. data/lib/active_support/message_verifier.rb +1 -1
  77. data/lib/active_support/multibyte/chars.rb +4 -3
  78. data/lib/active_support/multibyte/unicode.rb +1 -1
  79. data/lib/active_support/notifications.rb +9 -8
  80. data/lib/active_support/notifications/fanout.rb +3 -3
  81. data/lib/active_support/ordered_hash.rb +19 -2
  82. data/lib/active_support/ordered_options.rb +33 -3
  83. data/lib/active_support/railtie.rb +1 -1
  84. data/lib/active_support/rescuable.rb +1 -0
  85. data/lib/active_support/secure_random.rb +11 -5
  86. data/lib/active_support/test_case.rb +2 -10
  87. data/lib/active_support/testing/assertions.rb +17 -6
  88. data/lib/active_support/testing/mochaing.rb +7 -0
  89. data/lib/active_support/testing/pending.rb +27 -23
  90. data/lib/active_support/testing/setup_and_teardown.rb +8 -11
  91. data/lib/active_support/time_with_zone.rb +6 -3
  92. data/lib/active_support/version.rb +3 -3
  93. data/lib/active_support/whiny_nil.rb +1 -1
  94. data/lib/active_support/xml_mini.rb +4 -2
  95. data/lib/active_support/xml_mini/jdom.rb +9 -16
  96. data/lib/active_support/xml_mini/libxml.rb +1 -0
  97. data/lib/active_support/xml_mini/libxmlsax.rb +2 -1
  98. data/lib/active_support/xml_mini/nokogiri.rb +1 -0
  99. data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
  100. data/lib/active_support/xml_mini/rexml.rb +1 -0
  101. metadata +50 -32
  102. checksums.yaml +0 -7
  103. data/lib/active_support/core_ext/cgi.rb +0 -1
  104. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -19
  105. data/lib/active_support/core_ext/object/returning.rb +0 -43
  106. data/lib/active_support/json/backends/jsongem.rb +0 -47
  107. data/lib/active_support/json/backends/okjson.rb +0 -644
  108. data/lib/active_support/json/backends/yajl.rb +0 -44
  109. data/lib/active_support/json/backends/yaml.rb +0 -19
  110. data/lib/active_support/testing/default.rb +0 -9
@@ -2,49 +2,35 @@ require 'active_support/core_ext/module/anonymous'
2
2
  require 'active_support/core_ext/module/reachable'
3
3
 
4
4
  class Class #:nodoc:
5
- # Rubinius
6
- if defined?(Class.__subclasses__)
7
- alias :subclasses :__subclasses__
5
+ begin
6
+ ObjectSpace.each_object(Class.new) {}
8
7
 
9
8
  def descendants
10
9
  descendants = []
11
- __subclasses__.each do |k|
12
- descendants << k
13
- descendants.concat k.descendants
10
+ ObjectSpace.each_object(class << self; self; end) do |k|
11
+ descendants.unshift k unless k == self
14
12
  end
15
13
  descendants
16
14
  end
17
- else # MRI
18
- begin
19
- ObjectSpace.each_object(Class.new) {}
20
-
21
- def descendants
22
- descendants = []
23
- ObjectSpace.each_object(class << self; self; end) do |k|
24
- descendants.unshift k unless k == self
25
- end
26
- descendants
27
- end
28
- rescue StandardError # JRuby
29
- def descendants
30
- descendants = []
31
- ObjectSpace.each_object(Class) do |k|
32
- descendants.unshift k if k < self
33
- end
34
- descendants.uniq!
35
- descendants
15
+ rescue StandardError # JRuby
16
+ def descendants
17
+ descendants = []
18
+ ObjectSpace.each_object(Class) do |k|
19
+ descendants.unshift k if k < self
36
20
  end
21
+ descendants.uniq!
22
+ descendants
37
23
  end
24
+ end
38
25
 
39
- # Returns an array with the direct children of +self+.
40
- #
41
- # Integer.subclasses # => [Bignum, Fixnum]
42
- def subclasses
43
- subclasses, chain = [], descendants
44
- chain.each do |k|
45
- subclasses << k unless chain.any? { |c| c > k }
46
- end
47
- subclasses
26
+ # Returns an array with the direct children of +self+.
27
+ #
28
+ # Integer.subclasses # => [Bignum, Fixnum]
29
+ def subclasses
30
+ subclasses, chain = [], descendants
31
+ chain.each do |k|
32
+ subclasses << k unless chain.any? { |c| c > k }
48
33
  end
34
+ subclasses
49
35
  end
50
36
  end
@@ -5,6 +5,8 @@ require 'active_support/core_ext/date/zones'
5
5
  require 'active_support/core_ext/time/zones'
6
6
 
7
7
  class Date
8
+ DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
9
+
8
10
  if RUBY_VERSION < '1.9'
9
11
  undef :>>
10
12
 
@@ -127,6 +129,11 @@ class Date
127
129
  )
128
130
  end
129
131
 
132
+ # Returns a new Date/DateTime representing the time a number of specified weeks ago.
133
+ def weeks_ago(weeks)
134
+ advance(:weeks => -weeks)
135
+ end
136
+
130
137
  # Returns a new Date/DateTime representing the time a number of specified months ago.
131
138
  def months_ago(months)
132
139
  advance(:months => -months)
@@ -185,10 +192,15 @@ class Date
185
192
  alias :sunday :end_of_week
186
193
  alias :at_end_of_week :end_of_week
187
194
 
195
+ # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
196
+ def prev_week(day = :monday)
197
+ result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
198
+ self.acts_like?(:time) ? result.change(:hour => 0) : result
199
+ end
200
+
188
201
  # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
189
202
  def next_week(day = :monday)
190
- days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
191
- result = (self + 7).beginning_of_week + days_into_week[day]
203
+ result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
192
204
  self.acts_like?(:time) ? result.change(:hour => 0) : result
193
205
  end
194
206
 
@@ -93,6 +93,12 @@ class Date
93
93
  ::DateTime.civil(year, month, day, 0, 0, 0, 0)
94
94
  end if RUBY_VERSION < '1.9'
95
95
 
96
+ def iso8601
97
+ strftime('%F')
98
+ end if RUBY_VERSION < '1.9'
99
+
100
+ alias_method :rfc3339, :iso8601 if RUBY_VERSION < '1.9'
101
+
96
102
  def xmlschema
97
103
  to_time_in_current_zone.xmlschema
98
104
  end
@@ -1,12 +1,10 @@
1
1
  require 'rational' unless RUBY_VERSION >= '1.9.2'
2
- require 'active_support/core_ext/object/acts_like'
3
- require 'active_support/core_ext/time/zones'
4
2
 
5
3
  class DateTime
6
4
  class << self
7
5
  # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
8
6
  def local_offset
9
- ::Time.local(2012).utc_offset.to_r / 86400
7
+ ::Time.local(2007).utc_offset.to_r / 86400
10
8
  end
11
9
 
12
10
  # Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>.
@@ -83,6 +81,29 @@ class DateTime
83
81
  change(:hour => 23, :min => 59, :sec => 59)
84
82
  end
85
83
 
84
+ # 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
85
+ if DateTime.public_instance_methods(false).include?(:+)
86
+ def plus_with_duration(other) #:nodoc:
87
+ if ActiveSupport::Duration === other
88
+ other.since(self)
89
+ else
90
+ plus_without_duration(other)
91
+ end
92
+ end
93
+ alias_method :plus_without_duration, :+
94
+ alias_method :+, :plus_with_duration
95
+
96
+ def minus_with_duration(other) #:nodoc:
97
+ if ActiveSupport::Duration === other
98
+ plus_with_duration(-other)
99
+ else
100
+ minus_without_duration(other)
101
+ end
102
+ end
103
+ alias_method :minus_without_duration, :-
104
+ alias_method :-, :minus_with_duration
105
+ end
106
+
86
107
  # Adjusts DateTime to UTC by adding its offset value; offset is set to 0
87
108
  #
88
109
  # Example:
@@ -105,11 +126,7 @@ class DateTime
105
126
  end
106
127
 
107
128
  # Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
108
- def compare_with_coercion(other)
109
- other = other.comparable_time if other.respond_to?(:comparable_time)
110
- other = other.to_datetime unless other.acts_like?(:date)
111
- compare_without_coercion(other)
129
+ def <=>(other)
130
+ super other.to_datetime
112
131
  end
113
- alias_method :compare_without_coercion, :<=>
114
- alias_method :<=>, :compare_with_coercion
115
132
  end
@@ -66,7 +66,7 @@ class DateTime
66
66
  # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
67
67
  # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
68
68
  def to_time
69
- self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self
69
+ self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
70
70
  end
71
71
 
72
72
  # To be able to keep Times, Dates and DateTimes interchangeable on conversions
@@ -16,6 +16,6 @@ class DateTime
16
16
  def in_time_zone(zone = ::Time.zone)
17
17
  return self unless zone
18
18
 
19
- ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone))
19
+ ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
20
20
  end
21
21
  end
@@ -90,14 +90,11 @@ module Enumerable
90
90
  # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
91
91
  #
92
92
  def index_by
93
- inject({}) do |accum, elem|
94
- accum[yield(elem)] = elem
95
- accum
96
- end
93
+ Hash[map { |elem| [yield(elem), elem] }]
97
94
  end
98
95
 
99
96
  # Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
100
- # Works with a block too ala any?, so people.many? { |p| p.age > 26 } # => returns true if more than 1 person is over 26.
97
+ # Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
101
98
  def many?(&block)
102
99
  size = block_given? ? select(&block).size : self.size
103
100
  size > 1
@@ -16,4 +16,4 @@ class Float
16
16
  precisionless_round
17
17
  end
18
18
  end
19
- end
19
+ end if RUBY_VERSION < '1.9'
@@ -1,5 +1,6 @@
1
1
  require 'active_support/core_ext/hash/conversions'
2
2
  require 'active_support/core_ext/hash/deep_merge'
3
+ require 'active_support/core_ext/hash/deep_dup'
3
4
  require 'active_support/core_ext/hash/diff'
4
5
  require 'active_support/core_ext/hash/except'
5
6
  require 'active_support/core_ext/hash/indifferent_access'
@@ -26,10 +26,22 @@ class Hash
26
26
  #
27
27
  # * If +value+ is a callable object it must expect one or two arguments. Depending
28
28
  # on the arity, the callable is invoked with the +options+ hash as first argument
29
- # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its
30
- # return value becomes a new node.
29
+ # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
30
+ # callable can add nodes by using <tt>options[:builder]</tt>.
31
+ #
32
+ # "foo".to_xml(lambda { |options, key| options[:builder].b(key) })
33
+ # # => "<b>foo</b>"
31
34
  #
32
35
  # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
36
+ #
37
+ # class Foo
38
+ # def to_xml(options)
39
+ # options[:builder].bar "fooing!"
40
+ # end
41
+ # end
42
+ #
43
+ # {:foo => Foo.new}.to_xml(:skip_instruct => true)
44
+ # # => "<hash><bar>fooing!</bar></hash>"
33
45
  #
34
46
  # * Otherwise, a node with +key+ as tag is created with a string representation of
35
47
  # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
@@ -73,33 +85,15 @@ class Hash
73
85
  end
74
86
  end
75
87
 
76
- class DisallowedType < StandardError #:nodoc:
77
- def initialize(type)
78
- super "Disallowed type attribute: #{type.inspect}"
79
- end
80
- end
81
-
82
- DISALLOWED_XML_TYPES = %w(symbol yaml)
83
-
84
88
  class << self
85
- def from_xml(xml, disallowed_types = nil)
86
- typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)), disallowed_types)
87
- end
88
-
89
- def from_trusted_xml(xml)
90
- from_xml xml, []
89
+ def from_xml(xml)
90
+ typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
91
91
  end
92
92
 
93
93
  private
94
- def typecast_xml_value(value, disallowed_types = nil)
95
- disallowed_types ||= DISALLOWED_XML_TYPES
96
-
94
+ def typecast_xml_value(value)
97
95
  case value.class.to_s
98
96
  when 'Hash'
99
- if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
100
- raise DisallowedType, value['type']
101
- end
102
-
103
97
  if value['type'] == 'array'
104
98
  _, entries = Array.wrap(value.detect { |k,v| k != 'type' })
105
99
  if entries.nil? || (c = value['__content__'] && c.blank?)
@@ -107,15 +101,14 @@ class Hash
107
101
  else
108
102
  case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
109
103
  when "Array"
110
- entries.collect { |v| typecast_xml_value(v, disallowed_types) }
104
+ entries.collect { |v| typecast_xml_value(v) }
111
105
  when "Hash"
112
- [typecast_xml_value(entries, disallowed_types)]
106
+ [typecast_xml_value(entries)]
113
107
  else
114
108
  raise "can't typecast #{entries.inspect}"
115
109
  end
116
110
  end
117
- elsif value['type'] == 'file' ||
118
- (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
111
+ elsif value['type'] == 'file' || value["__content__"].present?
119
112
  content = value["__content__"]
120
113
  if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
121
114
  parser.arity == 1 ? parser.call(content) : parser.call(content, value)
@@ -133,17 +126,14 @@ class Hash
133
126
  elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
134
127
  nil
135
128
  else
136
- xml_value = value.inject({}) do |h,(k,v)|
137
- h[k] = typecast_xml_value(v, disallowed_types)
138
- h
139
- end
129
+ xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }]
140
130
 
141
131
  # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
142
132
  # how multipart uploaded files from HTML appear
143
133
  xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
144
134
  end
145
135
  when 'Array'
146
- value.map! { |i| typecast_xml_value(i, disallowed_types) }
136
+ value.map! { |i| typecast_xml_value(i) }
147
137
  value.length > 1 ? value : value.first
148
138
  when 'String'
149
139
  value
@@ -155,10 +145,7 @@ class Hash
155
145
  def unrename_keys(params)
156
146
  case params.class.to_s
157
147
  when "Hash"
158
- params.inject({}) do |h,(k,v)|
159
- h[k.to_s.tr("-", "_")] = unrename_keys(v)
160
- h
161
- end
148
+ Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
162
149
  when "Array"
163
150
  params.map { |v| unrename_keys(v) }
164
151
  else
@@ -0,0 +1,11 @@
1
+ class Hash
2
+ # Returns a deep copy of hash.
3
+ def deep_dup
4
+ duplicate = self.dup
5
+ duplicate.each_pair do |k,v|
6
+ tv = duplicate[k]
7
+ duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v
8
+ end
9
+ duplicate
10
+ end
11
+ end
@@ -35,11 +35,13 @@ class Hash
35
35
  # as keys, this will fail.
36
36
  #
37
37
  # ==== Examples
38
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
39
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
38
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
39
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
40
40
  # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
41
41
  def assert_valid_keys(*valid_keys)
42
- unknown_keys = keys - [valid_keys].flatten
43
- raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
42
+ valid_keys.flatten!
43
+ each_key do |k|
44
+ raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
45
+ end
44
46
  end
45
47
  end
@@ -1,27 +1,22 @@
1
1
  class Hash
2
- # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
3
- # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
2
+ # Merges the caller into +other_hash+. For example,
4
3
  #
5
- # def setup(options = {})
6
- # options.reverse_merge! :size => 25, :velocity => 10
7
- # end
4
+ # options = options.reverse_merge(:size => 25, :velocity => 10)
8
5
  #
9
- # Using <tt>merge</tt>, the above example would look as follows:
6
+ # is equivalent to
10
7
  #
11
- # def setup(options = {})
12
- # { :size => 25, :velocity => 10 }.merge(options)
13
- # end
8
+ # options = {:size => 25, :velocity => 10}.merge(options)
14
9
  #
15
- # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
16
- # have the respective key.
10
+ # This is particularly useful for initializing an options hash
11
+ # with default values.
17
12
  def reverse_merge(other_hash)
18
13
  other_hash.merge(self)
19
14
  end
20
15
 
21
- # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
22
- # Modifies the receiver in place.
16
+ # Destructive +reverse_merge+.
23
17
  def reverse_merge!(other_hash)
24
- merge!( other_hash ){|k,o,n| o }
18
+ # right wins if there is no left
19
+ merge!( other_hash ){|key,left,right| left }
25
20
  end
26
21
 
27
22
  alias_method :reverse_update, :reverse_merge!
@@ -59,4 +59,23 @@ module Kernel
59
59
  raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
60
60
  end
61
61
  end
62
+
63
+ # Captures the given stream and returns it:
64
+ #
65
+ # stream = capture(:stdout){ puts "Cool" }
66
+ # stream # => "Cool\n"
67
+ #
68
+ def capture(stream)
69
+ begin
70
+ stream = stream.to_s
71
+ eval "$#{stream} = StringIO.new"
72
+ yield
73
+ result = eval("$#{stream}").string
74
+ ensure
75
+ eval("$#{stream} = #{stream.upcase}")
76
+ end
77
+
78
+ result
79
+ end
80
+ alias :silence :capture
62
81
  end
@@ -4,18 +4,17 @@ require 'active_support/core_ext/class/attribute_accessors'
4
4
  class Logger #:nodoc:
5
5
  def self.define_around_helper(level)
6
6
  module_eval <<-end_eval, __FILE__, __LINE__ + 1
7
- def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
8
- self.#{level}(before_message) # self.debug(before_message)
9
- return_value = block.call(self) # return_value = block.call(self)
10
- self.#{level}(after_message) # self.debug(after_message)
11
- return return_value # return return_value
12
- end # end
7
+ def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block)
8
+ self.#{level}(before_message) # self.debug(before_message)
9
+ return_value = yield(self) # return_value = yield(self)
10
+ self.#{level}(after_message) # self.debug(after_message)
11
+ return_value # return_value
12
+ end # end
13
13
  end_eval
14
14
  end
15
15
  [:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
16
16
  end
17
17
 
18
-
19
18
  require 'logger'
20
19
 
21
20
  # Extensions to the built-in Ruby logger.
@@ -65,11 +64,11 @@ class Logger
65
64
  formatter.datetime_format if formatter.respond_to?(:datetime_format)
66
65
  end
67
66
 
68
- alias :old_formatter :formatter if method_defined?(:formatter)
69
- # Get the current formatter. The default formatter is a SimpleFormatter which only
70
- # displays the log message
71
- def formatter
72
- @formatter ||= SimpleFormatter.new
67
+ alias :old_initialize :initialize
68
+ # Overwrite initialize to set a default formatter.
69
+ def initialize(*args)
70
+ old_initialize(*args)
71
+ self.formatter = SimpleFormatter.new
73
72
  end
74
73
 
75
74
  # Simple formatter which only displays the message.
@@ -79,30 +78,4 @@ class Logger
79
78
  "#{String === msg ? msg : msg.inspect}\n"
80
79
  end
81
80
  end
82
-
83
- private
84
- alias old_format_message format_message
85
-
86
- # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
87
- # We can't test RUBY_VERSION because some distributions don't keep Ruby
88
- # and its standard library in sync, leading to installations of Ruby 1.8.2
89
- # with Logger from 1.8.3 and vice versa.
90
- if method_defined?(:formatter=)
91
- def format_message(severity, timestamp, progname, msg)
92
- formatter.call(severity, timestamp, progname, msg)
93
- end
94
- else
95
- def format_message(severity, timestamp, msg, progname)
96
- formatter.call(severity, timestamp, progname, msg)
97
- end
98
-
99
- attr_writer :formatter
100
- public :formatter=
101
-
102
- alias old_format_datetime format_datetime
103
- def format_datetime(datetime) datetime end
104
-
105
- alias old_msg2str msg2str
106
- def msg2str(msg) msg end
107
- end
108
81
  end