Coder Social home page Coder Social logo

dynamo's Introduction

Dynamo

Build Status

Run, Dynamo, Run!

Dynamo is an experimental web framework that runs on Elixir. It leverages the power of the Erlang VM to build highly performant and concurrent web applications. Dynamo's goals are performance, robustness and simplicity.

As an experiment, Dynamo is as alpha-software as alpha gets. The main goal for Dynamo was to be a testbed for ideas and many of its APIs were extracted and improved into separated projects like Plug.

Dynamo is currently in maintenance mode for those who are already using it in production. Developers who seek for an actively developed framework are recommended to look for other alternatives in Elixir.

Description

Dynamo shows excellent performance out of the box, beating similar frameworks like Sinatra and Express, available in other languages, in a callback-free programming fashion. By using the Erlang VM, all of the network I/O is asynchronous but your code appears to be synchronous. The Erlang VM also allows you to use all cores available by default, without a need to start many instances of your web server, and performs well under heavy load with many concurrent open connections.

On the developer side, Dynamo focuses on simplicity by shipping with a bare stack, allowing a team to get started quickly while making it easy to extend the application as and when they see fit.

It is currently alpha software and it supports:

  • Light-weight connections with streaming.
  • Code organization in environments.
  • Code reloading for fast development cycles.
  • Lazy parsing and handling of cookies, sessions, params and headers.
  • Handling of static assets.
  • Template rendering.
  • An exceptional exception handler for development.
  • Integration with Erlang OTP applications.

This README will go into Dynamo installation and a basic walk-through.

Installation

As an alpha-software, Dynamo installation is a bit manual but can be done in few steps:

  1. Ensure you are on Elixir master (available on Homebrew, see below)

  2. Clone this repository and go to its directory

  3. Get Dynamo dependencies and compile: mix do deps.get, compile

  4. Create a project: mix dynamo path/to/your/project

Congratulations! You created your first project with Dynamo! Let's run it:

  1. Go to your app

  2. Get dependencies and compile with: mix do deps.get, compile

  3. Run it: iex -S mix server

If you do not want a console, then start the app with mix server.

Check the lib/ and web/ folders for more information. Changes done in the web directory are picked up without a need to reload the server.

Static content is served from priv/static/ folder and from the /static route.

Your project can be compiled and used in production with: MIX_ENV=prod mix do compile, server.

Installation using the master branch of Elixir

Elixir evolves rapidly and Dynamo follows it. If you get build or test errors, please use the current master branch of Elixir before reporting any bugs.

If you're on OSX and use Homebrew, this is easily done as follows:

$ brew unlink elixir # remove non-head installation of Elixir
$ brew install elixir --HEAD

Walk-through

This walk-through is going to put you in touch with the four main elements in Dynamo: routers, connection, OTP and the Dynamo itself. This walk-through requires you to be familiar with Elixir, so please read Elixir's getting started guide if you haven't yet.

Routers

A Dynamo is organized in routers. Routers are kept in the web/routers directory and by default a project contains an ApplicationRouter defined at web/routers/application_router.ex, which is your Dynamo main entry point:

defmodule ApplicationRouter do
  use Dynamo.Router

  prepare do
    # Pick which parts of the request you want to fetch
    # You can comment the line below if you don't need
    # any of them or move them to a forwarded router
    conn.fetch([:cookies, :params])
  end

  # It is common to break your Dynamo in many
  # routers forwarding the requests between them
  # forward "/posts", to: PostsRouter

  get "/" do
    conn = conn.assign(:title, "Welcome to Dynamo!")
    render conn, "index.html"
  end
end

All routers must use the Dynamo.Router module. By using this module you have access to the macros get, post, put, patch, delete and options that allows developers to generate routes. Here is a simple route:

get "/hello/world" do
  conn.resp(200, "Hello world")
end

The conn value above represents a connection, about which we are going to get into more details soon. In the example above in particular, we are setting the connection to have response status 200 with the body "Hello world". Routes can also contain dynamic segments:

put "/users/:user_id" do
  conn.resp 200, "Got user id: #{conn.params[:user_id]}"
end
get "/hello/*" do
  conn.resp 200, "Match all routes starting with /hello"
end

Each route is compiled down to a function clause, making route matching extremely fast and also allows the use of guard constraints:

get "/section/:section" when section in ["contact", "about"] do
  render conn, "#{section}.html"
end

In general, Dynamos are broken into many routers, with requests forwarded between them. Furthermore, routers also provide hooks, such as the prepare hook seen above.

Hooks

Hooks are a mechanism to encapsulate actions that are shared between many routes. For example, one may write a hook that only allows requests with a given parameter to proceed:

prepare do
  unless conn.params[:user_info] do
    halt! conn.status(400)
  end
end

Another common functionality in hooks is to prepare the connection, sometimes by setting a layout:

prepare do
  conn.assign :layout, "home.html"
end

Or by fetching information used throughout all routes:

prepare do
  conn.fetch :params
end

There is also a finalize hook which is used after the request is processed.

Besides adding hooks to a router as a whole, hooks can also be added to a specific route using the @prepare and @finalize attributes before each route:

@prepare :authenticate_user
get "/users/:user_id" do
  # ...
end

defp authenticate_user(conn) do
  unless get_session(conn, :user_id) do
    halt! conn.status(401)
  end
end

Forwarding

