Skip to content

Instantly share code, notes, and snippets.

@trabus
Last active April 16, 2018 11:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trabus/6c2ecfbd8e5145e7b328 to your computer and use it in GitHub Desktop.
Save trabus/6c2ecfbd8e5145e7b328 to your computer and use it in GitHub Desktop.
Pods in ember-cli

Overview

Pods are a type of file organization structure in ember-cli. They allow you to gather related modules and assets together in your file structure. Currently pod structured modules can live side by side with your basic modules because the resolver that ships with ember-cli will look up your path in pod structure, and then fall back to basic structure if no paths are resolved.

Soon there will be a point where some blueprints will only generate in pod structure, and others will only generate in basic structure. This will allow us to collect a list of types that support each lookup structure and only need to lookup modules once. This will also cause less ambiguity around what a project structure looks like.

TLDR

My opinion on pods is that they should not live in the same namespace as type based modules. We should re-appropriate 'components',' models', and 'routes' (or 'resources') as pod prefixes to house the concerns of global ui, data, and route specific ui, respectively. Everything else stays the same, so type based modules like 'helpers', 'services', or 'utils' would stay as is. Blueprints should only generate as either a pod based module or a type based module, but not both, as it complicates the scheme and adds ambiguity to what is where in a project.

The need for multiple pod prefixes to separate the concerns is a pattern I've seen coming out of all the pods discussions and requests. To me, that sounds like a cowpath being made, and we should address those concerns somehow.

When engines come into play, they would live in a sibling folder to app, much like lib with in-repo-addons, but would use the same scheme as the app folder.

Definitions

Basic structure

A type based structure, where files are organized by type. Basic files are located in a root folder with the pluralized name of the type of files it contains, and named by the concern they are related to. For instance, a user template file would be contained in app/templates/user.hbs, and a user route would be in app/routes/user.js.

Pod structure

A concern based structure, where files are organized by what they are related to. Pod files are located in a folder named with the name of the concern it relates to, and named by the type of file they are. For instance, all files relating to a user are contained in app/user folder. So a user template would be app/user/template.hbs and route would be app/user/route.js.

Discoverability

There is still need for modules in basic structure. Certain file types lose discoverability when in pod structure. Helpers, initializers, mixins, models, services, and utils all make more sense in basic structure. When these file types in pod structure, they are indistinguishable from pods that represent routes and their associated assets.

Naming and Path Resolution

The term "pod" almost describes a naming convention moreso than the structure a module is held in. The difference with a pod is that it has a prefix to define where it should belong. The prefix is necessary to prevent naming collisions with pods using ember types as their name (app/services would be a common case). If you were to break down a structure by its parts, you would see a path is made up of four parts:

  • root: The root folder, generally app can be addon as well
  • prefix: The grouping namespace for a pod file, like pods
  • name: The name of the file including nesting paths, like taco or users/edit/edit-profile
  • type: The type of the file, like component or route

To understand this breakdown, let's look at a few files in the current basic and pod structures.

Default

basic app/routes/taco.js

root prefix type name
app - route taco

pod app/taco/route.js

root prefix type name
app - route taco

Note how the type and the filename just swap positions between structures (with different plurarization). Let's look at the same pod generated in an environment with a podModulePrefix defined.

pod app/pods/taco/route.js

root prefix type name
app pods route taco

The prefix is the second part of the podModulePrefix value in the environment config (sans the app-name). All examples after this should be assumed to have a podModulePrefix defined.

Components

Components are a special case because they have a secondary prefix in pod structure. It could be argued that the components part of the prefix is actually part of the namespace, but I feel that it's more like a prefix due to the way they are looked up.

basic app/components/taco-bar.js

root prefix type name
app - component foo-bar

pod app/pods/components/taco-bar/component.js

root prefix type name
app pods/components component taco-bar

Nested Components

Nested components are essentially the same, with additonal entities in the namespace. The first element of the namespace

basic app/components/taco/bar/taco-bar.js

root prefix type name
app - component taco/bar/taco-bar

pod app/pods/components/taco/bar/taco-bar/component.js

root prefix type name
app pods/components component taco/bar/taco-bar

Addons and root

The root comes into play when developing in an addon.

addon/components/taco-bar.js

root prefix type name
addon - component taco-bar

app/components/taco-bar.js

root prefix type name
app - component taco-bar

Blueprints

Currently most blueprints support both pod and basic structure. For pods to become the default, we will need to remove that flexibility and force blueprint types to only generate in the one structure they support. Helpers, initializers, mixins, models, services, and utils all will still generate in basic structure. Blueprints contain a files folder, which contains a template of the files to be generated by the blueprint. The file paths contain tokens (indicated by __ surrounding the name of the token) which are replaced when the blueprint is generated.

A blueprint's files folder structure can be used to implicitly indicate whether a blueprint supports a structure or not (via a test to check for the existence of necessary tokens to support the structure). For example:

routes

// current: can be pod or basic structure
files/app/__path__/__name__.js 

// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/route.js 

services

// current: basic structure only
files/app/services/__name__.js 

// proposed: basic structure only
files/__root__/services/__name__.js 

components

// current: can be pod or basic structure
files/__root__/__path__/__name__.js 

// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/component.js 

models

// current: can be pod or basic structure
files/app/__path__/__name__.js 

// proposed: pod only, can go in shared or routes namespace
files/__root__/__prefix__/__path__/model.js 

Regarding the blueprints for shared vs route namespaces, the default blueprint will be route namespaced. The shared blueprint will simply re-use the default blueprint and override the __prefix__ token value to be in the shared namespace.

PodModulePrefix

I think that a single podModulePrefix is too constrictive for ember-cli projects. I propose that we should provide more than one namespace for files in pod structure, as the structure can apply to more than one concern.

I believe that pods make sense for the following concerns:

  • shared ui (components + templates)
  • route specific ui (components + templates)
  • shared models
  • route specific models

All of these concerns can be separated and still support pod structure. We just have to provide namespaces for them that are standardized and make the resolver aware of them.

This issue may be solved by using engines as namespaces, but not enough is known about how engines will work yet to be sure.

Structures

Structures are key to providing a way to describe multiple pod prefixes/namespaces, as well as the ability to move files between them. A structure is a simple construct that:

  • describes file path tokens it is aware of (__root__, __path__, __name__)
  • provides a prefix (if any)
  • provides a way to test if a blueprint is supported by the structure
  • provides the resolver pattern it conforms to (type or pod based lookup)

Structures will identify how to generate modules in a project, they will specify what resolver lookup pattern they conform to, but the resolver will not need to be aware of the structures.

@locks
Copy link

locks commented Mar 23, 2015

/app
  /models
    /person
      model.js
      adapter.js
      serializer.js

  /resources
    /person
      route.js
      flubarb.js
      template.hbs

  /components
    /avatar
      component.js
      template.hs

  /mixins

  /etc

/engines
  /admin
    /app
      /models
      /resources
      /components

/lib

@trabus
Copy link
Author

trabus commented Mar 23, 2015

For reference on the resolver:
this is the original issue that brought about podModulePrefix
ember-cli/ember-resolver#16
this is the original issue that brought about podModulePrefix/components:
ember-cli/ember-resolver#30

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment