module Baurets module Optionsful class Documentator def initialize(app) @app = app @config = Baurets::Optionsful::Config.new end def call(env) unless env["PATH_INFO"].index(@config.base_path) == 0 response = @app.call(env) else response = extract_documentation(env) end response end private require 'rubygems' require 'yaml' require 'RedCloth' def verify_path(env) end def extract_documentation(env) path = env["PATH_INFO"] path = path.gsub(@config.base_path, '') # do routing introspection: routes = ::Baurets::Optionsful::Introspections.do_routing_introspection # do request path investigation route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes,path) # do the matches: allow = ::Baurets::Optionsful::Introspections.do_the_matches(routes, route_guess) http_methods = allow.split(", ") path_parts = ::Baurets::Optionsful::Introspections.prepare_request_path(path) controller_actions = [] http_methods.each do |verb| controller_actions << [verb, relate_action_to_method(path_parts, verb)] end controller_actions.delete_if {|pair| pair[1].empty? } controller_name = (::Baurets::Optionsful::Introspections.discover_controller_name(path_parts) + "_controller" ) if ::Baurets::Optionsful::Introspections.discover_controller_name(path_parts) file = "" file = File.join(RAILS_ROOT, "app", "controllers", controller_name + ".rb") if controller_name if File.exist? file controller_class = controller_name.camelize service_doc = extract_comments_above(file, find_line_for(file, controller_class, :class)) methods_docs = [] controller_actions.each do |info| methods_docs << [info, extract_comments_above(file, find_line_for(file, info[1], :method)).join("\n")] end body = build_html(service_doc, methods_docs) [200, {"Content-Type" => "text/html"}, body] else [404, {}, " "] end end def build_html(srvc, methods) html = "
" comments = srvc.join("\n").gsub(/^#+\s/, '') srvc_doc = YAML::parse(comments) if srvc_doc && srvc_doc["service"] resource_title = srvc_doc["service"]["title"].value title = "h1. " + resource_title.to_s if resource_title html += RedCloth.new(title).to_html html += RedCloth.new("#{srvc_doc["service"]["description"].value}").to_html html += "\n" methods.each do |meth| meth_verb = meth[0][0] method_comms = meth[1].gsub(/^#+\s/, '') unless meth[1].empty? resource = YAML::parse(method_comms) html += RedCloth.new("h2. " + meth_verb).to_html if resource && resource["resource"] html += RedCloth.new(resource["resource"]["title"].value).to_html html += RedCloth.new(resource["resource"]["identifier"].value).to_html else html += RedCloth.new("Could not find or understand any annotated metadata related to method #{meth_verb}.").to_html end end else html += RedCloth.new("Could not understand anything.").to_html end html += "" html end def relate_action_to_method(path, verb) action = "" routes = ::Baurets::Optionsful::Introspections.do_routing_introspection route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes, path) routes.each do |route| if ((route.first == route_guess) && (route[1][0] == verb)) action = route[1][1] unless route[1][1].empty? end end action end def file_lines(file_name) lines = [] begin file = File.new(file_name, "r") while (line = file.gets) line = line.strip lines << line unless line.empty? end file.close rescue => err puts "Exception: #{err}" err end lines.delete(nil) lines end def extract_comments_above(file_name, line_number) lines = file_lines(file_name) doc = [] line_number = line_number -1 while ((line_number = line_number -1) && (line_number >= 0) && (!lines.nil?) && (!lines[line_number].empty?)) line = lines[line_number].lstrip if line[0] == 35 doc << line else line_number = 0 end end doc = doc.reverse doc end def find_line_for(file, name, type) lines = file_lines(file) signature = "" if type == :class signature = "class " + name elsif type == :method signature = "def " + name end counter = 1; lines.each do |line| if line.include? signature return counter end counter += 1 end counter = 0 end end end end