As your Dynamo grows, it would be impractical to have all routes in a single router. For this reason, Dynamo allows you to easily compose and forward requests between routers. Let's consider the router generated by default:

defmodule ApplicationRouter do
  use Dynamo.Router

  prepare do
    conn.fetch([:cookies, :params])
  end

  forward "/posts", to: PostsRouter

  get "/" do
    conn = conn.assign(:title, "Welcome to Dynamo!")
    render conn, "index.html"
  end
end

In the example above, all routes starting with /posts will be automatically routed to the PostsRouter. Furthermore, the PostsRouter will receive on the trailing part of the url. A request to /posts/recent will be seen by the PostsRouter as /recent:

defmodule PostsRouter do
  use Dynamo.Router

  get "/recent" do
    # Get all recent posts
  end
end

Finally, in the ApplicationRouter above, notice we fetch cookies and the request params for every request. However, if just the PostsRouter is using such information, the prepare hook could be moved to inside the PostsRouter, so it will be fetched only when it is needed.

This forwarding mechanism makes it very easy to split and organize your code into its logical sections while also having control on which features are required by each router.

Connection

The connection plays a huge part when building Dynamos. As we have seen above, each route has access to the connection as the variable conn. The authenticate_user prepare hook defined above also receives the connection as an argument:

defp authenticate_user(conn) do
  unless get_session(conn, :user_id) do
    halt! conn.status(401)
  end
end

The connection is a direct interface to the underlying web server. In the examples above, we have used conn.resp(status, body) to set up our response. This will set a status and a response that will be sent back to the client after the route finishes processing.

However, a developer could also use conn.send(status, body), which will send the response immediately:

conn.send(200, "Hello world")

In the example above there is no wait and the response is sent immediately. The fact the connection is a direct interface makes streaming equally trivial:

conn = conn.send_chunked(200)
conn.chunk("Hello World")

There are no dirty hacks nor work-around required. You get a fast and clean API to interact with the web server.

Immutability

One common confusion for developers starting with Dynamo (and to certain extent functional programming too) is immutability.

Elixir data structures are immutable. If you have a tuple, you can't modify this tuple in place. Adding or removing elements will actually return a new tuple:

first  = { 1, 2, 3 }
second = set_elem(first, 0, :hello)

first  #=> { 1, 2, 3 }
second #=> { :hello, 2, 3 }

Notice how the elements of the first tuple remain intact. The same happens with the connection data structure. Consider this route:

get "/" do
  conn.assign(:title, "Welcome to Dynamo!")
  render conn, "index.html"
end

The first line conn.assign(:title, "Welcome to Dynamo!") has no effect whatsoever because we don't store the result of the operation anywhere. In fact, if someone writes IO.inspect conn.assigns[:title] in the next line, the value of the :title assign will still be nil. That said, as we do with tuples, we need to store the new connection in the updated variable:

get "/" do
  conn = conn.assign(:title, "Welcome to Dynamo!")
  render conn, "index.html"
end

Almost all functions throughout Dynamo will receive and return a connection. For example, the render function above will return a new connection with the render template as a body. Similarly, all routes and hooks must return the connection, you will get a gentle exception if you don't.

APIs

The connection data structure keeps the low-level API to interact with web servers. You can set status codes and response bodies, handle headers and parse request information as :params, :cookies, :session and so forth.

Furthermore, the connection is responsible to carry out the information in between routers and between routers and templates via assigns. For example:

get "/" do
  conn = conn.assign(:title, "Welcome to Dynamo!")
  render conn, "index.html"
end

A prepare hook may also set the a current_user assign which could then be retrieved in any router as conn.assigns[:current_user]. You can find more information about assigns and other connection functions in Dynamo.Connection.

Dynamo also builds many functionalities on top of this low-level connection API:

All those functions in Dynamo.HTTP.* are imported by default into your Dynamo.Router.

Templating

Dynamo templates uses EEx to let you embed Elixir logic into your HTML templates. For example, if you had this route:

get "/fruits" do
  conn = conn.assign(:fruits, [{0, "Apple"}, {1, "Orange"}, {2, "Banana"}])
  render conn, "fruits.html"
end

To display the list of fruits, fruits.html.eex might look like:

<html>
<body>
  <ul>
  <%= for { id, name } <- @fruits do %>
    <li><a href="/fruit/<%= id %>"><%= name %></a></li>
  <% end %>
  </ul>
</body>
</html>

Notice we have used list comprehensions but any of the Enum functions, like Enum.filter/2 and Enum.map/2 are also available.

OTP application

The next two sections will focus on the Dynamo integration with OTP, which is a set of tools and libraries for building robust, fault-tolerant applications. Talking about "applications", you may have noticed throughout this tutorial we haven't used the word "application" a lot so far. This is intentional as there is no such thing as a "Dynamo application"!

I will explain. When we first invoked mix dynamo path/to/your/project, it generated a project. This project may contain one or more OTP applications (by default, just one) and the Dynamo is always part of an OTP application.

In this section we are going to detail what it means to be an OTP application. Before continuing, make sure you have the guide about building OTP apps with Mix, because we will build on top of it.

Getting the names right

