power_stencil 0.8.13 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -15
  3. data/doc/faq.md +4 -7
  4. data/doc/git_integration.md +38 -12
  5. data/etc/meta_templates/plugin_seed/.gitignore +12 -0
  6. data/etc/meta_templates/plugin_seed/.gitlab-ci.yml +11 -0
  7. data/etc/meta_templates/plugin_seed/README.md +20 -5
  8. data/etc/meta_templates/plugin_seed/etc/plugin_capabilities.yaml +3 -0
  9. data/etc/meta_templates/plugin_seed/psplugin_{entity}.gemspec +2 -0
  10. data/etc/meta_templates/plugin_seed/spec/project_helper.rb +50 -0
  11. data/etc/meta_templates/plugin_seed/spec/spec_helper.rb +2 -1
  12. data/etc/meta_templates/plugin_seed/spec/{entity}_spec.rb +21 -1
  13. data/etc/power_stencil.yaml +6 -1
  14. data/etc/templates/plugin_definition/.gitignore +2 -1
  15. data/etc/templates/plugin_definition/.gitlab-ci.yml +11 -0
  16. data/etc/templates/plugin_definition/README.md +20 -5
  17. data/etc/templates/plugin_definition/etc/plugin_capabilities.yaml +3 -0
  18. data/etc/templates/plugin_definition/psplugin_{entity}.gemspec +2 -0
  19. data/etc/templates/plugin_definition/spec/project_helper.rb +50 -0
  20. data/etc/templates/plugin_definition/spec/spec_helper.rb +2 -1
  21. data/etc/templates/plugin_definition/spec/{entity}_spec.rb +21 -1
  22. data/etc/templates/project/.ps_project/versioned-config.yaml +1 -0
  23. data/etc/templates/zsh_command_line_completion/_power_stencil.sh.erb +18 -8
  24. data/lib/power_stencil.rb +4 -0
  25. data/lib/power_stencil/command_processors/adm.rb +29 -1
  26. data/lib/power_stencil/command_processors/create.rb +14 -4
  27. data/lib/power_stencil/dsl/completion.rb +37 -0
  28. data/lib/power_stencil/engine/base.rb +2 -0
  29. data/lib/power_stencil/engine/directory_processor.rb +20 -1
  30. data/lib/power_stencil/engine/project_engine.rb +1 -1
  31. data/lib/power_stencil/engine/renderers/haml.rb +21 -0
  32. data/lib/power_stencil/initializer.rb +6 -1
  33. data/lib/power_stencil/plugins/base.rb +0 -1
  34. data/lib/power_stencil/plugins/capabilities.rb +4 -0
  35. data/lib/power_stencil/plugins/command_line.rb +3 -1
  36. data/lib/power_stencil/plugins/require.rb +6 -6
  37. data/lib/power_stencil/project/base.rb +2 -0
  38. data/lib/power_stencil/project/plugins.rb +35 -2
  39. data/lib/power_stencil/ultra_command_line/command_line_manager.rb +35 -0
  40. data/lib/power_stencil/ultra_command_line/option_definition.rb +5 -0
  41. data/lib/power_stencil/ultra_command_line/providers_manager.rb +21 -0
  42. data/lib/power_stencil/ultra_command_line/sub_command.rb +12 -0
  43. data/lib/power_stencil/utils/completion.rb +19 -9
  44. data/lib/power_stencil/utils/dependency_solver.rb +19 -0
  45. data/lib/power_stencil/version.rb +1 -1
  46. data/power_stencil.gemspec +2 -1
  47. metadata +35 -11
@@ -32,3 +32,6 @@
32
32
  :templates:
33
33
  - etc/templates
34
34
 
35
+ # Plugin dependencies to other plugins. Use the plugin name and NOT the plugin
36
+ # gem name (in the case of a gem plugin). Declare as well dependencies to local plugins.
37
+ :dependencies: []
@@ -11,6 +11,8 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = %q{<%= plugin_name %> is a plugin for the PowerStencil framework.}
13
13
  spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ # For official plugins
15
+ # spec.homepage = "https://powerstencil.brizone.org/#plugins"
14
16
  spec.homepage = "TODO: Put your gem's website or public repo URL here."
15
17
  spec.license = 'MIT'
16
18
 
