I started my application before the embedded_schema API was available and the Assumption that a RDBMS was backing everything was a major pain-point for me at the time leading me to create a custom form abstraction using Vex (https://github.com/CargoSense/vex). Now with embedded_schema the pain went away and I ended up refactoring my form modules away completely in favour of the new API.
I was using Ruby myself for 10 years before using Elixir and the Form object pattern was one I used very often myself. A general principle of Elixir I have often seen that if you think something doesn't work, or is the wrong way you in fact need to unlearn some of the brain damage acquired from Ruby.
A great example of this is Wojtek Mach's github_ecto project (https://github.com/wojtekmach/github_ecto). His abstracting of the Github API behind a Ecto.Repo was mindblowing to me. Your app communicates with a HTTP API without knowing it's any different than your DB.
Elixir is very much a different way of thinking about problems than Ruby.
No data validation at the moment, but it would be nice to generate the form parsing and validation, as well as the getters and setters, from a declarative spec.
Totally of topic but could someone explain Elixirs syntax to me?
In the following code (from the linked site):
What is embeds_many?
Are :string and :map type information or Atoms?
What happens to :changes, Change, primary_key?
Does the code between do and end just call the field function twice?
embeds_many :changes, Change, primary_key: false do
2. Anything that begins with a colon is an Atom in Elixir, including :string, :map, :changes, :field, and :value. These atoms are used by Ecto at runtime to identify certain things. `:changes` is a unique name given to the embedded schema, `:field` and `:value` are field names on that schema, and :string and :map are informing Ecto of how to treat certain values.
3. `Change` is the name of a Module which describes the Schema we are embedding
4. `primary_key: false` is a configuration option
5. Everything after `do...` is describing what the embedded schema would look like. So it has a `:field` field that is of type string, and a `:value` field that is of type map.
Hopefully that makes some sense... I'm not totally clear on it either (having not used Ecto before) but that's what I'm grokking.
This code snippet is a little bit confusing if you aren't already familiar with Elixir, because most of the syntax you're seeing is actually from Ecto's [0] DSL. Calling "use Ecto.Schema" at the top of the module brings some extra functionality into the current scope. For example, embedded_schema is a macro exported by Ecto.Schema [1].
embeds_many is another such macro [2], that allows you embed another schema, in-line, into the current schema. This is contrast to your more common has_many relationship, which references another table entirely.
Here, :string, :map, etc are atoms that are being passed to the field macro [3], to define the schema for the table.
Others provided an explanation of the Ecto DSL, I just wanted to add the explanation of basic syntax. It goes like this.
In Elixir, the most basic function application looks fairly standard:
func_name(arg1, arg2, ...)
But the parens are optional, so you can write:
func_name arg1, arg2, ...
as well.
Next, in Erlang and Elixir function arity (number of arguments it takes) is always fixed (i.e. no varargs or Python's *args). Because of this you generally have to pack your arguments in a list; functions like `printf` work that way:
iex(2)> :io.format("fmt ~p foo: ~p bar: ~p", [1, 3, 4])
fmt 1 foo: 3 bar: 4
That takes care of (positional) varargs. There are some other functions, however, which would benefit from "keyword arguments", which also isn't directly supported. To get around this, you can take a list of pairs (keyword, value) as one of your arguments (usually last):
Where `{}` creates a tuple and `:` creates an atom (called symbols in Lisp), which you can think of as a special[1] string value. This pattern is so common, that Elixir provides a syntactic sugar for it. The above is equivalent to:
Edited to add: Capitalized names in Elixir are actually special atoms, mostly used for naming modules. It works like this:
iex(5)> :'Elixir.Change' == Change
true
There is more going on because of the fact you can alias module names, but mostly you can think about this as (another) special syntax for atoms.
[1] Allocated once and cached. When working with normal strings:
a = "Asd"
b = "Asd"
you have no guarantee that `a` and `b` point to the same thing in memory, while with atoms you do have that guarantee. This makes comparing them efficient.
It's good to see more people developing in Elixir.
But having said that, I'm against server-side forms. I feel client side frameworks like vue.js completely alleviates the need for server-side form objects and does a lot more. Server-side forms at times can be a real pita to work with.
server side validation (which is indeed requried) != forms.
also, Ecto does really good job with changesets, including hard-to-do-right pieces like "uniqueness"
IMHO, reusable server side validation == forms without widgets (and widgets are nice to have if one feels that JS is unnecessarily complicating what could be a simple static HTML form).
I was using Ruby myself for 10 years before using Elixir and the Form object pattern was one I used very often myself. A general principle of Elixir I have often seen that if you think something doesn't work, or is the wrong way you in fact need to unlearn some of the brain damage acquired from Ruby.
A great example of this is Wojtek Mach's github_ecto project (https://github.com/wojtekmach/github_ecto). His abstracting of the Github API behind a Ecto.Repo was mindblowing to me. Your app communicates with a HTTP API without knowing it's any different than your DB.
Elixir is very much a different way of thinking about problems than Ruby.