Added a test for comment view

Added a test for comment view

defmodule CodeCorps.CommentViewTest do
  use CodeCorps.ConnCase, async: true

  alias CodeCorps.Repo

  # Bring render/3 and render_to_string/3 for testing custom views
  import Phoenix.View

  test "renders all attributes and relationships properly" do
    task = insert(:task)
    user = insert(:user)
    comment = insert(:comment, user: user, task: task)

    comment = CodeCorps.Comment
    |> Repo.get(comment.id)
    |> Repo.preload([:task, :user])

    rendered_json = render(CodeCorps.CommentView, "show.json-api", data: comment)

    expected_json = %{
      data: %{
        id: comment.id |> Integer.to_string,
        type: "comment",
        attributes: %{
          "body" => comment.body,
          "inserted-at" => comment.inserted_at,
          "markdown" => comment.markdown,
          "updated-at" => comment.updated_at
        },
        relationships: %{
          "task" => %{
            data: %{
              id: comment.task_id |> Integer.to_string,
              type: "task"
            }
          },
          "user" => %{
            data: %{
              id: comment.user_id |> Integer.to_string,
              type: "user"
            }
          }
        }
      },
      jsonapi: %{
        version: "1.0"
      }
    }

    assert expected_json == rendered_json
  end
end

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


add test to ensure failing scenarios are returned

defmodule WhiteBread.FeatureRunnerTest do
  use ExUnit.Case
  alias WhiteBread.Gherkin.Elements.Steps, as: Steps
  alias WhiteBread.Gherkin.Elements.Feature, as: Feature
  alias WhiteBread.Gherkin.Elements.Scenario, as: Scenario
  alias WhiteBread.FeatureRunnerTest.ExampleContext, as: ExampleContext

  test "feature runner should return succesful scenarios" do
    steps = [
      %Steps.When{text: "step one"},
      %Steps.When{text: "step two"}
    ]

    scenario = %Scenario{name: "test scenario", steps: steps}
    feature = %Feature{name: "test feature", scenarios: [scenario]}

    output = WhiteBread.Outputers.Console.start
    result = WhiteBread.FeatureRunner.run(ExampleContext, feature, output)
    output |> WhiteBread.Outputers.Console.stop

    assert result == %{failures: [], successes: [{scenario, {:ok, "test scenario"}}]}
  end

  test "feature runner should return succesful and failed scenarios" do
    steps = [
      %Steps.When{text: "step one"},
      %Steps.When{text: "step two"}
    ]

    failing_step = %Steps.When{text: "step two"}
    failing_steps = [
      %Steps.When{text: "step that blocks step two"},
      failing_step
    ]

    scenario = %Scenario{name: "test scenario", steps: steps}
    failing_scenario = %Scenario{name: "failing scenario", steps: failing_steps}
    feature = %Feature{name: "test feature", scenarios: [scenario, failing_scenario]}

    output = WhiteBread.Outputers.Console.start
    result = WhiteBread.FeatureRunner.run(ExampleContext, feature, output)
    output |> WhiteBread.Outputers.Console.stop

    assert result == %{
      failures: [{failing_scenario, {:failed, {:no_clause_match, failing_step}}}],
      successes: [{scenario, {:ok, "test scenario"}}]
    }
  end
end

defmodule WhiteBread.FeatureRunnerTest.ExampleContext do
  use WhiteBread.Context

  when_ "step one", fn _state ->
    {:ok, :step_one_complete}
  end

  when_ "step that blocks step two", fn _state ->
    {:ok, :unexpected_state}
  end

  when_ "step two", fn :step_one_complete ->
    {:ok, :step_two_complete}
  end

  when_ "make a failing assestion", fn _state ->
    assert 1 == 0
    {:ok, :impossible}
  end

end

defmodule WhiteBread.FeatureRunnerTest do
  use ExUnit.Case
  alias WhiteBread.Gherkin.Elements.Steps, as: Steps
  alias WhiteBread.Gherkin.Elements.Feature, as: Feature
  alias WhiteBread.Gherkin.Elements.Scenario, as: Scenario
  alias WhiteBread.FeatureRunnerTest.ExampleContext, as: ExampleContext

  test "feature runner should return succesful scenarios" do
    steps = [
      %Steps.When{text: "step one"},
      %Steps.When{text: "step two"}
    ]

    scenario = %Scenario{name: "test scenario", steps: steps}
    feature = %Feature{name: "test feature", scenarios: [scenario]}

    output = WhiteBread.Outputers.Console.start
    result = WhiteBread.FeatureRunner.run(ExampleContext, feature, output)
    output |> WhiteBread.Outputers.Console.stop

    assert result == %{failures: [], successes: [{scenario, {:ok, "test scenario"}}]}
  end
end

defmodule WhiteBread.FeatureRunnerTest.ExampleContext do
  use WhiteBread.Context

  when_ "step one", fn _state ->
    {:ok, :step_one_complete}
  end

  when_ "step that blocks step two", fn _state ->
    {:ok, :unexpected_state}
  end

  when_ "step two", fn :step_one_complete ->
    {:ok, :step_two_complete}
  end

  when_ "make a failing assestion", fn _state ->
    assert 1 == 0
    {:ok, :impossible}
  end

end


Add test cases for `Project.by_owner`

Add test cases for `Project.by_owner`

