viperaptor 2.0.0
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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +10 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +64 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/viperaptor +8 -0
- data/lib/viperaptor/cli/cli.rb +16 -0
- data/lib/viperaptor/cli/gen_command.rb +132 -0
- data/lib/viperaptor/cli/setup_command.rb +122 -0
- data/lib/viperaptor/cli/setup_username_command.rb +21 -0
- data/lib/viperaptor/cli/template/template_create_command.rb +40 -0
- data/lib/viperaptor/cli/template/template_group.rb +14 -0
- data/lib/viperaptor/cli/template/template_install_command.rb +21 -0
- data/lib/viperaptor/cli/template/template_list_command.rb +16 -0
- data/lib/viperaptor/cli/template/template_search_command.rb +30 -0
- data/lib/viperaptor/cli/thor_extension.rb +47 -0
- data/lib/viperaptor/cli/version_command.rb +25 -0
- data/lib/viperaptor/code_generation/Rambafile.liquid +54 -0
- data/lib/viperaptor/code_generation/code_module.rb +104 -0
- data/lib/viperaptor/code_generation/content_generator.rb +43 -0
- data/lib/viperaptor/code_generation/module_template.rb +28 -0
- data/lib/viperaptor/code_generation/rambafile_generator.rb +23 -0
- data/lib/viperaptor/configuration/user_preferences.rb +87 -0
- data/lib/viperaptor/constants/constants.rb +13 -0
- data/lib/viperaptor/constants/rambafile_constants.rb +34 -0
- data/lib/viperaptor/constants/rambaspec_constants.rb +18 -0
- data/lib/viperaptor/constants/user_preferences_constants.rb +7 -0
- data/lib/viperaptor/helpers/dependency_checker.rb +54 -0
- data/lib/viperaptor/helpers/gen_command_table_parameters_formatter.rb +33 -0
- data/lib/viperaptor/helpers/module_info_generator.rb +33 -0
- data/lib/viperaptor/helpers/module_validator.rb +85 -0
- data/lib/viperaptor/helpers/print_table.rb +17 -0
- data/lib/viperaptor/helpers/rambafile.rb +75 -0
- data/lib/viperaptor/helpers/template_helper.rb +76 -0
- data/lib/viperaptor/helpers/xcodeproj_helper.rb +256 -0
- data/lib/viperaptor/module_generator.rb +104 -0
- data/lib/viperaptor/template/creator/new_template/Code/Service/service.h.liquid +11 -0
- data/lib/viperaptor/template/creator/new_template/Code/Service/service.m.liquid +13 -0
- data/lib/viperaptor/template/creator/new_template/Tests/Service/service_tests.m.liquid +35 -0
- data/lib/viperaptor/template/creator/new_template/template.rambaspec.liquid +20 -0
- data/lib/viperaptor/template/creator/template_creator.rb +39 -0
- data/lib/viperaptor/template/helpers/catalog_downloader.rb +107 -0
- data/lib/viperaptor/template/helpers/catalog_template_list_helper.rb +55 -0
- data/lib/viperaptor/template/helpers/catalog_template_search_helper.rb +27 -0
- data/lib/viperaptor/template/helpers/catalog_terminator.rb +21 -0
- data/lib/viperaptor/template/helpers/rambaspec_validator.rb +52 -0
- data/lib/viperaptor/template/installer/abstract_installer.rb +9 -0
- data/lib/viperaptor/template/installer/catalog_installer.rb +78 -0
- data/lib/viperaptor/template/installer/local_installer.rb +32 -0
- data/lib/viperaptor/template/installer/remote_installer.rb +51 -0
- data/lib/viperaptor/template/installer/template_installer_factory.rb +22 -0
- data/lib/viperaptor/template/processor/template_declaration.rb +36 -0
- data/lib/viperaptor/template/processor/template_processor.rb +73 -0
- data/lib/viperaptor/tools/string-colorize.rb +23 -0
- data/lib/viperaptor/version.rb +5 -0
- data/lib/viperaptor.rb +16 -0
- data/viperaptor.gemspec +36 -0
- metadata +274 -0
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            require 'thor'
         | 
| 2 | 
            +
            require 'viperaptor/version.rb'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Viperaptor::CLI
         | 
| 5 | 
            +
              class Application < Thor
         | 
| 6 | 
            +
                include Viperaptor
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                desc 'version', 'Prints out Viperaptor current version'
         | 
| 9 | 
            +
                def version
         | 
| 10 | 
            +
                  options = {}
         | 
| 11 | 
            +
                  options['Version'] = Viperaptor::VERSION.green
         | 
