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