map inside macro injected in Ecto from - macros

from these two macros properly defined in a module
defmacro binary(key) do
quote do
[
fragment("some fragment", unquote(key)),
fragment("some fragment", unquote(key))
]
end
end
defmacro mapbin(key) do
quote do
%{
"1" => fragment("some fragment same as above", unquote(key)),
"2" => fragment("some fragment same as above", unquote(key))
}
end
end
this works
from u in Table, select: binary("key")
but not this
from u in Table, select: mapbin("key")
What is the problem with the map in the macro ?
the message is compile error
%{"1" => fragment("", ""),...} is not a valid query expression.

it was a bug in ecto
it has been fixed
see https://github.com/elixir-ecto/ecto/issues/1936

Related

How to correctly pass Postgrex.Notifications to a supervisor

I'm trying to setup postgres triggers in phoenix.
The docs state
In order to use it, first you need to start the notification process. In your supervision tree:
{Postgrex.Notifications, name: MyApp.Notifications}
So here's my implementation:
application.ex
defmodule Foo.Application do
...
def start do
children = [Foo.Repo, {Postgrex.Notifications, Name: Foo.Notifier}]
opts = [strategy: :one_for_one, name: Foo.Supervisor]
Supervisor.start_link(children, ops)
end
end
notifier.ex
defmodule Foo.Notifier do
def child_spec(opts) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [opts]}
}
end
def start_link(opts \\ []),
do: GenServer.start_link(__MODULE__, opts)
def init(opts) do
# setup postgres listeners
end
end
I get an error message saying
The module Postgrex.Notifications was given as a child to a supervisor but it does not implement child_spec/1.
If you own the given module, please define a child_spec/1 function
that receives an argument and returns a child specification as a map.
However, if you don't own the given module and it doesn't implement
child_spec/1, instead of passing the module name directly as a supervisor
child, you will have to pass a child specification as a map
So I followed the example in the error
%{
id: Postgrex.Notifications,
start: {Postgrex.Notifications, :start_link, [arg1, arg2]}
}
my implementation:
children = [
Foo.Repo,
%{
id: Postgrex.Notifications,
start: {Postgrex.Notifications, :start_link, opts}
}
]
Supervisor.start_link(children, opts)
But now I get the error
** (Mix) Could not start application survey: exited in: Foo.Application.start(:normal, [])
** (EXIT) an exception was raised:
** (UndefinedFunctionError) function Supervisor.start_link/1 is undefined or private
I found this tutorial
Instead of passing a tuple to the supervisor tree just pass your module and call start_link manually
application.ex
children = [Foo.Repo, Foo.Notifier]
notifier.ex
#impl true
def init(opts) do
with {:ok, _pid, _ref} <- Repo.listen("some_channel_name") do
{:ok, opts}
else
error -> {:stop, error}
end
end
repo.ex
defmodule Foo.Repo do
def listen(channel) do
with {:ok, pid} <- Postgrex.Notifications.start_link(__MODULE__.config()),
{:ok, ref} <- Postgrex.Notifications.listen(pid, channel) do
{:ok, pid, ref}
end
end

Are Erlang -callbacks to be invoked only through MFA functions (apply/3, spawn/3, ...)? (Custom Behaviors HOWTO)