defmodule Aelita2.ProjectTest do
  use Aelita2.ModelCase

  alias Aelita2.Installation
  alias Aelita2.Project
  alias Aelita2.User
  alias Aelita2.LinkUserProject

  setup do
    installation = Repo.insert!(%Installation{
      installation_xref: 31,
      })
    {:ok, installation: installation}
  end

  test "grab project by user", %{installation: installation} do
    project = Repo.insert!(%Project{
      installation_id: installation.id,
      repo_xref: 13,
      name: "example/project",
      })
    user = Repo.insert!(%User{
      login: "X",
      })
    Repo.insert!(%LinkUserProject{user: user, project: project})
    [project_x] = Repo.all(Project.by_owner(user.id))
    assert project_x.id == project.id
  end

  test "avoid project by other user", %{installation: installation} do
    project = Repo.insert!(%Project{
      installation_id: installation.id,
      repo_xref: 13,
      name: "example/project",
      })
    user = Repo.insert!(%User{
      login: "X",
      })
    user2 = Repo.insert!(%User{
      login: "Y",
      })
    Repo.insert!(%LinkUserProject{user: user, project: project})
    res = Repo.all(Project.by_owner(user2.id))
    assert res == []
  end

  test "avoid other project", %{installation: installation} do
    project = Repo.insert!(%Project{
      installation_id: installation.id,
      repo_xref: 13,
      name: "example/project",
      })
    _project2 = Repo.insert!(%Project{
      installation_id: installation.id,
      repo_xref: 14,
      name: "example/project2",
      })
    user = Repo.insert!(%User{
      login: "X",
      })
    Repo.insert!(%LinkUserProject{user: user, project: project})
    [project_x] = Repo.all(Project.by_owner(user.id))
    assert project_x.id == project.id
  end
end

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


Remove boilerplate code

Remove boilerplate code

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


Change non-image response to be 502

Change non-image response to be 502

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


swap function

swap function

swap = fn {a, b} -> {b, a} end

IO.inspect swap.({2,3})

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


Testing on on, on off, off on

Testing on on, on off, off on

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/mjpuser/ai.ex.git/'


Add board tests

Add board tests

defmodule Chess.BoardTest do
  @moduledoc false

  use Chess.DataCase

  alias Chess.Board

  test "returns a piece from the board" do
    board = Board.default

    expected_piece = %{"type" => "pawn", "colour" => "white"}
    assert Board.piece(board, {4, 1}) == expected_piece
  end

  test "finds pieces on the board" do
    board = Board.default

    piece = %{"type" => "pawn", "colour" => "white"}
    expected_result = [
      {0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1},
    ]
    assert Board.search(board, piece) == expected_result
  end

  test "finds a single piece on the board" do
    board = Board.default

    piece = %{"type" => "king", "colour" => "black"}
    assert Board.search(board, piece) == [{4, 7}]
  end

  test "moves a piece" do
    board = %{
      "3,0" => %{"type" => "queen",  "colour" => "white"},
    }

    new_board = Board.move_piece(board, %{"from" => [3, 0], "to" => [5, 2]})

    assert new_board == %{
      "5,2" => %{"type" => "queen",  "colour" => "white"},
    }
  end
end

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


Add new multi_tenant_person_test file

Add new multi_tenant_person_test file

defmodule MultiTenantPersonTest do
  use ExUnit.Case
  import Ecto.Query

  setup_all do
    MultiTenantHelper.setup_tenant(Repo)

    %{name: "Acme LLC", website: "http://www.acme.com"}
    |> ChangesetHelper.new_company(:multitenant)
    |> Repo.insert()

    %{name: "Another Company Corp.", is_active: true, address: "Sesame street 100/3, 101010"}
    |> ChangesetHelper.new_company(:multitenant)
    |> Repo.insert()

    :ok
  end

  test "[multi tenant] creating a person with meta tag creates a person version with correct attributes" do
    company = QueryHelper.first_company(:multitenant) |> Repo.one()

    {:ok, result} =
      %{first_name: "Izel", last_name: "Nakri", gender: true, company_id: company.id}
      |> ChangesetHelper.new_person(:multitenant)
      |> PaperTrail.insert(origin: "admin", meta: %{}, prefix: MultiTenantHelper.tenant())

    person_count = QueryHelper.person_count(:multitenant) |> Repo.all()
    version_count = QueryHelper.version_count(:multitenant) |> Repo.all()

    person = result[:model] |> Map.drop([:__meta__, :__struct__, :inserted_at, :updated_at, :id])
    version = result[:version] |> Map.drop([:__meta__, :__struct__, :inserted_at])

    first_person = QueryHelper.first_person(:multitenant) |> Repo.one()

    assert person_count == [1]
    assert version_count == [1]

    assert Map.drop(person, [:company]) == %{
      first_name: "Izel",
      last_name: "Nakri",
      gender: true,
      visit_count: nil,
      birthdate: nil,
      company_id: company.id
    }

    assert Map.drop(version, [:id]) == %{
      event: "insert",
      item_type: "Person",
      item_id: first_person.id,
      item_changes: Map.drop(result[:model], [:__meta__, :__struct__, :company]),
      origin: "admin",
      originator_id: nil,
      meta: %{}
    }
  end

  test "[multi tenant] updating a person creates a person version with correct attributes" do
    first_person = QueryHelper.first_person(:multitenant) |> Repo.one()

    target_company =
      [name: "Another Company Corp.", limit: 1]
      |> QueryHelper.filter_company(:multitenant)
      |> Repo.one()

    {:ok, result} =
      ChangesetHelper.update_person(first_person, %{
        first_name: "Isaac",
        visit_count: 10,
        birthdate: ~D[1992-04-01],
        company_id: target_company.id
      }, :multitenant)
      |> PaperTrail.update([origin: "user:1", meta: %{linkname: "izelnakri"},
        prefix: MultiTenantHelper.tenant()])

    person_count = QueryHelper.person_count(:multitenant) |> Repo.all()
    version_count = QueryHelper.version_count(:multitenant) |> Repo.all()

    person = result[:model] |> Map.drop([:__meta__, :__struct__, :inserted_at, :updated_at, :id])
    version = result[:version] |> Map.drop([:__meta__, :__struct__, :inserted_at])

    assert person_count == [1]
    assert version_count == [2]

    assert Map.drop(person, [:company]) == %{
      company_id: target_company.id,
      first_name: "Isaac",
      visit_count: 10,
      birthdate: elem(Ecto.Date.cast(~D[1992-04-01]), 1), #  this is the only problem
      last_name: "Nakri",
      gender: true
    }

    assert Map.drop(version, [:id]) == %{
      event: "update",
      item_type: "Person",
      item_id: first_person.id,
      item_changes: %{
        first_name: "Isaac",
        visit_count: 10,
        birthdate: elem(Ecto.Date.cast(~D[1992-04-01]), 1),
        company_id: target_company.id
      },
      origin: "user:1",
      originator_id: nil,
      meta: %{
        linkname: "izelnakri"
      }
    }
  end

  test "[multi tenant] deleting a person creates a person version with correct attributes" do
    person = QueryHelper.first_person(:multitenant) |> Repo.one()

    {:ok, result} =
      person
      |> PaperTrail.delete(prefix: MultiTenantHelper.tenant())

    person_count = QueryHelper.person_count(:multitenant) |> Repo.all()
    version_count = QueryHelper.version_count(:multitenant) |> Repo.all()

    version = result[:version] |> Map.drop([:__meta__, :__struct__, :inserted_at])

    assert person_count == [0]
    assert version_count == [3]

    assert Map.drop(version, [:id]) == %{
      event: "delete",
      item_type: "Person",
      item_id: person.id,
      item_changes: %{
        id: person.id,
        inserted_at: person.inserted_at,
        updated_at: person.updated_at,
        first_name: "Isaac",
        last_name: "Nakri",
        gender: true,
        visit_count: 10,
        birthdate: elem(Ecto.Date.cast(~D[1992-04-01]), 1),
        company_id: person.company.id
      },
      origin: nil,
      originator_id: nil,
      meta: nil
    }
  end