@@ -0,0 +1,50 @@
1
+ module PowerStencilTests
2
+
3
+ module Project
4
+
5
+ TEMP_DIR_PREFIX = 'PS_TESTS_'
6
+
7
+ module ClassMethods
8
+
9
+
10
+ def temporary_project(project_name, with_git_support: false, scope: :all)
11
+ self.instance_eval do
12
+ puts scope
13
+ around(scope) do |execution|
14
+ Dir.mktmpdir(TEMP_DIR_PREFIX) do |tests_root_dir|
15
+ tmp_project_path = File.join tests_root_dir, project_name
16
+ create_project_including_this_plugin tmp_project_path, with_git_support: with_git_support
17
+ execution.run
18
+ @tmp_project_path = nil
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def plugin_path
25
+ File.expand_path File.join('..', '..'), __FILE__
26
+ end
27
+
28
+ end
29
+
30
+ attr_reader :tmp_project_path
31
+
32
+ def self.included(base)
33
+ base.extend(ClassMethods)
34
+ end
35
+
36
+ def create_project_including_this_plugin(project_path, with_git_support: false)
37
+ cmd = "power_stencil init --project-path '#{project_path}'"
38
+ cmd << ' --no-git' unless with_git_support
39
+ `#{cmd}`
40
+ project_plugin_path = File.join project_path, '.ps_project', 'plugins'
41
+ `ln -s '#{self.class.plugin_path}' '#{project_plugin_path}'`
42
+ @tmp_project_path = project_path
43
+ end
44
+
45
+ def temporary_project_cmd(params)
46
+ "power_stencil #{params} --project-path '#{tmp_project_path}'"
47
+ end
48
+
49
+ end
50
+ end
@@ -1,6 +1,7 @@
1
- require "bundler/setup"
1
+ require 'bundler/setup'
2
2
  require 'power_stencil'
3
3
  require "<%= plugin_name %>"
4
+ require 'project_helper'
4
5
 
5
6
  RSpec.configure do |config|
6
7
  # Enable flags like --only-failures and --next-failure
@@ -1,10 +1,30 @@
1
1
  RSpec.describe <%= plugin_module_name %> do
2
+
3
+ include PowerStencilTests::Project
4
+ temporary_project 'test_project', scope: :all
5
+
6
+ let(:plugin_list) { 'plugin --list' }
7
+ let(:test_entity) { '<%= plugin_name %>_entity/test_<%= plugin_name %>' }
8
+ let(:create_<%= plugin_name %>) { "create #{test_entity}" }
9
+ let(:get_<%= plugin_name %>) { "get #{test_entity}" }
10
+
2
11
  it 'has a version number' do
3
12
  expect(<%= plugin_module_name %>::VERSION).not_to be nil
4
13
  end
5
14
 
6
- it 'does something useful' do
15
+ it 'should add a "<%= plugin_name %>" sub-command' do
16
+ expect(`#{temporary_project_cmd plugin_list}`).to match '- <%= plugin_name %>'
17
+ end
18
+
19
+ it 'should be possible to create a "<%= plugin_name %>_entity"' do
20
+ `#{temporary_project_cmd create_<%= plugin_name %>}`
21
+ expect($?.success?).to be_truthy
22
+ expect(`#{temporary_project_cmd get_<%= plugin_name %>}`).not_to be_empty
23
+ end
24
+
25
+ it 'should do something useful' do
7
26
  pending 'Tests implementation'
8
27
  RSpec.fail
9
28
  end
29
+
10
30
  end
@@ -7,6 +7,7 @@
7
7
 
8
8
  # Project plugins gems
9
9
  # List of plugins, provided as real Ruby gems (outside of this project), that you want to use in this project.
10
+ # Here declare the gem name, not the plugin name.
10
11
  # :project_plugins: []
11
12
 
12
13
  # Graphviz configuration
@@ -2,10 +2,10 @@
2
2
 
3
3
  # zsh shell completion script for <%= script_name %>
4
4
  # To regenerate this file: '<%= script_name %> adm --zsh-completion'
5
- # Generated on the <%= Time.now %> by <%= script_name %> v<%= PowerStencil::VERSION %>
5
+ # Generated<%= timestamp %> by <%= script_name %> v<%= PowerStencil::VERSION %>
6
6
 