To recap, when we invoke mix dynamo path/to/your/project, we have:

  • a project - the project is everything that was generated and its backbone is the mix.exs file that contains the project dependencies, tasks to compile and run tests and much more;

  • one or more applications - a project may contain one or more application, but most of the cases, it contains just one. In general, an application is an artifact generated by a project. For example, when you compile your code, you will have a bunch of .beam files and an .app file inside the ebin directory. The priv directory is also part of the application and it contains everything that is needed at runtime but is not source code, like assets, database configuration, etc. Your application also has dependencies and they are a subset of your project dependencies (in general, test and compile-time dependencies are not application dependencies);

  • one or more dynamos - one application may contain one or more dynamos. Yes, you heard it right: Dynamo was designed from the ground up to be isolated, so you can have many Dynamos, listening to and serving different ports in the same project and running in the same OS process.

Applications and supervisors

A project generated with mix dynamo contains the same elements as a basic OTP application. Assume we generate the same project as we did in building OTP apps with Mix but now using the dynamo command:

$ mix dynamo stacker

In the mix.exs file, we will see the project definition, containing the main application, the version and the project dependencies. We will also see a function named application, this function returns the application specification:

def application do
  [ applications: [:cowboy, :dynamo],
    mod: { Stacker, [] } ]
end

This application specification declares it depends on two other applications, in this case :cowboy (the web server) and :dynamo (this web framework). It also specifies an application callback. The application callback is the module Stacker and it will be initialized receiving an empty list as the argument.

If we open up the lib/hello.ex file, we can see the application callback implementation:

defmodule Stacker do
  use Application.Behaviour

  @doc """
  The application callback used to start this
  application and its Dynamos.
  """
  def start(_type, _args) do
    Stacker.Dynamo.start_link([max_restarts: 5, max_seconds: 5])
  end
end

As explained in the building OTP apps with Mix guide, the application callback must return the PID of an OTP Supervisor. In this case, we are returning the PID of the Dynamo supervisor. In this case, if our Dynamo fails, the Erlang VM will act on it.

In case your application has other workers and supervisors, you can create your own supervisor tree and simply embed the Dynamo supervisor in your own tree. For example, imagine we copy both Stacker.Supervisor and Stacker.Server from the Mix guide, the supervisor looks like this:

defmodule Stacker.Supervisor do
  use Supervisor.Behaviour

  # A convenience to start the supervisor
  def start_link(stack) do
    :supervisor.start_link(__MODULE__, stack)
  end

  # The callback invoked when the supervisor starts
  def init(stack) do
    children = [ worker(Stacker.Server, [stack]) ]
    supervise children, strategy: :one_for_one
  end
end

Notice this supervisor has its own worker, called Stacker.Server, that should be part of the supervision tree. We can embed the Dynamo supervisor in that tree by changing the init function:

def init(stack) do
  children = [
    worker(Stacker.Server, [stack]),
    supervisor(Stacker.Dynamo, [])
  ]
  supervise children, strategy: :one_for_one
end

Now all we need to do is to change the Stacker module to initialize the new supervisor tree instead of the Dynamo directly:

defmodule Stacker do
  use Application.Behaviour

  def start(_type, _args) do
    Stacker.Supervisor.start_link([])
  end
end

This shows that your application is the one in control and a Dynamo is simply embedded into it.

Dynamo

