Add ip info module

Add ip info module

defmodule MicrocrawlerWebapp.IpInfoLoader do
  use Bitwise

  def get_ip_infos(dir) do
    {:ok, files} = File.ls Path.join dir
    files
    |> Enum.map(&Path.join(dir ++ [&1]))
    |> Enum.reduce([], &process_file/2)
    |> Enum.sort
    |> List.to_tuple
  end

  def ip_to_int(ip) when is_tuple(ip) do
    ip
    |> Tuple.to_list
    |> ip_to_int
  end

  def ip_to_int(ip) when is_binary(ip) do
    ip
    |> String.split(".")
    |> Enum.map(&String.to_integer/1)
    |> ip_to_int
  end

  def ip_to_int(ip) when is_list(ip) do
    ip
    |> Enum.reduce(0, &((&2 <<< 8) + &1))
  end

  defp process_file(filename, lines) do
    File.stream!(filename, [:read])
    |> Enum.reduce(lines, &process_line/2)
  end

  defp process_line(line, lines) do
    case line |> String.trim_trailing |> String.split("|") |> parse_line do
      {:ok, parsed} -> [parsed | lines]
      :error        -> lines
    end
  end

  defp parse_line([reg, code, "ipv4", ip, count, _date, status | _])
  when byte_size(code) == 2 do
    start = ip_to_int(ip)
    {:ok, {
      start,
      start + String.to_integer(count) - 1,
      code,
      :ipv4,
      String.to_atom(status),
      String.to_atom(reg)
    }}
  end

  defp parse_line([_, _, "ipv6" | _]), do: :error

  defp parse_line([_, _, "asn" | _]), do: :error

  defp parse_line(line) do
    IO.puts ~s(UNKNOWN: #{Enum.join(line, "|")})
    :error
  end
end

defmodule MicrocrawlerWebapp.IpInfo do
  require MicrocrawlerWebapp.IpInfoLoader

  @dir ["data", "ip"]

  @external_resource Path.join(@dir ++ ["delegated-afrinic-latest"])
  @external_resource Path.join(@dir ++ ["delegated-apnic-latest"])
  @external_resource Path.join(@dir ++ ["delegated-arin-extended-latest"])
  @external_resource Path.join(@dir ++ ["delegated-iana-latest"])
  @external_resource Path.join(@dir ++ ["delegated-lacnic-latest"])
  @external_resource Path.join(@dir ++ ["delegated-ripencc-latest"])

  def for(ip) do
    search(MicrocrawlerWebapp.IpInfoLoader.ip_to_int(ip), all_ranges)
  end

  defp search(ip, ranges) do
    bsearch(ip, ranges, 0, tuple_size(ranges) - 1)
  end

  defp bsearch(_ip, _ranges, low, high) when low > high do
    :error
  end

  defp bsearch(ip, ranges, low, high) do
    mid = div(low + high, 2)
    {start, stop, code, type, status, reg} = elem(ranges, mid)
    case {ip >= start, ip <= stop} do
      {true, true}  -> {:ok, {code, type, status, reg}}
      {true, false} -> bsearch(ip, ranges, mid + 1, high)
      {false, true} -> bsearch(ip, ranges, low, mid - 1)
    end
  end

  infos = MicrocrawlerWebapp.IpInfoLoader.get_ip_infos(["data", "ip"])

  defp all_ranges do
    unquote(Macro.escape(infos))
  end
end

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


Add empty separator line

Add empty separator line

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/'


[System Simulation] cylinder liquid volume

error: pathspec 'system' did not match any file(s) known to git
error: pathspec 'simulation/2017/week5/main.exs' did not match any file(s) known to git


Added lead_source param on create zoho contact

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/'


Handled nill user object condition

Handled nill user object condition


Delete duplicated migration.

Delete duplicated migration.

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/'


Adding 'add comments table' migration.

Adding 'add comments table' migration.

defmodule Discuss.Repo.Migrations.AddCommentsTable do
  use Ecto.Migration

  def change do
    create table(:comments) do
      add :content, :string
      add :user_id, references(:users)
      add :topic_id, references(:topics)

      timestamps()
    end
  end
end

error: pathspec 'InProgress-The-Complete-Elixir-And-Phoenix-Bootcamp/05-Phoenix/discuss/priv/repo/migrations/20190127155326_add_comments_table.exs' did not match any file(s) known to git


improve code

improve code

defmodule Glasnost.Worker.AuthorSync do
  use GenServer
  alias Glasnost.Repo
  import Logger

  def start_link(args, opts \\ []) do
    GenServer.start_link(__MODULE__, args, opts)
  end

  def init(config) do
    %{account_name: account_name} = config
    Logger.info("AuthorSync GenServer process for #{account_name} is being initialized...")
    import Ecto.Query
    Repo.delete_all(from c in Glasnost.Post, where: c.author == ^account_name)

    config = %{current_cursor: "",
      account_name: config.account_name,
      tags: config.tags,
      client_mod: RuntimeConfig.blockchain_client_mod}

    Process.send_after(self(), :tick, 1_000)
    {:ok, config}
  end

  def handle_info(:tick, state) do
     utc_now_str = NaiveDateTime.utc_now |> NaiveDateTime.to_iso8601 |> trim_trailing_ms
     %{account_name: account_name, current_cursor: current_cursor, client_mod: client_mod} = state
     {:ok, posts} = client_mod.get_discussions_by_author_before_date(account_name, current_cursor, utc_now_str, 100)
     posts = posts
      |> Enum.map(&parse_json_metadata/1)
      |> Enum.map(&extract_put_tags/1)
      |> filter_whitelisted(state.tags.whitelist)
      |> filter_blacklisted(state.tags.blacklist)
     for post <- posts do
       save_to_db(post)
     end
     state = iterate(posts, state)
     {:noreply, state}
  end

  def trim_trailing_ms(date) when is_bitstring(date) do
     String.replace(date, ~r/\..+/, "")
  end

  def parse_json_metadata(post) do
     update_in(post["json_metadata"], &Poison.Parser.parse!/1)
  end

  def extract_put_tags(post) do
     put_in(post, ["tags"], post["json_metadata"]["tags"])
  end

  def iterate(posts, state) do
    if length(posts) == 100 do
       Process.send_after(self(), :tick, 100, [])
       next_permlink = posts |> List.last() |> Map.get("permlink")
       put_in(state.current_cursor, next_permlink)
     else
       Process.send_after(self(), :tick, :timer.minutes(60), [])
       put_in(state.current_cursor, "")
     end
  end

  def save_to_db(post) do
     result =
       case Repo.get(Glasnost.Post, post["id"]) do
         nil  -> %Glasnost.Post{}
         comment -> comment
       end
       |> Glasnost.Post.changeset(post)
       |> Repo.insert_or_update

     case result do
       {:ok, _}       -> :ok
       {:error, _} -> :error
     end
  end

  def filter_whitelisted(posts, []) do
    posts
  end

  def filter_whitelisted(posts, tags) do
    for post <- posts,
      !disjoint_tags?(post["tags"], tags),
      do: post
  end

  def filter_blacklisted(posts, tags) do
    for post <- posts,
      disjoint_tags?(post["tags"], tags),
      do: post
  end

  def disjoint_tags?(tags1, tags2) do
    tags1_set = Enum.into(tags1, MapSet.new)
    tags2_set = Enum.into(tags2, MapSet.new)
    MapSet.disjoint?(tags1_set, tags2_set)
  end

end

defmodule Glasnost.Worker.AuthorSync do
  use GenServer
  alias Glasnost.Repo
  import Logger

  def start_link(args, opts \\ []) do
    GenServer.start_link(__MODULE__, args, opts)
  end

  def init(config) do
    Logger.info("AuthorSync GenServer process for #{config.account_name} is being initialized...")
    import Ecto.Query
    Repo.delete_all(from c in Glasnost.Post, where: c.author == config.account_name)

    config = %{current_cursor: "",
      account_name: config.account_name,
      tags: config.tags,
      client_mod: RuntimeConfig.blockchain_client_mod}

    Process.send_after(self(), :tick, 1_000)
    {:ok, config}
  end

  def handle_info(:tick, state) do
     utc_now_str = NaiveDateTime.utc_now |> NaiveDateTime.to_iso8601 |> trim_trailing_ms
     %{account_name: account_name, current_cursor: current_cursor, client_mod: client_mod} = state
     {:ok, posts} = client_mod.get_discussions_by_author_before_date(account_name, current_cursor, utc_now_str, 100)
     posts = posts
      |> Enum.map(&parse_json_metadata/1)
      |> Enum.map(&extract_put_tags/1)
      |> filter_whitelisted(state.tags.whitelist)
      |> filter_blacklisted(state.tags.blacklist)
     for post <- posts do
       save_to_db(post)
     end
     state = iterate(posts, state)
     {:noreply, state}
  end

  def trim_trailing_ms(date) when is_bitstring(date) do
     String.replace(date, ~r/\..+/, "")
  end

  def parse_json_metadata(post) do
     update_in(post["json_metadata"], &Poison.Parser.parse!/1)
  end

  def extract_put_tags(post) do
     put_in(post, ["tags"], post["json_metadata"]["tags"])
  end

  def iterate(posts, state) do
    if length(posts) == 100 do
       Process.send_after(self(), :tick, 100, [])
       next_permlink = posts |> List.last() |> Map.get("permlink")
       put_in(state.current_cursor, next_permlink)
     else
       Process.send_after(self(), :tick, :timer.minutes(60), [])
       put_in(state.current_cursor, "")
     end
  end

  def save_to_db(post) do
     result =
       case Repo.get(Glasnost.Post, post["id"]) do
         nil  -> %Glasnost.Post{}
         comment -> comment
       end
       |> Glasnost.Post.changeset(post)
       |> Repo.insert_or_update

     case result do
       {:ok, _}       -> :ok
       {:error, _} -> :error
     end
  end

  def filter_whitelisted(posts, []) do
    posts
  end

  def filter_whitelisted(posts, tags) do
    for post <- posts,
      !disjoint_tags?(post["tags"], tags),
      do: post
  end

  def filter_blacklisted(posts, tags) do
    for post <- posts,
      disjoint_tags?(post["tags"], tags),
      do: post
  end

  def disjoint_tags?(tags1, tags2) do
    tags1_set = Enum.into(tags1, MapSet.new)
    tags2_set = Enum.into(tags2, MapSet.new)
    MapSet.disjoint?(tags1_set, tags2_set)
  end

end


Do not yell about function specs (for now)

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/'


Do not underscore used vars

Do not underscore used vars

defmodule ElixirStatus.Publisher do
  @moduledoc """
    The Publisher comes into play whenever a Posting is made or updated.

    He is e.g. tasked with promoting it on Twitter.
  """

  alias ElixirStatus.Posting

  @doc """
    Called when a posting is created by PostingController.

    Promotes the posting on Twitter, among other things.
  """
  def after_create(new_posting) do
    new_posting
      |> create_all_short_links
      |> post_to_twitter
  end

  @doc """
    Called when a posting is updated by PostingController.
  """
  def after_update(updated_posting) do
    updated_posting
      |> create_all_short_links
  end

  @doc """
    Returns a permalink consisting of an unmodified UID and a kebap-cased title.

        Publisher.permalink("aB87", "I really like this TiTlE")
        # => "aB87-i-really-like-this-title"
  """
  def permalink(_uid, nil) do
    nil
  end

  def permalink(uid, title) do
    permatitle = Regex.split(~r/\s|\%20/, title)
                  |> Enum.join("-")
                  |> String.downcase
                  |> String.replace(~r/[^a-z0-9\-]/, "")
    "#{uid}-#{permatitle}"
  end

  defp create_all_short_links(posting) do
    # TODO: implement URL shortener
    posting
  end

  defp post_to_twitter(posting) do
    %Posting{title: title, permalink: permalink} = posting

    url = "http://elixirstatus.com/p/#{permalink}" |> short_url

    "#{short_title(title)} #{url} #elixirlang"
      |> update_on_twitter(Mix.env)

    posting
  end

  defp update_on_twitter(tweet, :prod) do
    ExTwitter.update(tweet)
  end

  defp update_on_twitter(tweet, _) do
    IO.inspect {:tweeting, tweet}
  end

  def short_title(title, max \\ 100, truncate_with \\ "...") do
    if String.length(title) <= max do
      title
    else
      String.slice(title, 0..max-String.length(truncate_with)-1) <> truncate_with
    end
  end

  defp short_url(url) do
    # TODO: implement URL shortener
    url
  end
end

defmodule ElixirStatus.Publisher do
  @moduledoc """
    The Publisher comes into play whenever a Posting is made or updated.

    He is e.g. tasked with promoting it on Twitter.
  """

  alias ElixirStatus.Posting

  @doc """
    Called when a posting is created by PostingController.

    Promotes the posting on Twitter, among other things.
  """
  def after_create(new_posting) do
    new_posting
      |> create_all_short_links
      |> post_to_twitter
  end

  @doc """
    Called when a posting is updated by PostingController.
  """
  def after_update(updated_posting) do
    updated_posting
      |> create_all_short_links
  end

  @doc """
    Returns a permalink consisting of an unmodified UID and a kebap-cased title.

        Publisher.permalink("aB87", "I really like this TiTlE")
        # => "aB87-i-really-like-this-title"
  """
  def permalink(_uid, nil) do
    nil
  end

  def permalink(uid, title) do
    permatitle = Regex.split(~r/\s|\%20/, title)
                  |> Enum.join("-")
                  |> String.downcase
                  |> String.replace(~r/[^a-z0-9\-]/, "")
    "#{uid}-#{permatitle}"
  end

  defp create_all_short_links(posting) do
    # TODO: implement URL shortener
    posting
  end

  defp post_to_twitter(posting) do
    %Posting{title: title, permalink: permalink} = posting

    url = "http://elixirstatus.com/p/#{permalink}" |> short_url

    "#{short_title(title)} #{url} #elixirlang"
      |> update_on_twitter(Mix.env)

    posting
  end

  defp update_on_twitter(_tweet, :prod) do
    ExTwitter.update(_tweet)
  end

  defp update_on_twitter(_tweet, _) do
    IO.inspect {:tweeting, _tweet}
  end

  def short_title(title, max \\ 100, truncate_with \\ "...") do
    if String.length(title) <= max do
      title
    else
      String.slice(title, 0..max-String.length(truncate_with)-1) <> truncate_with
    end
  end

  defp short_url(url) do
    # TODO: implement URL shortener
    url
  end
end


mention Node.connect

mention Node.connect

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/atweiden/scripts.git/'


Added more videos meta data (#607)

Added more videos meta data (#607)


Add missing SnapshotRepo module

Add missing SnapshotRepo module


cleanup

cleanup

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/izakp/APR.git/'


atoms

atoms

error: pathspec 'elixir/141.exs' did not match any file(s) known to git


Catch all routes for Vue router setup.

Catch all routes for Vue router 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/futhr/ribbon.git/'


Presence stub for posts

Presence stub for posts

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/udia-software/udia.git/'


Include default list of files to be formatted by `mix format`

[
  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

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


added elixir version

added elixir version

defmodule Bst do
  def start(val), do: spawn(fn -> loop val end)

  def add(pid, val) do
    send pid, {:add, val}
  end

  def contains(pid, val) do
    send pid, {self, {:contains, val}}
    receive do res -> res end
  end

  defp check_tree(tree, value, sender) do
    if !tree do
      notify_sender_not_found sender
    else
      send tree, {sender, {:contains, value}}
    end
  end

  defp notify_sender_not_found(sender) do
    notify_sender sender, false
  end

  defp notify_sender_found(sender) do
    notify_sender sender, true
  end

  defp notify_sender(sender, found) do
    send sender, found
  end

  defp loop(val), do: loop(val, nil, nil)
  defp loop(val, left, right) do
    receive do
      {sender, {:contains, value}} ->
        cond do
          val == value ->
            notify_sender_found sender
          val > value ->
            check_tree left, value, sender
          val < value ->
            check_tree right, value, sender
        end
      {:add, value} ->
        goes_in_right_tree? = (val < value)
        goes_in_left_tree?  = (val > value)
        no_right_tree?      = (right == nil)
        no_left_tree?       = (left == nil)

        cond do
          goes_in_right_tree? ->
            if no_right_tree? do
              right = Bst.start value
            else
              add right, value
            end
          goes_in_left_tree? ->
            if no_left_tree? do
              left = Bst.start value
            else
              add left, value
            end
        end
    end
    loop val, left, right
  end

  def test do
    root = Bst.start 10_000
    result = Bst.contains root, 10_000
    IO.puts "#{result}"

    Bst.add root, 100
    result = Bst.contains root, 100
    IO.puts "#{result}"

    result = Bst.contains root, 10 #fail
    IO.puts "#{result}"
  end
end

Bst.test

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


fix (elixir/gigasecond): Adapt to new test suite

Expected types changed from `:calendar.date` to `:calendar.datetime` in
new test suite.

Change type signature and logic of `Gigasecond.from` accordingly.

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/Bugfry/exercises.git/'


Cleanup of some anonymous functions

Cleanup of some anonymous functions

defmodule GameStateMacro do

    defmacro create_updater(setting) do
       quote do
          def unquote(:"set_#{setting}")(state, val) do
              %{state | String.to_atom(unquote(setting)) => val}
          end
       end
    end

end
defmodule GameState do

    require GameStateMacro
    def initial() do
        %{:timebank => 0, :time_per_move=>0,
          :max_rounds=> 0, :bot_name =>"", :opponent_bot_name => "",
          :starting_armies => 0,
          :starting_regions => [],
          :starting_pick_amount => 0,
          :map => %{},
          :neighbors => %{},
          :ownership => %{},
          :opponent_starting_regions => [],
          :last_opponent_moves => []}
    end

    GameStateMacro.create_updater "timebank"
    GameStateMacro.create_updater "time_per_move"
    GameStateMacro.create_updater "max_rounds"
    GameStateMacro.create_updater "bot_name"
    GameStateMacro.create_updater "opponent_bot_name"
    GameStateMacro.create_updater "starting_armies"
    GameStateMacro.create_updater "starting_regions"
    GameStateMacro.create_updater "starting_pick_amount"

    GameStateMacro.create_updater "opponent_starting_regions"
    GameStateMacro.create_updater "last_opponent_moves"

    def set_super_regions(state, super_regions) do
      regions = for [super_region, bonus] <- super_regions, into: %{}, do: {super_region, %{:bonus_armies => bonus, :regions => []}}
      %{state | :map => regions}
    end

    defp update_neighbors({region, neighbors}, state) do
       List.foldl(neighbors, state,
                            fn neighbor, state ->
                                  put_in state, [:neighbors, neighbor],
                                       (if state.neighbors[neighbor] == nil do
                                           [region]
                                        else
                                           state.neighbors[neighbor] ++ [region]
                                        end)
                            end)
    end

    def set_neighbors(state, neighbors) do
       new_state=  %{state | :neighbors => neighbors}
       List.foldl(Map.to_list(neighbors), new_state, &update_neighbors/2)
    end

    defp put_in_region({super_region, regions}, state) do
       put_in state, [:map, super_region, :regions], regions
    end

    defp update_regions(state, regions) do
      List.foldl(regions, state, &put_in_region/2 )
    end

    defp set_initial_ownership(state, regions) do
       all_regions = regions |> Enum.map(&(elem(&1, 1))) |> List.flatten
       %{state | :ownership => (for region <- all_regions, into: %{}, do: {region, {"neutral", 2} }) }
    end

    def set_regions(state, regions) do
        state |>
        update_regions(regions) |>
        set_initial_ownership(regions)

    end

    defp update_ownership({region, name, armies}, state) do
        put_in state, [:ownership, region], {name, armies}
    end

    defp update_fog_of_war({region, {_, armies}}, state) do
        update_ownership({region, "fog", armies}, state)
    end

    defp get_other_regions(regions, state) do
       region_list = Enum.map(regions, &(elem &1, 0))
       Enum.filter(state.ownership, &(!Enum.find_value(region_list, fn(x) -> x == elem &1, 0 end)))
    end

    def update_map(state, regions) do
        new_state = List.foldl(regions, state, &update_ownership/2)
        List.foldl(get_other_regions(regions, new_state), new_state, &update_fog_of_war/2)
    end

    def set_wastelands(state, wastelands) do
        marked_wastelands = Enum.map(wastelands, &{&1, "neutral", 6})
        update_map(state, marked_wastelands)
    end

    def get_armies(state, region) do
        elem(state.ownership[region], 1)
    end

end

defmodule GameStateMacro do

    defmacro create_updater(setting) do
       quote do
          def unquote(:"set_#{setting}")(state, val) do
              %{state | String.to_atom(unquote(setting)) => val}
          end
       end
    end

end
defmodule GameState do

    require GameStateMacro
    def initial() do
        %{:timebank => 0, :time_per_move=>0,
          :max_rounds=> 0, :bot_name =>"", :opponent_bot_name => "",
          :starting_armies => 0,
          :starting_regions => [],
          :starting_pick_amount => 0,
          :map => %{},
          :neighbors => %{},
          :ownership => %{},
          :opponent_starting_regions => [],
          :last_opponent_moves => []}
    end

    GameStateMacro.create_updater "timebank"
    GameStateMacro.create_updater "time_per_move"
    GameStateMacro.create_updater "max_rounds"
    GameStateMacro.create_updater "bot_name"
    GameStateMacro.create_updater "opponent_bot_name"
    GameStateMacro.create_updater "starting_armies"
    GameStateMacro.create_updater "starting_regions"
    GameStateMacro.create_updater "starting_pick_amount"

    GameStateMacro.create_updater "opponent_starting_regions"
    GameStateMacro.create_updater "last_opponent_moves"

    def set_super_regions(state, super_regions) do
      regions = for [super_region, bonus] <- super_regions, into: %{}, do: {super_region, %{:bonus_armies => bonus, :regions => []}}
      %{state | :map => regions}
    end

    defp update_neighbors({region, neighbors}, state) do
       List.foldl(neighbors, state,
                            fn neighbor, state ->
                                  put_in state, [:neighbors, neighbor],
                                       (if state.neighbors[neighbor] == nil do
                                           [region]
                                        else
                                           state.neighbors[neighbor] ++ [region]
                                        end)
                            end)
    end

    def set_neighbors(state, neighbors) do
       new_state=  %{state | :neighbors => neighbors}
       List.foldl(Map.to_list(neighbors), new_state, &update_neighbors/2)
    end

    defp put_in_region({super_region, regions}, state) do
       put_in state, [:map, super_region, :regions], regions
    end

    defp update_regions(state, regions) do
      List.foldl(regions, state, &put_in_region/2 )
    end

    defp set_initial_ownership(state, regions) do
       all_regions = regions |> Enum.map(&(elem(&1, 1))) |> List.flatten
       %{state | :ownership => (for region <- all_regions, into: %{}, do: {region, {"neutral", 2} }) }
    end

    def set_regions(state, regions) do
        state |>
        update_regions(regions) |>
        set_initial_ownership(regions)

    end

    defp update_ownership({region, name, armies}, state) do
        put_in state, [:ownership, region], {name, armies}
    end

    defp update_fog_of_war({region, {_, armies}}, state) do
        update_ownership({region, "fog", armies}, state)
    end

    defp get_other_regions(regions, state) do
       region_list = Enum.map(regions, &(elem &1, 0))
       Enum.filter(state.ownership, &(!Enum.find_value(region_list, fn(x) -> x == elem &1, 0 end)))
    end

    def update_map(state, regions) do
        new_state = List.foldl(regions, state, &update_ownership/2)
        List.foldl(get_other_regions(regions, new_state), new_state, &update_fog_of_war/2)
    end

    def set_wastelands(state, wastelands) do
        marked_wastelands = Enum.map(wastelands, &({&1, "neutral", 6}))
        update_map(state, marked_wastelands)
    end

    def get_armies(state, region) do
        elem(state.ownership[region], 1)
    end

end


episodes_podcast_id_id and epsiodes_podcast_id_publishing_date_inserted_at indexes

defmodule Pan.Repo.Migrations.RemoveCommentAndRecommendToFromLike do
  use Ecto.Migration

  def change do
    create index(:episodes, [:podcast_id, :id])
    create index(:episodes, [:podcast_id, :publishing_date, :inserted_at])
  end
end

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


fix bug in rel/config

fix bug in rel/config

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/'


fix whitespace

fix whitespace

defmodule Mix.Tasks.WhiteBread.Run do
  use Mix.Task

  @shortdoc "Runs all the feature files with WhiteBread"

  def run(argv) do
    {options, arguments, _} = OptionParser.parse(argv)

    case arguments do
      [context_name | _ ] -> run(context_name, options)
      [] -> Mix.raise "Expected context module to be given. Use `mix white_bread.run Context`"
    end

  end
cd
  def run(context_name, _options) do
    {context, []} = Code.eval_string(context_name)
    result = WhiteBread.run(context)

    result
    |> WhiteBread.FinalResultPrinter.text
    |> IO.puts

    %{failures: failures} = result
    System.at_exit fn _ ->
      if Enum.count(failures) > 0, do: exit({:shutdown, 1})
    end
  end

end


Added bcc in the camera share request emails


Move cache invalidation into background process to speedup camera deletion (#376)


Let's try elixir!

Let's try elixir!

defmodule Anagram do
  def hash(word) do
    Enum.reduce(Enum.sort(String.split(word, "")), "", fn(x,y) -> x <> y end)
  end

  def is_anagram(w1, w2) do
    hash(w1) == hash(w2)
  end

  def collect_anagrams(words) do
    anagrams = collect_anagrams(words, %{})
    pretty_print Map.values anagrams
  end

  def collect_anagrams([h | t], acc) do
    key = hash(h)
    if not Map.has_key?(acc, key) do
      acc = Map.put(acc, key, [])
    end
    acc = Map.update!(acc, key, fn val -> val ++ [h] end)
    collect_anagrams(t, acc)
  end

  def collect_anagrams([], acc) do
    acc
  end

  def pretty_print([h|t]) do
    res = case h do
            [h2|t2] ->
              "[#{h2}," <> pretty_print(t2) <> "],"
            _ ->
              "#{h},"
          end
    res <> pretty_print(t)
  end

  def pretty_print([]) do
    ""
  end

end

IO.puts Anagram.collect_anagrams(["ho", "oh", "hello", "loner", "llohe"])

error: pathspec 'elixir/anagram.exs' did not match any file(s) known to git


ignoring a value

ignoring a value

error: pathspec 'elixir/8.exs' did not match any file(s) known to git


bind once per match

bind once per match

[a,a] = [1, 1]
IO.inspect a

#[a,a] = [1, 2]  #Raise MatchError

error: pathspec 'elixir/9.exs' did not match any file(s) known to git


Add grouping field to contest and host tables

The default value, "2", is set to migrate all existing contests and hosts, which historically have belonged to what is now "grouping 2".

defmodule Jumubase.Repo.Migrations.AddGroupingToContestsAndHosts do
  use Ecto.Migration

  # By default, use the grouping that "existed" before multi-grouping support
  @default_grouping "2"

  def change do
    alter table(:contests) do
      add :grouping, :string, null: false, default: @default_grouping
    end

    alter table(:hosts) do
      add :current_grouping, :string, null: false, default: @default_grouping
    end
  end
end

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


Chapter 12 answers

Chapter 12 answers

defmodule Chain do
	def counter(next_pid) do
		receive do
			n ->
				send next_pid, n + 1
		end
	end

	def create_processes(n) do
		last = Enum.reduce 1..n, self,
			fn (_, send_to) ->
				spawn(Chain, :counter, [send_to])
			end

		# start the count by sending
		send last, 0

		# and wait for the result to come back to us
		receive do
			final_answer when is_integer(final_answer) ->
				"Result is #{inspect(final_answer)}"
		end
	end

	def run(n) do
		IO.puts inspect :timer.tc(Chain, :create_processes, [n])
	end
end

#
# Results
#

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(10)"
# {4262, "Result is 10"}

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(100)"
# {4318, "Result is 100"}

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(1000)"
# {12883, "Result is 1000"}

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(10_000)"
# {100670, "Result is 10000"}

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(400_000)"
# {2946674, "Result is 400000"}

# $ elixir --erl "+P 1000000" -r 12.1.exs -e "Chain.run(1_000_000)"
# {8020080, "Result is 1000000"}

error: pathspec 'WorkingWithMultipleProcesses/14.1.exs' did not match any file(s) known to git


Add test for AnnotationTagController/create

defmodule Core.AnnotationTagControllerTest do
  use Core.ConnCase

  setup do
    # Create a super user
    user = insert_user(%{})
    |> Map.put(:permissions, ["super_user"])
    {:ok, jwt, _full_claims} = Guardian.encode_and_sign(user)

    article = insert_article(%{})

    # Create an authored annotation
    annotation = insert_annotation(user, %{article_id: article.id})

    # Create a tag
    tag = insert_tag(%{})

    {:ok, %{annotation: annotation, jwt: jwt, article: article, tag: tag}}
  end

  test "Create correctly", %{annotation: annotation, jwt: jwt, tag: tag} do
    conn = build_conn()
    conn = conn
    |> put_req_header("authorization", "Bearer #{jwt}")
    |> post(annotation_annotation_tag_path(conn, :create, annotation.id), %{tag_id: tag.id})

    # Check response body
    assert json_response(conn, 201)
  end

  test "Create AnnotationTag: Tag not found", %{annotation: annotation, jwt: jwt, tag: tag} do
    conn = build_conn()
    conn = conn
    |> put_req_header("authorization", "Bearer #{jwt}")
    |> post(annotation_annotation_tag_path(conn, :create, annotation.id), %{tag_id: tag.id+1})

    assert json_response(conn, 422)
  end

  # TODO: Check delete /annotations/<A_ID>/tags/<T_ID>
  # When tag.id = <T_ID> doesn't exist
  test "Delete AnnotationTag: Tag not found", %{annotation: annotation, jwt: jwt, tag: tag} do
    # Create the AnnotationTag using models...
    # Create the connection
    # assert response(conn, 404)
  end
end

error: pathspec 'test/controllers/annotation_tag_controller_test.exs' did not match any file(s) known to 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/'


Increment version [ci skip]

Increment version [ci skip]


Added Account initial migration.

Added Account initial migration.

defmodule HELM.Account.Repo.Migrations.CreateAccountDatabase do
  use Ecto.Migration

  def change do
    create table(:accounts, primary_key: false) do
      add :account_id, :string, primary_key: true
      add :password, :string, null: false
      add :email, :string
      add :confirmed, :boolean, default: false

      timestamps
    end

    create unique_index(:accounts, [:account_id], name: :unique_account_id)
    create unique_index(:accounts, [:email], name: :unique_account_email)
  end
end

error: pathspec 'apps/account/priv/repo/migrations/20160919214425_create_account_database.exs' did not match any file(s) known to git


Simple VFS implementation

Simple VFS implementation

defmodule TheScratch.Vfs do
  use GenServer

  require Logger

  def start_link(entries) do
    cache = Enum.reduce(entries, %{}, fn(entry, cache) ->
      {:ok, vfs_name} = Map.fetch(entry, :name)
      Map.put(cache, vfs_name, load_vfs(entry))
    end)

    Logger.debug(inspect(cache))

    # GenServer.start_link(__MODULE__, %{}, name: __MODULE__)

    Task.start_link(fn -> loop(%{}) end)
  end

  def get(key) do
    send(self(), {:get, key})
    nil
  end

  defp load_vfs(entry) do
    {:ok, regex} = Map.fetch(entry, :regex)

    read_files = fn(path, cache) ->
      Logger.debug("#{__MODULE__} Loading #{path}")
      {:ok, content} = File.read(path)
      Map.put(cache, Regex.replace(regex, path, "\\1"), content)
    end

    {:ok, pattern} = Map.fetch(entry, :pattern)
    Path.wildcard(pattern)
      |> Enum.reduce(%{}, read_files)
  end

  defp loop(map) do
    receive do
      key -> Logger.debug("KEY: #{inspect(key)}")
    end
  end
end

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


Import the test config to avoid dup.

Import the test config to avoid dup.

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/futhr/fux.git/'


add an elixir port for fun!

add an elixir port for fun!

defmodule LogFilt do

  @match_table [
          {~r/INFO/, :green},
          {~r/WARNING/, :yellow},
          {~r/ERROR/, :red},
          {~r/CRITICAL/, :magenta},
          {~r/^.*!!!.*$/, :cyan},
          {~r/\d{4}-\d{2}-\d{2}[^]]*]/, :grey},
      ]

  @color_map %{
                 red: "\x1b[31;1m",
                 green: "\x1b[32;1m",
                 yellow: "\x1b[33;1m",
                 magenta: "\x1b[35;1m",
                 cyan: "\x1b[36;1m",
                 grey: "\x1b[30;1m",
                 clear: "\x1b[0m",
             }

  @doc """
  Get color annotations for matches of a pattern in the provided string.

  A priority parameter controls how conflicting colorations are resolved.

  Returns: {priority, start_position, len, color}
  """
  def maybe_colorize_partial priority, color, pat, line do
    Regex.scan(pat, line, return: :index)
    |> Enum.map fn [{start, len}|_] -> {priority, start, len, color} end
  end

  @doc """
  Get the color from the color annotation prior to the specified position.

  Default to :clear if there's no annotation before the current position.
  """
  def prev_color(accumlated_colors, pos) do
    accumlated_colors
    |> Enum.sort
    |> Enum.take_while(fn {i, _} -> i <= pos end)
    |> List.last
    |> case do
         nil -> :clear
         {_, color} -> color
       end
  end

  @doc """
  Turn a list of colorization tuples into a list of positions where the color changes.

  The colorization tuples are {priority, start, len, color}.  Colorizations
  are applied according to start poistion, using the priority parameter to
  break any ties.

  Return a list of the format [{pos, color}...]
  """
  def prioritized_colorization_to_color_change_offsets colorization do
    colorization
    |> Enum.sort_by(fn {priority, start, len, color} ->
                      {start, -1 * priority, len, color} end)
    |> Enum.reduce([], fn {_, start, len, color}, acc ->
                     acc ++ [{start, color},
                             {start + len, prev_color(acc, start + len)}]
                   end)
  end

  @doc """
  Apply a list of positions where the color changes to a string.

  This will insert the appropriate escape codes into the string at those
  positions.
  """
  def apply_prioritized_colorization colorization, line do
    color_tup = (prioritized_colorization_to_color_change_offsets(colorization)
      |> Enum.sort
      |> Enum.reduce({0, ""}, fn {pos, color}, {startpos, str} ->
                       newstr = (str <> String.slice(line, startpos, pos - startpos)
                                 <> @color_map[color])
                       newpos = pos
                       {newpos, newstr}
                     end))

    {last_pos, color_str} = color_tup
    color_str <> String.slice(line, last_pos, String.length(line)) <> @color_map[:clear]
  end

  @doc """
  Colorize a string of text accorting to the rules in @match_table.
  """
  def colorize_line line do
    @match_table
    |> Enum.with_index
    |> Enum.map(fn {{pat, color}, i} -> maybe_colorize_partial(i, color, pat, line) end)
    |> List.flatten
    |> apply_prioritized_colorization(line)
  end

  @doc """
  Continually read from stdin, colorize the lines, and write to stdout.
  """
  def main do
    case IO.read :line do
      {:error, _} ->
        nil
      :eof ->
        nil
      s ->
        IO.puts colorize_line String.strip s
        main
    end
  end

end

LogFilt.main

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


Add test for agents koans

Add test for agents koans

defmodule AgentTests do
  use ExUnit.Case
  import TestHarness

  test "Agents" do
    answers = [
      "Hi there",
      "HI THERE",
      {:multiple, [["Milk"], ["Bread", "Milk"]]},
      false,
    ]

    test_all(Agents, answers)
  end
end

error: pathspec 'test/koans/agents_koans_test.exs' did not match any file(s) known to git


Fixed requests that use basic auth with credentials

defmodule EvercamMedia.HTTPClient do
  alias EvercamMedia.HTTPClient.DigestAuth

  def get(url) do
    HTTPotion.get url
  end

  def get(url, ":") do
    get(url)
  end

  def get(:basic_auth, url, username, password) do
    HTTPotion.get url, [basic_auth: {username, password}]
  end

  def get(:digest_auth, url, username, password) do
    response = get(url)
    get(:digest_auth, response, url, username, password)
  end

  def get(:cookie_auth, snapshot_url, username, password) do
    u = URI.parse(snapshot_url)
    login_url = u.scheme <> "://" <> u.authority <> "/login.cgi"
    cookie = get_cookie(login_url, username, password)
    HTTPotion.get snapshot_url, [headers: ["Cookie": cookie]]
  end

  def get(:digest_auth, response, url, username, password) do
    digest_token =  DigestAuth.get_digest_token(response, url, username, password)
    HTTPotion.get url, headers: ["Authorization": "Digest #{digest_token}"]
  end

  defp get_cookie(url, username, password) do
    request = HTTPotion.get url
    cookie_string = Dict.get(request.headers, :"Set-Cookie") |> Enum.join(",")
    cookie = Regex.run(~r/(AIROS_SESSIONID=[a-z0-9]+)/, cookie_string)
             |> hd
    HTTPotion.post url, [body: multipart_text(username, password),
      headers: ["Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryEq1VsbBovj79sSoL", "Cookie": cookie]]
    cookie
  end

  defp multipart_text(username, password) do
    "------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data;name=\"uri\"\r\n\r\n\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition:form-data; name=\"username\"\r\n\r\n#{username}\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n#{password}\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data; name=\"Submit\"\r\n\r\nLogin\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\n"
  end
end

defmodule EvercamMedia.HTTPClient.DigestAuth do
  def get_digest_token(response, url, username, password) do
    digest_head = parse_digest_header(response.headers |> Dict.get(:"WWW-Authenticate"))
    %{"realm" => realm, "nonce"  => nonce} = digest_head
    cnonce = :crypto.strong_rand_bytes(16) |> md5
    url = URI.parse(url)
    response = create_digest_response(
                username,
                password,
                realm,
                digest_head |> Map.get("qop"),
                url.path,
                nonce,
                cnonce)
      [{"username", username},
       {"realm", realm},
       {"nonce", nonce},
       {"uri", url.path},
       {"cnonce", cnonce},
       {"response", response}]
      |> add_opaque(digest_head |> Map.get("opaque"))
      |> Enum.map(fn {key, val} -> {key, "\"#{val}\""} end)
      |> add_nonce_counter
      |> add_auth(digest_head |> Map.get("qop"))
      |> Enum.map(fn {key, val} -> "#{key}=#{val}" end)
      |> Enum.join(", ")
  end

  defp parse_digest_header(auth_head) do
    cond do
      parsed = Regex.scan(~r/(\w+\s*)=\"([\w=\s\\]+)/, auth_head) ->
        parsed
        |> Enum.map(fn [_, key, val] -> {key, val} end)
        |> Enum.into(%{})
      true -> raise "Error in digest authentication header: #{auth_head}"
    end
  end

  defp create_digest_response(username, password, realm, qop, uri, nonce, cnonce) do
    ha1 = [username, realm, password] |> Enum.join(":") |> md5
    ha2 = ["GET", uri] |> Enum.join(":") |> md5
    create_digest_response(ha1, ha2, qop, nonce, cnonce)
  end

  defp create_digest_response(ha1, ha2, nil, nonce, _cnonce),
  do: [ha1, nonce, ha2] |> Enum.join(":") |> md5

  defp create_digest_response(ha1, ha2, _qop, nonce, cnonce) do
    [ha1, nonce, "00000001", cnonce, "auth", ha2] |> Enum.join(":") |> md5
  end

  defp add_opaque(digest, nil), do: digest
  defp add_opaque(digest, opaque), do: [{"opaque", opaque} | digest]

  defp add_nonce_counter(digest), do: [{"nc", "00000001"} | digest]

  defp add_auth(digest, nil) do
    digest
  end

  defp add_auth(digest, cop) do
    cond do
      cop |> String.contains?("auth") -> [{"qop", "\"auth\""} | digest]
      true -> digest
    end
  end

  defp md5(data) do
    Base.encode16(:erlang.md5(data), case: :lower)
  end
end

defmodule EvercamMedia.HTTPClient do
  alias EvercamMedia.HTTPClient.DigestAuth

  def get(url) do
    HTTPotion.get url
  end

  def get(url, ":") do
    get(url)
  end

  def get(:basic_auth, url, username, password) do
    HTTPotion.get url, [:basic_auth, {username, password}]
  end

  def get(:digest_auth, url, username, password) do
    response = get(url)
    get(:digest_auth, response, url, username, password)
  end

  def get(:cookie_auth, snapshot_url, username, password) do
    u = URI.parse(snapshot_url)
    login_url = u.scheme <> "://" <> u.authority <> "/login.cgi"
    cookie = get_cookie(login_url, username, password)
    HTTPotion.get snapshot_url, [headers: ["Cookie": cookie]]
  end

  def get(:digest_auth, response, url, username, password) do
    digest_token =  DigestAuth.get_digest_token(response, url, username, password)
    HTTPotion.get url, headers: ["Authorization": "Digest #{digest_token}"]
  end

  defp get_cookie(url, username, password) do
    request = HTTPotion.get url
    cookie_string = Dict.get(request.headers, :"Set-Cookie") |> Enum.join(",")
    cookie = Regex.run(~r/(AIROS_SESSIONID=[a-z0-9]+)/, cookie_string)
             |> hd
    HTTPotion.post url, [body: multipart_text(username, password),
      headers: ["Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryEq1VsbBovj79sSoL", "Cookie": cookie]]
    cookie
  end

  defp multipart_text(username, password) do
    "------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data;name=\"uri\"\r\n\r\n\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition:form-data; name=\"username\"\r\n\r\n#{username}\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n#{password}\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\nContent-Disposition: form-data; name=\"Submit\"\r\n\r\nLogin\r\n------WebKitFormBoundaryEq1VsbBovj79sSoL\r\n"
  end
end

defmodule EvercamMedia.HTTPClient.DigestAuth do
  def get_digest_token(response, url, username, password) do
    digest_head = parse_digest_header(response.headers |> Dict.get(:"WWW-Authenticate"))
    %{"realm" => realm, "nonce"  => nonce} = digest_head
    cnonce = :crypto.strong_rand_bytes(16) |> md5
    url = URI.parse(url)
    response = create_digest_response(
                username,
                password,
                realm,
                digest_head |> Map.get("qop"),
                url.path,
                nonce,
                cnonce)
      [{"username", username},
       {"realm", realm},
       {"nonce", nonce},
       {"uri", url.path},
       {"cnonce", cnonce},
       {"response", response}]
      |> add_opaque(digest_head |> Map.get("opaque"))
      |> Enum.map(fn {key, val} -> {key, "\"#{val}\""} end)
      |> add_nonce_counter
      |> add_auth(digest_head |> Map.get("qop"))
      |> Enum.map(fn {key, val} -> "#{key}=#{val}" end)
      |> Enum.join(", ")
  end

  defp parse_digest_header(auth_head) do
    cond do
      parsed = Regex.scan(~r/(\w+\s*)=\"([\w=\s\\]+)/, auth_head) ->
        parsed
        |> Enum.map(fn [_, key, val] -> {key, val} end)
        |> Enum.into(%{})
      true -> raise "Error in digest authentication header: #{auth_head}"
    end
  end

  defp create_digest_response(username, password, realm, qop, uri, nonce, cnonce) do
    ha1 = [username, realm, password] |> Enum.join(":") |> md5
    ha2 = ["GET", uri] |> Enum.join(":") |> md5
    create_digest_response(ha1, ha2, qop, nonce, cnonce)
  end

  defp create_digest_response(ha1, ha2, nil, nonce, _cnonce),
  do: [ha1, nonce, ha2] |> Enum.join(":") |> md5

  defp create_digest_response(ha1, ha2, _qop, nonce, cnonce) do
    [ha1, nonce, "00000001", cnonce, "auth", ha2] |> Enum.join(":") |> md5
  end

  defp add_opaque(digest, nil), do: digest
  defp add_opaque(digest, opaque), do: [{"opaque", opaque} | digest]

  defp add_nonce_counter(digest), do: [{"nc", "00000001"} | digest]

  defp add_auth(digest, nil) do
    digest
  end

  defp add_auth(digest, cop) do
    cond do
      cop |> String.contains?("auth") -> [{"qop", "\"auth\""} | digest]
      true -> digest
    end
  end

  defp md5(data) do
    Base.encode16(:erlang.md5(data), case: :lower)
  end
end


Increase pool timeout

Increase pool timeout

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/'


Union Find in Elixir

Union Find in Elixir

defmodule UnionFind do
  def initset(arr, index) when index < 0 do
    {:ok, arr}
  end

  def initset(arr, index) do
    initset([index] ++ arr, index-1)
  end

  def findset(arr, x) do
    if (Enum.at(arr, x) == x), do: x,
    else: findset(arr, Enum.at(arr, x))
  end

  def unionset(arr, a, b) do
    x = findset(arr, a)
    y = findset(arr, b)

    if (x != y), do:
      arr = List.update_at(arr, y, &(&1 = x))

    {:ok, arr}
  end
end

{:ok, arr} = UnionFind.initset([], 10)

{:ok, arr} = UnionFind.unionset(arr, 2, 4)
{:ok, arr} = UnionFind.unionset(arr, 4, 6)
{:ok, arr} = UnionFind.unionset(arr, 6, 8)
{:ok, arr} = UnionFind.unionset(arr, 8, 10)

{:ok, arr} = UnionFind.unionset(arr, 1, 3)
{:ok, arr} = UnionFind.unionset(arr, 3, 5)
{:ok, arr} = UnionFind.unionset(arr, 5, 7)
{:ok, arr} = UnionFind.unionset(arr, 7, 9)

{:ok, arr} = UnionFind.unionset(arr, 1, 2)

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


INNM sctid could be NULL

INNM sctid could be NULL


[patch] set default declaration_limit (#1891)


Remove Intercom creds.

Remove Intercom creds.

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-admin.git/'


cleaned up a little

cleaned up a little

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/'


select assoc tests

select assoc tests

defmodule Formex.SelectAssocBasicTestType do
  use Formex.Type
  require Formex.CustomField.SelectAssoc

  def build_form(form) do
    form
    |> add(Formex.CustomField.SelectAssoc, :category_id, phoenix_opts: [
      prompt: "Choose category"
    ])
  end
end

defmodule Formex.SelectAssocChoiceNameAtomTestType do
  use Formex.Type
  require Formex.CustomField.SelectAssoc

  def build_form(form) do
    form
    |> add(Formex.CustomField.SelectAssoc, :category_id, choice_name: :id)
  end
end

defmodule Formex.SelectAssocChoiceNameFunctionTestType do
  use Formex.Type
  require Formex.CustomField.SelectAssoc

  def build_form(form) do
    form
    |> add(Formex.CustomField.SelectAssoc, :category_id, choice_name: fn category ->
      category.name <> category.name
    end)
  end
end

defmodule Formex.SelectAssocTest do
  use Formex.TestCase
  import Formex.Builder
  alias Formex.SelectAssocBasicTestType
  alias Formex.SelectAssocChoiceNameAtomTestType
  alias Formex.SelectAssocChoiceNameFunctionTestType
  alias Formex.TestModel.Article
  alias Formex.TestModel.Category
  alias Formex.TestRepo

  def insert_categories() do
    TestRepo.insert(%Category{name: "asd"})
    TestRepo.insert(%Category{name: "bcd"})
    TestRepo.insert(%Category{name: "cfg"})
  end

  test "basic" do
    insert_categories()

    form = create_form(SelectAssocBasicTestType, %Article{})

    {:safe, form_html} = Formex.View.formex_form_for(form, "", fn f ->
      Formex.View.formex_rows(f)
    end)

    assert form_html
    |> to_string
    |> String.match?(~r/asd/)
  end

  test "choice name atom" do
    insert_categories()

    form = create_form(SelectAssocChoiceNameAtomTestType, %Article{})

    choices = Enum.at(form.fields, 0).data[:choices]
    choice  = Enum.at(choices, 0)
    {choice_name, _} = choice
    assert is_number(choice_name)
  end

  test "choice name function" do
    insert_categories()

    form = create_form(SelectAssocChoiceNameFunctionTestType, %Article{})

    choices = Enum.at(form.fields, 0).data[:choices]
    choice  = Enum.at(choices, 0)
    {choice_name, _} = choice
    assert choice_name == "asdasd"
  end

end

error: pathspec 'test/formex/select_assoc_test.exs' did not match any file(s) known to git


Added comments to the repo/seeds file

Added comments to the repo/seeds file

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/riverrun/openmaize-phoenix.git/'


adding DB adapter to streamline implementation different storages (thanks @aleDsz)

defmodule Guardian.DB.Adapter do
  @moduledoc """
  The Guardian DB Adapter.

  This behaviour allows to use any storage system
  for Guardian Tokens.
  """

  @typep query :: Ecto.Query.t()
  @typep schema :: Ecto.Schema.t()
  @typep schema_or_changeset :: schema() | Ecto.Changeset.t()
  @typep queryable :: query() | schema()
  @typep opts :: keyword()
  @typep id :: pos_integer() | binary() | Ecto.UUID.t()

  @callback one(queryable()) :: nil | schema()
  @callback get(queryable(), id()) :: nil | schema()
  @callback insert(schema_or_changeset()) :: {:ok, schema()}
  @callback delete(schema_or_changeset()) :: {:ok, schema()}
  @callback delete_all(queryable()) :: {:ok, pos_integer()}
  @callback delete_all(queryable(), opts()) :: {:ok, pos_integer()}
end

error: pathspec 'lib/guardian/db/adapter.ex' did not match any file(s) known to git


add control_flow_2

add control_flow_2

defmodule ControlFlow2 do
  # cond の例
  def fizzbuzz_cond(n) do
    cond do
      rem(n, 3) == 0 and rem(n, 5) == 0 -> "FizzBuzz"
      rem(n, 3) == 0 -> "Fizz"
      rem(n, 5) == 0 -> "Buzz"
      true -> n
    end
  end
  # case の例 (ControlFlow1)
  def fizzbuzz_case(n) do
    case {rem(n, 3), rem(n, 5)} do
      {0, 0} -> "FizzBuzz"
      {0, _} -> "Fizz"
      {_, 0} -> "Buzz"
      {_, _} -> n
    end
  end
  # パターンマッチの例
  def fizzbuzz_match(n), do: _fizzbuzz_match(n, rem(n, 3), rem(n, 5))
  defp _fizzbuzz_match(_, 0, 0), do: "FizzBuzz"
  defp _fizzbuzz_match(_, 0, _), do: "Fizz"
  defp _fizzbuzz_match(_, _, 0), do: "Buzz"
  defp _fizzbuzz_match(n, _, _), do: n
end

"""
「ガード節を使って、関数を分けたものだ」に相当する実装が見当たらないので、
P124 の fizzbuzz3.ex の例 (パターンマッチ) を指していると解釈。

Q「どれが、問題を最も表現しているだろうか。どれが、最もメンテナンスしやすいだろうか。」
A:
"3" や "5" を複数個所に書かないといけない cond の例はまず除外。
パターンマッチの例はシンプルだが引数が多い。また個々の関数を離れた場所にも書けてしまい、この場合は好ましくない。
case の例が一番凝縮され、メンテナンスしやすい表現のように思える。

Q「これらの選択肢を考慮するよう、注意を喚起する方法を思いつけるだろうか。」
A:
静的解析などを用い、冗長な記述や条件式には警告を出したり、機械的に変換可能なものはより良いほうに変換するよう提案させたりが考えられる。
例えば Java の IDE の中には for 文を拡張 for 文に、拡張 for 文を Stream に変換するよう提案できるものがある。
"""

error: pathspec 'lib/control_flow_2/answer.exs' did not match any file(s) known to git


Transport timeout tweak for Heroku.

Transport timeout tweak for Heroku.

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/futhr/ribbon.git/'


locations

locations

defmodule Locations do
  def loc(place) do
    key = String.to_atom(place)
    map = %{:"collegiate" => "P.H.D", :"the movies" => "M.S.S", :"gucci kroger" => "M.S.S", :"ghetto kroger" => "U.C.B", :"the math empo" => "U.C.B", :"maple ridge" => "M.S.N", :"pheasant run" => "P.H.D", :"terrace view" => "P.H.D", :"foxridge" => "H.W.D", :"the village" => "P.R.O"}
    map[key]
  end
end

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


Increment version [ci skip]

Increment version [ci skip]


Disable changing camera status on general http error from a controller


add ListsAndRecursion-3

add ListsAndRecursion-3

defmodule MyList4 do
  def caesar([], _), do: []
  def caesar([head|tail], y), do: [loop(head, y) | caesar(tail, y)]
  defp loop(x, y), do: rem(((x - 97) + y), 26) + 97
end

IO.puts MyList4.caesar 'abcde', 13 # nopqr
IO.puts MyList4.caesar 'z', 1 # a
IO.puts MyList4.caesar 'b', -1 # a

error: pathspec 'lib/ListsAndRecursion-3/caesar.ex' did not match any file(s) known to git


Remove .iex.exs file

Remove .iex.exs file

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/'


print

print

error: pathspec 'elixir/125.exs' did not match any file(s) known to git


chore: mimic Enum.filter

chore: mimic Enum.filter

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/rockdragon/julia-programming.git/'


Raise on updating the camera status

Raise on updating the camera status


change /admin to /api URL prefix for connections (#3104)

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/'


allow to qualify only active medication requests


Remove unused alias

Remove unused alias


Added signing/verying logging

Added signing/verying logging

defmodule Joken.Signer do
  alias Joken.Token
  alias Joken.Signer
  require Logger

  @moduledoc """
  Signer is the JWK (JSON Web Key) and JWS (JSON Web Signature) configuration of Joken.

  JWK is used by JWS to generate a token _signature_ that is appended to the end of the
  token compact representation.

  Joken uses https://hex.pm/packages/jose to do signing and verification.
  """

  @type jwk :: %{}
  @type jws :: %{}

  @type t :: %__MODULE__{
    jwk: jwk,
    jws: jws
  }

  defstruct [:jwk, :jws]

  @doc "Convenience for generating an HS*** Joken.Signer"
  @spec hs(binary, binary) :: Signer.t
  def hs(alg, secret) when is_binary(secret)
    and alg in ["HS256", "HS384", "HS512"] do
    %Signer{jws: %{ "alg" => alg },
            jwk: %{ "kty" => "oct", "k" => :base64url.encode(secret) }}
  end

  @doc "Convenience for generating an ES*** Joken.Signer"
  @spec es(binary, map) :: Signer.t
  def es(alg, key) when is_map(key)
    and alg in ["ES256", "ES384", "ES512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc "Convenience for generating an RS*** Joken.Signer"
  @spec rs(binary, map) :: Signer.t
  def rs(alg, key) when is_map(key)
    and alg in ["RS256", "RS384", "RS512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc "Convenience for generating an PS*** Joken.Signer"
  @spec ps(binary, map) :: Signer.t
  def ps(alg, key) when is_map(key)
    and alg in ["PS256", "PS384", "PS512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc """
  Signs a payload (JOSE header + claims) with the configured signer.

  It raises ArgumentError if no signer was configured.
  """
  @spec sign(Token.t) :: Token.t
  def sign(%Token{signer: nil}) do
    raise ArgumentError, message: "Missing Signer"
  end
  def sign(token = %Token{signer: signer = %Signer{}}) do
    sign(token, signer)
  end

  @doc """
  Signs a payload (JOSE header + claims) with the given signer.

  This will override the configured signer.
  """
  @spec sign(Token.t, Signer.t) :: Token.t
  def sign(token, %Signer{ jws: nil, jwk: %{ "kty" => "oct" } = jwk }) do
    jws = %{ "alg" => "HS256" }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, %Signer{ jws: nil, jwk: jwk }) when is_binary(jwk) do
    jws = %{ "alg" => "HS256" }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, %Signer{ jws: jws, jwk: secret }) when is_binary(secret) do
    jwk = %{ "kty" => "oct", "k" => :base64url.encode(:erlang.iolist_to_binary(secret)) }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, signer) do
    token = %{ token | signer: signer }
    Logger.debug fn -> "Signing #{inspect token.claims} with #{inspect signer}" end

    claims = prepare_claims(token.claims)

    {_, compacted_token} = JOSE.JWS.compact(JOSE.JWT.sign(signer.jwk, signer.jws, claims))
    %{ token | token: compacted_token }
  end

  @doc """
  Verifies a token signature and decodes its payload. This assumes a signer was configured.
  It raises if there was none.
  """
  @spec verify(Token.t) :: Token.t
  def verify(%Token{signer: nil}) do
    raise ArgumentError, message: "Missing Signer"
  end
  def verify(token = %Token{signer: signer = %Signer{}}) do
    verify(token, signer)
  end

  @doc """
  Verifies a token signature and decodes its payload.
  It uses the given signer and sets it on the token.
  If a module is given as the third argument, the claims
  will be converted into a struct using the module
  """
  @spec verify(Token.t, Signer.t, module) :: Token.t
  def verify(t, signer, struct \\ nil) do
    do_verify(t, signer, struct)
  end

  ### PRIVATE
  defp do_verify(t = %Token{token: nil}, _signer, _struct) do
    %{ t | error: "No compact token set for verification"}
  end
  defp do_verify(t = %Token{token: token}, s = %Signer{jwk: jwk, jws: %{ "alg" => algorithm}}, struct_name) do

    Logger.debug fn -> "Verifying #{token} using #{inspect s}" end

    t = %{ t | signer: s }

    try do
      case JOSE.JWK.verify(token, jwk) do
        {true, payload, jws} ->
          jws = :erlang.element(2, JOSE.JWS.to_map(jws))
          algorithm_string = algorithm |> to_string
          case jws["alg"] do
            ^algorithm_string ->
              map_payload = decode_payload(t, payload)
              validate_all_claims(t, map_payload, struct_name)
            _ ->
              %{ t | error: "Invalid signature algorithm" }
          end
        _ ->
          %{ t | error: "Invalid signature" }
      end
    catch
      :error, _ ->
        %{ t | error: "Could not verify token" }
    end
  end

  defp decode_payload(%Token{json_module: json}, payload) when is_binary(payload) do
    json.decode! payload
  end

  defp validate_all_claims(t = %Token{validations: validations}, map_payload, struct_name)
    when is_map(map_payload) do

    try do
      claims = Enum.reduce map_payload, [], fn({key, value}, acc) ->
        case Map.has_key? validations, key do
          false ->
            [{key, value} | acc]
          true ->
            case validations[key].(value) do
              true ->
                [{key, value} | acc]
              false ->
                raise ArgumentError
            end
        end
      end

      claims = Enum.into(claims, %{})
      if struct_name do
        claims = struct(struct_name, Enum.map(claims, fn({key, value}) ->
          { String.to_existing_atom(key), value }
        end))
      end

      %{ t | claims: claims }
    catch
      _,_ ->
        %{ t | error: "Invalid payload" }
    end
  end

  defp prepare_claims(%{__struct__: _} = claims) do
    Map.from_struct(claims)
  end
  defp prepare_claims(claims) when is_map(claims) do
    claims
  end

end

defmodule Joken.Signer do
  alias Joken.Token
  alias Joken.Signer

  @moduledoc """
  Signer is the JWK (JSON Web Key) and JWS (JSON Web Signature) configuration of Joken.

  JWK is used by JWS to generate a token _signature_ that is appended to the end of the
  token compact representation.

  Joken uses https://hex.pm/packages/jose to do signing and verification.
  """

  @type jwk :: %{}
  @type jws :: %{}

  @type t :: %__MODULE__{
    jwk: jwk,
    jws: jws
  }

  defstruct [:jwk, :jws]

  @doc "Convenience for generating an HS*** Joken.Signer"
  @spec hs(binary, binary) :: Signer.t
  def hs(alg, secret) when is_binary(secret)
    and alg in ["HS256", "HS384", "HS512"] do
    %Signer{jws: %{ "alg" => alg },
            jwk: %{ "kty" => "oct", "k" => :base64url.encode(secret) }}
  end

  @doc "Convenience for generating an ES*** Joken.Signer"
  @spec es(binary, map) :: Signer.t
  def es(alg, key) when is_map(key)
    and alg in ["ES256", "ES384", "ES512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc "Convenience for generating an RS*** Joken.Signer"
  @spec rs(binary, map) :: Signer.t
  def rs(alg, key) when is_map(key)
    and alg in ["RS256", "RS384", "RS512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc "Convenience for generating an PS*** Joken.Signer"
  @spec ps(binary, map) :: Signer.t
  def ps(alg, key) when is_map(key)
    and alg in ["PS256", "PS384", "PS512"] do
    %Signer{jws: %{ "alg" => alg }, jwk: key }
  end

  @doc """
  Signs a payload (JOSE header + claims) with the configured signer.

  It raises ArgumentError if no signer was configured.
  """
  @spec sign(Token.t) :: Token.t
  def sign(%Token{signer: nil}) do
    raise ArgumentError, message: "Missing Signer"
  end
  def sign(token = %Token{signer: signer = %Signer{}}) do
    sign(token, signer)
  end

  @doc """
  Signs a payload (JOSE header + claims) with the given signer.

  This will override the configured signer.
  """
  @spec sign(Token.t, Signer.t) :: Token.t
  def sign(token, %Signer{ jws: nil, jwk: %{ "kty" => "oct" } = jwk }) do
    jws = %{ "alg" => "HS256" }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, %Signer{ jws: nil, jwk: jwk }) when is_binary(jwk) do
    jws = %{ "alg" => "HS256" }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, %Signer{ jws: jws, jwk: secret }) when is_binary(secret) do
    jwk = %{ "kty" => "oct", "k" => :base64url.encode(:erlang.iolist_to_binary(secret)) }
    sign(token, %Signer{ jwk: jwk, jws: jws})
  end
  def sign(token, signer) do
    token = %{ token | signer: signer }
    claims = prepare_claims(token.claims)

    {_, compacted_token} = JOSE.JWS.compact(JOSE.JWT.sign(signer.jwk, signer.jws, claims))
    %{ token | token: compacted_token }
  end

  @doc """
  Verifies a token signature and decodes its payload. This assumes a signer was configured.
  It raises if there was none.
  """
  @spec verify(Token.t) :: Token.t
  def verify(%Token{signer: nil}) do
    raise ArgumentError, message: "Missing Signer"
  end
  def verify(token = %Token{signer: signer = %Signer{}}) do
    verify(token, signer)
  end

  @doc """
  Verifies a token signature and decodes its payload.
  It uses the given signer and sets it on the token.
  If a module is given as the third argument, the claims
  will be converted into a struct using the module
  """
  @spec verify(Token.t, Signer.t, module) :: Token.t
  def verify(t, signer, struct \\ nil) do
    do_verify(t, signer, struct)
  end

  ### PRIVATE
  defp do_verify(t = %Token{token: nil}, _signer, _struct) do
    %{ t | error: "No compact token set for verification"}
  end
  defp do_verify(t = %Token{token: token}, s = %Signer{jwk: jwk, jws: %{ "alg" => algorithm}}, struct_name) do

    t = %{ t | signer: s }

    try do
      case JOSE.JWK.verify(token, jwk) do
        {true, payload, jws} ->
          jws = :erlang.element(2, JOSE.JWS.to_map(jws))
          algorithm_string = algorithm |> to_string
          case jws["alg"] do
            ^algorithm_string ->
              map_payload = decode_payload(t, payload)
              validate_all_claims(t, map_payload, struct_name)
            _ ->
              %{ t | error: "Invalid signature algorithm" }
          end
        _ ->
          %{ t | error: "Invalid signature" }
      end
    catch
      :error, _ ->
        %{ t | error: "Could not verify token" }
    end
  end

  defp decode_payload(%Token{json_module: json}, payload) when is_binary(payload) do
    json.decode! payload
  end

  defp validate_all_claims(t = %Token{validations: validations}, map_payload, struct_name)
    when is_map(map_payload) do

    try do
      claims = Enum.reduce map_payload, [], fn({key, value}, acc) ->
        case Map.has_key? validations, key do
          false ->
            [{key, value} | acc]
          true ->
            case validations[key].(value) do
              true ->
                [{key, value} | acc]
              false ->
                raise ArgumentError
            end
        end
      end

      claims = Enum.into(claims, %{})
      if struct_name do
        claims = struct(struct_name, Enum.map(claims, fn({key, value}) ->
          { String.to_existing_atom(key), value }
        end))
      end

      %{ t | claims: claims }
    catch
      _,_ ->
        %{ t | error: "Invalid payload" }
    end
  end

  defp prepare_claims(%{__struct__: _} = claims) do
    Map.from_struct(claims)
  end
  defp prepare_claims(claims) when is_map(claims) do
    claims
  end

end


chore: handle all errors in cases ael response (#5051)


assignment

assignment

error: pathspec 'elixir/1.exs' did not match any file(s) known to git


add a mix task to build the site

add a mix task to build the site

defmodule Mix.Tasks.Hyde.Build do
  use Mix.Task

  @shortdoc "Rebuild the whole site"
  def run(_), do: Hyde.build
end

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


Add oauther as a dep

Add oauther as a dep

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/'


Verify user auth on /login and properly redirect

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/jaysoifer/jonathansoifer.com.git/'


Adding test for GoogleAuthHandler

Adding test for GoogleAuthHandler

defmodule EspiDni.GoogleAuthHandlerTest do
  use EspiDni.ModelCase

  alias EspiDni.GoogleAuthHandler

  @valid_params %{credentials: %{token: "atoken", refresh_token: "refresh_token"}}

  setup do
    team = insert_team
    {:ok, team: team}
  end

  test "update_from_auth with valid params", %{team: team} do
    case GoogleAuthHandler.update_from_auth(team, @valid_params) do
      {:ok, team} ->
        assert team.google_token == "atoken"
        assert team.google_refresh_token == "refresh_token"
    end
  end
end

error: pathspec 'test/models/google_auth_handler_test.exs' did not match any file(s) known to git


[Elixir Quiz] Identicons implementation.

defmodule Identicon do
  def make(username) do
    username
    |> encode
    |> draw
  end

  defp encode(username) do
    :crypto.hash(:md5, username)
    |> color
    |> alpha
  end

  defp color(md5), do: _color(md5 |> :binary.bin_to_list)
  defp _color([ r, g, b | tail ]), do: { { r, g, b }, tail ++ tail }

  defp alpha({ { r, g, b }, [ a | tail ] }), do: { { r, g, b, a }, tail }

  defp draw({ _, [] }), do: nil
  defp draw({ _, [ a, b, c, d, e | _tail ]}) do
    Enum.flat_map([ a, b, c, d, e ], &([ &1, &1, &1, &1, &1 ]))
    |> Enum.map(&choose_color/1)
    |> render
  end

  defp choose_color(color) when color < 86, do: :red_background
  defp choose_color(color) when color < 172, do: :green_background
  defp choose_color(_), do: :blue_background

  defp render(array) do
    Enum.flat_map(array, &([ &1, " " ]))
    |> IO.ANSI.format(true)
    |> draw_lines
  end

  defp draw_lines(string) do
    IO.puts string
    IO.puts string
    IO.puts string
  end
end

Identicon.make("afronski")

error: pathspec 'elixir/elixir-quiz/identicons/identicon.exs' did not match any file(s) known to git


Code solution in elixir, of the Ominous Omino problem

defmodule Omino do

    def divide(x, grid) do
        rem(grid, x) === 0
    end

    def getWinner(x, r, c) do

        s = min(r, c)
        l = max(r, c)

        cond do
            not divide(x, s*l) -> "RICHARD"
            x === 3 and s === 1 -> "RICHARD"
            x === 4 and s <= 2 -> "RICHARD"
            x === 5 and ( (s <= 2) || (s === 3 and l === 5) ) -> "RICHARD"
            x === 6 and s <= 3 -> "RICHARD"
            x >= 7 -> "RICHARD"
            true -> "GABRIEL"
        end

    end

    def getWinnerLoop(times, total) when times<=1 do
        input = IO.gets ""

        xOmino = hd Tuple.to_list((Integer.parse hd String.split(input, " ")))
        rGrid = hd Tuple.to_list(( Integer.parse hd(tl(String.split(input, " "))) ))
        cGrid = hd Tuple.to_list( Integer.parse(hd tl tl String.split(input)) )

        IO.puts "Case ##{(total - times) + 1}: #{getWinner(xOmino, rGrid, cGrid)}"
    end

    def getWinnerLoop(times, total) do
        input = IO.gets ""

        xOmino = hd Tuple.to_list((Integer.parse hd String.split(input, " ")))
        rGrid = hd Tuple.to_list(( Integer.parse hd(tl(String.split(input, " "))) ))
        cGrid = hd Tuple.to_list(Integer.parse(hd tl tl String.split(input, " ")))

        IO.puts "Case ##{(total - times) + 1}: #{getWinner(xOmino, rGrid, cGrid)}"
        getWinnerLoop(times - 1, total)
    end

end

times = IO.gets ""
timesConverted = elem(Integer.parse(times), 0)

Omino.getWinnerLoop(timesConverted, timesConverted)

error: pathspec 'solutions/ominus-omino/Omino.exs' did not match any file(s) known to git


Modify session structure

Modify session structure


Add schema validator

Add schema validator

defmodule Xema.SchemaError do
  defexception [:message]
end

defmodule Xema.Schema.Validator do
  @moduledoc false

  import Xema.Validator, only: [is_unique?: 1]

  @type result :: :ok | {:error, String.t()}

  @default_keys MapSet.new([
                  :all_of,
                  :any_of,
                  :as,
                  :description,
                  :enum,
                  :id,
                  :not,
                  :one_of,
                  :schema,
                  :title,
                  :type
                ])

  @list_keys @default_keys
             |> MapSet.union(
               MapSet.new([
                 :items,
                 :max_items,
                 :min_items,
                 :additional_items,
                 :unique_items
               ])
             )

  @number_keys @default_keys
               |> MapSet.union(
                 MapSet.new([
                   :minimum,
                   :maximum,
                   :exclusive_maximum,
                   :exclusive_minimum,
                   :multiple_of
                 ])
               )

  @string_keys @default_keys
               |> MapSet.union(
                 MapSet.new([
                   :max_length,
                   :min_length,
                   :pattern
                 ])
               )

  @map_keys @default_keys
            |> MapSet.union(
              MapSet.new([
                :additional_properties,
                :dependencies,
                :keys,
                :max_properties,
                :min_properties,
                :pattern_properties,
                :properties,
                :required
              ])
            )

  @any_keys @number_keys
            |> MapSet.union(@string_keys)
            |> MapSet.union(@list_keys)
            |> MapSet.union(@map_keys)

  @keys [
    any: @any_keys,
    boolean: @default_keys,
    float: @number_keys,
    integer: @number_keys,
    list: @list_keys,
    map: @map_keys,
    number: @number_keys,
    string: @string_keys
  ]

  @spec validate(atom, keyword) :: :ok | {:error, String.t()}
  def validate(_, []), do: :ok

  def validate(:any, opts) do
    with :ok <- validate_keywords(:any, opts),
         :ok <- enum(:any, opts[:enum]),
         :ok <- schemas(opts, :all_of),
         :ok <- schemas(opts, :any_of),
         do: :ok
  end

  def validate(:boolean, opts) do
    with :ok <- validate_keywords(:boolean, opts) do
      :ok
    end
  end

  def validate(:list, opts) do
    with :ok <- validate_keywords(:list, opts),
         :ok <- items(opts[:items]),
         :ok <- non_negative_integer(:max_items, opts[:max_items]),
         :ok <- non_negative_integer(:min_items, opts[:min_items]),
         :ok <- additional_items(opts[:additional_items], opts[:items]) do
      :ok
    end
  end

  def validate(:map, opts) do
    with :ok <- validate_keywords(:map, opts),
         :ok <-
           additional_properties(
             opts[:additional_properties],
             opts[:properties],
             opts[:pattern_properties]
           ),
         :ok <- dependencies(opts[:dependencies]),
         :ok <- non_negative_integer(:max_properties, opts[:max_properties]),
         :ok <- non_negative_integer(:min_properties, opts[:min_properties]),
         :ok <- properties(opts[:properties]),
         :ok <- pattern_properties(opts[:pattern_properties]) do
      :ok
    end
  end

  def validate(type, opts)
      when type == :number or type == :integer or type == :float do
    with :ok <-
           ex_min_max(
             type,
             :exclusive_maximum,
             opts[:exclusive_maximum],
             opts[:maximum]
           ),
         :ok <-
           ex_min_max(
             type,
             :exclusive_minimum,
             opts[:exclusive_minimum],
             opts[:minimum]
           ),
         :ok <- min_max(type, :maximum, opts[:maximum]),
         :ok <- min_max(type, :minimum, opts[:minimum]),
         :ok <- multiple_of(type, opts[:multiple_of]),
         :ok <- validate_keywords(type, opts),
         :ok <- enum(type, opts[:enum]) do
      :ok
    end
  end

  def validate(:string, opts) do
    with :ok <- validate_keywords(:string, opts),
         :ok <- enum(:string, opts[:enum]),
         :ok <- non_negative_integer(:max_length, opts[:max_length]),
         :ok <- non_negative_integer(:min_length, opts[:min_length]),
         :ok <- regex(:pattern, opts[:pattern]) do
      :ok
    end
  end

  # Check for unsupported keywords.

  defp validate_keywords(type, opts) do
    case difference(type, opts) do
      [] ->
        :ok

      keywords ->
        {
          :error,
          "Keywords #{inspect(keywords)} are not supported by #{inspect(type)}."
        }
    end
  end

  defp difference(type, opts),
    do:
      opts
      |> Keyword.keys()
      |> MapSet.new()
      |> MapSet.difference(@keys[type])
      |> MapSet.to_list()

  # Check for a list

  defp schemas(opts, key) do
    case opts[key] do
      nil -> :ok
      items when is_list(items) -> :ok
      _ -> {:error, "#{key} has to be a list."}
    end
  end

  # Keyword: additional_items
  # The value of `additional_items` must be either a boolean or a schema.

  defp additional_items(nil, _), do: :ok

  defp additional_items(_, nil), do: {:error, "additional_items has no effect if items not set."}

  defp additional_items(_, items)
       when not is_list(items),
       do: {:error, "additional_items has no effect if items is not a list."}

  defp additional_items(_, _), do: :ok

  # Keyword: additional_properties
  # The value of `additional_properties` must be a boolean or a schema.

  defp additional_properties(nil, _, _), do: :ok

  defp additional_properties(_, nil, nil),
    do: {:error, "additional_properties has no effect if properties not set."}

  defp additional_properties(_, properties, nil)
       when not is_map(properties),
       do:
         {
           :error,
           "additional_properties has no effect if properties is not a map."
         }

  defp additional_properties(_, _, _), do: :ok

  # Keyword: dependencies
  # This keyword's value must be a map. Each property specifies a dependency.
  # Each dependency value must be an array or a valid schema.

  defp dependencies(nil), do: :ok

  defp dependencies(value) when is_map(value), do: :ok

  defp dependencies(_), do: {:error, "dependencies must be a map."}

  # Keyword: enum
  # The value of this keyword must be an array. This array should have at least
  # one element. Elements in the array should be unique.

  defp enum(_, nil), do: :ok

  defp enum(_, []), do: {:error, "enum can not be an empty list."}

  defp enum(type, value) when is_list(value) do
    case is_unique?(value) do
      false -> {:error, "enum must be unique."}
      true -> do_enum(type, value)
    end
  end

  defp enum(_, _), do: {:error, "enum must be a list."}

  defp do_enum(:any, _), do: :ok

  defp do_enum(:number, value) do
    case Enum.all?(value, fn item -> is_number(item) end) do
      true -> :ok
      false -> {:error, "Entries of enum have to be Integers or Floats."}
    end
  end

  defp do_enum(:integer, value) do
    case Enum.all?(value, fn item -> is_integer(item) end) do
      true -> :ok
      false -> {:error, "Entries of enum have to be Integers."}
    end
  end

  defp do_enum(:float, value) do
    case Enum.all?(value, fn item -> is_float(item) end) do
      true -> :ok
      false -> {:error, "Entries of enum have to be Floats."}
    end
  end

  defp do_enum(:string, value) do
    case Enum.all?(value, fn item -> is_binary(item) end) do
      true -> :ok
      false -> {:error, "Entries of enum have to be Strings."}
    end
  end

  # Keyword: exclusive_maximum
  # Draft-06: The value of `exclusive_maximum` must be number, representing an
  # exclusive upper limit for a numeric instance.
  # Draft-04: if `exclusive_maximum` has boolean value true, the instance is
  # valid if it is strictly lower than the value of `maximum`.
  #
  # Keyword: exclusive_minimum
  # Draft-06: The value of `exclusive_minimum` must be number, representing an
  # exclusive lower limit for a numeric instance.
  # Draft-04: if `exclusive_minimum` has boolean value true, the instance is
  # valid if it is strictly higher than the value of `maximum`.

  defp ex_min_max(_, _, nil, _), do: :ok

  defp ex_min_max(:integer, _, value, nil) when is_integer(value), do: :ok

  defp ex_min_max(:float, _, value, nil) when is_number(value), do: :ok

  defp ex_min_max(:number, _, value, nil) when is_number(value), do: :ok

  defp ex_min_max(_, _, value, maximum)
       when is_boolean(value) and is_number(maximum),
       do: :ok

  defp ex_min_max(_, :exclusive_maximum, value, _maximum)
       when is_boolean(value),
       do: {:error, "No maximum value found for exclusive_maximum."}

  defp ex_min_max(_, :exclusive_minimum, value, _maximum)
       when is_boolean(value),
       do: {:error, "No minimum value found for exclusive_minimum."}

  defp ex_min_max(:integer, keyword, value, nil),
    do: {:error, "Expected a integer for #{keyword}, got #{inspect(value)}"}

  defp ex_min_max(_, keyword, value, nil),
    do: {:error, "Expected a number for #{keyword}, got #{inspect(value)}"}

  defp ex_min_max(_, :exclusive_maximum, value, _maximum)
       when is_number(value),
       do: {:error, "The exclusive_maximum overwrites maximum."}

  defp ex_min_max(_, :exclusive_minimum, value, _maximum)
       when is_number(value),
       do: {:error, "The exclusive_minimum overwrites minimum."}

  defp ex_min_max(_, keyword, value, _maximum),
    do: {:error, "Expected a boolean for #{keyword}, got #{inspect(value)}"}

  # Keyword: items
  # The value of `items` MUST be either a valid JSON Schema or an array of
  # valid JSON Schemas.

  defp items(nil), do: :ok

  defp items(value)
       when is_list(value) or is_tuple(value) or is_atom(value) or is_map(value),
       do: :ok

  defp items(value),
    do: {:error, "Expected a schema or a list of schemas, got #{inspect(value)}."}

  # Keyword: maximum
  # The value of `maximum` must be a number, representing an inclusive upper
  # limit for a numeric instance.
  #
  # Keyword: minimum
  # The value of `minimum` must be a number, representing an inclusive upper
  # limit for a numeric instance.

  defp min_max(_, _, nil), do: :ok

  defp min_max(:number, _, value) when is_number(value), do: :ok

  defp min_max(:integer, _, value) when is_integer(value), do: :ok

  defp min_max(:float, _, value) when is_number(value), do: :ok

  defp min_max(:integer, keyword, value),
    do: {:error, "Expected an Integer for #{keyword}, got #{inspect(value)}."}

  defp min_max(_, keyword, value),
    do: {:error, "Expected a number for #{keyword}, got #{inspect(value)}."}

  # Keyword: multiple_of
  # The value of `multipleOf` must be a number, strictly greater than 0.

  defp multiple_of(_, nil), do: :ok

  defp multiple_of(:integer, value)
       when is_integer(value),
       do: do_multiple_of(value)

  defp multiple_of(:float, value)
       when is_number(value),
       do: do_multiple_of(value)

  defp multiple_of(:number, value)
       when is_number(value),
       do: do_multiple_of(value)

  defp multiple_of(:integer, value),
    do:
      {
        :error,
        "Expected an Integer for multiple_of, got #{inspect(value)}."
      }

  defp multiple_of(_, value),
    do:
      {
        :error,
        "Expected a number for multiple_of, got #{inspect(value)}."
      }

  @spec non_negative_integer(atom, any) :: result
  defp non_negative_integer(_, nil), do: :ok

  defp non_negative_integer(_, value)
       when is_integer(value) and value >= 0,
       do: :ok

  defp non_negative_integer(keyword, value),
    do:
      {
        :error,
        "Expected a non negative integer for #{keyword}, got #{inspect(value)}."
      }

  @spec properties(any) :: result
  defp properties(nil), do: :ok

  defp properties(value) when is_map(value) do
    value
    |> Map.keys()
    |> Enum.reduce_while(:ok, &property_key/2)
  end

  defp properties(value), do: {:error, "Expected a map for properties, got #{inspect(value)}."}

  @spec property_key(any, any) :: {:cont, :ok} | {:error, String.t()}
  defp property_key(key, _)
       when is_binary(key) or is_atom(key),
       do: {:cont, :ok}

  defp property_key(key, _),
    do:
      {
        :halt,
        {
          :error,
          "Expected a string or atom for key in properties, got #{inspect(key)}."
        }
      }

  @spec pattern_properties(any) :: result
  defp pattern_properties(nil), do: :ok

  defp pattern_properties(value) when is_map(value) do
    case value |> Map.keys() |> Enum.drop_while(&is_regex/1) do
      [] ->
        :ok

      [key | _] ->
        {
          :error,
          "Expected a regular expression for key in pattern_properties, got #{inspect(key)}."
        }
    end
  end

  defp pattern_properties(value),
    do: {:error, "Expected a map for pattern_properties, got #{inspect(value)}."}

  @spec regex(atom, any) :: result
  defp regex(_, nil), do: :ok

  defp regex(keyword, value) do
    case is_regex(value) do
      true ->
        :ok

      false ->
        {
          :error,
          "Expected a regular expression for #{keyword}, got #{inspect(value)}."
        }
    end
  end

  @spec is_regex(any) :: boolean
  defp is_regex(value) when is_map(value),
    do: Map.has_key?(value, :__struct__) && value.__struct__ == Regex

  defp is_regex(_), do: false

  @compile {:inline, do_multiple_of: 1}
  defp do_multiple_of(value) do
    case value > 0 do
      true -> :ok
      false -> {:error, "multiple_of must be strictly greater than 0."}
    end
  end
end

error: pathspec 'lib/xema/schema/validator.ex' did not match any file(s) known to git


[WIP] Implement Serum.HeaderParser

[WIP] Implement Serum.HeaderParser

TODO: include filename to error objects
TODO: implement `skip_header` function

defmodule Serum.HeaderParser do
  alias Serum.Error

  @type options :: [{atom, value_type}]
  @type value_type :: :string | :integer | {:list, value_type}
  @type value :: binary | integer | [binary] | [integer]

  @spec parse_header(IO.device, options, [atom]) :: Error.result(map)

  def parse_header(device, options, required \\ []) do
    case extract_header device, [], false do
      {:ok, lines} ->
        key_strings = options |> Keyword.keys |> Enum.map(&Atom.to_string/1)
        kv_list =
          lines
          |> Enum.map(&split_kv/1)
          |> Enum.filter(fn {k, _} -> k in key_strings end)
        with [] <- find_missing(kv_list, required),
             {:ok, new_kv} <- transform_values(kv_list, options, []) do
          {:ok, Map.new(new_kv)}
        else
          error -> handle_error error
        end
      {:error, error} ->
        {:error, :invalid_header,
         {"header parse error: #{error}", "nofile", 0}} # TODO: accept filename
    end
  end

  defp handle_error([missing]) do # TODO: accept filename
    {:error, :invalid_header,
     {"`#{missing}` field is required, but not specified.",
      "nofile", 0}}
  end

  defp handle_error([_|_] = missing) do # TODO: accept filename
    repr = missing |> Enum.map(&"`#{&1}`") |> Enum.join(", ")
    {:error, :invalid_header,
     {"#{repr} fields are required, but not specified.",
      "nofile", 0}}
  end

  defp handle_error({:error, error}) do # TODO: accept filename
    {:error, :invalid_header,
     {"header parse error: #{error}", "nofile", 0}}
  end

  @spec extract_header(IO.device, [binary], boolean)
    :: {:ok, [binary]} | {:error, binary}

  defp extract_header(device, lines, open?)

  defp extract_header(device, lines, false) do
    case IO.read device, :line do
      "===\n" ->
        extract_header device, lines, true
      line when is_binary(line) ->
        extract_header device, lines, false
      :eof ->
        {:error, "header not found"}
    end
  end

  defp extract_header(device, lines, true) do
    case IO.read device, :line do
      "===\n" ->
        {:ok, lines}
      line when is_binary(line) ->
        extract_header device, [line|lines], true
      :eof ->
        {:error, "unexpected end of file"}
    end
  end

  @spec split_kv(binary) :: {binary, binary}

  defp split_kv(line) do
    line
    |> String.split(":", parts: 2)
    |> Enum.map(&String.trim/1)
    |> List.to_tuple
  end

  @spec find_missing([{binary, binary}], [atom]) :: [atom]

  defp find_missing(kvlist, required) do
    keys = Enum.map kvlist, fn {k, _} -> k end
    do_find_missing keys, required
  end

  @spec do_find_missing([binary], [atom], [atom]) :: [atom]

  defp do_find_missing(keys, required, acc \\ [])

  defp do_find_missing(_keys, [], acc) do
    acc
  end

  defp do_find_missing(keys, [h|t], acc) do
    if Atom.to_string(h) in keys do
      do_find_missing keys, t, acc
    else
      do_find_missing keys, t, [h|acc]
    end
  end

  @spec transform_values([{binary, binary}], keyword(atom), keyword(value))
    :: {:error, binary} | {:ok, keyword(value)}

  defp transform_values([], _options, acc) do
    {:ok, acc}
  end

  defp transform_values([{k, v}|rest], options, acc) do
    atom_k = String.to_existing_atom k
    case transform_value v, options[atom_k] do
      {:error, _} = error -> error
      value -> transform_values rest, options, [{atom_k, value}|acc]
    end
  end

  @spec transform_value(binary, value_type) :: value | {:error, binary}

  defp transform_value(valstr, :string) do
    valstr
  end

  defp transform_value(valstr, :integer) do
    case Integer.parse valstr do
      {value, ""} -> value
      _ -> {:error, "invalid integer"}
    end
  end

  defp transform_value(_valstr, {:list, {:list, _type}}) do
    {:error, "\"list of lists\" type is not supported"}
  end

  defp transform_value(valstr, {:list, type}) when is_atom(type) do
    list =
      valstr
      |> String.split(",")
      |> Stream.map(&String.trim/1)
      |> Stream.reject(& &1 == "")
      |> Stream.map(&transform_value &1, type)
    case Enum.filter list, &error?/1 do
      [] -> Enum.to_list list
      [{:error, _} = error|_] -> error
    end
  end

  defp transform_value(_valstr, _type) do
    {:error, "invalid value type"}
  end

  @spec error?(term) :: boolean

  defp error?({:error, _}), do: true
  defp error?(_), do: false
end

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


Skip one snapshot request if you get a device busy error

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/'