monetra-ruby 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/Rakefile +35 -0
  2. data/lib/monetra.rb +239 -0
  3. data/lib/monetra/active_support.rb +42 -0
  4. data/lib/monetra/active_support/binding_of_caller.rb +84 -0
  5. data/lib/monetra/active_support/breakpoint.rb +523 -0
  6. data/lib/monetra/active_support/caching_tools.rb +62 -0
  7. data/lib/monetra/active_support/clean_logger.rb +38 -0
  8. data/lib/monetra/active_support/core_ext.rb +1 -0
  9. data/lib/monetra/active_support/core_ext/array.rb +7 -0
  10. data/lib/monetra/active_support/core_ext/array/conversions.rb +72 -0
  11. data/lib/monetra/active_support/core_ext/array/grouping.rb +46 -0
  12. data/lib/monetra/active_support/core_ext/bigdecimal.rb +3 -0
  13. data/lib/monetra/active_support/core_ext/bigdecimal/formatting.rb +7 -0
  14. data/lib/monetra/active_support/core_ext/blank.rb +50 -0
  15. data/lib/monetra/active_support/core_ext/cgi.rb +5 -0
  16. data/lib/monetra/active_support/core_ext/cgi/escape_skipping_slashes.rb +14 -0
  17. data/lib/monetra/active_support/core_ext/class.rb +3 -0
  18. data/lib/monetra/active_support/core_ext/class/attribute_accessors.rb +44 -0
  19. data/lib/monetra/active_support/core_ext/class/inheritable_attributes.rb +115 -0
  20. data/lib/monetra/active_support/core_ext/class/removal.rb +24 -0
  21. data/lib/monetra/active_support/core_ext/date.rb +6 -0
  22. data/lib/monetra/active_support/core_ext/date/conversions.rb +39 -0
  23. data/lib/monetra/active_support/core_ext/enumerable.rb +62 -0
  24. data/lib/monetra/active_support/core_ext/exception.rb +33 -0
  25. data/lib/monetra/active_support/core_ext/hash.rb +13 -0
  26. data/lib/monetra/active_support/core_ext/hash/conversions.rb +148 -0
  27. data/lib/monetra/active_support/core_ext/hash/diff.rb +11 -0
  28. data/lib/monetra/active_support/core_ext/hash/indifferent_access.rb +88 -0
  29. data/lib/monetra/active_support/core_ext/hash/keys.rb +53 -0
  30. data/lib/monetra/active_support/core_ext/hash/reverse_merge.rb +25 -0
  31. data/lib/monetra/active_support/core_ext/integer.rb +7 -0
  32. data/lib/monetra/active_support/core_ext/integer/even_odd.rb +24 -0
  33. data/lib/monetra/active_support/core_ext/integer/inflections.rb +15 -0
  34. data/lib/monetra/active_support/core_ext/kernel.rb +4 -0
  35. data/lib/monetra/active_support/core_ext/kernel/agnostics.rb +11 -0
  36. data/lib/monetra/active_support/core_ext/kernel/daemonizing.rb +15 -0
  37. data/lib/monetra/active_support/core_ext/kernel/reporting.rb +51 -0
  38. data/lib/monetra/active_support/core_ext/kernel/requires.rb +24 -0
  39. data/lib/monetra/active_support/core_ext/load_error.rb +38 -0
  40. data/lib/monetra/active_support/core_ext/logger.rb +16 -0
  41. data/lib/monetra/active_support/core_ext/module.rb +7 -0
  42. data/lib/monetra/active_support/core_ext/module/aliasing.rb +57 -0
  43. data/lib/monetra/active_support/core_ext/module/attr_internal.rb +31 -0
  44. data/lib/monetra/active_support/core_ext/module/attribute_accessors.rb +44 -0
  45. data/lib/monetra/active_support/core_ext/module/delegation.rb +41 -0
  46. data/lib/monetra/active_support/core_ext/module/inclusion.rb +11 -0
  47. data/lib/monetra/active_support/core_ext/module/introspection.rb +21 -0
  48. data/lib/monetra/active_support/core_ext/module/loading.rb +13 -0
  49. data/lib/monetra/active_support/core_ext/name_error.rb +20 -0
  50. data/lib/monetra/active_support/core_ext/numeric.rb +7 -0
  51. data/lib/monetra/active_support/core_ext/numeric/bytes.rb +44 -0
  52. data/lib/monetra/active_support/core_ext/numeric/time.rb +72 -0
  53. data/lib/monetra/active_support/core_ext/object.rb +2 -0
  54. data/lib/monetra/active_support/core_ext/object/extending.rb +47 -0
  55. data/lib/monetra/active_support/core_ext/object/misc.rb +34 -0
  56. data/lib/monetra/active_support/core_ext/pathname.rb +7 -0
  57. data/lib/monetra/active_support/core_ext/pathname/clean_within.rb +14 -0
  58. data/lib/monetra/active_support/core_ext/proc.rb +12 -0
  59. data/lib/monetra/active_support/core_ext/range.rb +5 -0
  60. data/lib/monetra/active_support/core_ext/range/conversions.rb +21 -0
  61. data/lib/monetra/active_support/core_ext/string.rb +13 -0
  62. data/lib/monetra/active_support/core_ext/string/access.rb +58 -0
  63. data/lib/monetra/active_support/core_ext/string/conversions.rb +19 -0
  64. data/lib/monetra/active_support/core_ext/string/inflections.rb +153 -0
  65. data/lib/monetra/active_support/core_ext/string/iterators.rb +17 -0
  66. data/lib/monetra/active_support/core_ext/string/starts_ends_with.rb +20 -0
  67. data/lib/monetra/active_support/core_ext/symbol.rb +12 -0
  68. data/lib/monetra/active_support/core_ext/time.rb +7 -0
  69. data/lib/monetra/active_support/core_ext/time/calculations.rb +188 -0
  70. data/lib/monetra/active_support/core_ext/time/conversions.rb +36 -0
  71. data/lib/monetra/active_support/dependencies.rb +187 -0
  72. data/lib/monetra/active_support/deprecation.rb +106 -0
  73. data/lib/monetra/active_support/inflections.rb +53 -0
  74. data/lib/monetra/active_support/inflector.rb +179 -0
  75. data/lib/monetra/active_support/json.rb +37 -0
  76. data/lib/monetra/active_support/json/encoders.rb +25 -0
  77. data/lib/monetra/active_support/json/encoders/core.rb +65 -0
  78. data/lib/monetra/active_support/option_merger.rb +25 -0
  79. data/lib/monetra/active_support/ordered_options.rb +50 -0
  80. data/lib/monetra/active_support/reloadable.rb +30 -0
  81. data/lib/monetra/active_support/values/time_zone.rb +180 -0
  82. data/lib/monetra/active_support/vendor/builder.rb +13 -0
  83. data/lib/monetra/active_support/vendor/builder/blankslate.rb +63 -0
  84. data/lib/monetra/active_support/vendor/builder/xchar.rb +112 -0
  85. data/lib/monetra/active_support/vendor/builder/xmlbase.rb +145 -0
  86. data/lib/monetra/active_support/vendor/builder/xmlevents.rb +63 -0
  87. data/lib/monetra/active_support/vendor/builder/xmlmarkup.rb +328 -0
  88. data/lib/monetra/active_support/vendor/flexmock.rb +84 -0
  89. data/lib/monetra/active_support/vendor/xml_simple.rb +1019 -0
  90. data/lib/monetra/active_support/version.rb +9 -0
  91. data/lib/monetra/active_support/whiny_nil.rb +38 -0
  92. data/test/test.rb +21 -0
  93. metadata +167 -0
