merb_footnotes 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +14 -0
- data/Rakefile +51 -0
- data/lib/merb-plugins-footnotes.rb +79 -0
- data/lib/merb_footnotes/filter.rb +258 -0
- data/lib/merb_footnotes/formatter.rb +63 -0
- data/lib/merb_footnotes/instrumentation.rb +134 -0
- data/lib/merb_footnotes/notes/abstract_note.rb +166 -0
- data/lib/merb_footnotes/notes/controller_note.rb +57 -0
- data/lib/merb_footnotes/notes/cookies_note.rb +19 -0
- data/lib/merb_footnotes/notes/env_note.rb +19 -0
- data/lib/merb_footnotes/notes/files_note.rb +40 -0
- data/lib/merb_footnotes/notes/filters_note.rb +46 -0
- data/lib/merb_footnotes/notes/javascripts_note.rb +16 -0
- data/lib/merb_footnotes/notes/layout_note.rb +33 -0
- data/lib/merb_footnotes/notes/log_note.rb +38 -0
- data/lib/merb_footnotes/notes/params_note.rb +19 -0
- data/lib/merb_footnotes/notes/partials_note.rb +59 -0
- data/lib/merb_footnotes/notes/queries_note.rb +139 -0
- data/lib/merb_footnotes/notes/routes_note.rb +35 -0
- data/lib/merb_footnotes/notes/session_note.rb +19 -0
- data/lib/merb_footnotes/notes/stylesheets_note.rb +16 -0
- data/lib/merb_footnotes/notes/view_note.rb +37 -0
- data/spec/merb_footnotes_spec.rb +7 -0
- data/spec/spec_helper.rb +1 -0
- metadata +89 -0
@@ -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('&', '&').gsub('<', '<').gsub('>', '>')
|
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
|