That's my suspicion as this simple code
-module(simple_server).
-export( [sayHello/0] ).
-callback say(Num :: term()) -> term().
sayHello() ->
io:fwrite( "Hello 1: ~p\n", [ say(1) ]) ,
io:fwrite( "Hello 2: ~p\n", [ say(2) ]) .
fails to be compiled:
$ erlc simple_server.erl
simple_server.erl:7: function say/1 undefined
simple_server.erl:8: function say/1 undefined
If that is the case, then this is not explicitly commented elsewhere:
official docs, "learn erlang", this answer.
You need to provide the name of the callback module, which can be done through apply and spawn, but you can also make a simple call using a variable as the module name, e.g. CallbackModule:say(1).
So you could do either:
sayHello(CallbackModule) ->
io:fwrite( "Hello 1: ~p\n", [ CallbackModule:say(1) ]) ,
io:fwrite( "Hello 2: ~p\n", [ CallbackModule:say(2) ]) .
or
sayHello(CallbackModule) ->
io:fwrite( "Hello 1: ~p\n", [ apply(CallbackModule, say, [1]) ]) ,
io:fwrite( "Hello 2: ~p\n", [ apply(CallbackModule, say, [2]) ]) .
Those two versions are equivalent.
Let's create a callback module implementing the simple_server behaviour:
-module(my_callback).
-behaviour(simple_server).
-export([say/1]).
say(N) ->
{N, is, the, loneliest, number}.
Now we can call simple_server:sayHello with the module name as the argument:
> simple_server:sayHello(my_callback).
Hello 1: {1,is,the,loneliest,number}
Hello 2: {2,is,the,loneliest,number}
HOWTO Erlang Custom Behaviors (template method pattern).
-1. Write a generic module. Here, an algorithm is defined, but some steps (callback functions in Erlang nomenclature) are left for a future specific definition.
%% generic.erl
-module(generic).
-export( [sayHello/1] ).
-callback say(Num :: term()) -> term(). %% future definition
%% generic algorithm: needs the reference to the provider module
sayHello(ProviderModule) ->
io:fwrite( "Hello 1: ~p\n", [ ProviderModule:say(1) ]) ,
io:fwrite( "Hello 2: ~p\n", [ ProviderModule:say(2) ]) .
-2. Compile generic.erl
erlc generic.erl
-(3.) Try to write a provider (callback) module
%% callbacks1.erl (fails to implement say/1 callback)
-module( callbacks1 ).
-behaviour( generic ).
-(4.) Compile callbacks1.erl: use -pa . to say where generic.beam is. (Therefore, the expected warning about say/1 is issued).
erlc -pa . callbacks1.erl
callbacks1.erl:2: Warning: undefined callback function say/1 (behaviour 'generic')
(Note: If -pa is not given, you'll got this: "callbacks1.erl:2: Warning: behaviour generic undefined")
-3. Write a correct provider (callback) module.
%% callbacks2.erl
-module( callbacks2 ).
-behaviour( generic ).
-export( [say/1] ).
say(1) -> "good morning";
say(2) -> "bon jour";
say(_) -> "hi".
-4. Compile it
erlc -pa . callbacks2.erl
(Ok now).
-5. Write a main.erl to gather generic module with callback module.
%% main.erl
-module( main ).
-export( [main/0] ).
main() ->
%% call the generic algorithm telling it what callback module to use
generic:sayHello( callbacks2 )
. % ()
-6. Compile and run main
erlc main.erl
erl -noshell -s main main -s init stop
We get:
Hello 1: "good morning"
Hello 2: "bon jour"

Mongodb '$where' query using javascript regex

I am trying to reproduce the REPLACE function in sql on mongodb.
My collection 'log' has this data in the 'text' field.
[01]ABC0007[0d0a]BB BABLOXC[0d0a]067989 PLPXBNS[0d0a02]BBR OIC002 L5U0/P AMD KAP 041800 T1200AND 2+00[0d0a0b03]
All I'm trying to do is remove the '[..]' using regex (javascript) and use contains('PLPXBNSBBR') like this so that the expression return true per the javadocs in mongo documentation.
This query below successfully works and returns the matching rows.
db.log.find({"$where":"return this.message.replace(new RegExp('0d0a02'),'').contains('PLPXBNS[]BBR') "});
However, I would need to remove the '[..]' and match like this PLPXBNSBBR.
These are the ones I tried unsuccessfully
db.log.find({"$where" : " return this.message.replace( new
RegExp('\[.*?\]', 'g'), '' ).contains('PLPXBNSBBR') " });
db.log.find({"$where" : " return this.message.replace( new
RegExp('/[(.*?)]', 'g'), '' ).contains('PLPXBNSBBR') " });
db.log.find({"$where" : " return this.message.replace( new
RegExp('//[.*//]'), '' ).contains('PLPXBNSBBR') " });
db.log.find({"$where" : " return this.message.replace( new
RegExp('[.*?]'), '' ).contains('PLPXBNSBBR') " });
From the earlier discussion it appears that if I can pass the pattern as /[[^]]+]/g, it should strip the [..] but it is not doing that and not returning the matching rows.
Okay, I was able to use chaining replace successfully to get my desired results.

Why this 'fail ->' caused invalid indentation error

This is my original code
new Data({ data_id: #model.get 'id' })
.fetch()
.done (result) =>
#_setUserInput(result.text)
.fail ->
bugsense.notify 'quickform results cannot be fetched' # error here
It caused the coffeescript compiler to throw this error message: Line contains inconsistent indentation (indentation)
Then I rewrite to
fail = -> bugsense.notify 'results cannot be fetched'
new Data({ data_id: #model.get 'id' })
.fetch()
.done (result) =>
#_setUserInput(result.text)
.fail fail
It passes
My first version looks correct. So what it caused the invalid indentation error?
You should have tabs and spaces mixed in your file, because I have tried to compile your code and It seems to be ok (you can check here that your code is right).
If your file uses 2 or 4 spaces to indent, and you are mixing spaces with tabs your compilation is going to fail because your file has unconsistent indentation.

ChangeSet Ecto.Model all fields of Struct are nil

I'm working on some Phoenix framework and i have encountered a weird problem (as usual). Whenever I try to create some Users, i get User with all fields set to nil. I'm using Mongo.Ecto/
def post_login(conn, %{"login" => login, "password" => password}) do
# IO.inspect Plug.Conn.read_body(conn)
a = User.changeset(%User{}, %{"login" => "login", "password" => "password"})
IO.inspect a
Repo.insert( a )
redirect conn, to: "/default"
end
And the model:
defmodule HelloWorld.User do
use HelloWorld.Web, :model
#primary_key {:id, :binary_id, autogenerate: true}
schema "users" do
field :login, :string
field :password, :string
end
#required_fields ~w()
#optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
end
end
And the screen from console:
As you can see in the picture, both login and password fields are nils which makes me feel I've done something incredibly stupid.
The fields need to exist in the options to the cast/4 function:
#required_fields ~w()
#optional_fields ~w(login password)
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
end
Anything that is in required_fields but not in the params will add an error to that field on the changeset. If you want the fields to be required just move them to the required_fields list.