Giter Site home page Giter Site logo

phoenixframework / phoenix Goto Github PK

View Code? Open in Web Editor NEW
20.6K 566.0 2.8K 21.19 MB

Peace of mind from prototype to production

Home Page: https://www.phoenixframework.org

License: MIT License

JavaScript 11.36% Elixir 85.71% CSS 0.98% HTML 1.62% Shell 0.01% Earthly 0.15% Euphoria 0.17%
elixir web-framework distributed realtime api-server

phoenix's Introduction

phoenix logo

Peace of mind from prototype to production.

Build Status Hex.pm Documentation

Getting started

See the official site at https://www.phoenixframework.org/.

Install the latest version of Phoenix by following the instructions at https://hexdocs.pm/phoenix/installation.html#phoenix.

Phoenix requires Elixir v1.11+ & Erlang v22.1+.

Documentation

API documentation is available at https://hexdocs.pm/phoenix.

Phoenix.js documentation is available at https://hexdocs.pm/phoenix/js.

Contributing

We appreciate any contribution to Phoenix. Check our CODE_OF_CONDUCT.md and CONTRIBUTING.md guides for more information. We usually keep a list of features and bugs in the issue tracker.

Generating a Phoenix project from unreleased versions

You can create a new project using the latest Phoenix source installer (the phx.new Mix task) with the following steps:

  1. Remove any previously installed phx_new archives so that Mix will pick up the local source code. This can be done with mix archive.uninstall phx_new or by simply deleting the file, which is usually in ~/.mix/archives/.
  2. Copy this repo via git clone https://github.com/phoenixframework/phoenix or by downloading it
  3. Run the phx.new Mix task from within the installer directory, for example:
cd phoenix/installer
mix phx.new dev_app --dev

The --dev flag will configure your new project's :phoenix dep as a relative path dependency, pointing to your local Phoenix checkout:

