Module: JSONRPC2::HTML

Defined in:
lib/jsonrpc2/html.rb

Overview

HTML output helpers for browseable API interface

Constant Summary collapse

ASSET_DIR =
File.dirname(File.dirname(__FILE__))+'/assets'
MISSING =
<<-EOM
<!DOCTYPE html>
<html>
<head><title>404 Not found</title></head>
<body>404 Not found</body>
</html>
EOM

Class Method Summary collapse

Class Method Details

.call(interface, request) ⇒ Rack::Response

Process browser request for #interface

Parameters:

  • interface (JSONRPC2::Interface)

    Interface being accessed

  • request (Rack::Request)

    Request being processed

Returns:

  • (Rack::Response)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/jsonrpc2/html.rb', line 63

def call(interface, request)
  #require 'pp'; pp interface.about

  if interface.auth_with
    response = catch(:rack_response) do
      interface.auth_with.browser_check(request.env); nil
    end
    return response if response
  end

  case request.path_info
  when %r'^/_assets/((img|css|js)/[a-z][-a-z0-9.]+)$'
    name = $1
    path = File.join(ASSET_DIR, name)

    if File.exist?(path)
      [200, {'Content-Type' => {
        '.js' => 'text/javascript',
        '.css' => 'text/css',
        '.png' => 'image/png'}[File.extname(name)]}, [File.read(path)]]
    else
      [404, {'Content-Type' => 'text/html'}, []]
    end
  when /^\/([a-zA-Z_0-9]+)/
    method = $1
    if info = interface.about_method(method)
      if json = request.POST['__json__']
        begin
          data = JSON.parse(json)
          result = interface.new(request.env).dispatch(data)
        rescue => e
          result = e.class.name + ": " + e.message
        end
      end
      [200, {'Content-Type' => 'text/html'}, html5(method,describe(interface, request, info, :result => result), :request => request) ]
    else
      [404, {'Content-Type' => 'text/html'}, html5("Method not found", "<h1>No such method</h1>", :request => request)]
    end
  else
    body = RedCloth.new(interface.to_textile).to_html.gsub(/\<h3\>(.*?)\<\/h3\>/, '<h3><a href="'+request.script_name+'/\1">\1</a></h3>')
    [200, {'Content-Type' => 'text/html'}, 
            html5('Interface: '+interface.name.to_s, body, :request => request)]
  end
end

.describe(interface, request, info, options = {}) ⇒ Object

Returns HTML page describing method



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/jsonrpc2/html.rb', line 108

def describe interface, request, info, options = {}
  params = {}
  if info[:params]
    info[:params].each do |param|
      params[param[:name]] = case param[:type]
      when 'String'
        ""
      when 'Boolean', 'false'
        false
      when 'true'
        true
      when 'null'
        nil
      when 'Number', 'Integer'
        0
      when /^Array/
        []
      when 'Time'
        "00:00"
      when 'DateTime'
        Time.at(0).iso8601
      when 'Date'
        '1970-01-01'
      else
        {}
      end
    end
  end
  <<-EOS
<h1>Method Info: #{info[:name]}</h1>
#{RedCloth.new(interface.method_to_textile(info)).to_html}

<hr>

<div class="row">
<div class="span6">
<h2>Test method</h2>
</div>
<div class="span6">
<h3>Result</h3>
</div>
</div>
<div class="row">
<div class="span6">
<form method="POST" action="#{request.script_name}/#{info[:name]}">
<textarea name="__json__" cols="60" rows="8" class="span6">
#{CGI.escapeHTML((request.POST['__json__'] || JSON.pretty_unparse({'jsonrpc'=>'2.0', 'method' => info[:name], 'id' => 1, 'params' => params})).strip)}
</textarea>
<div class="form-actions">
<input type="submit" class="btn btn-primary" value="Call Method">
</div>
</form>
</div>
<div class="span6">
<pre style="white-space: prewrap">#{format_result(options[:result])}</pre>
</div>
</div>

EOS
end

.format_result(result) ⇒ Object

Format JSON result



169
170
171
172
173
174
175
176
# File 'lib/jsonrpc2/html.rb', line 169

def format_result(result)
  CGI.escapeHTML(JSON.pretty_unparse(JSON.parse(result))).gsub(%r<("|&quot;)https?://[^"]+?("|&quot;)>) do |str|
    url = CGI.unescapeHTML(str)[1...-1]
     %Q["<a href="#{CGI.escapeHTML(url)}">#{CGI.escapeHTML(url)}</a>"]
  end
rescue Exception => e
  CGI.escapeHTML(result.to_s)
end

.html5(title, body, options = {}) ⇒ Object

Wrap body in basic bootstrap template using cdn



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/jsonrpc2/html.rb', line 8

def html5(title, body, options={})
  request = options[:request]
  [
  <<-HTML5
<!DOCTYPE html><html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>#{title}</title>
#{options[:head]}
<link rel="stylesheet" href="#{request.script_name}/_assets/css/bootstrap.min.css">
<script src="#{request.script_name}/_assets/js/bootstrap.min.js"></script>
 <style>
    body {
      padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
    }
  </style>
</head>
<body> 
<div class="navbar navbar-fixed-top">
  <div class="navbar-inner">
    <div class="container">
      <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </a>
      <a class="brand" href="#">JSON-RPC Interface</a>
      <div class="nav-collapse">
        <ul class="nav">
          <li class="#{['', '/'].include?(request.path_info) ? 'active' : ''}"><a href="#{request.script_name}/">API Overview</a></li>
        </ul>
      </div><!--/.nav-collapse -->
    </div>
  </div>
</div>
<div class="container">#{body}</div>
</body>
</html>
HTML5
]
end