1
votes

I am building a little test app with the Phoenix Framework. I cannot use Ecto since my DB is not supported. But I still want the MVC pattern. So I need a way to implement custom models. Since I'm coming from RoR I always catch myself thinking in Object Oriented Programming patterns where you have a class definition and instances with attributes that you can get and set. So that something like this is possible:

@mike = User.new
@mike.name = "Mike Power"
@mike.save

# ect

But this is clearly not how Elixir works.

I'm having a hard time wrapping my head around how to approach this. The implementation should allow me to:

  • Have attributes
  • Store values
  • Manipulate values
  • Validate values
  • Save them to my database

The best way I have come up with so far is using structs. So something like this:

defmodule User do
  defstruct name: "", age: 0

  def sayhello do
    IO.puts "Hi, I am #{name}"
    # This is not working, I just put it here to make a point
  end
end 

tim = %User{}
# -> %User{age: 0, name: ""}
tim = %User{name: "Tim Turbo", age: 25}
# -> %User{age: 25, name: "Tim Turbo"}
tim.name
# -> "Tim Turbo"

# We can also change the values 
tim = %User{name: "Michael"}
# -> %User{age: 25, name: "Michael"}

# It also makes sure that only the values defined in the struct can be set (which is nice)

tom = %User{unknown: "something"}
# ** (CompileError) iex:11: unknown key :unknown for struct User

Using vex I would even be able to implement validations quite easily

defmodule User do
  defstruct username: nil, password: nil, password_confirmation: nil
  use Vex.Struct

  validates :username, presence: true,
                       length: [min: 4],
                       format: ~r/^[[:alpha:]][[:alnum:]]+$/
  validates :password, length: [min: 4],
                       confirmation: true
end

So I am basically asking:

  • Is this the way to go? Or is there a better one?
  • And would I implement the function to actually save the user to the database also in my User module? Where I just enter the parameters

Something like this:

user = %User{params}
User.create user
2
reading ecto's source is a good starting point: github.com/elixir-lang/ectosobolevn

2 Answers

2
votes

My thoughts on reverting OO paternilized way of thinking:

Try to build your logic around data and what you need to do with data. Not the object's you need to create and modify.

On example of "Say Hello":

defmodule UserWelcomer do
  def sayhello(user) do
    IO.puts "Hi, I am #{user.name}"
  end
end 

Basicly you trying to be functional all the way and working with "User" data through different functions.

Sorry, this not an answer. Just a comment that off the comment limits.

1
votes

This is over a year old, but I think a good answer would help others who stumble across this one on google...

Is this the way to go? Or is there a better one?

You're on the right track! Data is stored in structs, rather than objects. Take a look at the Ecto.Changeset docs regarding model validations and Ecto.Schema docs regarding storing data in a struct. It's very similar to what you're doing.

It's also important to note that you can use components of Ecto that you want (like Changeset and Schema) without having to use things that don't work for your application, like Ecto.Repo.

And would I implement the function to actually save the user to the database also in my User module? Where I just enter the parameters

This is almost more a philosophical question at this point. Going along with using Ecto as an example:

  • A "model" with Ecto (at least as used in Phoenix, probably the most common case) is really just an Ecto.Schema that has some extra functions for changesets and validations.
  • Saving or updating (persistence) is handled by a separate module that makes use of Ecto.Repo.

Combining these results in something more like Ruby on Rails' ActiveRecord which people have found more difficult to test, whereas this separation helps make your models very easy to test.