defp deps do
  [{:phoenix, path: "../..", override: true},

To create projects outside of the installer/ directory, add the latest archive to your machine by following the instructions in installer/README.md

To build the documentation from source:

npm install --prefix assets
MIX_ENV=docs mix docs

To build Phoenix from source:

mix deps.get
mix compile

To build the Phoenix installer from source:

mix deps.get
mix compile
mix archive.build

Building phoenix.js

cd assets
npm install

Important links

Copyright and License

Copyright (c) 2014, Chris McCord.

Phoenix source code is licensed under the MIT License.

phoenix's People

Contributors

aaronrenner avatar appdevnick avatar bcardarella avatar chrismccord avatar darkofabijan avatar dependabot[bot] avatar eksperimental avatar ericmj avatar fertapric avatar gazler avatar gjaldon avatar jayjun avatar josevalim avatar lancehalvorsen avatar lostkobrakai avatar mcrumm avatar michallepicki avatar michalmuskala avatar milmazz avatar mitchellhenke avatar nathanl avatar phoebe100 avatar rhcarvalho avatar rossta avatar scrogson avatar snewcomer avatar stevedomin avatar ugisozols avatar whatyouhide avatar wojtekmach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phoenix's Issues

Issue setting up Pheonix

Trying to setup Elixir/Pheonix for the first time, having ran the following commands

brew install erlang --devel
brew install elixir
git clone https://github.com/phoenixframework/phoenix.git && cd phoenix && mix do deps.get, compile

The error that I get is:

== Compilation error on file lib/plug/adapters/cowboy/conn.ex ==
** (ArgumentError) cannot access module File.Stat because it is not a record
    (elixir) expanding macro: Kernel.access/2
    lib/plug/adapters/cowboy/conn.ex:33: Plug.Adapters.Cowboy.Conn.send_file/4
    (elixir) src/elixir.erl:157: :elixir.erl_eval/2
    (elixir) src/elixir.erl:150: :elixir.eval_forms/4
    (elixir) src/elixir_lexical.erl:17: :elixir_lexical.run/2
    (elixir) lib/kernel/parallel_compiler.ex:91: anonymous fn/3 in Kernel.ParallelCompiler.spawn_compilers/8

could not compile dependency plug, mix compile failed. You can recompile this dependency with `mix deps.compile plug` or update it with `mix deps.update plug`

I've tried both suggested commands and the same error occurs on compiling plug.

Running Elixir 0.13.2 against erlang: stable R16B03-1 (bottled), devel 17.0, HEAD

Very new to Elixir so any pointing in the right direction would be greatly appreciated.

RFC: Feedback on new websocket/channels/pubsub implementation

Hey All!

I'm really excited to unveil my plans for the websocket/pubsub layer I've been envisioning for Phoenix. I have an initial working implementation and I'm looking for direct feedback on the api, naming/concepts, and conventions before I merge with master. Here's what we have today:

Mutiplexed websocket connection with phoenix.js dep

A single websocket connection from the client is shared for all subscribed channels. This is required since browsers limit simultaneous websocket connections. A light javascript library provides socket connection, channel subscription, event binding, etc.

Pubsub layer that uses process groups backed by :pg2

I don't have hard benchmarks yet, but pg2 seemed ideal to built pubsub on top of since we get process groups distributed on the mesh for free. This comes with the caveat of potential overhead of nodes being locked for consistency, but I'd like to see how this scales out. Initially stress tests are very promising.

Channels

Channels can be thought of as namespaces for "topics" to be subscribed to, as well as a place to group common events and behavior. You can almost conceptually think of them as a typical controller action, except they handle events from clients, and broadcast events to other channels.

Topics

Topics are simply a string for clients to subscribe/broadcast about on a particular channel. This could be anything from the string "lobby" on a "rooms" channel, or a particular user id, for a "profiles" channel.

Let's write a bare-bones chat application using these ideas:

Router: We need a rooms Channel to broadcast on:

defmodule Chat.Router do
  use Phoenix.Router
  use Phoenix.Router.Socket, mount: "/ws"
  plug Plug.Static, at: "/static", from: :chat

  get "/", Chat.Controllers.Pages, :index, as: :page
  channel "rooms", Chat.Channels.Rooms
end

Channel: Let's authorize clients to join channel topics and pub/sub events

defmodule Chat.Channels.Rooms do
  use Phoenix.Channel

  @doc """
  Authorize socket to join for sub/pub events on this channel & topic

  Possible Return Values

  {:ok, socket} to authorize sub for channel for requested topic

  {:error, socket, reason} to deny sub/pub on this channel
  for the requested topic
  """
  def join(socket, message) do
    reply socket, "join", status: "connected"
    broadcast socket, "user:entered", username: "anonymous"
    {:ok, socket}
  end

  def event("new:message", socket, message) do
    broadcast socket, "new:message", message
    {:ok, socket}
  end
end

Markup/javascript:

<h1>Phoenix Chat Example</h1>
<h3>Status: <small id="status">Not Connected</small></h3>
<div id="messages"></div>
<input id="message-input" type="text">

<script type="text/javascript">
  $(function(){
    var socket    = new Phoenix.Socket("ws://" + location.host +  "/ws");
    var $status   = $("#status");
    var $messages = $("#messages");
    var $input    = $("#message-input");

    socket.join("rooms", "lobby", {}, function(chan){

      $input.off("keypress").on("keypress", function(e) {
        if (e.keyCode == 13) {
          chan.send("new:message", {body: $input.val()});
          $input.val("");
        }
      });

      chan.on("join", function(message){ $status.text("joined"); });

      chan.on("new:message", function(message){
        $messages.append("<br/>" + message.body);
      });

      chan.on("user:entered", function(msg){
        $messages.append("<br/><i>[" + msg.username + " entered]</i>");
      });
    });
  });
</script>

The neat thing about channels is we can broadcast to them from anywhere in our application. Consider the chat example with active clients in the "rooms" channel, "lobby" topic. We could push a message to the browser directly from iex:

iex> Phoenix.Channel.broadcast("rooms", "lobby", "new:message", body: "hello!")
:ok

This opens up all kinds of realtime updates from any parts of the application to subscribed clients.

That's it! I believe this is flexible enough to accommodate most use-cases and extensible enough for us to iterate on for more advanced features.

Here's the chat example packaged as a little app:
https://github.com/chrismccord/phoenix_chat_example

cm-channels branch:
https://github.com/phoenixframework/phoenix/tree/cm-channels

Big thanks to @jeregrine for doing the legwork on the websocket handler and @HashNuke for helping me flesh out the channel/topic concepts.

Let me know how it looks!

Resources actions should prefix scoped helper path

Currently the scope helper path prefixes the entire helper function name. I'm accustomed to the Rails resource action prefixing the rest of the helper method, i.e. edit_admin_user_path. We could deviate here with our own convention of the current behavior, but I prefer the action prefixing the helper as it's easy to grep.

Current behavior:

  scope path: "admin", alias: Admin, helper: "admin" do
    resources "users", Users
  end
mix phoenix.routes Router
    admin_users  GET     admin/users           Elixir.Admin.Users#index
admin_edit_user  GET     admin/users/:id/edit  Elixir.Admin.Users#edit
     admin_user  GET     admin/users/:id       Elixir.Admin.Users#show
 admin_new_user  GET     admin/users/new       Elixir.Admin.Users#new
                 POST    admin/users           Elixir.Admin.Users#create
                 PUT     admin/users/:id       Elixir.Admin.Users#update
                 PATCH   admin/users/:id       Elixir.Admin.Users#update
                 DELETE  admin/users/:id       Elixir.Admin.Users#destroy

RFC: View layer implementation

Greetings all,

I've put together a functioning view layer that I'm pleased with and would like feedback on the approach and conventions before it lands in master.
/cc @josevalim, @ericmj, @darkofabijan, @nurugger07

branch: https://github.com/phoenixframework/phoenix/tree/cm/templates

Here's a rundown of how things work:

Directory Structure (views and templates share same directory)

views
├── Layouts
│   ├── application.html.eex
│   └── layouts.ex            - optional, implicity created if missing
├── Profiles
│   ├── profiles.ex           - optional, implicity created if missing
│   └── show.html.eex
├── Users
│   ├── nav
│   │   └── top.html.eex
│   ├── show.html.eex
│   └── users.ex              - optional, implicity created if missing
└── views.ex

Views / Templates

Phoenix makes a distinction between what Templates and Views are. Put simply, Views render Templates.

  • Views also serve as a presentation layer for their templates, providing helper functions that are exposed to the templates
  • Templates are precompiled into the View for performant rendering

Base Application View (lib/my_app/views/views.ex)

All subviews use MyApp.Views, allowing the base view to serve as
an area to import/define common view functionality (helpers, aliases, etc)

defmodule MyApp.Views do
  use Phoenix.View.Base

  defmacro __using__(_) do
    quote do
      use Phoenix.View
      alias MyApp.Views
      import unquote(__MODULE__)
    end
  end

  def default_title, do: "Welcome"
  ...
end

Subviews

  • Subviews simply use MyApp.Views to have base functionality defined
  • Subview directories are named in camel case to matched their module name. This aids in implicit module creation (below)
  • All templates located in the subview directory, are precompiled into the module

Implicit Subviews

Subview modules are created automatically in the event the user simply wants to render templates using only functions exposed from the base view. At compile time, Phoenix will check for the existence of a subview when it sees a camel-cased directory within the lib/my_app/views folder. If the corresponding subview elixir module does not exist, a skeleton module is compiled automatically.

Example Subview (lib/my_app/views/Users/users.ex)

defmodule MyApp.Views.Users do
  use MyApp.Views

  def truncate_bio(bio, length), do: String.slice(bio, 0, length) <> "..."
  ...
end

Explicit Rendering (outside of controller)

Views.Users.render("show.html", name: "chris")

Views.Profiles.render("show.html", homepage_url: "example.com")

# render a view within another view for layout support, 
# exposes `@inner` assign to parent view
Views.Users.render("show.html", 
  name: "chris",
  within: {Views.Layouts, "application.html"}
)

Example View

safe is required right now when rendering inline templates. I may rework to safe nested renders automatically:

<%= render("nav/top.html", []) |> safe %>
<div>
  User <%= @name %>
</div>

Layout

<html>
  <title><%= @title || default_title %></title>
  <%= @inner %>
</html>

Controller, render macro

The render/3 macro expands at compile time to conventionally call a View module whose name mirrors the the Controller's name. The template extension is determined from the request content-type. i.e.

defmodule MyApp.Controllers.Users do
  use Phoenix.Controller

  def show(conn) do
    render conn, "show", name: "José"
  end
end

Expands at compile time to:

def show(conn) do
  render_view conn, MyApp.Views.Users, MyApp.Views.Layouts, "show", name: "José"
end

  # For an html request at runtime, `render_view` would invoke:

    MyApp.Views.Users.render("show.html",
      name: "José",
      within: {MyApp.Views.Layouts, "application.html"}
    )

The current implementation can also be easily extended to support other tempting engines. For example, calliope integration would only require a small shim that compiled the templates to conform to Phoenix's precompiled naming conventions.

That's it. There's still work to do, but everything here is implemented. I plan to have an initial release sometime this June. Thanks!

Compilation error on file lib/phoenix/examples/router.ex

Seem to have trouble compiling phoenix via mix.deps.compile:

== Compilation error on file lib/phoenix/examples/router.ex ==
could not compile dependency phoenix, mix compile failed. You can recompile this dependency with mix deps.compile phoenix or update it with mix deps.update phoenix
** (CompileError) deps/phoenix/lib/phoenix/examples/router.ex:2: module Plug.Builder is not loaded and could not be found

Need a way to halt connections.

RIght now I'm not sure we have a way to halt connections, we can send a response but that doesn't stop phoenix from running the rest of the commands or plugs.

Documentation Request: Accessing a POST/PUT/PATCH request body

I'm a bit lost here; I'd like to access the raw request body of a POST/PUT/PATCH request, and can't figure out how to do it. I had a look at the Plug library, but it seems to require one to use a Plug.Parser, which in turn restricts the request to use either a urlencoded or web form content type.

A POST action in the examples would be nice, to see how I could use phoenix for an API service.

conn.params empty

Hey,
when calling conn.params I would expect a Dict of the parsed Url params. But no matter what I do I get an empty Dict.

Also I can't seem to find the phoenix test case for Http Post with params.

I supposed you rely on the correctness of the Plug.Parsers.URLENCODED but somewhere my params are lost.

Has anyone experienced that before? If not I have to keep digging.

$ http POST http://localhost:4000/alarms alarm=foo --form --verbose

POST /alarms HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 9
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:4000
User-Agent: HTTPie/0.7.2

alarm=foo
pry(1)> conn
Plug.Conn[adapter: {Plug.Adapters.Cowboy.Connection,
  {:http_req, #Port<0.7906>, :ranch_tcp, :keepalive, #PID<0.283.0>, "POST",
   :"HTTP/1.1", {{127, 0, 0, 1}, 52806}, "localhost", :undefined, 4000,
   "/alarms", :undefined, "", :undefined, [],
   [{"host", "localhost:4000"}, {"content-length", "9"},
    {"content-type", "application/x-www-form-urlencoded; charset=utf-8"},
    {"accept-encoding", "gzip, deflate, compress"}, {"accept", "*/*"},
    {"user-agent", "HTTPie/0.7.2"}], [], :undefined, [], :waiting, "alarm=foo",
   :undefined, false, :waiting, [], "", :undefined}}, assigns: [],
 before_send: [], cookies: Plug.Connection.Unfetched[aspect: :cookies],
 host: "localhost", method: "POST", params: [], path_info: ["alarms"],
 port: 4000, private: [], query_string: "",
 req_cookies: Plug.Connection.Unfetched[aspect: :cookies],
 req_headers: [{"host", "localhost:4000"}, {"content-length", "9"},
  {"content-type", "application/x-www-form-urlencoded; charset=utf-8"},
  {"accept-encoding", "gzip, deflate, compress"}, {"accept", "*/*"},
  {"user-agent", "HTTPie/0.7.2"}], resp_body: nil, resp_cookies: [],
 resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}],
 scheme: :http, state: :unset, status: nil]
