The post belongs to NectarCommerce and Extension Framework Awareness Series
- NectarCommerce Vision
- Extension Framework Game Plan
- Introduction to Metaprogramming
- Ecto Model Schema Extension
- Ecto Model Support Functions Extension
- Phoenix Router Extension
- Phoenix View Extension
- Running Multiple Elixir Apps Together
- Extension Approach Explained
- Learning from failures: First Experiment at NectarCommerce Extension Approach
- Developing NectarCommerce Extensions
- Building an exrm release including NectarCommerce
Developing NectarCommerce Extensions
Where we left off
In the past few posts we have learned how to write code that extends existing models, routers, added methods to override view rendering and run multiple phoenix application together in the same umbrella project. Letâ€™s Continue to build upon that and write our first extension for NectarCommerce favorite products and ultimately our store based on NectarCommerce.
For our curious readers, we tried another extension approach which had linear dependency (nectar => extensions_manager => extensions) helpful in development but turned out to had the serious limitation of Nectar being unavailable for testing.
To overcome the barrier of testing and pleasant developer workflow, what we need is that Nectar is available and compiled while developing extensions and if an extension is added it should recompile itself to include the new extensions.
We have modified Nectar for this approach to seek for extensions and used a custom compiler step(A story for another time) to mark files for recompilation. Letâ€™s get started and see if we can scale the testing barrier.
A layered guide to NectarCommerce extensions
- Model Layer
- View Layer
- Starting the server
- Creating your store with NectarCommerce
- Suggested Workflow
Create a new phoenix application to hold the favorite products application.
In your shell run inside the umbrella/apps folder:
We could have gone with a regular mix application, but phoenix/ecto will come in handy in this case, since we want to have views to display stuff and a model to store data.
While we are at it letâ€™s configure our dev.exs to use the same db as Nectar, we could write some code and share the db settings between Nectar and our extensions see: running multiple phoenix application together for more details. But now for simplicityâ€™s sake we are just copying the settings from Nectar to get started.
We need to let the extension manager know that this application is an extension for Nectar.
Update the dependencies in extension_manager/mix.exs with the favorite_products dependency.
And now for the big differentiator, we will add Nectar as dependency of the favorite_products extension, effectively ensuring it is compiled before the extension.
We want a NectarCommerce user to have some products to like and a way to remember them in short a join table and with two associations letâ€™s generate them:
Now to point to correct Nectar models. Open up the source and change the associations to from favorite products model to Nectar models. In the end we have a schema like:
Fun Fact: Since we are depending upon Nectar now we can use
Nectar.Web, :modelinstead of
FavoriteProducts.Web, :modelin user_like.ex and make our extensions available for extension.
Of, course this is only the extension view of this relationship, We want the Nectar user to be aware of this relationship and most important of all, we should be able to do something like
Nectar.User.liked_products(user) to fetch the liked products of the user.
Calling our handy macros to perform the dark art of compile time code injection. Letâ€™s create the nectar_extension.ex file in favorite_products/lib/ directory and place this code there:
Donâ€™t forget to update the install file in extensions_manager.
Now we have a user that can like products and product from which we can query what users liked it. Time to play.
From the root of umbrella, run the following to start the shell:
This should trigger another round of compilation. Ultimately loading the extension code into Nectar. Lets see if we were successful. But before doing that we should migrate the database.
Voila, we can now save and retrieve records to a relation we defined outside Nectar from Nectar models.
Now that we can save the user likes, we should probably add an interface for the user to like them as well. Which leads us to the first shortcoming in our current approach, we can replace existing views but right now we donâ€™t have anything for adding to an existing view(Please leave us a note here if you know of a clean performant approach to do this). Meanwhile we expect most people will end up overriding the existing views to something more custom then updating it piecemeal but i digress. For now letâ€™s have a page where we list all the products and user can mark them as liked or unlike the previously liked ones.
Notice how we use the Nectar.Repo itself instead of using the FavoriteProducts.Repo, infact beside migration, we wonâ€™t be utilizing or starting the FavoriteProducts.Repo, which will help us keep the number of connections open to database limited via only the Nectar.Repo
the view file: index.html.eex
In both of the files we refer to routes via NectarRoutes alias instead of favorite products. To get the assets from Nectar add nectar/web/static to the brunch configâ€™s watched folders:
and in app.js, initialize the nectar code:
Finally for adding the route to Nectar, update nectar_extension.ex with the following code:
And add to install.ex the call:
Now we can see the added routes
letâ€™s update the layout as well:
Starting the server to preview the code
In the previous attempt we were directly running the Nectar server, However since we are essentially working from ground up, let us make another change and add a forward from favorite_products to nectar.
All the usual caveats for forwards apply here. Before doing so please ensure that nectar is added to list of applications in favorite_products.ex.
Note: We have disabled the supervisor for Nectar.Endpoint to specifically allow this and suggest all the extensions do this as well once development is complete. More on this later but suffice to say two endpoints cannot start at and we are now running nectar along-with favorite_products extension.
Now we can run our
mix phoenix.server and go about marking our favorites with nectar layout and all.
We are almost done now. To ensure that we know when things break we should add a few tests of-course, we need to make sure that Nectar migrations are run before running the migrations for favorite products and we need the Nectar repo running as well.
for the former letâ€™s update the test_helper.ex with:
And now to write the tests
The tests for code injection:
Create a new test file tests/model/user_test.ex:
We can test for user_like just like any other ecto model. Letâ€™s skip that for now.
Bonus Guide: Creating your store based on NectarCommerce
We already did this, when we were creating favorite_products extension. A forward to Nectar is all it takes. You can create your Phoenix application and add a forward to Nectar.Router to run your user application. Some extensions might require to be added in the application list for their processes to start, in such cases we need to add a dependency here as well. You might want to do that anyway to support exrm release properly(We will be covering this in our next post).
We can now see developing extensions is not very different from building our store with custom functionality based on NectarCommerce. You start with your store and extract out the functionality into self contained applications and load them back as extensions into Nectar.
Our aim with these posts is to start a dialog with the Elixir community on validity and technical soundness of our approach. We would really appreciate your feedback and reviews, and any ideas/suggestions/pull requests for improvements to our current implementation or entirely different and better way to do things to achieve the goals we have set out for NectarCommerce.
Enjoy the Elixir potion !!