A quick update about some new Nitrogen functionality

60 views
Skip to first unread message

Jesse Gumm

unread,
Feb 4, 2017, 1:05:42 PM2/4/17
to nitrogenweb
Hey Nitrogen folks,

I've got a quick announcement for those of you patiently waiting for the new version to be released.

I've got an experimental new feature deployed to the nitrogen_core master branch that I'd love some feedback on.

It's the employment of a new parse_transform (and new rebar dependency of nitrogen_core) to allow creating clones of records more simply.

Here's the use case:

You're making a new element which is largely based on another element.  Let's say you're making a specific kind of dropdown box element that you want to copy basically all the fields of the base #dropdown{} element, but you want to add a few more options without.

Up until now, the process has required basically making a complete copy of the #dropdown{} record definition, calling it something like #mydropdown{}, and then in your element_mydropdown.erl file, copying all the fields from one record to the other, either manually with something painful like

Rec2 = Rec#dropdown{class=OldRec#mydropdownclass}

Or using:

Rec2 = wf_util:copy_fields(Rec, #dropdown{}).

(the latter of which is slower, but significantly cuts down on the lines of code).

Both approaches are a little putzy.

With this new parse transform (called rekt: https://github.com/nitrogen/rekt), you can create your own record definitions using the original record definition as the core, then adding your own fields or redefining existing fields.

Using the example above, say you wanted to add the field 'foo'  with a default value of "myvalue" to the #dropdown{} record, you could do so with the -extend module attribute as follows:

-extend(dropdown, mydropdown, [{foo, "myvalue"}]).

The above would then create a new #mydropdown{} record definition that would be exactly the same as #dropdown{}, except it would add foo="myvalue" to the end of the record.

This works great, except that in Nitrogen, a new element also needs a new module definition, to fix this, we should actually define our #mydropdown{} like this:

-extend(dropdown, mydropdown, [{module, element_mydropdown}, {foo, "myvalue"}]).

This will properly tell the element which module should render the new element (just like how the rendering module is provided in the ubiquitous ?ELEMENT_BASE macro).

However, since this is such a common pattern, I've added a ?WF_EXTEND macro to allow us to quickly define our new element with this:

?WF_EXTEND(dropdown, mydropdown, element_mydropdown, [{foo, "myvalue"}]).

The arguments are:

?WF_EXTEND(OrigRecord, NewRecord, RenderModule, Attributes).


To go along with this, I've also added a new functions for faster field_copying. While wf_utils:copy_fields is guaranteed to work for copying fields from one element to another, wf_utils:fast_copy_fields is designed specifically to work with elements extended using ?WF_EXTEND (since it expects the same record structure plus a few fields).

As a practical example in the works, I've been slowly working on coldstrap (https://github.com/choptastic/coldstrap), a Nitrogen plugin for defining new elements that are bootstrap specific. You can see the include file is quite sparse for recreating a bunch of new elements:  https://github.com/choptastic/coldstrap/blob/master/include/records.hrl

And you can see a sample element definition, here is the bootstrapified #dropdown{} clone called #dd{}: https://github.com/choptastic/coldstrap/blob/master/src/element_dd.erl

Anyway, those of you experimenting with things, feel free to give this a gander and let me know what you think.  You can pull from the nitrogen_core master branch, and run `make` from the root of your project to have it pull down `rekt`.

Also, feel free to have a look at the rekt definition. It's a pretty simple parse transform, and it's my first time digging around in parse transforms beyond a cursory glance, so if I missed or messed anything up, please call me on it, or feel free to put in a pull request.

Thanks folks, and have a great weekend!

-Jesse

--
Jesse Gumm
Owner, Sigma Star Systems
414.940.4866 || sigma-star.com || @jessegumm

Jesse Gumm

unread,
Feb 5, 2017, 8:12:45 PM2/5/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
Oops, I had one mistake (which I repeated).

The -extend attribute needs to be a tuple

So, for example, this will fail to compile:

-extend(dropdown, mydropdown, [{foo, "myvalue"}]).




But this will work:

-extend({dropdown, mydropdown, [{foo, "myvalue"}]}).



That said, if you're using the ?WF_EXTEND macro, you don't need to wrap the arguments in tuples, so this is still proper:


?WF_EXTEND(dropdown, mydropdown, element_mydropdown, [{foo, "myvalue"}]).

Sorry for any confusion this might have caused.  Conveniently, if you're using this with Nitrogen, there's a good chance you really only want to use the macro anyway :)

Anyway, happy hacking!

-Jesse

Franklin Brauning

unread,
Feb 21, 2017, 3:24:19 PM2/21/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
That looks promising, I was actually in need of something like that for a bulma.io wrapper I've been writing.

Going to test it

Jesse Gumm

unread,
Feb 21, 2017, 3:36:13 PM2/21/17
to nitrogenweb
Awesome. Please share when you have something to look at :)

--
You received this message because you are subscribed to the Google Groups "Nitrogen Project / The Nitrogen Web Framework for Erlang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb+unsubscribe@googlegroups.com.
To post to this group, send email to nitro...@googlegroups.com.
Visit this group at https://groups.google.com/group/nitrogenweb.
For more options, visit https://groups.google.com/d/optout.

