rails_info 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +58 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/rails_info/application.js +6 -0
  5. data/app/assets/javascripts/rails_info/base.js.coffee +6 -0
  6. data/app/assets/javascripts/rails_info/bootstrap.js.coffee +4 -0
  7. data/app/assets/stylesheets/rails_info/application.css +14 -0
  8. data/app/assets/stylesheets/rails_info/base.css.scss +22 -0
  9. data/app/assets/stylesheets/rails_info/bootstrap_and_overrides.css.less +63 -0
  10. data/app/assets/stylesheets/rails_info/exception.css +17 -0
  11. data/app/assets/stylesheets/rails_info/exception/base.css.scss +31 -0
  12. data/app/assets/stylesheets/rails_info/exception/pygments.css.erb +5 -0
  13. data/app/controllers/rails_info/data_controller.rb +9 -0
  14. data/app/controllers/rails_info/logs/server_controller.rb +10 -0
  15. data/app/controllers/rails_info/logs/test/rspec_controller.rb +16 -0
  16. data/app/controllers/rails_info/model_controller.rb +5 -0
  17. data/app/controllers/rails_info/properties_controller.rb +4 -0
  18. data/app/controllers/rails_info/routes_controller.rb +4 -0
  19. data/app/controllers/rails_info/stack_traces_controller.rb +11 -0
  20. data/app/controllers/rails_info_controller.rb +16 -0
  21. data/app/helpers/rails_info/resources_helper.rb +194 -0
  22. data/app/presenters/rails_info/code_presenter.rb +63 -0
  23. data/app/presenters/rails_info/data/object_presenter.rb +34 -0
  24. data/app/presenters/rails_info/data/row_set_presenter.rb +38 -0
  25. data/app/presenters/rails_info/data_presenter.rb +21 -0
  26. data/app/presenters/rails_info/logs/server/action_presenter.rb +109 -0
  27. data/app/presenters/rails_info/logs/server_presenter.rb +57 -0
  28. data/app/presenters/rails_info/logs/test/rspec/file_presenter.rb +87 -0
  29. data/app/presenters/rails_info/logs/test/rspec_presenter.rb +32 -0
  30. data/app/presenters/rails_info/model_presenter.rb +68 -0
  31. data/app/presenters/rails_info/presenter.rb +19 -0
  32. data/app/presenters/rails_info/stack_trace_presenter.rb +98 -0
  33. data/app/views/layouts/_layout.html.erb +19 -0
  34. data/app/views/layouts/rails_info.html.erb +1 -0
  35. data/app/views/layouts/rails_info/exception.html.erb +1 -0
  36. data/app/views/layouts/shared/_navigation.html.erb +7 -0
  37. data/app/views/rails_info/data/index.html.erb +23 -0
  38. data/app/views/rails_info/logs/server/_table.html.erb +16 -0
  39. data/app/views/rails_info/logs/server/new.html.erb +19 -0
  40. data/app/views/rails_info/logs/test/rspec/new.html.erb +10 -0
  41. data/app/views/rails_info/model/index.html.erb +14 -0
  42. data/app/views/rails_info/properties/index.html.erb +1 -0
  43. data/app/views/rails_info/routes/index.html.erb +18 -0
  44. data/app/views/rails_info/stack_traces/_accordion.html.erb +1 -0
  45. data/app/views/rails_info/stack_traces/_form.html.erb +11 -0
  46. data/app/views/rails_info/stack_traces/_with_request_and_response.html.erb +16 -0
  47. data/app/views/rails_info/stack_traces/new.html.erb +10 -0
  48. data/config/navigation.rb +20 -0
  49. data/config/routes.rb +20 -0
  50. data/lib/rails_info.rb +19 -0
  51. data/lib/rails_info/controller.rb +2 -0
  52. data/lib/rails_info/controller/exception_diagnostics.rb +28 -0
  53. data/lib/rails_info/data.rb +55 -0
  54. data/lib/rails_info/engine.rb +9 -0
  55. data/lib/rails_info/logs.rb +2 -0
  56. data/lib/rails_info/logs/server.rb +230 -0
  57. data/lib/rails_info/logs/test.rb +2 -0
  58. data/lib/rails_info/logs/test/rspec.rb +160 -0
  59. data/lib/rails_info/model.rb +221 -0
  60. data/lib/rails_info/stack_trace.rb +155 -0
  61. data/lib/rails_info/version.rb +3 -0
  62. data/lib/tasks/rails_info_tasks.rake +4 -0
  63. data/spec/dummy/README.rdoc +261 -0
  64. data/spec/dummy/Rakefile +7 -0
  65. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  66. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  67. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  68. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  69. data/spec/dummy/app/models/user.rb +3 -0
  70. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  71. data/spec/dummy/config.ru +4 -0
  72. data/spec/dummy/config/application.rb +59 -0
  73. data/spec/dummy/config/boot.rb +10 -0
  74. data/spec/dummy/config/database.yml +42 -0
  75. data/spec/dummy/config/environment.rb +5 -0
  76. data/spec/dummy/config/environments/development.rb +39 -0
  77. data/spec/dummy/config/environments/production.rb +69 -0
  78. data/spec/dummy/config/environments/test.rb +39 -0
  79. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  80. data/spec/dummy/config/initializers/inflections.rb +15 -0
  81. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  82. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  83. data/spec/dummy/config/initializers/session_store.rb +8 -0
  84. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  85. data/spec/dummy/config/locales/en.yml +5 -0
  86. data/spec/dummy/config/routes.rb +2 -0
  87. data/spec/dummy/log/development.log +4876 -0
  88. data/spec/dummy/public/404.html +26 -0
  89. data/spec/dummy/public/422.html +26 -0
  90. data/spec/dummy/public/500.html +25 -0
  91. data/spec/dummy/public/favicon.ico +0 -0
  92. data/spec/dummy/script/rails +6 -0
  93. metadata +274 -0
