Phoenix View Extension

The post belongs to NectarCommerce and Extension Framework Awareness Series

  1. NectarCommerce Vision
  2. Extension Framework Game Plan
  3. Introduction to Metaprogramming
  4. Ecto Model Schema Extension
  5. Ecto Model Support Functions Extension
  6. Phoenix Router Extension
  7. Phoenix View Extension
  8. Running Multiple Elixir Apps Together
  9. Extension Approach Explained
  10. Learning from failures: First Experiment at NectarCommerce Extension Approach
  11. Developing NectarCommerce Extensions
  12. Building an exrm release including NectarCommerce

What will be NectarCommerce

Off-the-shelf Opensource E-commerce application for building an online store.

Provides an Extension Framework to support features not included in core as extensions.

Strives for unobtrusive parallel development of NectarCommerce and Extensions

NectarCommerce is committed to providing a ready-to-use e-commerce solution but the definition of 100% is different under different business domains. It aims to solve common use-cases as part of the project and relying on extension framework to tap the rest.

Phoenix View Extension

Why

We want to allow Extensions to provide alternate view for an existing view in Nectar or provide an alternate template path for all views without changing the Nectar Views.

How

Note: Please refer Introduction to Metaprogramming for more information on Metaprogramming in Elixir

Alternate View Templates Path than Default for all templates override

To fully understand the changes below, please refer Phoenix View Implementation

As per the reference above, we simply defined a function which adds the ability to check custom path for templates and if not found will fallback to default view paths :)

Only Few Template Overrides than all

Note: This section would be very similar to Ecto Model Support Functions Extension, so only complete code-snippets are shown and not incremental walkthrough

Check the library code, service code and consumer code as used with Nectar Wallet Extension

Library Code

Service Code

Consumer Code

Partial Override from Extension


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 !!


Phoenix Router Extension

The post belongs to NectarCommerce and Extension Framework Awareness

  1. NectarCommerce Vision
  2. Extension Framework Game Plan
  3. Introduction to Metaprogramming
  4. Ecto Model Schema Extension
  5. Ecto Model Support Functions Extension
  6. Phoenix Router Extension
  7. Phoenix View Extension
  8. Running Multiple Elixir Apps Together
  9. Extension Approach Explained
  10. Learning from failures: First Experiment at NectarCommerce Extension Approach
  11. Developing NectarCommerce Extensions
  12. Building an exrm release including NectarCommerce

What will be NectarCommerce

Off-the-shelf Opensource E-commerce application for building an online store.

Provides an Extension Framework to support features not included in core as extensions.

Strives for unobtrusive parallel development of NectarCommerce and Extensions

NectarCommerce is committed to providing a ready-to-use e-commerce solution but the definition of 100% is different under different business domains.It aims to solve common use-cases as part of the project and relying on extension framework to tap the rest.

Phoenix Router Extension

Note: This blog post is very similar to Ecto Model Schema Extension, so if you are already familiar with it, you can jump straight to the final version

Why

We want to allow Extensions to add routes into Nectar Router without modifying the Nectar Router source.

How

Minimum three parts are needed to create & use an extension effectively:

  • Library Code
  • Service Code
  • Consumer Code

An extension and its use with Nectar can be viewed as Producer / Consumer relationship bound by a communication protocol.

Extension which wants to add a route, say a list of favorites, is a Producer (Service Code).

Nectar Router is a Consumer (Consumer Code) allowing the route additions through a communication protocol (Library Code)

Let’s begin the journey of incremental changes to bring consumer, service and library code into existence starting from a simple use-case of adding a route for showing favorites.

