Document skip_deleted

Document skip_deleted

defmodule Phoenix.HTML.Form do
  @moduledoc ~S"""
  Helpers related to producing HTML forms.

  The functions in this module can be used in three
  distinct scenario:

    * with model data - when information to populate
      the form comes from a model

    * with connection data - when a form is created based
      on the information in the connection (aka `Plug.Conn`)

    * without form data - when the functions are used directly,
      outside of a form

  We will explore all three scenarios below.

  ## With model data

  The entry point for defining forms in Phoenix is with
  the `form_for/4` function. For this example, we will
  use `Ecto.Changeset`, which integrate nicely with Phoenix
  forms via the `phoenix_ecto` package.

  Imagine you have the following action in your controller:

      def new(conn, _params) do
        changeset = User.changeset(%User{})
        render conn, "new.html", changeset: changeset
      end

  where `User.changeset/2` is defined as follows:

      def changeset(user, params \\ :empty) do
        cast(user, params)
      end

  Now a `@changeset` assign is available in views which we
  can pass to the form:

      <%= form_for @changeset, user_path(@conn, :create), fn f -> %>
        <label>
          Name: <%= text_input f, :name %>
        </label>

        <label>
          Age: <%= select f, :age, 18..100 %>
        </label>

        <%= submit "Submit" %>
      <% end %>

  `form_for/4` receives the `Ecto.Changeset` and converts it
  to a form, which is passed to the function as the argument
  `f`. All the remaining functions in this module receive
  the form and automatically generate the input fields, often
  by extracting information from the given changeset. For example,
  if the user had a default value for age set, it will
  automatically show up as selected in the form.

  ## With connection data

  `form_for/4` expects as first argument any data structure that
  implements the `Phoenix.HTML.FormData` protocol. By default,
  Phoenix implements this protocol for `Plug.Conn`, allowing us
  to create forms based only on connection information.

  This is useful when you are creating forms that are not backed
  by any kind of model data, like a search form.

      <%= form_for @conn, search_path(@conn, :new), [name: :search], fn f -> %>
        <%= text_input f, :for %>
        <%= submit "Search" %>
      <% end %>

  ## Without form data

  Sometimes we may want to generate a `text_input/3` or any other
  tag outside of a form. The functions in this module also support
  such usage by simply passing an atom as first argument instead
  of the form.

      <%= text_input :user, :name, value: "This is a prepopulated value" %>

  ## Nested inputs

  If your model layer supports embedding or nested associations,
  you can use `inputs_for` to attach nested data to the form.

  Imagine the models:

      defmodule User do
        use Ecto.Model

        schema "users" do
          field :name
          embeds_one :permalink, Permalink
        end
      end

      defmodule Permalink do
        use Ecto.Model

        embedded_schema do
          field :url
        end
      end

  In the form, you now can:

      <%= form_for @changeset, user_path(@conn, :create), fn f -> %>
        <%= text_input f, :name %>

        <%= inputs_for f, :permalink, fn fp -> %>
          <%= text_input fp, :url %>
        <% end %>
      <% end %>

  The default option can be given to populate the fields if none
  is given:

      <%= inputs_for f, :permalink, [default: %Permalink{title: "default"}], fn fp -> %>
        <%= text_input fp, :url %>
      <% end %>

  `inputs_for/4` can be used to work with single entities or
  collections. When working with collections, `:prepend` and
  `:append` can be used to add entries to the collection
  stored in the changeset.
  """

  alias Phoenix.HTML.Form
  import Phoenix.HTML
  import Phoenix.HTML.Tag

  @doc """
  Defines the Phoenix.HTML.Form struct.

  Its fields are:

    * `:source` - the data structure given to `form_for/4` that
      implements the form data protocol

    * `:impl` - the module with the form data protocol implementation.
      This is used to avoid multiple protocol dispatches.

    * `:id` - the id to be used when generating input fields

    * `:name` - the name to be used when generating input fields

    * `:model` - the model used to lookup field data

    * `:params` - the parameters associated to this form in case
      they were sent as part of a previous request

    * `:hidden` - a keyword list of fields that are required for
      submitting the form behind the scenes as hidden inputs

    * `:options` - a copy of the options given when creating the
      form via `form_for/4` without any form data specific key

    * `:errors` - a keyword list of errors that associated with
      the form
  """
  defstruct source: nil, impl: nil, id: nil, name: nil, model: %{},
            hidden: [], params: %{}, errors: [], options: [], index: nil

  @type t :: %Form{source: Phoenix.HTML.FormData.t, name: String.t, model: %{atom => term},
                   params: %{binary => term}, hidden: Keyword.t, options: Keyword.t,
                   errors: Keyword.t, impl: module, id: String.t, index: nil | non_neg_integer}

  @doc """
  Converts an attribute/form field into its humanize version.

      iex> humanize(:username)
      "Username"
      iex> humanize(:created_at)
      "Created at"
      iex> humanize("user_id")
      "User"

  """
  def humanize(atom) when is_atom(atom),
    do: humanize(Atom.to_string(atom))
  def humanize(bin) when is_binary(bin) do
    bin =
      if String.ends_with?(bin, "_id") do
        binary_part(bin, 0, byte_size(bin) - 3)
      else
        bin
      end

    bin |> String.replace("_", " ") |> String.capitalize
  end

  @doc """
  Generates a form tag with a form builder.

  See the module documentation for examples of using this function.

  ## Options

    * `:name` - the name to be used in the form. May be inflected
      if a model is available

    * `:method` - the HTTP method. If the method is not "get" nor "post",
      an input tag with name `_method` is generated along-side the form tag.
      Defaults to "post".

    * `:multipart` - when true, sets enctype to "multipart/form-data".
      Required when uploading files

    * `:csrf_token` - for "post" requests, the form tag will automatically
      include an input tag with name `_csrf_token`. When set to false, this
      is disabled

    * `:enforce_utf8` - when false, does not enforce utf8

  See `Phoenix.HTML.Tag.form_tag/2` for more information on the
  options above.
  """
  @spec form_for(Phoenix.HTML.FormData.t, String.t,
                 Keyword.t, (t -> Phoenix.HTML.unsafe)) :: Phoenix.HTML.safe
  def form_for(form_data, action, options \\ [], fun) when is_function(fun, 1) do
    form = Phoenix.HTML.FormData.to_form(form_data, options)
    html_escape [form_tag(action, form.options), fun.(form), raw("</form>")]
  end

  @doc """
  Generate a new form builder for the given parameter in form.

  See the module documentation for examples of using this function.

  ## Options

    * `:id` - the id to be used in the form, defaults to the
      concatenation of the given `field` to the parent form id

    * `:name` - the name to be used in the form, defaults to the
      concatenation of the given `field` to the parent form name

    * `:default` - the value to use if none is available

    * `:prepend` - the values to prepend when rendering. This only
      applies if the field value is a list and no parameters were
      sent through the form.

    * `:append` - the values to append when rendering. This only
      applies if the field value is a list and no parameters were
      sent through the form.

    * `:skip_deleted` - do now show inputs for changesets that
      have been deleted (supported only when working with changesets)

  """
  @spec inputs_for(t, atom, Keyword.t, (t -> Phoenix.HTML.unsafe)) :: Phoenix.HTML.safe
  def inputs_for(form, field, options \\ [], fun) do
    forms = form.impl.to_form(form.source, form, field, options)
    html_escape Enum.map(forms, fn form ->
      hidden = Enum.map(form.hidden, fn {k, v} -> hidden_input(form, k, value: v) end)
      [hidden, fun.(form)]
    end)
  end

  @doc """
  Returns the HTML5 validations that would apply to
  the given field.
  """
  @spec input_validations(t, atom) :: boolean
  def input_validations(form, field) do
    form.impl.input_validations(form.source, field)
  end

  @mapping %{
    "url"      => :url_input,
    "email"    => :email_input,
    "search"   => :search_input,
    "password" => :password_input
  }

  @doc """
  Gets the input type for a given field.

  If the underlying input type is a `:text_field`,
  a mapping could be given to further inflect
  the input type based solely on the field name.
  The default mapping is:

      %{"url"      => :url_input,
        "email"    => :email_input,
        "search"   => :search_input,
        "password" => :password_input}

  """
  @spec input_type(t, atom) :: atom
  def input_type(form, field, mapping \\ @mapping) do
    type = form.impl.input_type(form.source, field)

    if type == :text_input do
      field = Atom.to_string(field)
      Enum.find_value(mapping, type, fn {k, v} ->
        String.contains?(field, k) && v
      end)
    else
      type
    end
  end

  ## Form helpers

  @doc """
  Generates a text input.

  The form should either be a `Phoenix.HTML.Form` emitted
  by `form_for` or an atom.

  All given options are forwarded to the underlying input,
  default values are provided for id, name and value if
  possible.

  ## Examples

      # Assuming form contains a User model
      text_input(form, :name)
      #=> <input id="user_name" name="user[name]" type="text" value="">

      text_input(:user, :name)
      #=> <input id="user_name" name="user[name]" type="text" value="">

  """
  def text_input(form, field, opts \\ []) do
    generic_input(:text, form, field, opts)
  end

  @doc """
  Generates a hidden input.

  See `text_input/3` for example and docs.
  """
  def hidden_input(form, field, opts \\ []) do
    generic_input(:hidden, form, field, opts)
  end

  @doc """
  Generates an email input.

  See `text_input/3` for example and docs.
  """
  def email_input(form, field, opts \\ []) do
    generic_input(:email, form, field, opts)
  end

  @doc """
  Generates a number input.

  See `text_input/3` for example and docs.
  """
  def number_input(form, field, opts \\ []) do
    generic_input(:number, form, field, opts)
  end

  @doc """
  Generates a password input.

  See `text_input/3` for example and docs.
  """
  def password_input(form, field, opts \\ []) do
    generic_input(:password, form, field, opts)
  end

  @doc """
  Generates an url input.

  See `text_input/3` for example and docs.
  """
  def url_input(form, field, opts \\ []) do
    generic_input(:url, form, field, opts)
  end

  @doc """
  Generates a search input.

  See `text_input/3` for example and docs.
  """
  def search_input(form, field, opts \\ []) do
    generic_input(:search, form, field, opts)
  end

  @doc """
  Generates a telephone input.

  See `text_input/3` for example and docs.
  """
  def telephone_input(form, field, opts \\ []) do
    generic_input(:tel, form, field, opts)
  end

  @doc """
  Generates a range input.

  See `text_input/3` for example and docs.
  """
  def range_input(form, field, opts \\ []) do
    generic_input(:range, form, field, opts)
  end

  defp generic_input(type, form, field, opts) when is_atom(field) and is_list(opts) do
    opts =
      opts
      |> Keyword.put_new(:type, type)
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))
      |> Keyword.put_new(:value, value_from(form, field))
    tag(:input, opts)
  end

  @doc """
  Generates a textarea input.

  All given options are forwarded to the underlying input,
  default values are provided for id, name and textarea
  content if possible.

  ## Examples

      # Assuming form contains a User model
      textarea(form, :description)
      #=> <textarea id="user_description" name="user[description]"></textarea>

  ## New lines

  Notice the generated textarea includes a new line after
  the opening tag. This is because the HTML spec says new
  lines after tags must be ignored and all major browser
  implementations do that.

  So in order to avoid new lines provided by the user
  from being ignored when the form is resubmitted, we
  automatically add a new line before the text area
  value.
  """
  def textarea(form, field, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    {value, opts} = Keyword.pop(opts, :value, value_from(form, field) || "")
    content_tag(:textarea, html_escape(["\n", value]), opts)
  end

  @doc """
  Generates a file input.

  It requires the given form to be configured with `multipart: true`
  when invoking `form_for/4`, otherwise it fails with `ArgumentError`.

  See `text_input/3` for example and docs.
  """
  def file_input(form, field, opts \\ []) do
    if match?(%Form{}, form) and !form.options[:multipart] do
      raise ArgumentError, "file_input/3 requires the enclosing form_for/4 " <>
                           "to be configured with multipart: true"
    end

    opts =
      opts
      |> Keyword.put_new(:type, :file)
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    tag(:input, opts)
  end

  @doc """
  Generates a submit input to send the form.

  All options are forwarded to the underlying input tag.

  ## Examples

      submit "Submit"
      #=> <input type="submit" value="Submit">

  """
  def submit(value, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "submit")
      |> Keyword.put_new(:value, value)
    tag(:input, opts)
  end

  @doc """
  Generates a reset input to reset all the form fields to
  their original state.

  All options are forwarded to the underlying input tag.

  ## Examples

      reset "Reset"
      #=> <input type="reset" value="Reset">

      reset "Reset", class: "btn"
      #=> <input type="reset" value="Reset" class="btn">

  """
  def reset(value, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "reset")
      |> Keyword.put_new(:value, value)
    tag(:input, opts)
  end

  @doc """
  Generates a radio button.

  Invoke this function for each possible value you to be
  sent to the server.

  ## Examples

      # Assuming form contains a User model
      radio_button(form, :role, "admin")
      #=> <input id="user_role_admin" name="user[role]" type="radio" value="admin">

  ## Options

  All options are simply forwarded to the underlying HTML tag.
  """
  def radio_button(form, field, value, opts \\ []) do
    value = html_escape(value)

    opts =
      opts
      |> Keyword.put_new(:type, "radio")
      |> Keyword.put_new(:id, id_from(form, field) <> "_" <> elem(value, 1))
      |> Keyword.put_new(:name, name_from(form, field))

    if value == html_escape(value_from(form, field)) do
      opts = Keyword.put_new(opts, :checked, true)
    end

    tag(:input, [value: value] ++ opts)
  end

  @doc """
  Generates a checkbox.

  This function is useful for sending boolean values to the server.

  ## Examples

      # Assuming form contains a User model
      checkbox(form, :famous)
      #=> <input name="user[famous]" type="hidden" value="false">
          <input checked="checked" id="user_famous" name="user[famous]"> type="checkbox" value="true")

  ## Options

    * `:checked_value` - the value to be sent when the checkbox is checked.
      Defaults to "true"

    * `:unchecked_value` - the value to be sent then the checkbox is unchecked,
      Defaults to "false"

    * `:value` - the value used to check if a checkbox is checked or unchecked.
      The default value is extracted from the model if a model is available

  All other options are forwarded to the underlying HTML tag.

  ## Hidden fields

  Because an unchecked checkbox is not sent to the server, Phoenix
  automatically generates a hidden field with the unchecked_value
  *before* the checkbox field to ensure the `unchecked_value` is sent
  when the checkbox is not marked.
  """
  def checkbox(form, field, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "checkbox")
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    {value, opts}           = Keyword.pop(opts, :value, value_from(form, field))
    {checked_value, opts}   = Keyword.pop(opts, :checked_value, true)
    {unchecked_value, opts} = Keyword.pop(opts, :unchecked_value, false)

    # We html escape all values to be sure we are comparing
    # apples to apples. After all we may have true in the model
    # but "true" in the params and both need to match.
    value           = html_escape(value)
    checked_value   = html_escape(checked_value)
    unchecked_value = html_escape(unchecked_value)

    if value == checked_value do
      opts = Keyword.put_new(opts, :checked, true)
    end

    html_escape [tag(:input, name: Keyword.get(opts, :name), type: "hidden", value: unchecked_value),
                 tag(:input, [value: checked_value] ++ opts)]
  end

  @doc """
  Generates a select tag with the given `values`.

  Values are expected to be an Enumerable containing two-item tuples
  (like maps and keyword lists) or any Enumerable where the element
  will be used both as key and value for the generated select.

  ## Examples

      # Assuming form contains a User model
      select(form, :age, 0..120)
      #=> <select id="user_age" name="user[age]">
          <option value="0">0</option>
          ...
          <option value="120">120</option>
          </select>

      select(form, :role, ["Admin": "admin", "User": "user"])
      #=> <select id="user_role" name="user[role]">
          <option value="admin">Admin</option>
          <option value="user">User</option>
          </select>

      select(form, :role, ["Admin": "admin", "User": "user"], prompt: "Choose your role")
      #=> <select id="user_role" name="user[role]">
          <option value="">Choose your role</option>
          <option value="admin">Admin</option>
          <option value="user">User</option>
          </select>

  ## Options

    * `:prompt` - an option to include at the top of the options with
      the given prompt text

    * `:value` - the value used to select a given option.
      The default value is extracted from the model if a model is available

    * `:default` - the default value to use when none was given in
      `:value` and none was available in the model

  All other options are forwarded to the underlying HTML tag.
  """
  def select(form, field, values, opts \\ []) do
    {default, opts} = Keyword.pop(opts, :default)
    {value, opts}   = Keyword.pop(opts, :value, value_from(form, field) || default)

    {options, opts} = case Keyword.pop(opts, :prompt) do
      {nil, opts}    -> {raw(""), opts}
      {prompt, opts} -> {content_tag(:option, prompt, value: ""), opts}
    end

    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    options = options_for_select(values, options, html_escape(value))
    content_tag(:select, options, opts)
  end

  defp options_for_select(values, options, value) do
    Enum.reduce values, options, fn
      {option_key, option_value}, acc ->
        option_key   = html_escape(option_key)
        option_value = html_escape(option_value)
        option(option_key, option_value, value, acc)
      option, acc ->
        option = html_escape(option)
        option(option, option, value, acc)
    end
  end

  defp option(option_key, option_value, value_list, acc) when is_list(value_list) do
    opts = [value: option_value, selected: option_value in value_list]
    html_escape [acc|content_tag(:option, option_key, opts)]
  end

  defp option(option_key, option_value, value, acc) do
    opts = [value: option_value, selected: value == option_value]
    html_escape [acc|content_tag(:option, option_key, opts)]
  end

  @doc """
  Generates a select tag with the given `values`.

  Values are expected to be an Enumerable containing two-item tuples
  (like maps and keyword lists) or any Enumerable where the element
  will be used both as key and value for the generated select.

  ## Examples

      # Assuming form contains a User model
      multiple_select(form, :roles, ["Admin": 1, "Power User": 2])
      #=> <select id="user_roles" name="user[roles][]">
          <option value="1">Admin</option>
          <option value="2">Power User</option>
          </select>

      multiple_select(form, :role, ["Admin": 1, "Power User": 2], values: [1])
      #=> <select id="user_role" name="user[role]">
          <option value="1" selected="selected" >Admin</option>
          <option value="2">Power User</option>
          </select>

  ## Options

    * `:value` - an Enum of values used to select given options.

    * `:default` - the default value to use when none was given in
      `:values` and none was available in the model

  All other options are forwarded to the underlying HTML tag.
  """
  def multiple_select(form, field, values, opts \\ []) do
    {default, opts} = Keyword.pop(opts, :default)
    {value_list, opts}  = case Keyword.pop(opts, :value, default) do
      {nil, opts} -> {[], opts}
      {value_list, opts} -> {Enum.map(value_list, &html_escape(&1)), opts}
    end

    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field) <> "[]")
      |> Keyword.put_new(:multiple, "")

    options = options_for_select(values, "", value_list)
    content_tag(:select, options, opts)
  end

  ## Datetime

  @doc ~S"""
  Generates select tags for datetime.

  ## Examples

      # Assuming form contains a User model
      datetime_select form, :born_at
      #=> <select id="user_born_at_year" name="user[born_at][year]">...</select> /
          <select id="user_born_at_month" name="user[born_at][month]">...</select> /
          <select id="user_born_at_day" name="user[born_at][day]">...</select> —
          <select id="user_born_at_hour" name="user[born_at][hour]">...</select> :
          <select id="user_born_at_min" name="user[born_at][min]">...</select>

  If you want to include the seconds field (hidden by default), pass `sec: []`:

      # Assuming form contains a User model
      datetime_select form, :born_at, sec: []

  If you want to configure the years range:

      # Assuming form contains a User model
      datetime_select form, :born_at, year: [options: 1900..2100]

  You are also able to configure `:month`, `:day`, `:hour`, `:min` and
  `:sec`. All options given to those keys will be forwarded to the
  underlying select. See `select/4` for more information.

  ## Options

    * `:value` - the value used to select a given option.
      The default value is extracted from the model if a model is available

    * `:default` - the default value to use when none was given in
      `:value` and none was available in the model

    * `:year`, `:month`, `:day`, `:hour`, `:min`, `:sec` - options passed
      to the underlying select. See `select/4` for more information.
      The available values can be given in `:options`.

    * `:builder` - specify how the select can be build. It must be a function
      that receives a builder that should be invoked with the select name
      and a set of options. See builder below for more information.

  ## Builder

  The generated datetime_select can be customized at will by providing a
  builder option. Here is an example from EEx:

      <%= datetime_select form, :born_at, builder: fn b -> %>
        Date: <%= b.(:day, []) %> / <%= b.(:month, []) %> / <%= b.(:hour, []) %>
        Time: <%= b.(:hour, []) %> : <%= b.(:min, []) %>
      <% end %>

  Although we have passed empty lists as options (they are required), you
  could pass any option there and it would be given to the underlying select
  input.

  In practice, we recommend you to create your own helper with your default
  builder:

      def my_datetime_select(form, field, opts \\ []) do
        builder = fn b ->
          ~e"\""
          Date: <%= b.(:day, []) %> / <%= b.(:month, []) %> / <%= b.(:hour, []) %>
          Time: <%= b.(:hour, []) %> : <%= b.(:min, []) %>
          "\""
        end

        datetime_select(form, field, [builder: builder] ++ opts)
      end

  Then you are able to use your own datetime_select throughout your whole
  application.

  ## Supported date values

  The following values are supported as date:

    * a map containing the `year`, `month` and `day` keys (either as strings or atoms)
    * a tuple with three elements: `{year, month, day}`
    * `nil`

  ## Supported time values

  The following values are supported as time:

    * a map containing the `hour` and `min` keys and an optional `sec` key (either as strings or atoms)
    * a tuple with three elements: `{hour, min, sec}`
    * a tuple with four elements: `{hour, min, sec, usec}`
    * `nil`

  """
  def datetime_select(form, field, opts \\ []) do
    value = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))

    builder =
      Keyword.get(opts, :builder) || fn b ->
        date = date_builder(b, opts)
        time = time_builder(b, opts)
        html_escape [date, raw(" — "), time]
      end

    builder.(datetime_builder(form, field, date_value(value), time_value(value), opts))
  end

  @doc """
  Generates select tags for date.

  Check `datetime_select/3` for more information on options and supported values.
  """
  def date_select(form, field, opts \\ []) do
    value   = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))
    builder = Keyword.get(opts, :builder) || &date_builder(&1, opts)
    builder.(datetime_builder(form, field, date_value(value), nil, opts))
  end

  defp date_builder(b, _opts) do
    html_escape [b.(:year, []), raw(" / "), b.(:month, []), raw(" / "), b.(:day, [])]
  end

  defp date_value(%{"year" => year, "month" => month, "day" => day}),
    do: %{year: year, month: month, day: day}
  defp date_value(%{year: year, month: month, day: day}),
    do: %{year: year, month: month, day: day}

  defp date_value({{year, month, day}, _}),
    do: %{year: year, month: month, day: day}
  defp date_value({year, month, day}),
    do: %{year: year, month: month, day: day}

  defp date_value(nil),
    do: %{year: nil, month: nil, day: nil}
  defp date_value(other),
    do: raise(ArgumentError, "unrecognized date #{inspect other}")

  @doc """
  Generates select tags for time.

  Check `datetime_select/3` for more information on options and supported values.
  """
  def time_select(form, field, opts \\ []) do
    value   = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))
    builder = Keyword.get(opts, :builder) || &time_builder(&1, opts)
    builder.(datetime_builder(form, field, nil, time_value(value), opts))
  end

  defp time_builder(b, opts) do
    time = html_escape [b.(:hour, []), raw(" : "), b.(:min, [])]

    if Keyword.get(opts, :sec) do
      html_escape [time, raw(" : "), b.(:sec, [])]
    else
      time
    end
  end

  defp time_value(%{"hour" => hour, "min" => min} = map),
    do: %{hour: hour, min: min, sec: Map.get(map, "sec", 0)}
  defp time_value(%{hour: hour, min: min} = map),
    do: %{hour: hour, min: min, sec: Map.get(map, :sec, 0)}

  defp time_value({_, {hour, min, sec, _msec}}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({hour, min, sec, _mseg}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({_, {hour, min, sec}}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({hour, min, sec}),
    do: %{hour: hour, min: min, sec: sec}

  defp time_value(nil),
    do: %{hour: nil, min: nil, sec: nil}
  defp time_value(other),
    do: raise(ArgumentError, "unrecognized time #{inspect other}")

  @months [
    {"January", "1"},
    {"February", "2"},
    {"March", "3"},
    {"April", "4"},
    {"May", "5"},
    {"June", "6"},
    {"July", "7"},
    {"August", "8"},
    {"September", "9"},
    {"October", "10"},
    {"November", "11"},
    {"December", "12"},
  ]

  map = &Enum.map(&1, fn i ->
    i = Integer.to_string(i)
    {String.rjust(i, 2, ?0), i}
  end)

  @days   map.(1..31)
  @hours  map.(0..23)
  @minsec map.(0..59)

  defp datetime_builder(form, field, date, time, parent) do
    id   = Keyword.get(parent, :id, id_from(form, field))
    name = Keyword.get(parent, :name, name_from(form, field))

    fn
      :year, opts when date != nil ->
        {year, _, _}  = :erlang.date()
        {value, opts} = datetime_options(:year, year-5..year+5, id, name, parent, date, opts)
        select(:datetime, :year, value, opts)
      :month, opts when date != nil ->
        {value, opts} = datetime_options(:month, @months, id, name, parent, date, opts)
        select(:datetime, :month, value, opts)
      :day, opts when date != nil ->
        {value, opts} = datetime_options(:day, @days, id, name, parent, date, opts)
        select(:datetime, :day, value, opts)
      :hour, opts when time != nil ->
        {value, opts} = datetime_options(:hour, @hours, id, name, parent, time, opts)
        select(:datetime, :hour, value, opts)
      :min, opts when time != nil ->
        {value, opts} = datetime_options(:min, @minsec, id, name, parent, time, opts)
        select(:datetime, :min, value, opts)
      :sec, opts when time != nil ->
        {value, opts} = datetime_options(:sec, @minsec, id, name, parent, time, opts)
        select(:datetime, :sec, value, opts)
    end
  end

  defp datetime_options(type, values, id, name, parent, datetime, opts) do
    opts = Keyword.merge Keyword.get(parent, type, []), opts
    suff = Atom.to_string(type)

    {value, opts} = Keyword.pop(opts, :options, values)

    {value,
      opts
      |> Keyword.put_new(:id, id <> "_" <> suff)
      |> Keyword.put_new(:name, name <> "[" <> suff <> "]")
      |> Keyword.put_new(:value, Map.get(datetime, type))}
  end

  @doc """
  Generates a label tag.

  The form should either be a `Phoenix.HTML.Form` emitted
  by `form_for` or an atom.

  All given options are forwarded to the underlying tag.
  A default value is provided for `for` attribute but can
  be overriden if you pass a value to the `for` option.
  Text content would be inferred from `field` if not specified.

  ## Examples

      # Assuming form contains a User model
      label(form, :name, "Name")
      #=> <label for="user_name">Name</label>

      label(:user, :email, "Email")
      #=> <label for="user_email">Email</label>

      label(:user, :email)
      #=> <label for="user_email">Email</label>

      label(:user, :email, class: "control-label")
      #=> <label for="user_email" class="control-label">Email</label>

      label :user, :email do
        "E-mail Address"
      end
      #=> <label for="user_email">E-mail Address</label>

      label :user, :email, class: "control-label" do
        "E-mail Address"
      end
      #=> <label class="control-label" for="user_email">E-mail Address</label>
  """
  def label(form, field) do
    label(form, field, humanize(field), [])
  end

  @doc """
  See `label/2`.
  """
  def label(form, field, text) when is_binary(text) do
    label(form, field, text, [])
  end
  def label(form, field, [do: block]) do
    label(form, field, [], do: block)
  end
  def label(form, field, opts) when is_list(opts) do
    label(form, field, humanize(field), opts)
  end

  @doc """
  See `label/2`.
  """
  def label(form, field, text, opts) when is_binary(text) and is_list(opts) do
    opts = Keyword.put_new(opts, :for, id_from(form, field))
    content_tag(:label, text, opts)
  end
  def label(form, field, opts, [do: block]) do
    opts = Keyword.put_new(opts, :for, id_from(form, field))
    content_tag(:label, opts, do: block)
  end

  ## Helpers

  defp value_from(%{model: model, params: params}, field) do
    case Map.fetch(params, Atom.to_string(field)) do
      {:ok, value} -> value
      :error -> Map.get(model, field)
    end
  end

  defp value_from(name, _field) when is_atom(name),
    do: nil

  defp id_from(%{id: id}, field),
    do: "#{id}_#{field}"
  defp id_from(name, field) when is_atom(name),
    do: "#{name}_#{field}"

  defp name_from(%{name: name}, field),
    do: "#{name}[#{field}]"
  defp name_from(name, field) when is_atom(name),
    do: "#{name}[#{field}]"
end

defmodule Phoenix.HTML.Form do
  @moduledoc ~S"""
  Helpers related to producing HTML forms.

  The functions in this module can be used in three
  distinct scenario:

    * with model data - when information to populate
      the form comes from a model

    * with connection data - when a form is created based
      on the information in the connection (aka `Plug.Conn`)

    * without form data - when the functions are used directly,
      outside of a form

  We will explore all three scenarios below.

  ## With model data

  The entry point for defining forms in Phoenix is with
  the `form_for/4` function. For this example, we will
  use `Ecto.Changeset`, which integrate nicely with Phoenix
  forms via the `phoenix_ecto` package.

  Imagine you have the following action in your controller:

      def new(conn, _params) do
        changeset = User.changeset(%User{})
        render conn, "new.html", changeset: changeset
      end

  where `User.changeset/2` is defined as follows:

      def changeset(user, params \\ :empty) do
        cast(user, params)
      end

  Now a `@changeset` assign is available in views which we
  can pass to the form:

      <%= form_for @changeset, user_path(@conn, :create), fn f -> %>
        <label>
          Name: <%= text_input f, :name %>
        </label>

        <label>
          Age: <%= select f, :age, 18..100 %>
        </label>

        <%= submit "Submit" %>
      <% end %>

  `form_for/4` receives the `Ecto.Changeset` and converts it
  to a form, which is passed to the function as the argument
  `f`. All the remaining functions in this module receive
  the form and automatically generate the input fields, often
  by extracting information from the given changeset. For example,
  if the user had a default value for age set, it will
  automatically show up as selected in the form.

  ## With connection data

  `form_for/4` expects as first argument any data structure that
  implements the `Phoenix.HTML.FormData` protocol. By default,
  Phoenix implements this protocol for `Plug.Conn`, allowing us
  to create forms based only on connection information.

  This is useful when you are creating forms that are not backed
  by any kind of model data, like a search form.

      <%= form_for @conn, search_path(@conn, :new), [name: :search], fn f -> %>
        <%= text_input f, :for %>
        <%= submit "Search" %>
      <% end %>

  ## Without form data

  Sometimes we may want to generate a `text_input/3` or any other
  tag outside of a form. The functions in this module also support
  such usage by simply passing an atom as first argument instead
  of the form.

      <%= text_input :user, :name, value: "This is a prepopulated value" %>

  ## Nested inputs

  If your model layer supports embedding or nested associations,
  you can use `inputs_for` to attach nested data to the form.

  Imagine the models:

      defmodule User do
        use Ecto.Model

        schema "users" do
          field :name
          embeds_one :permalink, Permalink
        end
      end

      defmodule Permalink do
        use Ecto.Model

        embedded_schema do
          field :url
        end
      end

  In the form, you now can:

      <%= form_for @changeset, user_path(@conn, :create), fn f -> %>
        <%= text_input f, :name %>

        <%= inputs_for f, :permalink, fn fp -> %>
          <%= text_input fp, :url %>
        <% end %>
      <% end %>

  The default option can be given to populate the fields if none
  is given:

      <%= inputs_for f, :permalink, [default: %Permalink{title: "default"}], fn fp -> %>
        <%= text_input fp, :url %>
      <% end %>

  `inputs_for/4` can be used to work with single entities or
  collections. When working with collections, `:prepend` and
  `:append` can be used to add entries to the collection
  stored in the changeset.
  """

  alias Phoenix.HTML.Form
  import Phoenix.HTML
  import Phoenix.HTML.Tag

  @doc """
  Defines the Phoenix.HTML.Form struct.

  Its fields are:

    * `:source` - the data structure given to `form_for/4` that
      implements the form data protocol

    * `:impl` - the module with the form data protocol implementation.
      This is used to avoid multiple protocol dispatches.

    * `:id` - the id to be used when generating input fields

    * `:name` - the name to be used when generating input fields

    * `:model` - the model used to lookup field data

    * `:params` - the parameters associated to this form in case
      they were sent as part of a previous request

    * `:hidden` - a keyword list of fields that are required for
      submitting the form behind the scenes as hidden inputs

    * `:options` - a copy of the options given when creating the
      form via `form_for/4` without any form data specific key

    * `:errors` - a keyword list of errors that associated with
      the form
  """
  defstruct source: nil, impl: nil, id: nil, name: nil, model: %{},
            hidden: [], params: %{}, errors: [], options: [], index: nil

  @type t :: %Form{source: Phoenix.HTML.FormData.t, name: String.t, model: %{atom => term},
                   params: %{binary => term}, hidden: Keyword.t, options: Keyword.t,
                   errors: Keyword.t, impl: module, id: String.t, index: nil | non_neg_integer}

  @doc """
  Converts an attribute/form field into its humanize version.

      iex> humanize(:username)
      "Username"
      iex> humanize(:created_at)
      "Created at"
      iex> humanize("user_id")
      "User"

  """
  def humanize(atom) when is_atom(atom),
    do: humanize(Atom.to_string(atom))
  def humanize(bin) when is_binary(bin) do
    bin =
      if String.ends_with?(bin, "_id") do
        binary_part(bin, 0, byte_size(bin) - 3)
      else
        bin
      end

    bin |> String.replace("_", " ") |> String.capitalize
  end

  @doc """
  Generates a form tag with a form builder.

  See the module documentation for examples of using this function.

  ## Options

    * `:name` - the name to be used in the form. May be inflected
      if a model is available

    * `:method` - the HTTP method. If the method is not "get" nor "post",
      an input tag with name `_method` is generated along-side the form tag.
      Defaults to "post".

    * `:multipart` - when true, sets enctype to "multipart/form-data".
      Required when uploading files

    * `:csrf_token` - for "post" requests, the form tag will automatically
      include an input tag with name `_csrf_token`. When set to false, this
      is disabled

    * `:enforce_utf8` - when false, does not enforce utf8

  See `Phoenix.HTML.Tag.form_tag/2` for more information on the
  options above.
  """
  @spec form_for(Phoenix.HTML.FormData.t, String.t,
                 Keyword.t, (t -> Phoenix.HTML.unsafe)) :: Phoenix.HTML.safe
  def form_for(form_data, action, options \\ [], fun) when is_function(fun, 1) do
    form = Phoenix.HTML.FormData.to_form(form_data, options)
    html_escape [form_tag(action, form.options), fun.(form), raw("</form>")]
  end

  @doc """
  Generate a new form builder for the given parameter in form.

  See the module documentation for examples of using this function.

  ## Options

    * `:id` - the id to be used in the form, defaults to the
      concatenation of the given `field` to the parent form id

    * `:name` - the name to be used in the form, defaults to the
      concatenation of the given `field` to the parent form name

    * `:default` - the value to use if none is available

    * `:prepend` - the values to prepend when rendering. This only
      applies if the field value is a list and no parameters were
      sent through the form.

    * `:append` - the values to append when rendering. This only
      applies if the field value is a list and no parameters were
      sent through the form.
  """
  @spec inputs_for(t, atom, Keyword.t, (t -> Phoenix.HTML.unsafe)) :: Phoenix.HTML.safe
  def inputs_for(form, field, options \\ [], fun) do
    forms = form.impl.to_form(form.source, form, field, options)
    html_escape Enum.map(forms, fn form ->
      hidden = Enum.map(form.hidden, fn {k, v} -> hidden_input(form, k, value: v) end)
      [hidden, fun.(form)]
    end)
  end

  @doc """
  Returns the HTML5 validations that would apply to
  the given field.
  """
  @spec input_validations(t, atom) :: boolean
  def input_validations(form, field) do
    form.impl.input_validations(form.source, field)
  end

  @mapping %{
    "url"      => :url_input,
    "email"    => :email_input,
    "search"   => :search_input,
    "password" => :password_input
  }

  @doc """
  Gets the input type for a given field.

  If the underlying input type is a `:text_field`,
  a mapping could be given to further inflect
  the input type based solely on the field name.
  The default mapping is:

      %{"url"      => :url_input,
        "email"    => :email_input,
        "search"   => :search_input,
        "password" => :password_input}

  """
  @spec input_type(t, atom) :: atom
  def input_type(form, field, mapping \\ @mapping) do
    type = form.impl.input_type(form.source, field)

    if type == :text_input do
      field = Atom.to_string(field)
      Enum.find_value(mapping, type, fn {k, v} ->
        String.contains?(field, k) && v
      end)
    else
      type
    end
  end

  ## Form helpers

  @doc """
  Generates a text input.

  The form should either be a `Phoenix.HTML.Form` emitted
  by `form_for` or an atom.

  All given options are forwarded to the underlying input,
  default values are provided for id, name and value if
  possible.

  ## Examples

      # Assuming form contains a User model
      text_input(form, :name)
      #=> <input id="user_name" name="user[name]" type="text" value="">

      text_input(:user, :name)
      #=> <input id="user_name" name="user[name]" type="text" value="">

  """
  def text_input(form, field, opts \\ []) do
    generic_input(:text, form, field, opts)
  end

  @doc """
  Generates a hidden input.

  See `text_input/3` for example and docs.
  """
  def hidden_input(form, field, opts \\ []) do
    generic_input(:hidden, form, field, opts)
  end

  @doc """
  Generates an email input.

  See `text_input/3` for example and docs.
  """
  def email_input(form, field, opts \\ []) do
    generic_input(:email, form, field, opts)
  end

  @doc """
  Generates a number input.

  See `text_input/3` for example and docs.
  """
  def number_input(form, field, opts \\ []) do
    generic_input(:number, form, field, opts)
  end

  @doc """
  Generates a password input.

  See `text_input/3` for example and docs.
  """
  def password_input(form, field, opts \\ []) do
    generic_input(:password, form, field, opts)
  end

  @doc """
  Generates an url input.

  See `text_input/3` for example and docs.
  """
  def url_input(form, field, opts \\ []) do
    generic_input(:url, form, field, opts)
  end

  @doc """
  Generates a search input.

  See `text_input/3` for example and docs.
  """
  def search_input(form, field, opts \\ []) do
    generic_input(:search, form, field, opts)
  end

  @doc """
  Generates a telephone input.

  See `text_input/3` for example and docs.
  """
  def telephone_input(form, field, opts \\ []) do
    generic_input(:tel, form, field, opts)
  end

  @doc """
  Generates a range input.

  See `text_input/3` for example and docs.
  """
  def range_input(form, field, opts \\ []) do
    generic_input(:range, form, field, opts)
  end

  defp generic_input(type, form, field, opts) when is_atom(field) and is_list(opts) do
    opts =
      opts
      |> Keyword.put_new(:type, type)
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))
      |> Keyword.put_new(:value, value_from(form, field))
    tag(:input, opts)
  end

  @doc """
  Generates a textarea input.

  All given options are forwarded to the underlying input,
  default values are provided for id, name and textarea
  content if possible.

  ## Examples

      # Assuming form contains a User model
      textarea(form, :description)
      #=> <textarea id="user_description" name="user[description]"></textarea>

  ## New lines

  Notice the generated textarea includes a new line after
  the opening tag. This is because the HTML spec says new
  lines after tags must be ignored and all major browser
  implementations do that.

  So in order to avoid new lines provided by the user
  from being ignored when the form is resubmitted, we
  automatically add a new line before the text area
  value.
  """
  def textarea(form, field, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    {value, opts} = Keyword.pop(opts, :value, value_from(form, field) || "")
    content_tag(:textarea, html_escape(["\n", value]), opts)
  end

  @doc """
  Generates a file input.

  It requires the given form to be configured with `multipart: true`
  when invoking `form_for/4`, otherwise it fails with `ArgumentError`.

  See `text_input/3` for example and docs.
  """
  def file_input(form, field, opts \\ []) do
    if match?(%Form{}, form) and !form.options[:multipart] do
      raise ArgumentError, "file_input/3 requires the enclosing form_for/4 " <>
                           "to be configured with multipart: true"
    end

    opts =
      opts
      |> Keyword.put_new(:type, :file)
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    tag(:input, opts)
  end

  @doc """
  Generates a submit input to send the form.

  All options are forwarded to the underlying input tag.

  ## Examples

      submit "Submit"
      #=> <input type="submit" value="Submit">

  """
  def submit(value, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "submit")
      |> Keyword.put_new(:value, value)
    tag(:input, opts)
  end

  @doc """
  Generates a reset input to reset all the form fields to
  their original state.

  All options are forwarded to the underlying input tag.

  ## Examples

      reset "Reset"
      #=> <input type="reset" value="Reset">

      reset "Reset", class: "btn"
      #=> <input type="reset" value="Reset" class="btn">

  """
  def reset(value, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "reset")
      |> Keyword.put_new(:value, value)
    tag(:input, opts)
  end

  @doc """
  Generates a radio button.

  Invoke this function for each possible value you to be
  sent to the server.

  ## Examples

      # Assuming form contains a User model
      radio_button(form, :role, "admin")
      #=> <input id="user_role_admin" name="user[role]" type="radio" value="admin">

  ## Options

  All options are simply forwarded to the underlying HTML tag.
  """
  def radio_button(form, field, value, opts \\ []) do
    value = html_escape(value)

    opts =
      opts
      |> Keyword.put_new(:type, "radio")
      |> Keyword.put_new(:id, id_from(form, field) <> "_" <> elem(value, 1))
      |> Keyword.put_new(:name, name_from(form, field))

    if value == html_escape(value_from(form, field)) do
      opts = Keyword.put_new(opts, :checked, true)
    end

    tag(:input, [value: value] ++ opts)
  end

  @doc """
  Generates a checkbox.

  This function is useful for sending boolean values to the server.

  ## Examples

      # Assuming form contains a User model
      checkbox(form, :famous)
      #=> <input name="user[famous]" type="hidden" value="false">
          <input checked="checked" id="user_famous" name="user[famous]"> type="checkbox" value="true")

  ## Options

    * `:checked_value` - the value to be sent when the checkbox is checked.
      Defaults to "true"

    * `:unchecked_value` - the value to be sent then the checkbox is unchecked,
      Defaults to "false"

    * `:value` - the value used to check if a checkbox is checked or unchecked.
      The default value is extracted from the model if a model is available

  All other options are forwarded to the underlying HTML tag.

  ## Hidden fields

  Because an unchecked checkbox is not sent to the server, Phoenix
  automatically generates a hidden field with the unchecked_value
  *before* the checkbox field to ensure the `unchecked_value` is sent
  when the checkbox is not marked.
  """
  def checkbox(form, field, opts \\ []) do
    opts =
      opts
      |> Keyword.put_new(:type, "checkbox")
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    {value, opts}           = Keyword.pop(opts, :value, value_from(form, field))
    {checked_value, opts}   = Keyword.pop(opts, :checked_value, true)
    {unchecked_value, opts} = Keyword.pop(opts, :unchecked_value, false)

    # We html escape all values to be sure we are comparing
    # apples to apples. After all we may have true in the model
    # but "true" in the params and both need to match.
    value           = html_escape(value)
    checked_value   = html_escape(checked_value)
    unchecked_value = html_escape(unchecked_value)

    if value == checked_value do
      opts = Keyword.put_new(opts, :checked, true)
    end

    html_escape [tag(:input, name: Keyword.get(opts, :name), type: "hidden", value: unchecked_value),
                 tag(:input, [value: checked_value] ++ opts)]
  end

  @doc """
  Generates a select tag with the given `values`.

  Values are expected to be an Enumerable containing two-item tuples
  (like maps and keyword lists) or any Enumerable where the element
  will be used both as key and value for the generated select.

  ## Examples

      # Assuming form contains a User model
      select(form, :age, 0..120)
      #=> <select id="user_age" name="user[age]">
          <option value="0">0</option>
          ...
          <option value="120">120</option>
          </select>

      select(form, :role, ["Admin": "admin", "User": "user"])
      #=> <select id="user_role" name="user[role]">
          <option value="admin">Admin</option>
          <option value="user">User</option>
          </select>

      select(form, :role, ["Admin": "admin", "User": "user"], prompt: "Choose your role")
      #=> <select id="user_role" name="user[role]">
          <option value="">Choose your role</option>
          <option value="admin">Admin</option>
          <option value="user">User</option>
          </select>

  ## Options

    * `:prompt` - an option to include at the top of the options with
      the given prompt text

    * `:value` - the value used to select a given option.
      The default value is extracted from the model if a model is available

    * `:default` - the default value to use when none was given in
      `:value` and none was available in the model

  All other options are forwarded to the underlying HTML tag.
  """
  def select(form, field, values, opts \\ []) do
    {default, opts} = Keyword.pop(opts, :default)
    {value, opts}   = Keyword.pop(opts, :value, value_from(form, field) || default)

    {options, opts} = case Keyword.pop(opts, :prompt) do
      {nil, opts}    -> {raw(""), opts}
      {prompt, opts} -> {content_tag(:option, prompt, value: ""), opts}
    end

    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field))

    options = options_for_select(values, options, html_escape(value))
    content_tag(:select, options, opts)
  end

  defp options_for_select(values, options, value) do
    Enum.reduce values, options, fn
      {option_key, option_value}, acc ->
        option_key   = html_escape(option_key)
        option_value = html_escape(option_value)
        option(option_key, option_value, value, acc)
      option, acc ->
        option = html_escape(option)
        option(option, option, value, acc)
    end
  end

  defp option(option_key, option_value, value_list, acc) when is_list(value_list) do
    opts = [value: option_value, selected: option_value in value_list]
    html_escape [acc|content_tag(:option, option_key, opts)]
  end

  defp option(option_key, option_value, value, acc) do
    opts = [value: option_value, selected: value == option_value]
    html_escape [acc|content_tag(:option, option_key, opts)]
  end

  @doc """
  Generates a select tag with the given `values`.

  Values are expected to be an Enumerable containing two-item tuples
  (like maps and keyword lists) or any Enumerable where the element
  will be used both as key and value for the generated select.

  ## Examples

      # Assuming form contains a User model
      multiple_select(form, :roles, ["Admin": 1, "Power User": 2])
      #=> <select id="user_roles" name="user[roles][]">
          <option value="1">Admin</option>
          <option value="2">Power User</option>
          </select>

      multiple_select(form, :role, ["Admin": 1, "Power User": 2], values: [1])
      #=> <select id="user_role" name="user[role]">
          <option value="1" selected="selected" >Admin</option>
          <option value="2">Power User</option>
          </select>

  ## Options

    * `:value` - an Enum of values used to select given options.

    * `:default` - the default value to use when none was given in
      `:values` and none was available in the model

  All other options are forwarded to the underlying HTML tag.
  """
  def multiple_select(form, field, values, opts \\ []) do
    {default, opts} = Keyword.pop(opts, :default)
    {value_list, opts}  = case Keyword.pop(opts, :value, default) do
      {nil, opts} -> {[], opts}
      {value_list, opts} -> {Enum.map(value_list, &html_escape(&1)), opts}
    end

    opts =
      opts
      |> Keyword.put_new(:id, id_from(form, field))
      |> Keyword.put_new(:name, name_from(form, field) <> "[]")
      |> Keyword.put_new(:multiple, "")

    options = options_for_select(values, "", value_list)
    content_tag(:select, options, opts)
  end

  ## Datetime

  @doc ~S"""
  Generates select tags for datetime.

  ## Examples

      # Assuming form contains a User model
      datetime_select form, :born_at
      #=> <select id="user_born_at_year" name="user[born_at][year]">...</select> /
          <select id="user_born_at_month" name="user[born_at][month]">...</select> /
          <select id="user_born_at_day" name="user[born_at][day]">...</select> —
          <select id="user_born_at_hour" name="user[born_at][hour]">...</select> :
          <select id="user_born_at_min" name="user[born_at][min]">...</select>

  If you want to include the seconds field (hidden by default), pass `sec: []`:

      # Assuming form contains a User model
      datetime_select form, :born_at, sec: []

  If you want to configure the years range:

      # Assuming form contains a User model
      datetime_select form, :born_at, year: [options: 1900..2100]

  You are also able to configure `:month`, `:day`, `:hour`, `:min` and
  `:sec`. All options given to those keys will be forwarded to the
  underlying select. See `select/4` for more information.

  ## Options

    * `:value` - the value used to select a given option.
      The default value is extracted from the model if a model is available

    * `:default` - the default value to use when none was given in
      `:value` and none was available in the model

    * `:year`, `:month`, `:day`, `:hour`, `:min`, `:sec` - options passed
      to the underlying select. See `select/4` for more information.
      The available values can be given in `:options`.

    * `:builder` - specify how the select can be build. It must be a function
      that receives a builder that should be invoked with the select name
      and a set of options. See builder below for more information.

  ## Builder

  The generated datetime_select can be customized at will by providing a
  builder option. Here is an example from EEx:

      <%= datetime_select form, :born_at, builder: fn b -> %>
        Date: <%= b.(:day, []) %> / <%= b.(:month, []) %> / <%= b.(:hour, []) %>
        Time: <%= b.(:hour, []) %> : <%= b.(:min, []) %>
      <% end %>

  Although we have passed empty lists as options (they are required), you
  could pass any option there and it would be given to the underlying select
  input.

  In practice, we recommend you to create your own helper with your default
  builder:

      def my_datetime_select(form, field, opts \\ []) do
        builder = fn b ->
          ~e"\""
          Date: <%= b.(:day, []) %> / <%= b.(:month, []) %> / <%= b.(:hour, []) %>
          Time: <%= b.(:hour, []) %> : <%= b.(:min, []) %>
          "\""
        end

        datetime_select(form, field, [builder: builder] ++ opts)
      end

  Then you are able to use your own datetime_select throughout your whole
  application.

  ## Supported date values

  The following values are supported as date:

    * a map containing the `year`, `month` and `day` keys (either as strings or atoms)
    * a tuple with three elements: `{year, month, day}`
    * `nil`

  ## Supported time values

  The following values are supported as time:

    * a map containing the `hour` and `min` keys and an optional `sec` key (either as strings or atoms)
    * a tuple with three elements: `{hour, min, sec}`
    * a tuple with four elements: `{hour, min, sec, usec}`
    * `nil`

  """
  def datetime_select(form, field, opts \\ []) do
    value = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))

    builder =
      Keyword.get(opts, :builder) || fn b ->
        date = date_builder(b, opts)
        time = time_builder(b, opts)
        html_escape [date, raw(" — "), time]
      end

    builder.(datetime_builder(form, field, date_value(value), time_value(value), opts))
  end

  @doc """
  Generates select tags for date.

  Check `datetime_select/3` for more information on options and supported values.
  """
  def date_select(form, field, opts \\ []) do
    value   = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))
    builder = Keyword.get(opts, :builder) || &date_builder(&1, opts)
    builder.(datetime_builder(form, field, date_value(value), nil, opts))
  end

  defp date_builder(b, _opts) do
    html_escape [b.(:year, []), raw(" / "), b.(:month, []), raw(" / "), b.(:day, [])]
  end

  defp date_value(%{"year" => year, "month" => month, "day" => day}),
    do: %{year: year, month: month, day: day}
  defp date_value(%{year: year, month: month, day: day}),
    do: %{year: year, month: month, day: day}

  defp date_value({{year, month, day}, _}),
    do: %{year: year, month: month, day: day}
  defp date_value({year, month, day}),
    do: %{year: year, month: month, day: day}

  defp date_value(nil),
    do: %{year: nil, month: nil, day: nil}
  defp date_value(other),
    do: raise(ArgumentError, "unrecognized date #{inspect other}")

  @doc """
  Generates select tags for time.

  Check `datetime_select/3` for more information on options and supported values.
  """
  def time_select(form, field, opts \\ []) do
    value   = Keyword.get(opts, :value, value_from(form, field) || Keyword.get(opts, :default))
    builder = Keyword.get(opts, :builder) || &time_builder(&1, opts)
    builder.(datetime_builder(form, field, nil, time_value(value), opts))
  end

  defp time_builder(b, opts) do
    time = html_escape [b.(:hour, []), raw(" : "), b.(:min, [])]

    if Keyword.get(opts, :sec) do
      html_escape [time, raw(" : "), b.(:sec, [])]
    else
      time
    end
  end

  defp time_value(%{"hour" => hour, "min" => min} = map),
    do: %{hour: hour, min: min, sec: Map.get(map, "sec", 0)}
  defp time_value(%{hour: hour, min: min} = map),
    do: %{hour: hour, min: min, sec: Map.get(map, :sec, 0)}

  defp time_value({_, {hour, min, sec, _msec}}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({hour, min, sec, _mseg}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({_, {hour, min, sec}}),
    do: %{hour: hour, min: min, sec: sec}
  defp time_value({hour, min, sec}),
    do: %{hour: hour, min: min, sec: sec}

  defp time_value(nil),
    do: %{hour: nil, min: nil, sec: nil}
  defp time_value(other),
    do: raise(ArgumentError, "unrecognized time #{inspect other}")

  @months [
    {"January", "1"},
    {"February", "2"},
    {"March", "3"},
    {"April", "4"},
    {"May", "5"},
    {"June", "6"},
    {"July", "7"},
    {"August", "8"},
    {"September", "9"},
    {"October", "10"},
    {"November", "11"},
    {"December", "12"},
  ]

  map = &Enum.map(&1, fn i ->
    i = Integer.to_string(i)
    {String.rjust(i, 2, ?0), i}
  end)

  @days   map.(1..31)
  @hours  map.(0..23)
  @minsec map.(0..59)

  defp datetime_builder(form, field, date, time, parent) do
    id   = Keyword.get(parent, :id, id_from(form, field))
    name = Keyword.get(parent, :name, name_from(form, field))

    fn
      :year, opts when date != nil ->
        {year, _, _}  = :erlang.date()
        {value, opts} = datetime_options(:year, year-5..year+5, id, name, parent, date, opts)
        select(:datetime, :year, value, opts)
      :month, opts when date != nil ->
        {value, opts} = datetime_options(:month, @months, id, name, parent, date, opts)
        select(:datetime, :month, value, opts)
      :day, opts when date != nil ->
        {value, opts} = datetime_options(:day, @days, id, name, parent, date, opts)
        select(:datetime, :day, value, opts)
      :hour, opts when time != nil ->
        {value, opts} = datetime_options(:hour, @hours, id, name, parent, time, opts)
        select(:datetime, :hour, value, opts)
      :min, opts when time != nil ->
        {value, opts} = datetime_options(:min, @minsec, id, name, parent, time, opts)
        select(:datetime, :min, value, opts)
      :sec, opts when time != nil ->
        {value, opts} = datetime_options(:sec, @minsec, id, name, parent, time, opts)
        select(:datetime, :sec, value, opts)
    end
  end

  defp datetime_options(type, values, id, name, parent, datetime, opts) do
    opts = Keyword.merge Keyword.get(parent, type, []), opts
    suff = Atom.to_string(type)

    {value, opts} = Keyword.pop(opts, :options, values)

    {value,
      opts
      |> Keyword.put_new(:id, id <> "_" <> suff)
      |> Keyword.put_new(:name, name <> "[" <> suff <> "]")
      |> Keyword.put_new(:value, Map.get(datetime, type))}
  end

  @doc """
  Generates a label tag.

  The form should either be a `Phoenix.HTML.Form` emitted
  by `form_for` or an atom.

  All given options are forwarded to the underlying tag.
  A default value is provided for `for` attribute but can
  be overriden if you pass a value to the `for` option.
  Text content would be inferred from `field` if not specified.

  ## Examples

      # Assuming form contains a User model
      label(form, :name, "Name")
      #=> <label for="user_name">Name</label>

      label(:user, :email, "Email")
      #=> <label for="user_email">Email</label>

      label(:user, :email)
      #=> <label for="user_email">Email</label>

      label(:user, :email, class: "control-label")
      #=> <label for="user_email" class="control-label">Email</label>

      label :user, :email do
        "E-mail Address"
      end
      #=> <label for="user_email">E-mail Address</label>

      label :user, :email, class: "control-label" do
        "E-mail Address"
      end
      #=> <label class="control-label" for="user_email">E-mail Address</label>
  """
  def label(form, field) do
    label(form, field, humanize(field), [])
  end

  @doc """
  See `label/2`.
  """
  def label(form, field, text) when is_binary(text) do
    label(form, field, text, [])
  end
  def label(form, field, [do: block]) do
    label(form, field, [], do: block)
  end
  def label(form, field, opts) when is_list(opts) do
    label(form, field, humanize(field), opts)
  end

  @doc """
  See `label/2`.
  """
  def label(form, field, text, opts) when is_binary(text) and is_list(opts) do
    opts = Keyword.put_new(opts, :for, id_from(form, field))
    content_tag(:label, text, opts)
  end
  def label(form, field, opts, [do: block]) do
    opts = Keyword.put_new(opts, :for, id_from(form, field))
    content_tag(:label, opts, do: block)
  end

  ## Helpers

  defp value_from(%{model: model, params: params}, field) do
    case Map.fetch(params, Atom.to_string(field)) do
      {:ok, value} -> value
      :error -> Map.get(model, field)
    end
  end

  defp value_from(name, _field) when is_atom(name),
    do: nil

  defp id_from(%{id: id}, field),
    do: "#{id}_#{field}"
  defp id_from(name, field) when is_atom(name),
    do: "#{name}_#{field}"

  defp name_from(%{name: name}, field),
    do: "#{name}[#{field}]"
  defp name_from(name, field) when is_atom(name),
    do: "#{name}[#{field}]"
end


find two numbers in the sequence that yield maximum product

defmodule MaxProduct do

  def divide(1, [num]), do: [num]

  def divide(n, arr) do
    {l, r} = Enum.split(arr, div(n,2))
    l_res = divide(l |> length, l)
    r_res = divide(r |> length, r)
    Enum.sort(l_res ++ r_res, &(&1 > &2)) |> Enum.take(2)
  end

  def findmax(n, arr) do
    [max, second_max] = divide(n, arr)
    max * second_max
  end

end

IO.inspect MaxProduct.findmax(3, [1,2,3]) == 6

IO.inspect MaxProduct.findmax(3, [3,2,1]) == 6

IO.inspect MaxProduct.findmax(10, [7, 5, 14, 2, 8, 8, 10, 1, 2, 3]) == 140

IO.inspect MaxProduct.findmax(5, [4, 6, 2, 6, 1]) == 36

error: pathspec 'max_product.ex' did not match any file(s) known to git


add page controller test

add page controller test

defmodule PhxOembed.PageControllerTest do
  use PhxOembed.ConnCase

  setup do
    {:ok, conn: Phoenix.ConnTest.conn()}
  end

  test "renders the front end container" do
    assert html_response(conn, 200) =~ "PhxOembed"
  end
end

error: pathspec 'test/controllers/page_controller_test.ex' did not match any file(s) known to git


feat(refresh): feed entry refresh mix task

defmodule Mix.Tasks.WhistlerNewsReader.Refresh do
  use Mix.Task
  import Mix.Ecto

  alias WhistlerNewsReader.Repo
  alias WhistlerNewsReader.Feed
  alias WhistlerNewsReader.FeedServer

  def run(args) do
    repos = parse_repo(args)

    Enum.each repos, fn repo ->
      ensure_repo(repo, args)
      {:ok, pid, _} = ensure_started(repo, all: true)

      feeds = repo.all(Feed)
      FeedServer.refresh_all(feeds)
    end
  end
end

error: pathspec 'lib/mix/tasks/refresh.ex' did not match any file(s) known to git


Dev 3629 show division addresses on template 3 (#3638)

* Cast address struct to map with string keys

* Cast address struct to map with string keys

* match struct and convert it to map with strings

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/edenlabllc/ehealth.api.git/'


cleanup controller call to snapshot

cleanup controller call to snapshot

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-server.git/'


Do not store snapshots to s3 when retrived for live view

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-media.git/'


[Registers] More statuses to skip during termination (#2881)


Disable BroadcastHandler

Disable BroadcastHandler


add 'Receiver' service

add 'Receiver' service


Add an initial MergeRequest module with FSM

To keep things simple, I'm trying to just use the struct and pattern matching
in order to implement the state changes. This may have to be tweaked later on,
but seems like a straight-forward way to go without having to couple state
transitions to cross-process communication via `:gen_fsm` or `:gen_statem`.

References:
* https://github.com/sasa1977/fsm/issues/3
* [The missing finite state machine, Handling a finite state machine with isolated side effects and without erlangs gen_fsm](https://groups.google.com/d/topic/elixir-lang-talk/Ac3B1Dj8br0/discussion)

defmodule Gullintanni.MergeRequest do
  @moduledoc """
  """

  alias __MODULE__, as: MergeRequest

  @typedoc "A merge request identifier"
  @type id :: pos_integer

  @typedoc "A 40 character SHA-1 hash"
  @type sha :: String.t

  @typedoc "Supported merge request states"
  @type state :: :discussing | :approved | :pending | :passed | :failed | :error

  @type t ::
    %MergeRequest{
      id: id,
      title: String.t,
      clone_url: String.t,
      branch_name: String.t,
      target_branch: String.t,
      latest_commit: sha,
      state: state
    }

  @enforce_keys [:id]
  defstruct [
    :id,
    :title,
    :clone_url,
    :branch_name,
    :target_branch,
    :latest_commit,
    state: :discussing
  ]

  @doc """
  Creates a new merge request with the given `id`.

  ## Options

  The accepted options are:

    * `:title` - the title of the merge request
    * `:clone_url` - the URL for Git to use when cloning the merge request
    * `:branch_name` - the Git branch name of the merge request
    * `:target_branch` - the name of the Git branch that the merge request is targeting
    * `:latest_commit` - the SHA-1 hash of the latest commit on the merge request
    * `:state` - the state of the merge request; valid states are defined by `t:state/0`
  """
  @spec new(id, Keyword.t) :: t
  def new(id, opts \\ []) do
    Enum.reduce(opts, %MergeRequest{id: id}, fn({key, value}, req) ->
      %{req | key => value}
    end)
  end

  # A basic Finite State Machine

  def approve(%MergeRequest{state: :discussing} = req),
    do: %{req | state: :approved}

  def unapprove(%MergeRequest{} = req),
    do: %{req | state: :discussing}

  def merge_pass(%MergeRequest{state: :approved} = req),
    do: %{req | state: :pending}

  def merge_fail(%MergeRequest{state: :approved} = req),
    do: %{req | state: :error}

  def build_pass(%MergeRequest{state: :pending} = req),
    do: %{req | state: :passed}

  def build_fail(%MergeRequest{state: :pending} = req),
    do: %{req | state: :failed}

  def build_error(%MergeRequest{state: :pending} = req),
    do: %{req | state: :error}

  def ffwd_fail(%MergeRequest{state: :passed} = req),
    do: %{req | state: :error}
end

error: pathspec 'lib/gullintanni/merge_request.ex' did not match any file(s) known to git


added tests

added tests

defmodule FollowRedirectsTest do
  use ExUnit.Case

  use Tesla.Middleware.TestCase, middleware: Tesla.Middleware.FollowRedirects

  defmodule Client do
    use Tesla

    plug Tesla.Middleware.FollowRedirects

    adapter fn (env) ->
      {status, headers, body} = case env.url do
        "/0" ->
          {200, %{'Content-Type' => 'text/plain'}, "foo bar"}
        "/" <> n ->
          next = String.to_integer(n) - 1
          {301, %{'Location' => '/#{next}'}, ""}
      end

      %{env | status: status, headers: headers, body: body}
    end
  end

  test "redirects if default max redirects isn't exceeded" do
    assert Client.get("/5").status == 200
  end

  test "raise error when redirect default max redirects is exceeded" do
    assert_raise(Tesla.Error, "too many redirects", fn-> Client.get("/6") end)
  end

  defmodule CustomMaxRedirectsClient do
    use Tesla

    plug Tesla.Middleware.FollowRedirects, max_redirects: 1

    adapter fn (env) ->
      {status, headers, body} = case env.url do
        "/0" ->
          {200, %{'Content-Type' => 'text/plain'}, "foo bar"}
        "/" <> n ->
          next = String.to_integer(n) - 1
          {301, %{'Location' => '/#{next}'}, ""}
      end

      %{env | status: status, headers: headers, body: body}
    end
  end

  alias CustomMaxRedirectsClient, as: CMRClient

  test "redirects if custom max redirects isn't exceeded" do
    assert CMRClient.get("/1").status == 200
  end

  test "raise error when custom max redirects is exceeded" do
    assert_raise(Tesla.Error, "too many redirects", fn-> CMRClient.get("/2") end)
  end

end

error: pathspec 'test/tesla/middleware/follow_redirects_test.exs' did not match any file(s) known to git


Don't invalidate cache on each thumbnail update

This seemed like a good idea but made Redis instance go out of memory
and Sidekiq workers couldn't keep up with the growing queue.
The camera cache expires after 5 minutes so it's not a big deal.


Remove obsolete thumbnail function

Remove obsolete thumbnail function


Formatting

Formatting


Increment version [ci skip]

Increment version [ci skip]


[patch] Major version bump

[patch] Major version bump


Removed count function

Removed count function


web-interface for my OTP app

web-interface for my OTP app

defmodule Todo.Web do
  use Plug.Router

  plug :match
  plug :dispatch

  def child_spec(_arg) do
    Plug.Adapters.Cowboy.child_spec(
      scheme: :http,
      options: [port: 5454],
      plug: __MODULE__
    )
  end

  post "/add_entry" do
    conn = Plug.Conn.fetch_query_params(conn)
    list_name = Map.fetch!(conn.params, "list")
    title = Map.fetch!(conn.params, "title")
    date = Date.from_iso8601!((Map.fetch!(conn.params, "date")))

    list_name
    |> Todo.Cache.server_process()
    |> Todo.Server.add_entry(%{title: title, date: date})

    conn
    |> Plug.Conn.put_resp_content_type("text/plain")
    |> Plug.Conn.send_resp(200, "OK")
  end

  # The code above roughly unwraps to something like that:
  #
  # defp do_match("POST", ["add_entry"]) do
  #   fn(conn) ->
  #     ...
  #   end
  # end
  # defp do_match("POST", ["add_entry"]) do
  #   ...
  # end
  # defp do_match("POST", ["delete_entry"]) do
  #   ...
  # end

  get "/entries" do
    conn = Plug.Conn.fetch_query_params(conn)
    list_name = Map.fetch!(conn.params, "list")
    date = Date.from_iso8601!(Map.fetch!(conn.params, "date"))

    entries =
      list_name
      |> Todo.Cache.server_process()
      |> Todo.Server.entries(date)

    formatted_entries =
      entries
      |> Enum.map(&"#{&1.date} #{&1.title}")
      |> Enum.join("\n")

    conn
    |> Plug.Conn.put_resp_content_type("text/plain")
    |> Plug.Conn.send_resp(200, formatted_entries)
  end
end

error: pathspec 'elixir/otp_todo_app/lib/todo/web.ex' did not match any file(s) known to git


Update camera controller tests

Update camera controller tests


Add refs to the server state.

Add refs to the server state.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/julianespinel/tutorials.git/'


Changed request content-type in intercom sync tasks


Add role seeder

Add role seeder

This is a script that seeds the role table
with the values "ADMIN", "GO" and "REGULAR"

defmodule JwMinistryApi.RoleSeeder do
  alias JwMinistryApi.Repo
  alias JwMinistryApi.Role

  @roles_list ~w(ADMIN GO REGULAR)

  def insert_roles do
    Repo.delete_all Role
    @roles_list
    |> Enum.each(fn x ->
         Repo.insert! %Role{
           name: x
         }
       end)
  end
end

error: pathspec 'priv/repo/role_seeder.exs' did not match any file(s) known to git


Accept jpg format in the api pipeline

Accept jpg format in the api pipeline

fatal: unable to access 'https://github.com/evercam/evercam-media.git/': The requested URL returned error: 403


add missing resolvers to reimbursement contract request raw data fields


[patch] - try to fix version builds (#2081)


sorry about that

sorry about that

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/FarmBot-Labs/laughing-octo-telegram.git/'


Normalize test

Normalize test

defmodule Tesla.Middleware.NormalizeTest do
  use ExUnit.Case
  alias Tesla.Middleware.Normalize

  defp call(args) do
    Normalize.call(struct!(Tesla.Env, args), [], [])
  end

  describe "status" do
    test "when nil" do
      env = call(status: nil)
      assert env.status == nil
    end

    test "when num" do
      env = call(status: 200)
      assert env.status == 200
    end

    test "when string" do
      env = call(status: "200")
      assert env.status == 200
    end

    test "when charlist" do
      env = call(status: '200')
      assert env.status == 200
    end
  end

  describe "body" do
    test "when nil" do
      env = call(body: nil)
      assert env.body == nil
    end

    test "when string" do
      env = call(body: "some-body")
      assert env.body == "some-body"
    end

    test "when charlist" do
      env = call(body: 'some-body')
      assert env.body == "some-body"
    end
  end

  describe "headers" do
    test "when empty list" do
      env = call(headers: [])
      assert env.headers == %{}
    end

    test "when list" do
      env = call(headers: [a: "1", b: 2])
      assert env.headers == %{"a" => "1", "b" => "2"}
    end

    test "when map" do
      env = call(headers: %{"User-Agent" => "tesla"})
      assert env.headers == %{"user-agent" => "tesla"}
    end
  end
end

error: pathspec 'test/tesla/middleware/normalize_test.exs' did not match any file(s) known to git


Fix syntax: add missing space

Fix syntax: add missing space

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/preciz/insta_scrapex.git/'


Remove not used variable

Remove not used variable


Added an error handler module to better report errors and log them nicely. This change was introduced because I was often repeating myself in modules when it came to logging HTTP errors.

defmodule TougouBot.Util.Error_Handler do
  @moduledoc """
  A separate module to handle different types of errors.
  I found that I was often repeating myself (especially when it came to handling http errors),
  so this functionality has been modularised.
  """

  #Function to report an error into the console, in our live version we pipe
  # all output into a log txt file so we just output like normal here and it'll all be logged.
  def handle_http_error(status, headers, body) do
    IO.puts("-----HTTP Error Report Start-----")

    IO.puts("HTTP Status: "<>status)

    IO.puts("---HTTP Headers Start---")
    IO.inspect(headers)
    IO.puts("---HTTP Headers End---")

    IO.puts("---HTTP Body Start---")
    IO.inspect(body)
    IO.puts("---HTTP Body End---")

    IO.puts("-----HTTP Error Report End-----")
  end
end

error: pathspec 'lib/util/error_handler.ex' did not match any file(s) known to git


Be stricter about exposing public camera's attributes


Override token permissions

Override token permissions


Add .iex.exs config

Add .iex.exs config

fatal: unable to access 'https://github.com/sax/dotfiles.git/': The requested URL returned error: 403


Add TweetBroadcaster module

Add TweetBroadcaster module

defmodule EmojiMap.TweetBroadcaster do
  @moduledoc """
  When events arrive and there are no consumers, we store the event in
  the queue alongside the process information that broadcasted the event. When
  consumers send demand and there are not enough events, we increase the pending
  demand. Once we have both the data and the demand, we acknowledge the process
  that has sent the event to the broadcaster and finally broadcast the event
  downstream.

  This follows very closely the example in the Docs on Bufferin Demand:
  https://hexdocs.pm/gen_stage/GenStage.html
  """

  use GenStage

  @doc "Starts the broadcaster."
  def start_link() do
    GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  @doc "Sends an event and returns only after the event is dispatched."
  def sync_notify(event, timeout \\ 5000) do
    GenStage.call(__MODULE__, {:notify, event}, timeout)
  end

  ## Callbacks

  def init(:ok) do
    {:producer, {:queue.new, 0}, dispatcher: GenStage.BroadcastDispatcher}
  end

  def handle_call({:notify, event}, from, {queue, pending_demand}) do
    # If you get confused here, have a look at Erlangs queue module
    # http://erlang.org/doc/man/queue.html
    queue = :queue.in({from, event}, queue)
    dispatch_events(queue, pending_demand, [])
  end

  def handle_demand(incoming_demand, {queue, pending_demand}) do
    dispatch_events(queue, incoming_demand + pending_demand, [])
  end

  defp dispatch_events(queue, 0, events) do
    {:noreply, Enum.reverse(events), {queue, 0}}
  end
  defp dispatch_events(queue, demand, events) do
    case :queue.out(queue) do
      {{:value, {from, event}}, queue} ->
        GenStage.reply(from, :ok)
        dispatch_events(queue, demand - 1, [event | events])
      {:empty, queue} ->
        {:noreply, Enum.reverse(events), {queue, demand}}
    end
  end
end

error: pathspec 'backend/lib/emoji_map/tweet_broadcaster.ex' did not match any file(s) known to git


Add more logs and handle errors

Add more logs and handle errors


Changed poistion for broadcast & log status && Send updated camera fo… (#721)

* Changed poistion for broadcast & log status && Send updated camera for broadcast.

* Changed position for log camera status.


added solution reverese words elixir

added solution reverese words elixir

raw = File.read! "../tests/B-small-practice.in"

list = String.split(raw, "\n")
list_wthoNo = List.delete_at(list, 0)

list_wthoNo |> Enum.with_index |> Enum.each( fn {n, i} ->
  words = Enum.reverse(String.split(n, " "))
  IO.puts "Case #"<>Integer.to_string(i+1)<>": "<>Enum.join(words, " ")
end)

error: pathspec 'solutions/reverse-word/elixir/reverse.exs' did not match any file(s) known to git


Limit training logs index to last 5

Limit training logs index to last 5

This view is growing too big and is annoying on mobile.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/alex-handley/climbing_training_app.git/'


Added function to update company

Added function to update company


add golos channel

add golos channel

defmodule Glasnost.Web.GolosEventsChannel do
  use Phoenix.Channel

  def join("channel:golos_events", _message, socket) do
    {:ok, socket}
  end

  def join("channel:" <> _private_room_id, _params, _socket) do
    {:error, %{reason: "unauthorized"}}
  end

end

error: pathspec 'lib/glasnost/web/channels/golos_channel.ex' did not match any file(s) known to git


Fix permissions for creating camera shares


add monitoring to our backend processes

add monitoring to our backend processes

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jacklin10/rumbl.git/'


remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/sharkspeed/dororis.git/'


Correct the function call order

Correct the function call order

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-media.git/'


Increment version [ci skip]

Increment version [ci skip]

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/edenlabllc/ehealth.api.git/'


set version to 0.2.0

set version to 0.2.0


Increment version [ci skip]

Increment version [ci skip]


Initial checkin of dice parser/roller.

Initial checkin of dice parser/roller.

fatal: unable to access 'https://github.com/olhado/dicer.git/': The requested URL returned error: 403


minimises list of directly started applications

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/elixytics/monolith.git/'


Increment version [ci skip]

Increment version [ci skip]

fatal: unable to access 'https://github.com/edenlabllc/ehealth.api.git/': The requested URL returned error: 403


Refactored code.

Refactored code.

fatal: unable to access 'https://github.com/evercam/evercam-media.git/': The requested URL returned error: 403


Add exists?/1 function to Repo

Add exists?/1 function to Repo


Add Policy Tests

Add Policy Tests

defmodule CodeCorps.Helpers.PolicyTest do
  use CodeCorps.ModelCase
  alias Ecto.Changeset
  alias CodeCorps.{
    Organization, User, Helpers.Policy,
    ProjectUser
  }

  def create_project_user_with_role(role) do
    user = insert(:user)
    project = insert(:project)
    insert(:project_user, project: project, user: user, role: role)
    {project, user}
  end

  describe "owned_by/2" do
    test "returns false when organization is not owned by user" do
      refute Policy.owned_by?(%Organization{owner_id: 1}, %User{id: 2})
    end

    test "returns false when invalid arguments are passed" do
      refute Policy.owned_by?(nil, 2)
    end

    test "returns false if a project is not owned by the user" do
      project = insert(:project)
      some_other_user = %User{id: 1}
      refute Policy.owned_by?(project, some_other_user)
    end

    test "returns true if a project is owned by the user" do
      {project, user} = create_project_user_with_role("owner")
      assert Policy.owned_by?(project, user)
    end

    test "returns false if a project is admined by the user" do
      {project, user} = create_project_user_with_role("admin")
      refute Policy.owned_by?(project, user)
    end

    test "returns false if a project is contributed by the user" do
      {project, user} = create_project_user_with_role("contributor")
      refute Policy.owned_by?(project, user)
    end

    test "returns false if a project user role is pending" do
      {project, user} = create_project_user_with_role("pending")
      refute Policy.owned_by?(project, user)
    end

    test "returns true when organization is owned by user" do
      assert Policy.owned_by?(%Organization{owner_id: 1}, %User{id: 1})
    end
  end

  describe "administered_by?/2" do

    test "returns false if given invalid arguments" do
      refute Policy.administered_by?(nil, 2)
    end

    test "returns true if the user is an admin" do
      {project, user} = create_project_user_with_role("admin")
      assert Policy.administered_by?(project, user)
    end

    test "returns true if the user is an owner" do
      {project, user} = create_project_user_with_role("admin")
      assert Policy.administered_by?(project, user)
    end

    test "returns false if the user is a contributor" do
      {project, user} = create_project_user_with_role("contributor")
      refute Policy.administered_by?(project, user)
    end

    test "returns false if the user is pending" do
      {project, user} = create_project_user_with_role("pending")
      refute Policy.administered_by?(project, user)
    end
  end

  describe "contributed_by?/2" do

    test "returns false if given invalid arguments" do
      refute Policy.contributed_by?(nil, 2)
    end

    test "returns true if the user is an admin" do
      {project, user} = create_project_user_with_role("admin")
      assert Policy.contributed_by?(project, user)
    end

    test "returns true if the user is an owner" do
      {project, user} = create_project_user_with_role("admin")
      assert Policy.contributed_by?(project, user)
    end

    test "returns true if the user is a contributor" do
      {project, user} = create_project_user_with_role("contributor")
      assert Policy.contributed_by?(project, user)
    end

    test "returns false if the user is pending" do
      {project, user} = create_project_user_with_role("pending")
      refute Policy.contributed_by?(project, user)
    end
  end

  describe "get_organization/1" do
    test "return organization if the organization_id is defined on the struct" do
      organization = insert(:organization)
      project = insert(:project, organization: organization)
      result = Policy.get_organization(project)
      assert result.id == organization.id
      assert result.name == organization.name
    end

    test "return organization if the organization_id is defined on the changeset" do
      organization = insert(:organization)
      changeset = %Changeset{changes: %{organization_id: organization.id}}
      result = Policy.get_organization(changeset)
      assert result.id == organization.id
      assert result.name == organization.name
    end

    test "return nil for structs with no organization_id" do
      assert Policy.get_organization(%{foo: "bar"}) == nil
    end

    test "return nil for any" do
      assert Policy.get_organization("foo") == nil
    end
  end

  describe "get_project/1" do
    test "return project if the project_id is defined on the struct" do
      project = insert(:project)
      project_category = insert(:project_category, project: project)
      result = Policy.get_project(project_category)
      assert result.id == project.id
      assert result.title == project.title
    end

    test "return project if the project_id is defined on the changeset" do
      project = insert(:project)
      changeset = %Changeset{changes: %{project_id: project.id}}
      result = Policy.get_project(changeset)
      assert result.id == project.id
      assert result.title == project.title
    end

    test "return nil for structs with no project_id" do
      assert Policy.get_project(%{foo: "bar"}) == nil
    end

    test "return nil for any" do
      assert Policy.get_project("foo") == nil
    end
  end

  describe "get_role/1" do
    test "should return a project user's role if it's defined" do
      assert Policy.get_role(%ProjectUser{role: "admin"}) == "admin"
    end

    test "should return a changeset's role if it's defined" do
      assert Policy.get_role(%Changeset{data: %{role: "contributor"}, types: %{role: :string}}) == "contributor"
    end

    test "should return nil if no role is defined on a project user" do
      assert Policy.get_role(%ProjectUser{}) == nil
    end

    test "should return nil if no role is defined on a changeset" do
      assert Policy.get_role(%Changeset{data: %{role: nil}, types: %{role: :string}}) == nil
    end

    test "should return nil if nil is passed in" do
      assert Policy.get_role(nil) == nil
    end
  end

  describe "get_task/1" do
    test "should return task of a TaskSkill" do
      task = insert(:task)
      task_skill = insert(:task_skill, task: task)
      result = Policy.get_task(task_skill)
      assert result.id == task.id
    end

    test "should return task of a UserTask" do
      task = insert(:task)
      user_task = insert(:user_task, task: task)
      result = Policy.get_task(user_task)
      assert result.id == task.id
    end

    test "should return task of a Changeset" do
      task = insert(:task)
      changeset = %Changeset{changes: %{task_id: task.id}}
      result = Policy.get_task(changeset)
      assert result.id == task.id
    end
  end

  describe "task_authored_by?/1" do
    test "returns true if the user is the author of the task" do
      user = insert(:user)
      task = insert(:task, user: user)
      assert Policy.task_authored_by?(task, user)
    end

    test "returns false if the user is not the author of the task" do
      user = insert(:user)
      other_user = insert(:user)
      task = insert(:task, user: user)
      refute Policy.task_authored_by?(task, other_user)
    end
  end

end

error: pathspec 'test/lib/code_corps/helpers/policy_test.exs' did not match any file(s) known to git


medication_dispense process fixes (#4083)


Remove unused function

Remove unused function


Refactor active_token_for

Refactor active_token_for


add test for user controller

add test for user controller

fatal: unable to access 'https://github.com/udia-software/udia.git/': The requested URL returned error: 403


Formatting

Formatting


Revert "Disable snapshot retries temporarily"

This reverts commit c0da12869854d900bac5073cb7acb64a7b32c1f2.


Add basic test for tracing additions

Add basic test for tracing additions

fatal: unable to access 'https://github.com/appsignal/appsignal-elixir.git/': The requested URL returned error: 403


add blockchain field migration

add blockchain field migration

fatal: unable to access 'https://github.com/cyberpunk-ventures/glasnost.git/': The requested URL returned error: 403


Add iex helper

Add iex helper

fatal: unable to access 'https://github.com/FarmBot/farmbot_os.git/': The requested URL returned error: 403


Whitespace

Whitespace

fatal: unable to access 'https://github.com/shankardevy/evercam-media.git/': The requested URL returned error: 403


Add solution for standing ovation problem

defmodule Ovation do
  def standing() do
    {:ok, fin} = File.open("./A-large-practice.in", [:read])
    {:ok, fout} = File.open("./A-large-attempt1.out", [:write])

    t = IO.read(fin, :line) |> String.strip |> String.to_integer
    1..t
    |> Enum.each(fn(i)->
        str = IO.read(fin, :line) |> String.strip
        totalFriends = ovation(str)
        IO.puts(fout, "Case ##{i}: #{totalFriends}")
      end)

    File.close(fin)
    File.close(fout)
  end

  def ovation(str) do
    str = str |> String.split() |> Enum.at(1)
    len = str |> String.length
    {_, friends} = 0..(len-1)
    |> Enum.reduce({0, 0}, fn(i, {shynessLevel, standing})->
        curr = str |> String.at(i) |> String.to_integer
        if i > shynessLevel do
          standing = standing + (i-shynessLevel)
          shynessLevel = shynessLevel + (i-shynessLevel)
        end
        shynessLevel = shynessLevel + curr

        {shynessLevel, standing}
      end)
    friends
  end
end

error: pathspec 'solutions/standing-ovation/standing_ovation.ex' did not match any file(s) known to git


fixes application controller test storage setup

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/elixytics/report.git/'


Added options route.

Added options route.


Refactor camera share show action slightly


Parellel map

Parellel map

defmodule Parallel do
  def pmap(collection, fun) do
    me = self
    collection
    |> Enum.map(fn (elem) ->
      spawn_link fn -> (send me, { self, fun.(elem) }) end
    end)
    |> Enum.map(fn (pid) ->
      receive do { ^pid, result } -> result end
    end)
  end
end

error: pathspec 'programming_elixir/chapter14/pmap.exs' did not match any file(s) known to git


Used plural gif and mp4 urls

Used plural gif and mp4 urls


fix credo issues (#1155)

fix credo issues (#1155)


Added images into online and offline notifications (#441)


Lower snapshot cache duration

Lower snapshot cache duration


fix/add env variable for secret_key_base in prod

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bit-engine/dustbin.git/'


Reduce pool_size

Reduce pool_size

fatal: unable to access 'https://github.com/wwselleck/Rekrewtor.git/': The requested URL returned error: 403


Change storage directory

Change storage directory


Initial code from Chapter 14.

Initial code from Chapter 14.

fatal: unable to access 'https://github.com/hectoregm/elixir.git/': The requested URL returned error: 403


:memo: W.I.P. Implementing SubServer.

:memo: W.I.P. Implementing SubServer.

fatal: unable to access 'https://github.com/budougumi0617/ElixirTraining.git/': The requested URL returned error: 403


Uncommented player login plugs

Uncommented player login plugs

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/mononym/exmud.git/'


Update storage tests

Update storage tests


First pass on SKI combinators

First pass on SKI combinators

fatal: unable to access 'https://github.com/robot-overlord/quark.git/': The requested URL returned error: 403


remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/sharkspeed/dororis.git/'