@@ -0,0 +1,2 @@
1
+ module RailsInfo::Controller
2
+ end
@@ -0,0 +1,28 @@
1
+ module RailsInfo::Controller::ExceptionDiagnostics
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ rescue_from Exception, with: :custom_stack_trace
6
+ end
7
+
8
+ private
9
+
10
+ def custom_stack_trace(exception)
11
+ =begin
12
+ wrapper = ActionDispatch::ExceptionWrapper.new(env, exception)
13
+ trace = {
14
+ exception: wrapper.exception,
15
+ :application_trace => wrapper.application_trace,
16
+ :framework_trace => wrapper.framework_trace,
17
+ :full_trace => wrapper.full_trace
18
+ }
19
+ =end
20
+ @stack_trace = RailsInfo::StackTracePresenter.new(
21
+ view_context, stack_trace: {
22
+ body: exception.backtrace, exception: exception , request: request
23
+ }
24
+ )
25
+
26
+ render 'rails_info/stack_traces/new', layout: 'rails_info/exception'
27
+ end
28
+ end
@@ -0,0 +1,55 @@
1
+ class RailsInfo::Data
2
+ class << self
3
+ def update_multiple(data)
4
+ collection = {}
5
+
6
+ data.each do |element|
7
+ klass, id = element.split(';')
8
+ collection[klass] ||= []
9
+ collection[klass] << id
10
+ end
11
+
12
+ collection.each do |klass,ids|
13
+ klass.constantize.delete(ids)
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize
19
+ @rails_info_model = ::RailsInfo::Model.new
20
+ end
21
+
22
+ def last_objects
23
+ klasses = @rails_info_model.classes
24
+ objects = {}
25
+
26
+ klasses.each do |klass|
27
+ sort_field = [:updated_at, :created_at].select{|v| klass.columns.map{|c|c.name.to_sym}.include?(v)}.first
28
+
29
+ raise NotImplementedError.new("No known sort fields found in attribute keys: #{object.attributes.keys.inspect}") if sort_field.blank?
30
+
31
+ klass.limit(10).each do |object|
32
+ objects[object.send(sort_field).to_i] = object.attributes
33
+ objects[object.send(sort_field).to_i][:class] = object.class
34
+ end
35
+ end
36
+
37
+ row_sets = []
38
+ last_class = ""
39
+ last_objects = []
40
+
41
+ objects.keys.sort {|x,y| y <=> x }.each do |sort_value|
42
+ if last_class != "" && objects[sort_value][:class] != last_class
43
+ row_sets << last_objects
44
+ last_objects = []
45
+ end
46
+
47
+ last_objects << objects[sort_value]
48
+ last_class = objects[sort_value][:class]
49
+ end
50
+
51
+ row_sets << last_objects
52
+
53
+ row_sets
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module RailsInfo
2
+ class Engine < ::Rails::Engine
3
+ config.before_initialize do |app|
4
+ if Rails.env.development?
5
+ SimpleNavigation.config_file_paths << "#{File.expand_path(File.dirname(__FILE__))}/../../config"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ module RailsInfo::Logs
2
+ end
@@ -0,0 +1,230 @@
1
+ class RailsInfo::Logs::Server
2
+ def initialize(options = {log: {}, debug: false})
3
+ options ||= {log: {}, debug: false}
4
+ options[:log] ||= {}
5
+
6
+ @debug = options[:debug]
7
+ path = options[:log][:path] || "#{Rails.root}/log/"
8
+ env = options[:log][:env] || Rails.env
9
+ @body = options[:log][:body]
10
+
11
+ unless @body
12
+ file_path = "#{path}#{env}.log"
13
+
14
+ @body = File.new(file_path, 'r').read if File.exist?(file_path)
15
+ end
16
+
17
+ @log ||= {}
18
+ @writes = {}
19
+
20
+ process if @body.present?
21
+ end
22
+
23
+ def hash
24
+ @log
25
+ end
26
+
27
+ def writes
28
+ @writes
29
+ end
30
+
31
+ private
32
+
33
+ def process
34
+ @start_after_last_shutdown = true
35
+ @body = @body.split("\n").map(&:strip)
36
+
37
+ reset_log
38
+
39
+ group_by_action = false
40
+
41
+ @body.each do |line|
42
+ if line.match('Processing') && line.match('#') && line.match(/\[/) && line.match(/\]/)
43
+ # rails 2
44
+ group_by_action = true
45
+ elsif line.match('Started') && line.match(' for ') && line.match(' at ') && line.split(' ').length == 9
46
+ # rails 3
47
+ group_by_action = true
48
+ elsif line.match('Processing by') && line.match('#') && line.match(' as ') && line.split(' ').length == 5
49
+ # rails 3
50
+ group_by_action = true
51
+ end
52
+ end
53
+
54
+ unless group_by_action
55
+ @action = 'no_action_grouping'
56
+ @log[@action] ||= { 'Parameters' => '', 'WRITE' => {}, 'READ' => [], 'views' => [], 'misc' => [], 'errors' => []}
57
+ end
58
+
59
+ @body.each do |line|
60
+ process_line(line)
61
+ end
62
+
63
+ @log.each do |action,tabs|
64
+ tabs.each do |tab,content|
65
+ @log[action].delete(tab) if @log[action][tab].blank?
66
+ end
67
+ end
68
+
69
+ @log.each do |action,tabs|
70
+ tabs.each do |tab,content|
71
+ next unless tab == 'WRITE'
72
+
73
+ # define WHERE column as last one
74
+ content.each do |table_name,data|
75
+ @log[action][tab][table_name]['columns'] << 'WHERE'
76
+ end
77
+
78
+ break
79
+ end
80
+ end
81
+
82
+ @writes.each do |table_name,data|
83
+ @writes[table_name]['columns'] << 'WHERE'
84
+ end
85
+ end
86
+
87
+ def reset_log
88
+ @log, @action_indexes, @action, @last_action = {}, {}, "", ""
89
+ end
90
+
91
+ # TODO: process test.log which don't have action grouping, too
92
+ # TODO: look if newrelic & Co. already does that but are only doing it for the last action
93
+ # TODO: parse stack traces by grouping the lines by file and than method, generate a pseudo web sequence diagram code for files as participants and their methods
94
+ # TODO: integrate a code snippet for each line of the stack trace like brakeman & co. do
95
+ # TODO: extend rails stack trace output by code snippets
96
+ def process_line(line)
97
+ if line.match('Ctrl-C to shutdown server') && @start_after_last_shutdown
98
+ reset_log
99
+ elsif line.match('Processing') && line.match('#') && line.match(/\[/) && line.match(/\]/)
100
+ # rails 2 start of action
101
+ # Processing Clickworker::DashboardsController#show (for 127.0.0.1 at 2011-08-26 12:22:58) [GET]
102
+ @action = line.split(' ')[1]
103
+
104
+ init_log_action
105
+ #elsif line.match('Started') && line.match(' for ') && line.match(' at ') && line.split(' ').length == 9
106
+ # rails 3 start of action
107
+ # Started GET "/orders/815?truncate_length=1000" for 127.0.0.1 at 2011-10-04 19:58:44 +0200
108
+ elsif line.match('Processing by') && line.match('#') && line.match(' as ') && line.split(' ').length == 5
109
+ # rails 3 start of action
110
+ # Processing by OrdersController#show as HTML
111
+ @action = line.split(' ')[2]
112
+
113
+ init_log_action
114
+ elsif @action.blank?
115
+ elsif line.match('Parameters:') && line.match('{') && line.match('}')
116
+ line = line.split(' ')
117
+ line.pop
118
+ @log[@action]['Parameters'] = line.join(' ').strip
119
+ elsif line.match('INSERT INTO') || line.match('UPDATE')
120
+ table_name = table_name_from_line(line)
121
+
122
+ data = line.match('INSERT INTO') ? process_insert(line) : process_update(table_name, line)
123
+
124
+ # TaskTemplate Create (0.2ms) INSERT INTO `task_templates` (`slug`, `name`, `created_at`, `product_id`, `updated_at`, `customer_id`, `state`) VALUES('the code', 'a customer name', '2011-08-26 10:22:54', 2, '2011-08-26 10:22:54', 1002, 'draft')
125
+ #
126
+ # InputDataItem Update (0.3ms) UPDATE `data_items` SET `input` = '<opt>\n <input>\n <__dynamic_form__>\n <df_create>\n <the_input></the_input>\n <the_output>Output field 1</the_output>\n </df_create>\n </__dynamic_form__>\n </input>\n</opt>\n', `updated_at` = '2011-08-26 10:22:55' WHERE `id` = 5485
127
+
128
+
129
+ @log[@action]['WRITE'][table_name] ||= { 'columns' => ['id'], 'rows' => [] }
130
+ @writes[table_name] ||= { 'columns' => ['id'], 'rows' => [] }
131
+
132
+ @log[@action]['WRITE'][table_name]['columns'] = @log[@action]['WRITE'][table_name]['columns'].concat(data.keys).uniq
133
+ @writes[table_name]['columns'] = @writes[table_name]['columns'].concat(data.keys).uniq
134
+ @log[@action]['WRITE'][table_name]['rows'] << data
135
+ @writes[table_name]['rows'] << data
136
+ elsif (line.match('Load') && line.match('SELECT')) || (line.match('CACHE') && line.match('\(') && line.match('ms\)'))
137
+ line = line.split(')')
138
+ line.shift
139
+ @log[@action]['READ'] ||= []
140
+ @log[@action]['READ'] << line.join(')').strip
141
+ elsif line.match('Rendered') && line.match('\(') && line.match('ms\)')
142
+ @log[@action]['Views'] ||= []
143
+ line = line.split('Rendered')
144
+ line.shift
145
+ @log[@action]['Views'] << line.join('Rendered').split('(').first.strip
146
+ elsif line.match('Rendering')
147
+ @log[@action]['Views'] ||= []
148
+ line = line.split('Rendering')
149
+ line.shift
150
+ @log[@action]['Views'] << line.join('').strip
151
+ elsif (
152
+ line.match('SHOW FIELDS FROM') || (line.match('SQL \(') && line.match('ms\)'))
153
+ )
154
+ else
155
+ @log[@action]['misc'] << line
156
+ end
157
+
158
+ @last_action = @action
159
+ end
160
+
161
+ def table_name_from_line(line)
162
+ line = line.split(')')
163
+ line.shift
164
+ line = line.join(')')
165
+
166
+ line = line.split('`')
167
+ line[1]
168
+ end
169
+
170
+ def process_insert(line)
171
+ data = {}
172
+
173
+ columns = line.match(/\(`(.)+\)(( |)VALUES)/)[0].split('VALUES').first.strip.gsub('`', '')[1..-2].split(',').map(&:strip)
174
+
175
+ cells = nil
176
+
177
+ #begin
178
+ cells = line.match(/(VALUES(| )\()(.)+\)/)[0].split('VALUES').last.strip.
179
+ gsub(/\('/, '(').
180
+ gsub(/,(| )'/, ",").
181
+ gsub(/',(| )'/, ",").
182
+ gsub(/',(| )/, ",").
183
+ strip[1..-2].split(',').map(&:strip)
184
+ #rescue
185
+ # raise [line, line.match(/(VALUES(| )\()(.)+\)/)].inspect
186
+ #end
187
+
188
+ columns.each_index {|column_index| data[columns[column_index]] = cells[column_index]}
189
+
190
+ data
191
+ end
192
+
193
+ def process_update(table_name, line)
194
+ data = {}
195
+
196
+ # SET `input` = '<opt>\n <input>\n <__dynamic_form__>\n <df_create>\n <the_input></the_input>\n <the_output>Output field 1</the_output>\n </df_create>\n </__dynamic_form__>\n </input>\n</opt>\n', `updated_at` = '2011-08-26 10:22:55' WHERE `id` = 5485
197
+ line = line.split('WHERE')
198
+ data_string = line.first
199
+ conditions = line.last
200
+ data_string = data_string.split(' '); data_string.shift; data_string = data_string.join(' ')
201
+ data_string = data_string.gsub("', `", "||||").gsub(", `", '||||').gsub("` = '", "=").split('||||')
202
+
203
+ data_string.each do |data_element|
204
+ data_element = data_element.split('=')
205
+ data[data_element.shift.gsub('`', '').strip] = data_element.join('=')
206
+ end
207
+
208
+ if conditions.match("`#{table_name}`.`id` = ([0-9]+)")
209
+ data['id'] = conditions.match("`#{table_name}`.`id` = ([0-9]+)")[0].split('=').last.strip
210
+ elsif conditions.match(/`id` = ([0-9]+)/)
211
+ data['id'] = conditions.match(/`id` = ([0-9]+)/)[0].split('=').last.strip
212
+ end
213
+
214
+ data['WHERE'] = conditions
215
+
216
+ data
217
+ end
218
+
219
+ def init_log_action
220
+ if @action_indexes.has_key?(@action)
221
+ @action_indexes[@action] = @action_indexes[@action] + 1
222
+ else
223
+ @action_indexes[@action] = 1
224
+ end
225
+
226
+ @action = "#{@action} ##{@action_indexes[@action]}"
227
+
228
+ @log[@action] ||= { 'Parameters' => '', 'WRITE' => {}, 'READ' => [], 'views' => [], 'misc' => [], 'errors' => []}
229
+ end
230
+ end
@@ -0,0 +1,2 @@
1
+ module RailsInfo::Logs::Test
2
+ end
@@ -0,0 +1,160 @@
1
+ class RailsInfo::Logs::Test::Rspec
2
+ FILE_NAME_REGEXP = /[a-zA-Z]|[0-9]|\/|\.|_|-/
3
+
4
+ def initialize(options = {log: {}, debug: false})
5
+ options ||= {log: {}, debug: false}
6
+ options[:log] ||= {}
7
+
8
+ @rails_root = options[:log][:rails_root] || Rails.root.to_s
9
+ @body = options[:log][:body]
10
+ @show_all = options[:log][:show_all] || false
11
+ @debug = options[:debug]
12
+
13
+ unless @body
14
+ file_path = "#{@rails_root}/log/rspec.log"
15
+
16
+ @body = File.new(file_path, 'r').read if File.exist?(file_path)
17
+ end
18
+
19
+ @log ||= {}
20
+
21
+ process if @body.present?
22
+ end
23
+
24
+ def summary
25
+ @summary
26
+ end
27
+
28
+ def hash
29
+ @log
30
+ end
31
+
32
+ def rails_root
33
+ @rails_root
34
+ end
35
+
36
+ private
37
+
38
+ def process
39
+ @body = @body.split("\n").map(&:strip)
40
+
41
+ failures_found, line_index = false, -1
42
+ example, failure_code, exception_class, exception_message, stack_trace = nil, nil, nil, nil, []
43
+ after_stack_trace_entry = nil
44
+
45
+ @body.each do |line|
46
+ line_index += 1
47
+
48
+ if line.match('Failures:')
49
+ failures_found = true
50
+
51
+ next
52
+ elsif line.match(/Finished in/) && @body[line_index + 1].match(/examples|failures|pending/)
53
+ add_entry(example, failure_code, exception_class, exception_message, stack_trace, after_stack_trace_entry)
54
+
55
+ @summary = @body[line_index + 1]
56
+
57
+ break
58
+ elsif line.blank?
59
+ next
60
+ end
61
+
62
+ if failures_found && line.match(/^[0-9]+\)( ){1}([a-zA-Z]){1}/)
63
+ if example.present?
64
+ add_entry(example, failure_code, exception_class, exception_message, stack_trace, after_stack_trace_entry)
65
+ end
66
+
67
+ after_stack_trace_entry, stack_trace = nil, []
68
+
69
+ # 1) Community::CronJobs::Statistics.total_feedbacks_one_hour_ago principally works
70
+
71
+ =begin
72
+ line = line.split(')').second.strip.split(' ')
73
+ line.shift
74
+ example = line.join(' ') # principally works
75
+ =end
76
+
77
+ example = line.split(')').second.strip
78
+
79
+ #Failure/Error: Feedback.make!(community: @community, user: @user)
80
+ failure_code = @body[line_index + 1]
81
+ #oMethodError:
82
+ exception_class = @body[line_index + 2].split(':').first.strip
83
+
84
+ if exception_class == 'expected' || @body[line_index + 3].split(':').first.strip == 'expected'
85
+ #Reaction it should behave like objects that are 'hidable'#text_or_reason_for_hiding should return a notice if the entry has been hidden
86
+ #Failure/Error: subject.content_or_reason_for_hiding.should == "Dieser Beitrag wurde am 2000-01-01 21:15:01 +0100 von Johann Wolfgang von Goethe gelöscht. Der Grund war: This entry sucks!"
87
+ #expected: "Dieser Beitrag wurde am 2000-01-01 21:15:01 +0100 von Johann Wolfgang von Goethe gelöscht. Der Grund war: This entry sucks!"
88
+ #got: "Dieser Beitrag wurde am 2000-01-01 20:15:01 +0000 von Johann Wolfgang von Goethe gelöscht. Der Grund war: This entry sucks!" (using ==)
89
+ #Shared Example Group: "objects that are 'hidable'" called from ./spec/models/shared/hidable_trait_spec.rb:113
90
+ ## ./spec/models/shared/hidable_trait_spec.rb:102:in `block (3 levels) in <top (required)>'
91
+ exception_class, exception_message, after_stack_trace_entry = alternative_exception_message(line_index, 2..4)
92
+ else
93
+ #undefined method `moderators' for nil:NilClass
94
+ exception_message = @body[line_index + 3]
95
+ end
96
+ elsif failures_found && line.match("^#( ){1}\.\/(#{FILE_NAME_REGEXP}){1,}:[0-9]{1,}:in( ){1}`((.){1,})'")
97
+ ## ./app/models/notification/email.rb:38:in `block in receivers'
98
+ stack_trace << line
99
+ end
100
+ end
101
+ end
102
+
103
+ def add_entry(example, failure_code, exception_class, exception_message, stack_trace, after_stack_trace_entry = nil)
104
+ stack_trace << after_stack_trace_entry if after_stack_trace_entry
105
+
106
+ return unless stack_trace.any?
107
+
108
+ # from spec line of code down to application line of code
109
+ stack_trace.reverse!
110
+ file_name = stack_trace.first.match("^#( ){1}(\.\/(#{FILE_NAME_REGEXP}){1,})")[2]
111
+
112
+ @log[file_name] ||= {}
113
+
114
+ if example.match(/\./) && example.split('.').length == 2 && !example.split('.').first.match(/ |'/)
115
+ #Community::CronJobs::Statistics.total_feedbacks_one_hour_ago principally works
116
+ example = ".#{example.split('.').second.strip}"
117
+ elsif example.match(/#/) && example.split('#').length == 2 && !example.split('#').first.match(/ |'/)
118
+ #Community::CronJobs::Statistics#total_feedbacks_one_hour_ago principally works
119
+ example = "##{example.split('#').second.strip}"
120
+ elsif example.split(' ').length == 1
121
+ #Reaction
122
+ #Failure/Error: it {should have_one(:spam_flag)}
123
+
124
+ # make example unique by setting it to the failure code
125
+ example = failure_code
126
+ elsif example.split(' -').length == 2
127
+ #Feedback -validations
128
+ #Failure/Error: it { should belong_to(:community) }
129
+
130
+ # make example unique by including failure code
131
+ example += " #{failure_code}"
132
+ end
133
+
134
+ if @log[file_name].has_key?(example)
135
+ raise NotImplementedError.new(
136
+ "RSpec file #{file_name} not expected to have more than 1 example named #{example.inspect}"
137
+ )
138
+ end
139
+
140
+ @log[file_name][example] = {
141
+ failure_code: failure_code, exception_class: exception_class, exception_message: exception_message,
142
+ stack_trace: stack_trace.join('\n')
143
+ }
144
+ end
145
+
146
+ def alternative_exception_message(line_index, span)
147
+ exception_class = '', after_stack_trace_entry = nil
148
+
149
+ exception_message = span.to_a.map {|i| @body[line_index + i] }
150
+
151
+ if exception_message.last.match("^(.){1,}( ){1}called from( ){1}\.(#{FILE_NAME_REGEXP}){1,}:([0-9]){1,}$")
152
+ stack_trace_entry = exception_message.last.split('called from').last.strip
153
+ after_stack_trace_entry = "# #{stack_trace_entry}:in `SORRY_BUT_NOT_PASSED_BY_RSPEC'"
154
+ end
155
+
156
+ exception_message = exception_message.join('\n')
157
+
158
+ [exception_class, exception_message, after_stack_trace_entry]
159
+ end
160
+ end