Talk given by Chris McCuller, Associate Software Engineer at Salesforce, at GEMConf Triangle in August 2016
Did you know you can ember g an ember g? Mind blown? Take a wading pool splash into how to use Ember's blueprints to streamline common development tasks.
1. blueprints git:(master)ember g awesome
Ember-CLI Blueprints
for fun and profit
version: 2.6.2
installing awesome
create app/awesome/awesome.js
installing awesome-test
create tests/unit/awesome/awesome-test.js
Chris McCuller
@ultimatemonty
2. Who is this fella?
Software Engineer at Pardot
Pardot is B2B marketing automation by
Salesforce, a software-as-a-service application
built for marketing and sales departments.
Based in Atlanta, GA
3. Other boring details
● Disc sports enthusiast
● Atlanta sports enthusiast
● Games-where-you-throw-things-at-things
enthusiast
● Hat enthusiast
6. What are Blueprints?
Ember CLI ships with “Blueprints”, snippet generators for
many of the entities - models, controllers, components,
and so on - that you’ll need in your app. Blueprints allow
us to share common Ember patterns in the community
and you can even define your own.
ember g --help
7. I can define my own Blueprints?
blueprints/thing
├── files
│ └── .gitkeep
└── index.js
ember g thing a-thing
8. Anatomy of the `g` command
ember g thing aThing propKey:propValue --
someOption=true
● thing: name of the blueprint to run
route, component, acceptance-test, etc...
● aThing propKey:propValue: anonymousOptions. No option key
needed. Accessible via options.entity.options
{ aThing: ‘’, propKey: ‘propValue’ }
● --someOption=true: availableOptions. Option key needed
Accessible via options.taskOptions
9. Anatomy of a Blueprint
blueprints/blueprint-name/index.js
● Set filenames
● Set template variables
● Installation/Uninstallation tasks
blueprints/blueprint-name/files/
● Templates for all files to be installed in the target
directory
● fileMapTokens for filenames
● locals for template variables
10. fileMapTokens (4 by default)
● __name__: dasherized name of the thing being generated e.g.
mirage-model
● __root__: root of the application
○ app/ folder for normal blueprints
addon/ for addon blueprints)
● __path__: substitued with blueprint name at generation. Generally used
with addons
● __test__: only used in pods. Appends -test the end of __name__
11. locals (5 by default)
● dasherizedPackageName: dasherized name of project e.g. tomster-
tracker
● classifiedPackageName: classified name of project e.g.
TomsterTracker
● dasherizedModuleName: dasherized name of thing generated e.g.
anchor-tag
● classifiedModuleName: classified name of thing being generated e.g.
AnchorTag
● camelizedModuleName: camelized name of thing being generated e.g.
anchorTag
12. index.js
Hooks:
● normalizeEntityName: normalize and validate entity name
● locals: setup template variables
● fileMapTokens: setup file names
● install: installs things. rarely overridden
● beforeInstall
● afterInstall
● beforeUninstall
● afterUninstall
Where the logic of your blueprint lives
13. index.js
Passing in options:
● anonymousOptions: options that are not preceded by --
optionName
ember g model user firstName:string lastName:string
● availableOptions: Explicitly defined options that are preceded by
--optionName
ember g component anchor-tag --test-type=unit
ember g component anchor-tag -u
Where the logic of your blueprint lives
14. index.js: The `options` hash
fileMapTokenslocals
ember g model user name:string isAdmin:boolean
{ entity: {
name: ‘user’,
options: {
name: ‘string’,
isAdmin: ‘boolean’
}
}
}
// other useful stuff
{ pod: false,
podPath: '',
hasPathToken: null,
inAddon: false,
inRepoAddon: null,
inDummy: false,
blueprintName: ‘model’,
originBlueprintName: ‘model’,
dasherizedModuleName: ‘user’,
locals: {
<entity.options from locals>
}
15. Let’s take a look
at one!
Please to make
offerings of praise to
demo gods on my
behalf.
16. Goals
● Generate a route and a component in the same command
● Use generated component in the route template
● Options for destructuring namespaces and providing a
name for the model property on the component
Not only can friendship and camraderie be forged over a casual game of horseshoes. So can world peace.
The TLDR is that blueprints are a bundle of template files with optional install logic
44 total blueprints are shipped with Ember
Ember-CLI comes with 10 default blueprints
Ember comes with 26 default blueprints
Ember-Data comes with 8 default blueprints
Can be used in-app or for addons
Blueprints tend to have a very narrow use case. The most common one is in addons where the addon author might want to provide the ability to easily generate addon related files. They are also used to install bower dependencies if needed for an addon. Mirage is a good example of this - it provides blueprints for mirage models, serializers, fixtures, and factories. In a broader sense, blueprints are useful when you are repeatedly creating a thing, usually made up of multiple files, that is defined by the values of it’s properties. Components are a great example. The default component blueprint creates 1 JS file, 1 HBS file, and 1 test.
The point of this talk isn’t to convince you that blueprints are amazing and you should go create some in your app right now. Most Ember developers don’t have a good use case for blueprints because Ember ships with so many useful ones
Fun fact: if we were to add an `thing-test` blueprint along side the `thing` blueprint for generating tests ember-cli would automatically run it when running the `thing` blueprint. The `-test` suffix also changes some behavior of some of the default tokens provided by ember-cli.
You can have as many anonymous options as you want.
They will always be keyed by key:value. Key required, value is not.
Name is always the first anonymous option. aThing in this example
availableOptions can have aliases ala component-test --test-type=[‘integration’,’unit’] or -i or -u
fileMapTokens and locals both have predefined variables that can be accessed via the options hash.
fileMapTokens:
__name__: dasherized name of the thing being generated
__root__: root of the application (app/ folder for normal blueprints, addon/ for addon blueprints)
__path__: substitued with blueprint name at generation. Generally used with addons
__test__: only used in pod scenarios
Locals (template variables):
dasherizedPackageName - dasherized name of project e.g. tomster-tracker
classifiedPackageName - classified name of project e.g. TomsterTracker
dasherizedModuleName - dasherized name of thing generated e.g. anchor-tag
classifiedModuleName - classified name of thing being generated e.g. AnchorTag
camelizedModuleName - camelized name of thing being generated e.g. anchorTag
Run order is:
normalizeEntityName
locals
fileMapTokens
beforeInstall/unInstall
install
afterInstall/uninstall
NormalizeEntityName by default checks to see if an entityName is specified and doesn’t contain a trailing slash.
A good example of customization is the component blueprint. normalizeEntityName checks the entityName has a dash in it and throws an error otherwise.
Anonymous options are defined as key:value and you can have as many of them as you like
Available options require the --optionKey=value format. The can be loosely typed and can have aliases
The options object is the lone argument for locals, fileMapTokens, and install. This is not the same as options for the blueprint.
Some other potentially useful props on the locals options hash:
Ui: gives you the ability to interact with the UI (write line to console, etc…)
Project: access to project level properties like project path, installed addons, etc…
taskOptions: access to pre-defined options in the `availableOptions` array
Talk about the purpose of this blueprint:
A common pattern is to have a top-level route component that receives the model from the route and contains all the logic you might normally put into a controller. This has the benefit of helping you get rid of controllers (unless you need query params) and also making your app a little easier to follow with one less layer of things to deal with.
Currently this has to be done manually by first generating the route, then the component.
Particularly interesting blueprints for further exploration:
Ember’s route blueprint (modifies the router.js file along with generating the correct files)
Ember’s resource blueprint (overrides install hook)
Ember’s component blueprint (good example of availableOptions and alias use)
Ember-Data’s model blueprint (good example of anonymousOptions use).