7
- <% commands = PowerStencil.command_line_manager.commands -%>
8
- _<%= script_name %>() {
7
+ <% commands = context_commands -%>
8
+ _<%= script_name %>_first_level() {
9
9
  local cmd
10
10
  local <%= script_name %>_sub_commands
11
11
  <%= script_name %>_sub_commands=(<%= commands.reject { |c| c.name.empty? }.sort { |a,b| a.name <=> b.name }.map(&:name).join ' ' %>)
@@ -21,7 +21,7 @@ _<%= script_name %>() {
21
21
  (( $+functions[_<%= script_name %>_cmd_$cmd] )) && _<%= script_name %>_cmd_$cmd
22
22
  else
23
23
  _values : \
24
- <%= continued_multilines(root_command(commands).options.sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}, number_spaces: 10) %>
24
+ <%= continued_multilines(context_options(root_command commands).sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}, number_spaces: 10) %>
25
25
  fi
26
26
  else
27
27
  _values : \
@@ -33,7 +33,7 @@ _<%= script_name %>() {
33
33
  <% end -%>
34
34
  <%= command_representation command %> \
35
35
  <% end -%>
36
- <%= continued_multilines(root_command.options.sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}, number_spaces: 8) %>
36
+ <%= continued_multilines(context_options(root_command).sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}, number_spaces: 8) %>
37
37
  fi
38
38
  }
39
39
 
@@ -46,7 +46,7 @@ _<%= script_name %>_cmd_<%= command.name %>() {
46
46
  _arguments -s : \
47
47
  <%
48
48
  command_param = default_command_param_type command
49
- options_lines = command.options.sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}
49
+ options_lines = context_options(command).sort { |a,b| a.name <=> b.name }.map {|o| option_representation o}
50
50
  options_lines << command_param unless command_param.empty?
51
51
  -%>
52
52
  <%= continued_multilines(options_lines) %>
@@ -124,5 +124,15 @@ _power_stencil_do_nothing() {
124
124
  return 1
125
125
  }
126
126
 
127
- # Let's rock
128
- _<%= script_name %> "$@"
127
+
128
+ _<%= script_name %>() {
129
+ if $( _within_power_stencil_project ); then
130
+ local project_root=$( _power_stencil_project_root )
131
+ if [ -f "${project_root}/.ps_project/<%= PowerStencil.config[:completion_target][:zsh][:project_completion_filename] %>" ]; then
132
+ . "${project_root}/.ps_project/<%= PowerStencil.config[:completion_target][:zsh][:project_completion_filename] %>"
133
+ fi
134
+ else
135
+ . "<%= File.expand_path(File.join PowerStencil.config[:completion_target][:zsh][:completion_dir], "_#{script_name}") %>"
136
+ fi
137
+ _<%= script_name %>_first_level "$@"
138
+ }
@@ -5,6 +5,10 @@ require 'dir_glob_ignore'
5
5
 
6
6
  $DO_NOT_AUTOSTART_CLIMATIC=true
7
7
  require 'climatic'
8
+ require 'power_stencil/ultra_command_line/command_line_manager'
9
+ require 'power_stencil/ultra_command_line/providers_manager'
10
+ require 'power_stencil/ultra_command_line/option_definition'
11
+ require 'power_stencil/ultra_command_line/sub_command'
8
12
 
9
13
  require 'power_stencil/error'
10
14
  require 'power_stencil/utils/os'
@@ -12,7 +12,35 @@ module PowerStencil
12
12
  def execute
13
13
 
14
14
  if config[:'zsh-completion']
15
- generate_zsh_completion 'power_stencil'
15
+ target_dir = File.expand_path config[:completion_target][:zsh][:completion_dir]
16
+ script_name = 'power_stencil'
17
+ user_completion_script = File.join target_dir, "_#{script_name}"
18
+ begin
19
+ current_dir = Dir.pwd
20
+ Dir.mktmpdir 'completion_generation' do |tmpdir|
21
+ Dir.chdir tmpdir
22
+ generate_zsh_completion script_name, user_completion_script, false
23
+ end
24
+ ensure
25
+ Dir.chdir current_dir
26
+ end
27
+
28
+ begin
29
+ project
30
+ shortname_project_completion_script = File.join config[:default_config_directory_name], config[:completion_target][:zsh][:project_completion_filename]
31
+ project.track_action_with_git("Adding project specific zsh completion file: '#{ shortname_project_completion_script }'.") do
32
+ project_completion_script = File.join project.project_config_root, config[:completion_target][:zsh][:project_completion_filename]
33
+ generate_zsh_completion script_name, project_completion_script, true
34
+ puts_and_logs "A specific completion has been generated for this project in '#{project_completion_script}'.", check_verbose: false
35
+ end
36
+ rescue
37
+ # Do not check errors. This is just to load project config...
38
+ logger.debug "Outside of a PowerStencil project... Not generating zsh project completion."
39
+ end
40
+ puts_and_logs "zsh global auto_completion has been installed in '#{user_completion_script}'.", check_verbose: false
41
+ puts
42
+ puts "You should ensure you have something like 'fpath=(#{target_dir} $fpath)' in your ~/.zshrc file..."
43
+ puts 'You may have to relog for changes to be applied.'
16
44
  return
