rails6-footnotes 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +25 -0
- data/.gitignore +15 -0
- data/.rspec.example +1 -0
- data/CHANGELOG +129 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +187 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +188 -0
- data/Rakefile +18 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/gemfiles/Gemfile.rails-3.2.22 +5 -0
- data/gemfiles/Gemfile.rails-4.0.x +6 -0
- data/gemfiles/Gemfile.rails-4.1.x +5 -0
- data/gemfiles/Gemfile.rails-4.2.x +5 -0
- data/gemfiles/Gemfile.rails-edge +5 -0
- data/lib/generators/rails_footnotes/install_generator.rb +14 -0
- data/lib/generators/templates/rails_footnotes.rb +26 -0
- data/lib/rails-footnotes.rb +68 -0
- data/lib/rails-footnotes/abstract_note.rb +178 -0
- data/lib/rails-footnotes/each_with_rescue.rb +36 -0
- data/lib/rails-footnotes/extension.rb +24 -0
- data/lib/rails-footnotes/filter.rb +359 -0
- data/lib/rails-footnotes/notes/all.rb +1 -0
- data/lib/rails-footnotes/notes/assigns_note.rb +60 -0
- data/lib/rails-footnotes/notes/controller_note.rb +55 -0
- data/lib/rails-footnotes/notes/cookies_note.rb +17 -0
- data/lib/rails-footnotes/notes/env_note.rb +24 -0
- data/lib/rails-footnotes/notes/files_note.rb +49 -0
- data/lib/rails-footnotes/notes/filters_note.rb +51 -0
- data/lib/rails-footnotes/notes/javascripts_note.rb +16 -0
- data/lib/rails-footnotes/notes/layout_note.rb +26 -0
- data/lib/rails-footnotes/notes/log_note.rb +47 -0
- data/lib/rails-footnotes/notes/log_note/note_logger.rb +59 -0
- data/lib/rails-footnotes/notes/params_note.rb +21 -0
- data/lib/rails-footnotes/notes/partials_note.rb +38 -0
- data/lib/rails-footnotes/notes/queries_note.rb +121 -0
- data/lib/rails-footnotes/notes/routes_note.rb +60 -0
- data/lib/rails-footnotes/notes/session_note.rb +27 -0
- data/lib/rails-footnotes/notes/stylesheets_note.rb +16 -0
- data/lib/rails-footnotes/notes/view_note.rb +41 -0
- data/lib/rails-footnotes/version.rb +3 -0
- data/lib/rails6-footnotes.rb +1 -0
- data/rails-footnotes.gemspec +22 -0
- data/spec/abstract_note_spec.rb +89 -0
- data/spec/app/assets/config/manifest.js +2 -0
- data/spec/app/assets/javascripts/foobar.js +1 -0
- data/spec/app/assets/stylesheets/foobar.css +0 -0
- data/spec/app/views/files/index.html.erb +1 -0
- data/spec/app/views/layouts/application.html.erb +12 -0
- data/spec/app/views/partials/_foo.html.erb +1 -0
- data/spec/app/views/partials/index.html.erb +1 -0
- data/spec/controllers/files_note_controller_spec.rb +38 -0
- data/spec/controllers/footnotes_controller_spec.rb +128 -0
- data/spec/controllers/log_note_controller_spec.rb +32 -0
- data/spec/controllers/partials_note_controller_spec.rb +28 -0
- data/spec/env_note_spec.rb +73 -0
- data/spec/fixtures/html_download.html +5 -0
- data/spec/footnotes_spec.rb +234 -0
- data/spec/notes/assigns_note_spec.rb +50 -0
- data/spec/notes/controller_note_spec.rb +12 -0
- data/spec/notes/files_note_spec.rb +26 -0
- data/spec/notes/javascripts_note_spec.rb +18 -0
- data/spec/notes/stylesheets_note_spec.rb +19 -0
- data/spec/notes/view_note_spec.rb +12 -0
- data/spec/spec_helper.rb +68 -0
- metadata +153 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Footnotes
|
4
|
+
module RailsFootnotesExtension
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
prepend_before_action :rails_footnotes_before_filter
|
9
|
+
after_action :rails_footnotes_after_filter
|
10
|
+
end
|
11
|
+
|
12
|
+
def rails_footnotes_before_filter
|
13
|
+
Footnotes::Filter.start!(self) if Footnotes.enabled?(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def rails_footnotes_after_filter
|
17
|
+
return unless Footnotes.enabled?(self)
|
18
|
+
|
19
|
+
filter = Footnotes::Filter.new(self)
|
20
|
+
filter.add_footnotes!
|
21
|
+
filter.close!(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,359 @@
|
|
1
|
+
module Footnotes
|
2
|
+
class Filter
|
3
|
+
@@no_style = false
|
4
|
+
@@multiple_notes = false
|
5
|
+
@@klasses = []
|
6
|
+
@@lock_top_right = false
|
7
|
+
@@font_size = '11px'
|
8
|
+
|
9
|
+
# Default link prefix is textmate
|
10
|
+
@@prefix = 'txmt://open?url=file://%s&line=%d&column=%d'
|
11
|
+
|
12
|
+
# Edit notes
|
13
|
+
@@notes = [ :controller, :view, :layout, :partials, :stylesheets, :javascripts ]
|
14
|
+
# Show notes
|
15
|
+
@@notes += [ :assigns, :session, :cookies, :params, :filters, :routes, :env, :queries, :log]
|
16
|
+
|
17
|
+
# :no_style => If you don't want the style to be appended to your pages
|
18
|
+
# :notes => Class variable that holds the notes to be processed
|
19
|
+
# :prefix => Prefix appended to FootnotesLinks
|
20
|
+
# :multiple_notes => Set to true if you want to open several notes at the same time
|
21
|
+
# :lock_top_right => Lock a btn to toggle notes to the top right of the browser
|
22
|
+
# :font_size => CSS font-size property
|
23
|
+
cattr_accessor :no_style, :notes, :prefix, :multiple_notes, :lock_top_right, :font_size
|
24
|
+
|
25
|
+
class << self
|
26
|
+
include Footnotes::EachWithRescue
|
27
|
+
|
28
|
+
# Calls the class method start! in each note
|
29
|
+
# Sometimes notes need to set variables or clean the environment to work properly
|
30
|
+
# This method allows this kind of setup
|
31
|
+
#
|
32
|
+
def start!(controller)
|
33
|
+
self.each_with_rescue(Footnotes.before_hooks) {|hook| hook.call(controller, self)}
|
34
|
+
|
35
|
+
@@klasses = []
|
36
|
+
self.each_with_rescue(@@notes.flatten) do |note|
|
37
|
+
klass = "Footnotes::Notes::#{note.to_s.camelize}Note".constantize
|
38
|
+
klass.start!(controller) if klass.respond_to?(:start!)
|
39
|
+
@@klasses << klass
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# If none argument is sent, simply return the prefix.
|
44
|
+
# Otherwise, replace the args in the prefix.
|
45
|
+
#
|
46
|
+
def prefix(*args)
|
47
|
+
if args.empty?
|
48
|
+
@@prefix
|
49
|
+
else
|
50
|
+
args.map! { |arg| arg.to_s.split("/").map{|s| ERB::Util.url_encode(s) }.join("/") }
|
51
|
+
|
52
|
+
if @@prefix.respond_to? :call
|
53
|
+
@@prefix.call *args
|
54
|
+
else
|
55
|
+
format(@@prefix, *args)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(controller)
|
63
|
+
@controller = controller
|
64
|
+
@template = controller.instance_variable_get(:@template)
|
65
|
+
@notes = []
|
66
|
+
|
67
|
+
revert_pos(controller.response_body) do
|
68
|
+
@body = controller.response.body
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_footnotes!
|
73
|
+
add_footnotes_without_validation! if valid?
|
74
|
+
rescue Exception => e
|
75
|
+
# Discard footnotes if there are any problems
|
76
|
+
self.class.log_error("Footnotes Exception", e)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Calls the class method close! in each note
|
80
|
+
# Sometimes notes need to finish their work even after being read
|
81
|
+
# This method allows this kind of work
|
82
|
+
#
|
83
|
+
def close!(controller)
|
84
|
+
self.each_with_rescue(@@klasses) {|klass| klass.close!(controller)}
|
85
|
+
self.each_with_rescue(Footnotes.after_hooks) {|hook| hook.call(controller, self)}
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
def valid?
|
90
|
+
@body.is_a?(String) && performed_render? && valid_format? && valid_content_type? &&
|
91
|
+
!component_request? && !xhr? && !footnotes_disabled? && !attached_file?
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_footnotes_without_validation!
|
95
|
+
initialize_notes!
|
96
|
+
insert_styles unless @@no_style
|
97
|
+
insert_footnotes
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize_notes!
|
101
|
+
each_with_rescue(@@klasses) do |klass|
|
102
|
+
note = klass.new(@controller)
|
103
|
+
@notes << note if note.valid?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def revert_pos(file)
|
108
|
+
return yield unless file.respond_to?(:pos) && file.respond_to?(:pos=)
|
109
|
+
original = file.pos
|
110
|
+
yield
|
111
|
+
file.pos = original
|
112
|
+
end
|
113
|
+
|
114
|
+
def performed_render?
|
115
|
+
@controller.respond_to?(:performed?) && @controller.performed?
|
116
|
+
end
|
117
|
+
|
118
|
+
def valid_format?
|
119
|
+
format = @controller.response.content_type
|
120
|
+
format.nil? || format.include?("text/html")
|
121
|
+
end
|
122
|
+
|
123
|
+
def valid_content_type?
|
124
|
+
c = @controller.response.headers['Content-Type'].to_s
|
125
|
+
(c.empty? || c =~ /html/)
|
126
|
+
end
|
127
|
+
|
128
|
+
def component_request?
|
129
|
+
@controller.instance_variable_get(:@parent_controller)
|
130
|
+
end
|
131
|
+
|
132
|
+
def xhr?
|
133
|
+
@controller.request.xhr?
|
134
|
+
end
|
135
|
+
|
136
|
+
def footnotes_disabled?
|
137
|
+
@controller.params[:footnotes] == "false"
|
138
|
+
end
|
139
|
+
|
140
|
+
def attached_file?
|
141
|
+
!!(@controller.headers['Content-Disposition'] =~ /attachment/)
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Insertion methods
|
146
|
+
#
|
147
|
+
|
148
|
+
def insert_styles
|
149
|
+
#TODO More customizable(reset.css, from file etc.)
|
150
|
+
if @@lock_top_right
|
151
|
+
extra_styles = <<-STYLES
|
152
|
+
#footnotes_debug {position: fixed; top: 0px; right: 0px; width: 100%; z-index: 10000; margin-top: 0;}
|
153
|
+
#footnotes_debug #toggle_footnotes {position: absolute; right: 0; top: 0; background: #fff; border: 1px solid #ccc; color: #9b1b1b; font-size: 20px; text-align: center; padding: 8px; opacity: 0.9;}
|
154
|
+
#footnotes_debug #toggle_footnotes:hover {opacity: 1;}
|
155
|
+
#footnotes_debug #all_footnotes {display: none; padding: 15px; background: #fff; box-shadow: 0 0 5px rgba(0,0,0,0.4);}
|
156
|
+
#footnotes_debug fieldset > div {max-height: 500px; overflow: scroll;}
|
157
|
+
STYLES
|
158
|
+
else
|
159
|
+
extra_styles = <<-STYLES
|
160
|
+
#footnotes_debug #toggle_footnotes {display: none;}
|
161
|
+
STYLES
|
162
|
+
end
|
163
|
+
insert_text :before, /<\/head>/i, <<-HTML
|
164
|
+
<!-- Footnotes Style -->
|
165
|
+
<style type="text/css">
|
166
|
+
#footnotes_debug {font-size: #{@@font_size}; font-family: Consolas, monaco, monospace; font-weight: normal; margin: 2em 0 1em 0; text-align: center; color: #444; line-height: 16px; background: #fff;}
|
167
|
+
#footnotes_debug th, #footnotes_debug td {color: #444; line-height: 18px;}
|
168
|
+
#footnotes_debug a {color: #9b1b1b; font-weight: inherit; text-decoration: none; line-height: 18px;}
|
169
|
+
#footnotes_debug table {text-align: left; width: 100%;}
|
170
|
+
#footnotes_debug table td {padding: 5px; border-bottom: 1px solid #ccc;}
|
171
|
+
#footnotes_debug table td strong {color: #9b1b1b;}
|
172
|
+
#footnotes_debug table th {padding: 5px; border-bottom: 1px solid #ccc;}
|
173
|
+
#footnotes_debug table tr:nth-child(2n) td {background: #f5f5f5;}
|
174
|
+
#footnotes_debug table tr:nth-child(2n + 1) td {background: #fff;}
|
175
|
+
#footnotes_debug tbody {text-align: left;}
|
176
|
+
#footnotes_debug .name_values td {vertical-align: top;}
|
177
|
+
#footnotes_debug legend {background-color: #fff;}
|
178
|
+
#footnotes_debug fieldset {text-align: left; border: 1px dashed #aaa; padding: 0.5em 1em 1em 1em; margin: 1em 2em; color: #444; background-color: #FFF;}
|
179
|
+
#{extra_styles}
|
180
|
+
/* Aditional Stylesheets */
|
181
|
+
#{@notes.map(&:stylesheet).compact.join("\n")}
|
182
|
+
</style>
|
183
|
+
<!-- End Footnotes Style -->
|
184
|
+
HTML
|
185
|
+
end
|
186
|
+
|
187
|
+
def insert_footnotes
|
188
|
+
# Fieldsets method should be called first
|
189
|
+
content = fieldsets
|
190
|
+
element_style = ''
|
191
|
+
if @@lock_top_right
|
192
|
+
element_style = 'style="display: none;"'
|
193
|
+
end
|
194
|
+
footnotes_html = <<-HTML
|
195
|
+
<!-- Footnotes -->
|
196
|
+
<div style="clear:both"></div>
|
197
|
+
<div id="footnotes_debug">
|
198
|
+
<a id="toggle_footnotes" href="#" onclick="Footnotes.toggle('all_footnotes'); return false;">fn</a>
|
199
|
+
<div id="all_footnotes" #{element_style}>
|
200
|
+
#{links}
|
201
|
+
#{content}
|
202
|
+
</div>
|
203
|
+
<script type="text/javascript">
|
204
|
+
var Footnotes = function() {
|
205
|
+
|
206
|
+
function hideAll(){
|
207
|
+
#{close unless @@multiple_notes}
|
208
|
+
}
|
209
|
+
|
210
|
+
function hideAllAndToggle(id) {
|
211
|
+
var n = note(id);
|
212
|
+
var display = n.style.display;
|
213
|
+
hideAll();
|
214
|
+
// Restore original display to allow toggling
|
215
|
+
n.style.display = display;
|
216
|
+
toggle(id)
|
217
|
+
|
218
|
+
location.href = '#footnotes_debug';
|
219
|
+
}
|
220
|
+
|
221
|
+
function note(id) {
|
222
|
+
return (document.getElementById(id));
|
223
|
+
}
|
224
|
+
|
225
|
+
function toggle(id){
|
226
|
+
var el = note(id);
|
227
|
+
if (el.style.display == 'none') {
|
228
|
+
Footnotes.show(el);
|
229
|
+
} else {
|
230
|
+
Footnotes.hide(el);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
function show(element) {
|
235
|
+
element.style.display = 'block'
|
236
|
+
}
|
237
|
+
|
238
|
+
function hide(element) {
|
239
|
+
element.style.display = 'none'
|
240
|
+
}
|
241
|
+
|
242
|
+
return {
|
243
|
+
show: show,
|
244
|
+
hide: hide,
|
245
|
+
toggle: toggle,
|
246
|
+
hideAllAndToggle: hideAllAndToggle
|
247
|
+
}
|
248
|
+
}();
|
249
|
+
/* Additional Javascript */
|
250
|
+
#{@notes.map(&:javascript).compact.join("\n")}
|
251
|
+
</script>
|
252
|
+
</div>
|
253
|
+
<!-- End Footnotes -->
|
254
|
+
HTML
|
255
|
+
|
256
|
+
placeholder = /<div[^>]+id=['"]footnotes_holder['"][^>]*>/i
|
257
|
+
if @controller.response.body =~ placeholder
|
258
|
+
insert_text :after, placeholder, footnotes_html
|
259
|
+
else
|
260
|
+
insert_text :before, /<\/body>/i, footnotes_html
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Process notes to gets their links in their equivalent row
|
265
|
+
#
|
266
|
+
def links
|
267
|
+
links = Hash.new([])
|
268
|
+
order = []
|
269
|
+
each_with_rescue(@notes) do |note|
|
270
|
+
order << note.row
|
271
|
+
links[note.row] += [link_helper(note)]
|
272
|
+
end
|
273
|
+
|
274
|
+
html = ''
|
275
|
+
order.uniq!
|
276
|
+
order.each do |row|
|
277
|
+
html << "#{row.is_a?(String) ? row : row.to_s.camelize}: #{links[row].join(" | \n")}<br />"
|
278
|
+
end
|
279
|
+
html
|
280
|
+
end
|
281
|
+
|
282
|
+
# Process notes to get their content
|
283
|
+
#
|
284
|
+
def fieldsets
|
285
|
+
content = ''
|
286
|
+
each_with_rescue(@notes) do |note|
|
287
|
+
next unless note.has_fieldset?
|
288
|
+
content << <<-HTML
|
289
|
+
<fieldset id="#{note.to_sym}_debug_info" style="display: none">
|
290
|
+
<legend>#{note.legend}</legend>
|
291
|
+
<div>#{note.content}</div>
|
292
|
+
</fieldset>
|
293
|
+
HTML
|
294
|
+
end
|
295
|
+
content
|
296
|
+
end
|
297
|
+
|
298
|
+
# Process notes to get javascript code to close them.
|
299
|
+
# This method is only used when multiple_notes is false.
|
300
|
+
#
|
301
|
+
def close
|
302
|
+
javascript = ''
|
303
|
+
each_with_rescue(@notes) do |note|
|
304
|
+
next unless note.has_fieldset?
|
305
|
+
javascript << close_helper(note)
|
306
|
+
end
|
307
|
+
javascript
|
308
|
+
end
|
309
|
+
|
310
|
+
#
|
311
|
+
# Helpers
|
312
|
+
#
|
313
|
+
|
314
|
+
# Helper that creates the javascript code to close the note
|
315
|
+
#
|
316
|
+
def close_helper(note)
|
317
|
+
"Footnotes.hide(document.getElementById('#{note.to_sym}_debug_info'));\n"
|
318
|
+
end
|
319
|
+
|
320
|
+
# Helper that creates the link and javascript code when note is clicked
|
321
|
+
#
|
322
|
+
def link_helper(note)
|
323
|
+
onclick = note.onclick
|
324
|
+
unless href = note.link
|
325
|
+
href = '#'
|
326
|
+
onclick ||= "Footnotes.hideAllAndToggle('#{note.to_sym}_debug_info');return false;" if note.has_fieldset?
|
327
|
+
end
|
328
|
+
|
329
|
+
"<a href=\"#{href}\" onclick=\"#{onclick}\">#{note.title}</a>"
|
330
|
+
end
|
331
|
+
|
332
|
+
# Inserts text in to the body of the document
|
333
|
+
# +pattern+ is a Regular expression which, when matched, will cause +new_text+
|
334
|
+
# to be inserted before or after the match. If no match is found, +new_text+ is appended
|
335
|
+
# to the body instead. +position+ may be either :before or :after
|
336
|
+
#
|
337
|
+
def insert_text(position, pattern, new_text)
|
338
|
+
index = case pattern
|
339
|
+
when Regexp
|
340
|
+
if match = @controller.response.body.match(pattern)
|
341
|
+
match.offset(0)[position == :before ? 0 : 1]
|
342
|
+
else
|
343
|
+
@controller.response.body.size
|
344
|
+
end
|
345
|
+
else
|
346
|
+
pattern
|
347
|
+
end
|
348
|
+
newbody = @controller.response.body
|
349
|
+
newbody.insert index, new_text
|
350
|
+
@controller.response.body = newbody
|
351
|
+
end
|
352
|
+
|
353
|
+
# Instance each_with_rescue method
|
354
|
+
#
|
355
|
+
def each_with_rescue(*args, &block)
|
356
|
+
self.class.each_with_rescue(*args, &block)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '*_note.rb')].each {|note| require note}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Footnotes
|
2
|
+
module Notes
|
3
|
+
class AssignsNote < AbstractNote
|
4
|
+
@@ignored_assigns = [
|
5
|
+
:@real_format,
|
6
|
+
:@before_filter_chain_aborted,
|
7
|
+
:@performed_redirect,
|
8
|
+
:@performed_render,
|
9
|
+
:@_params,
|
10
|
+
:@_response,
|
11
|
+
:@url,
|
12
|
+
:@template,
|
13
|
+
:@_request,
|
14
|
+
:@db_rt_before_render,
|
15
|
+
:@db_rt_after_render,
|
16
|
+
:@view_runtime,
|
17
|
+
:@marked_for_same_origin_verification
|
18
|
+
]
|
19
|
+
cattr_accessor :ignored_assigns, :instance_writer => false
|
20
|
+
@@ignored_assigns_pattern = /^@_/
|
21
|
+
cattr_accessor :ignored_assigns_pattern, :instance_writer => false
|
22
|
+
|
23
|
+
def initialize(controller)
|
24
|
+
@controller = controller
|
25
|
+
end
|
26
|
+
|
27
|
+
def title
|
28
|
+
"Assigns (#{assigns.size})"
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
assigns.present?
|
33
|
+
end
|
34
|
+
|
35
|
+
def content
|
36
|
+
mount_table(to_table, :summary => "Debug information for #{title}")
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
def to_table
|
41
|
+
table = assigns.inject([]) do |rr, var|
|
42
|
+
class_name = assigned_value(var).class.name
|
43
|
+
var_name = var.to_s
|
44
|
+
rr << ["<strong>#{var.to_s}</strong>" + "<br /><em>#{class_name}</em>", escape(assigned_value(var).inspect)]
|
45
|
+
end
|
46
|
+
|
47
|
+
table.unshift(['Name', 'Value'])
|
48
|
+
end
|
49
|
+
|
50
|
+
def assigns
|
51
|
+
@assigns ||= @controller.instance_variables.map {|v| v.to_sym}.select {|v| v.to_s !~ ignored_assigns_pattern } - ignored_assigns
|
52
|
+
end
|
53
|
+
|
54
|
+
def assigned_value(key)
|
55
|
+
@controller.instance_variable_get(key)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Footnotes
|
2
|
+
module Notes
|
3
|
+
class ControllerNote < AbstractNote
|
4
|
+
def initialize(controller)
|
5
|
+
@controller = controller
|
6
|
+
end
|
7
|
+
|
8
|
+
def row
|
9
|
+
:edit
|
10
|
+
end
|
11
|
+
|
12
|
+
def link
|
13
|
+
Footnotes::Filter.prefix(controller_filename, controller_line_number + 1, 3)
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
prefix? && controller_filename && File.exists?(controller_filename)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def controller_path
|
22
|
+
@controller_path = @controller.class.name.underscore
|
23
|
+
end
|
24
|
+
|
25
|
+
def controller_filename
|
26
|
+
@controller_filename ||= Gem.find_files(controller_path).first # tnx https://github.com/MasterLambaster
|
27
|
+
end
|
28
|
+
|
29
|
+
def controller_text
|
30
|
+
@controller_text ||= IO.read(controller_filename)
|
31
|
+
end
|
32
|
+
|
33
|
+
def action_index
|
34
|
+
(controller_text =~ /def\s+#{@controller.action_name}[\s\(]/)
|
35
|
+
end
|
36
|
+
|
37
|
+
def controller_line_number
|
38
|
+
lines_from_index(controller_text, action_index) || 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def lines_from_index(string, index)
|
42
|
+
return nil if string.blank? || index.blank?
|
43
|
+
|
44
|
+
lines = string.respond_to?(:to_a) ? string.to_a : string.lines.to_a
|
45
|
+
running_length = 0
|
46
|
+
lines.each_with_index do |line, i|
|
47
|
+
running_length += line.length
|
48
|
+
if running_length > index
|
49
|
+
return i
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|