| 12 | 
            +
                  options['Release date'] = Viperaptor::RELEASE_DATE.green
         | 
| 13 | 
            +
                  options['Change notes'] = Viperaptor::RELEASE_LINK.green
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  values = []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  options.each do |title, value|
         | 
| 18 | 
            +
                    values.push("#{title}: #{value}")
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  output = values.join("\n")
         | 
| 22 | 
            +
                  puts(output)
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            ### Headers settings
         | 
| 2 | 
            +
            company: {{ company }}
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            ### Xcode project settings
         | 
| 5 | 
            +
            project_name: {{ project_name }}
         | 
| 6 | 
            +
            xcodeproj_path: {{ xcodeproj_path }}
         | 
| 7 | 
            +
            {% if prefix != "" %}prefix: {{ prefix }}{% endif %}
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            {% if (project_target != nil and project_target != "") or (project_file_path != nil and project_file_path != "") or (project_group_path != nil and project_group_path != "") %}### Code generation settings section
         | 
| 10 | 
            +
            {% if project_target != nil and project_target != "" %}# The main project target name
         | 
| 11 | 
            +
            project_target: {{ project_target }}{% endif %}
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            {% if project_file_path != nil and project_file_path != "" %}# The file path for new modules
         | 
| 14 | 
            +
            project_file_path: {{ project_file_path }}{% endif %}
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            {% if project_group_path != nil and project_group_path != "" %}# The Xcode group path to new modules
         | 
| 17 | 
            +
            project_group_path: {{ project_group_path }}{% endif %}
         | 
| 18 | 
            +
            {% endif %}
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            {% if (test_target != nil and test_target != "") or (test_file_path != nil and test_file_path != "") or (test_group_path != nil and test_group_path != "") %}### Tests generation settings section
         | 
| 21 | 
            +
            {% if test_target != nil and test_target != "" %}# The tests target name
         | 
| 22 | 
            +
            test_target: {{ test_target }}{% endif %}
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            {% if test_file_path != nil and test_file_path != "" %}# The file path for new tests
         | 
| 25 | 
            +
            test_file_path: {{ test_file_path }}{% endif %}
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            {% if test_group_path != nil and test_group_path != "" %}# The Xcode group path to new tests
         | 
| 28 | 
            +
            test_group_path: {{ test_group_path }}{% endif %}
         | 
| 29 | 
            +
            {% endif %}
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            ### Extra arguments [Optional]
         | 
| 32 | 
            +
            custom_parameters:
         | 
| 33 | 
            +
                mcflurry_swift: true
         | 
| 34 | 
            +
                extended_configure: true
         | 
| 35 | 
            +
                extended_configure_vars: services=Services
         | 
| 36 | 
            +
                embeddable_extended_configure_vars: services=Services
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            {% if podfile_path != nil or cartfile_path != nil %}### Dependencies settings section{% endif %}
         | 
| 39 | 
            +
            {% if podfile_path != nil %}podfile_path: {{ podfile_path }}{% endif %}
         | 
| 40 | 
            +
            {% if cartfile_path != nil %}cartfile_path: {{ cartfile_path }}{% endif %}
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            ### Templates filter [Optional]
         | 
| 43 | 
            +
            #templates_filter: regex_filter
         | 
| 44 | 
            +
            #templates_filter:
         | 
| 45 | 
            +
            #- '!viper' # should not match
         | 
| 46 | 
            +
            #- 'serv'   # should match
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            ### Templates [Optional], if not defined, then templates from shared catalogs will be used
         | 
| 49 | 
            +
            templates:
         | 
| 50 | 
            +
            {% if templates.size > 0 %}{% for item in templates %}- {{ item }}
         | 
| 51 | 
            +
            {% endfor %}{% else %}#- {name: local_template_name, local: 'absolute/file/path'}
         | 
| 52 | 
            +
            #- {name: remote_template_name, git: 'https://github.com/igrekde/remote_template'}
         | 
| 53 | 
            +
            #- {name: catalog_template_name}
         | 
| 54 | 
            +
            {% endif %}
         | 
| @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              SLASH_REGEX = /^\/|\/$/
         | 
| 4 | 
            +
              C99IDENTIFIER = /[^\w]/
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              PATH_TYPE_PROJECT = 'project'
         | 
| 7 | 
            +
              PATH_TYPE_TEST = 'test'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              # Represents currently generating code module
         | 
| 10 | 
            +
              class CodeModule
         | 
| 11 | 
            +
                attr_reader :name,
         | 
| 12 | 
            +
                            :description,
         | 
| 13 | 
            +
                            :author,
         | 