end

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


Disable thumbnail symlink generation

Disable thumbnail symlink generation


Added function to update single camera sharees


Used company_id to query from intercom

Used company_id to query from intercom


Added task to sync intercom users and remove deleted users from intercom


default parameter

default parameter

defmodule Example do
  def func(p1, p2 \\2, p3 \\3, p4 ) do
    IO.inspect([p1,p2,p3,p4])
  end
end

Example.func("a","b")
Example.func("a","b","c")
Example.func("a","b","c","d")

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


Add test for slugged route view

Add test for slugged route view

defmodule CodeCorps.SluggedRouteViewTest do
  use CodeCorps.ConnCase, async: true

  import Phoenix.View, only: [render: 3]

  test "renders all attributes and relationships properly for organization" do
    organization = insert(:organization)
    slugged_route = insert(:slugged_route, organization: organization)

    rendered_json = render(CodeCorps.SluggedRouteView, "show.json-api", data: slugged_route)

    expected_json = %{
      "data" => %{
        "id" => slugged_route.id |> Integer.to_string,
        "type" => "slugged-route",
        "attributes" => %{
          "inserted-at" => slugged_route.inserted_at,
          "slug" => slugged_route.slug,
          "updated-at" => slugged_route.updated_at,
        },
        "relationships" => %{
          "organization" => %{
            "data" => %{"id" => slugged_route.organization_id |> Integer.to_string, "type" => "organization"}
          },
          "user" => %{
            "data" => nil
          }
        }
      },
      "jsonapi" => %{
        "version" => "1.0"
      }
    }

    assert rendered_json == expected_json
  end

  test "renders all attributes and relationships properly for user" do
    user = insert(:user)
    slugged_route = insert(:slugged_route, user: user)

    rendered_json = render(CodeCorps.SluggedRouteView, "show.json-api", data: slugged_route)

    expected_json = %{
      "data" => %{
        "id" => slugged_route.id |> Integer.to_string,
        "type" => "slugged-route",
        "attributes" => %{
          "inserted-at" => slugged_route.inserted_at,
          "slug" => slugged_route.slug,
          "updated-at" => slugged_route.updated_at,
        },
        "relationships" => %{
          "organization" => %{
            "data" => nil
          },
          "user" => %{
            "data" => %{"id" => slugged_route.user_id |> Integer.to_string, "type" => "user"}
          }
        }
      },
      "jsonapi" => %{
        "version" => "1.0"
      }
    }

    assert rendered_json == expected_json
  end
end

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


Added script to sync shared camera contacts

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


Added else condition to sync share-requests


Add tests to 'prettify_json' onresponse hook

defmodule HTTParrotTest do
  use ExUnit.Case
  import :meck
  import HTTParrot

  setup do
    new :cowboy_req
  end

  teardown do
    unload :cowboy_req
  end

  test "'prettify_json' prettifies body response if it's a JSON" do
    expect(:cowboy_req, :reply, [{[:status, [{"content-length", '14'}], "{\n  \"a\": \"b\"\n}", :req1], {:ok, :req2}}])
    assert prettify_json(:status, [{"content-length", '12'}], "{\"a\":\"b\"}", :req1) == :req2
    assert validate :cowboy_req
  end

  test "'prettify_json' does nothing if body is not a JSON" do
    expect(:cowboy_req, :reply, [{[:status, :headers, "<xml></xml>", :req1], {:ok, :req2}}])
    assert prettify_json(:status, :headers, "<xml></xml>", :req1) == :req2
    assert validate :cowboy_req
  end
end

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


Remove forced timeout from live/snapshot endpoint


Add a few tests to ProcessAPI

Add a few tests to ProcessAPI

