Last active
December 27, 2015 13:09
-
-
Save spiegela/7330532 to your computer and use it in GitHub Desktop.
Dynamo Restful Resource Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Run this app from Dynamo root with: | |
| # mix run -r examples/fruit_handler.exs --no-halt | |
| # | |
| # Open a browser at URL http://localhost:3030 see the wonderment of rest. | |
| # | |
| defmodule Fruit do | |
| # Cowboy REST handler resources | |
| # | |
| # * User guide: http://ninenines.eu/docs/en/cowboy/HEAD/guide/rest_handlers | |
| # * Reference: http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_rest | |
| # | |
| require :cowboy_req, as: R # Used for request body parsing. | |
| require :jiffy, as: J | |
| defmodule Collection do | |
| # Display and manage a collection of resources. This includes modifying a | |
| # collection by adding new members via POST | |
| def init(_trans, _req, []) do | |
| {:upgrade, :protocol, :cowboy_rest} | |
| end | |
| # The following callbacks are all optional and have intelligent defaults as | |
| # defined int he links above. | |
| def allowed_methods(req, state) do | |
| {["GET", "OPTIONS", "POST"], req, state} | |
| end | |
| def allow_missing_post do true end | |
| def content_types_provided(req, state) do | |
| # Define content types provided and their associated ProvideResource | |
| # callbacks as atoms | |
| # | |
| { [ | |
| { "application/json", :get_json }, | |
| { "text/html", :get_html }, | |
| { "text/plain", :get_plain } | |
| ], req, state } | |
| end | |
| def content_types_accepted(req, state) do | |
| # Define content types accepted and their associated ProvideResource | |
| # callbacks as atoms | |
| # | |
| { [ | |
| { "application/json", :post_json} | |
| ], req, state } | |
| end | |
| def get_json(req, state) do | |
| # JSON ProvideResource callback | |
| # | |
| {FruitServer.list(:fruit_server) |> J.encode, req, state} | |
| end | |
| def get_html(req, state) do | |
| {FruitServer.list(:fruit_server) |> format_html, req, state} | |
| end | |
| defp format_html(list) do | |
| "<div><h3>Fruit:</h3><ul>#{list_html(list)}</ul></div>" | |
| end | |
| defp list_html(list) do | |
| (lc fruit inlist list, do: "<li>#{fruit}</li>") |> Enum.join | |
| end | |
| def get_plain(req, state) do | |
| # Plain ProvideResource callback | |
| # | |
| {FruitServer.list(:fruit_server) |> format_plain, req, state} | |
| end | |
| defp format_plain(list) do | |
| "Fruit: \n#{list_plain(list)}" | |
| end | |
| defp list_plain(list) do | |
| (lc fruit inlist list, do: " - #{fruit}") |> Enum.join("\n") | |
| end | |
| def post_json(req, state) do | |
| get_fruit_by_body(req) |> FruitServer.add(:fruit_server) | |
| {true, req, state} | |
| end | |
| defp get_fruit_by_body(req) do | |
| { [ { "fruit", fruit } ] } = R.body(req) | |
| |> elem(1) | |
| |> J.decode | |
| fruit | |
| end | |
| end | |
| defmodule Object do | |
| # Display and manage a single resource. | |
| def init(_trans, _req, []) do | |
| {:upgrade, :protocol, :cowboy_rest} | |
| end | |
| # The following callbacks are all optional and have intelligent defaults as | |
| # defined int he links above. | |
| def allowed_methods(req, state) do | |
| {["GET", "OPTIONS", "PUT", "DELETE"], req, state} | |
| end | |
| def content_types_provided(req, state) do | |
| # Define content types provided and their associated ProvideResource | |
| # callbacks as atoms | |
| { [ | |
| { "application/json", :get_json }, | |
| { "text/html", :get_html }, | |
| { "text/plain", :get_plain } | |
| ], req, state } | |
| end | |
| def content_types_accepted(req, state) do | |
| # Define content types accepted and their associated ProvideResource | |
| # callbacks as atoms | |
| { [ | |
| { "application/json", :put_json} | |
| ], req, state } | |
| end | |
| def delete_resource(req, state) do | |
| get_fruit_index(req) |> FruitServer.remove(:fruit_server) | |
| {true, req, state} | |
| end | |
| def get_json(req, state) do | |
| # JSON ProvideResource callback | |
| { get_fruit_by_req(req) |> format_json, req, state } | |
| end | |
| defp format_json(fruit) do | |
| { [{:fruit, fruit}] } |> J.encode | |
| end | |
| def get_html(req, state) do | |
| # HTML ProvideResource callback | |
| { get_fruit_by_req(req) |> format_html, req, state} | |
| end | |
| defp format_html(fruit) do | |
| "<label>Fruit</label><span>#{fruit}</span>" | |
| end | |
| def get_plain(req, state) do | |
| # Plain ProvideResource callback | |
| {get_fruit_by_req(req) |> format_plain, req, state} | |
| end | |
| defp format_plain(fruit) do | |
| "Fruit: #{fruit}" | |
| end | |
| defp get_fruit_by_req(req) do | |
| get_fruit_index(req) | |
| |> FruitServer.get(:fruit_server) | |
| end | |
| defp get_fruit_index(req) do | |
| R.binding(:index, req) | |
| |> elem(0) | |
| |> String.to_integer | |
| |> elem(0) | |
| end | |
| def put_json(req, state) do | |
| fruit = get_fruit_by_body(req) | |
| get_fruit_index(req) |> FruitServer.change(fruit, :fruit_server) | |
| {true, req, state} | |
| end | |
| defp get_fruit_by_body(req) do | |
| { [ { "fruit", fruit } ] } = R.body(req) | |
| |> elem(1) | |
| |> J.decode | |
| fruit | |
| end | |
| end | |
| defmodule Router do | |
| use Dynamo | |
| use Dynamo.Router | |
| config :server, | |
| port: 3030, | |
| dispatch: [ | |
| { :_, | |
| [ | |
| {"/fruit", Collection, []}, | |
| {"/fruit/:index", Object, []} | |
| ] | |
| } | |
| ] | |
| end | |
| end | |
| f_pid = FruitServer.start_link | |
| Process.register(f_pid, :fruit_server) | |
| FruitServer.add("apple", :fruit_server) | |
| FruitServer.add("banana", :fruit_server) | |
| FruitServer.add("cantaloupe", :fruit_server) | |
| FruitServer.add("date", :fruit_server) | |
| Fruit.Router.start_link | |
| Fruit.Router.run |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule FruitServer do | |
| use GenServer.Behaviour | |
| ## Public API | |
| def start_link do | |
| {:ok, fruit_server} = :gen_server.start_link FruitServer, [], [] | |
| fruit_server | |
| end | |
| def add(item, fruit_server) do | |
| :gen_server.call(fruit_server, {:add, item}) | |
| end | |
| def remove(index, fruit_server) do | |
| :gen_server.call(fruit_server, {:remove, index}) | |
| end | |
| def list(fruit_server) do | |
| :gen_server.call(fruit_server, :list) | |
| end | |
| def get(index, fruit_server) do | |
| :gen_server.call(fruit_server, {:get, index}) | |
| end | |
| def change(index, value, fruit_server) do | |
| :gen_server.call(fruit_server, {:change, index, value}) | |
| end | |
| ## GenServer API | |
| def init(items) do | |
| {:ok, items} | |
| end | |
| def handle_call({:add, item}, _from, items) do | |
| {:reply, :ok, [item|items]} | |
| end | |
| def handle_call({:remove, index}, _from, items) do | |
| items1 = Enum.with_index(items) |> Enum.reverse | |
| items2 = lc {val, i} inlist items1, index != i, do: val | |
| {:reply, :ok, items2} | |
| end | |
| def handle_call(:list, _from, items) do | |
| {:reply, Enum.reverse(items), items} | |
| end | |
| def handle_call({:get, index}, _from, items) do | |
| fruit = Enum.reverse(items) |> Enum.at(index) | |
| {:reply, fruit, items} | |
| end | |
| def handle_call({:change, index, value}, _from, items) do | |
| {:reply, :ok, Enum.reverse(items) |> List.replace_at(index, value) |> Enum.reverse} | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment