rails_info 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +58 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/rails_info/application.js +6 -0
- data/app/assets/javascripts/rails_info/base.js.coffee +6 -0
- data/app/assets/javascripts/rails_info/bootstrap.js.coffee +4 -0
- data/app/assets/stylesheets/rails_info/application.css +14 -0
- data/app/assets/stylesheets/rails_info/base.css.scss +22 -0
- data/app/assets/stylesheets/rails_info/bootstrap_and_overrides.css.less +63 -0
- data/app/assets/stylesheets/rails_info/exception.css +17 -0
- data/app/assets/stylesheets/rails_info/exception/base.css.scss +31 -0
- data/app/assets/stylesheets/rails_info/exception/pygments.css.erb +5 -0
- data/app/controllers/rails_info/data_controller.rb +9 -0
- data/app/controllers/rails_info/logs/server_controller.rb +10 -0
- data/app/controllers/rails_info/logs/test/rspec_controller.rb +16 -0
- data/app/controllers/rails_info/model_controller.rb +5 -0
- data/app/controllers/rails_info/properties_controller.rb +4 -0
- data/app/controllers/rails_info/routes_controller.rb +4 -0
- data/app/controllers/rails_info/stack_traces_controller.rb +11 -0
- data/app/controllers/rails_info_controller.rb +16 -0
- data/app/helpers/rails_info/resources_helper.rb +194 -0
- data/app/presenters/rails_info/code_presenter.rb +63 -0
- data/app/presenters/rails_info/data/object_presenter.rb +34 -0
- data/app/presenters/rails_info/data/row_set_presenter.rb +38 -0
- data/app/presenters/rails_info/data_presenter.rb +21 -0
- data/app/presenters/rails_info/logs/server/action_presenter.rb +109 -0
- data/app/presenters/rails_info/logs/server_presenter.rb +57 -0
- data/app/presenters/rails_info/logs/test/rspec/file_presenter.rb +87 -0
- data/app/presenters/rails_info/logs/test/rspec_presenter.rb +32 -0
- data/app/presenters/rails_info/model_presenter.rb +68 -0
- data/app/presenters/rails_info/presenter.rb +19 -0
- data/app/presenters/rails_info/stack_trace_presenter.rb +98 -0
- data/app/views/layouts/_layout.html.erb +19 -0
- data/app/views/layouts/rails_info.html.erb +1 -0
- data/app/views/layouts/rails_info/exception.html.erb +1 -0
- data/app/views/layouts/shared/_navigation.html.erb +7 -0
- data/app/views/rails_info/data/index.html.erb +23 -0
- data/app/views/rails_info/logs/server/_table.html.erb +16 -0
- data/app/views/rails_info/logs/server/new.html.erb +19 -0
- data/app/views/rails_info/logs/test/rspec/new.html.erb +10 -0
- data/app/views/rails_info/model/index.html.erb +14 -0
- data/app/views/rails_info/properties/index.html.erb +1 -0
- data/app/views/rails_info/routes/index.html.erb +18 -0
- data/app/views/rails_info/stack_traces/_accordion.html.erb +1 -0
- data/app/views/rails_info/stack_traces/_form.html.erb +11 -0
- data/app/views/rails_info/stack_traces/_with_request_and_response.html.erb +16 -0
- data/app/views/rails_info/stack_traces/new.html.erb +10 -0
- data/config/navigation.rb +20 -0
- data/config/routes.rb +20 -0
- data/lib/rails_info.rb +19 -0
- data/lib/rails_info/controller.rb +2 -0
- data/lib/rails_info/controller/exception_diagnostics.rb +28 -0
- data/lib/rails_info/data.rb +55 -0
- data/lib/rails_info/engine.rb +9 -0
- data/lib/rails_info/logs.rb +2 -0
- data/lib/rails_info/logs/server.rb +230 -0
- data/lib/rails_info/logs/test.rb +2 -0
- data/lib/rails_info/logs/test/rspec.rb +160 -0
- data/lib/rails_info/model.rb +221 -0
- data/lib/rails_info/stack_trace.rb +155 -0
- data/lib/rails_info/version.rb +3 -0
- data/lib/tasks/rails_info_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +42 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +39 -0
- data/spec/dummy/config/environments/production.rb +69 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/dummy/log/development.log +4876 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- metadata +274 -0
@@ -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,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,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
|