pry(2)> conn.params
[]

Resources DSL should generate route names

I have detected that we are missing this while testing mix phoenix routes on example phoenix application.

Output is:

      GET     posts/:id                    Posts#show
      GET     posts/new                    Posts#new
      GET     posts                        Posts#index
      POST    posts                        Posts#create
      PUT     posts/:id                    Posts#update
      PATCH   posts/:id                    Posts#update
      DELETE  posts/:id                    Posts#destroy
      GET     posts/:post_id/comments/:id  Comments#show
      GET     posts/:post_id/comments/new  Comments#new
      GET     posts/:post_id/comments      Comments#index
      POST    posts/:post_id/comments      Comments#create
      PUT     posts/:post_id/comments/:id  Comments#update
      PATCH   posts/:post_id/comments/:id  Comments#update
      DELETE  posts/:post_id/comments/:id  Comments#destroy

While it should be something like this:

posts              GET     posts                        Posts#index
                   POST    posts                        Posts#create
new_post           GET     posts/new                    Posts#new
post               GET     posts/:id                    Posts#show
                   PUT     posts/:id                    Posts#update
                   PATCH   posts/:id                    Posts#update
                   DELETE  posts/:id                    Posts#destroy

post_comments      GET     posts/:post_id/comments      Comments#index
                   POST    posts/:post_id/comments      Comments#create