Note: Please refer Introduction to Metaprogramming for more information on Metaprogramming in Elixir

  1. A straightforward way to add favorites route in Nectar.Router would be adding it directly in Nectar.Router, see full version here, but it requires change in Nectar source. Let’s move to next step for avoiding any modification to Nectar.Router

  2. We can add a function to Nectar to which other extensions can delegate the responsibility of route additions, see full version here. See Nectar.ExtendRouter example below on how to use it

  3. Now, with delegation function mount in place, we can work towards providing a way to register the routes to be added, see full version here. Please check the usage of Module attributes for same below.

  4. Earlier, Module.put_attribute need to be used multiple times to define multiple routes whereas now we wrapped it in an anonymous function to encapsulate the collection of routes through a simple and consistent interface, see full version here. There can be multiple extensions used for different functionality and hence multiple routes need to be registered and defined

  5. Now, Nectar.ExtendRouter is getting cluttered with ancillary method definitions. Let’s move it out to another module and use it, see full version here

  6. Let’s further reduce the boilerplate of registering defined_routes module attribute and importing define_route method definition with using callback, see full version here

  7. Reference of defined_routes Module attribute is scattered across Nectar.RouterExtender and Nectar.RouterExtension so let’s move it out to Nectar.RouterExtension to consolidate the usage via __before_compile__ and definition together, see full version here

  8. With above changes, it’s now possible to define routes any number of times, see full version here. Also, routes can now be added using define_route in Nectar.ExtendRouter without making any changes to Nectar.Router.

Check all the revisions at once, here

 Now, in the final version, you can easily find the three components, consumer, service and library code, as desired in extensible system

Please refer the demonstration approach for library code, service code and consumer code as used with favorite products extension

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 !!


Ecto Model Support Functions Extension

The post belongs to NectarCommerce and Extension Framework Awareness Series

  1. NectarCommerce Vision
  2. Extension Framework Game Plan
  3. Introduction to Metaprogramming
  4. Ecto Model Schema Extension
  5. Ecto Model Support Functions Extension
  6. Phoenix Router Extension
  7. Phoenix View Extension
  8. Running Multiple Elixir Apps Together
  9. Extension Approach Explained
  10. Learning from failures: First Experiment at NectarCommerce Extension Approach
  11. Developing NectarCommerce Extensions
  12. Building an exrm release including NectarCommerce

What will be NectarCommerce

Off-the-shelf Opensource E-commerce application for building an online store.

Provides an Extension Framework to support features not included in core as extensions.

Strives for unobtrusive parallel development of NectarCommerce and Extensions

NectarCommerce is committed to providing a ready-to-use e-commerce solution but the definition of 100% is different under different business domains. It aims to solve common use-cases as part of the project and relying on extension framework to tap the rest.

Ecto Model Support Functions Extension

Note: This blog post is very similar to Ecto Model Schema Extension, so if you are already familiar with it, you can jump straight to the final version

Why

We want to allow Extensions to add support functions to existing Nectar Models without changing the Nectar Models.

How

Minimum three parts are needed to create & use an extension effectively:

  • Library Code
  • Service Code
  • Consumer Code

An extension and its use with Nectar can be viewed as Producer / Consumer relationship bound by a communication protocol.

Extension which wants to add a function, say fn_from_outside to Nectar Product Model, is a Producer (Service Code).

Nectar Model is a Consumer (Consumer Code) allowing the new function additions through a communication protocol (Library Code)

Let’s begin the journey of incremental changes to bring consumer, service and library code into existence starting from a simple use-case of adding a function, say fn_from_outside, to Nectar Product.

Note: Please refer Introduction to Metaprogramming for more information on Metaprogramming in Elixir

  1. A straightforward way to add a function, say fn_from_outside, to Nectar Product would be adding it directly in Nectar.Product, but it requires change in Nectar source. Let’s move to the next step for avoiding any modification to Nectar.Product

  2. Like, other extensions, adding a function to Nectar Model and importing it would not solve our purpose of calling function as Nectar.Product.fn_from_outside.
    import <Module> makes the <Module> functions available inside the module and can only be called inside Module and not from outside like Nectar.Product.fn_from_outside

    As expected, Elixir is well-aware of the use-case and provides @before_compile <Module> hook to inject functions in Modules as if their own and can be called as Nectar.Product.fn_from_outside, see full version here. See Nectar.ExtendProduct example below on how to use it.


  3. Now, with @before_compile Nectar.ExtendProduct in place, we can work towards providing a way to register the new functions, see full version here. Please check the usage of Module attributes for same below.

  4. Earlier, Module.put_attribute was used multiple times to define many functions whereas now we wrapped it in an anonymous function to encapsulate the collection of function additions through a simple and consistent interface, see full version here. There can be multiple extensions used for different functionality and hence many functions need to be registered and defined

  5. Now, Nectar.ExtendProduct is getting cluttered with ancillary method definitions. Let’s move it out to another module and use it, see full version here

  6. Let’s further reduce the boilerplate of registering method_block module attribute and importing include_method method definition with __using__ callback, see full version here


  7. Reference of method_block Module attribute is scattered across Nectar.ExtendProduct and Nectar.ModelExtension so let’s move it out to Nectar.ModelExtension to consolidate the usage via __before_compile__ and definition together, see full version here

  8. Now, Nectar.ExtendProduct has __using__ macro definition, which can also be moved to Nectar.ModelExtension to just have new method defintions in Nectar.ExtendProduct, see full version here

  9. With above changes, it’s now possible to define any number of functions, see full version here. Also, any number of functions can now be added using include_method in Nectar.ExtendProduct without making any changes to Nectar.Product.