@@ -0,0 +1,17 @@
1
+ require 'strscan'
2
+
3
+ module ActiveSupport #:nodoc:
4
+ module CoreExtensions #:nodoc:
5
+ module String #:nodoc:
6
+ # Custom string iterators
7
+ module Iterators
8
+ # Yields a single-character string for each character in the string.
9
+ # When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.
10
+ def each_char
11
+ scanner, char = StringScanner.new(self), /./mu
12
+ loop { yield(scanner.scan(char) || break) }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module String #:nodoc:
4
+ # Additional string tests.
5
+ module StartsEndsWith
6
+ # Does the string start with the specified +prefix+?
7
+ def starts_with?(prefix)
8
+ prefix = prefix.to_s
9
+ self[0, prefix.length] == prefix
10
+ end
11
+
12
+ # Does the string end with the specified +suffix+?
13
+ def ends_with?(suffix)
14
+ suffix = suffix.to_s
15
+ self[-suffix.length, suffix.length] == suffix
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ class Symbol
2
+ # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples:
3
+ #
4
+ # # The same as people.collect { |p| p.name }
5
+ # people.collect(&:name)
6
+ #
7
+ # # The same as people.select { |p| p.manager? }.collect { |p| p.salary }
8
+ # people.select(&:manager?).collect(&:salary)
9
+ def to_proc
10
+ Proc.new { |*args| args.shift.__send__(self, *args) }
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/time/calculations'
2
+ require File.dirname(__FILE__) + '/time/conversions'
3
+
4
+ class Time#:nodoc:
5
+ include ActiveSupport::CoreExtensions::Time::Calculations
6
+ include ActiveSupport::CoreExtensions::Time::Conversions
7
+ end
@@ -0,0 +1,188 @@
1
+ module ActiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Time #:nodoc:
4
+ # Enables the use of time calculations within Time itself
5
+ module Calculations
6
+ def self.included(base) #:nodoc:
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # Return the number of days in the given month. If a year is given,
12
+ # February will return the correct number of days for leap years.
13
+ # Otherwise, this method will always report February as having 28
14
+ # days.
15
+ def days_in_month(month, year=nil)
16
+ if month == 2
17
+ !year.nil? && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)) ? 29 : 28
18
+ elsif month <= 7
19
+ month % 2 == 0 ? 30 : 31
20
+ else
21
+ month % 2 == 0 ? 31 : 30
22
+ end
23
+ end
24
+ end
25
+
26
+ # Seconds since midnight: Time.now.seconds_since_midnight
27
+ def seconds_since_midnight
28
+ self.hour.hours + self.min.minutes + self.sec + (self.usec/1.0e+6)
29
+ end
30
+
31
+ # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
32
+ # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
33
+ # minute is passed, then sec and usec is set to 0.
34
+ def change(options)
35
+ ::Time.send(
36
+ self.utc? ? :utc : :local,
37
+ options[:year] || self.year,
38
+ options[:month] || self.month,
39
+ options[:mday] || self.mday,
40
+ options[:hour] || self.hour,
41
+ options[:min] || (options[:hour] ? 0 : self.min),
42
+ options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
43
+ options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
44
+ )
45
+ end
46
+
47
+ # Uses Date to provide precise Time calculations for years, months, and days. The +options+ parameter takes a hash with
48
+ # any of these keys: :months, :days, :years.
49
+ def advance(options)
50
+ d = ::Date.new(year + (options.delete(:years) || 0), month, day)
51
+ d = d >> options.delete(:months) if options[:months]
52
+ d = d + options.delete(:days) if options[:days]
53
+ change(options.merge(:year => d.year, :month => d.month, :mday => d.day))
54
+ end
55
+
56
+ # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
57
+ # Do not use this method in combination with x.months, use months_ago instead!
58
+ def ago(seconds)
59
+ seconds.until(self)
60
+ end
61
+
62
+ # Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
63
+ #the Numeric extension. Do not use this method in combination with x.months, use months_since instead!
64
+ def since(seconds)
65
+ seconds.since(self)
66
+ end
67
+ alias :in :since
68
+
69
+ # Returns a new Time representing the time a number of specified months ago
70
+ def months_ago(months)
71
+ months_since(-months)
72
+ end
73
+
74
+ def months_since(months)
75
+ year, month, mday = self.year, self.month, self.mday
76
+
77
+ month += months
78
+
79
+ # in case months is negative
80
+ while month < 1
81
+ month += 12
82
+ year -= 1
83
+ end
84
+
85
+ # in case months is positive
86
+ while month > 12
87
+ month -= 12
88
+ year += 1
89
+ end
90
+
91
+ max = ::Time.days_in_month(month, year)
92
+ mday = max if mday > max
93
+
94
+ change(:year => year, :month => month, :mday => mday)
95
+ end
96
+
97
+ # Returns a new Time representing the time a number of specified years ago
98
+ def years_ago(years)
99
+ change(:year => self.year - years)
100
+ end
101
+
102
+ def years_since(years)
103
+ change(:year => self.year + years)
104
+ end
105
+
106
+ # Short-hand for years_ago(1)
107
+ def last_year
108
+ years_ago(1)
109
+ end
110
+
111
+ # Short-hand for years_since(1)
112
+ def next_year
113
+ years_since(1)
114
+ end
115
+
116
+
117
+ # Short-hand for months_ago(1)
118
+ def last_month
119
+ months_ago(1)
120
+ end
121
+
122
+ # Short-hand for months_since(1)
123
+ def next_month
124
+ months_since(1)
125
+ end
126
+
127
+ # Returns a new Time representing the "start" of this week (Monday, 0:00)
128
+ def beginning_of_week
129
+ days_to_monday = self.wday!=0 ? self.wday-1 : 6
130
+ (self - days_to_monday.days).midnight
131
+ end
132
+ alias :monday :beginning_of_week
133
+ alias :at_beginning_of_week :beginning_of_week
134
+
135
+ # Returns a new Time representing the start of the given day in next week (default is Monday).
136
+ def next_week(day = :monday)
137
+ days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
138
+ since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
139
+ end
140
+
141
+ # Returns a new Time representing the start of the day (0:00)
142
+ def beginning_of_day
143
+ (self - self.seconds_since_midnight).change(:usec => 0)
144
+ end
145
+ alias :midnight :beginning_of_day
146
+ alias :at_midnight :beginning_of_day
147
+ alias :at_beginning_of_day :beginning_of_day
148
+
149
+ # Returns a new Time representing the start of the month (1st of the month, 0:00)
150
+ def beginning_of_month
151
+ #self - ((self.mday-1).days + self.seconds_since_midnight)
152
+ change(:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
153
+ end
154
+ alias :at_beginning_of_month :beginning_of_month
155
+
156
+ # Returns a new Time representing the end of the month (last day of the month, 0:00)
157
+ def end_of_month
158
+ #self - ((self.mday-1).days + self.seconds_since_midnight)
159
+ last_day = ::Time.days_in_month( self.month, self.year )
160
+ change(:mday => last_day,:hour => 0, :min => 0, :sec => 0, :usec => 0)
161
+ end
162
+ alias :at_end_of_month :end_of_month
163
+
164
+ # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
165
+ def beginning_of_quarter
166
+ beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
167
+ end
168
+ alias :at_beginning_of_quarter :beginning_of_quarter
169
+
170
+ # Returns a new Time representing the start of the year (1st of january, 0:00)
171
+ def beginning_of_year
172
+ change(:month => 1,:mday => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
173
+ end
174
+ alias :at_beginning_of_year :beginning_of_year
175
+
176
+ # Convenience method which returns a new Time representing the time 1 day ago
177
+ def yesterday
178
+ self.ago(1.day)
179
+ end
180
+
181
+ # Convenience method which returns a new Time representing the time 1 day since the instance time
182
+ def tomorrow
183
+ self.since(1.day)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,36 @@
1
+ require 'date'
2
+ require 'time'
3
+
4
+ module ActiveSupport #:nodoc:
5
+ module CoreExtensions #:nodoc:
6
+ module Time #:nodoc:
7
+ # Getting times in different convenient string representations and other objects
8
+ module Conversions
9
+ DATE_FORMATS = {
10
+ :db => "%Y-%m-%d %H:%M:%S",
11
+ :short => "%d %b %H:%M",
12
+ :long => "%B %d, %Y %H:%M",
13
+ :rfc822 => "%a, %d %b %Y %H:%M:%S %z"
14
+ }
15
+
16
+ def self.included(klass)
17
+ klass.send(:alias_method, :to_default_s, :to_s)
18
+ klass.send(:alias_method, :to_s, :to_formatted_s)
19
+ end
20
+
21
+ def to_formatted_s(format = :default)
22
+ DATE_FORMATS[format] ? strftime(DATE_FORMATS[format]).strip : to_default_s
23
+ end
24
+
25
+ def to_date
26
+ ::Date.new(year, month, day)
27
+ end
28
+
29
+ # To be able to keep Dates and Times interchangeable on conversions
30
+ def to_time
31
+ self
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,187 @@
1
+ require 'set'
2
+ require File.dirname(__FILE__) + '/core_ext/module/attribute_accessors'
3
+ require File.dirname(__FILE__) + '/core_ext/load_error'
4
+ require File.dirname(__FILE__) + '/core_ext/kernel'
5
+
6
+ module Dependencies #:nodoc:
7
+ extend self
8
+
9
+ # Should we turn on Ruby warnings on the first load of dependent files?
10
+ mattr_accessor :warnings_on_first_load
11
+ self.warnings_on_first_load = false
12
+
13
+ # All files ever loaded.
14
+ mattr_accessor :history
15
+ self.history = Set.new
16
+
17
+ # All files currently loaded.
18
+ mattr_accessor :loaded
19
+ self.loaded = Set.new
20
+
21
+ # Should we load files or require them?
22
+ mattr_accessor :mechanism
23
+ self.mechanism = :load
24
+
25
+ def load?
26
+ mechanism == :load
27
+ end
28
+
29
+ def depend_on(file_name, swallow_load_errors = false)
30
+ require_or_load(file_name)
31
+ rescue LoadError
32
+ raise unless swallow_load_errors
33
+ end
34
+
35
+ def associate_with(file_name)
36
+ depend_on(file_name, true)
37
+ end
38
+
39
+ def clear
40
+ loaded.clear
41
+ end
42
+
43
+ def require_or_load(file_name)
44
+ file_name = $1 if file_name =~ /^(.*)\.rb$/
45
+ return if loaded.include?(file_name)
46
+
47
+ # Record that we've seen this file *before* loading it to avoid an
48
+ # infinite loop with mutual dependencies.
49
+ loaded << file_name
50
+
51
+ if load?
52
+ begin
53
+ # Enable warnings iff this file has not been loaded before and
54
+ # warnings_on_first_load is set.
55
+ if !warnings_on_first_load or history.include?(file_name)
56
+ load "#{file_name}.rb"
57
+ else
58
+ enable_warnings { load "#{file_name}.rb" }
59
+ end
60
+ rescue
61
+ loaded.delete file_name
62
+ raise
63
+ end
64
+ else
65
+ require file_name
66
+ end
67
+
68
+ # Record history *after* loading so first load gets warnings.
69
+ history << file_name
70
+ end
71
+
72
+ # Return the a constant path for the provided parent and constant name
73
+ def constant_path_for(mod, name)
74
+ ([Object, Kernel].include? mod) ? name.to_s : "#{mod}::#{name}"
75
+ end
76
+
77
+ class LoadingModule
78
+ # Old style environment.rb referenced this method directly. Please note, it doesn't
79
+ # actualy *do* anything any more.
80
+ def self.root(*args)
81
+ if defined?(RAILS_DEFAULT_LOGGER)
82
+ RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
83
+ RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ Object.send(:define_method, :require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load)
90
+ Object.send(:define_method, :require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency)
91
+ Object.send(:define_method, :require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association)
92
+
93
+ class Module #:nodoc:
94
+ # Rename the original handler so we can chain it to the new one
95
+ alias :rails_original_const_missing :const_missing
96
+
97
+ # Use const_missing to autoload associations so we don't have to
98
+ # require_association when using single-table inheritance.
99
+ def const_missing(class_id)
100
+ file_name = class_id.to_s.demodulize.underscore
101
+ file_path = as_load_path.empty? ? file_name : "#{as_load_path}/#{file_name}"
102
+ begin
103
+ require_dependency(file_path)
104
+ brief_name = self == Object ? '' : "#{name}::"
105
+ raise NameError.new("uninitialized constant #{brief_name}#{class_id}") unless const_defined?(class_id)
106
+ return const_get(class_id)
107
+ rescue MissingSourceFile => e
108
+ # Re-raise the error if it does not concern the file we were trying to load.
109
+ raise unless e.is_missing? file_path
110
+
111
+ # Look for a directory in the load path that we ought to load.
112
+ if $LOAD_PATH.any? { |base| File.directory? "#{base}/#{file_path}" }
113
+ mod = Module.new
114
+ const_set class_id, mod # Create the new module
115
+ return mod
116
+ end
117
+
118
+ # Attempt to access the name from the parent, unless we don't have a valid
119
+ # parent, or the constant is already defined in the parent. If the latter
120
+ # is the case, then we are being queried via self::class_id, and we should
121
+ # avoid returning the constant from the parent if possible.
122
+ if parent && parent != self && ! parents.any? { |p| p.const_defined?(class_id) }
123
+ suppress(NameError) do
124
+ return parent.send(:const_missing, class_id)
125
+ end
126
+ end
127
+
128
+ qualified_name = Dependencies.constant_path_for self, class_id
129
+ raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
130
+ end
131
+ end
132
+ end
133
+
134
+ class Class
135
+ def const_missing(class_id)
136
+ if [Object, Kernel].include?(self) || parent == self
137
+ super
138
+ else
139
+ begin
140
+ parent.send :const_missing, class_id
141
+ rescue NameError => e
142
+ # Make sure that the name we are missing is the one that caused the error
143
+ parent_qualified_name = Dependencies.constant_path_for parent, class_id
144
+ raise unless e.missing_name? parent_qualified_name
145
+ qualified_name = Dependencies.constant_path_for self, class_id
146
+ raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ class Object #:nodoc:
153
+ def load(file, *extras)
154
+ super(file, *extras)
155
+ rescue Object => exception
156
+ exception.blame_file! file
157
+ raise
158
+ end
159
+
160
+ def require(file, *extras)
161
+ super(file, *extras)
162
+ rescue Object => exception
163
+ exception.blame_file! file
164
+ raise
165
+ end
166
+ end
167
+
168
+ # Add file-blaming to exceptions
169
+ class Exception #:nodoc:
170
+ def blame_file!(file)
171
+ (@blamed_files ||= []).unshift file
172
+ end
173
+
174
+ def blamed_files
175
+ @blamed_files ||= []
176
+ end
177
+
178
+ def describe_blame
179
+ return nil if blamed_files.empty?
180
+ "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
181
+ end
182
+
183
+ def copy_blame!(exc)
184
+ @blamed_files = exc.blamed_files.clone
185
+ self
186
+ end
187
+ end