| 14 | 
            +
                            :company,
         | 
| 15 | 
            +
                            :year,
         | 
| 16 | 
            +
                            :prefix,
         | 
| 17 | 
            +
                            :project_name,
         | 
| 18 | 
            +
                            :product_module_name,
         | 
| 19 | 
            +
                            :xcodeproj_path,
         | 
| 20 | 
            +
                            :project_file_path,
         | 
| 21 | 
            +
                            :project_group_path,
         | 
| 22 | 
            +
                            :test_file_path,
         | 
| 23 | 
            +
                            :test_group_path,
         | 
| 24 | 
            +
                            :project_targets,
         | 
| 25 | 
            +
                            :test_targets,
         | 
| 26 | 
            +
                            :podfile_path,
         | 
| 27 | 
            +
                            :cartfile_path,
         | 
| 28 | 
            +
                            :custom_parameters
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def initialize(name, rambafile, options)
         | 
| 31 | 
            +
                  # Base initialization
         | 
| 32 | 
            +
                  @name = name
         | 
| 33 | 
            +
                  @description = options[:description] ? options[:description] : "#{name} module"
         | 
| 34 | 
            +
                  @author = rambafile[AUTHOR_NAME_KEY] ? rambafile[AUTHOR_NAME_KEY] : UserPreferences.obtain_username
         | 
| 35 | 
            +
                  @company = rambafile[COMPANY_KEY]
         | 
| 36 | 
            +
                  @year = Time.now.year.to_s
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  @prefix = rambafile[PROJECT_PREFIX_KEY]
         | 
| 39 | 
            +
                  @project_name = rambafile[PROJECT_NAME_KEY]
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  @product_module_name = rambafile[PRODUCT_MODULE_NAME_KEY]
         | 
| 42 | 
            +
                  @product_module_name = @project_name.gsub(C99IDENTIFIER, '_') if !@product_module_name && @project_name
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  @xcodeproj_path = rambafile[XCODEPROJ_PATH_KEY]
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  setup_file_and_group_paths(rambafile[PROJECT_FILE_PATH_KEY], rambafile[PROJECT_GROUP_PATH_KEY], PATH_TYPE_PROJECT)
         | 
| 47 | 
            +
                  setup_file_and_group_paths(rambafile[TEST_FILE_PATH_KEY], rambafile[TEST_GROUP_PATH_KEY], PATH_TYPE_TEST)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  @project_targets = [rambafile[PROJECT_TARGET_KEY]] if rambafile[PROJECT_TARGET_KEY]
         | 
| 50 | 
            +
                  @project_targets = rambafile[PROJECT_TARGETS_KEY] if rambafile[PROJECT_TARGETS_KEY]
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  @test_targets = [rambafile[TEST_TARGET_KEY]] if rambafile[TEST_TARGET_KEY]
         | 
| 53 | 
            +
                  @test_targets = rambafile[TEST_TARGETS_KEY] if rambafile[TEST_TARGETS_KEY]
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  # Custom parameters
         | 
| 56 | 
            +
                  custom_parameters_from_rambafile = rambafile[CUSTOM_PARAMETERS_KEY] || {}
         | 
| 57 | 
            +
                  custom_parameters_from_rambafile.each { |k, v| custom_parameters_from_rambafile[k] = v.to_s }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  @custom_parameters = custom_parameters_from_rambafile.merge(options[:custom_parameters] || {})
         | 
| 60 | 
            +
                  puts "@custom_parameters = #{@custom_parameters}"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  # Options adaptation
         | 
| 63 | 
            +
                  @author = options[:author] if options[:author]
         | 
| 64 | 
            +
                  @project_targets = options[:project_targets].split(',') if options[:project_targets]
         | 
| 65 | 
            +
                  @test_targets = options[:test_targets].split(',') if options[:test_targets]
         | 
| 66 | 
            +
                  
         | 
| 67 | 
            +
                  setup_file_and_group_paths(options[:project_file_path], options[:project_group_path], PATH_TYPE_PROJECT)
         | 
| 68 | 
            +
                  setup_file_and_group_paths(options[:test_file_path], options[:test_group_path], PATH_TYPE_TEST)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  # The priority is given to `module_path` and 'test_path' options
         | 
| 71 | 
            +
                  setup_file_and_group_paths(options[:module_path], options[:module_path], PATH_TYPE_PROJECT)
         | 
| 72 | 
            +
                  setup_file_and_group_paths(options[:test_path], options[:test_path], PATH_TYPE_TEST)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  @podfile_path = rambafile[PODFILE_PATH_KEY] if rambafile[PODFILE_PATH_KEY]
         | 