Check all the revisions at once, here

  Now, in the final version, you can easily find the three components, consumer, service and library code, as desired in extensible system

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 !!


Ecto Model Schema Extension

The post belongs to NectarCommerce and Extension Framework Awareness Series

  1. NectarCommerce Vision
  2. Extension Framework Game Plan
  3. Introduction to Metaprogramming
  4. Ecto Model Schema Extension
  5. Ecto Model Support Functions Extension
  6. Phoenix Router Extension
  7. Phoenix View Extension
  8. Running Multiple Elixir Apps Together
  9. Extension Approach Explained
  10. Learning from failures: First Experiment at NectarCommerce Extension Approach
  11. Developing NectarCommerce Extensions
  12. Building an exrm release including NectarCommerce

What will be NectarCommerce

Off-the-shelf Opensource E-commerce application for building an online store.

Provides an Extension Framework to support features not included in core as extensions.

Strives for unobtrusive parallel development of NectarCommerce and Extensions

NectarCommerce is committed to providing a ready-to-use e-commerce solution but the definition of 100% is different under different business domains. It aims to solve common use-cases as part of the project and relying on extension framework to tap the rest.

Ecto Model Schema Extension

Why

We want to allow Extensions to modify the schema of existing Nectar Models without changing the Nectar Models.

Extensions should be able to add new fields and associations to existing models as and when needed.

How

Minimum three parts are needed to create & use an extension effectively:

  • Library Code
  • Service Code
  • Consumer Code

An extension and its use with Nectar can be viewed as Producer / Consumer relationship bound by a communication protocol.

Extension which wants to add a virtual field, say special, to Nectar Product Model Schema is a Producer (Service Code).

Nectar Model is a Consumer (Consumer Code) allowing the schema changes through a communication protocol (Library Code)

Let’s begin the journey of incremental changes to bring consumer, service and library code into existence starting from a simple use-case of adding a virtual boolean field, say special, to Nectar Product.

Note: Please refer Introduction to Metaprogramming for more information on Metaprogramming in Elixir

  1. A straightforward way to add a virtual field, say special, to Nectar Product would be adding it directly in Nectar.Product Schema definition, but it requires change in Nectar source. Let’s move to the next step for avoiding any modification to Nectar.Product

  2. We can add a function to Nectar Model Schema to which other extensions can delegate the responsibility of schema changes, see full version here. See Nectar.ExtendProduct example below on how to use it.

  3. Now, with delegation function extensions in place, we can work towards providing a way to register the schema changes, see full version here. Please check the usage of Module attributes for same below.

  4. Earlier, Module.put_attribute was used multiple times to define multiple schema changes whereas now we wrapped it in an anonymous function to encapsulate the collection of schema changes through a simple and consistent interface, see full version here. There can be multiple extensions used for different functionality and hence multiple schema changes need to be registered and defined

  5. Now, Nectar.ExtendProduct is getting cluttered with ancillary method definitions. Let’s move it out to another module and use it, see full version here

  6. Let’s further reduce the boilerplate of registering schema_changes module attribute and importing add_to_schema method definition with using callback, see full version here

  7. Reference of schema_changes Module attribute is scattered across Nectar.ExtendProduct and Nectar.ModelExtension so let’s move it out to Nectar.ModelExtension to consolidate the usage via __before_compile__ and definition together, see full version here

  8. With above changes, it’s now possible to define schema changes any number of times, see full version here. Also, schema changes can now be added using include_method in Nectar.ExtendProduct without making any changes to Nectar.Product.

