Perhaps try requiring it first. require BookList.User
I used mix phx.new --no-brunch --no-html book_list
and have lib/book_list and lib/book_list_web
. See session below. Can’t figure out what is wrong.
$ iex -S mix phoenix.server
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
mix phoenix.server is deprecated. Use phx.server instead.
[info] Running BookListWeb.Endpoint with Cowboy using http://0.0.0.0:4000
Interactive Elixir (1.6.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> BookList.User.__info__(:functions)
** (UndefinedFunctionError) function BookList.User.__info__/1 is undefined (module BookList.User is not available)
BookList.User.__info__(:functions)
iex(1)> require BookList.Use
** (CompileError) iex:1: module BookList.Use is not loaded and could not be found
You forgot the r
in BookList.User
Where is that user.ex
? (phx.new
wouldn’t create that web
directory.)
In a “healthy project”:
iex(2)> Rumbl.Accounts.User.__info__(:functions)
[
__changeset__: 0,
__schema__: 1,
__schema__: 2,
__struct__: 0,
__struct__: 1,
changeset: 2,
registration_changeset: 2
]
for rumbl/lib/rumbl/accounts/user.ex
Assuming you’re using Phoenix 1.3, try replacing use BookList.Web, :model
with use Ecto.Schema
and import Ecto.Changeset
. But I agree with @peerreynders, the existence of separate web
and lib
directories suggests that something’s not kosher. Are you sure you’re using 1.3?
Sorry, typo, but the result is the same:
iex(2)> require BookList.User
** (CompileError) iex:2: module BookList.User is not loaded and could not be found
How do I check the Phoenix version?
The project version is listed in mix.exs
:
defp deps do
[
{:phoenix, "~> 1.3.2"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:comeonin, "~> 4.1"},
{:bcrypt_elixir, "~> 1.0"}
]
end
For the installed version:
$ mix phx.new --version
Phoenix v1.3.2
You can check the version of the phoenix mix archive by running mix phx.new -v
.
This is what I have:’
$ mix phx.new --version
Phoenix v1.3.2
Maybe I should start over. I was just setting up this project, so no harm would be done. Does seem fishy.
A 1.3 project created with phoenix.new
would create book_list/web/web.ex
which contains:
defmodule BookList.Web do
# ...
def model do
quote do
# Define common model functionality
end
end
A 1.3 project created with phx.new
would create book_list/lib/book_list_web.ex
which contains:
defmodule BookListWeb do
# ...
without any model
definition.
So use BookList.Web, :model
couldn’t work in a phx.new
project.
You might want to try out the Phoenix generators to get a feel for how things go together. https://hexdocs.pm/phoenix/Mix.Tasks.Phoenix.Gen.Html.html#content
Or
https://hexdocs.pm/phoenix/Mix.Tasks.Phoenix.Gen.Model.html#content
Those links discuss the old style generators (phoenix.gen) which are only relevant for existing applications. For new applications only the new style generators (phx.gen) should be used.
Like: https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Json.html#content
My previous troubles arose from mixing phoenix
and phx
– I had been using generators, as in the session below. So here is a new try with
660 mix phx.new --no-brunch --no-html book_list
661 cd book_list/
662 atom .
663 mix phx.gen.json BookSpace User users username:string email:string
664 mix ecto.migrate
T’his gives the error
$ mix ecto.migrate
Compiling 16 files (.ex)
== Compilation error in file lib/book_list_web/controllers/user_controller.ex ==
** (CompileError) lib/book_list_web/controllers/user_controller.ex:18: undefined function user_path/3
(stdlib) lists.erl:1338: :lists.foreach/2
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:198: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6
I would expect this to work with the --no-html
flag, but it’s not working for me either. However, without the --no-html
flag, and running mix ecto.create
before migrating works
Did you do this, it’s part of the output after generating the new project:
Add the resource to your :api scope in lib/book_list_web/router.ex:
resources "/users", UserController, except: [:new, :edit]
In your current project the /api scope is commented out. But even if it wasn’t there would be no entry for the User schema.
You just need to uncomment the /api scope and add the entry. The user_path/3 function is path helper so it’s trying to return a path based on a route definition which doesn’t exist in this scenario.
Here’s a link to some information on path helpers.
https://hexdocs.pm/phoenix/routing.html#path-helpers
Hope that helps
Paul
Thank you all !!
The solution: (1) Use the phx
prefix consistently – no mixing with phoenix
; (2) The phx
generators require a context module name before the model module name and its lowercase plural; (3) You have to add the route, as @paulsullivanjr noted – this is not done automatically.
All working now; I very much appreciate everyone’s help.
I’d go as far as saying forget about models (essentially what phx
does by shifting from mix phoenix.gen.model
to mix phx.gen.schema
).
mix phx.gen.context
is largely a convenience tool rather than a prescription for contexts:
mix phx.gen.context Accounts User users name:string age:integer
simply creates the persisted User
data type inside the Accounts
context (which is created if it doesn’t already exist). The generator doesn’t convey that
- a single context can have multiple persisted data types
- the type persisted may not be the type that is shared through the module API because the persisted type may contain details that are appropriate for internal context use only.
In the simplest terms “model” has a habit of putting the data type persisted as a table front and center based on an oversimplified “wiring” of resource based Server MVC:
GET resource/:id → SELECT * FROM resource WHERE id = :id
POST resource → INSERT INTO resource
PUT resource/:id → UPDATE resource WHERE id = :id
DELETE resource/:id → DELETE resource WHERE id = :id
The CRUD oversimplification isn’t what REpresentational State Transfer is all about. For example creating a “money transfer resource” could be responsible updating the two accounts represented in the state of the resource.
In contrast to “model” the context is supposed to put related behaviour front and center - at the same time giving the context autonomy over its internal data types and how they are persisted. So an E-commerce site could have Sales
, Billing
, Shipping
contexts while Sales
and Shipping
each have their own internal Product
type (collaborating via some common Product Key
or public (non-persisted) Product type
). And contexts aren’t about REST resources - they deal with domain types and their behaviours.
Thank you very much. This is a great help!
I should note however that even Programming Phoenix ≥ 1.4 still advocates this implementation of “Phoenix MVC”:
The “model” is the collection of persisted data types, the associated queries, and changesets - but without Repo
access. This is justified by striving to keep everything inside the “model” free from side effects, leaving it ultimately up to the controller to effect changes in persisted state by pushing the built-up changesets into the appropriate Repo
functions.
This is in contrast to the typical understanding of a bounded context which puts a boundary around its internal types (and associated transformations usually implemented in a persistence-ignorant manner with direct access to/on top of a Repository)
(Edit: some of the Programming Phoenix ≥ 1.4 code actually looks less like the above graphic as context functions will access the Ecto repo themselves - in which case side effects are accepted in order to keep implementation details out of the controller)
“Phoenix MVC” is the basis of Dave Thomas’s claim that:
first 1.2 and then 1.3 actually tightened the coupling between the two.
[i.e. Phoenix and Ecto] - making a case for removing the database (and Ecto) entirely out of Phoenix and using them inside a separate OTP component (OTP application) which exposes an API to which Phoenix connects.