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.
- 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
|