rails-footnotes 3.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
1
+ module Footnotes
2
+ module Notes
3
+ # This is the abstract class for notes.
4
+ # You can overwrite all instance public methods to create your notes.
5
+ #
6
+ class AbstractNote
7
+
8
+ # Class methods. Do NOT overwrite them.
9
+ #
10
+ class << self
11
+ # Returns the symbol that represents this note.
12
+ # It's the name of the class, underscored and without _note.
13
+ #
14
+ # For example, for ControllerNote it will return :controller.
15
+ #
16
+ def to_sym
17
+ @note_sym ||= self.title.underscore.to_sym
18
+ end
19
+
20
+ # Returns the title that represents this note.
21
+ # It's the name of the class without Note.
22
+ #
23
+ # For example, for ControllerNote it will return Controller.
24
+ #
25
+ def title
26
+ @note_title ||= self.name.match(/^Footnotes::Notes::(\w+)Note$/)[1]
27
+ end
28
+
29
+ # Return true if Note is included in notes array.
30
+ #
31
+ def included?
32
+ Footnotes::Filter.notes.include?(self.to_sym)
33
+ end
34
+
35
+ # Action to be called to start the Note.
36
+ # This is applied as a before_filter.
37
+ #
38
+ def start!(controller = nil)
39
+ end
40
+
41
+ # Action to be called after the Note was used.
42
+ # This is applied as an after_filter.
43
+ #
44
+ def close!(controller = nil)
45
+ end
46
+ end
47
+
48
+ # Initialize notes.
49
+ # Always receives a controller.
50
+ #
51
+ def initialize(controller = nil)
52
+ end
53
+
54
+ # Returns the symbol that represents this note.
55
+ #
56
+ def to_sym
57
+ self.class.to_sym
58
+ end
59
+
60
+ # Specifies in which row should appear the title.
61
+ # The default is :show.
62
+ #
63
+ def row
64
+ :show
65
+ end
66
+
67
+ # Returns the title to be used as link.
68
+ # The default is the note title.
69
+ #
70
+ def title
71
+ self.class.title
72
+ end
73
+
74
+ # If has_fieldset? is true, create a fieldset with the value returned as legend.
75
+ # By default, returns the title of the class (defined above).
76
+ #
77
+ def legend
78
+ self.class.title
79
+ end
80
+
81
+ # If content is defined, has_fieldset? returns true and the value of content
82
+ # is displayed when the Note is clicked. See has_fieldset? below for more info.
83
+ #
84
+ # def content
85
+ # end
86
+
87
+ # Set href field for Footnotes links.
88
+ # If it's nil, Footnotes will use '#'.
89
+ #
90
+ def link
91
+ end
92
+
93
+ # Set onclick field for Footnotes links.
94
+ # If it's nil, Footnotes will make it open the fieldset.
95
+ #
96
+ def onclick
97
+ end
98
+
99
+ # Insert here any additional stylesheet.
100
+ # This is directly inserted into a <style> tag.
101
+ #
102
+ def stylesheet
103
+ end
104
+
105
+ # Insert here any additional javascript.
106
+ # This is directly inserted into a <script> tag.
107
+ #
108
+ def javascript
109
+ end
110
+
111
+ # Specifies when should create a note for it.
112
+ # By default, it's valid.
113
+ #
114
+ def valid?
115
+ true
116
+ end
117
+
118
+ # Specifies when should create a fieldset for it, considering it's valid.
119
+ #
120
+ def has_fieldset?
121
+ self.respond_to?(:content)
122
+ end
123
+
124
+ # Some helpers to generate notes.
125
+ #
126
+ protected
127
+ # Return if Footnotes::Filter.prefix exists or not.
128
+ # Some notes only work with prefix set.
129
+ #
130
+ def prefix?
131
+ !Footnotes::Filter.prefix.blank?
132
+ end
133
+
134
+ # Escape HTML special characters.
135
+ #
136
+ def escape(text)
137
+ text.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
138
+ end
139
+
140
+ # Gets a bidimensional array and create a table.
141
+ # The first array is used as label.
142
+ #
143
+ def mount_table(array, options = {})
144
+ header = array.shift
145
+ return '' if array.empty?
146
+
147
+ header = header.collect{|i| escape(i.to_s.humanize) }
148
+ rows = array.collect{|i| "<tr><td>#{i.join('</td><td>')}</td></tr>" }
149
+
150
+ <<-TABLE
151
+ <table #{hash_to_xml_attributes(options)}>
152
+ <thead><tr><th>#{header.join('</th><th>')}</th></tr></thead>
153
+ <tbody>#{rows.join}</tbody>
154
+ </table>
155
+ TABLE
156
+ end
157
+
158
+ # Mount table for hash, using name and value and adding a name_value class
159
+ # to the generated table.
160
+ #
161
+ def mount_table_for_hash(hash)
162
+ rows = []
163
+ hash.each do |key, value|
164
+ rows << [ key.to_sym.inspect, escape(value.inspect) ]
165
+ end
166
+ mount_table(rows.unshift(['Name', 'Value']), :class => 'name_value')
167
+ end
168
+
169
+ def hash_to_xml_attributes(hash)
170
+ newstring = ""
171
+ hash.each do |key, value|
172
+ newstring << "#{key.to_s}=\"#{value.gsub('"','\"')}\" "
173
+ end
174
+ return newstring
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,44 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class AssignsNote < AbstractNote
6
+ @@ignored_assigns = %w( @template @_request @db_rt_before_render @db_rt_after_render @view_runtime )
7
+ cattr_accessor :ignored_assigns, :instance_writter => false
8
+
9
+ def initialize(controller)
10
+ @controller = controller
11
+ end
12
+
13
+ def title
14
+ "Assigns (#{assigns.size})"
15
+ end
16
+
17
+ def valid?
18
+ assigns
19
+ end
20
+
21
+ def content
22
+ rows = []
23
+ assigns.each do |key|
24
+ rows << [ key, assigned_value(key) ]
25
+ end
26
+ mount_table(rows.unshift(['Name', 'Value']), :class => 'name_values')
27
+ end
28
+
29
+ protected
30
+
31
+ def assigns
32
+ return @assigns if @assigns
33
+
34
+ @assigns = @controller.instance_variables
35
+ @assigns -= @controller.protected_instance_variables
36
+ @assigns -= ignored_assigns
37
+ end
38
+
39
+ def assigned_value(key)
40
+ escape(@controller.instance_variable_get(key).inspect)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,65 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class ControllerNote < AbstractNote
6
+ def initialize(controller)
7
+ @controller = controller
8
+ end
9
+
10
+ def row
11
+ :edit
12
+ end
13
+
14
+ def link
15
+ escape(Footnotes::Filter.prefix(controller_filename, controller_line_number + 1, 3))
16
+ end
17
+
18
+ def valid?
19
+ prefix?
20
+ end
21
+
22
+ protected
23
+ # Some controller classes come with the Controller:: module and some don't
24
+ # (anyone know why? -- Duane)
25
+ def controller_filename
26
+ controller_name=@controller.class.to_s.underscore
27
+ controller_name='application' if controller_name=='application_controller'
28
+ if ActionController::Routing.respond_to? :controller_paths
29
+ ActionController::Routing.controller_paths.each do |controller_path|
30
+ full_controller_path = File.join(File.expand_path(controller_path), "#{controller_name}.rb")
31
+ return full_controller_path if File.exists?(full_controller_path)
32
+ end
33
+ raise "File not found"
34
+ else
35
+ File.join(File.expand_path(RAILS_ROOT), 'app', 'controllers', "#{controller_name}.rb").sub('/controllers/controllers/', '/controllers/')
36
+ end
37
+ end
38
+
39
+ def controller_text
40
+ @controller_text ||= IO.read(controller_filename)
41
+ end
42
+
43
+ def action_index
44
+ (controller_text =~ /def\s+#{@controller.action_name}[\s\(]/)
45
+ end
46
+
47
+ def controller_line_number
48
+ lines_from_index(controller_text, action_index) || 0
49
+ end
50
+
51
+ def lines_from_index(string, index)
52
+ return nil if string.blank? || index.blank?
53
+
54
+ lines = string.to_a
55
+ running_length = 0
56
+ lines.each_with_index do |line, i|
57
+ running_length += line.length
58
+ if running_length > index
59
+ return i
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class CookiesNote < AbstractNote
6
+ def initialize(controller)
7
+ @cookies = (controller.__send__(:cookies) || {}).symbolize_keys
8
+ end
9
+
10
+ def title
11
+ "Cookies (#{@cookies.length})"
12
+ end
13
+
14
+ def content
15
+ mount_table_for_hash(@cookies)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class EnvNote < AbstractNote
6
+ def initialize(controller)
7
+ @env = controller.request.env.dup
8
+ end
9
+
10
+ def content
11
+ # Replace HTTP_COOKIE for a link
12
+ @env['HTTP_COOKIE'] = '<a href="#" style="color:#009" onclick="Footnotes.hideAllAndToggle(\'cookies_debug_info\');return false;">See cookies on its tab</a>'
13
+
14
+ # Create the env table
15
+ mount_table(@env.to_a.sort.unshift([:key, :value]))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class FilesNote < AbstractNote
6
+ def initialize(controller)
7
+ @files = scan_text(controller.response.body)
8
+ parse_files!
9
+ end
10
+
11
+ def row
12
+ :edit
13
+ end
14
+
15
+ def content
16
+ if @files.empty?
17
+ ""
18
+ else
19
+ "<ul><li>#{@files.join("</li><li>")}</li></ul>"
20
+ end
21
+ end
22
+
23
+ def valid?
24
+ prefix?
25
+ end
26
+
27
+ protected
28
+ def scan_text(text)
29
+ []
30
+ end
31
+
32
+ def parse_files!
33
+ @files.collect! do |filename|
34
+ if filename =~ %r{^/}
35
+ full_filename = File.join(File.expand_path(RAILS_ROOT), 'public', filename)
36
+ %[<a href="#{Footnotes::Filter.prefix(full_filename, 1, 1)}">#{filename}</a>]
37
+ else
38
+ %[<a href="#{filename}">#{filename}</a>]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class FiltersNote < AbstractNote
6
+ def initialize(controller)
7
+ @controller = controller
8
+ @parsed_filters = parse_filters
9
+ end
10
+
11
+ def legend
12
+ "Filter chain for #{@controller.class.to_s}"
13
+ end
14
+
15
+ def content
16
+ mount_table(@parsed_filters.unshift([:name, :type, :actions]))
17
+ end
18
+
19
+ protected
20
+ # Get controller filter chain
21
+ #
22
+ def parse_filters
23
+ return @controller.class.filter_chain.collect do |filter|
24
+ [parse_method(filter.method), filter.type.inspect, controller_filtered_actions(filter).inspect]
25
+ end
26
+ end
27
+
28
+ # This receives a filter, creates a mock controller and check in which
29
+ # actions the filter is performed
30
+ #
31
+ def controller_filtered_actions(filter)
32
+ mock_controller = Footnotes::Extensions::MockController.new
33
+
34
+ return @controller.class.action_methods.select { |action|
35
+ mock_controller.action_name = action
36
+
37
+ #remove conditions (this would call a Proc on the mock_controller)
38
+ filter.options.merge!(:if => nil, :unless => nil)
39
+
40
+ filter.__send__(:should_run_callback?, mock_controller)
41
+ }.map(&:to_sym)
42
+ end
43
+
44
+ def parse_method(method = '')
45
+ escape(method.inspect.gsub(RAILS_ROOT, ''))
46
+ end
47
+ end
48
+ end
49
+
50
+ module Extensions
51
+ class MockController < Struct.new(:action_name); end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module Footnotes
4
+ module Notes
5
+ class GeneralNote < AbstractNote
6
+ def title
7
+ 'General Debug'
8
+ end
9
+
10
+ def legend
11
+ 'General (id="general_debug_info")'
12
+ end
13
+
14
+ def content
15
+ 'You can use this tab to debug other parts of your application, for example Javascript.'
16
+ end
17
+ end
18
+ end
19
+ end