power_stencil 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +198 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/update_plugin_template +79 -0
- data/doc/builds.md +267 -0
- data/doc/entities.md +964 -0
- data/doc/example_use_cases.md +59 -0
- data/doc/images/power-stencil-entity-build.svg +481 -0
- data/doc/images/power-stencil-entity-creation.svg +375 -0
- data/doc/images/power-stencil-simple-flow.svg +265 -0
- data/doc/plugins.md +169 -0
- data/doc/templates.md +333 -0
- data/etc/base_commands_definition.yml +259 -0
- data/etc/meta_templates/plugin_seed/etc/command_line.yaml +12 -0
- data/etc/meta_templates/plugin_seed/etc/plugin_config.yaml +4 -0
- data/etc/meta_templates/plugin_seed/etc/templates/.git_keep +0 -0
- data/etc/meta_templates/plugin_seed/lib/{entity}.rb.erb +34 -0
- data/etc/meta_templates/plugin_seed/lib/{entity}/entity_definitions/{entity}_entity.rb.erb +11 -0
- data/etc/meta_templates/plugin_seed/lib/{entity}/plugin_helper.rb.erb +17 -0
- data/etc/meta_templates/plugin_seed/lib/{entity}/version.rb.erb +3 -0
- data/etc/meta_templates/plugin_seed/lib/{entity}/{entity}_processor.rb.erb +19 -0
- data/etc/meta_templates/plugin_seed/psplugin_{entity}.gemspec +36 -0
- data/etc/power_stencil.yaml +92 -0
- data/etc/templates/plugin_definition/.gitignore +11 -0
- data/etc/templates/plugin_definition/.rspec +3 -0
- data/etc/templates/plugin_definition/.travis.yml +5 -0
- data/etc/templates/plugin_definition/CODE_OF_CONDUCT.md +74 -0
- data/etc/templates/plugin_definition/Gemfile +6 -0
- data/etc/templates/plugin_definition/LICENSE.txt +21 -0
- data/etc/templates/plugin_definition/README.md +43 -0
- data/etc/templates/plugin_definition/Rakefile +6 -0
- data/etc/templates/plugin_definition/bin/console +14 -0
- data/etc/templates/plugin_definition/bin/setup +8 -0
- data/etc/templates/plugin_definition/etc/command_line.yaml +12 -0
- data/etc/templates/plugin_definition/etc/plugin_config.yaml +4 -0
- data/etc/templates/plugin_definition/etc/templates/.git_keep +1 -0
- data/etc/templates/plugin_definition/lib/{entity}.rb.erb +34 -0
- data/etc/templates/plugin_definition/lib/{entity}/entity_definitions/{entity}_entity.rb.erb +11 -0
- data/etc/templates/plugin_definition/lib/{entity}/plugin_helper.rb.erb +17 -0
- data/etc/templates/plugin_definition/lib/{entity}/version.rb.erb +3 -0
- data/etc/templates/plugin_definition/lib/{entity}/{entity}_processor.rb.erb +19 -0
- data/etc/templates/plugin_definition/psplugin_{entity}.gemspec +36 -0
- data/etc/templates/plugin_definition/spec/spec_helper.rb +14 -0
- data/etc/templates/plugin_definition/spec/{entity}_spec.rb +9 -0
- data/etc/templates/project/.copy_ignore +2 -0
- data/etc/templates/project/.gitignore.erb +6 -0
- data/etc/templates/project/.ps_project/entities/.gitkeep +0 -0
- data/etc/templates/project/.ps_project/entities/README.md +11 -0
- data/etc/templates/project/.ps_project/entity_definitions/.gitkeep +0 -0
- data/etc/templates/project/.ps_project/entity_definitions/README.md +15 -0
- data/etc/templates/project/.ps_project/personal-config.yaml +8 -0
- data/etc/templates/project/.ps_project/plugins/.gitkeep +0 -0
- data/etc/templates/project/.ps_project/templates-templates/.gitkeep +0 -0
- data/etc/templates/project/.ps_project/templates-templates/README.md +13 -0
- data/etc/templates/project/.ps_project/user_entities/.gitkeep +0 -0
- data/etc/templates/project/.ps_project/user_entities/README.md +12 -0
- data/etc/templates/project/.ps_project/versioned-config.yaml +6 -0
- data/etc/templates/simple_exec/main.sh +4 -0
- data/exe/power_stencil +28 -0
- data/lib/power_stencil.rb +53 -0
- data/lib/power_stencil/command_processors/build.rb +43 -0
- data/lib/power_stencil/command_processors/check.rb +35 -0
- data/lib/power_stencil/command_processors/create.rb +70 -0
- data/lib/power_stencil/command_processors/delete.rb +38 -0
- data/lib/power_stencil/command_processors/edit.rb +35 -0
- data/lib/power_stencil/command_processors/entity_helper.rb +105 -0
- data/lib/power_stencil/command_processors/get.rb +22 -0
- data/lib/power_stencil/command_processors/info.rb +41 -0
- data/lib/power_stencil/command_processors/init.rb +47 -0
- data/lib/power_stencil/command_processors/new_plugin.rb +31 -0
- data/lib/power_stencil/command_processors/root.rb +27 -0
- data/lib/power_stencil/command_processors/shell.rb +37 -0
- data/lib/power_stencil/command_processors/trace_helper.rb +20 -0
- data/lib/power_stencil/dsl/base.rb +24 -0
- data/lib/power_stencil/dsl/entities.rb +46 -0
- data/lib/power_stencil/dsl/plugin_generation.rb +17 -0
- data/lib/power_stencil/engine/base.rb +50 -0
- data/lib/power_stencil/engine/build_handling.rb +77 -0
- data/lib/power_stencil/engine/directory_processor.rb +111 -0
- data/lib/power_stencil/engine/entities_definitions.rb +42 -0
- data/lib/power_stencil/engine/entities_handling.rb +76 -0
- data/lib/power_stencil/engine/entity_engine.rb +20 -0
- data/lib/power_stencil/engine/init_engine.rb +18 -0
- data/lib/power_stencil/engine/project_engine.rb +75 -0
- data/lib/power_stencil/engine/renderers/erb.rb +21 -0
- data/lib/power_stencil/error.rb +20 -0
- data/lib/power_stencil/initializer.rb +87 -0
- data/lib/power_stencil/plugins/base.rb +54 -0
- data/lib/power_stencil/plugins/capabilities.rb +30 -0
- data/lib/power_stencil/plugins/command_line.rb +27 -0
- data/lib/power_stencil/plugins/config.rb +32 -0
- data/lib/power_stencil/plugins/dependencies.rb +32 -0
- data/lib/power_stencil/plugins/gem.rb +57 -0
- data/lib/power_stencil/plugins/require.rb +77 -0
- data/lib/power_stencil/plugins/templates.rb +21 -0
- data/lib/power_stencil/project/base.rb +79 -0
- data/lib/power_stencil/project/config.rb +54 -0
- data/lib/power_stencil/project/create.rb +30 -0
- data/lib/power_stencil/project/info.rb +72 -0
- data/lib/power_stencil/project/paths.rb +119 -0
- data/lib/power_stencil/project/plugins.rb +89 -0
- data/lib/power_stencil/project/proxy.rb +13 -0
- data/lib/power_stencil/project/templates.rb +56 -0
- data/lib/power_stencil/project/versioning.rb +29 -0
- data/lib/power_stencil/system_entity_definitions/all.rb +14 -0
- data/lib/power_stencil/system_entity_definitions/buildable.rb +23 -0
- data/lib/power_stencil/system_entity_definitions/entity_override.rb +8 -0
- data/lib/power_stencil/system_entity_definitions/entity_project_common.rb +33 -0
- data/lib/power_stencil/system_entity_definitions/has_associated_files.rb +13 -0
- data/lib/power_stencil/system_entity_definitions/non_persistent.rb +14 -0
- data/lib/power_stencil/system_entity_definitions/plugin.rb +17 -0
- data/lib/power_stencil/system_entity_definitions/process_descriptor.rb +15 -0
- data/lib/power_stencil/system_entity_definitions/project_config.rb +24 -0
- data/lib/power_stencil/system_entity_definitions/project_entity.rb +16 -0
- data/lib/power_stencil/system_entity_definitions/simple_exec.rb +47 -0
- data/lib/power_stencil/utils/directory_processor.rb +54 -0
- data/lib/power_stencil/utils/file_edit.rb +87 -0
- data/lib/power_stencil/utils/file_helper.rb +56 -0
- data/lib/power_stencil/utils/gem_utils.rb +13 -0
- data/lib/power_stencil/utils/secure_require.rb +26 -0
- data/lib/power_stencil/utils/semantic_version.rb +128 -0
- data/lib/power_stencil/version.rb +3 -0
- data/power_stencil.gemspec +32 -0
- metadata +287 -0
data/doc/builds.md
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
Builds
|
2
|
+
======
|
3
|
+
|
4
|
+
<!-- TOC -->
|
5
|
+
|
6
|
+
- [Overview of the build process](#overview-of-the-build-process)
|
7
|
+
- [The "_compilation_" process](#the-_compilation_-process)
|
8
|
+
- [Extending an entity](#extending-an-entity)
|
9
|
+
- [Overriding entities and build scenarii](#overriding-entities-and-build-scenarii)
|
10
|
+
- [The "_detemplatization_" process](#the-_detemplatization_-process)
|
11
|
+
- [Post-process actions](#post-process-actions)
|
12
|
+
|
13
|
+
<!-- /TOC -->
|
14
|
+
[:back:][Documentation root]
|
15
|
+
|
16
|
+
|
17
|
+
# Overview of the build process
|
18
|
+
|
19
|
+
|
20
|
+
You build a [buildable] entity by issuing `power_stencil build <entity_type>/<entity_name>`. If you try to build an entity which is not buildable, the process fail with an error message.
|
21
|
+
|
22
|
+
Let's describe a bit more in detail what this build process is:
|
23
|
+
|
24
|
+
![entity-build-flow]
|
25
|
+
|
26
|
+
Humm :thinking:... What is this _compilation_ step that appears on this schema ??! I thought Ruby was an interpreted language...
|
27
|
+
|
28
|
+
I guess you understood this is not a "real" _compilation_ process we are discussing about here, like in the sense of compiling some Java or C code.
|
29
|
+
|
30
|
+
# The "_compilation_" process
|
31
|
+
|
32
|
+
The mechanism of the compilation is coming from the [universe_compiler] Gem, but the two following features are the ones you really need to understand.
|
33
|
+
|
34
|
+
:information_source: The compilation process can be applied to any entity, ie it doesn't need to be [buildable] (see in next paragraph how to run the compilation). But an entity needs to be [buildable] to use `power_stencil build` which corresponds actually to _compilation_ + _detemplatization_.
|
35
|
+
|
36
|
+
## Extending an entity
|
37
|
+
|
38
|
+
We could describe this mechanism as inheritance at entity level (not entity type !), said differently this a way for an entity to _inherit_ (or _extend_ in the `PowerStencil` paradigm) the content of another entity.
|
39
|
+
|
40
|
+
Let's try this with two `base_entities`.
|
41
|
+
|
42
|
+
First let's define a root entity to inherit from:
|
43
|
+
|
44
|
+
$ power_stencil create base_entity/root_entity --edit
|
45
|
+
|
46
|
+
and set the folloging content:
|
47
|
+
|
48
|
+
```yaml
|
49
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
50
|
+
:name: root_entity
|
51
|
+
:property_root: value in root_entity
|
52
|
+
:array_root:
|
53
|
+
- value A
|
54
|
+
- value B
|
55
|
+
:hash_root:
|
56
|
+
item1: value 1
|
57
|
+
item2: value 2
|
58
|
+
```
|
59
|
+
|
60
|
+
Now let's create a second entity to extend this one:
|
61
|
+
|
62
|
+
$ power_stencil create base_entity/inherited_entity --edit
|
63
|
+
|
64
|
+
And define its content as:
|
65
|
+
|
66
|
+
```yaml
|
67
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
68
|
+
:name: inherited_entity
|
69
|
+
:a_specific_property: This one only exists in inherited entity
|
70
|
+
:extends: !psref
|
71
|
+
type: :base_entity
|
72
|
+
name: root_entity
|
73
|
+
:array_root:
|
74
|
+
- value from inherited_entity
|
75
|
+
:hash_root:
|
76
|
+
item2: value2 but from inherited
|
77
|
+
item3: value3
|
78
|
+
```
|
79
|
+
|
80
|
+
Once done, if we do a `power_stencil get base_entity/inherited_entity --raw` this exactly what will get. But `PowerStencil` gives a possibility to see how this entity will look like after the compilation process using the `--compiled` option:
|
81
|
+
|
82
|
+
So if you do
|
83
|
+
|
84
|
+
$ power_stencil get base_entity/inherited_entity --raw --compiled
|
85
|
+
|
86
|
+
This is what you get:
|
87
|
+
|
88
|
+
```yaml
|
89
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
90
|
+
:name: inherited_entity
|
91
|
+
:a_specific_property: This one only exists in inherited entity
|
92
|
+
:property_root: value in root_entity
|
93
|
+
:array_root:
|
94
|
+
- value from inherited_entity
|
95
|
+
:hash_root:
|
96
|
+
item1: value 1
|
97
|
+
item2: value2 but from inherited
|
98
|
+
item3: value3
|
99
|
+
:extends: !psref
|
100
|
+
type: :base_entity
|
101
|
+
name: root_entity
|
102
|
+
```
|
103
|
+
|
104
|
+
You can notice that this second object looks now like a merged entity. Here are the rules of the merge:
|
105
|
+
|
106
|
+
- the entity extending another one has always precedence, meaning that if a property is defined in both, only the latter is kept.
|
107
|
+
- hashes are treated a bit differently as a real merge of the two hashes is performed (which is not the case of arrays or simple properties).
|
108
|
+
- the `extends` property is kept even after compilation for reference (meaning that in a template you can still access values of the entity you are extending).
|
109
|
+
|
110
|
+
:warning: **The entity you extend has to be of the same entity type** or the compilation will fail (actually unless you created the entity yaml file manually, the framework won't even let you save it if the entity inheritance is invalid) !
|
111
|
+
|
112
|
+
## Overriding entities and build scenarii
|
113
|
+
|
114
|
+
You may have noticed from the output of `power_stencil info` that there is a type of entity named `entity_override`.
|
115
|
+
|
116
|
+
This is a very specific type of entity linked to the compilation process.
|
117
|
+
|
118
|
+
The goal of this specific entity is to provide a mechanism to override some of the properties of other entities in the scope of a _build scenario_. This is really a feature turned towards testing possibilities, by it for developers or ops, and as such generally `entity_override` are often created as [local entities] (although they really could be created as standard versioned entities).
|
119
|
+
|
120
|
+
Let's create our first override:
|
121
|
+
|
122
|
+
$ power_stencil create entity_override/test_override --edit --user-storage
|
123
|
+
|
124
|
+
And define its content:
|
125
|
+
|
126
|
+
```yaml
|
127
|
+
--- !ruby/object:UniverseCompiler::Entity::Override
|
128
|
+
:overrides:
|
129
|
+
- !psref
|
130
|
+
type: :base_entity
|
131
|
+
name: inherited_entity
|
132
|
+
:name: test_override
|
133
|
+
:property_from_overide: foo
|
134
|
+
:scenario: dev_tests
|
135
|
+
:hash_root:
|
136
|
+
item3: overridden value
|
137
|
+
```
|
138
|
+
:information_source: First thing to notice is that an `entity_override` is **able to override properties of multiples entities at once** ! Hence the fact the `overrides` property is an array.
|
139
|
+
|
140
|
+
:information_source: **There is no constraint on the entity types this override applies to** (as opposed to the extend mechanism)
|
141
|
+
|
142
|
+
The `scenario` property specifies the build scenario this override will be applied for.
|
143
|
+
|
144
|
+
:information_source: You can define as many `entity_overrides` as you want for a specific build scenario.
|
145
|
+
|
146
|
+
Ok, then what happens to `base_entity/inherited_entity` ?
|
147
|
+
|
148
|
+
As expected, without specifying a build scenario nothing changes
|
149
|
+
|
150
|
+
$ power_stencil get base_entity/inherited_entity --raw --compiled
|
151
|
+
|
152
|
+
```yaml
|
153
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
154
|
+
:name: inherited_entity
|
155
|
+
:property_root: value in root_entity
|
156
|
+
:array_root:
|
157
|
+
- value from inherited_entity
|
158
|
+
:hash_root:
|
159
|
+
item1: value 1
|
160
|
+
item2: value2 but from inherited
|
161
|
+
item3: value3
|
162
|
+
:a_specific_property: This one only exists in inherited entity
|
163
|
+
:extends: !psref
|
164
|
+
type: :base_entity
|
165
|
+
name: root_entity
|
166
|
+
```
|
167
|
+
But if you specify the dev_tests scenario:
|
168
|
+
|
169
|
+
$ power_stencil get base_entity/inherited_entity --raw --compiled --scenario dev_tests
|
170
|
+
```yaml
|
171
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
172
|
+
:name: inherited_entity
|
173
|
+
:property_root: value in root_entity
|
174
|
+
:array_root:
|
175
|
+
- value from inherited_entity
|
176
|
+
:hash_root:
|
177
|
+
item1: value 1
|
178
|
+
item2: value2 but from inherited
|
179
|
+
item3: overridden value
|
180
|
+
:a_specific_property: This one only exists in inherited entity
|
181
|
+
:extends: !psref
|
182
|
+
type: :base_entity
|
183
|
+
name: root_entity
|
184
|
+
:property_from_overide: foo
|
185
|
+
```
|
186
|
+
|
187
|
+
:information_source: You can notice that **the override mechanism occurs after the extend mechanism**, and both can be used on the same entities.
|
188
|
+
|
189
|
+
:information_source: The `--scenario` command line option obviously exists as well for the `build` sub-command so that you can run a build in the context of a scenario.
|
190
|
+
|
191
|
+
This override mechanism is really a great way for developers or ops to test something while being sure to avoid introducing errors in the entities repository (by "temporarily patching" an entity to perform a test... and then committing it by mistake... smells like real life, doesn't it ?).
|
192
|
+
|
193
|
+
|
194
|
+
# The "_detemplatization_" process
|
195
|
+
|
196
|
+
Now we have almost everything we need:
|
197
|
+
|
198
|
+
- We have a nice traceable mechanism to manage [entities] (our shared data), including [special features to keep them dry](#extending-an-entity) and perform [test scenarii](#overriding-entities-and-build-scenarii).
|
199
|
+
- We have a pluggable [templating mechanism][templates], with mechanisms to decide to [which files it may be applied to](#xxx_ignore-files) (or actually which files it should _not_ apply to).
|
200
|
+
|
201
|
+
|
202
|
+
When we are using `power_stencil build`, the entities are first compiled then templates associated to the entity specified are used in order to generate the "_detemplatized_" files.
|
203
|
+
|
204
|
+
Those generated files are located in a subfolder of the `build` directory of the project (which by default is not versioned as per `.git_ignore` located at the root folder of the project).
|
205
|
+
|
206
|
+
The pattern for the name of the folder where the files are generated is the following:
|
207
|
+
|
208
|
+
`build/<date_and_time_stamp><entity_type>_<entity_name>/<entity_type>_<entity_name>`
|
209
|
+
|
210
|
+
Why is there 2 times entity type and name ?
|
211
|
+
|
212
|
+
This is because `power_stencil build` accepts more than one entity as parameter and in this case the first level would be the type and name of all the entities concatenated and then there would be second level folders, one per per entity.
|
213
|
+
|
214
|
+
# Post-process actions
|
215
|
+
|
216
|
+
|
217
|
+
By default `PowerStencil` proposes the `simple_exec` entity type which provides the possibility to run any executable after the build completed. By default it will run a script `main.sh` (automatically generated), but this is completely customizable.
|
218
|
+
|
219
|
+
When you create a `simple_exec` entity:
|
220
|
+
|
221
|
+
$ power_stencil create simple_exec/example
|
222
|
+
|
223
|
+
Actually two entities are created under the hood:
|
224
|
+
|
225
|
+
```shell
|
226
|
+
$ power_stencil get example --regexp
|
227
|
+
---
|
228
|
+
PowerStencil::SystemEntityDefinitions::SimpleExec:
|
229
|
+
:type: :simple_exec
|
230
|
+
:fields:
|
231
|
+
:name: example
|
232
|
+
:post_process: !psref
|
233
|
+
type: :process_descriptor
|
234
|
+
name: simple_exec_example.process
|
235
|
+
|
236
|
+
---
|
237
|
+
PowerStencil::SystemEntityDefinitions::ProcessDescriptor:
|
238
|
+
:type: :process_descriptor
|
239
|
+
:fields:
|
240
|
+
:name: simple_exec_example.process
|
241
|
+
:process: "./main.sh"
|
242
|
+
|
243
|
+
```
|
244
|
+
It creates a `process_descriptor` entity (which is referenced from the `simple_exec` entity as the `post_process` field). So you can edit this process descriptor and define there whatever executable you want to be called.
|
245
|
+
|
246
|
+
This should already cover 95% of everything needed.
|
247
|
+
|
248
|
+
If you want to do something more custom after the build process completed, this is where you will have to do a [plugin][plugins].
|
249
|
+
|
250
|
+
[:back:][Documentation root]
|
251
|
+
<!-- End of Document -->
|
252
|
+
|
253
|
+
<!-- Pages -->
|
254
|
+
[Documentation root]: ../README.md "Back to documentation root"
|
255
|
+
[entities]: entities.md "Entities in PowerStencil"
|
256
|
+
[templates]: templates.md "Templates in PowerStencil"
|
257
|
+
[plugins]: plugins.md "Plugins in PowerStencil"
|
258
|
+
[buildable]: entities.md#buildable--buildable_by "How to make an entity buildable ?"
|
259
|
+
[local entities]: entities.md#local-unversioned-entities "Local unversioned entities"
|
260
|
+
|
261
|
+
<!-- Code links -->
|
262
|
+
|
263
|
+
<!-- Illustrations -->
|
264
|
+
[entity-build-flow]: images/power-stencil-entity-build.svg
|
265
|
+
|
266
|
+
<!-- External links -->
|
267
|
+
[universe_compiler]: https://gitlab.com/tools4devops/universe_compiler "The underlying engine to manage entities and compilation !"
|
data/doc/entities.md
ADDED
@@ -0,0 +1,964 @@
|
|
1
|
+
Entities
|
2
|
+
========
|
3
|
+
|
4
|
+
<!-- TOC -->
|
5
|
+
|
6
|
+
- [What are entities ?](#what-are-entities-)
|
7
|
+
- [Default entity types](#default-entity-types)
|
8
|
+
- [Default entities](#default-entities)
|
9
|
+
- [Structure overview](#structure-overview)
|
10
|
+
- [Loose schema entities](#loose-schema-entities)
|
11
|
+
- [Manipulating entities](#manipulating-entities)
|
12
|
+
- [Checking entities](#checking-entities)
|
13
|
+
- [Querying entities](#querying-entities)
|
14
|
+
- [Creating entities](#creating-entities)
|
15
|
+
- [Versioned entities](#versioned-entities)
|
16
|
+
- [Local (unversioned) entities](#local-unversioned-entities)
|
17
|
+
- [Updating entities](#updating-entities)
|
18
|
+
- [Deleting entities](#deleting-entities)
|
19
|
+
- [The `PowerStencil` shell](#the-powerstencil-shell)
|
20
|
+
- [Creating custom entity types](#creating-custom-entity-types)
|
21
|
+
- [Anatomy of an entity type](#anatomy-of-an-entity-type)
|
22
|
+
- [Where do I create new entity types ?](#where-do-i-create-new-entity-types-)
|
23
|
+
- [My first custom entity](#my-first-custom-entity)
|
24
|
+
- [Directives](#directives)
|
25
|
+
- [entity_type](#entity_type)
|
26
|
+
- [auto_named_entity_type](#auto_named_entity_type)
|
27
|
+
- [field](#field)
|
28
|
+
- [has_one](#has_one)
|
29
|
+
- [has_many](#has_many)
|
30
|
+
- [is_array](#is_array)
|
31
|
+
- [is_hash](#is_hash)
|
32
|
+
- [not_null](#not_null)
|
33
|
+
- [not_empty](#not_empty)
|
34
|
+
- [should_match](#should_match)
|
35
|
+
- [class_name](#class_name)
|
36
|
+
- [buildable / buildable_by](#buildable--buildable_by)
|
37
|
+
- [Module you could include in your entity types](#module-you-could-include-in-your-entity-types)
|
38
|
+
- [Adding functional code](#adding-functional-code)
|
39
|
+
|
40
|
+
<!-- /TOC -->
|
41
|
+
[:back:][Documentation root]
|
42
|
+
|
43
|
+
# What are entities ?
|
44
|
+
|
45
|
+
In a nutshell, `entities` are [Ruby] objects persisted into [YAML] files, but unless you have the need the create your own entity definitions (entity types), you don't really need to know the internals as you can fully manage entities without even knowing anything about code.
|
46
|
+
|
47
|
+
There is a lot to read there but you may find surprisingly easy how it is to master entities...
|
48
|
+
|
49
|
+
## Default entity types
|
50
|
+
|
51
|
+
The `power_stencil` CLI provides a number of options to interact with entities. But first let's have a look at default entities and entities definitions. The `power_stencil info` is the perfect command for that:
|
52
|
+
|
53
|
+
```shell
|
54
|
+
$ power_stencil info
|
55
|
+
--------------------------------------------------------------------------------
|
56
|
+
PROJECT REPORT
|
57
|
+
--------------------------------------------------------------------------------
|
58
|
+
General information:
|
59
|
+
- Project required version: 0.2.18
|
60
|
+
- PowerStencil version: 0.2.18
|
61
|
+
--------------------------------------------------------------------------------
|
62
|
+
Paths:
|
63
|
+
- Project root path: '/tmp/tst2'
|
64
|
+
- Project configuration path: '/tmp/tst2/.ps_project'
|
65
|
+
- Local user configuration file: '/tmp/tst2/.ps_project/personal-config.yaml'
|
66
|
+
- Project (versioned) configuration file: '/tmp/tst2/.ps_project/versioned-config.yaml'
|
67
|
+
- Project entities: '/tmp/tst2/.ps_project/entities'
|
68
|
+
- User (unversioned) entities: '/tmp/tst2/.ps_project/user_entities'
|
69
|
+
- Project specific entity definitions: '/tmp/tst2/.ps_project/entity_definitions'
|
70
|
+
--------------------------------------------------------------------------------
|
71
|
+
Entities:
|
72
|
+
- Contains 1 entities.
|
73
|
+
- project_config: 1
|
74
|
+
--------------------------------------------------------------------------------
|
75
|
+
Available entity types:
|
76
|
+
- Type 'base_entity' --> PowerStencil::SystemEntityDefinitions::ProjectEntity
|
77
|
+
- Type 'entity_override' --> UniverseCompiler::Entity::Override
|
78
|
+
- Type 'plugin_definition' --> PowerStencil::SystemEntityDefinitions::Plugin (template-template path: '/opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/power_stencil-0.2.18/etc/templates/plugin_definition')
|
79
|
+
- Type 'process_descriptor' --> PowerStencil::SystemEntityDefinitions::ProcessDescriptor
|
80
|
+
- Type 'project_config' --> PowerStencil::SystemEntityDefinitions::ProjectConfig
|
81
|
+
- Type 'simple_exec' --> PowerStencil::SystemEntityDefinitions::SimpleExec (template-template path: '/opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/power_stencil-0.2.18/etc/templates/simple_exec')
|
82
|
+
```
|
83
|
+
|
84
|
+
It provides a lot of information about the project, but let's focus on the `Entities` and `Available entity types` parts of the report.
|
85
|
+
|
86
|
+
You can see here above that only 6 types of entity are defined by default in a brand new created `PowerStencil` project. You can find them below with some of their core features:
|
87
|
+
|
88
|
+
| entity type | system | persisted | buildable | has associated files |
|
89
|
+
|-------------|:------:|:---------:|:---------:|:--------------------:|
|
90
|
+
|base_entity||X
|
91
|
+
|entity_override||X
|
92
|
+
|plugin_definition|X
|
93
|
+
|process_descriptor||X
|
94
|
+
|project_config|X
|
95
|
+
|simple_exec||X|X|X
|
96
|
+
|
97
|
+
- You should not care about _system_ entities unless you are developing features in `PowerStencil`.
|
98
|
+
- By default any entity type, including those you may create, is _persistent_.
|
99
|
+
- A _buildable_ entity is an entity you can run `power_stencil build` against. More about this in the [builds] part.
|
100
|
+
- When an entity _has associated files_, it means its [templates] are in `<project_root>/<entity_type>/<entity_name>` folder. We'll talk about this more in depth in the [templates] and [builds] part.
|
101
|
+
|
102
|
+
On top of this `power_stencil info` brings you information about the actual Ruby classes implementing those entity types.
|
103
|
+
|
104
|
+
For a typical project which goal is only to generate some files from templates, the only entity types you may need are:
|
105
|
+
|
106
|
+
- base_entity
|
107
|
+
- entity_override
|
108
|
+
- simple_exec
|
109
|
+
|
110
|
+
More information about this in [builds].
|
111
|
+
|
112
|
+
|
113
|
+
## Default entities
|
114
|
+
|
115
|
+
From the `power_stencil info` command output another important information, is that it says that the project contains only one entity, and that this entity is of the type `project_config`.
|
116
|
+
|
117
|
+
`project_config` is a special entity type containing the full config of the project as seen by `PowerStencil`, we will here after how to query it (like any other entity). But there are 2 very important things to note about this special entity:
|
118
|
+
|
119
|
+
- First, this singleton entity will be available at [build time][builds] from any [template][templates].
|
120
|
+
- You can add your own properties in this entity by adding properties to either the project (versioned) configuration file, or to the local user configuration file (see in the `Paths` part of the `power_stencil info` output).
|
121
|
+
|
122
|
+
|
123
|
+
## Structure overview
|
124
|
+
|
125
|
+
As said in introduction, `entities` are persisted as [YAML] files, and therefore only a light knowledge of the [YAML] format is required.
|
126
|
+
|
127
|
+
The type of an entity is actually not part of the entity YAML properties, but it is determined when you [create a new entity](#creating-entities), and it appears at the top of the generated YAML file as a reference to the Ruby class representing the entity type (the ones you can see in the output of `power_stencil info`).
|
128
|
+
Then the YAML file should at least contain the `:name` property.
|
129
|
+
So for a `basic_entity` which is the most simple entity type, the minimalistic generated YAML file may look like:
|
130
|
+
|
131
|
+
```yaml
|
132
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
133
|
+
:name: my_1st_entity
|
134
|
+
```
|
135
|
+
|
136
|
+
The `:name` property may contain any string including space or weird characters. It just has to be a valid YAML string...
|
137
|
+
|
138
|
+
:warning: You may have noticed the leading `:` in the YAML file for the name property. We will come back to this in [Creating custom entity types](#creating-custom-entity-types), but a good pratice would be to prefix all your properties with this colon `:` character. Although it is not always mandatory it will ease the readability of your [templates]. Yet it is mandatory for the name property.
|
139
|
+
|
140
|
+
:information_source: Each entity has an optional `:description` property.
|
141
|
+
|
142
|
+
**:information_source: Each entity has a unique ID which is of the form: `<entity_type>/<entity_name>`**
|
143
|
+
|
144
|
+
:warning: Meaning that **for a particular type of entity, its name has to be unique** !
|
145
|
+
|
146
|
+
Any valid YAML is actually a valid entity !
|
147
|
+
|
148
|
+
## Loose schema entities
|
149
|
+
|
150
|
+
An interesting feature of `PowerStencil`, is that we could describe it as _loose-schema enforced_. What does it mean ? Actually entity types can define some of their properties as mandatory, and the rest is completely free !
|
151
|
+
For example the `basic_entity` has only `:name` declared mandatory property, and for the rest you do whatever you want.
|
152
|
+
|
153
|
+
So as a result the following YAML is a perfectly valid `basic_entity`:
|
154
|
+
|
155
|
+
```yaml
|
156
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
157
|
+
:name: my_2nd_entity
|
158
|
+
:an_array_property:
|
159
|
+
- item 1
|
160
|
+
- item 2
|
161
|
+
:a_hash_property:
|
162
|
+
item1: value1
|
163
|
+
item2: value2
|
164
|
+
a_non_colon_prefixed_property: blah blah blah
|
165
|
+
```
|
166
|
+
You will be able to access all those properties in your [templates]. Even those not known by the entity type. Only the way you will access them may differ a bit. We will cover that more in detail when we will create [custom entity types](#creating-custom-entity-types).
|
167
|
+
|
168
|
+
|
169
|
+
# Manipulating entities
|
170
|
+
|
171
|
+
## Checking entities
|
172
|
+
|
173
|
+
We have seen the `power_stencil info` returns (among other) the number of entities present in the repository (grouped by entity type). But we can have a bit more information about each entity using the `power_stencil check` command:
|
174
|
+
|
175
|
+
```shell
|
176
|
+
$ power_stencil check
|
177
|
+
RAW ENTITIES
|
178
|
+
'base_entity/my_1st_entity':
|
179
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/base_entity/my_1st_entity.yaml'
|
180
|
+
- Status : Valid
|
181
|
+
- Buildable : false
|
182
|
+
'base_entity/my_2nd_entity':
|
183
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/base_entity/my_2nd_entity.yaml'
|
184
|
+
- Status : Valid
|
185
|
+
- Buildable : false
|
186
|
+
'project_config/Project Config':
|
187
|
+
- Storage path: ''
|
188
|
+
- Status : Valid
|
189
|
+
- Buildable : false
|
190
|
+
```
|
191
|
+
|
192
|
+
The goal of this command is normally to check the validity of entities, but it brings as well extra information like if the entity is _buildable_ or even where it is stored in the repository.
|
193
|
+
|
194
|
+
:information_source: You can notice that the `project_config` entity has no storage path, the reason being [it is not persisted](#default-entity-types).
|
195
|
+
|
196
|
+
This command may show some extra information, for more complex entities. We will see that later on.
|
197
|
+
|
198
|
+
By default `power_stencil check` will check all entities in the repository but you can specify on the command line an arbitrary list of entities:
|
199
|
+
|
200
|
+
```shell
|
201
|
+
$ power_stencil check base_entity/my_1st_entity 'project_config/Project Config'
|
202
|
+
RAW ENTITIES
|
203
|
+
'base_entity/my_1st_entity':
|
204
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/base_entity/my_1st_entity.yaml'
|
205
|
+
- Status : Valid
|
206
|
+
- Buildable : false
|
207
|
+
'project_config/Project Config':
|
208
|
+
- Storage path: ''
|
209
|
+
- Status : Valid
|
210
|
+
- Buildable : false
|
211
|
+
```
|
212
|
+
Another possibility is to check a list of objects using a regular expression by using the `--regexp` option:
|
213
|
+
|
214
|
+
```shell
|
215
|
+
$ power_stencil check base_ --regexp
|
216
|
+
RAW ENTITIES
|
217
|
+
'base_entity/my_1st_entity':
|
218
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/base_entity/my_1st_entity.yaml'
|
219
|
+
- Status : Valid
|
220
|
+
- Buildable : false
|
221
|
+
'base_entity/my_2nd_entity':
|
222
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/base_entity/my_2nd_entity.yaml'
|
223
|
+
- Status : Valid
|
224
|
+
- Buildable : false
|
225
|
+
```
|
226
|
+
|
227
|
+
## Querying entities
|
228
|
+
|
229
|
+
The command to query entities from the repository is `power_stencil get`, and it works basically like the `power_stencil check` command.
|
230
|
+
|
231
|
+
No parameter will return all entities, you can pass multiple ids or regexp on the command line.
|
232
|
+
|
233
|
+
By default it will return entities in a format closer to their Ruby counterpart:
|
234
|
+
|
235
|
+
```shell
|
236
|
+
$ power_stencil get base_entity/my_2nd_entity
|
237
|
+
---
|
238
|
+
PowerStencil::SystemEntityDefinitions::ProjectEntity:
|
239
|
+
:type: :base_entity
|
240
|
+
:fields:
|
241
|
+
:name: my_2nd_entity
|
242
|
+
:an_array_property:
|
243
|
+
- item 1
|
244
|
+
- item 2
|
245
|
+
:a_hash_property:
|
246
|
+
item1: value1
|
247
|
+
item2: value2
|
248
|
+
a_non_colon_prefixed_property: blah blah blah
|
249
|
+
```
|
250
|
+
We will see later why this format.
|
251
|
+
|
252
|
+
But you can get the exact YAML version as well using the `--raw` option:
|
253
|
+
|
254
|
+
```shell
|
255
|
+
$ power_stencil get base_entity/my_2nd_entity --raw
|
256
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
257
|
+
:name: my_2nd_entity
|
258
|
+
:an_array_property:
|
259
|
+
- item 1
|
260
|
+
- item 2
|
261
|
+
:a_hash_property:
|
262
|
+
item1: value1
|
263
|
+
item2: value2
|
264
|
+
a_non_colon_prefixed_property: blah blah blah
|
265
|
+
```
|
266
|
+
|
267
|
+
You can also get the names only, which is basically only interesting if you query using a regexp:
|
268
|
+
|
269
|
+
```shell
|
270
|
+
$ power_stencil get base_entity --names-only --regexp
|
271
|
+
- my_2nd_entity
|
272
|
+
|
273
|
+
- my_1st_entity
|
274
|
+
```
|
275
|
+
There are 2 other options (`--compiled` and `--scenario`) but they will be covered in the scope of [builds].
|
276
|
+
|
277
|
+
## Creating entities
|
278
|
+
|
279
|
+
### Versioned entities
|
280
|
+
|
281
|
+
You can create new entities in the repository using the `power_stencil create` command.
|
282
|
+
|
283
|
+
```
|
284
|
+
$ power_stencil create base_entity/my_3rd_entity
|
285
|
+
Created 'base_entity/my_3rd_entity'
|
286
|
+
```
|
287
|
+
|
288
|
+
You can also immediately edit the generated entity using the `--edit` option, which is particularly useful if the entity to created requires some mandatory properties. `PowerStencil` will not let you create invalid entities, so you may have to fill in all mandatory properties before saving.
|
289
|
+
|
290
|
+
The editor used can be specified by multiple ways:
|
291
|
+
|
292
|
+
- In the `EDITOR` environment variable
|
293
|
+
- In one of the two project config files using the `:editor` property
|
294
|
+
- Directly on the command line using the `--editor` option
|
295
|
+
|
296
|
+
After you saved the entity and exited the editor, if the entity you saved is invalid, `PowerStencil` will reopen the file to give you a chance to fix the issue.
|
297
|
+
|
298
|
+
By default the number of retries is specified in the config by the `:max_file_edit_retry_times` property. You can check that in the
|
299
|
+
`project_config/Project Config` entity by issuing:
|
300
|
+
|
301
|
+
$ power_stencil get 'project_config/Project Config'
|
302
|
+
|
303
|
+
And check the `:max_file_edit_retry_times` property in the output. The value of this property can be overridden in one of the two project config files (in this case typically the one that is not versioned).
|
304
|
+
|
305
|
+
Alternatively, instead of using an editor, you can directly specify a property value directly on the command line using the `--property` option.
|
306
|
+
|
307
|
+
```shell
|
308
|
+
$ power_stencil create base_entity/my_4th_entity --property extra_prop:foo
|
309
|
+
Created 'base_entity/my_4th_entity'
|
310
|
+
|
311
|
+
$ power_stencil get base_entity/my_4th_entity --raw
|
312
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
313
|
+
:name: my_4th_entity
|
314
|
+
:extra_prop: foo
|
315
|
+
```
|
316
|
+
:warning: Be careful, that any property created this way will be prefixed using `:`
|
317
|
+
|
318
|
+
### Local (unversioned) entities
|
319
|
+
|
320
|
+
Entities created there are stored in `.ps_project/entities` which is a directory where everything is versioned by default. **But you have the possibility to create entities that will not be versioned**.
|
321
|
+
This is very **useful for developers** for a specific test for example, without risking to commit something they don't want.
|
322
|
+
|
323
|
+
For that you just need to use the `--user-storage` option. Then the entity will be persisted in the `.ps_project/user_entities` which is not versioned (see the root `.gitignore` file).
|
324
|
+
|
325
|
+
```shell
|
326
|
+
$ power_stencil create base_entity/dev_entity --user-storage
|
327
|
+
Created 'base_entity/dev_entity'
|
328
|
+
|
329
|
+
$ power_stencil check base_entity/dev_entity
|
330
|
+
RAW ENTITIES
|
331
|
+
'base_entity/dev_entity':
|
332
|
+
- Storage path: '/tmp/tst2/.ps_project/user_entities/base_entity/dev_entity.yaml'
|
333
|
+
- Status : Valid
|
334
|
+
- Buildable : false
|
335
|
+
```
|
336
|
+
You can notice where the YAML file has been created.
|
337
|
+
|
338
|
+
Of course you can then delete the entity like any other...
|
339
|
+
```shell
|
340
|
+
$ power_stencil delete base_entity/dev_entity --auto
|
341
|
+
Deleted 'base_entity/dev_entity'
|
342
|
+
|
343
|
+
```
|
344
|
+
|
345
|
+
|
346
|
+
## Updating entities
|
347
|
+
|
348
|
+
Not so much to say about updating entities. It works exactly the same way as the `power_stencil create`.
|
349
|
+
|
350
|
+
The command for that is `power_stencil edit`.
|
351
|
+
|
352
|
+
## Deleting entities
|
353
|
+
|
354
|
+
To delete an entity I guess you get it you have to use the `power_stencil delete` command.
|
355
|
+
|
356
|
+
You can delete multiple object in one command using `--regexp`, but `PowerStencil` will ask confirmation for any of the entity requested:
|
357
|
+
|
358
|
+
```shell
|
359
|
+
$ power_stencil delete base_entity/my_4th_entity
|
360
|
+
Are you sure you want delete 'base_entity/my_4th_entity' ? (Yes/y/[No]/n):
|
361
|
+
Cancelled by user input.
|
362
|
+
$ power_stencil delete base_entity/my_4th_entity
|
363
|
+
Are you sure you want delete 'base_entity/my_4th_entity' ? (Yes/y/[No]/n): y
|
364
|
+
Deleted 'base_entity/my_4th_entity'
|
365
|
+
```
|
366
|
+
|
367
|
+
:warning: You can also pass the `--auto` option that will bypass the user confirmation. **Use with caution!**
|
368
|
+
|
369
|
+
## The `PowerStencil` shell
|
370
|
+
|
371
|
+
`PowerStencil` provides a full Ruby shell (REPL) based on [Pry].
|
372
|
+
It could deserve a whole documentation in its own, because you could do a lot of things there.
|
373
|
+
Basically it is interesting, because you can automate easily the creation of a bunch of entities... But you can actually do anything as it is a fully fledged Ruby console.
|
374
|
+
|
375
|
+
Here under just an example of a short session:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
$ power_stencil shell
|
379
|
+
-------------------------------------------------------------------------------
|
380
|
+
# Welcome to the PowerStencil shell session
|
381
|
+
# In this shell you have access to anything the templating engine has access to.
|
382
|
+
# You can view, edit, save entities.
|
383
|
+
#
|
384
|
+
# - Retrieve and manipulate entities using the `entities` hash.
|
385
|
+
# - Persist your changes using the `save` method on each entity.
|
386
|
+
# - Create new project or user entities using `new_<type>` and `user_new_<type>`
|
387
|
+
# methods (see `available_entity_types` for a list of possible types).
|
388
|
+
# - And of course, it is a fully fledged Ruby Pry REPL, so you can do anything
|
389
|
+
# you want...
|
390
|
+
#
|
391
|
+
# Type `exit` to end your session.
|
392
|
+
-------------------------------------------------------------------------------
|
393
|
+
PowerStencil DSL> entities
|
394
|
+
=> [#<PowerStencil::SystemEntityDefinitions::ProjectConfig:47381736532780 composite_key=[:project_config, "Project Config"], @universe='Project entities (1566398755.703200)'>,
|
395
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735892000 composite_key=[:base_entity, "my_2nd_entity"], @universe='Project entities (1566398755.703200)'>,
|
396
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735871600 composite_key=[:base_entity, "my_3rd_entity"], @universe='Project entities (1566398755.703200)'>,
|
397
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735869240 composite_key=[:base_entity, "my_1st_entity"], @universe='Project entities (1566398755.703200)'>]
|
398
|
+
PowerStencil DSL> %w(entity1 entity2 entity3).each do |entity_name|
|
399
|
+
PowerStencil DSL --> e = new_base_entity name: entity_name
|
400
|
+
PowerStencil DSL --> e.save
|
401
|
+
PowerStencil DSL --> end
|
402
|
+
=> ["entity1", "entity2", "entity3"]
|
403
|
+
PowerStencil DSL> entities
|
404
|
+
=> [#<PowerStencil::SystemEntityDefinitions::ProjectConfig:47381736532780 composite_key=[:project_config, "Project Config"], @universe='Project entities (1566398755.703200)'>
|
405
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735892000 composite_key=[:base_entity, "my_2nd_entity"], @universe='Project entities (1566398755.703200)'>,
|
406
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735871600 composite_key=[:base_entity, "my_3rd_entity"], @universe='Project entities (1566398755.703200)'>,
|
407
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381735869240 composite_key=[:base_entity, "my_1st_entity"], @universe='Project entities (1566398755.703200)'>,
|
408
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381741017600 composite_key=[:base_entity, "entity1"], @universe='Project entities (1566398755.703200)'>,
|
409
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381740982060 composite_key=[:base_entity, "entity2"], @universe='Project entities (1566398755.703200)'>,
|
410
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47381740930300 composite_key=[:base_entity, "entity3"], @universe='Project entities (1566398755.703200)'>]
|
411
|
+
PowerStencil DSL> exit
|
412
|
+
```
|
413
|
+
And of course after this session the entities are really saved:
|
414
|
+
|
415
|
+
```shell
|
416
|
+
$ power_stencil get base_entity/ent --regexp --raw
|
417
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
418
|
+
:name: entity3
|
419
|
+
|
420
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
421
|
+
:name: entity1
|
422
|
+
|
423
|
+
--- !ruby/object:PowerStencil::SystemEntityDefinitions::ProjectEntity
|
424
|
+
:name: entity2
|
425
|
+
```
|
426
|
+
|
427
|
+
Basically, in this shell you can do anything you could do within [templates]. Use it to experiment with `PowerStencil`. It is important to understand that **you can't mess up anything within the shell** (Ok not completely true, as it is a [Pry] console under the hood, you could do everything you could do using the [Ruby] language, including wiping your hard drive, creating botnets or skynet terminators... but let's remain serious, I can't imagine you would do such bad things without knowing what you do ;)), if you don't save your entities, nothing is actually persisted. And if you do, who cares ? Git, your wingman, is there to back you up...
|
428
|
+
|
429
|
+
:sparkles: Use the shell Luke !! :sparkles:
|
430
|
+
|
431
|
+
|
432
|
+
# Creating custom entity types
|
433
|
+
|
434
|
+
|
435
|
+
:warning: As opposed to everything we've done until now, creating custom entity types requires a bit of knowledge of the [Ruby] programming language.
|
436
|
+
|
437
|
+
## Anatomy of an entity type
|
438
|
+
|
439
|
+
An entity type is actually a Ruby class inheriting from [PowerStencil::SystemEntityDefinitions::ProjectEntity]. Simply inherit from this class and you're good to go with a brand new valid entity type.
|
440
|
+
|
441
|
+
Internally an entity is a proxy to a Hash `#fields` which content is exactly the content of the YAML file where the entity is persisted. For example in the `PowerStencil` shell:
|
442
|
+
|
443
|
+
```ruby
|
444
|
+
PowerStencil DSL> e = entity :base_entity, "my_2nd_entity"
|
445
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47113681164160 composite_key=[:base_entity, "my_2nd_entity"], @universe='Project entities (1566475157.369747)'>
|
446
|
+
PowerStencil DSL> e.fields
|
447
|
+
=> {:name=>"my_2nd_entity", :an_array_property=>["item 1", "item 2"], :a_hash_property=>{"item1"=>"value1", "item2"=>"value2"}, "a_non_colon_prefixed_property"=>"blah blah blah"}
|
448
|
+
PowerStencil DSL> e.fields.to_yaml
|
449
|
+
```
|
450
|
+
We'll come back later on the DSL included but `entity(type, name)` is a method to retrieve an entity from the repository. So here we retrieve `my_2nd_entity` and we see that the content of the `#fields` is exactly what we defined.
|
451
|
+
This is as well a way to see that properties defined with a `:` prefix end up being `Symbols` whereas non-prefixed properties become `Strings`.
|
452
|
+
|
453
|
+
The [PowerStencil::SystemEntityDefinitions::ProjectEntity] class extends itself a class from the [universe_compiler] Gem and most of the complexity is hidden there in terms of fields, persistence, relationship between entities, builds, _inheritance_ (we are not talking about class inheritance there), overrides...
|
454
|
+
We will clarify some of those concepts here and some other in the [templates] and [builds] part, but you have to know that you don't really need to dig inside the [universe_compiler] unless you are really interested in the deepest internals...
|
455
|
+
|
456
|
+
:+1: Basically the main thing you need to understand is that the custom entity types you will create will inherit from that class.
|
457
|
+
|
458
|
+
## Where do I create new entity types ?
|
459
|
+
|
460
|
+
There mainly two places where you can create custom entity types.
|
461
|
+
|
462
|
+
1. Within a plugin. But plugins are the highest level of customization, and are [covered in a separated document][plugins]. But regarding what we will say here the definition of the classes will follow the same rules.
|
463
|
+
2. In the `.ps_project/entity_definitions/` project folder. Ruby files that will be created there will be required in their alphabetical order. You can name them how you want (provided the `.rb` extension), as their name don't need to match the name of the classes you will create.
|
464
|
+
|
465
|
+
## My first custom entity
|
466
|
+
|
467
|
+
Let's create a new file in `.ps_project/entity_definitions/` and let's name it `my_first_custom_entity_type.rb`, then fill it with the following content:
|
468
|
+
|
469
|
+
```ruby
|
470
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
471
|
+
entity_type :custom_entity
|
472
|
+
end
|
473
|
+
```
|
474
|
+
That's all folks ! You have created your first custom entity type. A proof ?
|
475
|
+
|
476
|
+
```shell
|
477
|
+
$ power_stencil info
|
478
|
+
.
|
479
|
+
.
|
480
|
+
.
|
481
|
+
--------------------------------------------------------------------------------
|
482
|
+
Available entity types:
|
483
|
+
- Type 'base_entity' --> PowerStencil::SystemEntityDefinitions::ProjectEntity
|
484
|
+
- Type 'custom_entity' --> MyCustomEntity
|
485
|
+
- Type 'entity_override' --> UniverseCompiler::Entity::Override
|
486
|
+
- Type 'plugin_definition' --> PowerStencil::SystemEntityDefinitions::Plugin (template-template path: '/opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/power_stencil-0.2.18/etc/templates/plugin_definition')
|
487
|
+
- Type 'process_descriptor' --> PowerStencil::SystemEntityDefinitions::ProcessDescriptor
|
488
|
+
- Type 'project_config' --> PowerStencil::SystemEntityDefinitions::ProjectConfig
|
489
|
+
- Type 'simple_exec' --> PowerStencil::SystemEntityDefinitions::SimpleExec (template-template path: '/opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/power_stencil-0.2.18/etc/templates/simple_exec')
|
490
|
+
```
|
491
|
+
You see that the `custom_entity` type appeared in the list. Need another proof ?
|
492
|
+
```shell
|
493
|
+
$ power_stencil create custom_entity/my_1st_custom_entity
|
494
|
+
Created 'custom_entity/my_1st_custom_entity'
|
495
|
+
|
496
|
+
$ power_stencil check cust -r
|
497
|
+
RAW ENTITIES
|
498
|
+
'custom_entity/my_1st_custom_entity':
|
499
|
+
- Storage path: '/tmp/tst2/.ps_project/entities/custom_entity/my_1st_custom_entity.yaml'
|
500
|
+
- Status : Valid
|
501
|
+
- Buildable : false
|
502
|
+
|
503
|
+
$ power_stencil get custom_entity/my_1st_custom_entity --raw
|
504
|
+
--- !ruby/object:MyCustomEntity
|
505
|
+
:name: my_1st_custom_entity
|
506
|
+
```
|
507
|
+
Ok, I must admit it's a pretty useless entity type, as we didn't add anything compared to the `base_entity` we are inheriting from, but it works, and notice how easy it was ! No complex configuration, no complex code... a breeze.
|
508
|
+
|
509
|
+
## Directives
|
510
|
+
|
511
|
+
You may have noticed in the previous example the directive `entity_type :custom_entity`... This looks a lot like directives some of you may be used to use in frameworks like [Ruby On Rails], and more specifically in [ActiveRecord]. If it is the case, then you won't be lost in how entity types work in `PowerStencil`. For the others you will discover how nice the Ruby syntax can be.
|
512
|
+
|
513
|
+
As said before, an entity is basically a proxy to an internal Hash accessible through the `#fields` accessor. That said, most directives are actually a simple way to define constraints and control over the content of that Hash.
|
514
|
+
|
515
|
+
Here are the available directives:
|
516
|
+
|
517
|
+
### entity_type
|
518
|
+
|
519
|
+
Obviously defines the type of entity. See previous example for how it is used. Although this is not mandatory, if you don't define an `entity_type`, the entity type in not listed in the output of `power_stencil info`. It doesn't mean the entity type is invalid. It just becomes an internal `entity_type`.
|
520
|
+
|
521
|
+
### auto_named_entity_type
|
522
|
+
|
523
|
+
Sometimes you don't want to give real names to some entity types. Without any parameter this directive will use the `entity_type` as default seed else of course the seed passed to this directive. The name of entities will be of the form `<seed>_ <an incremental counter>`.
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
527
|
+
entity_type :custom_entity
|
528
|
+
|
529
|
+
auto_named_entity_type
|
530
|
+
end
|
531
|
+
```
|
532
|
+
|
533
|
+
```ruby
|
534
|
+
PowerStencil DSL> new_custom_entity
|
535
|
+
=> #<MyCustomEntity:47058432471380 composite_key=[:custom_entity, "custom_entity_1"], @universe='Project entities (1566480766.837926)'>
|
536
|
+
PowerStencil DSL> new_custom_entity
|
537
|
+
=> #<MyCustomEntity:47058432258540 composite_key=[:custom_entity, "custom_entity_2"], @universe='Project entities (1566480766.837926)'>
|
538
|
+
PowerStencil DSL> new_custom_entity
|
539
|
+
=> #<MyCustomEntity:47058432038400 composite_key=[:custom_entity, "custom_entity_3"], @universe='Project entities (1566480766.837926)'>
|
540
|
+
PowerStencil DSL> MyCustomEntity.auto_named_entity_type?
|
541
|
+
=> true
|
542
|
+
PowerStencil DSL> MyCustomEntity.auto_named_entity_type_seed
|
543
|
+
=> "custom_entity"
|
544
|
+
```
|
545
|
+
|
546
|
+
|
547
|
+
### field
|
548
|
+
|
549
|
+
As any persisted data is actually in the `#fields` Hash, you can declare shortcuts using the `field` directive. Let's say we have the following entity type:
|
550
|
+
|
551
|
+
```ruby
|
552
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
553
|
+
entity_type :custom_entity
|
554
|
+
|
555
|
+
field :my_custom_field
|
556
|
+
end
|
557
|
+
```
|
558
|
+
Then it means I can access the `my_custom_field` property in two different ways:
|
559
|
+
|
560
|
+
```ruby
|
561
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
562
|
+
=> #<MyCustomEntity:47209361877520 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566476673.765155)'>
|
563
|
+
PowerStencil DSL> e.my_custom_field
|
564
|
+
=> nil
|
565
|
+
PowerStencil DSL> e.my_custom_field = 'foo'
|
566
|
+
=> "foo"
|
567
|
+
PowerStencil DSL> e.my_custom_field
|
568
|
+
=> "foo"
|
569
|
+
PowerStencil DSL> e.fields[:my_custom_field]
|
570
|
+
=> "foo"
|
571
|
+
```
|
572
|
+
:information_source: You can notice the `new_<entity_type>` DSL method which is a shortcut to create entities.
|
573
|
+
|
574
|
+
So you can see the field directive will actually create accessor methods for first level properties in the `#fields` Hash.
|
575
|
+
|
576
|
+
:warning: You can do pretty tricky things like:
|
577
|
+
|
578
|
+
```ruby
|
579
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
580
|
+
entity_type :custom_entity
|
581
|
+
|
582
|
+
field :my_custom_field
|
583
|
+
field 'another_field'
|
584
|
+
end
|
585
|
+
```
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
589
|
+
=> #<MyCustomEntity:47332930509380 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566477499.215253)'>
|
590
|
+
PowerStencil DSL> e.my_custom_field = :foo
|
591
|
+
=> :foo
|
592
|
+
PowerStencil DSL> e.another_field = :bar
|
593
|
+
=> :bar
|
594
|
+
PowerStencil DSL> e.fields
|
595
|
+
=> {:name=>"test_entity", :my_custom_field=>:foo, "another_field"=>:bar}
|
596
|
+
PowerStencil DSL> e.fields[:my_custom_field]
|
597
|
+
=> :foo
|
598
|
+
PowerStencil DSL> e.fields['another_field']
|
599
|
+
=> :bar
|
600
|
+
```
|
601
|
+
|
602
|
+
:warning: Do not create two fields with the "same" name, once as a `Symbol` and another as a `String` ! Although it is perfectly legit at `#fields` level or in the YAML persisted file, the accessor methods generated would have the same name and you could expect some strange behaviour...
|
603
|
+
|
604
|
+
:information_source: By default the [base_entity][PowerStencil::SystemEntityDefinitions::ProjectEntity] has only two fields declared:
|
605
|
+
- The `name` field that we already saw and which is mandatory.
|
606
|
+
- The `description` field which is optional, and which is pretty self-explanatory.
|
607
|
+
|
608
|
+
### has_one
|
609
|
+
|
610
|
+
This property may look familiar to users of [ActiveRecord], it introduces the concept of relationship between entities.
|
611
|
+
|
612
|
+
```ruby
|
613
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
614
|
+
entity_type :custom_entity
|
615
|
+
|
616
|
+
has_one :custom_entity, name: :parent
|
617
|
+
end
|
618
|
+
```
|
619
|
+
|
620
|
+
```ruby
|
621
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
622
|
+
=> #<MyCustomEntity:47215552142260 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566478203.584915)'>
|
623
|
+
PowerStencil DSL> p = new_custom_entity name: :foo
|
624
|
+
=> #<MyCustomEntity:47215551934000 composite_key=[:custom_entity, "foo"], @universe='Project entities (1566478203.584915)'>
|
625
|
+
PowerStencil DSL> e.parent = p
|
626
|
+
=> #<MyCustomEntity:47215551934000 composite_key=[:custom_entity, "foo"], @universe='Project entities (1566478203.584915)'>
|
627
|
+
PowerStencil DSL> e.fields
|
628
|
+
=> {:name=>"test_entity", :parent=>#<MyCustomEntity:47215551934000 composite_key=[:custom_entity, "foo"], @universe='Project entities (1566478203.584915)'>}
|
629
|
+
PowerStencil DSL> p.save=> #<MyCustomEntity:47215551934000 composite_key=[:custom_entity, "foo"], @universe='Project entities (1566478203.584915)'>
|
630
|
+
PowerStencil DSL> e.save
|
631
|
+
=> #<MyCustomEntity:47215552142260 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566478203.584915)'>
|
632
|
+
PowerStencil DSL> exit
|
633
|
+
```
|
634
|
+
|
635
|
+
Which translates at persistence level as:
|
636
|
+
|
637
|
+
```shell
|
638
|
+
$ power_stencil get custom_entity/test_entity --raw
|
639
|
+
--- !ruby/object:MyCustomEntity
|
640
|
+
:name: test_entity
|
641
|
+
:parent: !psref
|
642
|
+
type: :custom_entity
|
643
|
+
name: foo
|
644
|
+
```
|
645
|
+
Here we are referencing an entity of the same type, but of course you can reference any type of entity. Using `has_one` will enforce the type of data you reference. Let's try to mess-up:
|
646
|
+
|
647
|
+
```ruby
|
648
|
+
PowerStencil DSL> e.parent = :bar
|
649
|
+
=> :bar
|
650
|
+
PowerStencil DSL> e.fields
|
651
|
+
=> {:name=>"test_entity", :parent=>:bar}
|
652
|
+
PowerStencil DSL> e.valid?
|
653
|
+
=> false
|
654
|
+
PowerStencil DSL> e.valid? raise_error: true
|
655
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields parent !
|
656
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
657
|
+
PowerStencil DSL> e.save
|
658
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields parent !
|
659
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
660
|
+
```
|
661
|
+
So you see that if we try to set parent with something wrong, the accessor seems to accept, you can even see the `#fields` Hash updated. But as soon as you try to save to entity, or if you use the `#valid?` method, it complains about the type... Cool.
|
662
|
+
|
663
|
+
:information_source: The `name` property of the `has_one` directive is optional. If not present the field will be named from the entity_type referenced instead...
|
664
|
+
|
665
|
+
### has_many
|
666
|
+
|
667
|
+
Once you know the `has_one` directive, you should not be surprised by the `has_many` directive...
|
668
|
+
|
669
|
+
```ruby
|
670
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
671
|
+
entity_type :custom_entity
|
672
|
+
|
673
|
+
has_many :base_entity, name: :sub_properties
|
674
|
+
end
|
675
|
+
```
|
676
|
+
|
677
|
+
```ruby
|
678
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
679
|
+
=> #<MyCustomEntity:47312117001560 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566482056.789871)'>
|
680
|
+
PowerStencil DSL> sub_prop1 = new_base_entity name: :prop1
|
681
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116793880 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566482056.789871)'>
|
682
|
+
PowerStencil DSL> e.sub_properties << sub_prop1
|
683
|
+
=> [#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116793880 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566482056.789871)'>]
|
684
|
+
PowerStencil DSL> sub_prop2 = new_base_entity name: :prop2
|
685
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116175620 composite_key=[:base_entity, "prop2"], @universe='Project entities (1566482056.789871)'>
|
686
|
+
PowerStencil DSL> e.sub_properties << sub_prop2
|
687
|
+
=> [#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116793880 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566482056.789871)'>,
|
688
|
+
#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116175620 composite_key=[:base_entity, "prop2"], @universe='Project entities (1566482056.789871)'>]
|
689
|
+
PowerStencil DSL> sub_prop1.save
|
690
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116793880 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566482056.789871)'>
|
691
|
+
PowerStencil DSL> sub_prop2.save
|
692
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47312116175620 composite_key=[:base_entity, "prop2"], @universe='Project entities (1566482056.789871)'>
|
693
|
+
PowerStencil DSL> e.save
|
694
|
+
=> #<MyCustomEntity:47312117001560 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566482056.789871)'>
|
695
|
+
PowerStencil DSL> exit
|
696
|
+
```
|
697
|
+
|
698
|
+
Which translates at persistence level as:
|
699
|
+
|
700
|
+
```shell
|
701
|
+
--- !ruby/object:MyCustomEntity
|
702
|
+
:sub_properties:
|
703
|
+
- !psref
|
704
|
+
type: :base_entity
|
705
|
+
name: prop1
|
706
|
+
- !psref
|
707
|
+
type: :base_entity
|
708
|
+
name: prop2
|
709
|
+
:name: test_entity
|
710
|
+
```
|
711
|
+
Nice !
|
712
|
+
|
713
|
+
### is_array
|
714
|
+
|
715
|
+
You can define a field as being an array:
|
716
|
+
|
717
|
+
```ruby
|
718
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
719
|
+
entity_type :custom_entity
|
720
|
+
|
721
|
+
field :an_array
|
722
|
+
is_array :an_array
|
723
|
+
|
724
|
+
field :another_array, :is_array
|
725
|
+
end
|
726
|
+
```
|
727
|
+
|
728
|
+
```ruby
|
729
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
730
|
+
=> #<MyCustomEntity:47285777457100 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566482755.453123)'>
|
731
|
+
PowerStencil DSL> e.fields
|
732
|
+
=> {:name=>"test_entity", :an_array=>[], :another_array=>[]}
|
733
|
+
```
|
734
|
+
|
735
|
+
:information_source: Please note the two syntaxes are equivalent.
|
736
|
+
|
737
|
+
### is_hash
|
738
|
+
|
739
|
+
You can define a field as being an array:
|
740
|
+
|
741
|
+
```ruby
|
742
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
743
|
+
entity_type :custom_entity
|
744
|
+
|
745
|
+
field :a_hash
|
746
|
+
is_hash :a_hash
|
747
|
+
|
748
|
+
field :another_hash, :is_hash
|
749
|
+
end
|
750
|
+
```
|
751
|
+
|
752
|
+
```ruby
|
753
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
754
|
+
=> #<MyCustomEntity:46940048986180 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566483001.470293)'>
|
755
|
+
PowerStencil DSL> e.fields
|
756
|
+
=> {:name=>"test_entity", :a_hash=>{}, :another_hash=>{}}
|
757
|
+
```
|
758
|
+
|
759
|
+
:information_source: Please note the two syntaxes are equivalent.
|
760
|
+
|
761
|
+
### not_null
|
762
|
+
|
763
|
+
I guess you start getting used to what happens:
|
764
|
+
|
765
|
+
```ruby
|
766
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
767
|
+
entity_type :custom_entity
|
768
|
+
|
769
|
+
field :a_field_not_null
|
770
|
+
not_null :a_field_not_null
|
771
|
+
|
772
|
+
field :another_field_not_null, :not_null
|
773
|
+
end
|
774
|
+
```
|
775
|
+
|
776
|
+
```ruby
|
777
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
778
|
+
=> #<MyCustomEntity:47124565766660 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566483255.255375)'>
|
779
|
+
PowerStencil DSL> e.valid? raise_error: true
|
780
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields a_field_not_null, another_field_not_null !
|
781
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
782
|
+
PowerStencil DSL> e.a_field_not_null = ''
|
783
|
+
=> ""
|
784
|
+
PowerStencil DSL> e.valid? raise_error: true
|
785
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields another_field_not_null !
|
786
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
787
|
+
PowerStencil DSL> e.another_field_not_null = :yeah
|
788
|
+
=> :yeah
|
789
|
+
PowerStencil DSL> e.fields
|
790
|
+
=> {:name=>"test_entity", :a_field_not_null=>"", :another_field_not_null=>:yeah}
|
791
|
+
PowerStencil DSL> e.valid?
|
792
|
+
=> true
|
793
|
+
```
|
794
|
+
Nothing very surprising there. Please note an empty string is not a null value...
|
795
|
+
|
796
|
+
### not_empty
|
797
|
+
|
798
|
+
This directive will adapt depending on the field type
|
799
|
+
|
800
|
+
```ruby
|
801
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
802
|
+
entity_type :custom_entity
|
803
|
+
|
804
|
+
field :a_non_empty_field, :not_empty
|
805
|
+
field :a_non_empty_array, :is_array, :not_empty
|
806
|
+
field :a_non_empty_hash, :is_hash, :not_empty
|
807
|
+
|
808
|
+
has_many :base_entity, name: :friends
|
809
|
+
not_empty :friends
|
810
|
+
end
|
811
|
+
```
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity, a_non_empty_field: :foo, a_non_empty_array: [:bar], a_non_empty_hash: {foo: :bar}
|
815
|
+
=> #<MyCustomEntity:47261985525080 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566484354.658921)'>
|
816
|
+
PowerStencil DSL> e.valid? raise_error: true
|
817
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields friends !
|
818
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
819
|
+
PowerStencil DSL> sub_prop1 = new_base_entity name: :prop1
|
820
|
+
=> #<PowerStencil::SystemEntityDefinitions::ProjectEntity:47261985113260 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566484354.658921)'>
|
821
|
+
PowerStencil DSL> e.friends << sub_prop1
|
822
|
+
=> [#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47261985113260 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566484354.658921)'>]
|
823
|
+
PowerStencil DSL> e.valid?
|
824
|
+
=> true
|
825
|
+
PowerStencil DSL> e.fields
|
826
|
+
=> {:name=>"test_entity",
|
827
|
+
:a_non_empty_field=>:foo,
|
828
|
+
:a_non_empty_array=>[:bar],
|
829
|
+
:a_non_empty_hash=>{:foo=>:bar},
|
830
|
+
:friends=>[#<PowerStencil::SystemEntityDefinitions::ProjectEntity:47261985113260 composite_key=[:base_entity, "prop1"], @universe='Project entities (1566484354.658921)'>]}
|
831
|
+
```
|
832
|
+
|
833
|
+
Pretty simple nope ?
|
834
|
+
|
835
|
+
:warning: Please note that neither `has_one` nor `has_many` support the "_condensed_" version of the syntax (like `field` can). You have to specify the directive on a separated line.
|
836
|
+
|
837
|
+
:information_source: `has_one` does not support `not_empty` but support `not_null`, and the opposite for `has_many`...
|
838
|
+
|
839
|
+
### should_match
|
840
|
+
|
841
|
+
`should_match` will perform a validation of the content regarding a regular expression or a string. It should work on strings or symbols:
|
842
|
+
|
843
|
+
```ruby
|
844
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
845
|
+
entity_type :custom_entity
|
846
|
+
|
847
|
+
field :ipv4, should_match: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
|
848
|
+
field :title, :not_null, should_match: /(Mr)|(Ms)/
|
849
|
+
end
|
850
|
+
```
|
851
|
+
|
852
|
+
```ruby
|
853
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
854
|
+
=> #<MyCustomEntity:47277853516360 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566485485.196701)'>
|
855
|
+
PowerStencil DSL> e.valid? raise_error: true
|
856
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields title !
|
857
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
858
|
+
PowerStencil DSL> e.title = 'PhD'
|
859
|
+
=> "PhD"
|
860
|
+
PowerStencil DSL> e.valid? raise_error: true
|
861
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields title !
|
862
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
863
|
+
PowerStencil DSL> e.title = 'Mr'
|
864
|
+
=> "Mr"
|
865
|
+
PowerStencil DSL> e.valid? raise_error: true
|
866
|
+
=> true
|
867
|
+
PowerStencil DSL> e.ipv4 = 'a.b.c.d'
|
868
|
+
=> "a.b.c.d"
|
869
|
+
PowerStencil DSL> e.valid? raise_error: true
|
870
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields ipv4 !
|
871
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.7/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
872
|
+
PowerStencil DSL> e.ipv4 = '192.168.0.1'
|
873
|
+
=> "192.168.0.1"
|
874
|
+
PowerStencil DSL> e.valid? raise_error: true
|
875
|
+
=> true
|
876
|
+
```
|
877
|
+
|
878
|
+
:warning: As you can see there, unless specified as `not_null` or `not_empty`, the `should_match` validation will not be performed ! Pretty normal if you think about it...
|
879
|
+
|
880
|
+
### class_name
|
881
|
+
|
882
|
+
```ruby
|
883
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
884
|
+
entity_type :custom_entity
|
885
|
+
|
886
|
+
field :should_be_a_string, class_name: String
|
887
|
+
end
|
888
|
+
```
|
889
|
+
|
890
|
+
```ruby
|
891
|
+
PowerStencil DSL> e = new_custom_entity name: :test_entity
|
892
|
+
=> #<MyCustomEntity:47164532040760 composite_key=[:custom_entity, "test_entity"], @universe='Project entities (1566486839.687064)'>
|
893
|
+
PowerStencil DSL> e.valid? raise_error: true
|
894
|
+
=> true
|
895
|
+
PowerStencil DSL> e.should_be_a_string = 42
|
896
|
+
=> 42
|
897
|
+
PowerStencil DSL> e.valid? raise_error: true
|
898
|
+
UniverseCompiler::Error: Invalid entity '[:custom_entity, "test_entity"]' for fields should_be_a_string !
|
899
|
+
from /opt/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/universe_compiler-0.2.8/lib/universe_compiler/utils/error_propagation.rb:10:in `false_or_raise'
|
900
|
+
PowerStencil DSL> e.should_be_a_string = 'okay... then'
|
901
|
+
=> "okay... then"
|
902
|
+
PowerStencil DSL> e.valid? raise_error: true
|
903
|
+
=> true
|
904
|
+
```
|
905
|
+
`class_name` supports actually both a class (like here) or a class name.
|
906
|
+
|
907
|
+
:warning: Use this one with caution, as it does not test anything regarding inheritance...
|
908
|
+
|
909
|
+
### buildable / buildable_by
|
910
|
+
|
911
|
+
One of the most important directives !
|
912
|
+
More information is provided in the [builds] documentation, but basically this defines an `entity_type` as a potential target for the build process.
|
913
|
+
|
914
|
+
Most probably you will use `buildable`, `buildable_by` being used in the context of [plugins].
|
915
|
+
|
916
|
+
```ruby
|
917
|
+
class MyCustomEntity < PowerStencil::SystemEntityDefinitions::ProjectEntity
|
918
|
+
entity_type :custom_entity
|
919
|
+
|
920
|
+
buildable
|
921
|
+
end
|
922
|
+
```
|
923
|
+
|
924
|
+
## Module you could include in your entity types
|
925
|
+
|
926
|
+
There is one special module you could include in your entity types.
|
927
|
+
|
928
|
+
If you want to define an entity which is not supposed to be persisted you could just include the [PowerStencil::SystemEntityDefinitions::NonPersistent] module et voilà... It could be a directive, but this is not something your are supposed to do a lot. It could become a directive in the future if the need appears.
|
929
|
+
|
930
|
+
|
931
|
+
## Adding functional code
|
932
|
+
|
933
|
+
Now you know an entity type is just a regular Ruby class. As such you could add any code you want to, for example, get a token from a server, perform a task on a db...whatever...
|
934
|
+
|
935
|
+
**:warning: Although you can do anything there, if you see yourself starting writing a lot of code in your custom entities, you should probably consider creating a real [plugin][plugins] ! It will help you maintain and structure your code, on top of providing you a ton of extra cool stuff like extending command line by adding new sub-commands and/or options, having plugin-specific config, having templates-templates (:rofl:), plus the possibility to share/re-use your nice work across multiple projects !!**
|
936
|
+
|
937
|
+
|
938
|
+
|
939
|
+
[:back:][Documentation root]
|
940
|
+
<!-- End of Document -->
|
941
|
+
|
942
|
+
<!-- Pages -->
|
943
|
+
[Documentation root]: ../README.md "Back to documentation root"
|
944
|
+
[templates]: templates.md "Templates in PowerStencil"
|
945
|
+
[builds]: builds.md "Builds in PowerStencil"
|
946
|
+
[plugins]: plugins.md "Plugins in PowerStencil"
|
947
|
+
[example use cases]: example_use_cases.md "Example uses cases using PowerStencil"
|
948
|
+
|
949
|
+
|
950
|
+
<!-- Code links -->
|
951
|
+
[PowerStencil::SystemEntityDefinitions::ProjectEntity]: ../lib/power_stencil/system_entity_definitions/project_entity.rb "The base_entity entity type"
|
952
|
+
[PowerStencil::SystemEntityDefinitions::NonPersistent]: ../lib/power_stencil/system_entity_definitions/non_persistent.rb "To make your entity type not persistent"
|
953
|
+
[PowerStencil::SystemEntityDefinitions::HasAssociatedFiles]: ../lib/power_stencil/system_entity_definitions/has_associated_files.rb "Brings the templating power to your entity types"
|
954
|
+
|
955
|
+
<!-- Illustrations -->
|
956
|
+
|
957
|
+
|
958
|
+
<!-- External links -->
|
959
|
+
[YAML]: https://yaml.org/ "The YAML official site"
|
960
|
+
[Ruby]: https://www.ruby-lang.org "The powerful Ruby language"
|
961
|
+
[Pry]: https://github.com/pry/pry "The awesome Pry console"
|
962
|
+
[ActiveRecord]: https://guides.rubyonrails.org/active_record_basics.html "The ultimate ORM"
|
963
|
+
[Ruby On Rails]: https://rubyonrails.org/ "One of the best Web framework"
|
964
|
+
[universe_compiler]: https://gitlab.com/tools4devops/universe_compiler "The underlying engine to manage entities and compilation !"
|