new_post_comment   GET     posts/:post_id/comments/new  Comments#new
post_comment       GET     posts/:post_id/comments/:id  Comments#show
                   PUT     posts/:post_id/comments/:id  Comments#update
                   PATCH   posts/:post_id/comments/:id  Comments#update
                   DELETE  posts/:post_id/comments/:id  Comments#destroy

To get this nice output resources routes should be also reordered. Obviously edit is missing but we have issue for that #19.

Could Not Compile Plug When do mix do deps.get, compile

Can't compile plug. This is the error message:

== Compilation error on file lib/plug/mime.ex == ** (CompileError) lib/plug/mime.ex:51: undefined function lc/2 (elixir) src/elixir.erl:189: :elixir.quoted_to_erl/3 (stdlib) erl_eval.erl:657: :erl_eval.do_apply/6 (elixir) src/elixir.erl:157: :elixir.erl_eval/2

could not compile dependency plug, mix compile failed. You can recompile this dependency with mix deps.compile plug or update it with mix deps.update plug

did mix deps.update plug and mix deps.update plug but same error message appear.

Did on Elixir 0.13.1 & 0.13.3-dev with same result

Allow mix phoenix.new to work within an existing mix project

I can imagine this working in two ways (or both)

  1. Creating a new module/supervisor within an existing OTP project. Initially the user would need to integrate this into their Application.Behaviour or just let the mix run router handle things
  2. If the project is currently an umbrella project, create a child project that can be integrated in.