17
45
  end
18
46
 
@@ -17,8 +17,17 @@ module PowerStencil
17
17
  unless config[:property].nil?
18
18
  config[:property].each do |property_def|
19
19
  if md = property_def.match(/^\s*(?<prop_name>[^:=]+)\s*[:=]\s*(?<prop_val>.*)\s*$/)
20
- logger.debug "Using extra properties from command line '#{md['prop_name']}' = '#{md['prop_val']}'"
21
- entity_as_hash[md['prop_name'].to_sym] = md['prop_val']
20
+ property_value = md['prop_val']
21
+ logger.debug "Using extra properties from command line '#{md['prop_name']}' = '#{property_value}'"
22
+ if pmd = property_value.match(/^\s*!entity\s+(?<entity_type>[^\/]+)\/(?<entity_name>.*)\s*$/)
23
+ type = pmd['entity_type'].to_sym
24
+ name = pmd['entity_name']
25
+ referenced_entity = project.engine.entity(type, name)
26
+ property_value = referenced_entity.to_reference unless referenced_entity.nil?
27
+ puts_and_logs "Could not find entity '#{type.to_s}/#{name}' !", logs_as: :error, check_verbose: false if referenced_entity.nil?
28
+ end
29
+
30
+ entity_as_hash[md['prop_name'].to_sym] = property_value
22
31
  else
23
32
  raise PowerStencil::Error, "Invalid command line property definition: '#{property_def}'"
24
33
  end
@@ -35,14 +44,16 @@ module PowerStencil
35
44
  new_entity = edit_before_save new_entity
36
45
  end
37
46
 
47
+ new_entity.resolve_fields_references!
48
+
38
49
  new_entity.valid? raise_error: true
39
50
  project.track_action_with_git("Created '#{new_entity.as_path}' (and potential dependencies).") do
40
51
  new_entity.save
41
52
  end
42
53
  puts_and_logs "Generated templates in '#{new_entity.templates_path}'.", check_verbose: false if new_entity.buildable?
43
54
  rescue => e
44
- puts_and_logs "Failed to create '#{search_criterion.as_path}' with message '#{e.message}'.", logs_as: :error, check_verbose: false
45
55
  logger.debug PowerStencil::Error.report_error(e)
56
+ raise PowerStencil::Error, "Failed to create '#{search_criterion.as_path}' with message '#{e.message}'."
46
57
  end
47
58
  end
48
59
  end
@@ -63,7 +74,6 @@ module PowerStencil
63
74
  modifications_valid? modified_path, entity
64
75
  end
65
76
  project.engine.root_universe.replace entity, modified_entity
66
- modified_entity.resolve_fields_references!
67
77
  modified_entity
68
78
  end
69
79
 
@@ -5,15 +5,52 @@ module PowerStencil
5
5
 
6
6
  class << self
7
7
  attr_accessor :script_name
8
+ attr_accessor :generating_project_completion
8
9
  end
9
10
 
10
11
  attr_reader :encountered_types
11
12
 
13
+ def timestamp
14
+ if generating_user_completion?
15
+ " on the #{Time.now}"
16
+ else
17
+ ''
18
+ end
19
+ end
20
+
12
21
  def initialize(universe)
13
22
  super
14
23
  @encountered_types = {}
15
24
  end
16
25
 
26
+ def generating_project_completion?
27
+ self.class.generating_project_completion
28
+ end
29
+
30
+ def generating_user_completion?
31
+ !self.class.generating_project_completion
32
+ end
33
+
34
+ def context_commands
35
+ if generating_user_completion?
36
+ PowerStencil.command_line_manager.commands.select do |command|
37
+ command.providers.include? PowerStencil
38
+ end
39
+ else
40
+ PowerStencil.command_line_manager.commands
41
+ end
42
+ end
43
+
44
+ def context_options(command)
45
+ if generating_user_completion?
46
+ command.options.select do |option|
47
+ option.providers.include? PowerStencil
48
+ end
49
+ else
50
+ command.options
51
+ end
52
+ end
53
+
17
54
  def script_name
18
55
  self.class.script_name
19
56
  end
@@ -2,6 +2,7 @@ require 'power_stencil/engine/entities_definitions'
2
2
  require 'power_stencil/engine/directory_processor'
