merb_footnotes 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,134 @@
1
+ # Copyright (c) 2008 FiveRuns Corporation
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'pp'
22
+ module MerbFootnotes
23
+ module Instrumentation
24
+
25
+ def self.pretty(value)
26
+ CGI.escapeHTML(PP.pp(value, ''))
27
+ end
28
+
29
+ def self.format_sql(query, statement, attributes = nil)
30
+ values = query.bind_values + (attributes ? attributes.values : [])
31
+ [statement, "<b>Values:</b> " + CGI.escapeHTML(values.inspect)].join("<br/>")
32
+ end
33
+
34
+ def self.attrs_for(query)
35
+ [
36
+ [ :repository, query.repository.name ],
37
+ [ :model, query.model ],
38
+ [ :fields, query.fields ],
39
+ [ :links, query.links ],
40
+ [ :conditions, query.conditions ],
41
+ [ :order, query.order ],
42
+ [ :limit, query.limit ],
43
+ [ :offset, query.offset ],
44
+ [ :reload, query.reload? ],
45
+ [ :unique, query.unique? ]
46
+ ]
47
+ end
48
+
49
+ def self.format_query(query)
50
+ rows = attrs_for(query).map do |set|
51
+ %(<tr><th>%s</th><td><pre>%s</pre></td></tr>) % set.map { |item|
52
+ pretty item
53
+ }
54
+ end
55
+ "<table>%s</table>" % rows.join
56
+ end
57
+
58
+ module Merb
59
+ module Controller
60
+ def new(*args, &block)
61
+ super.extend(Ext)
62
+ end
63
+
64
+ module Ext
65
+
66
+ def footnotes
67
+ @_footnotes || {}
68
+ end
69
+
70
+ def add_footnote(bucket, content)
71
+ @_footnotes ||= {}
72
+ @_footnotes[bucket.to_sym] ||= []
73
+ @_footnotes[bucket.to_sym] << content
74
+ return @footnotes
75
+ end
76
+
77
+ def render(thing = nil, *args)
78
+ self.add_footnote("renders", [thing, args])
79
+ super
80
+ end
81
+
82
+ def partial(template, *args)
83
+ self.add_footnote("partials", [template, args])
84
+ return super
85
+ end
86
+
87
+ # PRIVATE API CALL. BAD BAD. MUST FIX
88
+ def _call_filters(filters)
89
+ if filters.empty?
90
+ super
91
+ else
92
+ filters.each do |filter|
93
+ self.add_footnote("filters", filter)
94
+ end
95
+ super
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ module DataMapper
103
+
104
+ module Repository
105
+
106
+ def new(*args, &block)
107
+ super.extend(Ext)
108
+ end
109
+
110
+ module Ext
111
+ def read_many(query)
112
+ ::Merb.logger.info("LOGGING READ MANY QUERY")
113
+ super
114
+ end
115
+
116
+ def read_one(query)
117
+ ::Merb.logger.info("LOGGING READ ONE QUERY")
118
+ super
119
+ end
120
+
121
+ def update(attributes, query)
122
+ ::Merb.logger.info("LOGGING UPDATE QUERY")
123
+ super
124
+ end
125
+
126
+ def delete(query)
127
+ ::Merb.logger.info("LOGGING DELETE QUERY")
128
+ super
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,166 @@
1
+ module MerbFootnotes
2
+ module Notes
3
+ # This is the abstrac class for notes.
4
+ #
5
+ class AbstractNote
6
+
7
+ class << self
8
+ # Returns the symbol that represents this note.
9
+ # It's the name of the class, underscored and without _note.
10
+ #
11
+ # For example, for ControllerNote it will return :controller.
12
+ #
13
+ def to_sym
14
+ @note_sym ||= self.title.to_const_path.to_sym
15
+ end
16
+
17
+ # Returns the title that represents this note.
18
+ # It's the name of the class without Note.
19
+ #
20
+ # For example, for ControllerNote it will return Controller.
21
+ #
22
+ def title
23
+ @note_title ||= self.name.match(/^MerbFootnotes::Notes::(\w+)Note$/)[1]
24
+ end
25
+
26
+ # Return true if Note is included in notes array.
27
+ #
28
+ def included?
29
+ MerbFootnotes::Filter.notes.include?(self.to_sym)
30
+ end
31
+
32
+ # Action to be called to start the Note.
33
+ # This is applied as a before_filter.
34
+ #
35
+ def start!(controller = nil)
36
+ end
37
+
38
+ # Action to be called after the Note was used.
39
+ # This is applied as an after_filter.
40
+ #
41
+ def close!(controller = nil)
42
+ end
43
+ end
44
+
45
+ # Initialize notes.
46
+ # Always receives a controller.
47
+ #
48
+ def initialize(controller = nil)
49
+ end
50
+
51
+ # Returns the symbol that represents this note.
52
+ #
53
+ def to_sym
54
+ self.class.to_sym
55
+ end
56
+
57
+ # Specifies in which row should appear the title.
58
+ # The default is :show.
59
+ #
60
+ def row
61
+ :show
62
+ end
63
+
64
+ # If valid?, create a tab on Footnotes Footer with the title returned.
65
+ # By default, returns the title of the class (defined above).
66
+ #
67
+ def title
68
+ self.class.title
69
+ end
70
+
71
+ # If fieldset?, create a fieldset with the value returned as legend.
72
+ # By default, returns the title of the class (defined above).
73
+ #
74
+ def legend
75
+ self.class.title
76
+ end
77
+
78
+ # If content is defined, fieldset? returns true and the value of content
79
+ # is displayed when the Note is clicked. See fieldset? below for more info.
80
+ #
81
+ # def content
82
+ # end
83
+
84
+ # Set href field for Footnotes links.
85
+ # If it's nil, Footnotes will use '#'.
86
+ #
87
+ def link
88
+ end
89
+
90
+ # Set onclick field for Footnotes links.
91
+ # If it's nil, Footnotes will make it open the fieldset.
92
+ #
93
+ def onclick
94
+ end
95
+
96
+ # Insert here any additional stylesheet.
97
+ # This is directly inserted into a <style> tag.
98
+ #
99
+ def stylesheet
100
+ end
101
+
102
+ # Insert here any additional javascript.
103
+ # This is directly inserted into a <script> tag.
104
+ #
105
+ def javascript
106
+ end
107
+
108
+ # Specifies when should create a note for it.
109
+ # By default, it's valid.
110
+ #
111
+ def valid?
112
+ true
113
+ end
114
+
115
+ # Specifies when should create a fieldset for it, considering it's valid.
116
+ #
117
+ def fieldset?
118
+ self.respond_to?(:content)
119
+ end
120
+
121
+ # Return if this note is incuded in MerbFootnotes::Filter.notes.
122
+ #
123
+ def included?
124
+ self.class.included?
125
+ end
126
+
127
+ # Some helpers to generate notes.
128
+ #
129
+ protected
130
+ # Return if MerbFootnotes::Filter.prefix exists or not.
131
+ # Some notes only work with prefix set.
132
+ #
133
+ def prefix?
134
+ Merb::Plugins.config[:merb_footnotes][:prefix]
135
+ end
136
+
137
+ # Escape HTML special characters.
138
+ #
139
+ def escape(text)
140
+ text.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
141
+ end
142
+
143
+ # Gets a bidimensional array and create a table.
144
+ # The first array is used as label.
145
+ #
146
+ def mount_table(array, options = {})
147
+ header = array.shift
148
+ return '' if array.empty?
149
+
150
+ header = header.collect{|i| escape(Extlib::Inflection.humanize(i.to_s)) }
151
+ rows = array.compact.collect{|i| "<tr><td>#{i.join('</td><td>')}</td></tr>" }
152
+
153
+ <<-TABLE
154
+ <table #{hash_to_xml_attributes(options)}>
155
+ <thead><tr><th>#{header.join('</th><th>')}</th></tr></thead>
156
+ <tbody>#{rows.join}</tbody>
157
+ </table>
158
+ TABLE
159
+ end
160
+
161
+ def hash_to_xml_attributes(hash)
162
+ return hash.collect{ |key, value| "#{key.to_s}=\"#{value.gsub('"','\"')}\"" }.join(' ')
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,57 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module MerbFootnotes
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(
16
+ Merb::Plugins.config[:merb_footnotes][:prefix] +
17
+ controller_filename +
18
+ (index_of_method ? "&line=#{controller_line_number + 1}&column=3" : '')
19
+ )
20
+ end
21
+
22
+ def valid?
23
+ prefix?
24
+ end
25
+
26
+ protected
27
+ # Some controller classes come with the Controller:: module and some don't
28
+ # (anyone know why? -- Duane)
29
+ def controller_filename
30
+ File.join(File.expand_path(Merb.root), 'app', 'controllers', "#{@controller.controller_name}.rb")
31
+ end
32
+
33
+ def controller_text
34
+ @controller_text ||= IO.read(controller_filename)
35
+ end
36
+
37
+ def index_of_method
38
+ (controller_text =~ /def\s+#{@controller.action_name}[\s\(]/)
39
+ end
40
+
41
+ def controller_line_number
42
+ lines_from_index(controller_text, index_of_method)
43
+ end
44
+
45
+ def lines_from_index(string, index)
46
+ lines = string.to_a
47
+ running_length = 0
48
+ lines.each_with_index do |line, i|
49
+ running_length += line.length
50
+ if running_length > index
51
+ return i
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module MerbFootnotes
4
+ module Notes
5
+ class CookiesNote < AbstractNote
6
+ def initialize(controller)
7
+ @cookies = controller.cookies
8
+ end
9
+
10
+ def title
11
+ "Cookies (#{@cookies.length})"
12
+ end
13
+
14
+ def content
15
+ escape(@cookies.inspect)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module MerbFootnotes
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_toogle(\'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,40 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module MerbFootnotes
4
+ module Notes
5
+ class FilesNote < AbstractNote
6
+ def initialize(controller)
7
+ @files = scan_text(controller.body)
8
+ parse_files!
9
+ end
10
+
11
+ def row
12
+ :edit
13
+ end
14
+
15
+ def content
16
+ @files.empty? ? "" : "<ul><li>#{@files.join("</li><li>")}</li></ul>"
17
+ end
18
+
19
+ def valid?
20
+ prefix?
21
+ end
22
+
23
+ protected
24
+ def scan_text(text)
25
+ []
26
+ end
27
+
28
+ def parse_files!
29
+ @files.collect! do |filename|
30
+ if filename =~ %r{^/}
31
+ full_filename = File.join(File.expand_path(Merb.root), 'public', filename)
32
+ %{<a href="#{Merb::Plugins.config[:merb_footnotes][:prefix]}#{full_filename}">#{filename}</a>}
33
+ else
34
+ %{<a href="#{filename}">#{filename}</a>}
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ require "#{File.dirname(__FILE__)}/abstract_note"
2
+
3
+ module MerbFootnotes
4
+ module Notes
5
+ class FiltersNote < AbstractNote
6
+ def initialize(controller)
7
+ @controller = controller
8
+ @filters = parse_filters.compact
9
+ end
10
+
11
+ def legend
12
+ "Filter chain for #{@controller.class.to_s}"
13
+ end
14
+
15
+ def title
16
+ "Filters (#{@filters.size})"
17
+ end
18
+
19
+ def content
20
+ mount_table(@filters.unshift([:filter, :arguments]))
21
+ end
22
+
23
+ protected
24
+ def parse_filters
25
+ filters = @controller.footnotes[:filters] || []
26
+ sets = filters.map do |filter|
27
+ filter_action = filter.first
28
+ filter_args = filter.last
29
+
30
+ filter_display = if filter_action.is_a?(Proc)
31
+ where = filter_action.inspect[/@(.+)>$/, 1]
32
+ "Block in "+MerbFootnotes::Formatter.editor_link_line(where, true).to_s
33
+ elsif filter_action.is_a? Symbol
34
+ # TODO: somehow figure out how to display the line #
35
+ # @controller.method(filter.first).inspect[/@(.+)>$/, 1].to_s rescue "Unknown"
36
+ "#{@controller.class.to_s}::#{filter_action.to_s}"
37
+ end
38
+
39
+ unless filter_display.to_s.include? "merb-plugins-footnotes.rb"
40
+ [filter_display, (filter.last||{}).inspect]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end