defmodule Helix.Process.Service.API.ProcessTest do

  use Helix.Test.IntegrationCase

  alias HELL.TestHelper.Random
  alias Software.Decryptor.ProcessType, as: Decryptor
  alias Software.Firewall.ProcessType.Passive, as: Firewall
  alias Helix.Process.Service.API.Process, as: API

  defp create_server do
    alias Helix.Account.Service.Flow.Account, as: AccountFlow
    alias Helix.Account.Factory, as: AccountFactory

    account = AccountFactory.insert(:account)
    {:ok, %{server: server}} = AccountFlow.setup_account(account)

    server
  end

  describe "get_running_processes_of_type_on_server/2" do
    test "returns what it should" do
      server = create_server()

      firewall = %{
        gateway_id: server.server_id,
        target_server_id: server.server_id,
        file_id: Random.pk(),
        process_data: %Firewall{version: 1},
        process_type: "firewall_passive"
      }

      decryptor = %{
        gateway_id: server.server_id,
        target_server_id: server.server_id,
        file_id: Random.pk(),
        process_data: %Decryptor{
          storage_id: Random.pk(),
          target_file_id: Random.pk(),
          scope: :global,
          software_version: 1
        },
        process_type: "decryptor"
      }

      {:ok, firewall1} = API.create(firewall)
      {:ok, firewall2} = API.create(firewall)
      {:ok, _} = API.create(decryptor)

      expected = MapSet.new([firewall1, firewall2], &(&1.process_id))

      result =
        server.server_id
        |> API.get_running_processes_of_type_on_server("firewall_passive")
        |> MapSet.new(&(&1.process_id))

      assert MapSet.equal?(expected, result)

      # API.create/1 starts the TOP machine, so we have to wait a bit to
      # gracefully stop the test
      :timer.sleep(100)
    end
  end

  describe "get_processes_on_server/1" do
    test "returns all processes on server" do
      server = create_server()

      firewall = %{
        gateway_id: server.server_id,
        target_server_id: server.server_id,
        file_id: Random.pk(),
        process_data: %Firewall{version: 1},
        process_type: "firewall_passive"
      }

      decryptor = %{
        gateway_id: server.server_id,
        target_server_id: server.server_id,
        file_id: Random.pk(),
        process_data: %Decryptor{
          storage_id: Random.pk(),
          target_file_id: Random.pk(),
          scope: :global,
          software_version: 1
        },
        process_type: "decryptor"
      }

      {:ok, _} = API.create(firewall)
      {:ok, _} = API.create(firewall)
      {:ok, _} = API.create(decryptor)
      {:ok, _} = API.create(decryptor)

      assert 4 == Enum.count(API.get_processes_on_server(server.server_id))

      # API.create/1 starts the TOP machine, so we have to wait a bit to
      # gracefully stop the test
      :timer.sleep(100)
    end
  end

  describe "get_processes_of_type_targeting_server" do
    @tag :pending
    test "returns expected external processes"
  end

  describe "get_processes_targeting_server/1" do
    test "returns processes that are not running on the gateway" do
      server1 = create_server()
      server2 = create_server()

      firewall = %{
        gateway_id: server1.server_id,
        target_server_id: server1.server_id,
        file_id: Random.pk(),
        process_data: %Firewall{version: 1},
        process_type: "firewall_passive"
      }

      decryptor = %{
        gateway_id: server2.server_id,
        target_server_id: server1.server_id,
        file_id: Random.pk(),
        process_data: %Decryptor{
          storage_id: Random.pk(),
          target_file_id: Random.pk(),
          scope: :global,
          software_version: 1
        },
        process_type: "decryptor"
      }

      {:ok, _} = API.create(firewall)
      {:ok, decryptor} = API.create(decryptor)

      assert [process] = API.get_processes_targeting_server(server1.server_id)
      assert decryptor.process_id == process.process_id

      # API.create/1 starts the TOP machine, so we have to wait a bit to
      # gracefully stop the test
      :timer.sleep(100)
    end
  end
end

error: pathspec 'test/process/service/api/process_test.exs' did not match any file(s) known to git


add steemlike block mod

add steemlike block mod

defmodule Glasnost.Steemlike.Block do
  require Logger
  alias Glasnost.Repo

  def update(event, blockchain: blockchain) do
    %{data: data, metadata: %{source: source}} = event
    {schema_mod, client_mod} = case blockchain do
      :steem -> {Glasnost.Steem.Block, Steemex}
      :golos -> {Glasnost.Golos.Block, Golos}
    end
    block = struct(schema_mod, Map.from_struct(event.data))
    Repo.insert!(block, on_conflict: :replace_all, conflict_target: [:height])
  end

  def add_timestamps(comment) do
    comment
    |> Map.put(:updated_at, NaiveDateTime.utc_now)
    |> Map.put(:inserted_at, NaiveDateTime.utc_now)
  end

end

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


Fibonacci implementation with streams.

Fibonacci implementation with streams.

Stream.unfold({0,1}, fn {f1, f2} -> {f1, {f2, f1+f2}} end) |> Enum.take(15)
|> IO.inspect

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


Fix setting the current time in archive creation


remove schema ambiguity

remove schema ambiguity

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


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


Remove dummy validation errors

Remove dummy validation errors

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


Added example for RPC

Added example for RPC

# This example want to show how to call a Remote Procedure.
# Inside the module RPC we have the module Callee where the procedure
# is defined and the module Calleer who will call the procedure.
#
# Run this script from the Spell root with
#
#     mix spell.example.rpc