Franklin Brauning

unread,
Feb 22, 2017, 8:50:06 PM2/22/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
I'm did a lame "make upgrade" on a running site  to pull my own enhancements but it also brought master nitrogen.

I'm getting this on "make"

==> nitrogen_fa (compile)
/home/aiken/instalacion/lib/nitrogen_fa/src/element_fa.erl:none: undefined parse transform 'rekt'
Compiling /home/aiken/instalacion/lib/nitrogen_fa/src/element_fa.erl failed:
ERROR: compile failed while processing /home/aiken/instalacion/lib/nitrogen_fa: rebar_abort

do I have to stop the VM ? or should I start with a new installation?


On Saturday, February 4, 2017 at 3:05:42 PM UTC-3, Jesse Gumm wrote:

Jesse Gumm

unread,
Feb 22, 2017, 8:55:09 PM2/22/17
to nitrogenweb
Yeah, I'd just kill the VM, do a `make` to be safe, and then start the VM again.

the `make upgrade` works well most of the time, but if you're pulling from nitrogen master, and your previous version didn't have `rekt` as a dependency, then `sync` won't properly load it on the fly as a new dependency  (which is why I'll be moving all of Nitrogen to rebar3 for Nitrogen 3.0).

No need to start a new installation.  It just added `rekt` as the dependency and didn't know what to do with it.  `make upgrade` doesn't imply the `rebar get-deps` that's expected.

--
You received this message because you are subscribed to the Google Groups "Nitrogen Project / The Nitrogen Web Framework for Erlang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb+unsubscribe@googlegroups.com.
To post to this group, send email to nitro...@googlegroups.com.
Visit this group at https://groups.google.com/group/nitrogenweb.
For more options, visit https://groups.google.com/d/optout.

Franklin Brauning

unread,
Feb 23, 2017, 1:09:50 AM2/23/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
I did move the nitrogen_core dependency to first place and now it compiles fine

My guess: the plugins are compiled against the parse-transform but it hasn't been built yet
 
Source:
http://stackoverflow.com/questions/20813513/configuring-lager-i-get-this-error-undefined-parse-transform-lager-transform#20816700
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb...@googlegroups.com.

To post to this group, send email to nitro...@googlegroups.com.
Visit this group at https://groups.google.com/group/nitrogenweb.
For more options, visit https://groups.google.com/d/optout.

Franklin Brauning

unread,
Mar 6, 2017, 3:19:46 PM3/6/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
Ok, I have changed a record definition with rekt... flawless semantics

What's the cost of this feature? Have little idea about parse transforms... does
it run when compiling or at runtime?


On Saturday, February 4, 2017 at 3:05:42 PM UTC-3, Jesse Gumm wrote:

Jesse Gumm

unread,
Mar 6, 2017, 3:28:59 PM3/6/17
to nitrogenweb
Awesome! Glad to hear that's working out for you. I'm admittedly happy with it myself - it really cuts down on the nuisance of making a new element that's based on another element.

The overhead is minimal with parse_transform - compile time only, and in those case, just compile time record generation. 

Also, if your render_element function uses fast_copy_fields, that will be faster rendering than using the slower copy_fields function. The former *assumes* you extended a record with rekt, the latter is more flexible. 

Thanks for the feedback! 

-Jesse

--
You received this message because you are subscribed to the Google Groups "Nitrogen Project / The Nitrogen Web Framework for Erlang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb+unsubscribe@googlegroups.com.

Franklin Brauning

unread,
Mar 6, 2017, 3:29:52 PM3/6/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
Ops:

-extend(rec_1, rec_2, [
    {y, 1000}
]).

y defaults to 1000...

-how do I specify type?
-how do I specify default value and type?


On Saturday, February 4, 2017 at 3:05:42 PM UTC-3, Jesse Gumm wrote:

Jesse Gumm

unread,
Mar 6, 2017, 3:37:24 PM3/6/17
to nitrogenweb

Type has to be a string currently and is the 3rd element of the tuple:

-extend(rec_1, rec_2, [
    {y, 1000, "integer()"}
]).

Default value is always the 2nd argument, and if a normally-defined record doesn't have a default value, the default value becomes 'undefined', so if you wanted to specify a type without the default value, you would do:

-extend(rec_1, rec_2, [
    {y, undefined, "some_type()"}
]).

Make sense?

-Jesse

--
You received this message because you are subscribed to the Google Groups "Nitrogen Project / The Nitrogen Web Framework for Erlang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb+unsubscribe@googlegroups.com.
To post to this group, send email to nitro...@googlegroups.com.
Visit this group at https://groups.google.com/group/nitrogenweb.
For more options, visit https://groups.google.com/d/optout.

Franklin Brauning

unread,
Mar 6, 2017, 5:07:17 PM3/6/17
to Nitrogen Project / The Nitrogen Web Framework for Erlang
simple enough. Ideally one would like the very same record syntax but
at least it respects the order of the syntax "attribute = defaultvalue :: type"
To unsubscribe from this group and stop receiving emails from it, send an email to nitrogenweb...@googlegroups.com.

To post to this group, send email to nitro...@googlegroups.com.
Visit this group at https://groups.google.com/group/nitrogenweb.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages