MartinElvar
MartinElvar

Reputation: 5804

How to do Elixir mixins

I'm trying to create a mixins for authentication login, so it can be applied to my models which should be able to login. Much like the the has_secure_password in Ruby.

Afaik this is done using the use statement which essential requires the module, and calls the __using__ macro. So I implemented my mixin like this.

defmodule MyApp.SecurePassword do
  defmacro __using__(_options) do
    quote do
      import MyApp.SecurePassword
    end
  end

  defmacro authenticate(password) do
    # Lets return true, for testing purposes.
    true
  end
end

I'm then calling the use, in my "user" model.

defmodule MyApp.Farm do
  use MyApp.Web, :model
  use MyApp.SecurePassword

  schema "farms" do
    field :name, :string
    field :email, :string
  #.....

In my controller, i'm then trying to use the method.

 def create(conn, %{"session" => session_params}) do
    user = Repo.get_by(Farm, email: session_params["email"])

    if user && user.authenticate(session_params["password"]) do
      conn = put_flash(conn, :success, "You were successfully logged in")
    else
      conn = put_flash(conn, :error, "Credentials didn't match")
    end

    redirect(conn, to: session_path(conn, :new))
 end

But when i hit the code, i just get a argument error, on the line where i call the authenticate function.

My macro skills is rather weak, what am i doing wrong? :)

Upvotes: 4

Views: 659

Answers (1)

Paweł Obrok
Paweł Obrok

Reputation: 23164

I think what you want is to call the authenticate function passing in the user and the password:

def authenticate(user, password) do
  # auth logic
end

And then:

import MyApp.SecurePassword
# ...
if user && authenticate(user, session_params["password"]) do
# ...

For now there doesn't seem to be any reason to use a macro or a use, a simple import will do - you only want those when generating some code compile-time and in this case it seems everything you want will be happening at runtime.

Upvotes: 6

Related Questions