defmodule RPC do
  @moduledoc """
  The `RPC` module implements the example for asynchronous Remote Procedure Call.
  """

  defmodule Caller do
    @moduledoc """
    This Caller need to be initialised with the `procedure` where the Callee is subscribed
    and the `params` used within the remote procedure

    iex> Caller.start_link("com.spell.rpc.sum", [arguments: [1, 2, 3]])
    """
    require Logger
    defstruct [:caller, :procedure, :params, interval: 1500]

    # Public Functions

    @doc """
    Initialize the caller
    """
    def start_link(procedure, params \\ [], options \\ []) do
      {:ok, spawn_link(fn -> init(procedure, params, options) end)}
    end

    # Private Functions

    defp init(procedure, params, options) do
      {:ok, caller} = RPC.new_peer([Spell.Role.Caller], options)
      %__MODULE__{caller: caller,
                  procedure: procedure,
                  params: params}
        |> struct(options)
        |> loop()
    end

    defp loop(state) do

      # `Spell.cast_call` performs an asyncronous call to the remote procedure, the result will
      # be intercepted and parsed by the block `Spell.receive_result`
      {:ok, call_id} = Spell.cast_call(state.caller, state.procedure, state.params)
      Logger.info("<Caller> send params: #{inspect(state.params)}")

      case Spell.receive_result(state.caller, call_id) do
        {:ok, result} -> IO.inspect handle_result(result)
        {:error, reason} -> {:error, reason}
      end

      :timer.sleep(1000)
      loop(state)
    end

    def handle_result([_result_id, _options, _arguments, %{"result" => res}]) do
      Logger.info("<Caller> received result: #{inspect(res)}")
    end
  end

  defmodule Callee do
    @moduledoc """
    The callee need to be registered to the WAMP Dealer in order to be accesseble and expose
    the procedure. Once it is registered it will wait an invocation from the Dealer and yield
    the result back. in this case it will receive an array of integer `[1, 2, 3]` and return
    the sum of them `6`

    iex> Callee.start_link("com.spell.rpc.sum")
    """
    require Logger
    defstruct [:callee, :procedure, timeout: 1000]

    # Public Functions

    @doc """
    Start the callee with the passed procedure.
    """
    def start_link(topics, options \\ []) do
      {:ok, spawn_link(fn -> init(topics, options) end)}
    end

    # Private Functions

    defp init(procedure, options) do
      {:ok, callee} = RPC.new_peer([Spell.Role.Callee], options)

      # `Spell.cast_register` receive the callee and the procedure where it
      # has to be registered
      {:ok, _register_id} = Spell.cast_register(callee, procedure)

      %__MODULE__{callee: callee,
                  procedure: procedure}
        |> struct(options)
        |> loop()
    end

    defp loop(state) do
      receive do
        :stop -> :ok
        {Spell.Peer, _pid, message} -> handle_message(state, message)
        _ -> {:error, :wrong_message}
      after
        state.timeout -> {:error, :timeout}
      end
      loop(state)
    end

    defp handle_message(_state, %Spell.Message{args: _args, code: _code, type: :registered}) do
      Logger.info("<Callee> registered")
    end

    defp handle_message(state, %Spell.Message{args: [request, _reg_id, _msg, params], code: _code, type: :invocation}) do
      Logger.info("<Callee> received #{inspect(params)} to sum")
      Spell.cast_yield(state.callee, request, [arguments_kw: %{result: sum(params, 0)}])
    end

    def sum([], acc), do: acc
    def sum(int, acc) when is_integer(int), do: sum([int], acc)
    def sum([h | t] = list, acc) when is_list(list) and is_integer(h), do: sum(t, acc + h)
    def sum([h | t] = list, acc) when is_list(list), do: sum(t, acc + String.to_integer(h))
  end

  # Public Interface

  @doc """
  Shared helper function for create a new peer configured with `roles` and
  `options`.
  """
  def new_peer(roles, options) do
    uri   = Keyword.get(options, :uri, Crossbar.uri)
    realm = Keyword.get(options, :realm, Crossbar.get_realm())
    Spell.connect(uri, realm: realm, roles: roles)
  end

end

alias RPC.Callee
alias RPC.Caller
require Logger

Logger.info("Starting the Crossbar.io test server...")

# Start the crossbar testing server
{:ok, _pid} = Crossbar.start()

procedure = "com.spell.rpc.sum"

# Register callee to the WAMP router
{:ok, _callee} = Callee.start_link(procedure)

# Call the remote procedure passing the agruments
{:ok, _caller} = Caller.start_link(procedure, [arguments: [1, 2, 3]])

:timer.sleep(10000)

Logger.info("DONE... Stopping Crossbar.io server")
# Stop the crossbar.io testing server
:ok = Crossbar.stop()

Logger.info("DONE.")

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