| 75 | 
            +
                  @cartfile_path = rambafile[CARTFILE_PATH_KEY] if rambafile[CARTFILE_PATH_KEY]
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                private
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def setup_file_and_group_paths(file_path, group_path, path_type)
         | 
| 81 | 
            +
                  if file_path || group_path
         | 
| 82 | 
            +
                    variable_name = "#{path_type}_file_path"
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    if file_path || !instance_variable_get("@#{variable_name}")
         | 
| 85 | 
            +
                      file_path = group_path unless file_path
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      variable_value = file_path.gsub(SLASH_REGEX, '')
         | 
| 88 | 
            +
                      variable_value = Pathname.new(variable_value).join(@name)
         | 
| 89 | 
            +
                      instance_variable_set("@#{variable_name}", variable_value)
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    variable_name = "#{path_type}_group_path"
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    if group_path || !instance_variable_get("@#{variable_name}")
         | 
| 95 | 
            +
                      group_path = file_path unless group_path
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                      variable_value = group_path.gsub(SLASH_REGEX, '')
         | 
| 98 | 
            +
                      variable_value = Pathname.new(variable_value).join(@name)
         | 
| 99 | 
            +
                      instance_variable_set("@#{variable_name}", variable_value)
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            require 'liquid'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Viperaptor
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # Responsible for generating code using provided liquid templates
         | 
| 6 | 
            +
            	class ContentGenerator
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                # Generates and returns a filename and a body of a specific code file.
         | 
| 9 | 
            +
                # @param file [Hash<String,String>] A hashmap with template's filename and filepath
         | 
| 10 | 
            +
                # @param scope [Hash<String,String>] A hashmap with module info
         | 
| 11 | 
            +
                # @param template [ModuleTemplate] The model describing a Viperaptor template used for code generation
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # @return [String], [String] The generated file_name and body
         | 
| 14 | 
            +
            		def self.create_file(file, scope, template)
         | 
| 15 | 
            +
            			file_source = IO.read(template.template_path.join(file[TEMPLATE_FILE_PATH_KEY]))
         | 
| 16 | 
            +
            			Liquid::Template.file_system = Liquid::LocalFileSystem.new(template.template_path.join('snippets'), '%s.liquid')
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            			template = Liquid::Template.parse(file_source)
         | 
| 19 | 
            +
            			filename_template = self.file_name_template(file)
         | 
| 20 | 
            +
                  
         | 
| 21 | 
            +
            			file_basename = File.basename(file[TEMPLATE_FILE_NAME_KEY])
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            			module_info = scope['module_info']
         | 
| 24 | 
            +
            			
         | 
| 25 | 
            +
            			module_info['file_basename'] = file_basename
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            			file_name = filename_template.render(scope)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            			module_info['file_name'] = file_name
         | 
| 30 | 
            +
            			module_info.delete('file_basename')
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            			content = template.render(scope)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            			return file_name, content
         | 
| 35 | 
            +
            		end
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
            		def self.file_name_template(file)
         | 
| 38 | 
            +
            			template_default_text = '{{ prefix }}{{ module_info.name }}{{ module_info.file_basename }}'
         | 
| 39 | 
            +
            			template_text = file[TEMPLATE_FILE_CUSTOM_NAME_KEY] || template_default_text
         | 
| 40 | 
            +
            			return Liquid::Template.parse(template_text)
         | 
| 41 | 
            +
            		end
         | 
| 42 | 
            +
            	end
         | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            require 'viperaptor/helpers/template_helper.rb'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Viperaptor
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # Represents a single Viperaptor module template
         | 
| 6 | 
            +
              class ModuleTemplate
         | 
| 7 | 
            +
                attr_reader :template_name, :template_path, :code_files, :test_files, :dependencies
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(name, options = nil)
         | 
| 10 | 
            +
                  spec_path = TemplateHelper.obtain_spec(name)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  unless options
         | 
| 13 | 
            +
                    spec = YAML.load_file(spec_path)
         | 
| 14 | 
            +
                  else
         | 
| 15 | 
            +
                    spec_source = IO.read(spec_path)
         | 
| 16 | 
            +
                    spec_template = Liquid::Template.parse(spec_source)
         | 
| 17 | 
            +
                    spec_content = spec_template.render(options)
         | 
| 18 | 
            +
                    spec = YAML.load(spec_content)
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  @code_files = spec[TEMPLATE_CODE_FILES_KEY]
         | 
| 22 | 
            +
                  @test_files = spec[TEMPLATE_TEST_FILES_KEY]
         | 
