FrogApps 技術ブログ始めました!
RailsやiOS、HTML5の情報を発信中!! → http://qiita.com/teams/frogapps
RailsでiOSなどのアプリ用のサーバを作ると、JSONを出力するAPIを作ることになると思います。
そのJSON APIを使って、HTMLをレンダリングするRackモジュールを作ってみました。
これを使うことで、Railsがモデルとコントローラ層、Rackでビュー層という風に別れます。
Railsのビューより機能は劣りますが、A/Bテストを仕込むなど、なにか手を加えたいときには全体が把握しやすく、Railsのバージョンにも依存しなくなるというメリットがあります。
json_render.rb
=begin
# This file is used by Rack-based servers to start the application.
require ::File.expand_path('../config/environment', __FILE__)
require './lib/json_render'
use JSONRender,
:routes => {
%r|/test/(\w+)$| => {
:json => "/home/\\1.json",
:erb => Proc.new do
['erb/home.1.html.erb', 'erb/home.2.html.erb'][rand(2)]
end
},
%r|^/$| => {
:json => "/home.json",
:erb => 'erb/index.html.erb'
}
}
run JsonRenderTest::Application
=end
require 'erubis'
require 'net/http'
class JSONRender
def initialize(app, options)
@app, @routes = app, options[:routes]
@templates = {}
end
def call(env)
path_info = env['PATH_INFO']
key, settings = @routes.find do |path, settings|
path_info.match(path)
end.to_a
return @app.call(env) unless key
# get json data from specific path
settings_json = settings[:json]
settings_json = settings_json.call(env) if settings_json.is_a?(Proc)
json_path = path_info.gsub(key, settings_json)
status, headers, response = @app.call(env.update('PATH_INFO' => json_path))
return [status, headers, response] if [Net::HTTPNotModified].include?(status)
response.close if response.respond_to?(:close)
if status / 100 == 2
# render data
erb_path = settings[:erb]
erb_path = erb_path.call(env) if erb_path.is_a?(Proc)
response = render(erb_path, JSON.parse(response.is_a?(Hash) ? response.first : response.body))
# Set new Content-Length, if it was set before we mutated the response body
if headers['Content-Length']
length = response.to_ary.inject(0) { |len, part| len + Rack::Utils::bytesize(part) }
headers['Content-Length'] = length.to_s
end
headers['Content-Type'] = 'text/html'
end
[status, headers, response]
end
def render(erb_file, params)
erb_file = File.expand_path(erb_file)
template = @templates[erb_file]
if !template || template[:timestamp] != File.mtime(erb_file)
erb = Erubis::Eruby.new(File.read(erb_file))
template = {:erb => erb, :timestamp => File.mtime(erb_file)}
@templates[erb_file] = template
end
[template[:erb].evaluate(params)]
end
end