3
3
 
4
4
  require 'power_stencil/engine/renderers/erb'
5
+ require 'power_stencil/engine/renderers/haml'
5
6
  require 'power_stencil/dsl/base'
6
7
  require 'power_stencil/dsl/plugin_generation'
7
8
  require 'power_stencil/dsl/completion'
@@ -18,6 +19,7 @@ module PowerStencil
18
19
  include PowerStencil::Engine::DirectoryProcessor
19
20
 
20
21
  include PowerStencil::Engine::Renderers::Erb
22
+ include PowerStencil::Engine::Renderers::Haml
21
23
 
22
24
  attr_accessor :dsl
23
25
  attr_reader :root_universe
@@ -1,5 +1,7 @@
1
1
  require 'fileutils'
2
2
  require 'erb'
3
+ require 'haml'
4
+
3
5
 
4
6
  module PowerStencil
5
7
  module Engine
@@ -30,10 +32,27 @@ module PowerStencil
30
32
  end
31
33
  end
32
34
  ensure
33
- @rendering_context = nil
35
+ @files_not_to_rename = nil
34
36
  @files_not_to_render = nil
35
37
  end
36
38
 
39
+
40
+ def render_single_template_file(source_template, destination_file, main_entry_point: nil)
41
+ compiled_universe = root_universe.compile scenario: config[:scenario]
42
+ puts_and_logs 'Entities analysis completed.'
43
+
44
+ logger.info 'Generating target file...'
45
+
46
+ process_file source_template, destination_file,
47
+ compiled_universe,
48
+ overwrite_files: true,
49
+ main_entry_point: main_entry_point
50
+ ensure
51
+ @files_not_to_rename = nil
52
+ @files_not_to_render = nil
53
+ end
54
+
55
+
37
56
  private
38
57
 
39
58
  def detemplatized_file_name(filename, replacement_text)
@@ -50,7 +50,7 @@ module PowerStencil
50
50
  private
51
51
 
52
52
  def load_plugins_entities_definition
53
- project.plugins.each do |_, plugin|
53
+ project.plugins_sorted_by_dependency.each do |plugin|
54
54
  plugin.require_plugin_entity_definitions
55
55
  end
56
56
  end
@@ -0,0 +1,21 @@
1
+ module PowerStencil
2
+ module Engine
3
+ module Renderers
4
+
5
+ module Haml
6
+
7
+ private
8
+
9
+ def render_haml_template(source, context)
10
+ logger.debug "Using HAML to render file '#{source}'"
11
+ ::Haml::Engine.new(File.read source).render(context)
12
+ rescue => e
13
+ logger.debug PowerStencil::Error.report_error(e)
14
+ raise PowerStencil::Error, "Error rendering '#{source}': '#{e.message}'"
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -55,14 +55,19 @@ module PowerStencil
55
55
  describe: PowerStencil::CommandProcessors::Describe,
56
56
  adm: PowerStencil::CommandProcessors::Adm
57
57
  }.each do |command_name, processor|
58
- command_line_manager.register_processor command_line_manager.command_by_alias(command_name),
58
+ command = command_line_manager.command_by_alias(command_name)
59
+ command_line_manager.register_processor command,
59
60
  processor.new
61
+ command.add_provider self
60
62
  end
61
63
  end
62
64
 
63
65
  def setup_climatic(cmd_line_args)
64
66
  mngr = Climatic::ConfigLayers::CommandLineLayer.build_command_line_manager base_commands_definition_file
65
67
  Climatic.bootstrap cmd_line_args: cmd_line_args, command_manager: mngr
68
+ mngr.commands.each do |command|
69
+ command.add_provider PowerStencil
70
+ end
66
71
  begin
67
72
  # Fix command line layer priority to allow a bigger number of plugins
68
73
  config.command_line_layer.priority = 999
@@ -70,7 +70,6 @@ module PowerStencil
70
70
  load_capabilities
71
71
  load_plugin_specific_config
72
72
  load_yaml_command_definition
73
- require_entry_point
74
73
  end
75
74
 
76
75
  end
@@ -13,6 +13,10 @@ module PowerStencil
13
13
  @capabilities ||= CAPABILITIES.dup.zip([false] * CAPABILITIES.size).to_h
14
14
  end
15
15
 
16
+ def dependencies
17
+ plugin_definition[:dependencies]
18
+ end
19
+
16
20
  private
17
21
 
18
22
  def load_capabilities