| 23 | 
            +
                  @template_name = spec[TEMPLATE_NAME_KEY]
         | 
| 24 | 
            +
                  @template_path = TemplateHelper.obtain_path(name)
         | 
| 25 | 
            +
                  @dependencies = spec[TEMPLATE_DEPENDENCIES_KEY]
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            require 'liquid'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Viperaptor
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # Responsible for creating Viperaptor configs
         | 
| 6 | 
            +
              class RambafileGenerator
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                # Creates a Rambafile using the provided properties hashmap
         | 
| 9 | 
            +
                # @param properties Rambafile properties
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # @return void
         | 
| 12 | 
            +
                def self.create_rambafile(properties)
         | 
| 13 | 
            +
                  file_source = IO.read(File.dirname(__FILE__) + '/Rambafile.liquid')
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  template = Liquid::Template.parse(file_source)
         | 
| 16 | 
            +
                  output = template.render(properties).gsub!(/[\n]{3,}/, "\n\n");
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  File.open(RAMBAFILE_NAME, 'w+') {|f|
         | 
| 19 | 
            +
                    f.write(output)
         | 
| 20 | 
            +
                  }
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            require 'viperaptor/constants/user_preferences_constants.rb'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Viperaptor
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # A class that provides methods for working with user-specific information.
         | 
| 6 | 
            +
              # Currently it has methods for obtaining and saving username, later it may be improved to something more general.
         | 
| 7 | 
            +
              class UserPreferences
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def self.obtain_templates_history
         | 
| 10 | 
            +
                  path = obtain_user_preferences_path
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  file_contents = open(path).read
         | 
| 13 | 
            +
                  preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  return preferences[USER_PREFERENCES_TEMPLATES_HISTORY_KEY] || []
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def self.add_template_to_history(template_name)
         | 
| 19 | 
            +
                  path = obtain_user_preferences_path
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  file_contents = open(path).read
         | 
| 22 | 
            +
                  preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  history = preferences[USER_PREFERENCES_TEMPLATES_HISTORY_KEY] || []
         | 
| 25 | 
            +
                  if history.count == 0 && history[0] != template_name
         | 
| 26 | 
            +
                    history.unshift(template_name)
         | 
| 27 | 
            +
                    max_history = 60
         | 
| 28 | 
            +
                    if history.count > max_history
         | 
| 29 | 
            +
                      history = history.slice(0, max_history)
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                  preferences[USER_PREFERENCES_TEMPLATES_HISTORY_KEY] = history
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  File.open(path, 'w+') { |f| f.write(preferences.to_yaml) }
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def self.obtain_custom_catalogs_repos
         | 
| 38 | 
            +
                  path = obtain_user_preferences_path
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  file_contents = open(path).read
         | 
| 41 | 
            +
                  preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  return preferences[USER_PREFERENCES_CATALOGS_KEY]
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def self.obtain_username
         | 
| 47 | 
            +
                  path = obtain_user_preferences_path
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  file_contents = open(path).read
         | 
| 50 | 
            +
                  preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  return preferences[USER_PREFERENCES_USERNAME_KEY]
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def self.save_username(username)
         | 
| 56 | 
            +
                  path = obtain_user_preferences_path
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  file_contents = open(path).read
         | 
| 59 | 
            +
                  preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  preferences[USER_PREFERENCES_USERNAME_KEY] = username
         | 
| 62 | 
            +
                  File.open(path, 'w+') { |f| f.write(preferences.to_yaml) }
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                private
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def self.obtain_user_preferences_path
         | 
| 68 | 
            +
                  home_path = Pathname.new(ENV['HOME'])
         | 
| 69 | 
            +
                             .join(APP_HOME_DIR)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  path_exists = Dir.exist?(home_path)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  unless path_exists
         | 
| 74 | 
            +
                    FileUtils.mkdir_p home_path
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  preferences_path = home_path.join(USER_PREFERENCES_FILE)
         | 
| 78 | 
            +
                  preferences_exist = File.file?(preferences_path)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  unless preferences_exist
         | 
| 81 | 
            +
                    File.open(preferences_path, 'w+') { |f| f.write('') }
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  return preferences_path
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # General constants
         | 
| 4 | 
            +
              RAMBAFILE_NAME = 'Rambafile'
         | 
| 5 | 
            +
              RAMBASPEC_EXTENSION = '.rambaspec'
         | 
| 6 | 
            +
              TEMPLATES_FOLDER = 'Templates'
         | 
| 7 | 
            +
              PREDEFINED_CATALOG_REPOS = %w[
         | 
| 8 | 
            +
                https://github.com/ladeiko/SwiftyViperTemplates
         | 
| 9 | 
            +
              ]
         | 