Docs

I would like to create ex_docs site for Phoenix. It would work pretty much the same way as it works for Elixir. Make script would assume that ex_doc is installed in ../ex_doc and it would for generate docs for master (for now) and store in the ../docs. I guess we could host it through GitHub pages. @chrismccord can you please create docs repository?

I got inspiration for this while writing docs for routes scope macro and seeing that we could improve in this area. It's easier to evaluate docs through generated html docs.

Cowboy adapter expected __MODULE__ to return Plug.Conn but got: {:ok, Plug.Conn[adapter: {Plug.Adapters.Cowboy.Connection,

In my example application, I'm able to get websockets working just fine. However, there's an error report:

=ERROR REPORT==== 12-Feb-2014::01:17:26 ===
Error in process <0.229.0> with exit value: {[{reason,{'Elixir.RuntimeError','__exception__',<<2319 bytes>>}},{mfa,{'Elixir.Plug.Adapters.Cowboy.Handler',init,3}},{stacktrace,[{'Elixir.Plug.Adapters.Cowboy.Handler',init,3,[{file,"lib/plug/adapters/cowboy/handler.ex"},{line,13}]},...

Elixir.T3WebsocketServer.Router: get: ["favicon.ico"]

=ERROR REPORT==== 12-Feb-2014::01:17:26 ===
Ranch listener 'Elixir.T3WebsocketServer.Router.HTTP' had connection process started with cowboy_protocol:start_link/4 at <0.229.0> exit with reason: {[{reason,{'Elixir.RuntimeError','__exception__',<<"Cowboy adapter expected T3WebsocketServer.Router to return Plug.Conn but got: {:ok, Plug.Conn[adapter: {Plug.Adapters.Cowboy.Connection, {:http_req, #Port<0.4969>, :ranch_tcp, :keepalive, #PID<0.229.0>, \"GET\", :\"HTTP/1.1\", {{127, 0, 0, 1}, 64201}, \"localhost\", :undefined, 3030, \"/pages\", :undefined, \"\", :undefined, [], [{\"host\", \"localhost:3030\"}, {\"connection\", \"keep-alive\"}, {\"cache-control\", \"max-age=0\"}, {\"accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"}, {\"user-agent\", \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31\"}, {\"accept-encoding\", \"gzip,deflate,sdch\"}, {\"accept-language\", \"en-US,en;q=0.8\"}, {\"accept-charset\", \"ISO-8859-1,utf-8;q=0.7,*;q=0.3\"}, {\"cookie\", \"jirafe.metric.boxes=%7B%22revenue%22%3A%7B%7D%2C%22orders%22%3A%7B%7D%2C%22visits%22%3A%7B%7D%2C%22conv_rate%22%3A%7B%7D%2C%22aov%22%3A%7B%7D%2C%22rpv%22%3A%7B%7D%7D; jirafe.active.site_id=68472; _pk_id.68472.1fff=3961f189ff30bb0b.1369279691.6.1372000564.1371725123.\"}], [{\"connection\", [\"keep-alive\"]}], :undefined, [], :waiting, \"\", :undefined, false, :done, [], \"\", :undefined}}, assigns: [], cookies: Plug.Connection.Unfetched[aspect: :cookies], host: \"localhost\", method: \"GET\", params: [], path_info: [\"pages\"], port: 3030, query_string: \"\", req_cookies: Plug.Connection.Unfetched[aspect: :cookies], req_headers: [{\"host\", \"localhost:3030\"}, {\"connection\", \"keep-alive\"}, {\"cache-control\", \"max-age=0\"}, {\"accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"}, {\"user-agent\", \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31\"}, {\"accept-encoding\", \"gzip,deflate,sdch\"}, {\"accept-language\", \"en-US,en;q=0.8\"}, {\"accept-charset\", \"ISO-8859-1,utf-8;q=0.7,*;q=0.3\"}, {\"cookie\", \"jirafe.metric.boxes=%7B%22revenue%22%3A%7B%7D%2C%22orders%22%3A%7B%7D%2C%22visits%22%3A%7B%7D%2C%22conv_rate%22%3A%7B%7D%2C%22aov%22%3A%7B%7D%2C%22rpv%22%3A%7B%7D%7D; jirafe.active.site_id=68472; _pk_id.68472.1fff=3961f189ff30bb0b.1369279691.6.1372000564.1371725123.\"}], resp_body: nil, resp_cookies: [], resp_headers: [{\"cache-control\", \"max-age=0, private, must-revalidate\"}, {\"content-type\", \"text/html; charset=utf-8\"}], scheme: :http, state: :sent, status: 200]}">>}},{mfa,{'Elixir.Plug.Adapters.Cowboy.Handler',init,3}},{stacktrace,[{'Elixir.Plug.Adapters.Cowboy.Handler',init,3,[{file,"lib/plug/adapters/cowboy/handler.ex"},{line,13}]},{cowboy_handler,handler_init,4,[{file,"src/cowboy_handler.erl"},{line,69}]},{cowboy_protocol,execute,4,[{file,"src/cowboy_protocol.erl"},{line,471}]}]},{req,[{socket,#Port<0.4969>},{transport,ranch_tcp},{connection,keepalive},{pid,<0.229.0>},{method,<<"GET">>},{version,'HTTP/1.1'},{peer,{{127,0,0,1},64201}},{host,<<"localhost">>},{host_info,undefined},{port,3030},{path,<<"/pages">>},{path_info,undefined},{qs,<<>>},{qs_vals,undefined},{bindings,[]},{headers,[{<<"host">>,<<"localhost:3030">>},{<<"connection">>,<<"keep-alive">>},{<<"cache-control">>,<<"max-age=0">>},{<<"accept">>,<<"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8">>},{<<"user-agent">>,<<"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31">>},{<<"accept-encoding">>,<<"gzip,deflate,sdch">>},{<<"accept-language">>,<<"en-US,en;q=0.8">>},{<<"accept-charset">>,<<"ISO-8859-1,utf-8;q=0.7,*;q=0.3">>},{<<"cookie">>,<<"jirafe.metric.boxes=%7B%22revenue%22%3A%7B%7D%2C%22orders%22%3A%7B%7D%2C%22visits%22%3A%7B%7D%2C%22conv_rate%22%3A%7B%7D%2C%22aov%22%3A%7B%7D%2C%22rpv%22%3A%7B%7D%7D; jirafe.active.site_id=68472; _pk_id.68472.1fff=3961f189ff30bb0b.1369279691.6.1372000564.1371725123.">>}]},{p_headers,[{<<"connection">>,[<<"keep-alive">>]}]},{cookies,undefined},{meta,[]},{body_state,waiting},{buffer,<<>>},{multipart,undefined},{resp_compress,false},{resp_state,waiting},{resp_headers,[]},{resp_body,<<>>},{onresponse,undefined}]},{opts,{'Elixir.T3WebsocketServer.Router',[]}}],[{cowboy_protocol,execute,4,[{file,"src/cowboy_protocol.erl"},{line,471}]}]}

Mix task that lists all routes

It could be run with mix phoenix.routes and would print:

   GET  /events                   MyApp.Controllers.Events#index
   GET  /events/new               MyApp.Controllers.Events#new
  POST  /events                   MyApp.Controllers.Events#create
   GET  /events/:id               MyApp.Controllers.Events#show
   GET  /events/:id/edit          MyApp.Controllers.Events#edit
   PUT  /events/:id               MyApp.Controllers.Events#update
DELETE  /events/:id               MyApp.Controllers.Events#destroy

Cannot install phoenix

I went through the installation instructions and ran into the following issue when trying to fetch dependencies + compiling:

☁  phoenix [master] mix do deps.get, compile
** (SyntaxError) nofile:1: invalid token: %{"cowboy": {:git, "git://github.com/extend/cowboy.git", "05024529679d1d0203b8dcd6e2932cc2a526d370", []},
    (elixir) src/elixir.erl:113: :elixir.eval/3
    (elixir) lib/code.ex:130: Code.do_eval_string/3
    (mix) lib/mix/deps/lock.ex:58: Mix.Deps.Lock.read/0
    (mix) lib/mix/tasks/deps.get.ex:22: Mix.Tasks.Deps.Get.run/1
    (elixir) lib/enum.ex:517: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:517: Enum.each/2
    (mix) lib/mix/cli.ex:59: Mix.CLI.run_task/2

Installed elixir 0.12.5 via homebrew

Got massive spam: "String.from_char_list!/1 is deprecated"

Current master of elixir, phoenix, freshest erlang:
mix phoenix.start

String.from_char_list!/1 is deprecated, please use String.from_char_data!/1 instead
    (elixir) lib/string.ex:1204: String.from_char_list!/1
    lib/ex_conf/utils.ex:5: ExConf.Utils.capitalize/1
    lib/ex_conf/config.ex:96: ExConf.Config.conf_module_for_env/2
    lib/phoenix/plugs/code_reloader.ex:7: Phoenix.Plugs.CodeReloader.call/2
    lib/foo/router.ex:1: anonymous fn/1 in Foo.Router.call/2
    lib/phoenix/plugs/error_handler.ex:8: Phoenix.Plugs.ErrorHandler.wrap/3
    lib/plug/adapters/cowboy/handler.ex:9: Plug.Adapters.Cowboy.Handler.init/3

Router is not working for root path

With following route specified in routes file it is not routing correctly:

get "/", Blog.Controllers.Pages, :index, as: :page

It works with:

get "", Blog.Controllers.Pages, :index, as: :page

@chrismccord knows about this but I am just documenting it here so we don't forget.

Routing DSL changes

What do you think about following changes to routing DSL?

Currently DSL looks like this:

  defmodule Router do
    use Phoenix.Router
    get "users/:id", UsersController, :show, as: :user
    get "profiles/profile-:id", UsersController, :show
    get "users/top", UsersController, :top, as: :top
    get "route_that_crashes", UsersController, :crash
    get "files/:user_name/*path", FilesController, :show
    get "backups/*path", FilesController, :show
    get "static/images/icons/*image", FilesController, :show

    resources "comments", CommentsController
    get "users/:user_id/comments/:id", CommentsController, :show
  end

With changes:

  defmodule Router do
    use Phoenix.Router
    get "users/:id", to: "users#show", as: :user
    get "profiles/profile-:id", to: "users#show"
    get "users/top", to: "users#top", as: :top
    get "route_that_crashes", to: "users#crash"
    get "files/:user_name/*path", to: "files#show"
    get "backups/*path", to: "files#show"
    get "static/images/icons/*image", to: "files#show"

    resources :comments
    get "users/:user_id/comments/:id", to: "comments#show"
  end

Implementation would be pretty simple.

Discussion: Add view layer

Template engine wise, EEx has no great means for xss protection, but we don't have many other options yet. I'm hoping to include @nurugger07's haml implementation once it's ready (https://github.com/nurugger07/calliope). If we can get xss protection in EEx, I'd be perfectly happy with that as the default template engine, but José has said as implemented today with just string concat that it can't easily be done.

Ability to override not_found and error handlers

I suggest adding the possibility of overriding the default error and not found handlers.

Currently not_found, error and error_with_trace are implemented in Phoenix.Controller. The problem is that when executing the catch all match or in the error handling plug we don't have the actual controller in the scope, only the router.

I think one way to implement this would be to move these functions to Phoenix.Router and allow them to be overridden in the app router using behaviours.

What do you think?

Discussion: Configuration

Handling configuration is up for discussion. Currently, the only portion of the application that needs configuration support is the Router, but we need to think ahead on how we'd like to handle Phoenix configuration in general. I'd like to frame configuration within the context of a non-monolith setup. We have a couple options off the top of my head:

  1. Simple code based configuration. We could let other modules (i.e. Routers), specify a config module. i.e:
defmodule Router do
  use Phoenix.Router, port: 4000

  def conf, do: Conf

  resources "users", Users
  ...
end

defmodule Conf do
  def domain(:prod), do: "example.com"
  def domain(:dev),  do: "example.dev"

  def enforce_ssl?(:prod), do: true
  def enforce_ssl?(_),     do: false
end
  1. YAML/JSON configuration
    I'm not sure what existing tooling is available within Elixir/Erlang for such support. I'm a heavy user of YAML, and it typically works well, but my current thoughts are it's best to limit the number of serialization formats we use, and I'd prefer staying as close to pure code where possible. For example, we're obviously going to have JSON support in Phoenix, so if we go the serialized config route, maybe it makes sense to just stick a single format.

This is just a brain dump of ideas. I'd love to hear what everyone thinks and other solutions not mentioned. My current preference is code based configuration, but this rules out nesting, unless we write a Config lib that sprinkles in some macro facilities.

Leading slash on nested single routes breaks router

Example:

resources "comments", Controllers.Comments do
  get "/special", Controllers.Comments, :special # it works without leading slash
end

The matching path that we generate is comments/:comment_id//special. Solution should be just a bit smarter path join function.

Also throughout router tests and in examples we always specify single route path without leading slash. It's different from Rails. We can make a switch and require leading slash or just support both. Leading slash makes it somehow more clear that first param is path.

Resource limiting options should work for nested resources

We should be able to limit actions for parent resource.

Example:

resources "files", Controllers.Files, only: [:index] do
  resources "comments", Controllers.Comments, :except [:destroy]
end

Currently it's throwing following exception:

** (ArgumentError) argument error
    (stdlib) :lists.keyfind(:do, 1, {{:., [line: 66], [Kernel, :access]}, [line: 66], [:except, [:destroy]]})
    (elixir) lib/keyword.ex:120: Keyword.get/3
    (phoenix) lib/phoenix/router/mapper.ex:155: Phoenix.Router.Mapper."MACRO-resources"/4
    (phoenix) /home/darko/phoenix/test/phoenix/router/nested_routing_test.exs:66: Phoenix.Router.Mapper.resources/3
    /home/darko/phoenix/test/phoenix/router/nested_routing_test.exs:66: Phoenix.Router.NestedTest.Router (module)

Add cookie handling module

Currently I'm just dropping in Dynamo's cookie module. Seems like a common enough need for web applications that Phoenix should support this OOTB.

Namespaced resource route generates incorrect aliases

defmodule Router do
  use Phoenix.Router, port: 4000

  resources "admin/users", Phoenix.Controllers.Users do
    resources "comments", Phoenix.Controllers.Comments
  end
end

mix phoenix.routes Router

       admin/users  GET     admin/users                Users#index
   edit_admin/user  GET     admin/users/:id/edit       Users#edit
        admin/user  GET     admin/users/:id            Users#show
    new_admin/user  GET     admin/users/new            Users#new

We need to detect these scenarios and convert to underscores

Dashes as URL separator

The implementation of Phoenix.Router.Path.split will not allow using dashes in the value of a named parameter.

For example, this route:

get "/resource/:id", App.Controllers.Resource, :show

will not match http://localhost:4000/resource/75f6306d-a090-46f9-8b80-80fd57ec9a41

But if we define a route that will match http://localhost:4000/resource?id=75f6306d-a090-46f9-8b80-80fd57ec9a41

we'll have the UUID with dashes included available in conn.params["id"]

Maybe I'm missing something, but I think the two cases above should yield similar results and dashes should not be considered a URL separator in the split function.

Forced to define `init/1` in router

In my example project, if I do not define init/1, it blows up with:

=INFO REPORT==== 12-Feb-2014::01:14:48 ===
    application: t3_websocket_server
    exited: {{shutdown,
                 {failed_to_start_child,'Elixir.T3WebsocketServer.Router',
                     {'EXIT',
                         {undef,
                             [{'Elixir.T3WebsocketServer.Router',init,[[]],[]},
                              {'Elixir.Plug.Adapters.Cowboy',run,4,
                                  [{file,"lib/plug/adapters/cowboy.ex"},
                                   {line,105}]},
                              {supervisor,do_start_child,2,
                                  [{file,"supervisor.erl"},{line,308}]},
                              {supervisor,start_children,3,
                                  [{file,"supervisor.erl"},{line,291}]},
                              {supervisor,init_children,2,
                                  [{file,"supervisor.erl"},{line,257}]},
                              {gen_server,init_it,6,
                                  [{file,"gen_server.erl"},{line,304}]},
                              {proc_lib,init_p_do_apply,3,
                                  [{file,"proc_lib.erl"},{line,239}]}]}}}},
             {'Elixir.T3WebsocketServer',start,[normal,[]]}}
    type: temporary
** (Mix) Could not start application t3_websocket_server: {{:shutdown, {:failed_to_start_child, T3WebsocketServer.Router, {:EXIT, {:undef, [{T3WebsocketServer.Router, :init, [[]], []}, {Plug.Adapters.Cowboy, :run, 4, [file: 'lib/plug/adapters/cowboy.ex', line: 105]}, {:supervisor, :do_start_child, 2, [file: 'supervisor.erl', line: 308]}, {:supervisor, :start_children, 3, [file: 'supervisor.erl', line: 291]}, {:supervisor, :init_children, 2, [file: 'supervisor.erl', line: 257]}, {:gen_server, :init_it, 6, [file: 'gen_server.erl', line: 304]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 239]}]}}}}, {T3WebsocketServer, :start, [:normal, []]}}

Installing ranch/rebar failed

I just did a

brew update
brew install erlang --devel # to get r17
brew install elixir # to get 0.13.0
git clone https://github.com/phoenixframework/phoenix.git && cd phoenix && mix do deps.get, compile

and the compile stage died with

* Compiling ranch
Could not find rebar, which is needed to build ranch
I can install a local copy which is just used by mix
Shall I install this local copy? [Yn]
** (Mix) Could not access url http://elixir-lang.org/rebar, error: :no_scheme

Am I missing something? I'm very new to elixir so I might be doing something silly. I also don't know what other info you need.

Thanks!

Move project to phoenixframework/phoenix

I set up a github org and will be moving the code over soon. So don't be alarmed if your push fails and you need to update your remote refs. The org will help give the project a face and allow easier contributor management. I also have phoenixframework.org where I'd like to see eventually see docs, getting started guides, etc.

Improve 'Creating a new phoenix application'

Two things would be nice

  1. Give the step by step instructions of cloning, compiling and creating the first project
  2. Have a curl executable script to get and compile phoenix

Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.