Return empty json on delete user (#360)

Return empty json on delete user (#360)


move haru_elixir_api.ex

move haru_elixir_api.ex

defmodule HaruElixirApi do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      # Start the endpoint when the application starts
      supervisor(HaruElixirApi.Endpoint, []),
      # Start the Ecto repository
      worker(HaruElixirApi.Repo, []),
      # Here you could define other workers and supervisors as children
      # worker(HaruElixirApi.Worker, [arg1, arg2, arg3]),
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: HaruElixirApi.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    HaruElixirApi.Endpoint.config_change(changed, removed)
    :ok
  end
end

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


handy duplicate script

handy duplicate script

defmodule Duplicate do
  alias Osdi.{Repo, Event}
  import Ecto.Query

  def by_name(name, new_month, new_day) do
    event = Repo.all(from e in Event, where: e.name == ^name) |> Repo.preload([:creator, :organizer, :modified_by, :tags, :location]) |> List.first()

    start_date = Map.put(event.start_date, :day, new_day)
    end_date = Map.put(event.end_date, :day, new_day)
    start_date = Map.put(start_date, :month, new_month)
    end_date = Map.put(end_date, :month, new_month)

    event = Map.put(event, :start_date, start_date)
    event = Map.put(event, :end_date, end_date)
    event = Map.put(event, :id, nil)
    event = Map.put(event, :attendances, [])

    event = Map.put(event, :name, Event.slug_for(event.title, event.start_date))

    Repo.insert(event)
  end
end

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


Don't use worker on Snapshot test as a worker may not be present

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


Add preliminary HelperScripts module

Add preliminary HelperScripts module

require Logger, as: L
require Bottler.Helpers, as: H

defmodule Bottler.HelperScripts do

  @moduledoc """
    Generate helper scripts and put them into PATH.

    See `Mix.Tasks.Bottler.HelperScripts`.
  """

  @doc """
    The main entry point.
    It reads project configuration and generates helper scripts based on them.
    Then puts links to them on PATH to make them usable.

    Returns `{:ok, details}` when done, `{:error, details}` if anything fails.
  """
  def helper_scripts(config) do
    L.info "Generating helper scripts..."

    generate_ssh_server_scripts
    link_scripts_to_path
  end

  defp generate_ssh_server_scripts do
    app = Mix.Project.get!.project[:app]
  end

  defp link_scripts_to_path do
  end

end

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


Added missing model change for rules templates

defmodule Serverboards.Repo.Migrations.RulesTemplates do
  use Ecto.Migration

  def change do
    alter table(:rules_rule) do
      add :from_template, :string, size: 256
    end
  end
end

error: pathspec 'backend/apps/serverboards/priv/repo/migrations/20160819125319_rules_templates.exs' did not match any file(s) known to git


oops did that test backwards

oops did that test backwards

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


[minor] drop accept header for man requests (#2307)


chore: tuple key

chore: tuple key

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


Add tests for Serum.Init

Add tests for Serum.Init

defmodule InitTest do
  use ExUnit.Case, async: true
  import ExUnit.CaptureIO
  alias Serum.Init
  alias Serum.Payload

  test "init new" do
    uniq = <<System.monotonic_time()::size(48)>> |> Base.url_encode64
    tmpname = "/tmp/serum_#{uniq}/"
    capture_io fn -> send self(), Init.init(tmpname) end
    assert_received :ok
    assert_files tmpname
    File.rm_rf! tmpname
  end

  test "dir already exists" do
    uniq = <<System.monotonic_time()::size(48)>> |> Base.url_encode64
    tmpname = "/tmp/serum_#{uniq}/"
    File.mkdir_p! tmpname
    warn =
      capture_io :stderr, fn ->
        capture_io fn -> send self(), Init.init(tmpname) end
      end
    assert_received :ok
    expected_head = "\x1b[33m * The directory `#{tmpname}` already exists"
    assert String.starts_with? warn, expected_head
    assert_files tmpname
    File.rm_rf! tmpname
  end

  defp assert_files(dir) do
    ["serum.json", "pages/index.md", "posts",
     "templates/base.html.eex", "templates/page.html.eex",
     "templates/list.html.eex", "templates/post.html.eex",
     "templates/nav.html.eex", "assets/css", "assets/js", "assets/images",
     "media", ".gitignore"]
    |> Enum.each(fn f -> assert File.exists? "#{dir}#{f}" end)
    ["base", "page", "list", "post", "nav"]
    |> Enum.each(fn t ->
      file = File.read! "#{dir}templates/#{t}.html.eex"
      assert file == apply Payload, :"template_#{t}", []
    end)
  end
end

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


Test: 25 repos scraped from trending page. #95

defmodule Krihelinator.Periodic.GithubTrendingTest do
  use ExUnit.Case
  alias Krihelinator.Periodic.GithubTrending

  test "scraping the trending page return 25 items" do
    num_of_trending =
      GithubTrending.scrape()
      |> Enum.to_list()
      |> length
    assert num_of_trending == 25
  end
end

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


Make snapshot error handler private

Make snapshot error handler private


Add :from and :to options in DB.delete(edge: ) (#9)

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


Remove singular status component usage

Remove singular status component usage

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/joshforisha/merchant-quest.git/'


Handle empty timestamp fields

Handle empty timestamp fields


Whitespace

Whitespace


Fix nested requirements

Fix nested requirements

defmodule Hypnotoad.Plan do

  use GenServer.Behaviour
  use Hypnotoad.Common

  def start(opts) do
    :supervisor.start_child(Hypnotoad.Plan.Sup, [opts])
  end

  def start_link(opts) do
    :gen_server.start_link {:local, opts[:module]}, __MODULE__, opts, []
  end

  def module(server) do
    :gen_server.call(server, :module)
  end

  def run(server) do
    :gen_server.cast(server, :run)
  end

  defrecordp :state, module: nil, jobs: [], done_jobs: [], running?: false, failed: false, overrides: nil

  def init(opts) do
    :gproc.add_local_property(__MODULE__, Keyword.put(opts, :status, :ready))
    {:ok, state(module: opts[:module], overrides: :ets.new(__MODULE__.Overrides, [:bag]))}
  end

  def handle_call(:module, _from, state(module: module) = s) do
    {:reply, module, s}
  end

  defp prepare_overrides(host, module, args, overrides) do
    Process.put(:host, host)
    result = if module.before_filter(args) do
      Enum.each(module.requirements(args), fn({req_mod, req_args, parent}) ->
        if parent do
          {m, a} = parent
          :ets.insert(overrides, {{host, m, a}, {req_mod, req_args}})
          prepare_overrides(host, m, a, overrides)
        end
        prepare_overrides(host, req_mod, req_args, overrides)
      end)
      true
    else
      false
    end
    Process.delete(:host)
    result
  end

  defp start_module(host, module, args, overrides) do
    Process.put(:host, host)
    result = if module.before_filter(args) do
      reqs = module.requirements(args) ++ Enum.map(:ets.lookup(overrides, {host, module, args}), fn({_, {m, a}}) ->
        {m, a, nil}
      end)
      requirements = Enum.reduce(reqs, [], fn({req_mod, req_args, _parent}, acc) ->
        if start_module(host, req_mod, req_args, overrides) do
          [{req_mod, req_args}|acc]
        else
          acc
        end
      end)
      start_job(host, module, args, requirements)
      true
    else
      false
    end
    Process.delete(:host)
    result
  end

  def handle_cast(:run, state(module: module, done_jobs: done_jobs, running?: false, overrides: overrides) = s) do
    update_status(:preparing, s)
    lc {_, _, _, job} inlist done_jobs, do: Hypnotoad.Job.done(job)
    # Start jobs
    :ets.match_delete(overrides, :"_")
    Enum.each(Hypnotoad.Hosts.hosts, fn({host, _}) ->
      try do
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :success})
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :failed})
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :excluded})
      rescue _ ->
      end
      prepare_overrides(host, module, [], overrides)
      start_module(host, module, [], overrides)
    end)
    :gen_server.cast(self, :ready)
    {:noreply, state(s, jobs: [], done_jobs: [], running?: true, failed: nil)}
  end

  def handle_cast(:run, state() = s) do
    {:noreply, s}
  end

  def handle_cast(:ready, state(jobs: jobs) = s) do
    update_status(:running, s, start_timestamp: Hypnotoad.Utils.timestamp)
    lc {_, _, _, job} inlist jobs, do: Hypnotoad.Job.ready(job)
    {:noreply, s}
  end

  def handle_cast({:new_job, job}, state(jobs: jobs) = s) do
    {:noreply, state(s, jobs: Enum.uniq([job|jobs]))}
  end

  def handle_info({:gproc_ps_event, {Hypnotoad.Job, host, status}, {module, options}}, state(jobs: jobs, done_jobs: done_jobs, running?: true, failed: failed) = s) do
    matched_job = Enum.find(jobs, fn({host1, module1, options1, _pid}) -> host1 == host and module1 == module and options1 == options end)
    case matched_job do
      {_, _, _, pid} ->
        jobs = jobs -- [{host, module, options, pid}]
        done_jobs = [{host, module, options, pid}|done_jobs]
        cond do
          jobs == [] and status == :failed ->
            update_status(:failed, s, end_timestamp: Hypnotoad.Utils.timestamp)
          jobs == [] and failed ->
            update_status(:failed, s, end_timestamp: Hypnotoad.Utils.timestamp)
          jobs == [] ->
            update_status(:done, s, end_timestamp: Hypnotoad.Utils.timestamp)
          true ->
            :ok
        end
        {:noreply, state(s, jobs: jobs, done_jobs: done_jobs, running?: jobs != [], failed: failed || (status == :failed))}
      nil ->
        L.error "Job ${module} ${options} on host ${host} reported status ${status} but was not in the list of pending jobs, in the done jobs: ${done?}",
                module: module, options: options, status: status, host: host, done?: Enum.any?(done_jobs, fn({host1, module1, options1, _pid}) -> host1 == host and module1 == module and options1 == options end)
        {:noreply, s}
    end
  end

  def handle_info({:"DOWN", _ref, _type, pid, info}, state(jobs: jobs, running?: true) = s) do
    case Enum.find(jobs, fn({_, _, _, pid1}) -> pid1 == pid end) do
      {host, module, options, _pid} ->
        L.error "Job ${module} ${options} at host ${host} exited with ${info}", module: module, options: options, host: host, info: info
      _ ->
        :ok # we already wiped jobs out
    end
    {:noreply, s}
  end

  def handle_info(_, state() = s) do
    {:noreply, s}
  end

  defp update_status(type, state(module: module), extra // []) do
    props = :gproc.lookup_local_properties(__MODULE__)
    start_timestamp = props[self][:start_timestamp]
    :gproc.unreg({:p,:l, __MODULE__})
    :gproc.add_local_property(__MODULE__, Keyword.merge([module: module, status: type, start_timestamp: start_timestamp], extra))
    :gproc_ps.publish(:l, Hypnotoad.Plan, self)
  end

  defp start_job(host, module, options, reqs) do
    {:ok, pid} = Hypnotoad.Job.start(host: host, module: module, requirements: reqs, options: options, plan: self)
    Process.monitor(pid)
    :gen_server.cast(self, {:new_job, {host, module, options, pid}})
  end

end

defmodule Hypnotoad.Plan.Sup do
  use Supervisor.Behaviour

  def start_link do
    :supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
  end

  def init([]) do
    children = [
      worker(Hypnotoad.Plan, [], restart: :transient)
    ]
    supervise(children, strategy: :simple_one_for_one)
  end
end

defmodule Hypnotoad.Plan do

  use GenServer.Behaviour
  use Hypnotoad.Common

  def start(opts) do
    :supervisor.start_child(Hypnotoad.Plan.Sup, [opts])
  end

  def start_link(opts) do
    :gen_server.start_link {:local, opts[:module]}, __MODULE__, opts, []
  end

  def module(server) do
    :gen_server.call(server, :module)
  end

  def run(server) do
    :gen_server.cast(server, :run)
  end

  defrecordp :state, module: nil, jobs: [], done_jobs: [], running?: false, failed: false, overrides: nil

  def init(opts) do
    :gproc.add_local_property(__MODULE__, Keyword.put(opts, :status, :ready))
    {:ok, state(module: opts[:module], overrides: :ets.new(__MODULE__.Overrides, []))}
  end

  def handle_call(:module, _from, state(module: module) = s) do
    {:reply, module, s}
  end

  defp start_module(host, module, args, overrides) do
    Process.put(:host, host)
    result = if module.before_filter(args) do
      reqs = module.requirements(args) ++ Enum.map(:ets.lookup(overrides, {module, args}), fn({_, {m, a}}) ->
        {m, a, nil}
      end)
      Enum.each(reqs, fn({req_mod, req_args, parent}) ->
        if parent do
          :ets.insert(overrides, {{req_mod, req_args}, parent})
        end
      end)
      requirements = Enum.reduce(reqs, [], fn({req_mod, req_args, _parent}, acc) ->
        if start_module(host, req_mod, req_args, overrides) do
          [{req_mod, req_args}|acc]
        else
          acc
        end
      end)
      start_job(host, module, args, requirements)
      true
    else
      false
    end
    Process.delete(:host)
    result
  end

  def handle_cast(:run, state(module: module, done_jobs: done_jobs, running?: false, overrides: overrides) = s) do
    update_status(:preparing, s)
    lc {_, _, _, job} inlist done_jobs, do: Hypnotoad.Job.done(job)
    # Start jobs
    :ets.match_delete(overrides, :"_")
    Enum.each(Hypnotoad.Hosts.hosts, fn({host, _}) ->
      try do
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :success})
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :failed})
        :gproc_ps.subscribe(:l, {Hypnotoad.Job, host, :excluded})
      rescue _ ->
      end
      start_module(host, module, [], overrides)
    end)
    :gen_server.cast(self, :ready)
    {:noreply, state(s, jobs: [], done_jobs: [], running?: true, failed: nil)}
  end

  def handle_cast(:run, state() = s) do
    {:noreply, s}
  end

  def handle_cast(:ready, state(jobs: jobs) = s) do
    update_status(:running, s, start_timestamp: Hypnotoad.Utils.timestamp)
    lc {_, _, _, job} inlist jobs, do: Hypnotoad.Job.ready(job)
    {:noreply, s}
  end

  def handle_cast({:new_job, job}, state(jobs: jobs) = s) do
    {:noreply, state(s, jobs: Enum.uniq([job|jobs]))}
  end

  def handle_info({:gproc_ps_event, {Hypnotoad.Job, host, status}, {module, options}}, state(jobs: jobs, done_jobs: done_jobs, running?: true, failed: failed) = s) do
    matched_job = Enum.find(jobs, fn({host1, module1, options1, _pid}) -> host1 == host and module1 == module and options1 == options end)
    case matched_job do
      {_, _, _, pid} ->
        jobs = jobs -- [{host, module, options, pid}]
        done_jobs = [{host, module, options, pid}|done_jobs]
        cond do
          jobs == [] and status == :failed ->
            update_status(:failed, s, end_timestamp: Hypnotoad.Utils.timestamp)
          jobs == [] and failed ->
            update_status(:failed, s, end_timestamp: Hypnotoad.Utils.timestamp)
          jobs == [] ->
            update_status(:done, s, end_timestamp: Hypnotoad.Utils.timestamp)
          true ->
            :ok
        end
        {:noreply, state(s, jobs: jobs, done_jobs: done_jobs, running?: jobs != [], failed: failed || (status == :failed))}
      nil ->
        L.error "Job ${module} ${options} on host ${host} reported status ${status} but was not in the list of pending jobs, in the done jobs: ${done?}",
                module: module, options: options, status: status, host: host, done?: Enum.any?(done_jobs, fn({host1, module1, options1, _pid}) -> host1 == host and module1 == module and options1 == options end)
        {:noreply, s}
    end
  end

  def handle_info({:"DOWN", _ref, _type, pid, info}, state(jobs: jobs, running?: true) = s) do
    case Enum.find(jobs, fn({_, _, _, pid1}) -> pid1 == pid end) do
      {host, module, options, _pid} ->
        L.error "Job ${module} ${options} at host ${host} exited with ${info}", module: module, options: options, host: host, info: info
      _ ->
        :ok # we already wiped jobs out
    end
    {:noreply, s}
  end

  def handle_info(_, state() = s) do
    {:noreply, s}
  end

  defp update_status(type, state(module: module), extra // []) do
    props = :gproc.lookup_local_properties(__MODULE__)
    start_timestamp = props[self][:start_timestamp]
    :gproc.unreg({:p,:l, __MODULE__})
    :gproc.add_local_property(__MODULE__, Keyword.merge([module: module, status: type, start_timestamp: start_timestamp], extra))
    :gproc_ps.publish(:l, Hypnotoad.Plan, self)
  end

  defp start_job(host, module, options, reqs) do
    {:ok, pid} = Hypnotoad.Job.start(host: host, module: module, requirements: reqs, options: options, plan: self)
    Process.monitor(pid)
    :gen_server.cast(self, {:new_job, {host, module, options, pid}})
  end

end

defmodule Hypnotoad.Plan.Sup do
  use Supervisor.Behaviour

  def start_link do
    :supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
  end

  def init([]) do
    children = [
      worker(Hypnotoad.Plan, [], restart: :transient)
    ]
    supervise(children, strategy: :simple_one_for_one)
  end
end


Don't load camera from the database when logging online status


Update cache invalidation for newest exq


Add Serum.Theme behaviour

Add Serum.Theme behaviour

defmodule Serum.Theme do
  @moduledoc """
  A behaviour that all Serum theme module must implement.

  A Serum theme is a set of predefined templates and assets which are used
  while Serum is building a project.

  More specifically, a Serum theme is a Mix project which has the following
  contents:

  - Modules that implement this behaviour,
  - And theme files such as templates, includes, and other assets.
    These files are usually stored in the `priv/` directory.

  Your theme package must have at least one module that implements this
  behaviour, as Serum will call callbacks of this behaviour to ensure that your
  modules provide appropriate theme resources when needed.

  ## Using Serum Themes

  To use a Serum theme, you first need to add the theme package to your
  dependencies list.

      # mix.exs
      defp deps do
      [
        {:serum, "~> 1.0"},
        # ...

        # If the theme is available on Hex.pm:
        {:serum_theme_sample, "~> 1.0"}

        # If the theme is available on somewhere else:
        {:serum_theme_sample, git: "https://github.com/..."}
      ]
      end

  Fetch and build the theme package using `mix`.

      $ mix do deps.get, deps.compile

  To configure your Serum project to use a theme, you need to put a `:theme`
  key in your `serum.exs`.

      # serum.exs:
      %{
        theme: Serum.Themes.Sample,
        # ...
      }

  Read the documentation provided by the theme author, to see the list of files
  the theme consists of. Files provided by the theme are always overridden by
  corresponding files in your project directory. So it is safe to remove files
  from your project if the theme has ones.

  Finally, try building your project to see if the theme is applied correctly.

      $ mix serum.server
      # Or,
      $ MIX_ENV=prod mix serum.build
  """

  @doc "Returns the theme name."
  @callback name() :: binary()

  @doc "Returns a short descriptive text about the theme."
  @callback description() :: binary()

  @doc "Returns information about the theme author."
  @callback author() :: binary()

  @doc "Returns the legal information about the theme, such as license."
  @callback legal() :: binary()

  @doc """
  Returns the theme version.

  The returned value must follow the semantic versioning scheme.
  """
  @callback version() :: binary()

  @doc """
  Returns the required version of Serum.

  Read the "Requirements" section in the documentation for `Version` module
  for more information about version requirement format.
  """
  @callback serum() :: binary()

  @doc """
  Returns a list of paths to include files.

  All paths in the list must end with `".html.eex"`. Anything that does not end
  with `".html.eex"` will be ignored.

  # Example Return Value

      [
        "/path/to/theme/priv/includes/nav.html.eex",
        "/path/to/theme/priv/includes/sidebar.html.eex",
        "/path/to/theme/priv/includes/footer.html.eex"
      ]
  """
  @callback get_includes() :: [binary()]

  @doc """
  Returns a list of paths to template files.

  Each path in the list must end with one of these: `"/base.html.eex"`,
  `"/list.html.eex"`, `"/page.html.eex"`, and `"/post.html.eex"`. Anything else
  will be ignored.

  # Example Return Value

      [
        "/path/to/theme/priv/templates/base.html.eex",
        "/path/to/theme/priv/templates/list.html.eex",
        "/path/to/theme/priv/templates/post.html.eex"
      ]
  """
  @callback get_templates() :: [binary()]

  @doc """
  Returns a path to the assets directory.

  All files in the directory pointed by the returned value will be copied to
  the destination assets directory using `File.cp_r/2`.
  """
  @callback get_assets() :: binary()
end

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


Add Entity.update/3

Add Entity.update/3