Check all the revisions at once, here

Now, in the final version, you can easily find the three components, consumer, service and library code, as desired in extensible system

Please refer the demonstration approach for library code, service code and consumer code as used with favorite products extension

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 !!


Introduction to Metaprogramming

The post belongs to NectarCommerce and Extension Framework Awareness

  1. NectarCommerce Vision
  2. Extension Framework Game Plan
  3. Introduction to Metaprogramming
  4. Ecto Model Schema Extension
  5. Ecto Model Support Functions Extension
  6. Phoenix Router Extension
  7. Phoenix View Extension
  8. Running Multiple Elixir Apps Together
  9. Extension Approach Explained
  10. Learning from failures: First Experiment at NectarCommerce Extension Approach
  11. Developing NectarCommerce Extensions
  12. Building an exrm release including NectarCommerce

What will be NectarCommerce

Off-the-shelf Opensource E-commerce application for building an online store.

Provides an Extension Framework to support features not included in core as extensions.

Strives for unobtrusive parallel development of NectarCommerce and Extensions

NectarCommerce is committed to providing a ready-to-use e-commerce solution but the definition of 100% is different under different business domains. It aims to solve common use-cases as part of the project and relying on extension framework to tap the rest.

Metaprogramming in Elixir

Note: If you are already familiar and experienced with Elixir Metaprogramming, you can jump to last section as it lists Metaprogramming resources
and constructs used in upcoming blogs when creating different extension DSL’s.

Elixir docs are excellent resources to get familiar with Metaprogramming:

Check out Understanding Elixir Macros blog series by @sasajuric

Book Metaprogramming Elixir by @chris_mccord is next step for diving deeper into metaprogramming.

Why another tutorial on an already well-documented metaprogramming topic ?

To revise and refresh something that we would refer time and again when reviewing Model, Router, View extension DSLs

Extension DSLs will be using below constructs to get the job done :)

  • Module.register_attribute/3
    • Registers an attribute. By registering an attribute, a developer is able to customize how Elixir will store and accumulate the attribute values.
  • Module.put_attribute/3
    • Puts an Erlang attribute to the given module with the given key and value
  • @before_compile
    • A hook __before_compile__/1 that will be invoked before the module is compiled
    • It allows us to inject code into the module when its definition is complete
  • __using__ hook
    • Checkout the title Examples for the usage & context and Best practices in link above
    • use ModuleName looks and invokes __using__ macro defined in ModuleName module
  • bind_quoted
    • Find the documentation under title Options for bind_quoted option in the link above
    • By using bind_quoted, we can automatically disable unquoting while still injecting the desired variables into the tree
  • Code.ensure_loaded?/1
    • Ensures the given module is loaded
  • apply/3
    • Invokes the given function from module with the array of arguments args.
  • Macro.escape/1
    • Recursively escapes a value so it can be inserted into a syntax tree

Metaprogramming pattern as used across extensions

Minimum three parts are needed to create & use an extension effectively:

  • Library Code
  • Service Code
  • Consumer Code

An extension and its use with Nectar can be viewed as Producer / Consumer relationship bound by a communication protocol.

Extension which wants to add a route, say a list of favorites, is a Producer (Service Code).

Nectar Router is a Consumer (Consumer Code) allowing the route additions through a communication protocol (Library Code)

  • Library code defines a macro providing a DSL, say define_route
  • Collect all definitions provided using DSL from Service code in Module attribute, say defined_routes
  • Library code defines a before_compile hook to add a macro extracting all collected definitions from module attribute, as module attributes are cleaned up as compilation completes
  • Inject generated code into targeting module Consumer code through __using__ hook as invoked by use ModuleWithDSLImplementation, say use ExtensionsManager.RouterExtension

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 !!


Privacy Preference Center