| 10 | 
            +
              APP_HOME_DIR = '.viperaptor'
         | 
| 11 | 
            +
              CATALOGS_DIR = 'catalogs'
         | 
| 12 | 
            +
              USER_PREFERENCES_FILE = 'user_preferences.yml'
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # Keys of Rambafile files
         | 
| 4 | 
            +
              COMPANY_KEY = 'company'
         | 
| 5 | 
            +
              AUTHOR_NAME_KEY = 'author'
         | 
| 6 | 
            +
              PROJECT_NAME_KEY = 'project_name'
         | 
| 7 | 
            +
              PROJECT_PREFIX_KEY = 'prefix'
         | 
| 8 | 
            +
              XCODEPROJ_PATH_KEY = 'xcodeproj_path'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              PROJECT_TARGET_KEY = 'project_target'
         | 
| 11 | 
            +
              PROJECT_TARGETS_KEY = 'project_targets'
         | 
| 12 | 
            +
              PROJECT_FILE_PATH_KEY = 'project_file_path'
         | 
| 13 | 
            +
              PROJECT_GROUP_PATH_KEY = 'project_group_path'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              PRODUCT_MODULE_NAME_KEY = 'product_module_name'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              TEST_TARGET_KEY = 'test_target'
         | 
| 18 | 
            +
              TEST_TARGETS_KEY = 'test_targets'
         | 
| 19 | 
            +
              TEST_FILE_PATH_KEY = 'test_file_path'
         | 
| 20 | 
            +
              TEST_GROUP_PATH_KEY = 'test_group_path'
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              PODFILE_PATH_KEY = 'podfile_path'
         | 
| 23 | 
            +
              CARTFILE_PATH_KEY = 'cartfile_path'
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              TEMPLATES_KEY = 'templates'
         | 
| 26 | 
            +
              CATALOGS_KEY = 'catalogs'
         | 
| 27 | 
            +
              TEMPLATE_DECLARATION_NAME_KEY = 'name'
         | 
| 28 | 
            +
              TEMPLATE_DECLARATION_LOCAL_KEY = 'local'
         | 
| 29 | 
            +
              TEMPLATE_DECLARATION_GIT_KEY = 'git'
         | 
| 30 | 
            +
              TEMPLATE_DECLARATION_BRANCH_KEY = 'branch'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              TEMPLATES_FILTER_KEY = 'templates_filter'
         | 
| 33 | 
            +
              CUSTOM_PARAMETERS_KEY = 'custom_parameters'
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              # Keys of .rambaspec files
         | 
| 4 | 
            +
              TEMPLATE_NAME_KEY = 'name'
         | 
| 5 | 
            +
              TEMPLATE_SUMMARY_KEY = 'summary'
         | 
| 6 | 
            +
              TEMPLATE_AUTHOR_KEY = 'author'
         | 
| 7 | 
            +
              TEMPLATE_VERSION_KEY = 'version'
         | 
| 8 | 
            +
              TEMPLATE_LICENSE_KEY = 'license'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              TEMPLATE_CODE_FILES_KEY = 'code_files'
         | 
| 11 | 
            +
              TEMPLATE_TEST_FILES_KEY = 'test_files'
         | 
| 12 | 
            +
              TEMPLATE_FILE_NAME_KEY = 'name'
         | 
| 13 | 
            +
              TEMPLATE_FILE_CUSTOM_NAME_KEY = 'custom_name'
         | 
| 14 | 
            +
              TEMPLATE_FILE_PATH_KEY = 'path'
         | 
| 15 | 
            +
              TEMPLATE_FILE_IS_RESOURCE_KEY = 'is_resource'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              TEMPLATE_DEPENDENCIES_KEY = 'dependencies'
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'cocoapods-core'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Viperaptor
         | 
| 4 | 
            +
              # Provides methods for check dependencies from rambaspec in podfile
         | 
| 5 | 
            +
              class DependencyChecker
         | 
| 6 | 
            +
                # Check Podfile for dependencies
         | 
| 7 | 
            +
                # @param dependencies [Array] Array of dependencies name
         | 
| 8 | 
            +
                # @param podfile_path [String] String of Podfile path
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # @return [void]
         | 
| 11 | 
            +
                def self.check_all_required_dependencies_has_in_podfile(dependencies, podfile_path)
         | 
| 12 | 
            +
                  return if !dependencies || dependencies.count == 0 || !podfile_path
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  dependencies_names = []
         | 
