LoginSignup
15
15

More than 5 years have passed since last update.

Railsで作ったJSONをRackでHTMLにする

Last updated at Posted at 2013-02-14

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
15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15