So what is a Dynamo after all? A Dynamo is nothing more than a module which can be found at lib/*/dynamo.ex of a newly generated project. Here is an example:

defmodule Stacker.Dynamo do
  use Dynamo

  config :dynamo,
    env: Mix.env,
    otp_app: :stacker,
    endpoint: ApplicationRouter,
    static_route: "/static"
end

When you run mix compile, this Dynamo is compiled and becomes functional when you start your application supervision tree, as we just discussed. The Dynamo is responsible for handling requests, compiling and managing the files under the web directory, and each Dynamo also has its own supervisor tree.

Finally, notice that in the same directory you find your Dynamo you can also find an environments directory which contains configuration specific for each environment. Take a look at them for examples on the available configuration options.

Summing up, when you run mix dynamo stacker to create a project and then fetch its dependencies, this is the final structure you get:

- deps                        # Your project dependencies
- lib                         # Contains your application files
  - lib/stacker.ex            # Your application callback
  - lib/stacker/dynamo.ex     # The Dynamo definition
  + lib/stacker/environments  # Dynamo environment related configuration
- mix.exs                     # Your project specification
- priv                        # Application files that are needed at runtime
  + priv/static               # Static assets (they are needed on runtime!)
- test                        # Your test files
  + test/features             # End-to-end tests
  + test/routers              # Routers unit tests
- web                         # Files managed and compiled by the Dynamo
  + web/routers               # Your application routers

That's all. If you haven't built an OTP application before, you may be a bit overwhelmed but there is nothing stop you from diving into the web directory and learning about OTP just when you need it. Before you know it, you will be leveraging the power of the Erlang VM to build robust, high performance and concurrent web applications!

Learn more

In the guides directory we include a bunch of small, simple guides that teach you how to achieve something in Dynamo:

There are also a number of examples of dynamo features in the examples/ directory. Look at the top each file for directions of how to run them.

License

Dynamo source code is released under Apache 2 License. Check LICENSE file for more information.

dynamo's People

Contributors

alandyer avatar alanpeabody avatar bbrietzke avatar carlosantoniodasilva avatar catsby avatar ericmj avatar guilleiguaran avatar hamiltop avatar hashnuke avatar jni- avatar josevalim avatar jwarwick avatar kimshrier avatar lau avatar mikepack avatar parroty avatar pauldub avatar pedrosnk avatar philipcristiano avatar quinnwilton avatar rafaelfranca avatar rcoppolo avatar rondy avatar sausheong avatar scrogson avatar seletz avatar stevedomin avatar thiagofm avatar timbuchwaldt avatar zatvobor 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

dynamo's Issues

Test compilation fails

I'm working on getting Dynamo running locally, and I'm having difficulty running the tests. I'm receiving the following error:

$ mix test
** (MatchError) no match of right hand side value: :undefined
    /Users/Mike/Documents/Work/dynamo/lib/dynamo.ex:135: Dynamo.under_test/0
    /Users/Mike/Documents/Work/dynamo/test/dynamo/filters/head_test.exs:5: Kernel.defmodule/2
    src/elixir_compiler.erl:62: :elixir_compiler."-eval_forms/4-fun-0-"/6
    src/elixir_compiler.erl:61: :elixir_compiler.eval_forms/4
    src/elixir_module.erl:132: :elixir_module.eval_form/5
    src/elixir_module.erl:59: :elixir_module.compile/5
    src/elixir_compiler.erl:62: :elixir_compiler."-eval_forms/4-fun-0-"/6
    src/elixir_compiler.erl:61: :elixir_compiler.eval_forms/4

Some rudimentary debugging tells me the under_test/1 is not being evaluated before under_test/0 is evaluated, leaving the RHS :undefined. My Elixir/Erlang is weak, so I'm not able to debug much further.

I'm running Elixir HEAD on Erlang R15B03:

Erlang R15B03 (erts-5.9.3) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false] [dtrace]

Ranch/Cowboy dependencies old

I assume I can fix this with changes to the buildpack but thought I'd open an issue to update Ranch anyway.

Locally the mix.lock file is fine and I can build an app. But pushing to Heroku it fails to compile -- though both are using R16B and Elixir master:

==> ranch (compile)
compile: warnings being treated as errors
Compiled src/ranch_protocol.erl
src/ranch_tcp.erl:21: behaviour ranch_transport undefined
Compiled src/ranch_listener_sup.erl
** (Mix) could not compile dependency ranch, rebar command failed. In case you want to recompile this dependency, please run: mix deps.compile ranch

Removing mix.lock so that it grabs the latest ranch and cowboy allows it to compile both locally and on Heroku.

But, this is what causes the crash on dispatch in the router. Dynamo relies on the old version of Ranch and Cowboy (0.4.x and 0.6.x).

Will look at both getting the compile to work as is and what it'd take to update Dynamo for current Ranch and Cowboy.

Add a step 0 to README.md

Just a minor suggestion. You might want to mention that you need a recent rebar executable in your search path. I ran into this problem when I was building dynamo on a machine that had Erlang and Elixir but I hadn't put rebar into /usr/local/bin.

Setting :source_paths in dynamo config doesn't work as expected

I would like to have modules in the /lib directory to be autocratically reloaded, just as modules in the /web directory.

If I understand the documentation correctly, this should be possible by setting the :source_paths property of the dynamo config. However, this does not seem to work for anything other than the web directory.

For reference, this my environments/dev.exs:

config :dynamo,                                                                                                                       
  # Compile modules as they are accessed.                                                                                             
  # This makes development easy as we don't                                                                                           
  # need to explicitly compile files.                                                                                                 
  compile_on_demand: true,                                                                                                            

  # Every time a module in web/ changes, we                                                                                           
  # will clean up defined modules and pick                                                                                            
  # up the latest versions.                                                                                                           
  reload_modules: true,                                                                                                               

  # Do not cache static assets, so they                                                                                               
  # are reloaded every page in development                                                                                            
  cache_static: false,                                                                                                                

  # Show a nice debugging exception page                                                                                              
  # in development                                                                                                                    
  exceptions_handler: Exceptions.Debug,                                                                                               

  source_paths: ["lib/*", "web/*"]                                                                                                    


# Run on port 4000 for development                                                                                                    
config :server, port: 4000

{:undef,[{DynamoHerokuDemo.Dynamo.CompiledTemplates,:requires_precompilation?,[],[]}

So yesterday https://github.com/tsloughter/dynamo_heroku_demo worked fine on Heroku. Doing a fresh deploy myself, and others have experienced the same, the following crash occurs on startup:

** (Mix) Could not start application dynamo_heroku_demo: {:bad_return,{{DynamoHerokuDemo,:start,[:normal,[]]},{:EXIT,{:undef,[{DynamoHerokuDemo.Dynamo.CompiledTemplates,:requires_precompilation?,[],[]},{Enum,:do_all?,2,[file: '/tmp/build_2uekmxivc70z3/ex/lib/elixir/lib/enum.ex', line: 1231]},{DynamoHerokuDemo.Dynamo,:initializer_start_dynamo_renderer,0,[file: '/tmp/build_2uekmxivc70z3/deps/dynamo/lib/dynamo.ex', line: 192]},{DynamoHerokuDemo.Dynamo,:run_initializers,0,[file: '/tmp/build_2uekmxivc70z3/deps/dynamo/lib/dynamo/base.ex', line: 191]},{DynamoHerokuDemo.Dynamo,:start_link,1,[file: '/tmp/build_2uekmxivc70z3/deps/dynamo/lib/dynamo.ex', line: 152]},{:application_master,:start_it_old,4,[file: 'application_master.erl', line: 274]}]}}}}

It is the same mix.lock as the working deployed copy so I'm not sure where to look for what could have changed.

Does the error message make any sense to you for where I should be looking?

Directory name different from the application name.

Is there a way to have a different root directory name from the application name?

If the root directory name is different it doesn't work in test environment (can't find ApplicationRouter), and in production environment it throws an error after serving the request (talking about directory name being different).

Depending on the directory name to make mix server work seems to be kind of prone to errors.

Enum.member? isn't yet available in Elixir 0.8.2

** (UndefinedFunctionError) undefined function: Enum.member?/2
    Enum.member?([], Dynamo.Base)
    ..../lib/dynamo/utils/once.ex:25: Dynamo.Utils.Once."MACRO-use_once"/2
    ..../test/dynamo/cowboy/router_test.exs:7: Dynamo.Utils.Once.use_once/1

Solved by brew upgrade elixir --HEAD

Tests still breaking for dynamo on fresh installed, I pulled from origin.

dynamo [master]$ MIX_ENV=test mix do deps.get, test

All dependencies up to date
........................................................................................................
=ERROR REPORT==== 24-Jan-2013::10:48:02 ===
Conn: GET /error
Reason: (RuntimeError) Oops
Stacktrace:
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:48: Dynamo.Filters.ExceptionsTest.ExceptionsApp.dispatch/3
/Users/callen/code/dynamo/lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
/Users/callen/code/dynamo/lib/dynamo/http/case.ex:190: Dynamo.HTTP.Case.do_process/2
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:104: Dynamo.Filters.ExceptionsTest."test invokes handler on exceptions"/0
/Users/callen/code/elixir/lib/ex_unit/lib/ex_unit/runner.ex:90: ExUnit.Runner.run_test/5
lists.erl:1262: :lists.foreach/2
/Users/callen/code/elixir/lib/elixir/lib/enum.ex:274: Enum.each/2
....
=ERROR REPORT==== 24-Jan-2013::10:48:02 ===
Conn: GET /unauthorized
Reason: (Dynamo.Filters.ExceptionsTest.UnauthorizedError) Unauthorized
Stacktrace:
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:58: Dynamo.Filters.ExceptionsTest.ExceptionsApp.dispatch/3
/Users/callen/code/dynamo/lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
/Users/callen/code/dynamo/lib/dynamo/http/case.ex:190: Dynamo.HTTP.Case.do_process/2
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:110: Dynamo.Filters.ExceptionsTest."test requests status code on exceptions"/0
/Users/callen/code/elixir/lib/ex_unit/lib/ex_unit/runner.ex:90: ExUnit.Runner.run_test/5
lists.erl:1262: :lists.foreach/2
/Users/callen/code/elixir/lib/elixir/lib/enum.ex:274: Enum.each/2
.................................
=ERROR REPORT==== 24-Jan-2013::10:48:02 ===
Conn: GET /other
Reason: (Dynamo.NotFoundError) no route found
Stacktrace:
test/dynamo/cowboy/router_test.exs:178: Dynamo.Cowboy.RouterTest.RouterApp.dispatch/3
lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
lib/dynamo/cowboy/handler.ex:15: Dynamo.Cowboy.Handler.init/3
src/cowboy_protocol.erl:477: :cowboy_protocol.handler_init/4
..................................................................F.......................

  1. test compiles an application (Mix.TasksTest)
    ** (ExUnit.ExpectationError)
    expected: "Compiled lib/my_app.ex\nCompiled lib/my_app/dynamo.ex\nGenerated my_compiled_app.app\n"
    to be a match (=~) with: %r"Compiled web/routers/application_router.ex"
    stacktrace:
    /Users/callen/code/dynamo/test/mix/tasks_test.exs:12: Mix.TasksTest."-test compiles an application/0-fun-0-"/0
    /Users/callen/code/elixir/lib/elixir/lib/file.ex:972: File.cd!/2
    /Users/callen/code/dynamo/test/mix/tasks_test.exs:8: Mix.TasksTest."test compiles an application"/0

231 tests, 1 failures.

Set Port at Runtime in Production

In order to run on services like Heroku there must be a way to set the port number when the application is started. While Heroku sets the os var PORT it would be better to be more generic and either let a port be passed somewhere as an argument in the code or to mix like: mix server --port 8000u

Custom port almost works :)

~/tmp/testdynamo ❯❯❯ mix server -p=4001
Running DynamoHead.Dynamo at http://localhost:4001 with Cowboy on dev

And then ...

~/tmp/testdynamo ❯❯❯ sudo lsof -n -i4TCP:4001
~/tmp/testdynamo ❯❯❯

Same with (export MIX_ENV=prod ; mix server -p=4001)

Option parsing works though. B )

Problem fetching latest updates

I have cloned dynamo into my github account and then cloned that onto my laptop. I am having a problem fetching the latest updates so that I can merge them to my github clone.

On my laptop, I have:

$ git remote -v
origin  https://github.com/kimshrier/dynamo.git (fetch)
origin  https://github.com/kimshrier/dynamo.git (push)
upstream    https://github.com/josevalim/dynamo.git (fetch)
upstream    https://github.com/josevalim/dynamo.git (push)

When I try to fetch the latest updates, I get:

$ git fetch upstream
fatal: https://github.com/josevalim/dynamo.git/info/refs?service=git-upload-pack not found: did you run git update-server-info on the server?

I am not sure how to remedy this but the error message implies that something needs to be done on https://github.com/josevalim/dynamo.git.

Any help would be appreciated.

Starting application fails

From within a fresh Dynamo master clone, I run the following commands:

$ mix dynamo ../dynamo_app
$ cd ../dynamo_app
$ mix do deps.get, server
** (Mix) The task app.start could not be found

mix help doesn't list app.start, or the app namespace at all. Looks like dynamo.start.ex is attempting to run it.

Got elixir in my env from the git repo, tried to get dynamo rolling again. Unit test fails

From running:

MIX_ENV=test mix do deps.get, test

Generated dynamo.app
.....................................................................................................
=ERROR REPORT==== 24-Jan-2013::10:24:56 ===
Conn: GET /error
Reason: (RuntimeError) Oops
Stacktrace:
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:48: Dynamo.Filters.ExceptionsTest.ExceptionsApp.dispatch/3
/Users/callen/code/dynamo/lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
/Users/callen/code/dynamo/lib/dynamo/http/case.ex:190: Dynamo.HTTP.Case.do_process/2
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:104: Dynamo.Filters.ExceptionsTest."test invokes handler on exceptions"/0
/Users/callen/code/elixir/lib/ex_unit/lib/ex_unit/runner.ex:90: ExUnit.Runner.run_test/5
lists.erl:1262: :lists.foreach/2
/Users/callen/code/elixir/lib/elixir/lib/enum.ex:274: Enum.each/2
......
=ERROR REPORT==== 24-Jan-2013::10:24:56 ===
Conn: GET /unauthorized
Reason: (Dynamo.Filters.ExceptionsTest.UnauthorizedError) Unauthorized
Stacktrace:
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:58: Dynamo.Filters.ExceptionsTest.ExceptionsApp.dispatch/3
/Users/callen/code/dynamo/lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
/Users/callen/code/dynamo/lib/dynamo/http/case.ex:190: Dynamo.HTTP.Case.do_process/2
/Users/callen/code/dynamo/test/dynamo/filters/exceptions_test.exs:110: Dynamo.Filters.ExceptionsTest."test requests status code on exceptions"/0
/Users/callen/code/elixir/lib/ex_unit/lib/ex_unit/runner.ex:90: ExUnit.Runner.run_test/5
lists.erl:1262: :lists.foreach/2
/Users/callen/code/elixir/lib/elixir/lib/enum.ex:274: Enum.each/2
..................................
=ERROR REPORT==== 24-Jan-2013::10:24:57 ===
Conn: GET /other
Reason: (Dynamo.NotFoundError) no route found
Stacktrace:
test/dynamo/cowboy/router_test.exs:178: Dynamo.Cowboy.RouterTest.RouterApp.dispatch/3
lib/dynamo/filters/exceptions.ex:15: Dynamo.Filters.Exceptions.service/3
lib/dynamo/cowboy/handler.ex:15: Dynamo.Cowboy.Handler.init/3
src/cowboy_protocol.erl:477: :cowboy_protocol.handler_init/4
..................................................................F.......................

  1. test compiles an application (Mix.TasksTest)
    ** (ExUnit.ExpectationError)
    expected: "Compiled lib/my_app.ex\nCompiled lib/my_app/dynamo.ex\nMix.Project.sources is deprecated, please use Mix.Project.config_files instead\n /Users/callen/code/elixir/lib/elixir/lib/exception.ex:125: Exception.print_stacktrace/1\n /Users/callen/code/elixir/lib/mix/lib/mix/project.ex:114: Mix.Project.sources/0\n /Users/callen/code/dynamo/lib/mix/tasks/compile.dynamo.ex:55: Mix.Tasks.Compile.Dynamo.do_compile/3\n lists.erl:1197: :lists.foldl/3\n /Users/callen/code/elixir/lib/mix/lib/mix/tasks/compile.ex:70: Mix.Tasks.Compile."-run/1-fun-2-"/3\n lists.erl:1197: :lists.foldl/3\n /Users/callen/code/elixir/lib/mix/lib/mix/utils.ex:101: Mix.Utils.preserving_mtime/2\nGenerated my_compiled_app.app\n"
    to be a match (=~) with: %r"Compiled web/routers/application_router.ex"
    stacktrace:
    /Users/callen/code/dynamo/test/mix/tasks_test.exs:12: Mix.TasksTest."-test compiles an application/0-fun-0-"/0
    /Users/callen/code/elixir/lib/elixir/lib/file.ex:972: File.cd!/2
    /Users/callen/code/dynamo/test/mix/tasks_test.exs:8: Mix.TasksTest."test compiles an application"/0

Compilation error on trying to use Dynamo for the first time

Elixir noob, just trying to tinker with Dynamo to see if it fits my web dev work.

After running:
MIX_ENV=test mix do deps.get, test

I get:

==> hackney (compile)
Compiled src/hackney_transform.erl
Compiled src/hackney_tcp_transport.erl
Compiled src/hackney_sup.erl
Compiled src/hackney_ssl_transport.erl
Compiled src/hackney_util.erl
Compiled src/hackney_url.erl
Compiled src/hackney_pool.erl
Compiled src/hackney_request.erl
Compiled src/hackney_headers.erl
Compiled src/hackney_multipart.erl
Compiled src/hackney_response.erl
Compiled src/hackney_form.erl
Compiled src/hackney_app.erl
Compiled src/hackney_deps.erl
Compiled src/hackney.erl
== Compilation error on file lib/dynamo.ex ==
** (SyntaxError) /Users/callen/code/dynamo/lib/dynamo.ex:179: syntax error before: '>'
/usr/local/Cellar/erlang/R15B03-1/lib/erlang/lib/parsetools-2.0.7/include/yeccpre.hrl:128: :elixir_parser.yecctoken_end_location/1
/usr/local/Cellar/erlang/R15B03-1/lib/erlang/lib/parsetools-2.0.7/include/yeccpre.hrl:113: :elixir_parser.yeccpars1/7
/usr/local/Cellar/erlang/R15B03-1/lib/erlang/lib/parsetools-2.0.7/include/yeccpre.hrl:56: :elixir_parser.yeccpars0/5
src/elixir_translator.erl:17: :elixir_translator.forms/4
src/elixir_interpolation.erl:171: :elixir_interpolation.forms/3
src/elixir_interpolation.erl:162: :elixir_interpolation.build_interpol/5
src/elixir_interpolation.erl:39: :elixir_interpolation.extract/8
src/elixir_tokenizer.erl:410: :elixir_tokenizer.handle_strings/5

Access config in routers?

What's the best way to access config variables in routers? Say I added an option in environment/prod.exs under config :server. How do I access this option later on? I tried :application.get_env(:server, :option) but it didn't work.

Thanks,
Derek

mix server isn't running

> mix server
** (Mix) The task  could not be found

I'm using master for both Dynamo and Elixir.

Wrong layout path in guide "How to render templates with layouts"

The guide "How to render templates with layouts" says that the layout file should be placed at the file path "web/templates/main.html.eex". This is not working for me.
Instead, than placing the file at "web/templates/layouts/main.html.eex" it works as expected.

Following getting started directions - ApplicationRouter not loaded - Dev environment.

When following the getting started directions I am experiencing:

$ mix server
** (RuntimeError) could not find endpoint ApplicationRouter, please ensure it is available
    /home/alan/AgilionApps/Projects/Chatter/deps/dynamo/lib/mix/tasks/server.ex:16: Mix.Tasks.Server."-run/1-fun-0-"/1
...

However, running in production gets me a functional application:

$ MIX_ENV=prod mix do clean, compile, server

I can also compile the file manually and run in dev, but I get template errors:

$ elixirc -o tmp/dev/chatter/ebin/ -pa deps/dynamo/ebin web/routers/application_router.ex
$ mix server

I have been digging around in the Dynamo.Loader module with no success.

If it helps I am running Ubuntu, not OSX.

Failure when deploy to another machine

I've been able to run my Dynamo site pretty well locally (thank you guys so much by the way :D), but today when I tried to deploy my site to another machine, I got this error:

root@ubuntu-13:~/Try-Elixir# mix server
Compiled lib/try_elixir.ex
Compiled lib/try_elixir/dynamo.ex
Generated try_elixir.app
** (RuntimeError) could not find endpoint ApplicationRouter, please ensure it is available
    /root/Try-Elixir/deps/dynamo/lib/mix/tasks/server.ex:33: Mix.Tasks.Server."-run/1-fun-0-"/2
    lists.erl:1313: :lists.foreach/2
    /root/elixir/lib/elixir/lib/enum.ex:284: Enum.each/2
    /root/Try-Elixir/deps/dynamo/lib/mix/tasks/server.ex:32: Mix.Tasks.Server.run/1
    /root/elixir/lib/mix/lib/mix/project.ex:172: Mix.Project.recur/2
    /root/elixir/lib/mix/lib/mix/cli.ex:38: Mix.CLI.run_task/2
    src/elixir_compiler.erl:65: :elixir_compiler."-eval_forms/4-fun-0-"/7

What could be wrong? I couldn't figure it out... I've made sure that I run mix deps.get before I run mix server.

compilation error

Just wanted to try dynamo, getting however compilation error when running MIX_ENV=test mix do deps.get, test

here details:
All dependencies up to date
Compiled lib/dynamo/app.ex
Compiled lib/dynamo/base.ex
Compiled lib/dynamo/connection/behaviour.ex
Compiled lib/dynamo/cowboy.ex
Compiled lib/dynamo/cowboy/handler.ex
Compiled lib/dynamo/connection/query_parser.ex
== Compilation error on file lib/dynamo/filters/exceptions.ex ==
** (CompileError) /Users/tarantoga/Projects/Elixir/dynamo/lib/dynamo/filters/exceptions.ex:6: cannot import Exception.format_stacktrace_entry/2 because it doesn't exist
/opt/local/lib/erlang/lib/parsetools-2.0.9/include/yeccpre.hrl:128: :elixir_parser.yecctoken_end_location/1
/opt/local/lib/erlang/lib/parsetools-2.0.9/include/yeccpre.hrl:113: :elixir_parser.yeccpars1/7
/opt/local/lib/erlang/lib/parsetools-2.0.9/include/yeccpre.hrl:56: :elixir_parser.yeccpars0/5
src/elixir_translator.erl:17: :elixir_translator.forms/4
src/elixir_translator.erl:27: :elixir_translator.forms!/4
src/elixir_compiler.erl:25: :elixir_compiler.string/2
src/elixir_compiler.erl:49: :elixir_compiler.file_to_path/2
/private/tmp/elixir-XQKM/elixir-0.8.2/lib/elixir/lib/kernel/parallel_compiler.ex:56: Kernel.ParallelCompiler."-spawn_compilers/7-fun-0-"/3

any idea what the problem is?

cheers

cannot fetch unknown :req_headers

The goal is to get the Host header

prepare do
  conn.fetch([:cookies, :params, :req_headers])
end

....

conn.req_headers['Host']

Updated: because were not using CGI. :)

When creating a new app, warn if name is dynamo

If you create a new application and call it "dynamo", the mix build breaks because you can't build dynamo (as your app already defines Dynamo.Mixfile)
This should be prevented by checking the name in the task.

How do you guys format Elixir code?

I was reading Dynamo's source code and I found the code impeccably formatted. I was just wondering if you guys are using some special tools to do the formatting, and if so could you tell us what they are?

How do you avoid evaluation of <%= %> in embed_template

In the mix task that generates yourapp/app/templates/index.html.eex, in lib/mix/tasks/dynamo.ex on line 168 you have:

<title><%= @title %></title>

The problem is that <%= @title %> is being evaluated at the time the dynamo app is being generated so that in yourapp/app/templates/index.html.eex you have:

<title></title>

since the title variable has no value at the time the file is generated.

I have only just started looking at Elixir and dynamo and the only thing I could figure out to avoid evaluating <%= @title %> was to do the following:

<title><%= "<" %>%= @title %></title>

This feels very wrong so I am sure there is a better way.

deps.compile fails with (ErlangError) erlang error: :eacces on windows

Hi all - not sure if this belongs here or on the elixir tracker so I apologize in advance if it's the wrong place.

Running into this while trying to compile dynamo deps on windows - this applies to dynamo projects generated via mix dynamo /path/ too.

The output is pretty abrupt:

* Compiling mimetypes
** (ErlangError) erlang error: :eacces

If I were to take a guess it'd be this line: https://github.com/elixir-lang/elixir/blob/master/lib/mix/lib/mix/tasks/deps.compile.ex#L62

I would love to jump in and fix it myself - but the current version of cygwin make has a bug that causes it to crash on elixir's makefile! I'll have to see about contributing from my mac.

Love the language!

Raise an error if a layout can't be found.

Right now, if a layout is not found (either because it's not available in the template format or because the file wasn't found) Dynamo acts as if there was no layout set (i.e. outputs just the rendered template). It'd be cool (at least IMHO) if it raised Dynamo.TemplateNotFound just as it does when the template wasn't found.

I can't figure how to send a chunked response to the browser.

I'm trying to send a chunked response to the browser following the example in the readme but I always get the same error:

ERROR REPORT==== 28-Apr-2013::19:48:19 ===
      Conn: GET /story1/play
    Reason: (UndefinedFunctionError) undefined function: :ok.state/1
Stacktrace:
  :ok.state({:ok,#Dynamo.Connection<GET /story1/play (cowboy)>})
  deps/dynamo/lib/dynamo/filters/exceptions.ex:17: Dynamo.Filters.Exceptions.service/3
  deps/dynamo/lib/dynamo/cowboy/handler.ex:15: Dynamo.Cowboy.Handler.init/3
  src/cowboy_protocol.erl:477: :cowboy_protocol.handler_init/4

The action Is very very simple:

  get "/story1/play" do
    conn = conn.send_chunked(200)
    conn.chunk("Animal")
    conn.chunk("Bellota")
    conn.chunk("Calasparra")
    conn.chunk("Dadivoso")
    conn.chunk("Emergencia")
    conn.chunk("Faisan")
    conn.chunk("Galicia")
    conn.chunk("Hipopotamo")
  end

I don't know if its important, but I tryed also to get the response with an XHR request

$(function(){
  $('.play').on('click', function(ev){
    ev.preventDefault();
    var xhr = new XMLHttpRequest()
    xhr.open("GET", $(this).href, true)
    xhr.onprogress = function () {
      $('#chat').append('<p class="story-line">' + xhr.responseText + '</p>');
    }
  });
});

If you want to see all the code, the repo is: https://github.com/cibernox/storyteller.

Anyone knows what is wrong with my code?

SSL examples doesn't work as expected

I followed the SSL example but whenever I connect to the SSL port I receive the error (in my browser) of "No data received".

Running mix run -r examples/ssl.exs --no-halt in the dynamo folder has the same result, HTTP works fine but HTTPS fails with "no data received" and nothing printed in the output console.

I'd be happy to try and fix this - if anyone had any pointers that'd be great. Dynamo.Cowboy.Handler.init and handle don't ever appear to be called when a HTTPS request in received.

undefined Enum.member

I checkedout the current master and created a new dynamo project from it but it now fails on an undefined function:

Compiled lib/dynamo_demo.ex
== Compilation error on file lib/dynamo_demo/dynamo.ex ==
** (UndefinedFunctionError) undefined function: Enum.member?/2
    Enum.member?([], Dynamo.Base)
    /home/tristan/Devel/dynamo_demo/deps/dynamo/lib/dynamo/utils/once.ex:23: Dynamo.Utils.Once."MACRO-use_once"/2
    /home/tristan/Devel/dynamo_demo/lib/dynamo_demo/dynamo.ex:2: Dynamo.Utils.Once.use_once/1

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.