| 15 | 
            +
                  Pod::Podfile.from_file(Pathname.new(podfile_path)).dependencies.each do |dependency|
         | 
| 16 | 
            +
                    dependencies_names.push(dependency.name.gsub(/ .*/, ''))
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  not_existing_dependency = []
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  dependencies.each do |dependency_name|
         | 
| 22 | 
            +
                    unless dependencies_names.include?(dependency_name)
         | 
| 23 | 
            +
                      not_existing_dependency.push(dependency_name)
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  if not_existing_dependency.count > 0
         | 
| 28 | 
            +
                    puts "[Warning] Dependencies #{not_existing_dependency} missed in Podfile".yellow
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # Check Cartfile for dependencies
         | 
| 33 | 
            +
                # @param dependencies [Array] Array of dependencies name
         | 
| 34 | 
            +
                # @param cartfile_path [String] String of Podfile path
         | 
| 35 | 
            +
                #
         | 
| 36 | 
            +
                # @return [void]
         | 
| 37 | 
            +
                def self.check_all_required_dependencies_has_in_cartfile(dependencies, cartfile_path)
         | 
| 38 | 
            +
                  return if !dependencies || dependencies.count == 0 || !cartfile_path
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  cartfile_string = File.read(cartfile_path)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  not_existing_dependency = []
         | 
| 43 | 
            +
                  dependencies.each do |dependency_name|
         | 
| 44 | 
            +
                    unless cartfile_string.include?(dependency_name)
         | 
| 45 | 
            +
                      not_existing_dependency.push(dependency_name)
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  if not_existing_dependency.count > 0
         | 
| 50 | 
            +
                    puts "[Warning] Dependencies #{not_existing_dependency} missed in Cartfile".yellow
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
              # Provides methods for prepare parameters for displaying in table.
         | 
| 3 | 
            +
              class GenCommandTableParametersFormatter
         | 
| 4 | 
            +
                require 'json'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # This method prepared parameter for displaying
         | 
| 7 | 
            +
                def self.prepare_parameters_for_displaying(code_module, template_name)
         | 
| 8 | 
            +
                  params = {}
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  params['Targets'] = code_module.project_targets.join(',') if code_module.project_targets
         | 
| 11 | 
            +
                  params['Module path'] = code_module.project_file_path if code_module.project_file_path
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  if code_module.project_file_path != code_module.project_group_path
         | 
| 14 | 
            +
                    params['Module group path'] = code_module.project_group_path
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  params['Test targets'] = code_module.test_targets.join(',') if code_module.test_targets
         | 
| 18 | 
            +
                  params['Test file path'] = code_module.test_file_path if code_module.test_file_path
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  if code_module.test_file_path != code_module.test_group_path
         | 
| 21 | 
            +
                    params['Test group path'] = code_module.test_group_path
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  params['Template'] = template_name
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  unless code_module.custom_parameters.empty?
         | 
| 27 | 
            +
                    params['Custom parameters'] = code_module.custom_parameters.to_json
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  params
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class ModuleInfoGenerator
         | 
| 4 | 
            +
                attr_reader :scope
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                def initialize(code_module)
         | 
| 7 | 
            +
                  module_info = {
         | 
| 8 | 
            +
                      'name' => code_module.name,
         | 
| 9 | 
            +
                      'description' => code_module.description,
         | 
| 10 | 
            +
                      'project_name' => code_module.project_name,
         | 
| 11 | 
            +
                      'product_module_name' => code_module.product_module_name,
         | 
| 12 | 
            +
                      'project_targets' => code_module.project_targets,
         | 
| 13 | 
            +
                      'test_targets' => code_module.test_targets
         | 
| 14 | 
            +
                  }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  developer = {
         | 
| 17 | 
            +
                      'name' => code_module.author,
         | 
| 18 | 
            +
                      'company' => code_module.company
         | 
| 19 | 
            +
                  }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  @scope = {
         | 
| 22 | 
            +
                      'year' => code_module.year,
         | 
| 23 | 
            +
                      'date' => Time.now.strftime('%d/%m/%Y'),
         | 
| 24 | 
            +
                      'developer' => developer,
         | 
| 25 | 
            +
                      'module_info' => module_info,
         | 
| 26 | 
            +
                      'prefix' => code_module.prefix,
         | 
| 27 | 
            +
                      'custom_parameters' => code_module.custom_parameters
         | 
| 28 | 
            +
                  }
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
              # Provides methods for validating module
         | 
| 3 | 
            +
              class ModuleValidator
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                TARGET_TYPE_PROJECT = 'project'
         | 
