power_stencil 0.3.1

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 (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +198 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/bin/update_plugin_template +79 -0
  13. data/doc/builds.md +267 -0
  14. data/doc/entities.md +964 -0
  15. data/doc/example_use_cases.md +59 -0
  16. data/doc/images/power-stencil-entity-build.svg +481 -0
  17. data/doc/images/power-stencil-entity-creation.svg +375 -0
  18. data/doc/images/power-stencil-simple-flow.svg +265 -0
  19. data/doc/plugins.md +169 -0
  20. data/doc/templates.md +333 -0
  21. data/etc/base_commands_definition.yml +259 -0
  22. data/etc/meta_templates/plugin_seed/etc/command_line.yaml +12 -0
  23. data/etc/meta_templates/plugin_seed/etc/plugin_config.yaml +4 -0
  24. data/etc/meta_templates/plugin_seed/etc/templates/.git_keep +0 -0
  25. data/etc/meta_templates/plugin_seed/lib/{entity}.rb.erb +34 -0
  26. data/etc/meta_templates/plugin_seed/lib/{entity}/entity_definitions/{entity}_entity.rb.erb +11 -0
  27. data/etc/meta_templates/plugin_seed/lib/{entity}/plugin_helper.rb.erb +17 -0
  28. data/etc/meta_templates/plugin_seed/lib/{entity}/version.rb.erb +3 -0
  29. data/etc/meta_templates/plugin_seed/lib/{entity}/{entity}_processor.rb.erb +19 -0
  30. data/etc/meta_templates/plugin_seed/psplugin_{entity}.gemspec +36 -0
  31. data/etc/power_stencil.yaml +92 -0
  32. data/etc/templates/plugin_definition/.gitignore +11 -0
  33. data/etc/templates/plugin_definition/.rspec +3 -0
  34. data/etc/templates/plugin_definition/.travis.yml +5 -0
  35. data/etc/templates/plugin_definition/CODE_OF_CONDUCT.md +74 -0
  36. data/etc/templates/plugin_definition/Gemfile +6 -0
  37. data/etc/templates/plugin_definition/LICENSE.txt +21 -0
  38. data/etc/templates/plugin_definition/README.md +43 -0
  39. data/etc/templates/plugin_definition/Rakefile +6 -0
  40. data/etc/templates/plugin_definition/bin/console +14 -0
  41. data/etc/templates/plugin_definition/bin/setup +8 -0
  42. data/etc/templates/plugin_definition/etc/command_line.yaml +12 -0
  43. data/etc/templates/plugin_definition/etc/plugin_config.yaml +4 -0
  44. data/etc/templates/plugin_definition/etc/templates/.git_keep +1 -0
  45. data/etc/templates/plugin_definition/lib/{entity}.rb.erb +34 -0
  46. data/etc/templates/plugin_definition/lib/{entity}/entity_definitions/{entity}_entity.rb.erb +11 -0
  47. data/etc/templates/plugin_definition/lib/{entity}/plugin_helper.rb.erb +17 -0
  48. data/etc/templates/plugin_definition/lib/{entity}/version.rb.erb +3 -0
  49. data/etc/templates/plugin_definition/lib/{entity}/{entity}_processor.rb.erb +19 -0
  50. data/etc/templates/plugin_definition/psplugin_{entity}.gemspec +36 -0
  51. data/etc/templates/plugin_definition/spec/spec_helper.rb +14 -0
  52. data/etc/templates/plugin_definition/spec/{entity}_spec.rb +9 -0
  53. data/etc/templates/project/.copy_ignore +2 -0
  54. data/etc/templates/project/.gitignore.erb +6 -0
  55. data/etc/templates/project/.ps_project/entities/.gitkeep +0 -0
  56. data/etc/templates/project/.ps_project/entities/README.md +11 -0
  57. data/etc/templates/project/.ps_project/entity_definitions/.gitkeep +0 -0
  58. data/etc/templates/project/.ps_project/entity_definitions/README.md +15 -0
  59. data/etc/templates/project/.ps_project/personal-config.yaml +8 -0
  60. data/etc/templates/project/.ps_project/plugins/.gitkeep +0 -0
  61. data/etc/templates/project/.ps_project/templates-templates/.gitkeep +0 -0
  62. data/etc/templates/project/.ps_project/templates-templates/README.md +13 -0
  63. data/etc/templates/project/.ps_project/user_entities/.gitkeep +0 -0
  64. data/etc/templates/project/.ps_project/user_entities/README.md +12 -0
  65. data/etc/templates/project/.ps_project/versioned-config.yaml +6 -0
  66. data/etc/templates/simple_exec/main.sh +4 -0
  67. data/exe/power_stencil +28 -0
  68. data/lib/power_stencil.rb +53 -0
  69. data/lib/power_stencil/command_processors/build.rb +43 -0
  70. data/lib/power_stencil/command_processors/check.rb +35 -0
  71. data/lib/power_stencil/command_processors/create.rb +70 -0
  72. data/lib/power_stencil/command_processors/delete.rb +38 -0
  73. data/lib/power_stencil/command_processors/edit.rb +35 -0
  74. data/lib/power_stencil/command_processors/entity_helper.rb +105 -0
  75. data/lib/power_stencil/command_processors/get.rb +22 -0
  76. data/lib/power_stencil/command_processors/info.rb +41 -0
  77. data/lib/power_stencil/command_processors/init.rb +47 -0
  78. data/lib/power_stencil/command_processors/new_plugin.rb +31 -0
  79. data/lib/power_stencil/command_processors/root.rb +27 -0
  80. data/lib/power_stencil/command_processors/shell.rb +37 -0
  81. data/lib/power_stencil/command_processors/trace_helper.rb +20 -0
  82. data/lib/power_stencil/dsl/base.rb +24 -0
  83. data/lib/power_stencil/dsl/entities.rb +46 -0
  84. data/lib/power_stencil/dsl/plugin_generation.rb +17 -0
  85. data/lib/power_stencil/engine/base.rb +50 -0
  86. data/lib/power_stencil/engine/build_handling.rb +77 -0
  87. data/lib/power_stencil/engine/directory_processor.rb +111 -0
  88. data/lib/power_stencil/engine/entities_definitions.rb +42 -0
  89. data/lib/power_stencil/engine/entities_handling.rb +76 -0
  90. data/lib/power_stencil/engine/entity_engine.rb +20 -0
  91. data/lib/power_stencil/engine/init_engine.rb +18 -0
  92. data/lib/power_stencil/engine/project_engine.rb +75 -0
  93. data/lib/power_stencil/engine/renderers/erb.rb +21 -0
  94. data/lib/power_stencil/error.rb +20 -0
  95. data/lib/power_stencil/initializer.rb +87 -0
  96. data/lib/power_stencil/plugins/base.rb +54 -0
  97. data/lib/power_stencil/plugins/capabilities.rb +30 -0
  98. data/lib/power_stencil/plugins/command_line.rb +27 -0
  99. data/lib/power_stencil/plugins/config.rb +32 -0
  100. data/lib/power_stencil/plugins/dependencies.rb +32 -0
  101. data/lib/power_stencil/plugins/gem.rb +57 -0
  102. data/lib/power_stencil/plugins/require.rb +77 -0
  103. data/lib/power_stencil/plugins/templates.rb +21 -0
  104. data/lib/power_stencil/project/base.rb +79 -0
  105. data/lib/power_stencil/project/config.rb +54 -0
  106. data/lib/power_stencil/project/create.rb +30 -0
  107. data/lib/power_stencil/project/info.rb +72 -0
  108. data/lib/power_stencil/project/paths.rb +119 -0
  109. data/lib/power_stencil/project/plugins.rb +89 -0
  110. data/lib/power_stencil/project/proxy.rb +13 -0
  111. data/lib/power_stencil/project/templates.rb +56 -0
  112. data/lib/power_stencil/project/versioning.rb +29 -0
  113. data/lib/power_stencil/system_entity_definitions/all.rb +14 -0
  114. data/lib/power_stencil/system_entity_definitions/buildable.rb +23 -0
  115. data/lib/power_stencil/system_entity_definitions/entity_override.rb +8 -0
  116. data/lib/power_stencil/system_entity_definitions/entity_project_common.rb +33 -0
  117. data/lib/power_stencil/system_entity_definitions/has_associated_files.rb +13 -0
  118. data/lib/power_stencil/system_entity_definitions/non_persistent.rb +14 -0
  119. data/lib/power_stencil/system_entity_definitions/plugin.rb +17 -0
  120. data/lib/power_stencil/system_entity_definitions/process_descriptor.rb +15 -0
  121. data/lib/power_stencil/system_entity_definitions/project_config.rb +24 -0
  122. data/lib/power_stencil/system_entity_definitions/project_entity.rb +16 -0
  123. data/lib/power_stencil/system_entity_definitions/simple_exec.rb +47 -0
  124. data/lib/power_stencil/utils/directory_processor.rb +54 -0
  125. data/lib/power_stencil/utils/file_edit.rb +87 -0
  126. data/lib/power_stencil/utils/file_helper.rb +56 -0
  127. data/lib/power_stencil/utils/gem_utils.rb +13 -0
  128. data/lib/power_stencil/utils/secure_require.rb +26 -0
  129. data/lib/power_stencil/utils/semantic_version.rb +128 -0
  130. data/lib/power_stencil/version.rb +3 -0
  131. data/power_stencil.gemspec +32 -0
  132. metadata +287 -0
@@ -0,0 +1,8 @@
1
+ class UniverseCompiler::Entity::Override
2
+
3
+ extend PowerStencil::SystemEntityDefinitions::Buildable
4
+ include PowerStencil::SystemEntityDefinitions::EntityProjectCommon
5
+
6
+ field :description
7
+
8
+ end
@@ -0,0 +1,33 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ module EntityProjectCommon
5
+
6
+ def save(uri = source_uri, force_files_generation: false)
7
+ super(uri)
8
+ unless PowerStencil.project.entity_type_templates[type].nil?
9
+ PowerStencil.project.generate_entity_dir_for_entity self, force: force_files_generation
10
+ end
11
+ self
12
+ end
13
+
14
+ def delete(force_files_deletion: false)
15
+ unless PowerStencil.project.entity_type_templates[type].nil?
16
+ PowerStencil.project.delete_entity_dir_for_entity self, force: force_files_deletion
17
+ end
18
+ super()
19
+ self
20
+ end
21
+
22
+ def buildable_by
23
+ self.class.buildable_by
24
+ end
25
+
26
+ def buildable?
27
+ self.class.buildable?
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ module HasAssociatedFiles
5
+
6
+ def templates_path
7
+ File.join PowerStencil.project.project_root, type.to_s, name
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ module NonPersistent
5
+
6
+ def save(uri = source_uri)
7
+ PowerStencil.logger.debug "Won't save '#{self.class.to_s}' because defined as non persistent"
8
+ self
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ class Plugin < PowerStencil::SystemEntityDefinitions::ProjectEntity
5
+
6
+ include PowerStencil::SystemEntityDefinitions::NonPersistent
7
+
8
+ DOC = 'A system class not to be used'
9
+
10
+ entity_type :plugin_definition
11
+
12
+ end
13
+
14
+
15
+ end
16
+ end
17
+
@@ -0,0 +1,15 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ class ProcessDescriptor < PowerStencil::SystemEntityDefinitions::ProjectEntity
5
+
6
+ DOC = 'Generic description of a process'
7
+
8
+ entity_type :process_descriptor
9
+
10
+ field :process, :not_null
11
+
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ class ProjectConfig < PowerStencil::SystemEntityDefinitions::ProjectEntity
5
+
6
+ include PowerStencil::SystemEntityDefinitions::NonPersistent
7
+
8
+ DOC = 'A system singleton read-only entity holding runtime PowerStencil configuration'
9
+
10
+ entity_type :project_config
11
+
12
+ field_reader :simulate, :debug
13
+
14
+ def initialize(fields: {}, universe: nil)
15
+ super
16
+ self.name = 'Project Config'
17
+ end
18
+
19
+ end
20
+
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,16 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ class ProjectEntity < UniverseCompiler::Entity::Base
5
+
6
+ extend PowerStencil::SystemEntityDefinitions::Buildable
7
+ include PowerStencil::SystemEntityDefinitions::EntityProjectCommon
8
+
9
+ entity_type :base_entity
10
+
11
+ field :description
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ module PowerStencil
2
+ module SystemEntityDefinitions
3
+
4
+ class SimpleExec < PowerStencil::SystemEntityDefinitions::ProjectEntity
5
+
6
+ include PowerStencil::SystemEntityDefinitions::HasAssociatedFiles
7
+
8
+ DOC = 'Describes a simple process to be called after source files have been rendered'.freeze
9
+
10
+ entity_type :simple_exec
11
+
12
+ buildable_by ''
13
+
14
+ has_one :process_descriptor, name: :post_process
15
+ not_null :post_process
16
+
17
+ def valid?(raise_error: false)
18
+ unless super(raise_error: false)
19
+ if self.post_process.nil?
20
+ self.post_process = PowerStencil.project.engine.new_entity universe, :process_descriptor, fields: {
21
+ name: "simple_exec_#{name}.process",
22
+ process: './main.sh'
23
+ }
24
+ end
25
+ end
26
+ super(raise_error: raise_error)
27
+ end
28
+
29
+ def save(uri = source_uri, force_files_generation: false)
30
+ valid?
31
+ self.post_process.save
32
+ super(uri, force_files_generation: force_files_generation)
33
+ end
34
+
35
+ def delete(force_files_deletion: false)
36
+ super
37
+ if self.post_process.name == "simple_exec_#{name}.process"
38
+ self.post_process.delete
39
+ end
40
+ self
41
+ end
42
+
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ module PowerStencil
2
+ module Utils
3
+
4
+ module DirectoryProcessor
5
+
6
+ def process_directory(source: nil,
7
+ destination: nil,
8
+ ignore_files_pattern: DirGlobIgnore::IgnoreFileLists::DEFAULT_FILE_NAME)
9
+ raise PowerStencil::Error, 'Source and destination have to be different' if source == destination
10
+ source = check_source_dir source
11
+ destination = check_destination_dir destination
12
+ # Dir.glob_with_ignore_file ignore_file, base_dir, *glob_args, &block
13
+ file_pattern = "#{source}/**/*"
14
+ res = {}
15
+ Dir.glob_with_ignore_file ignore_files_pattern, source, file_pattern, File::FNM_DOTMATCH do |original_file|
16
+ logger.debug "Processing '#{original_file}'"
17
+ res[original_file] = destination_file(original_file, source, destination)
18
+ if block_given?
19
+ yield original_file, destination_file(original_file, source, destination)
20
+ end
21
+ end
22
+ res
23
+ end
24
+
25
+ private
26
+
27
+ def destination_file(original_file, source, destination)
28
+ original_file.gsub /^#{source}/, destination
29
+ end
30
+
31
+ def check_source_dir(source)
32
+ raise PowerStencil::Error, 'Please specify a source directory !' if source.nil?
33
+ source = File.expand_path source
34
+ raise PowerStencil::Error, "Source directory '#{source}' does not exist !" unless File.exist? source
35
+ source
36
+ end
37
+
38
+ def check_destination_dir(destination)
39
+ raise PowerStencil::Error, 'Please specify a destination directory !' if destination.nil?
40
+ destination = File.expand_path destination
41
+ if File.exist? destination
42
+ raise PowerStencil::Error, "Destination '#{destination}' is not a directory !" unless File.directory? destination
43
+ end
44
+ # raise PowerStencil::Error, "Destination '#{destination}' is not a directory !" unless File.directory? destination
45
+ # raise PowerStencil::Error, "Destination directory '#{destination}' already exists !" if File.exist? destination
46
+ destination
47
+
48
+ end
49
+
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,87 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+
4
+ module PowerStencil
5
+ module Utils
6
+
7
+ module FileEdit
8
+
9
+ include Climatic::Proxy
10
+
11
+ EDITOR_ENVIRONMENT_VARIABLE = 'EDITOR'
12
+ LAST_EDITED_FILE = '/tmp/last-failed-edit.yaml'
13
+
14
+ attr_writer :editor, :nb_max_edit_retries
15
+
16
+ def editor
17
+ default = config[:editor] || ENV[EDITOR_ENVIRONMENT_VARIABLE]
18
+ @editor ||= default
19
+ end
20
+
21
+ def nb_max_edit_retries
22
+ @nb_max_edit_retries ||= config[:max_file_edit_retry_times]
23
+ end
24
+
25
+ def securely_edit_entity(entity, &modifications_validation_block)
26
+ initial_uri = entity.source_uri
27
+ modified_entity = nil
28
+ Tempfile.create([entity.type.to_s, entity.name.to_s]) do |tmpfile|
29
+ tmpfile.puts entity.to_yaml
30
+ tmpfile.flush
31
+ securely_edit_file(tmpfile, &modifications_validation_block)
32
+ modified_entity = UniverseCompiler::Entity::Persistence.load tmpfile
33
+ modified_entity.source_uri = initial_uri
34
+ end
35
+ return modified_entity
36
+ end
37
+
38
+ def securely_edit_file(file, &modifications_validation_block)
39
+ unless File.exists? file and File.writable? file
40
+ raise PowerStencil::Error, "Cannot edit file '#{file}' !"
41
+ end
42
+ ext = File.extname file
43
+ base = File.basename file, ext
44
+ tmp_file_path = Tempfile.new([base, ext]).path
45
+ retry_count = 1
46
+ begin
47
+ FileUtils.copy file, tmp_file_path if retry_count == 1
48
+ edit_file tmp_file_path
49
+ if block_given?
50
+ unless modifications_validation_block.call tmp_file_path, file
51
+ raise PowerStencil::Error, "Your modifications to '#{file}' are invalid !"
52
+ end
53
+ end
54
+ FileUtils.copy tmp_file_path, file
55
+ logger.info "File '#{file}' updated successfully."
56
+ file
57
+ rescue StandardError => e
58
+ retry_count += 1
59
+ if retry_count > nb_max_edit_retries
60
+ logger.error "Your modifications are kept in '#{LAST_EDITED_FILE}' as it was invalid !"
61
+ begin
62
+ FileUtils.copy tmp_file_path, LAST_EDITED_FILE
63
+ rescue => ue
64
+ logger.error 'Cannot keep your modifications !'
65
+ logger.debug PowerStencil::Error.report_error(ue)
66
+ end
67
+ else
68
+ retry
69
+ end
70
+ raise e
71
+ ensure
72
+ File.unlink tmp_file_path
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def edit_file(file)
79
+ raise PowerStencil::Error, 'No editor specified' if editor.nil?
80
+ logger.debug "Editing file '#{file}', using editor '#{editor}'"
81
+ system "#{editor} '#{file}'"
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,56 @@
1
+ module PowerStencil
2
+ module Utils
3
+
4
+ module FileHelper
5
+
6
+ def find_recursively_in_path(dir_or_file, from_dir)
7
+ raise PowerStencil::Error, "Invalid directory '#{from_dir}'" unless Dir.exist?(from_dir) and File.readable?(from_dir)
8
+
9
+ candidate = File.join from_dir, dir_or_file
10
+
11
+ if Dir.exists? candidate or File.exist? candidate
12
+ if File.readable? candidate and File.writable? candidate
13
+ candidate
14
+ else
15
+ PowerStencil.logger.warn "Humm, found something: '#{candidate}', but with wrong access rights."
16
+ nil
17
+ end
18
+ else
19
+ next_dir = File.expand_path '..', from_dir
20
+ if next_dir == from_dir
21
+ PowerStencil.logger.info "Didn't find any trace of '#{dir_or_file}'... :("
22
+ nil
23
+ else
24
+ find_recursively_in_path dir_or_file, next_dir
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ def yaml_file_to_hash(file_name)
31
+ raw_content = File.read file_name
32
+ res = YAML.load raw_content
33
+ if res
34
+ Hash[res.map { |k, v| [k, v] }]
35
+ else
36
+ raise PowerStencil::Error, "Invalid file content for '#{file_name}'" unless raw_content.empty?
37
+ []
38
+ end
39
+ end
40
+
41
+ def timestamped_uniq_dir(seed, start_time)
42
+ uniq_dir = timestamp start_time
43
+ uniq_dir += "-#{seed}" unless seed.nil?
44
+ uniq_dir
45
+ end
46
+
47
+ def timestamp(timedate)
48
+ timedate.strftime '%Y%m%d-%H%M-%S%L'
49
+ end
50
+
51
+
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ module PowerStencil
2
+ module Utils
3
+
4
+ module GemUtils
5
+
6
+ def is_available_gem?(gem_name)
7
+ false
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module PowerStencil
2
+ module Utils
3
+
4
+ module SecureRequire
5
+
6
+ def securely_require(ruby_source_file, fail_on_error: false)
7
+ PowerStencil.logger.debug "Securely requiring Ruby source file '#{ruby_source_file}'..."
8
+ require ruby_source_file
9
+ yield if block_given?
10
+ rescue => e
11
+ PowerStencil.logger.debug PowerStencil::Error.report_error(e)
12
+ msg = "Could not require Ruby source file '#{ruby_source_file}' !"
13
+ if fail_on_error
14
+ msg << ' Aborting !'
15
+ PowerStencil.logger.error msg
16
+ raise PowerStencil::Error, "Invalid Ruby source file: '#{ruby_source_file}' !"
17
+ else
18
+ msg << " (#{e.message})"
19
+ msg << ' Ignoring...'
20
+ PowerStencil.logger.warn msg
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,128 @@
1
+ module PowerStencil
2
+ module Utils
3
+
4
+ class SemanticVersion
5
+
6
+ include Comparable
7
+
8
+ MANDATORY_LEVELS = %i(major minor patch).freeze
9
+ OPTIONAL_LEVEL = :extra
10
+
11
+ class << self
12
+ def valid_version?(version, raise_error: false)
13
+ new version
14
+ true
15
+ rescue => e
16
+ raise e if raise_error
17
+ false
18
+ end
19
+ end
20
+
21
+ def initialize(semantic_version)
22
+ @version = analyze_semantic_version semantic_version
23
+ end
24
+
25
+ def <=>(other)
26
+ other_hash = analyze_semantic_version other
27
+ MANDATORY_LEVELS.each do |level|
28
+ r = @version[level] <=> other_hash[level]
29
+ return r unless r == 0
30
+ end
31
+ if @version[OPTIONAL_LEVEL].nil? and other_hash[OPTIONAL_LEVEL]
32
+ return 1
33
+ end
34
+ unless @version[OPTIONAL_LEVEL].nil?
35
+ return -1 if other_hash[OPTIONAL_LEVEL].nil?
36
+ return @version[OPTIONAL_LEVEL] <=> other_hash[OPTIONAL_LEVEL]
37
+ end
38
+ 0
39
+ end
40
+
41
+ def increment(level = :patch)
42
+ raise PowerStencil::Error, "'#{level}' is not a valid level !" unless MANDATORY_LEVELS.include? level
43
+ h = self.to_hash
44
+ increment_hash(h, level)
45
+ self.class.new h
46
+ end
47
+ alias :succ :increment
48
+
49
+
50
+ def increment!(level = :patch)
51
+ raise PowerStencil::Error, "'#{level}' is not a valid level !" unless MANDATORY_LEVELS.include? level
52
+ increment_hash(@version, level)
53
+ self
54
+ end
55
+ alias :succ! :increment!
56
+
57
+ def to_s
58
+ hash_to_version_string @version
59
+ end
60
+
61
+ def to_hash
62
+ @version.dup
63
+ end
64
+
65
+ private
66
+
67
+ def increment_hash(hash, level)
68
+ hash[level] = hash[level].succ
69
+ hash[:patch] = 0 if level == :minor
70
+ if level == :major
71
+ hash[:minor] = 0
72
+ hash[:patch] = 0
73
+ end
74
+ end
75
+
76
+ def analyze_semantic_version(semantic_version)
77
+ case semantic_version
78
+ when String
79
+ version_string_to_hash semantic_version
80
+ when SemanticVersion
81
+ semantic_version.to_hash
82
+ when Hash
83
+ MANDATORY_LEVELS.each do |level|
84
+ raise_analysis_error semantic_version unless semantic_version.keys.include? level
85
+ raise_analysis_error semantic_version unless semantic_version[level].is_a? Fixnum
86
+ end
87
+ semantic_version.dup
88
+ else
89
+ raise_analysis_error semantic_version
90
+ end
91
+ end
92
+
93
+ def raise_analysis_error(obj)
94
+ raise PowerStencil::Error, "#{obj.class} cannot be used as semantic version !"
95
+ end
96
+
97
+
98
+ def hash_to_version_string(hash)
99
+ version_string = MANDATORY_LEVELS.map {|l| hash[l]}.join '.'
100
+ unless hash[OPTIONAL_LEVEL].nil?
101
+ version_string << "-#{hash[OPTIONAL_LEVEL]}"
102
+ end
103
+ version_string
104
+ end
105
+
106
+ def version_string_to_hash(version_string)
107
+ res = {}
108
+ begin
109
+ version_string.match(/^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-(?<extra>.*))*$/) do |md|
110
+ res = {
111
+ major: md['major'].to_i,
112
+ minor: md['minor'].to_i,
113
+ patch: md['patch'].to_i,
114
+ extra: md['extra'],
115
+ }
116
+ return res
117
+ end
118
+ raise
119
+ rescue
120
+ raise PowerStencil::Error, "Invalid semantic version '#{version_string}' !"
121
+ end
122
+ res
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+ end