class String def line_from_index(index) lines = self.to_a running_length = 0 lines.each_with_index do |line, i| running_length += line.length if running_length > index return i end end end end class FootnoteFilter cattr_accessor :no_style, :abs_root, :textmate_prefix self.no_style = false self.textmate_prefix = "txmt://open?url=file://" attr_accessor :body, :abs_root def self.filter(controller) return if controller.render_without_footnotes filter = FootnoteFilter.new(controller) filter.add_footnotes! end def initialize(controller) @controller = controller @template = controller.instance_variable_get("@template") @body = controller.response.body @extra_html = "" self.abs_root = File.expand_path(RAILS_ROOT) end def add_footnotes! if performed_render? and first_render? if ["rhtml", "rxhtml"].include?(template_extension) && (content_type =~ /html/ || content_type.nil?) && !xhr? # If the user would like to be responsible for the styles, let them opt out of the styling here insert_styles unless FootnoteFilter.no_style insert_footnotes end end rescue Exception => e # Discard footnotes if there are any problems RAILS_DEFAULT_LOGGER.error "Textmate Footnotes Exception: #{e}\n#{e.backtrace.join("\n")}" end # Some controller classes come with the Controller:: module and some don't # (anyone know why? -- Duane) def controller_filename File.join(abs_root, "app", "controllers", "#{@controller.class.to_s.underscore}.rb"). sub('/controllers/controllers/', '/controllers/') end def controller_text @controller_text ||= IO.read(controller_filename) end def index_of_method (controller_text =~ /def\s+#{@controller.action_name}[\s\(]/) end def controller_line_number controller_text.line_from_index(index_of_method) end def performed_render? @controller.instance_variable_get("@performed_render") end def first_render? @template.respond_to?(:first_render) and @template.first_render end def xhr? @controller.request.xhr? end def template_path @template.first_render.sub(/\.(rhtml|rxhtml|rxml|rjs)$/, "") end def template_extension @template.first_render.scan(/\.(rhtml|rxhtml|rxml|rjs)$/).flatten.first || @template.pick_template_extension(template_path).to_s end def template_file_name File.expand_path(@template.send(:full_template_path, template_path, template_extension)) end def layout_file_name File.expand_path(@template.send(:full_template_path, @controller.active_layout, "rhtml")) end def content_type @controller.response.headers['Content-Type'] end def stylesheet_files @stylesheet_files ||= @body.scan(/]+href\s*=\s*['"]([^>?'"]+)/im).flatten end def javascript_files @javascript_files ||= @body.scan(/]+src\s*=\s*['"]([^>?'"]+)/im).flatten end def controller_url escape( textmate_prefix + controller_filename + (index_of_method ? "&line=#{controller_line_number + 1}&column=3" : "") ) end def view_url escape(textmate_prefix + template_file_name) end def layout_url escape(textmate_prefix + layout_file_name) end def insert_styles insert_text :before, /<\/head>/i, <<-HTML HTML end def insert_footnotes def tm_footnotes_toggle(id) "s = document.getElementById('#{id}').style; if(s.display == 'none') { s.display = '' } else { s.display = 'none' }" end footnotes_html = <<-HTML
#{textmate_links} Show: Session | Cookies | Params | Log | General Debug
(TextMate Footnotes) #{@extra_html}
HTML if @body =~ %r{]+id=['"]tm_footnotes['"][^>]*>} # Insert inside the "tm_footnotes" div if it exists insert_text :after, %r{]+id=['"]tm_footnotes['"][^>]*>}, footnotes_html else # Otherwise, try to insert as the last part of the html body insert_text :before, /<\/body>/i, footnotes_html end end def textmate_links html = "" if ::MAC_OS_X html = <<-HTML Edit: Controller | View | Layout HTML html += asset_file_links("Stylesheets", stylesheet_files) unless stylesheet_files.blank? html += asset_file_links("Javascripts", javascript_files) unless javascript_files.blank? html += "
" end html end def log_tail ansi = `tail -n 200 #{RAILS_DEFAULT_LOGGER.instance_variable_get("@logdev").filename}` html = ansi.gsub(/\e\[.+?m/, '') end def asset_file_links(link_text, files) return '' if files.size == 0 links = files.map do |filename| if filename =~ %r{^/} full_filename = File.join(abs_root, "public", filename) %{#{filename}} else %{#{filename}} end end @extra_html << <<-HTML HTML # Return the link that will open the 'extra html' div %{ | #{link_text}} end def indent(indentation, text) lines = text.to_a initial_indentation = lines.first.scan(/^(\s+)/).flatten.first lines.map do |line| if initial_indentation.nil? " " * indentation + line elsif line.index(initial_indentation) == 0 " " * indentation + line[initial_indentation.size..-1] else " " * indentation + line end end.join end # Inserts text in to the body of the document # +pattern+ is a Regular expression which, when matched, will cause +new_text+ # to be inserted before or after the match. If no match is found, +new_text+ is appended # to the body instead. +position+ may be either :before or :after def insert_text(position, pattern, new_text, indentation = 4) index = case pattern when Regexp if match = @body.match(pattern) match.offset(0)[position == :before ? 0 : 1] else @body.size end else pattern end @body.insert index, indent(indentation, new_text) end def escape(text) text.gsub("&", "&").gsub("<", "<").gsub(">", ">") end end