| 6 | 
            +
                TARGET_TYPE_TEST = 'test'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                # Method validates module
         | 
| 9 | 
            +
                # @param code_module [CodeModule] The instance of CodeModule
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # @return [Void]
         | 
| 12 | 
            +
                def validate(code_module)
         | 
| 13 | 
            +
                  mandatory_fields = [COMPANY_KEY,
         | 
| 14 | 
            +
                                      PROJECT_NAME_KEY,
         | 
| 15 | 
            +
                                      XCODEPROJ_PATH_KEY]
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  mandatory_fields.each do |field|
         | 
| 18 | 
            +
                    unless code_module.instance_variable_get("@#{field}")
         | 
| 19 | 
            +
                      puts "Module is broken! *#{field}* field cannot be empty, because it is mandatory.".red
         | 
| 20 | 
            +
                      exit
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  project_failure_fields = all_project_failure_fields(code_module)
         | 
| 25 | 
            +
                  test_failure_fields = all_test_failure_fields(code_module)
         | 
| 26 | 
            +
                  failure_fields = project_failure_fields + test_failure_fields
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  if failure_fields.count > 0
         | 
| 29 | 
            +
                    puts "Module is broken! *#{failure_fields}* field cannot be empty, because it is mandatory.".red
         | 
| 30 | 
            +
                    exit
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                # Method which return all project failure fields
         | 
| 37 | 
            +
                # @param code_module [CodeModule] The instance of CodeModule
         | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # @return [Array]
         | 
| 40 | 
            +
                def all_project_failure_fields(code_module)
         | 
| 41 | 
            +
                  return [] if !code_module.project_targets && !code_module.project_file_path && !code_module.project_group_path
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  all_nil_mandatory_fields_for_target_type(TARGET_TYPE_PROJECT, code_module)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                # Method which return all test failure fields
         | 
| 47 | 
            +
                # @param code_module [CodeModule] The instance of CodeModule
         | 
| 48 | 
            +
                #
         | 
| 49 | 
            +
                # @return [Array]
         | 
| 50 | 
            +
                def all_test_failure_fields(code_module)
         | 
| 51 | 
            +
                  return [] if !code_module.test_targets && !code_module.test_file_path && !code_module.test_group_path
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  all_nil_mandatory_fields_for_target_type(TARGET_TYPE_TEST, code_module)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                # Method which return all failure fields for target_type
         | 
| 57 | 
            +
                # @param target_type [String] "project" or "test"
         | 
| 58 | 
            +
                # @param code_module [CodeModule] The instance of CodeModule
         | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # @return [Array]
         | 
| 61 | 
            +
                def all_nil_mandatory_fields_for_target_type(target_type, code_module)
         | 
| 62 | 
            +
                  fields = []
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  variable_name = "#{target_type}_targets"
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  unless code_module.instance_variable_get("@#{variable_name}")
         | 
| 67 | 
            +
                    target_const_value = Viperaptor.const_get(target_type.upcase + '_TARGET_KEY')
         | 
| 68 | 
            +
                    targets_const_value = Viperaptor.const_get(target_type.upcase + '_TARGETS_KEY')
         | 
| 69 | 
            +
                    fields.push(target_const_value)
         | 
| 70 | 
            +
                    fields.push(targets_const_value)
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  variable_name = "#{target_type}_file_path"
         | 
| 74 | 
            +
                  file_path_const_value = Viperaptor.const_get(target_type.upcase + '_FILE_PATH_KEY')
         | 
| 75 | 
            +
                  fields.push(file_path_const_value) unless code_module.instance_variable_get("@#{variable_name}")
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  variable_name = "#{target_type}_group_path"
         | 
| 78 | 
            +
                  group_path_const_value = Viperaptor.const_get(target_type.upcase + '_GROUP_PATH_KEY')
         | 
| 79 | 
            +
                  fields.push(group_path_const_value) unless code_module.instance_variable_get("@#{variable_name}")
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  fields
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module Viperaptor
         | 
| 2 | 
            +
              # Provides methods for print parameters in nice table.
         | 
| 3 | 
            +
              class PrintTable
         | 
| 4 | 
            +
                # This method prints out all the user inputs in a nice table.
         | 
| 5 | 
            +
                def self.print_values(values: nil, title: nil)
         | 
| 6 | 
            +
                  require 'terminal-table'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  params = {}
         | 
| 9 | 
            +
                  params[:rows] = values
         | 
| 10 | 
            +
                  params[:title] = title.green if title
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  puts ''
         | 
| 13 | 
            +
                  puts Terminal::Table.new(params)
         | 
| 14 | 
            +
                  puts ''
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         |