├── .codeclimate.yml ├── .gitignore ├── .rspec ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── VISION.md ├── bin ├── console ├── generamba └── setup ├── docs └── 2.x │ └── roadmap.md ├── generamba.gemspec ├── lib ├── generamba.rb └── generamba │ ├── cli │ ├── cli.rb │ ├── gen_command.rb │ ├── setup_command.rb │ ├── setup_username_command.rb │ ├── template │ │ ├── template_create_command.rb │ │ ├── template_group.rb │ │ ├── template_install_command.rb │ │ ├── template_list_command.rb │ │ └── template_search_command.rb │ ├── thor_extension.rb │ └── version_command.rb │ ├── code_generation │ ├── Rambafile.liquid │ ├── code_module.rb │ ├── content_generator.rb │ ├── module_template.rb │ └── rambafile_generator.rb │ ├── configuration │ └── user_preferences.rb │ ├── constants │ ├── constants.rb │ ├── rambafile_constants.rb │ ├── rambaspec_constants.rb │ └── user_preferences_constants.rb │ ├── helpers │ ├── dependency_checker.rb │ ├── gen_command_table_parameters_formatter.rb │ ├── module_info_generator.rb │ ├── module_validator.rb │ ├── print_table.rb │ ├── rambafile_validator.rb │ ├── template_helper.rb │ └── xcodeproj_helper.rb │ ├── module_generator.rb │ ├── template │ ├── creator │ │ ├── new_template │ │ │ ├── Code │ │ │ │ └── Service │ │ │ │ │ ├── service.h.liquid │ │ │ │ │ └── service.m.liquid │ │ │ ├── Tests │ │ │ │ └── Service │ │ │ │ │ └── service_tests.m.liquid │ │ │ └── template.rambaspec.liquid │ │ └── template_creator.rb │ ├── helpers │ │ ├── catalog_downloader.rb │ │ ├── catalog_template_list_helper.rb │ │ ├── catalog_template_search_helper.rb │ │ ├── catalog_terminator.rb │ │ └── rambaspec_validator.rb │ ├── installer │ │ ├── abstract_installer.rb │ │ ├── catalog_installer.rb │ │ ├── local_installer.rb │ │ ├── remote_installer.rb │ │ └── template_installer_factory.rb │ └── processor │ │ ├── template_declaration.rb │ │ └── template_processor.rb │ ├── tools │ └── string-colorize.rb │ └── version.rb └── spec ├── catalog_downloader_spec.rb ├── catalog_installer_spec.rb ├── catalog_template_list_helper_spec.rb ├── catalog_template_search_helper_spec.rb ├── dependency_checker_spec.rb ├── gen_command_table_parameters_formatter_spec.rb ├── generamba_spec.rb ├── spec_helper.rb ├── template_installer_factory_spec.rb ├── template_processor_spec.rb └── xcodeproj_helper_spec.rb /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | rubocop: 3 | enabled: true 4 | 5 | ratings: 6 | paths: 7 | - "**.rb" 8 | 9 | exclude_paths: 10 | - spec/**/* 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .rakeTasks 3 | encodings.xml 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | 4 | cache: 5 | bundler: true 6 | 7 | bundler_args: --jobs 3 --retry 3 8 | 9 | rvm: 10 | - 2.2.1 11 | - 2.3.1 12 | 13 | before_install: 14 | - gem update --system --no-document 15 | - gem install bundler -v 1.17.3 --no-document 16 | 17 | script: bundle exec rake 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [1.5.0](https://github.com/rambler-digital-solutions/Generamba/tree/1.5.0) (2019-04-29) 4 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.4.1...1.5.0) 5 | 6 | **Merged pull requests:** 7 | 8 | * Resolve Cocoapods Dependency hell [\#230](https://github.com/strongself/Generamba/pull/230) 9 | * Update xcodeproj & cocoapods-core dependencies [\#222](https://github.com/strongself/Generamba/pull/222) 10 | * Bump gems versions [\#201](https://github.com/strongself/Generamba/pull/201) 11 | * Remove the author-specific information from Rambafile [\#1](https://github.com/strongself/Generamba/issues/1) 12 | * Grammar fixes [\#1](https://github.com/strongself/Generamba/pull/198) 13 | 14 | ## [1.4.1](https://github.com/rambler-digital-solutions/Generamba/tree/1.4.1) (2017-09-04) 15 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.4.0...1.4.1) 16 | 17 | **Merged pull requests:** 18 | 19 | * Do not clear tests group if **test\_group\_path** is the same as **project\_group\_path** [\#193](https://github.com/rambler-digital-solutions/Generamba/pull/193) ([RamTararam](https://github.com/RamTararam)) 20 | 21 | ## [1.2.2](https://github.com/rambler-digital-solutions/Generamba/tree/1.2.2) (2016-09-27) 22 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.2.1...1.2.2) 23 | 24 | ## [1.2.1](https://github.com/rambler-digital-solutions/Generamba/tree/1.2.1) (2016-09-26) 25 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.2.0...1.2.1) 26 | 27 | **Fixed bugs:** 28 | 29 | - Crashes for multiple project targets [\#150](https://github.com/rambler-digital-solutions/Generamba/issues/150) 30 | 31 | **Closed issues:** 32 | 33 | - Can I create a template to generate swift-like structure objects for ObjC? [\#157](https://github.com/rambler-digital-solutions/Generamba/issues/157) 34 | - Error generate module with option :exclusive=\>true in Podfile [\#154](https://github.com/rambler-digital-solutions/Generamba/issues/154) 35 | - Module Generation Settings with project\_targets [\#151](https://github.com/rambler-digital-solutions/Generamba/issues/151) 36 | 37 | ## [1.2.0](https://github.com/rambler-digital-solutions/Generamba/tree/1.2.0) (2016-09-05) 38 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.1.0...1.2.0) 39 | 40 | **Fixed bugs:** 41 | 42 | - Файлы в корне модуля [\#147](https://github.com/rambler-digital-solutions/Generamba/issues/147) 43 | 44 | **Merged pull requests:** 45 | 46 | - Bug fixes and refactoring [\#148](https://github.com/rambler-digital-solutions/Generamba/pull/148) ([Beniamiiin](https://github.com/Beniamiiin)) 47 | 48 | ## [1.1.0](https://github.com/rambler-digital-solutions/Generamba/tree/1.1.0) (2016-09-05) 49 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/1.0.0...1.1.0) 50 | 51 | **Implemented enhancements:** 52 | 53 | - Refactoring rambafile and module validation [\#144](https://github.com/rambler-digital-solutions/Generamba/issues/144) 54 | - Update gen command parameters table [\#142](https://github.com/rambler-digital-solutions/Generamba/issues/142) 55 | - If statement in .rambaspec [\#128](https://github.com/rambler-digital-solutions/Generamba/issues/128) 56 | - Ask user, he want create all modules by one path? [\#126](https://github.com/rambler-digital-solutions/Generamba/issues/126) 57 | 58 | **Fixed bugs:** 59 | 60 | - Generates files with a wrongs paths. [\#138](https://github.com/rambler-digital-solutions/Generamba/issues/138) 61 | - Install templates from git repo is broken? [\#135](https://github.com/rambler-digital-solutions/Generamba/issues/135) 62 | - Error creating the Xcode group path to new modules [\#129](https://github.com/rambler-digital-solutions/Generamba/issues/129) 63 | - If statement in .rambaspec [\#128](https://github.com/rambler-digital-solutions/Generamba/issues/128) 64 | 65 | **Closed issues:** 66 | 67 | - Error: `parse': \(Rambafile\): block sequence entries are not allowed [\#127](https://github.com/rambler-digital-solutions/Generamba/issues/127) 68 | 69 | **Merged pull requests:** 70 | 71 | - \[RESOLVED\] Added question in setup command \#126 [\#146](https://github.com/rambler-digital-solutions/Generamba/pull/146) ([Beniamiiin](https://github.com/Beniamiiin)) 72 | - \[RESOLVED\] Updated logic of rambafile and module validation \#144 [\#145](https://github.com/rambler-digital-solutions/Generamba/pull/145) ([Beniamiiin](https://github.com/Beniamiiin)) 73 | - \[RESOLVED\] Updated gen command parameters table \#142 [\#143](https://github.com/rambler-digital-solutions/Generamba/pull/143) ([Beniamiiin](https://github.com/Beniamiiin)) 74 | - \[RESOLVED\] Added ability use custom parameters in rambaspec \#128 [\#141](https://github.com/rambler-digital-solutions/Generamba/pull/141) ([Beniamiiin](https://github.com/Beniamiiin)) 75 | - \[FIXED\] Fixed bug \#138 [\#140](https://github.com/rambler-digital-solutions/Generamba/pull/140) ([Beniamiiin](https://github.com/Beniamiiin)) 76 | - Added product\_module\_name to code\_module [\#139](https://github.com/rambler-digital-solutions/Generamba/pull/139) ([mogol](https://github.com/mogol)) 77 | - A little refactoring [\#137](https://github.com/rambler-digital-solutions/Generamba/pull/137) ([Beniamiiin](https://github.com/Beniamiiin)) 78 | - Support Ruby 2.3.1 [\#134](https://github.com/rambler-digital-solutions/Generamba/pull/134) ([ivaravko](https://github.com/ivaravko)) 79 | - Helpers code style [\#133](https://github.com/rambler-digital-solutions/Generamba/pull/133) ([ivaravko](https://github.com/ivaravko)) 80 | - Adds Code Climate config [\#132](https://github.com/rambler-digital-solutions/Generamba/pull/132) ([ivaravko](https://github.com/ivaravko)) 81 | 82 | ## [1.0.0](https://github.com/rambler-digital-solutions/Generamba/tree/1.0.0) (2016-07-23) 83 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.8...1.0.0) 84 | 85 | **Implemented enhancements:** 86 | 87 | - Add more info to `generamba version` command [\#122](https://github.com/rambler-digital-solutions/Generamba/issues/122) 88 | - Empty group in Xcode structure [\#118](https://github.com/rambler-digital-solutions/Generamba/issues/118) 89 | - Gem dependency of cocoapods-core-0.39.0 [\#114](https://github.com/rambler-digital-solutions/Generamba/issues/114) 90 | - Remove adding module name in module\_group\_path if it set from cli [\#98](https://github.com/rambler-digital-solutions/Generamba/issues/98) 91 | - Check for missing Carthage dependencies [\#83](https://github.com/rambler-digital-solutions/Generamba/issues/83) 92 | - Automatic tests generation based on protocols and class interfaces [\#71](https://github.com/rambler-digital-solutions/Generamba/issues/71) 93 | - Track all modules created by Generamba [\#70](https://github.com/rambler-digital-solutions/Generamba/issues/70) 94 | 95 | **Fixed bugs:** 96 | 97 | - Can't add file with type . Only 'source' and 'resource' are acceptable [\#116](https://github.com/rambler-digital-solutions/Generamba/issues/116) 98 | - If option --module-group-path starts with '/' Generamba add folder with empty name in root folder [\#99](https://github.com/rambler-digital-solutions/Generamba/issues/99) 99 | 100 | ## [0.7.8](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.8) (2016-05-16) 101 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.7...0.7.8) 102 | 103 | ## [0.7.7](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.7) (2016-05-10) 104 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.6...0.7.7) 105 | 106 | **Implemented enhancements:** 107 | 108 | - Project target name in module\_info [\#110](https://github.com/rambler-digital-solutions/Generamba/issues/110) 109 | - Give a user more information about templates during `generamba setup` [\#86](https://github.com/rambler-digital-solutions/Generamba/issues/86) 110 | 111 | ## [0.7.6](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.6) (2016-04-30) 112 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.5...0.7.6) 113 | 114 | **Fixed bugs:** 115 | 116 | - Failed adding files to project [\#96](https://github.com/rambler-digital-solutions/Generamba/issues/96) 117 | 118 | ## [0.7.5](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.5) (2016-04-24) 119 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.4...0.7.5) 120 | 121 | **Implemented enhancements:** 122 | 123 | - Get templates from git by specific a branch [\#104](https://github.com/rambler-digital-solutions/Generamba/issues/104) 124 | 125 | **Fixed bugs:** 126 | 127 | - Non human exception when wrong template name or catalog [\#77](https://github.com/rambler-digital-solutions/Generamba/issues/77) 128 | 129 | **Closed issues:** 130 | 131 | - Module Name in .rambaspec support [\#102](https://github.com/rambler-digital-solutions/Generamba/issues/102) 132 | - Generate with project and tests targets in command line [\#97](https://github.com/rambler-digital-solutions/Generamba/issues/97) 133 | - Easy setup template path to local templates in Rambafile [\#94](https://github.com/rambler-digital-solutions/Generamba/issues/94) 134 | 135 | ## [0.7.4](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.4) (2016-02-21) 136 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.3...0.7.4) 137 | 138 | **Implemented enhancements:** 139 | 140 | - Asks the user if he needed tests in `generamba setup` command [\#85](https://github.com/rambler-digital-solutions/Generamba/issues/85) 141 | - Improve the visualization of commands with parameters [\#69](https://github.com/rambler-digital-solutions/Generamba/issues/69) 142 | - Can we introduce something similar to view partials in rails \(for duplicated parts of views\) [\#62](https://github.com/rambler-digital-solutions/Generamba/issues/62) 143 | 144 | **Fixed bugs:** 145 | 146 | - Add resources\(xib, storyboard and etc\) in project bundle resources. [\#84](https://github.com/rambler-digital-solutions/Generamba/issues/84) 147 | 148 | **Closed issues:** 149 | 150 | - Get "Liquid error: This liquid context does not allow includes." on all files in just generated module [\#93](https://github.com/rambler-digital-solutions/Generamba/issues/93) 151 | 152 | ## [0.7.3](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.3) (2016-01-24) 153 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.2...0.7.3) 154 | 155 | **Implemented enhancements:** 156 | 157 | - Enhance `generamba template search` to search templates in custom catalogs [\#75](https://github.com/rambler-digital-solutions/Generamba/issues/75) 158 | - Enhance `generamba template list` to browse custom catalogs [\#74](https://github.com/rambler-digital-solutions/Generamba/issues/74) 159 | 160 | **Closed issues:** 161 | 162 | - Can we use constants from rambafile in templates? [\#81](https://github.com/rambler-digital-solutions/Generamba/issues/81) 163 | 164 | ## [0.7.2](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.2) (2016-01-10) 165 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.1...0.7.2) 166 | 167 | **Fixed bugs:** 168 | 169 | - Don't set target in swift projects [\#65](https://github.com/rambler-digital-solutions/Generamba/issues/65) 170 | - Errors if default path is empty [\#64](https://github.com/rambler-digital-solutions/Generamba/issues/64) 171 | - Default template set can not be used [\#60](https://github.com/rambler-digital-solutions/Generamba/issues/60) 172 | 173 | ## [0.7.1](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.1) (2015-12-20) 174 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.7.0...0.7.1) 175 | 176 | ## [0.7.0](https://github.com/rambler-digital-solutions/Generamba/tree/0.7.0) (2015-12-13) 177 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.6.2...0.7.0) 178 | 179 | ## [0.6.2](https://github.com/rambler-digital-solutions/Generamba/tree/0.6.2) (2015-11-25) 180 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.6.1...0.6.2) 181 | 182 | ## [0.6.1](https://github.com/rambler-digital-solutions/Generamba/tree/0.6.1) (2015-11-21) 183 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.6.0...0.6.1) 184 | 185 | ## [0.6.0](https://github.com/rambler-digital-solutions/Generamba/tree/0.6.0) (2015-11-15) 186 | [Full Changelog](https://github.com/rambler-digital-solutions/Generamba/compare/0.5.0...0.6.0) 187 | 188 | ## [0.5.0](https://github.com/rambler-digital-solutions/Generamba/tree/0.5.0) (2015-11-01) 189 | 190 | 191 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 192 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in generamba.gemspec 4 | gemspec 5 | 6 | gem 'rspec', '~> 3.0' -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Egor Tolstoy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [![Build Status](https://travis-ci.org/rambler-digital-solutions/Generamba.svg)](https://travis-ci.org/rambler-digital-solutions/Generamba) 6 | [![Gem Version](https://badge.fury.io/rb/generamba.svg)](https://badge.fury.io/rb/generamba) 7 | [![Code Climate](https://codeclimate.com/github/rambler-digital-solutions/Generamba/badges/gpa.svg)](https://codeclimate.com/github/rambler-digital-solutions/Generamba) 8 | 9 | **Generamba** is a code generator made for working with Xcode. Primarily it is designed to generate VIPER modules but it is quite easy to customize it for generation of any other classes (both in Objective-C and Swift). 10 | 11 | *We keep evolving Generamba:* 12 | - [Vision](https://github.com/rambler-digital-solutions/Generamba/blob/develop/VISION.md), 13 | - [Changelog](https://github.com/rambler-digital-solutions/Generamba/blob/develop/CHANGELOG.md), 14 | - [Release notes](https://github.com/rambler-digital-solutions/Generamba/releases). 15 | 16 | Besides, we started working on 2.0 version with powerful DSL and plugins. Check the [release roadmap](https://github.com/rambler-digital-solutions/Generamba/blob/develop/docs/2.x/roadmap.md)! 17 | 18 | ![Generamba Screenshot](https://habrastorage.org/files/b98/770/b37/b98770b37dc54de98daf0e22fea38478.gif) 19 | 20 | ### Key features 21 | 22 | - Supports work with *.xcodeproj* files out of the box. All generated class files are automatically placed to specific folders and groups of Xcode project. 23 | - Can generate both code itself and tests adding them to right targets. 24 | - Based on work with [liquid-templates](https://github.com/Shopify/liquid) that have plain and readable syntax in comparison with templates for Xcode. 25 | - It is very easy to create a new module: `generamba gen [MODULE_NAME] [TEMPLATE_NAME]`. You do not need to input a bunch of data each time because each project corresponds to only one configuration file that holds standard file system and Xcode-project pathes, names of targets, information about the author. 26 | 27 | ### Installation 28 | 29 | > Ruby 2.2 or later version is required. To check your current Ruby version run this command in terminal: 30 | ```bash 31 | $ ruby --version 32 | ``` 33 | When necessary you can install the required Ruby version with the help of [`rvm`](http://octopress.org/docs/setup/rvm/) or [`rbenv`](http://octopress.org/docs/setup/rbenv/). 34 | 35 | Run the command `gem install generamba`. 36 | 37 | ### Usage 38 | 1. Run [`generamba setup`](https://github.com/rambler-digital-solutions/Generamba/wiki/Available-Commands#basic-generamba-configuration) in the project root folder. This command helps to create [Rambafile](https://github.com/rambler-digital-solutions/Generamba/wiki/Rambafile-Structure) that define all configuration needed to generate code. You can modify this file directly in future. 39 | 2. Add all templates planned to use in the project to the generated [Rambafile](https://github.com/rambler-digital-solutions/Generamba/wiki/Rambafile-Structure). You can begin with one of the templates from our catalog: `{name: 'rviper_controller'}`. 40 | 3. Run [`generamba template install`](https://github.com/rambler-digital-solutions/Generamba/wiki/Available-Commands#template-installation). All the templates will be placed in the '/Templates' folder of your current project. 41 | 4. Run [`generamba gen [MODULE_NAME] [TEMPLATE_NAME]`](https://github.com/rambler-digital-solutions/Generamba/wiki/Available-Commands#module-generation) - It creates module with specific name from specific template. 42 | 43 | ### Additional info 44 | 45 | Run `generamba help` to learn more about each of the Generamba features. 46 | 47 | **Wiki:** 48 | - [Command list](https://github.com/rambler-digital-solutions/Generamba/wiki/Available-Commands) 49 | - [Understanding the Rambafile](https://github.com/rambler-digital-solutions/Generamba/wiki/Rambafile-Structure) 50 | - [Understanding templates](https://github.com/rambler-digital-solutions/Generamba/wiki/Template-Structure) 51 | 52 | **Other materials:** 53 | - [Russian] Rambler.iOS V: Generamba and Code Generation ([Slides](http://www.slideshare.net/Rambler-iOS/viper-56423582) | [Video](http://www.youtube.com/watch?v=NXNiN9FaUnY)) 54 | - [Introduction to Generamba](http://etolstoy.com/2016/02/10/generamba/) 55 | 56 | ### Authors 57 | 58 | - Egor Tolstoy, Beniamin Sarkisyan, Andrey Zarembo and the rest of [Rambler.iOS team](https://github.com/orgs/rambler-digital-solutions/teams/ios-team). 59 | 60 | ### License 61 | 62 | MIT 63 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /VISION.md: -------------------------------------------------------------------------------- 1 | # Vision 2 | 3 | Generamba is a tool with one primary role: 4 | 5 | > Routine tasks automation through code generation. 6 | 7 | At first it was designed to work only with Xcode - IDE for iOS/macOS, but as time passed we saw a lot of opportunities to use it in other circumstances, e.g. in frontend, Android and backend development. 8 | 9 | ### What Is Generamba 10 | 11 | The Generamba core is a simple generator that takes a [liquid](https://shopify.github.io/liquid/) template and a set of parameters as an input and produces a text snippet as an output. Everything else is built upon this simple concept. 12 | 13 | There are a lot of IDE's, package managers and other stuff which is related to creating new code modules. We try to keep the core small but provide rich opportunities for integration with all these tools via plugins *(in development)* and templates *(ready to use)* systems. 14 | 15 | ### General Aim 16 | 17 | Our general aim is to provide a simple yet extensible way to generate any piece of code. The beginner should be able to create new code modules without complex configuration process. Generamba capabilities should grow as fast as the user needs, providing ways to validate the environment status, integrations with different file systems and IDEs, complex statements inside templates and so on. 18 | 19 | ### Templates 20 | 21 | Code templates are the fuel for Generamba. We use a [liquid](https://shopify.github.io/liquid/) markup, because of its beautiful syntax and the ability to implement really complex logic. 22 | 23 | Templates may be private as well as adopted for public. We provide a number of ways to install them - using local paths, remote git repository, public or private catalogs. 24 | 25 | We also maintain a [public catalog](https://github.com/rambler-digital-solutions/generamba-catalog) which has some popular templates for iOS/macOS projects. 26 | 27 | ### Communication 28 | 29 | We try to keep all the discussion about Generamba within [GitHub issues](https://github.com/rambler-digital-solutions/Generamba/issues). This attitude allows us to keep our processes and intentions transparent. 30 | 31 | Besides it there is a private Telegram channel where maintainers discuss work questions. We are considering moving to Slack or Gitter at some point in the future - but we haven't grown big enough yet. 32 | 33 | ### Contributions 34 | 35 | Despite main maintainers are engineers from Rambler&Co team, we are glad to welcome external contributors. Generamba has evolved a lot since the first alpha release thanks to a lot of ideas from its users that came in the form of issues and pull requests. 36 | 37 | The aim of our core team is to adopt Generamba possibilities to each user needs and provide him a way to extend its functionality himself. 38 | 39 | ### ... 40 | 41 | The structure and idea of *VISION.md* file was taken from the [Danger](https://github.com/danger/danger) project and [@orta](https://github.com/orta). 42 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "generamba" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/generamba: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'generamba' 4 | 5 | Generamba::CLI::Application.start(ARGV) -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /docs/2.x/roadmap.md: -------------------------------------------------------------------------------- 1 | # Generamba 2.0 Roadmap 2 | ### Overview 3 | 4 | The main aim of 2.0 release is to decouple Generamba from iOS/macOS development and make it available for other areas as well: backend, frontend, Android. 5 | 6 | We'll definitely break the backwards compatibility with *Rambafile* and templates created for the 1.x Generamba versions. To simplify the upgrading process we're considering writing some automatic migration tools. 7 | 8 | ### High-Level Tasks 9 | 10 | * Switch *Rambafile* from `yml` to a custom DSL format. 11 | * Wrap repetitive tasks into *ramba*'s - the nearest analogs are *lane*'s from `fastlane`. 12 | * Introduce the concept of two types of *plugins*: system (validation, save) and general-purpose. 13 | * Allow the usage of different types of hooks - `before`, `after`, `error`. 14 | * Add validation plugins, that are available to check the environment before code generation. 15 | * Extract the saving logic from the Generamba core to plugins, both to filesystem and Xcode. 16 | * Add saving plugins, that are available to process the text snippet created by generamba and save it somewhere. 17 | * Extract the validation logic from the Generamba core to plugins, both for CocoaPods and Carthage. 18 | 19 | ### Generamba Flow 20 | 21 | A typical generation process consists of three steps: 22 | * validation 23 | * generation 24 | * saving 25 | 26 | #### Validation 27 | 28 | That step is used to validate the environment status before code generation. It's a perfect place to implement logic of checking the required packages versions, the presence of system libraries, git status and so on. 29 | 30 | If one of checks returns an error, the overall validation process doesn't stop until all of validation methods execute. This is crucial for a smooth user experience. 31 | 32 | #### Generation 33 | 34 | That's the core functionality of Generamba. A user provides a template name and some options which are used as an input. After the generation Generamba produces a text snippet as an output. 35 | 36 | There can be multiple generation actions as well. All of them are executed in the writing order and their results are stored in-memory. 37 | 38 | #### Saving 39 | 40 | This step defines what to do with a generation output. The most obvious options are to store it as a file in some directory on the disk, embed it in your IDE, upload somewhere or print in log. 41 | 42 | If there are multiple saving actions, each of them is applied to each generation output. They are executed in the writing order. 43 | 44 | ### DSL 45 | 46 | #### Basic Rambafile Structure 47 | 48 | The main building block of *Rambafile* is `ramba`: 49 | 50 | ``` 51 | desc "Creates a new Xcode project" 52 | ramba :viper_module do 53 | # Detailed description of generation steps 54 | end 55 | ``` 56 | 57 | The *Rambafile* may be monolithic as well as decomposed to multiple files. This is available via special functions: 58 | - `ramba_import_local('path')` - imports the contents of `Rambafile` stored locally. 59 | - `ramba_import_git('git', 'branch')` - imports the contents of Rambafile stored in remote git repository. 60 | 61 | #### Setting Options 62 | 63 | A user can set options in any part of *Rambafile* structure. It's important to note, that option, defined on the next level of method hierarchy, overwrites it's previous declaration. That allows to have a default value for some key and redefine it in each `ramba`. 64 | ``` 65 | set :project_name, 'MyProject' 66 | 67 | ramba :viper_module do 68 | set :project_name, 'MyProject' 69 | end 70 | ``` 71 | 72 | It's also available to set an option using lambda: 73 | ``` 74 | set :date_string, -> { 75 | "Current date: #{Date.now}" 76 | } 77 | ``` 78 | 79 | > How options are set in Rambafile... 80 | > 81 | 82 | Besides `ramba` -specific options, there are template-specific options as well. They may correspond to a specific template and be defined in the *.rambaspec* file. 83 | 84 | > The saving action for Xcode should behave differently depending on whether a generated file it's a code file or some resource. So, there is a special option for each file of Xcode templates: 85 | > `- {name: Router/RouterTests.m, path: Tests/Router/router_tests.m.liquid, is_resource: false}` 86 | 87 | #### Hooks 88 | 89 | There are multiple types of hooks, which make possible to perform some action or redefine an option in certain moments of generation cycle. 90 | 91 | ##### `before` hooks 92 | 93 | A user can specify a hook, that'll execute before a specific `ramba`: 94 | ``` 95 | before :viper_module do 96 | # Some logic here... 97 | end 98 | ``` 99 | 100 | It's also possible to write a hook that'll execute before each `ramba`: 101 | ``` 102 | before_each do |ramba_name| 103 | # Some logic here... 104 | end 105 | ``` 106 | 107 | ##### `after` hooks 108 | 109 | A user can specify a hook, that'll execute after a specific `ramba`: 110 | ``` 111 | after :viper_module do 112 | # Some logic here... 113 | end 114 | ``` 115 | 116 | It's also possible to write a hook that'll execute after each `ramba`: 117 | ``` 118 | after_each do |ramba_name| 119 | # Some logic here... 120 | end 121 | ``` 122 | 123 | ##### `error` hooks 124 | 125 | If any action returns an error, it's possible to customize the resulting behavior for a specific `ramba` : 126 | ``` 127 | error :viper_module do 128 | # Some logic here... 129 | end 130 | ``` 131 | It's also possible to write a hook that'll execute in case of error in any `ramba`: 132 | ``` 133 | error_each do |ramba_name| 134 | # Some logic here... 135 | end 136 | ``` 137 | 138 | #### `ramba` structure 139 | 140 | As we've already mentioned, `ramba` consists of three main steps. Multiple actions in each step are executed in the writing order. 141 | 142 | ##### Validation actions 143 | 144 | ``` 145 | ramba :viper_module do 146 | ... 147 | validate :validate_plugin_name do 148 | set :some_option, 'some_value' 149 | any_custom_action() 150 | end 151 | ... 152 | end 153 | ``` 154 | 155 | This notation means the following steps: 156 | - The lambda passed to the validate action is executed. 157 | - The plugin with name `validate_plugin_name` is called with options modified by lambda. 158 | 159 | The notation can be simplified to: 160 | ``` 161 | validate :validate_plugin_name 162 | ``` 163 | 164 | ##### Generation actions 165 | 166 | ``` 167 | ramba :viper_module do 168 | ... 169 | gen :viper_module do 170 | set :some_option, 'some_value' 171 | any_custom_action(parameter1) 172 | end 173 | ... 174 | end 175 | ``` 176 | 177 | This notation means the following steps: 178 | - The lambda passed to the gen action is executed. 179 | * The `gen` command is called with a template *viper_module* and options modified by lambda. 180 | 181 | The notation can be simplified to: 182 | ``` 183 | gen :viper_module 184 | ``` 185 | 186 | ##### Saving actions 187 | 188 | ``` 189 | ramba :viper_module do 190 | ... 191 | save :save_plugin_name do 192 | set :some_option, 'some_value' 193 | any_custom_action() 194 | end 195 | ... 196 | end 197 | ``` 198 | 199 | This notation means the following steps: 200 | - The lambda passed to the saving action is executed. 201 | - The plugin with name `save_plugin_name` is called with options modified by lambda. 202 | 203 | The notation can be simplified to: 204 | ``` 205 | save :save_plugin_name 206 | ``` 207 | 208 | ##### Custom Actions 209 | 210 | Besides system plugins (validation and saving), a user can create a general-purpose plugin which contains some specific reusable logic. E.g. a plugin which clears the environment - calls `git reset`, uninstalls some packages and so on. It's called simply by calling it's name: 211 | ``` 212 | ramba :viper_module do 213 | ... 214 | any_custom_action() 215 | ... 216 | end 217 | ``` 218 | 219 | ##### Templates and Catalogs 220 | 221 | Templates and tempalte catalogs, used for code generation, can be specified right in the `ramba`'s. 222 | 223 | ``` 224 | ramba :viper_module do 225 | catalog 'https://github.com/user/catalog', branch:'develop' 226 | template 'viper_ios', version:'1.2.5' 227 | template 'viper_ios2', path:'/local_templates' 228 | end 229 | ``` 230 | 231 | - `catalog` keyword specifies a custom template catalog (like https://github.com/rambler-digital-solutions/generamba-catalog). 232 | - `branch` parameter specifies a concrete catalog branch 233 | - `template` keyword declares a template that needs to be installed 234 | - `version` parameter specifies a concrete template version. 235 | - `path` parameter specifies a local path to the template. 236 | - `git` parameter specifies a remote repository path to a template. 237 | - `branch` parameter is used together with `git` to specify a branch. 238 | 239 | The `generamba template install` command aggregates all of the templates from the `Rambafile` and installs them in a `.generamba` hidden directory. When a user triggers some `ramba`, templates are loaded from this directory. The only exception are local templates - they are always installed from the specified in the `Rambafile` local path. 240 | 241 | ### Plugins 242 | 243 | The main ideas behind plugin system are: 244 | 245 | - Increase code reusability between different projects, 246 | * Keep the Generamba core as simple as possible, 247 | * Abstract from specific implementations of different IDEs, 248 | * Allow users to easily extend Generamba functionality for their needs. 249 | 250 | As we've already mentioned, there are two types of plugins: 251 | 252 | - System plugins - validation and saving, 253 | * Custom plugins. 254 | 255 | The main difference between them is how system calls them during `ramba` execution. 256 | 257 | #### Plugin Structure 258 | 259 | ``` 260 | module Generamba 261 | module Plugins 262 | class CocoaPodsPackageVersionValidationPlugin < ValidationPlugin 263 | # The main body of a plugin 264 | def self.run(params) 265 | # Loads Podfile 266 | # Analyzez dependencies version 267 | # Compares these versions to the passed options 268 | end 269 | 270 | # The description of what this plugin does 271 | def self.description 272 | 'Verifies dependencies version in the project Podfile' 273 | end 274 | 275 | # Explicitly declaring available plugin options 276 | def self.available_options 277 | [ 278 | Generamba::ConfigItem.new(key: :package_versions, 279 | description: "The hash with dependencies names and required versions", 280 | default_value: []) 281 | ] 282 | end 283 | 284 | # Declaring the output parameters 285 | def self.output 286 | [ 287 | ['COCOAPODS_CHECK_RESULT', 'The result of dependency checking'] 288 | ] 289 | end 290 | 291 | # Who created this plugin 292 | def self.authors 293 | ["etolstoy"] 294 | end 295 | end 296 | end 297 | end 298 | ``` 299 | 300 | The same structure applies to other kinds of plugins. The only difference is the base class for the plugin - it can be `ValidationPlugin`, `SavingPlugin`, `Plugin`. 301 | 302 | We've borrowed the plugin structure from `fastlane`. 303 | 304 | #### Plugin Distribution 305 | 306 | To avoid over-engineering in this version plugins will be distributed together with Generamba binary. If there'll be a lot of community-created plugins, will think about switching to other distribution system. 307 | 308 | It's also possible to store plugins near the `Rambafile` in a separate directory `/plugins`. They'll be loaded by Generamba automatically. 309 | 310 | ### Rambafile Example 311 | 312 | ``` 313 | ramba_import_local('path') 314 | ramba_import_git('git', 'branch') 315 | 316 | set :project_name, 'LiveJournal' 317 | 318 | before :viper_module do 319 | set :company, 'Rambler&Co' 320 | set :xcodeproj_path, 'LiveJournal.xcodeproj' 321 | set :project_targets, ['LiveJournal1', 'LiveJournal2'] 322 | set :test_target, 'LiveJournalTests' 323 | end 324 | 325 | desc "Creates a simple VIPER module" 326 | ramba :viper_module do 327 | validate :plugin_validate_name do 328 | set :some_option, '456' 329 | any_custom_action() 330 | end 331 | 332 | gen :template_name do 333 | set :some_option, '123' 334 | any_custom_action(parameter1) 335 | end 336 | 337 | save :file_system do 338 | set :some_option, '789' 339 | end 340 | 341 | save :xcode_proj 342 | end 343 | 344 | desc "Creates a new Xcode project" 345 | ramba :create_project do 346 | sh("liftoff") # Calling a shell script which invokes a 'liftoff' utility 347 | add_pods(["Typhoon", "MagicalRecord"]) 348 | 349 | gen :app_delegate 350 | gen :core_data_stack 351 | 352 | save :file_system 353 | save :xcode_proj 354 | end 355 | 356 | # It's called after each 'ramba' execution 357 | after_each do |ramba_name| 358 | # Some logic here 359 | end 360 | 361 | # It's called in case of an error in any 'ramba' 362 | error :viper_module do 363 | # Some error handling logic here 364 | end 365 | ``` 366 | -------------------------------------------------------------------------------- /generamba.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'generamba/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'generamba' 8 | spec.version = Generamba::VERSION 9 | spec.authors = ['Egor Tolstoy', 'Andrey Zarembo', 'Beniamin Sarkisyan', 'Aleksandr Sychev'] 10 | spec.email = 'rambler.ios@rambler-co.ru' 11 | 12 | spec.summary = 'Advanced code generator for Xcode projects with a nice and flexible template system.' 13 | spec.description = 'Generamba is a powerful and easy-to-use Xcode code generator. It provides a project-based configuration, flexible templates system, the ability to generate code and tests simultaneously.' 14 | spec.homepage = 'https://github.com/rambler-digital-solutions/Generamba' 15 | spec.license = 'MIT' 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 18 | spec.executables = ['generamba'] 19 | spec.require_paths = ['lib'] 20 | 21 | spec.required_ruby_version = '>= 2.2' 22 | 23 | spec.add_runtime_dependency 'thor', '0.19.1' 24 | spec.add_runtime_dependency 'xcodeproj', '>= 1.6.0', '< 2.0.0' 25 | spec.add_runtime_dependency 'liquid', '4.0.0' 26 | spec.add_runtime_dependency 'git', '1.2.9.1' 27 | spec.add_runtime_dependency 'cocoapods-core', '>= 1.4.0', '< 2.0.0' 28 | spec.add_runtime_dependency 'terminal-table', '1.4.5' 29 | 30 | spec.add_development_dependency 'bundler', '~> 1.10' 31 | spec.add_development_dependency 'rake', '~> 10.0' 32 | spec.add_development_dependency 'rspec', '~> 3.4' 33 | spec.add_development_dependency 'fakefs', '~> 0.6.1' 34 | # ActiveSupport dependency is not used by dashramba; instead some other dependency 35 | # requires it. We lock it to 4.2.7 so as to avoid using 5.0, which is 36 | # not compatible with older versions of Ruby. 37 | spec.add_development_dependency 'activesupport', '~> 4.2', '>= 4.2.7' 38 | end 39 | -------------------------------------------------------------------------------- /lib/generamba.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | require 'generamba/constants/constants.rb' 3 | require 'generamba/constants/rambafile_constants.rb' 4 | require 'generamba/constants/rambaspec_constants.rb' 5 | require 'generamba/cli/cli.rb' 6 | require 'generamba/code_generation/code_module.rb' 7 | require 'generamba/code_generation/module_template.rb' 8 | require 'generamba/code_generation/content_generator.rb' 9 | require 'generamba/code_generation/rambafile_generator.rb' 10 | require 'generamba/module_generator.rb' 11 | require 'generamba/template/processor/template_processor.rb' 12 | require 'generamba/template/installer/template_installer_factory' 13 | require 'generamba/configuration/user_preferences.rb' 14 | require 'generamba/template/creator/template_creator.rb' 15 | require 'generamba/tools/string-colorize.rb' 16 | end -------------------------------------------------------------------------------- /lib/generamba/cli/cli.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'xcodeproj' 3 | require 'liquid' 4 | require 'git' 5 | require 'generamba/cli/gen_command.rb' 6 | require 'generamba/cli/setup_command.rb' 7 | require 'generamba/cli/version_command.rb' 8 | require 'generamba/cli/setup_username_command.rb' 9 | require 'generamba/cli/thor_extension.rb' 10 | require 'generamba/cli/template/template_group.rb' 11 | 12 | module Generamba::CLI 13 | class Application < Thor 14 | 15 | end 16 | end -------------------------------------------------------------------------------- /lib/generamba/cli/gen_command.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'generamba/helpers/print_table.rb' 3 | require 'generamba/helpers/rambafile_validator.rb' 4 | require 'generamba/helpers/xcodeproj_helper.rb' 5 | require 'generamba/helpers/dependency_checker.rb' 6 | require 'generamba/helpers/gen_command_table_parameters_formatter.rb' 7 | require 'generamba/helpers/module_validator.rb' 8 | require 'generamba/helpers/module_info_generator.rb' 9 | 10 | module Generamba::CLI 11 | class Application < Thor 12 | 13 | include Generamba 14 | 15 | desc 'gen [MODULE_NAME] [TEMPLATE_NAME]', 'Creates a new VIPER module with a given name from a specific template' 16 | method_option :description, :aliases => '-d', :desc => 'Provides a full description to the module' 17 | method_option :author, :desc => 'Specifies the author name for generated module' 18 | method_option :project_targets, :desc => 'Specifies project targets for adding new module files' 19 | method_option :project_file_path, :desc => 'Specifies a location in the filesystem for new files' 20 | method_option :project_group_path, :desc => 'Specifies a location in Xcode groups for new files' 21 | method_option :module_path, :desc => 'Specifies a location (both in the filesystem and Xcode) for new files' 22 | method_option :test_targets, :desc => 'Specifies project targets for adding new test files' 23 | method_option :test_file_path, :desc => 'Specifies a location in the filesystem for new test files' 24 | method_option :test_group_path, :desc => 'Specifies a location in Xcode groups for new test files' 25 | method_option :test_path, :desc => 'Specifies a location (both in the filesystem and Xcode) for new test files' 26 | method_option :custom_parameters, :type => :hash, :default => {}, :desc => 'Specifies extra parameters in format `key1:value1 key2:value2` for usage during code generation' 27 | def gen(module_name, template_name) 28 | does_rambafile_exist = Dir[RAMBAFILE_NAME].count > 0 29 | 30 | unless does_rambafile_exist 31 | puts('Rambafile not found! Run `generamba setup` in the working directory instead!'.red) 32 | return 33 | end 34 | 35 | rambafile_validator = Generamba::RambafileValidator.new 36 | rambafile_validator.validate(RAMBAFILE_NAME) 37 | 38 | setup_username_command = Generamba::CLI::SetupUsernameCommand.new 39 | setup_username_command.setup_username 40 | 41 | rambafile = YAML.load_file(RAMBAFILE_NAME) 42 | 43 | code_module = CodeModule.new(module_name, rambafile, options) 44 | 45 | module_validator = ModuleValidator.new 46 | module_validator.validate(code_module) 47 | 48 | module_info = ModuleInfoGenerator.new(code_module) 49 | template = ModuleTemplate.new(template_name, module_info.scope) 50 | 51 | parameters = GenCommandTableParametersFormatter.prepare_parameters_for_displaying(code_module, template_name) 52 | PrintTable.print_values( 53 | values: parameters, 54 | title: "Summary for gen #{module_name}" 55 | ) 56 | 57 | DependencyChecker.check_all_required_dependencies_has_in_podfile(template.dependencies, code_module.podfile_path) 58 | DependencyChecker.check_all_required_dependencies_has_in_cartfile(template.dependencies, code_module.cartfile_path) 59 | 60 | project = XcodeprojHelper.obtain_project(code_module.xcodeproj_path) 61 | module_group_already_exists = XcodeprojHelper.module_with_group_path_already_exists(project, code_module.project_group_path, code_module.create_logical_groups) 62 | 63 | if module_group_already_exists 64 | replace_exists_module = yes?("#{module_name} module already exists. Replace? (yes/no)") 65 | 66 | unless replace_exists_module 67 | return 68 | end 69 | end 70 | 71 | generator = Generamba::ModuleGenerator.new 72 | generator.generate_module(module_name, code_module, template) 73 | end 74 | 75 | end 76 | end -------------------------------------------------------------------------------- /lib/generamba/cli/setup_command.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'xcodeproj' 3 | require 'liquid' 4 | require 'git' 5 | require 'generamba/constants/rambafile_constants.rb' 6 | require 'generamba/helpers/print_table.rb' 7 | 8 | module Generamba::CLI 9 | class Application < Thor 10 | include Generamba 11 | 12 | desc 'setup', 'Creates a Rambafile with a config for a given project' 13 | def setup 14 | properties = {} 15 | 16 | setup_username_command = Generamba::CLI::SetupUsernameCommand.new 17 | setup_username_command.setup_username 18 | 19 | properties[COMPANY_KEY] = ask('The company name which will be used in the headers:') 20 | 21 | project_name = Pathname.new(Dir.getwd).basename.to_s 22 | is_right_project_name = yes?("The name of your project is #{project_name}. Do you want to use it? (yes/no)") 23 | 24 | properties[PROJECT_NAME_KEY] = is_right_project_name ? project_name : ask_non_empty_string('The project name:', 'Project name should not be empty') 25 | properties[PROJECT_PREFIX_KEY] = ask('The project prefix (if any):') 26 | 27 | xcodeproj_path = ask_file_with_path('*.xcodeproj', 28 | '.xcodeproj file of the project') 29 | 30 | properties[XCODEPROJ_PATH_KEY] = xcodeproj_path 31 | project = Xcodeproj::Project.open(xcodeproj_path) 32 | 33 | targets_prompt = '' 34 | project.targets.each_with_index { |element, i| targets_prompt += ("#{i}. #{element.name}" + "\n") } 35 | project_target = ask_index("Select the appropriate target for adding your MODULES (type the index):\n" + targets_prompt,project.targets) 36 | include_tests = yes?('Are you using unit-tests in this project? (yes/no)') 37 | 38 | test_target = nil 39 | 40 | if include_tests 41 | test_target = ask_index("Select the appropriate target for adding your TESTS (type the index):\n" + targets_prompt,project.targets) 42 | end 43 | 44 | should_add_all_modules_by_one_path = yes?('Do you want to add all your modules by one path? (yes/no)') 45 | 46 | project_file_path = nil 47 | project_group_path = nil 48 | 49 | test_file_path = nil 50 | test_group_path = nil 51 | 52 | create_logical_groups = nil 53 | 54 | if should_add_all_modules_by_one_path || include_tests 55 | should_use_same_paths = false 56 | 57 | if should_add_all_modules_by_one_path 58 | should_use_same_paths = yes?('Do you want to use the same paths for your files both in Xcode and the filesystem? (yes/no)') 59 | end 60 | 61 | if should_use_same_paths 62 | if should_add_all_modules_by_one_path 63 | project_group_path = ask('The default path for creating new modules:') 64 | project_file_path = project_group_path 65 | end 66 | 67 | if include_tests 68 | test_group_path = ask('The default path for creating tests:') 69 | test_file_path = test_group_path 70 | end 71 | else 72 | if should_add_all_modules_by_one_path 73 | project_group_path = ask('The default path for creating new modules (in Xcode groups):') 74 | project_file_path = ask('The default path for creating new modules (in the filesystem):') 75 | end 76 | 77 | if include_tests 78 | test_group_path = ask('The default path for creating tests (in Xcode groups):') 79 | test_file_path = ask('The default path for creating tests (in the filesystem):') 80 | end 81 | end 82 | end 83 | 84 | create_logical_groups = yes?('Do you want to create Groups in Xcode without folders in filesystem? (yes/no)') 85 | 86 | using_pods = yes?('Are you using Cocoapods? (yes/no)') 87 | if using_pods 88 | properties[PODFILE_PATH_KEY] = ask_file_with_path('Podfile', 'Podfile') 89 | end 90 | 91 | using_carthage = yes?('Are you using Carthage? (yes/no)') 92 | if using_carthage 93 | properties[CARTFILE_PATH_KEY] = ask_file_with_path('Cartfile', 'Cartfile') 94 | end 95 | 96 | should_add_templates = yes?('Do you want to add some well known templates to the Rambafile? (yes/no)') 97 | if should_add_templates 98 | properties[TEMPLATES_KEY] = [ 99 | '{name: rviper_controller}', 100 | '{name: mvvm_controller}', 101 | '{name: swifty_viper}' 102 | ] 103 | end 104 | 105 | properties[PROJECT_TARGET_KEY] = project_target.name if project_target 106 | properties[PROJECT_FILE_PATH_KEY] = project_file_path if project_file_path 107 | properties[PROJECT_GROUP_PATH_KEY] = project_group_path if project_group_path 108 | properties[TEST_TARGET_KEY] = test_target.name if test_target 109 | properties[TEST_FILE_PATH_KEY] = test_file_path if test_file_path 110 | properties[TEST_GROUP_PATH_KEY] = test_group_path if test_group_path 111 | properties[CREATE_LOGICAL_GROUPS_KEY] = create_logical_groups if create_logical_groups 112 | 113 | PrintTable.print_values( 114 | values: properties, 115 | title: 'Summary for generamba setup' 116 | ) 117 | 118 | Generamba::RambafileGenerator.create_rambafile(properties) 119 | if should_add_templates 120 | puts('Rambafile successfully created! Now run `generamba template install`.'.green) 121 | else 122 | puts('Rambafile successfully created!\n Go on and find some templates in our catalog using `generamba template list` command.\n Add any of them to the Rambafile and run `generamba template install`.'.green) 123 | end 124 | 125 | end 126 | end 127 | end -------------------------------------------------------------------------------- /lib/generamba/cli/setup_username_command.rb: -------------------------------------------------------------------------------- 1 | module Generamba::CLI 2 | class SetupUsernameCommand < Thor 3 | 4 | no_commands { 5 | def setup_username 6 | username = Generamba::UserPreferences.obtain_username 7 | unless username 8 | puts('The author name is not configured!'.red) 9 | git_username = Git.init.config['user.name'] 10 | if git_username != nil && yes?("Your name in git is configured as #{git_username}. Do you want to use it in code headers? (yes/no)") 11 | username = git_username 12 | else 13 | username = ask_non_empty_string('The author name which will be used in the headers:', 'User name should not be empty') 14 | end 15 | Generamba::UserPreferences.save_username(username) 16 | end 17 | 18 | end 19 | } 20 | end 21 | end -------------------------------------------------------------------------------- /lib/generamba/cli/template/template_create_command.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/helpers/print_table.rb' 2 | 3 | module Generamba::CLI 4 | class Template < Thor 5 | include Generamba 6 | 7 | desc 'create [TEMPLATE_NAME]', 'Creates a new Generamba template with a given name' 8 | def create(template_name) 9 | summary = ask('The brief description of your new template:') 10 | author = ask('Who is the author of this template:') 11 | license = ask('What license will be used (e.g. MIT):') 12 | 13 | has_dependencies = yes?('Will your template contain any third-party dependencies (available via Cocoapods or Carthage)? (yes/no)') 14 | if has_dependencies 15 | dependencies = ask_loop('Enter the name of your dependency (empty string to stop):') 16 | end 17 | 18 | properties = { 19 | TEMPLATE_NAME_KEY => template_name, 20 | TEMPLATE_SUMMARY_KEY => summary, 21 | TEMPLATE_AUTHOR_KEY => author, 22 | TEMPLATE_LICENSE_KEY => license 23 | } 24 | 25 | if dependencies and !dependencies.empty? 26 | properties[TEMPLATE_DEPENDENCIES_KEY] = dependencies 27 | end 28 | 29 | PrintTable.print_values( 30 | values: properties, 31 | title: "Summary for template create" 32 | ) 33 | 34 | template_creator = Generamba::TemplateCreator.new 35 | template_creator.create_template(properties) 36 | puts("The template #{template_name} is successfully generated! Now add some file templates into it.".green) 37 | end 38 | 39 | end 40 | end -------------------------------------------------------------------------------- /lib/generamba/cli/template/template_group.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/cli/template/template_install_command.rb' 2 | require 'generamba/cli/template/template_create_command.rb' 3 | require 'generamba/cli/template/template_list_command.rb' 4 | require 'generamba/cli/template/template_search_command.rb' 5 | 6 | module Generamba::CLI 7 | class Application < Thor 8 | register(Generamba::CLI::Template, 'template', 'template ', 'Provides a set of commands for working with templates') 9 | end 10 | 11 | class Template < Thor 12 | 13 | end 14 | end -------------------------------------------------------------------------------- /lib/generamba/cli/template/template_install_command.rb: -------------------------------------------------------------------------------- 1 | module Generamba::CLI 2 | class Template < Thor 3 | 4 | desc 'install', 'Installs all the templates specified in the Rambafile from the current directory' 5 | def install 6 | does_rambafile_exist = Dir[RAMBAFILE_NAME].count > 0 7 | 8 | unless does_rambafile_exist 9 | puts('Rambafile not found! Run `generamba setup` in the working directory instead!'.red) 10 | return 11 | end 12 | 13 | catalog_downloader = Generamba::CatalogDownloader.new 14 | installer_factory = Generamba::TemplateInstallerFactory.new 15 | template_processor = Generamba::TemplateProcessor.new(catalog_downloader, installer_factory) 16 | 17 | rambafile = YAML.load_file(RAMBAFILE_NAME) 18 | template_processor.install_templates(rambafile) 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/generamba/cli/template/template_list_command.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/helpers/catalog_downloader' 2 | require 'generamba/template/helpers/catalog_template_list_helper' 3 | 4 | module Generamba::CLI 5 | class Template < Thor 6 | include Generamba 7 | 8 | desc 'list', 'Prints out the list of all templates available in the shared GitHub catalog' 9 | def list 10 | downloader = CatalogDownloader.new 11 | catalog_template_list_helper = CatalogTemplateListHelper.new 12 | 13 | templates = [] 14 | catalog_paths = downloader.update_all_catalogs_and_return_filepaths 15 | catalog_paths.each do |path| 16 | templates += catalog_template_list_helper.obtain_all_templates_from_a_catalog(path) 17 | templates = templates.uniq 18 | end 19 | 20 | templates.each do |template_name| 21 | puts(template_name) 22 | end 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /lib/generamba/cli/template/template_search_command.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/helpers/catalog_downloader.rb' 2 | require 'generamba/template/helpers/catalog_template_search_helper' 3 | 4 | module Generamba::CLI 5 | class Template < Thor 6 | include Generamba 7 | 8 | desc 'search [SEARCH_STRING]', 'Searches a template with a given name in the shared GitHub catalog' 9 | def search(term) 10 | downloader = CatalogDownloader.new 11 | catalog_template_search_helper = CatalogTemplateSearchHelper.new 12 | 13 | catalog_paths = downloader.update_all_catalogs_and_return_filepaths 14 | 15 | templates = [] 16 | catalog_paths.each do |path| 17 | templates += catalog_template_search_helper.search_templates_in_a_catalog(path, term) 18 | templates = templates.uniq 19 | end 20 | 21 | templates.map { |template_name| 22 | keywords = term.squeeze.strip.split(' ').compact.uniq 23 | matcher = Regexp.new('(' + keywords.join('|') + ')') 24 | template_name.gsub(matcher) { |match| "#{match}".yellow } 25 | }.each { |template_name| 26 | puts(template_name) 27 | } 28 | end 29 | end 30 | end -------------------------------------------------------------------------------- /lib/generamba/cli/thor_extension.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | 3 | module Generamba::CLI 4 | class ::Thor 5 | no_commands do 6 | def ask_index(message, array) 7 | value_index = ask_with_validation(message,->(value){ (value.to_i >= 0 and value.to_i < array.count) },"Invalid selection. Please enter number from 0 to #{array.count-1}") 8 | return array[value_index.to_i] 9 | end 10 | 11 | def ask_non_empty_string(message, description = 'Value should be nonempty string') 12 | return ask_with_validation(message,->(value){value.length > 0 },description) 13 | end 14 | 15 | def ask_loop(message) 16 | array = Array.new 17 | loop do 18 | value = ask(message) 19 | break if value.empty? 20 | array.push(value) 21 | end 22 | return array 23 | end 24 | 25 | def ask_with_validation(message, is_valid_value, description = 'Invalid value') 26 | loop do 27 | value = ask(message) 28 | return value if is_valid_value.call(value) 29 | puts(description.red) 30 | end 31 | end 32 | 33 | def ask_file_with_path(pattern, message_file_name) 34 | project_files = Dir[pattern] 35 | count = project_files.count 36 | default_message = "The path to a #{message_file_name}:" 37 | if count == 1 38 | is_right_path = yes?"The path to a #{message_file_name} is '#{project_files[0]}'. Do you want to use it? (yes/no)" 39 | xcode_path = is_right_path ? project_files[0] : ask(default_message) 40 | else 41 | xcode_path = ask(default_message) 42 | end 43 | return xcode_path 44 | end 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /lib/generamba/cli/version_command.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'generamba/version.rb' 3 | 4 | module Generamba::CLI 5 | class Application < Thor 6 | include Generamba 7 | 8 | desc 'version', 'Prints out Generamba current version' 9 | def version 10 | options = {} 11 | options['Version'] = Generamba::VERSION.green 12 | options['Release date'] = Generamba::RELEASE_DATE.green 13 | options['Change notes'] = Generamba::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 -------------------------------------------------------------------------------- /lib/generamba/code_generation/Rambafile.liquid: -------------------------------------------------------------------------------- 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 | {% if create_logical_groups != nil and create_logical_groups %}# Create Groups in Xcode without folders in filesystem 32 | create_logical_groups: {{ create_logical_groups }} {% endif %} 33 | 34 | {% if podfile_path != nil or cartfile_path != nil %}### Dependencies settings section{% endif %} 35 | {% if podfile_path != nil %}podfile_path: {{ podfile_path }}{% endif %} 36 | {% if cartfile_path != nil %}cartfile_path: {{ cartfile_path }}{% endif %} 37 | 38 | ### Templates 39 | templates: 40 | {% if templates.size > 0 %}{% for item in templates %}- {{ item }} 41 | {% endfor %}{% else %}#- {name: local_template_name, local: 'absolute/file/path'} 42 | #- {name: remote_template_name, git: 'https://github.com/igrekde/remote_template'} 43 | #- {name: catalog_template_name} 44 | {% endif %} -------------------------------------------------------------------------------- /lib/generamba/code_generation/code_module.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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_file_root, 22 | :project_group_path, 23 | :test_file_path, 24 | :test_file_root, 25 | :test_group_path, 26 | :project_targets, 27 | :test_targets, 28 | :podfile_path, 29 | :cartfile_path, 30 | :custom_parameters, 31 | :create_logical_groups 32 | 33 | def initialize(name, rambafile, options) 34 | # Base initialization 35 | @name = name 36 | @description = options[:description] ? options[:description] : "#{name} module" 37 | @author = rambafile[AUTHOR_NAME_KEY] ? rambafile[AUTHOR_NAME_KEY] : UserPreferences.obtain_username 38 | @company = rambafile[COMPANY_KEY] 39 | @year = Time.now.year.to_s 40 | 41 | @prefix = rambafile[PROJECT_PREFIX_KEY] 42 | @project_name = rambafile[PROJECT_NAME_KEY] 43 | 44 | @product_module_name = rambafile[PRODUCT_MODULE_NAME_KEY] 45 | @product_module_name = @project_name.gsub(C99IDENTIFIER, '_') if !@product_module_name && @project_name 46 | 47 | @xcodeproj_path = rambafile[XCODEPROJ_PATH_KEY] 48 | 49 | setup_file_and_group_paths(rambafile[PROJECT_FILE_PATH_KEY], rambafile[PROJECT_GROUP_PATH_KEY], PATH_TYPE_PROJECT) 50 | setup_file_and_group_paths(rambafile[TEST_FILE_PATH_KEY], rambafile[TEST_GROUP_PATH_KEY], PATH_TYPE_TEST) 51 | 52 | @create_logical_groups = rambafile[CREATE_LOGICAL_GROUPS_KEY] if rambafile[CREATE_LOGICAL_GROUPS_KEY] 53 | if create_logical_groups 54 | @project_file_root = Pathname.new(rambafile[PROJECT_FILE_PATH_KEY]) 55 | @test_file_root = Pathname.new(rambafile[TEST_FILE_PATH_KEY]) 56 | end 57 | 58 | @project_targets = [rambafile[PROJECT_TARGET_KEY]] if rambafile[PROJECT_TARGET_KEY] 59 | @project_targets = rambafile[PROJECT_TARGETS_KEY] if rambafile[PROJECT_TARGETS_KEY] 60 | 61 | @test_targets = [rambafile[TEST_TARGET_KEY]] if rambafile[TEST_TARGET_KEY] 62 | @test_targets = rambafile[TEST_TARGETS_KEY] if rambafile[TEST_TARGETS_KEY] 63 | 64 | # Custom parameters 65 | @custom_parameters = options[:custom_parameters] 66 | 67 | # Options adaptation 68 | @author = options[:author] if options[:author] 69 | @project_targets = options[:project_targets].split(',') if options[:project_targets] 70 | @test_targets = options[:test_targets].split(',') if options[:test_targets] 71 | 72 | setup_file_and_group_paths(options[:project_file_path], options[:project_group_path], PATH_TYPE_PROJECT) 73 | setup_file_and_group_paths(options[:test_file_path], options[:test_group_path], PATH_TYPE_TEST) 74 | 75 | # The priority is given to `module_path` and 'test_path' options 76 | setup_file_and_group_paths(options[:module_path], options[:module_path], PATH_TYPE_PROJECT) 77 | setup_file_and_group_paths(options[:test_path], options[:test_path], PATH_TYPE_TEST) 78 | 79 | @podfile_path = rambafile[PODFILE_PATH_KEY] if rambafile[PODFILE_PATH_KEY] 80 | @cartfile_path = rambafile[CARTFILE_PATH_KEY] if rambafile[CARTFILE_PATH_KEY] 81 | end 82 | 83 | private 84 | 85 | def setup_file_and_group_paths(file_path, group_path, path_type) 86 | if file_path || group_path 87 | variable_name = "#{path_type}_file_path" 88 | 89 | if file_path || !instance_variable_get("@#{variable_name}") 90 | file_path = group_path unless file_path 91 | 92 | variable_value = file_path.gsub(SLASH_REGEX, '') 93 | variable_value = Pathname.new(variable_value).join(@name) 94 | instance_variable_set("@#{variable_name}", variable_value) 95 | end 96 | 97 | variable_name = "#{path_type}_group_path" 98 | 99 | if group_path || !instance_variable_get("@#{variable_name}") 100 | group_path = file_path unless group_path 101 | 102 | variable_value = group_path.gsub(SLASH_REGEX, '') 103 | variable_value = Pathname.new(variable_value).join(@name) 104 | instance_variable_set("@#{variable_name}", variable_value) 105 | end 106 | end 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/generamba/code_generation/content_generator.rb: -------------------------------------------------------------------------------- 1 | require 'liquid' 2 | 3 | module Generamba 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] A hashmap with template's filename and filepath 10 | # @param scope [Hash] A hashmap with module info 11 | # @param template [ModuleTemplate] The model describing a Generamba 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 44 | -------------------------------------------------------------------------------- /lib/generamba/code_generation/module_template.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/helpers/template_helper.rb' 2 | 3 | module Generamba 4 | 5 | # Represents a single Generamba 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 -------------------------------------------------------------------------------- /lib/generamba/code_generation/rambafile_generator.rb: -------------------------------------------------------------------------------- 1 | require 'liquid' 2 | 3 | module Generamba 4 | 5 | # Responsible for creating Generamba 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 -------------------------------------------------------------------------------- /lib/generamba/configuration/user_preferences.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/constants/user_preferences_constants.rb' 2 | 3 | module Generamba 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_username 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[USERNAME_KEY] 16 | end 17 | 18 | def self.save_username(username) 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 | preferences[USERNAME_KEY] = username 25 | File.open(path, 'w+') { |f| f.write(preferences.to_yaml) } 26 | end 27 | 28 | private 29 | 30 | def self.obtain_user_preferences_path 31 | home_path = Pathname.new(ENV['HOME']) 32 | .join(GENERAMBA_HOME_DIR) 33 | 34 | path_exists = Dir.exist?(home_path) 35 | 36 | unless path_exists 37 | FileUtils.mkdir_p home_path 38 | end 39 | 40 | preferences_path = home_path.join(USER_PREFERENCES_FILE) 41 | preferences_exist = File.file?(preferences_path) 42 | 43 | unless preferences_exist 44 | File.open(preferences_path, 'w+') { |f| f.write('') } 45 | end 46 | 47 | return preferences_path 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /lib/generamba/constants/constants.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # General constants 4 | RAMBAFILE_NAME = 'Rambafile' 5 | RAMBASPEC_EXTENSION = '.rambaspec' 6 | TEMPLATES_FOLDER = 'Templates' 7 | RAMBLER_CATALOG_REPO = 'https://github.com/rambler-digital-solutions/generamba-catalog' 8 | GENERAMBA_CATALOG_NAME = 'generamba-catalog' 9 | GENERAMBA_HOME_DIR = '.generamba' 10 | CATALOGS_DIR = 'catalogs' 11 | USER_PREFERENCES_FILE = 'user_preferences.yml' 12 | end -------------------------------------------------------------------------------- /lib/generamba/constants/rambafile_constants.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 | CREATE_LOGICAL_GROUPS_KEY = 'create_logical_groups' 26 | 27 | CATALOGS_KEY = 'catalogs' 28 | TEMPLATES_KEY = 'templates' 29 | TEMPLATE_DECLARATION_NAME_KEY = 'name' 30 | TEMPLATE_DECLARATION_LOCAL_KEY = 'local' 31 | TEMPLATE_DECLARATION_GIT_KEY = 'git' 32 | TEMPLATE_DECLARATION_BRANCH_KEY = 'branch' 33 | end 34 | -------------------------------------------------------------------------------- /lib/generamba/constants/rambaspec_constants.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 -------------------------------------------------------------------------------- /lib/generamba/constants/user_preferences_constants.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Keys of file user_preferences.yml 4 | USERNAME_KEY = 'username' 5 | end -------------------------------------------------------------------------------- /lib/generamba/helpers/dependency_checker.rb: -------------------------------------------------------------------------------- 1 | require 'cocoapods-core' 2 | 3 | module Generamba 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 55 | -------------------------------------------------------------------------------- /lib/generamba/helpers/gen_command_table_parameters_formatter.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 34 | -------------------------------------------------------------------------------- /lib/generamba/helpers/module_info_generator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 -------------------------------------------------------------------------------- /lib/generamba/helpers/module_validator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 = Generamba.const_get(target_type.upcase + '_TARGET_KEY') 68 | targets_const_value = Generamba.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 = Generamba.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 = Generamba.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 -------------------------------------------------------------------------------- /lib/generamba/helpers/print_table.rb: -------------------------------------------------------------------------------- 1 | module Generamba 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 18 | -------------------------------------------------------------------------------- /lib/generamba/helpers/rambafile_validator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | # Provides methods for validating Rambafile contents 3 | class RambafileValidator 4 | # Method validates Rambafile contents 5 | # @param path [String] The path to a Rambafile 6 | # 7 | # @return [Void] 8 | def validate(path) 9 | file_contents = open(path).read 10 | preferences = file_contents.empty? ? {} : YAML.load(file_contents).to_hash 11 | 12 | unless preferences[TEMPLATES_KEY] 13 | puts "You can't run *generamba gen* without any templates installed. Add their declarations to a Rambafile and run *generamba template install*.".red 14 | exit 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/generamba/helpers/template_helper.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | # Provides a number of helper methods for manipulating Generamba template files 3 | class TemplateHelper 4 | # Returns a file path for a specific template .rambaspec file 5 | # @param template_name [String] The Generamba template name 6 | # 7 | # @return [Pathname] 8 | def self.obtain_spec(template_name) 9 | template_path = self.obtain_path(template_name) 10 | spec_path = template_path.join(template_name + RAMBASPEC_EXTENSION) 11 | 12 | spec_path 13 | end 14 | 15 | # Returns a file path for a specific template folder 16 | # @param template_name [String] The Generamba template name 17 | # 18 | # @return [Pathname] 19 | def self.obtain_path(template_name) 20 | path = Pathname.new(Dir.getwd) 21 | .join(TEMPLATES_FOLDER) 22 | .join(template_name) 23 | 24 | error_description = "Cannot find template named #{template_name}! Add it to the Rambafile and run *generamba template install*".red 25 | raise StandardError, error_description unless path.exist? 26 | 27 | path 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/generamba/helpers/xcodeproj_helper.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | # Provides a number of helper methods for working with xcodeproj gem 3 | class XcodeprojHelper 4 | # Returns a PBXProject class for a given name 5 | # @param project_name [String] The name of the project file 6 | # 7 | # @return [Xcodeproj::Project] 8 | def self.obtain_project(project_name) 9 | Xcodeproj::Project.open(project_name) 10 | end 11 | 12 | # Adds a provided file to a specific Project and Target 13 | # @param project [Xcodeproj::Project] The target xcodeproj file 14 | # @param targets_name [String] Array of targets name 15 | # @param group_path [Pathname] The Xcode group path for current file 16 | # @param dir_path [Pathname] The directory path for current file 17 | # @param file_group_path [String] Directory path 18 | # @param file_name [String] Current file name 19 | # @param file_is_resource [TrueClass or FalseClass] If true then file is resource 20 | # 21 | # @return [void] 22 | def self.add_file_to_project_and_targets(project, targets_name, group_path, dir_path, file_group_path, file_name, root_path, file_is_resource = false) 23 | 24 | if root_path 25 | file_path = root_path 26 | else 27 | file_path = dir_path 28 | file_path = file_path.join(file_group_path) if file_group_path 29 | end 30 | 31 | file_path = file_path.join(file_name) if file_name 32 | 33 | module_group = self.retrieve_group_or_create_if_needed(group_path, dir_path, file_group_path, project, true, root_path) 34 | xcode_file = module_group.new_file(File.absolute_path(file_path)) 35 | 36 | targets_name.each do |target| 37 | xcode_target = obtain_target(target, project) 38 | 39 | if file_is_resource || self.is_bundle_resource?(file_name) 40 | xcode_target.add_resources([xcode_file]) 41 | elsif self.is_compile_source?(file_name) 42 | xcode_target.add_file_references([xcode_file]) 43 | end 44 | end 45 | end 46 | 47 | # Adds a provided directory to a specific Project 48 | # @param project [Xcodeproj::Project] The target xcodeproj file 49 | # @param group_path [Pathname] The Xcode group path for current directory 50 | # @param dir_path [Pathname] The directory path for current directory 51 | # @param directory_name [String] Current directory name 52 | # 53 | # @return [void] 54 | def self.add_group_to_project(project, group_path, dir_path, directory_name, group_is_logical) 55 | self.retrieve_group_or_create_if_needed(group_path, dir_path, directory_name, project, true, group_is_logical) 56 | end 57 | 58 | # File is a compiled source 59 | # @param file_name [String] String of file name 60 | # 61 | # @return [TrueClass or FalseClass] 62 | def self.is_compile_source?(file_name) 63 | File.extname(file_name) == '.m' || File.extname(file_name) == '.swift' || File.extname(file_name) == '.mm' 64 | end 65 | 66 | # File is a resource 67 | # @param resource_name [String] String of resource name 68 | # 69 | # @return [TrueClass or FalseClass] 70 | def self.is_bundle_resource?(resource_name) 71 | File.extname(resource_name) == '.xib' || File.extname(resource_name) == '.storyboard' 72 | end 73 | 74 | # Recursively clears children of the given group 75 | # @param project [Xcodeproj::Project] The working Xcode project file 76 | # @param group_path [Pathname] The full group path 77 | # 78 | # @return [Void] 79 | def self.clear_group(project, targets_name, group_path, group_is_logical) 80 | module_group = self.retrieve_group_or_create_if_needed(group_path, nil, nil, project, false, group_is_logical) 81 | return unless module_group 82 | 83 | files_path = self.files_path_from_group(module_group, project) 84 | return unless files_path 85 | 86 | files_path.each do |file_path| 87 | self.remove_file_by_file_path(file_path, targets_name, project) 88 | end 89 | 90 | module_group.clear 91 | end 92 | 93 | # Finds a group in a xcodeproj file with a given path 94 | # @param project [Xcodeproj::Project] The working Xcode project file 95 | # @param group_path [Pathname] The full group path 96 | # 97 | # @return [TrueClass or FalseClass] 98 | def self.module_with_group_path_already_exists(project, group_path, group_is_logical) 99 | module_group = self.retrieve_group_or_create_if_needed(group_path, nil, nil, project, false, group_is_logical) 100 | module_group.nil? ? false : true 101 | end 102 | 103 | private 104 | 105 | # Finds or creates a group in a xcodeproj file with a given path 106 | # @param group_path [Pathname] The Xcode group path for module 107 | # @param dir_path [Pathname] The directory path for module 108 | # @param file_group_path [String] Directory path 109 | # @param project [Xcodeproj::Project] The working Xcode project file 110 | # @param create_group_if_not_exists [TrueClass or FalseClass] If true nonexistent group will be created 111 | # 112 | # @return [PBXGroup] 113 | def self.retrieve_group_or_create_if_needed(group_path, dir_path, file_group_path, project, create_group_if_not_exists, group_is_logical = false) 114 | group_names = path_names_from_path(group_path) 115 | group_components_count = group_names.count 116 | group_names += path_names_from_path(file_group_path) if file_group_path 117 | 118 | final_group = project 119 | 120 | group_names.each_with_index do |group_name, index| 121 | next_group = final_group[group_name] 122 | 123 | unless next_group 124 | return nil unless create_group_if_not_exists 125 | 126 | if group_path != dir_path && index == group_components_count-1 127 | next_group = group_is_logical ? final_group.new_group(group_name) : final_group.new_group(group_name, dir_path, :project) 128 | else 129 | next_group = group_is_logical ? final_group.new_group(group_name) : final_group.new_group(group_name, group_name) 130 | end 131 | end 132 | 133 | final_group = next_group 134 | end 135 | 136 | final_group 137 | end 138 | 139 | # Returns an AbstractTarget class for a given name 140 | # @param target_name [String] The name of the target 141 | # @param project [Xcodeproj::Project] The target xcodeproj file 142 | # 143 | # @return [Xcodeproj::AbstractTarget] 144 | def self.obtain_target(target_name, project) 145 | project.targets.each do |target| 146 | return target if target.name == target_name 147 | end 148 | 149 | error_description = "Cannot find a target with name #{target_name} in Xcode project".red 150 | raise StandardError, error_description 151 | end 152 | 153 | # Splits the provided Xcode path to an array of separate paths 154 | # @param path The full group or file path 155 | # 156 | # @return [[String]] 157 | def self.path_names_from_path(path) 158 | path.to_s.split('/') 159 | end 160 | 161 | # Remove build file from target build phase 162 | # @param file_path [String] The path of the file 163 | # @param targets_name [String] Array of targets 164 | # @param project [Xcodeproj::Project] The target xcodeproj file 165 | # 166 | # @return [Void] 167 | def self.remove_file_by_file_path(file_path, targets_name, project) 168 | file_names = path_names_from_path(file_path) 169 | 170 | build_phases = nil 171 | 172 | if self.is_compile_source?(file_names.last) 173 | build_phases = self.build_phases_from_targets(targets_name, project) 174 | elsif self.is_bundle_resource?(file_names.last) 175 | build_phases = self.resources_build_phase_from_targets(targets_name, project) 176 | end 177 | 178 | self.remove_file_from_build_phases(file_path, build_phases) 179 | end 180 | 181 | def self.remove_file_from_build_phases(file_path, build_phases) 182 | return if build_phases.nil? 183 | 184 | build_phases.each do |build_phase| 185 | build_phase.files.each do |build_file| 186 | next if build_file.nil? || build_file.file_ref.nil? 187 | 188 | build_file_path = self.configure_file_ref_path(build_file.file_ref) 189 | 190 | if build_file_path == file_path 191 | build_phase.remove_build_file(build_file) 192 | end 193 | end 194 | end 195 | end 196 | 197 | # Find and return target build phases 198 | # @param targets_name [String] Array of targets 199 | # @param project [Xcodeproj::Project] The target xcodeproj file 200 | # 201 | # @return [[PBXSourcesBuildPhase]] 202 | def self.build_phases_from_targets(targets_name, project) 203 | build_phases = [] 204 | 205 | targets_name.each do |target_name| 206 | xcode_target = self.obtain_target(target_name, project) 207 | xcode_target.build_phases.each do |build_phase| 208 | if build_phase.isa == 'PBXSourcesBuildPhase' 209 | build_phases.push(build_phase) 210 | end 211 | end 212 | end 213 | 214 | build_phases 215 | end 216 | 217 | # Find and return target resources build phase 218 | # @param targets_name [String] Array of targets 219 | # @param project [Xcodeproj::Project] The target xcodeproj file 220 | # 221 | # @return [[PBXResourcesBuildPhase]] 222 | def self.resources_build_phase_from_targets(targets_name, project) 223 | resource_build_phase = [] 224 | 225 | targets_name.each do |target_name| 226 | xcode_target = self.obtain_target(target_name, project) 227 | resource_build_phase.push(xcode_target.resources_build_phase) 228 | end 229 | 230 | resource_build_phase 231 | end 232 | 233 | # Get configure file full path 234 | # @param file_ref [PBXFileReference] Build file 235 | # 236 | # @return [String] 237 | def self.configure_file_ref_path(file_ref) 238 | build_file_ref_path = file_ref.hierarchy_path.to_s 239 | build_file_ref_path[0] = '' 240 | 241 | build_file_ref_path 242 | end 243 | 244 | # Get all files path from group path 245 | # @param module_group [PBXGroup] The module group 246 | # @param project [Xcodeproj::Project] The target xcodeproj file 247 | # 248 | # @return [[String]] 249 | def self.files_path_from_group(module_group, _project) 250 | files_path = [] 251 | 252 | module_group.recursive_children.each do |file_ref| 253 | if file_ref.isa == 'PBXFileReference' 254 | file_ref_path = configure_file_ref_path(file_ref) 255 | files_path.push(file_ref_path) 256 | end 257 | end 258 | 259 | files_path 260 | end 261 | end 262 | end 263 | -------------------------------------------------------------------------------- /lib/generamba/module_generator.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | require 'generamba/helpers/xcodeproj_helper.rb' 4 | require 'generamba/helpers/module_info_generator.rb' 5 | 6 | module Generamba 7 | 8 | # Responsible for creating the whole code module using information from the CLI 9 | class ModuleGenerator 10 | 11 | def generate_module(name, code_module, template) 12 | # Setting up Xcode objects 13 | project = XcodeprojHelper.obtain_project(code_module.xcodeproj_path) 14 | 15 | # Configuring file paths 16 | if code_module.project_file_root && code_module.test_file_root 17 | FileUtils.mkdir_p code_module.project_file_root if code_module.project_file_root 18 | FileUtils.mkdir_p code_module.test_file_root if code_module.test_file_root 19 | else 20 | FileUtils.mkdir_p code_module.project_file_path if code_module.project_file_path 21 | FileUtils.mkdir_p code_module.test_file_path if code_module.test_file_path 22 | end 23 | 24 | # Creating code files 25 | if code_module.project_targets && code_module.project_group_path && code_module.project_file_path 26 | puts('Creating code files...') 27 | process_files_if_needed(template.code_files, 28 | code_module, 29 | template, 30 | project, 31 | code_module.project_targets, 32 | code_module.project_group_path, 33 | code_module.project_file_path, 34 | code_module.project_file_root) 35 | end 36 | 37 | 38 | # Creating test files 39 | if code_module.test_targets && code_module.test_group_path && code_module.test_file_path 40 | puts('Creating test files...') 41 | process_files_if_needed(template.test_files, 42 | code_module, 43 | template, 44 | project, 45 | code_module.test_targets, 46 | code_module.test_group_path, 47 | code_module.test_file_path, 48 | code_module.test_file_root, 49 | [code_module.project_group_path]) 50 | end 51 | 52 | # Saving the current changes in the Xcode project 53 | project.save 54 | 55 | puts 'Module successfully created!' 56 | puts "Name: #{name}".green 57 | puts "Project file path: #{code_module.project_file_path}".green if code_module.project_file_path 58 | puts "Project group path: #{code_module.project_group_path}".green if code_module.project_group_path 59 | puts "Test file path: #{code_module.test_file_path}".green if code_module.test_file_path 60 | puts "Test group path: #{code_module.test_group_path}".green if code_module.test_group_path 61 | end 62 | 63 | def process_files_if_needed(files, code_module, template, project, targets, group_path, dir_path, root_path, processed_groups = []) 64 | # It's possible that current project doesn't test targets configured, so it doesn't need to generate tests. 65 | # The same is for files property - a template can have only test or project files 66 | if targets.count == 0 || files == nil || files.count == 0 || dir_path == nil || group_path == nil 67 | return 68 | end 69 | 70 | XcodeprojHelper.clear_group(project, targets, group_path, code_module.create_logical_groups) unless processed_groups.include? group_path 71 | files.each do |file| 72 | unless file[TEMPLATE_FILE_PATH_KEY] 73 | directory_name = file[TEMPLATE_NAME_KEY].gsub(/^\/|\/$/, '') 74 | file_group = dir_path.join(directory_name) 75 | 76 | FileUtils.mkdir_p root_path 77 | XcodeprojHelper.add_group_to_project(project, group_path, dir_path, directory_name, code_module.create_logical_groups) 78 | 79 | next 80 | end 81 | 82 | file_group = File.dirname(file[TEMPLATE_NAME_KEY]) 83 | file_group = nil if file_group == '.' 84 | 85 | module_info = ModuleInfoGenerator.new(code_module) 86 | 87 | # Generating the content of the code file and it's name 88 | file_name, file_content = ContentGenerator.create_file(file, module_info.scope, template) 89 | if code_module.create_logical_groups 90 | file_path = root_path 91 | else 92 | file_path = dir_path 93 | file_path = file_path.join(file_group) if file_group 94 | end 95 | 96 | file_path = file_path.join(file_name) if file_name 97 | 98 | # Creating the file in the filesystem 99 | FileUtils.mkdir_p File.dirname(file_path) if !code_module.create_logical_groups 100 | File.open(file_path, 'w+') do |f| 101 | f.write(file_content) 102 | end 103 | 104 | file_is_resource = file[TEMPLATE_FILE_IS_RESOURCE_KEY] 105 | 106 | # Creating the file in the Xcode project 107 | XcodeprojHelper.add_file_to_project_and_targets(project, 108 | targets, 109 | group_path, 110 | dir_path, 111 | file_group, 112 | file_name, 113 | root_path, 114 | file_is_resource) 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/generamba/template/creator/new_template/Code/Service/service.h.liquid: -------------------------------------------------------------------------------- 1 | // 2 | // {{ prefix }}{{ module_info.name }}{{ module_info.file_name }} 3 | // {{ module_info.project_name }} 4 | // 5 | // Created by {{ developer.name }} on {{ date }}. 6 | // Copyright {{ year }} {{ developer.company }}. All rights reserved. 7 | // 8 | 9 | @interface {{ prefix }}{{ module_info.name }}Service : NSObject 10 | 11 | @end -------------------------------------------------------------------------------- /lib/generamba/template/creator/new_template/Code/Service/service.m.liquid: -------------------------------------------------------------------------------- 1 | // 2 | // {{ prefix }}{{ module_info.name }}{{ module_info.file_name }} 3 | // {{ module_info.project_name }} 4 | // 5 | // Created by {{ developer.name }} on {{ date }}. 6 | // Copyright {{ year }} {{ developer.company }}. All rights reserved. 7 | // 8 | 9 | #import "{{ prefix }}{{ module_info.name }}Service.h" 10 | 11 | @implementation {{ prefix }}{{ module_info.name }}Service 12 | 13 | @end -------------------------------------------------------------------------------- /lib/generamba/template/creator/new_template/Tests/Service/service_tests.m.liquid: -------------------------------------------------------------------------------- 1 | // 2 | // {{ prefix }}{{ module_info.name }}{{ module_info.file_name }} 3 | // {{ module_info.project_name }} 4 | // 5 | // Created by {{ developer.name }} on {{ date }}. 6 | // Copyright {{ year }} {{ developer.company }}. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "{{ prefix }}{{ module_info.name }}Service.h" 12 | 13 | @interface {{ prefix }}{{ module_info.name }}ServiceTests : XCTestCase 14 | 15 | @property (strong, nonatomic) {{ prefix }}{{ module_info.name }}Service *service; 16 | 17 | @end 18 | 19 | @implementation {{ prefix }}{{ module_info.name }}ServiceTests 20 | 21 | #pragma mark - Test environment setup 22 | 23 | - (void)setUp { 24 | [super setUp]; 25 | 26 | self.service = [[{{ prefix }}{{ module_info.name }}Service alloc] init]; 27 | } 28 | 29 | - (void)tearDown { 30 | self.service = nil; 31 | 32 | [super tearDown]; 33 | } 34 | 35 | @end -------------------------------------------------------------------------------- /lib/generamba/template/creator/new_template/template.rambaspec.liquid: -------------------------------------------------------------------------------- 1 | # Template information section 2 | name: {{ name }} 3 | summary: {{ summary }} 4 | author: {{ author }} 5 | version: 1.0.0 6 | license: {{ license }} 7 | 8 | # The declarations for code files 9 | code_files: 10 | - {name: Service/Service.h, path: Code/Service/service.h.liquid} 11 | - {name: Service/Service.m, path: Code/Service/service.m.liquid} 12 | 13 | # The declarations for test files 14 | test_files: 15 | - {name: Service/ServiceTests.m, path: Tests/Service/service_tests.m.liquid} 16 | 17 | # Template dependencies 18 | dependencies: 19 | {% for dependency in dependencies %}- {{ dependency }} 20 | {% endfor %} -------------------------------------------------------------------------------- /lib/generamba/template/creator/template_creator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Responsible for generating new .rambaspec files 4 | class TemplateCreator 5 | 6 | NEW_TEMPLATE_FOLDER = 'new_template' 7 | RAMBASPEC_TEMPLATE_NAME = 'template.rambaspec.liquid' 8 | CODE_FOLDER = 'Code' 9 | TESTS_FOLDER = 'Tests' 10 | 11 | # Generates and saves to filesystem a new template with a .rambaspec file and sample code and tests files 12 | # @param properties [Hash] User-inputted template properties 13 | # 14 | # @return [Void] 15 | def create_template(properties) 16 | template_dir_path = Pathname.new(File.dirname(__FILE__)).join(NEW_TEMPLATE_FOLDER) 17 | rambaspec_template_file_path = template_dir_path.join(RAMBASPEC_TEMPLATE_NAME) 18 | code_file_path = template_dir_path.join(CODE_FOLDER) 19 | tests_file_path = template_dir_path.join(TESTS_FOLDER) 20 | 21 | file_source = IO.read(rambaspec_template_file_path) 22 | 23 | template = Liquid::Template.parse(file_source) 24 | output = template.render(properties) 25 | 26 | result_name = properties[TEMPLATE_NAME_KEY] + RAMBASPEC_EXTENSION 27 | result_dir_path = Pathname.new(properties[TEMPLATE_NAME_KEY]) 28 | 29 | FileUtils.mkdir_p result_dir_path 30 | FileUtils.cp_r(code_file_path, result_dir_path) 31 | FileUtils.cp_r(tests_file_path, result_dir_path) 32 | 33 | File.open(result_dir_path.join(result_name), 'w+') {|f| 34 | f.write(output) 35 | } 36 | end 37 | 38 | end 39 | end -------------------------------------------------------------------------------- /lib/generamba/template/helpers/catalog_downloader.rb: -------------------------------------------------------------------------------- 1 | require 'git' 2 | 3 | module Generamba 4 | 5 | # Provides the functionality to download template catalogs from the remote repository 6 | class CatalogDownloader 7 | 8 | # Updates all of the template catalogs and returns their filepaths. 9 | # If there is a Rambafile in the current directory, it also updates all of the catalogs specified there. 10 | # 11 | # @return [Array] An array of filepaths to downloaded catalogs 12 | def update_all_catalogs_and_return_filepaths 13 | does_rambafile_exist = Dir[RAMBAFILE_NAME].count > 0 14 | 15 | if does_rambafile_exist 16 | rambafile = YAML.load_file(RAMBAFILE_NAME) 17 | catalogs = rambafile[CATALOGS_KEY] 18 | end 19 | 20 | terminator = CatalogTerminator.new 21 | terminator.remove_all_catalogs 22 | 23 | catalog_paths = [download_catalog(GENERAMBA_CATALOG_NAME, RAMBLER_CATALOG_REPO)] 24 | 25 | if catalogs != nil && catalogs.count > 0 26 | catalogs.each do |catalog_url| 27 | catalog_name = catalog_url.split('://').last 28 | catalog_name = catalog_name.gsub('/', '-'); 29 | catalog_paths.push(download_catalog(catalog_name, catalog_url)) 30 | end 31 | end 32 | return catalog_paths 33 | end 34 | 35 | # Clones a template catalog from a remote repository 36 | # 37 | # @param name [String] The name of the template catalog 38 | # @param url [String] The url of the repository 39 | # 40 | # @return [Pathname] A filepath to the downloaded catalog 41 | def download_catalog(name, url) 42 | catalogs_local_path = Pathname.new(ENV['HOME']) 43 | .join(GENERAMBA_HOME_DIR) 44 | .join(CATALOGS_DIR) 45 | current_catalog_path = catalogs_local_path 46 | .join(name) 47 | 48 | if File.exists?(current_catalog_path) 49 | g = Git.open(current_catalog_path) 50 | g.pull 51 | else 52 | Git.clone(url, name, :path => catalogs_local_path) 53 | end 54 | 55 | return current_catalog_path 56 | end 57 | end 58 | end -------------------------------------------------------------------------------- /lib/generamba/template/helpers/catalog_template_list_helper.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Provides the functionality to list all of the templates, available in the catalog 4 | class CatalogTemplateListHelper 5 | 6 | # Finds out all of the templates located in a catalog 7 | # 8 | # @param catalog_path [Pathname] The path to a template catalog 9 | # 10 | # @return [Array] An array with template names 11 | def obtain_all_templates_from_a_catalog(catalog_path) 12 | template_names = [] 13 | catalog_path.children.select { |child| 14 | File.directory?(child) && child.split.last.to_s[0] != '.' 15 | }.map { |template_path| 16 | template_path.split.last.to_s 17 | }.each { |template_name| 18 | template_names.push(template_name) 19 | } 20 | return template_names 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /lib/generamba/template/helpers/catalog_template_search_helper.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Provides the functionality to search templates, in catalogs 4 | class CatalogTemplateSearchHelper 5 | 6 | # Finds out all of the templates located in a catalog 7 | # 8 | # @param catalog_path [Pathname] The path to a template catalog 9 | # 10 | # @return [Array] An array with template names 11 | def search_templates_in_a_catalog(catalog_path, search_term) 12 | template_names = [] 13 | 14 | catalog_path.children.select { |child| 15 | File.directory?(child) && child.split.last.to_s[0] != '.' 16 | }.map { |template_path| 17 | template_path.split.last.to_s 18 | }.select { |template_name| 19 | template_name.include?(search_term) 20 | }.each { |template_name| 21 | template_names.push(template_name) 22 | } 23 | 24 | return template_names 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /lib/generamba/template/helpers/catalog_terminator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Provides a functionality to terminate all previously installed catalogs 4 | # 5 | # @return [Void] 6 | class CatalogTerminator 7 | def remove_all_catalogs 8 | catalogs_path = Pathname.new(ENV['HOME']) 9 | .join(GENERAMBA_HOME_DIR) 10 | .join(CATALOGS_DIR) 11 | if Dir.exist?(catalogs_path) == false 12 | FileUtils.mkdir_p catalogs_path 13 | end 14 | catalogs_path.children.select { |child| 15 | child.directory? && child.split.last.to_s[0] != '.' 16 | }.each { |catalog_path| 17 | FileUtils.rm_rf(catalog_path) 18 | } 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /lib/generamba/template/helpers/rambaspec_validator.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Provides methods that validate .rambaspec file existance and structure 4 | class RambaspecValidator 5 | 6 | # Validates the existance of a .rambaspec file for a given template 7 | # 8 | # @param template_name [String] The name of the template 9 | # @param template_path [String] The local filepath to the template 10 | # 11 | # @return [Bool] 12 | def self.validate_spec_existance(template_name, template_path) 13 | local_spec_path = self.obtain_spec_path(template_name, template_path) 14 | File.file?(local_spec_path) 15 | end 16 | 17 | # Validates the structure of a .rambaspec file for a given template 18 | # 19 | # @param template_name [String] The name of the template 20 | # @param template_path [String] The local filepath to the template 21 | # 22 | # @return [Bool] 23 | def self.validate_spec(template_name, template_path) 24 | spec_path = self.obtain_spec_path(template_name, template_path) 25 | 26 | spec_source = IO.read(spec_path) 27 | spec_template = Liquid::Template.parse(spec_source) 28 | spec_content = spec_template.render 29 | spec = YAML.load(spec_content) 30 | 31 | is_spec_valid = 32 | spec[TEMPLATE_NAME_KEY] != nil && 33 | spec[TEMPLATE_AUTHOR_KEY] != nil && 34 | spec[TEMPLATE_VERSION_KEY] != nil && 35 | (spec[TEMPLATE_CODE_FILES_KEY] != nil || spec[TEMPLATE_TEST_FILES_KEY] != nil) 36 | return is_spec_valid 37 | end 38 | 39 | private 40 | 41 | # Returns a filepath for a given .rambaspec filename 42 | # 43 | # @param template_name [String] The name of the template 44 | # @param template_path [String] The local filepath to the template 45 | # 46 | # @return [Bool] 47 | def self.obtain_spec_path(template_name, template_path) 48 | spec_filename = template_name + RAMBASPEC_EXTENSION 49 | Pathname.new(template_path).join(spec_filename) 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /lib/generamba/template/installer/abstract_installer.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # Abstract template installer class 4 | class AbstractInstaller 5 | def install_template(template_declaration) 6 | raise 'Abstract Method - you should implement it in the concrete subclass' 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /lib/generamba/template/installer/catalog_installer.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/installer/abstract_installer.rb' 2 | require 'generamba/template/helpers/rambaspec_validator.rb' 3 | require 'fileutils' 4 | require 'tmpdir' 5 | 6 | module Generamba 7 | 8 | # Incapsulates the logic of installing Generamba templates from the template catalog 9 | class CatalogInstaller < AbstractInstaller 10 | def install_template(template_declaration) 11 | template_name = template_declaration.name 12 | puts("Installing #{template_name}...") 13 | 14 | template_name = template_declaration.name 15 | catalogs_path = Pathname.new(ENV['HOME']) 16 | .join(GENERAMBA_HOME_DIR) 17 | .join(CATALOGS_DIR) 18 | 19 | catalog_path = catalogs_path.children.select { |child| 20 | child.directory? && child.split.last.to_s[0] != '.' 21 | }.select { |catalog_path| 22 | template_path = browse_catalog_for_a_template(catalog_path, template_name) 23 | template_path != nil 24 | }.first 25 | 26 | if catalog_path == nil 27 | error_description = "Cannot find #{template_name} in any catalog. Try another name.".red 28 | puts(error_description) 29 | return 30 | end 31 | 32 | template_path = catalog_path.join(template_name) 33 | rambaspec_exist = Generamba::RambaspecValidator.validate_spec_existance(template_name, template_path) 34 | unless rambaspec_exist 35 | error_description = "Cannot find #{template_name + RAMBASPEC_EXTENSION} in the template catalog #{catalog_path}. Try another name.".red 36 | puts(error_description) 37 | return 38 | end 39 | 40 | rambaspec_valid = Generamba::RambaspecValidator.validate_spec(template_name, template_path) 41 | unless rambaspec_valid 42 | error_description = "#{template_name + RAMBASPEC_EXTENSION} is not valid.".red 43 | puts(error_description) 44 | return 45 | end 46 | 47 | install_path = Pathname.new(TEMPLATES_FOLDER) 48 | .join(template_name) 49 | FileUtils.mkdir_p install_path 50 | 51 | src = template_path.to_s + '/.' 52 | FileUtils.cp_r(src, install_path) 53 | end 54 | 55 | private 56 | 57 | # Browses a given catalog and returns a template path 58 | # 59 | # @param catalog_path [Pathname] A path to a catalog 60 | # @param template_name [String] A name of the template 61 | # 62 | # @return [Pathname] A path to a template, if found 63 | def browse_catalog_for_a_template(catalog_path, template_name) 64 | template_path = catalog_path.join(template_name) 65 | 66 | if Dir.exist?(template_path) 67 | return template_path 68 | end 69 | 70 | return nil 71 | end 72 | end 73 | end -------------------------------------------------------------------------------- /lib/generamba/template/installer/local_installer.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/installer/abstract_installer.rb' 2 | require 'generamba/template/helpers/rambaspec_validator.rb' 3 | 4 | module Generamba 5 | 6 | # Incapsulates the logic of verifying and installing local templates 7 | class LocalInstaller < AbstractInstaller 8 | def install_template(template_declaration) 9 | template_name = template_declaration.name 10 | puts("Installing #{template_name}...") 11 | 12 | local_path = template_declaration.local 13 | rambaspec_exist = Generamba::RambaspecValidator.validate_spec_existance(template_name, local_path) 14 | 15 | unless rambaspec_exist 16 | error_description = "Cannot find #{template_name + RAMBASPEC_EXTENSION} in the specified directory. Try another path or name.".red 17 | raise StandardError.new(error_description) 18 | end 19 | 20 | rambaspec_valid = Generamba::RambaspecValidator.validate_spec(template_name, local_path) 21 | unless rambaspec_valid 22 | error_description = "#{template_name + RAMBASPEC_EXTENSION} is not valid.".red 23 | raise StandardError.new(error_description) 24 | end 25 | 26 | install_path = Pathname.new(TEMPLATES_FOLDER) 27 | .join(template_name) 28 | FileUtils.mkdir_p install_path 29 | FileUtils.copy_entry(local_path, install_path) 30 | end 31 | end 32 | end -------------------------------------------------------------------------------- /lib/generamba/template/installer/remote_installer.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/installer/abstract_installer.rb' 2 | require 'generamba/template/helpers/rambaspec_validator.rb' 3 | require 'git' 4 | require 'fileutils' 5 | require 'tmpdir' 6 | 7 | module Generamba 8 | 9 | # Incapsulates the logic of fetching remote templates, verifying and installing them 10 | class RemoteInstaller < AbstractInstaller 11 | def install_template(template_declaration) 12 | template_name = template_declaration.name 13 | puts("Installing #{template_name}...") 14 | 15 | repo_url = template_declaration.git 16 | repo_branch = template_declaration.branch 17 | 18 | temp_path = Dir.tmpdir() 19 | template_dir = Pathname.new(temp_path).join(template_name) 20 | 21 | if repo_branch != nil 22 | Git.export(repo_url, template_name, :branch => repo_branch, :path => temp_path) 23 | else 24 | Git.clone(repo_url, template_name, :path => temp_path) 25 | end 26 | 27 | template_path = "#{template_dir}" 28 | 29 | rambaspec_exist = Generamba::RambaspecValidator.validate_spec_existance(template_name, template_path) 30 | unless rambaspec_exist 31 | FileUtils.rm_rf(temp_path) 32 | error_description = "Cannot find #{template_name + RAMBASPEC_EXTENSION} in the root directory of specified repository.".red 33 | raise StandardError.new(error_description) 34 | end 35 | 36 | rambaspec_valid = Generamba::RambaspecValidator.validate_spec(template_name, template_path) 37 | unless rambaspec_valid 38 | error_description = "#{template_name + RAMBASPEC_EXTENSION} is not valid.".red 39 | raise StandardError.new(error_description) 40 | end 41 | 42 | install_path = Pathname.new(TEMPLATES_FOLDER) 43 | .join(template_name) 44 | FileUtils.mkdir_p install_path 45 | FileUtils.copy_entry(template_path, install_path) 46 | 47 | FileUtils.rm_rf(temp_path) 48 | end 49 | end 50 | end -------------------------------------------------------------------------------- /lib/generamba/template/installer/template_installer_factory.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/processor/template_declaration' 2 | 3 | module Generamba 4 | 5 | # Factory that creates a proper installer for a given template type 6 | class TemplateInstallerFactory 7 | 8 | # Provides the appropriate strategy for a given template type 9 | def installer_for_type(type) 10 | case type 11 | when TemplateDeclarationType::LOCAL_TEMPLATE 12 | return Generamba::LocalInstaller.new 13 | when TemplateDeclarationType::REMOTE_TEMPLATE 14 | return Generamba::RemoteInstaller.new 15 | when TemplateDeclarationType::CATALOG_TEMPLATE 16 | return Generamba::CatalogInstaller.new 17 | else 18 | return nil 19 | end 20 | end 21 | end 22 | end -------------------------------------------------------------------------------- /lib/generamba/template/processor/template_declaration.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | 3 | # This class acts as an Enumeration for TemplateDeclaration types 4 | class TemplateDeclarationType 5 | # A local template - usually it's stored somewhere outside the current project directory 6 | LOCAL_TEMPLATE = 0 7 | 8 | # A remote template - it's stored in a remote Git repository 9 | REMOTE_TEMPLATE = 1 10 | 11 | # A template from our shared catalog 12 | CATALOG_TEMPLATE = 2 13 | end 14 | 15 | # Describes a Generamba template declaration model 16 | class TemplateDeclaration 17 | 18 | attr_reader :name, :local, :git, :branch, :type 19 | 20 | def initialize(template_hash) 21 | @name = template_hash[TEMPLATE_DECLARATION_NAME_KEY] 22 | @local = template_hash[TEMPLATE_DECLARATION_LOCAL_KEY] 23 | @git = template_hash[TEMPLATE_DECLARATION_GIT_KEY] 24 | @branch = template_hash[TEMPLATE_DECLARATION_BRANCH_KEY] 25 | 26 | @type = TemplateDeclarationType::LOCAL_TEMPLATE if @local 27 | @type = TemplateDeclarationType::REMOTE_TEMPLATE if @git 28 | @type = TemplateDeclarationType::CATALOG_TEMPLATE if @git == nil && @local == nil 29 | end 30 | 31 | def install(strategy) 32 | strategy.install_template(self) 33 | end 34 | 35 | end 36 | end -------------------------------------------------------------------------------- /lib/generamba/template/processor/template_processor.rb: -------------------------------------------------------------------------------- 1 | require 'generamba/template/processor/template_declaration.rb' 2 | require 'generamba/template/installer/local_installer.rb' 3 | require 'generamba/template/installer/remote_installer.rb' 4 | require 'generamba/template/installer/catalog_installer.rb' 5 | require 'generamba/template/helpers/catalog_downloader.rb' 6 | require 'generamba/template/helpers/catalog_terminator' 7 | require 'git' 8 | 9 | module Generamba 10 | 11 | # Incapsulates logic of processing templates declaration section from Rambafile 12 | class TemplateProcessor 13 | 14 | def initialize(catalog_downloader, installer_factory) 15 | @catalog_downloader = catalog_downloader 16 | @installer_factory = installer_factory 17 | end 18 | 19 | # This method parses Rambafile, serializes templates hashes into model objects and install them 20 | def install_templates(rambafile) 21 | # We always clear previously installed templates to avoid conflicts in different versions 22 | clear_installed_templates 23 | 24 | templates = rambafile[TEMPLATES_KEY] 25 | 26 | if !templates || templates.count == 0 27 | puts 'You must specify at least one template in Rambafile under the key *templates*'.red 28 | return 29 | end 30 | 31 | # Mapping hashes to model objects 32 | templates = rambafile[TEMPLATES_KEY].map { |template_hash| 33 | Generamba::TemplateDeclaration.new(template_hash) 34 | } 35 | 36 | catalogs = rambafile[CATALOGS_KEY] 37 | # If there is at least one template from catalogs, we should update our local copy of the catalog 38 | update_catalogs_if_needed(catalogs, templates) 39 | 40 | templates.each do |template_declaration| 41 | strategy = @installer_factory.installer_for_type(template_declaration.type) 42 | template_declaration.install(strategy) 43 | end 44 | end 45 | 46 | private 47 | 48 | # Clears all of the currently installed templates 49 | def clear_installed_templates 50 | install_path = Pathname.new(TEMPLATES_FOLDER) 51 | FileUtils.rm_rf(Dir.glob(install_path)) 52 | end 53 | 54 | # Clones remote template catalogs to the local directory 55 | def update_catalogs_if_needed(catalogs, templates) 56 | needs_update = templates.any? {|template| template.type == TemplateDeclarationType::CATALOG_TEMPLATE} 57 | 58 | return unless needs_update 59 | 60 | terminator = CatalogTerminator.new 61 | terminator.remove_all_catalogs 62 | puts('Updating shared generamba-catalog specs...') 63 | @catalog_downloader.download_catalog(GENERAMBA_CATALOG_NAME, RAMBLER_CATALOG_REPO) 64 | 65 | return unless catalogs != nil && catalogs.count > 0 66 | 67 | catalogs.each do |catalog_url| 68 | catalog_name = catalog_url.split('://').last 69 | catalog_name = catalog_name.gsub('/', '-'); 70 | puts("Updating #{catalog_name} specs...") 71 | @catalog_downloader.download_catalog(catalog_name, catalog_url) 72 | end 73 | end 74 | end 75 | end -------------------------------------------------------------------------------- /lib/generamba/tools/string-colorize.rb: -------------------------------------------------------------------------------- 1 | # Adds a number of methods for colorizing output strings 2 | class String 3 | 4 | def colorize(color_code) 5 | "\e[#{color_code}m#{self}\e[0m" 6 | end 7 | 8 | def red 9 | colorize(31) 10 | end 11 | 12 | def green 13 | colorize(32) 14 | end 15 | 16 | def yellow 17 | colorize(33) 18 | end 19 | 20 | def blue 21 | colorize(34) 22 | end 23 | end -------------------------------------------------------------------------------- /lib/generamba/version.rb: -------------------------------------------------------------------------------- 1 | module Generamba 2 | VERSION = '1.6.0' 3 | RELEASE_DATE = '11.08.2019' 4 | RELEASE_LINK = "https://github.com/rambler-digital-solutions/Generamba/releases/tag/#{VERSION}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/catalog_downloader_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'CatalogDownloader' do 4 | describe 'method download_catalog' do 5 | 6 | it 'should pull catalog from remote repository if directory exists' do 7 | catalog_name = 'catalog' 8 | catalog_url = 'https://github.com/rambler-digital-solutions/generamba-catalog' 9 | 10 | allow(File).to receive(:exists?) { true } 11 | 12 | git = instance_double("Base") 13 | 14 | allow(Git).to receive(:open) { git } 15 | allow(git).to receive(:pull) 16 | expect(git).to receive(:pull) 17 | 18 | downloader = Generamba::CatalogDownloader.new 19 | downloader.download_catalog(catalog_name, catalog_url) 20 | end 21 | 22 | it 'should clone catalog from remote repository if directory not exists' do 23 | catalog_name = 'catalog' 24 | catalog_url = 'https://github.com/rambler-digital-solutions/generamba-catalog' 25 | 26 | allow(File).to receive(:exists?) { false } 27 | 28 | allow(Git).to receive(:clone) 29 | expect(Git).to receive(:clone).with(catalog_url, catalog_name, any_args) 30 | 31 | downloader = Generamba::CatalogDownloader.new 32 | downloader.download_catalog(catalog_name, catalog_url) 33 | end 34 | 35 | it 'should return local catalog directory' do 36 | catalog_name = 'catalog' 37 | catalog_url = 'https://github.com/rambler-digital-solutions/generamba-catalog' 38 | catalogs_local_path = Pathname.new(ENV['HOME']) 39 | .join(Generamba::GENERAMBA_HOME_DIR) 40 | .join(Generamba::CATALOGS_DIR) 41 | current_catalog_path = catalogs_local_path 42 | .join(catalog_name) 43 | 44 | allow(Git).to receive(:clone) 45 | 46 | downloader = Generamba::CatalogDownloader.new 47 | result = downloader.download_catalog(catalog_name, catalog_url) 48 | expect(result).to eq(current_catalog_path) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/catalog_installer_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'generamba/template/processor/template_declaration' 3 | 4 | describe 'method install_template' do 5 | 6 | it 'should install template from shared catalog' do 7 | template_name = 'test' 8 | catalog_path = Pathname.new(ENV['HOME']) 9 | .join(Generamba::GENERAMBA_HOME_DIR) 10 | .join(Generamba::CATALOGS_DIR) 11 | .join(Generamba::GENERAMBA_CATALOG_NAME) 12 | template_path = catalog_path.join(template_name) 13 | 14 | template_install_path = Pathname.new(Generamba::TEMPLATES_FOLDER) 15 | .join(template_name) 16 | 17 | allow(Generamba::RambaspecValidator).to receive(:validate_spec).and_return(true) 18 | allow(Generamba::RambaspecValidator).to receive(:validate_spec_existance).and_return(true) 19 | FakeFS do 20 | FileUtils.mkdir_p template_path 21 | 22 | declaration = Generamba::TemplateDeclaration.new({Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test'}) 23 | installer = Generamba::CatalogInstaller.new 24 | installer.install_template(declaration) 25 | 26 | result = Dir.exist?(template_install_path) 27 | expect(result).to eq(true) 28 | end 29 | end 30 | 31 | it 'should install template from other catalog' do 32 | template_name = 'test' 33 | catalog_name = 'custom_catalog' 34 | shared_catalog_path = Pathname.new(ENV['HOME']) 35 | .join(Generamba::GENERAMBA_HOME_DIR) 36 | .join(Generamba::CATALOGS_DIR) 37 | .join(Generamba::GENERAMBA_CATALOG_NAME) 38 | catalog_path = Pathname.new(ENV['HOME']) 39 | .join(Generamba::GENERAMBA_HOME_DIR) 40 | .join(Generamba::CATALOGS_DIR) 41 | .join(catalog_name) 42 | template_path = catalog_path.join(template_name) 43 | 44 | template_install_path = Pathname.new(Generamba::TEMPLATES_FOLDER) 45 | .join(template_name) 46 | 47 | allow(Generamba::RambaspecValidator).to receive(:validate_spec).and_return(true) 48 | allow(Generamba::RambaspecValidator).to receive(:validate_spec_existance) do |name, path| 49 | result = false 50 | if template_path.to_s == path.to_s 51 | result = true 52 | end 53 | result 54 | end 55 | 56 | FakeFS do 57 | FileUtils.mkdir_p template_path 58 | FileUtils.mkdir_p shared_catalog_path 59 | 60 | declaration = Generamba::TemplateDeclaration.new({Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test'}) 61 | installer = Generamba::CatalogInstaller.new 62 | installer.install_template(declaration) 63 | 64 | result = Dir.exist?(template_install_path) 65 | expect(result).to eq(true) 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /spec/catalog_template_list_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'generamba/template/helpers/catalog_template_list_helper' 3 | 4 | describe 'method obtain_all_templates_from_a_catalog' do 5 | 6 | it 'should return 0 template names for empty catalog' do 7 | test_catalog_path = Pathname.new('test-path/catalog') 8 | FakeFS do 9 | FileUtils.mkdir_p test_catalog_path 10 | 11 | list_helper = Generamba::CatalogTemplateListHelper.new 12 | result = list_helper.obtain_all_templates_from_a_catalog(test_catalog_path) 13 | expect(result.count).to eq(0) 14 | end 15 | end 16 | 17 | it 'should return template names for non-empty catalog' do 18 | test_catalog_path = Pathname.new('test-path/catalog') 19 | test_template_count = 5 20 | FakeFS do 21 | FileUtils.mkdir_p test_catalog_path 22 | for i in 1..test_template_count 23 | FileUtils.mkdir_p test_catalog_path.join(i.to_s) 24 | end 25 | 26 | list_helper = Generamba::CatalogTemplateListHelper.new 27 | result = list_helper.obtain_all_templates_from_a_catalog(test_catalog_path) 28 | expect(result.count).to eq(test_template_count) 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /spec/catalog_template_search_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'generamba/template/helpers/catalog_template_search_helper' 3 | 4 | describe 'method search_templates_in_a_catalog' do 5 | it 'should return zero templates if no matches' do 6 | test_catalog_path = Pathname.new('test-path/catalog') 7 | FakeFS do 8 | FileUtils.mkdir_p test_catalog_path 9 | 10 | search_helper = Generamba::CatalogTemplateSearchHelper.new 11 | result = search_helper.search_templates_in_a_catalog(test_catalog_path, 'viper') 12 | expect(result.count).to eq(0) 13 | end 14 | end 15 | 16 | it 'should return templates if there are matches' do 17 | test_catalog_path = Pathname.new('test-path/catalog') 18 | test_search_term = 'viper' 19 | test_viper_template_path = test_catalog_path.join("#{test_search_term}-template") 20 | FakeFS do 21 | FileUtils.mkdir_p test_viper_template_path 22 | 23 | search_helper = Generamba::CatalogTemplateSearchHelper.new 24 | result = search_helper.search_templates_in_a_catalog(test_catalog_path, test_search_term) 25 | expect(result.count).to eq(1) 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /spec/dependency_checker_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'DependencyChecker' do 4 | before(:each) do 5 | @checker = Generamba::DependencyChecker 6 | end 7 | 8 | describe 'method check_all_required_dependencies_has_in_podfile' do 9 | it 'should do nothing' do 10 | dependencies = ['ViperMcFlurry'] 11 | podfile_path = 'Podfile' 12 | 13 | expect(STDOUT).not_to receive(:puts).with("[Warning] Dependencies #{dependencies} missed in Podfile".yellow) 14 | 15 | @checker.check_all_required_dependencies_has_in_podfile(dependencies, nil) 16 | @checker.check_all_required_dependencies_has_in_podfile(nil, podfile_path) 17 | end 18 | 19 | it 'should show warning message' do 20 | dependencies = ['ViperMcFlurry'] 21 | podfile_path = 'Podfile' 22 | 23 | allow(Pod::Podfile).to receive(:from_file).and_return(Pod::Podfile.new) 24 | expect(STDOUT).to receive(:puts).with("[Warning] Dependencies #{dependencies} missed in Podfile".yellow) 25 | 26 | @checker.check_all_required_dependencies_has_in_podfile(dependencies, podfile_path) 27 | end 28 | end 29 | 30 | describe 'method check_all_required_dependencies_has_in_cartfile' do 31 | it 'should do nothing' do 32 | dependencies = ['ViperMcFlurry'] 33 | cartfile_path = 'Cartfile' 34 | 35 | expect(STDOUT).not_to receive(:puts).with("[Warning] Dependencies #{dependencies} missed in Cartfile".yellow) 36 | 37 | @checker.check_all_required_dependencies_has_in_cartfile(dependencies, nil) 38 | @checker.check_all_required_dependencies_has_in_cartfile(nil, cartfile_path) 39 | end 40 | 41 | it 'should show warning message if dependency missing' do 42 | dependencies = ['ViperMcFlurry'] 43 | cartfile_path = 'Cartfile' 44 | 45 | allow(File).to receive(:read).and_return('Typhoon') 46 | expect(STDOUT).to receive(:puts).with("[Warning] Dependencies #{dependencies} missed in Cartfile".yellow) 47 | 48 | @checker.check_all_required_dependencies_has_in_cartfile(dependencies, cartfile_path) 49 | end 50 | 51 | it 'should not show warning message if dependency is in place' do 52 | dependencies = ['ViperMcFlurry'] 53 | cartfile_path = 'Cartfile' 54 | 55 | allow(File).to receive(:read).and_return('github "Rambler-iOS/ViperMcFlurry"') 56 | expect(STDOUT).not_to receive(:puts).with("[Warning] Dependencies #{dependencies} missed in Cartfile".yellow) 57 | 58 | @checker.check_all_required_dependencies_has_in_cartfile(dependencies, cartfile_path) 59 | end 60 | end 61 | 62 | end -------------------------------------------------------------------------------- /spec/gen_command_table_parameters_formatter_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'json' 3 | require 'generamba/code_generation/code_module.rb' 4 | require 'generamba/constants/rambafile_constants.rb' 5 | 6 | describe 'GenCommandTableParametersFormatter' do 7 | 8 | describe 'method prepare_parameters_for_displaying' do 9 | it 'should convert array of hash to array of strings' do 10 | expected_params = {} 11 | 12 | expected_params['Targets'] = 'Target' 13 | expected_params['Module path'] = Pathname.new('Project/name') 14 | expected_params['Module group path'] = Pathname.new('Project/Modules/name') 15 | expected_params['Test targets'] = 'TargetTests' 16 | expected_params['Test file path'] = Pathname.new('ProjectTests/name') 17 | expected_params['Test group path'] = Pathname.new('ProjectTests/Modules/name') 18 | expected_params['Template'] = 'Template' 19 | expected_params['Custom parameters'] = {:key => 'value'}.to_json 20 | 21 | rambafile = {} 22 | rambafile[Generamba::PROJECT_NAME_KEY] = 'project' 23 | rambafile[Generamba::PROJECT_FILE_PATH_KEY] = 'file_path' 24 | rambafile[Generamba::PROJECT_GROUP_PATH_KEY] = 'group_path' 25 | rambafile[Generamba::PROJECT_TARGET_KEY] = 'Target' 26 | rambafile[Generamba::PROJECT_FILE_PATH_KEY] = 'Project' 27 | rambafile[Generamba::PROJECT_GROUP_PATH_KEY] = 'Project/Modules' 28 | rambafile[Generamba::TEST_TARGET_KEY] = 'TargetTests' 29 | rambafile[Generamba::TEST_FILE_PATH_KEY] = 'ProjectTests' 30 | rambafile[Generamba::TEST_GROUP_PATH_KEY] = 'ProjectTests/Modules' 31 | 32 | options = {} 33 | options[:custom_parameters] = {:key => 'value'} 34 | 35 | code_module = Generamba::CodeModule.new('name', rambafile, options) 36 | template_name = 'Template' 37 | 38 | params = Generamba::GenCommandTableParametersFormatter.prepare_parameters_for_displaying(code_module, template_name) 39 | 40 | expect(params).to eq(expected_params) 41 | end 42 | end 43 | 44 | end 45 | -------------------------------------------------------------------------------- /spec/generamba_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe Generamba do 4 | it 'has a version number' do 5 | expect(Generamba::VERSION).not_to be nil 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'generamba' 3 | -------------------------------------------------------------------------------- /spec/template_installer_factory_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'generamba/template/installer/template_installer_factory' 3 | require 'generamba/template/processor/template_declaration' 4 | 5 | describe 'TemplateInstallerFactory' do 6 | describe 'method installer_for_type' do 7 | subject(:factory) { Generamba::TemplateInstallerFactory.new } 8 | it 'returns local installer' do 9 | installer = factory.installer_for_type(Generamba::TemplateDeclarationType::LOCAL_TEMPLATE) 10 | result = installer.is_a?(Generamba::LocalInstaller) 11 | 12 | expect(result).to eq(true) 13 | end 14 | 15 | it 'returns remote installer' do 16 | installer = factory.installer_for_type(Generamba::TemplateDeclarationType::REMOTE_TEMPLATE) 17 | result = installer.is_a?(Generamba::RemoteInstaller) 18 | 19 | expect(result).to eq(true) 20 | end 21 | 22 | it 'returns catalog installer' do 23 | installer = factory.installer_for_type(Generamba::TemplateDeclarationType::CATALOG_TEMPLATE) 24 | result = installer.is_a?(Generamba::CatalogInstaller) 25 | 26 | expect(result).to eq(true) 27 | end 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /spec/template_processor_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | require 'fakefs/spec_helpers' 3 | 4 | describe 'TemplateProcessor' do 5 | include FakeFS::SpecHelpers 6 | 7 | describe 'method install_templates' do 8 | it 'should clear previously installed templates' do 9 | FakeFS do 10 | installed_templates_path = Pathname.new(Generamba::TEMPLATES_FOLDER) 11 | FileUtils.mkdir_p(installed_templates_path) 12 | 13 | processor = Generamba::TemplateProcessor.new(nil, nil) 14 | processor.install_templates({}) 15 | result = installed_templates_path.exist? 16 | 17 | expect(result).to eq(false) 18 | end 19 | end 20 | 21 | it 'should check for templates declaration' do 22 | expect(STDOUT).to receive(:puts).with('You must specify at least one template in Rambafile under the key *templates*'.red) 23 | processor = Generamba::TemplateProcessor.new(nil, nil) 24 | processor.install_templates({}) 25 | end 26 | 27 | it 'should update shared catalog if needed' do 28 | rambafile = { 29 | Generamba::TEMPLATES_KEY => [ 30 | { 31 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test' 32 | } 33 | ] 34 | } 35 | downloader = instance_double('Generamba::CatalogDownloader') 36 | allow(downloader).to receive(:download_catalog) 37 | 38 | mock_installer = instance_double('Generamba::CatalogInstaller') 39 | allow(mock_installer).to receive(:install_template) 40 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 41 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 42 | 43 | processor = Generamba::TemplateProcessor.new(downloader, installer_factory) 44 | processor.install_templates(rambafile) 45 | 46 | expect(downloader).to have_received(:download_catalog).with(Generamba::GENERAMBA_CATALOG_NAME, Generamba::RAMBLER_CATALOG_REPO) 47 | end 48 | 49 | it 'should update other catalogs if needed' do 50 | test_catalog_url = 'http://github.com/catalog1' 51 | test_catalog_name = test_catalog_url.split('://').last 52 | test_catalog_name = test_catalog_name.gsub('/', '-'); 53 | rambafile = { 54 | Generamba::TEMPLATES_KEY => [ 55 | { 56 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test' 57 | } 58 | ], 59 | Generamba::CATALOGS_KEY => [ 60 | test_catalog_url 61 | ] 62 | } 63 | 64 | downloader = instance_double('Generamba::CatalogDownloader') 65 | allow(downloader).to receive(:download_catalog) 66 | 67 | mock_installer = instance_double('Generamba::CatalogInstaller') 68 | allow(mock_installer).to receive(:install_template) 69 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 70 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 71 | 72 | processor = Generamba::TemplateProcessor.new(downloader, installer_factory) 73 | processor.install_templates(rambafile) 74 | expect(downloader).to have_received(:download_catalog).with(test_catalog_name, test_catalog_url) 75 | end 76 | 77 | it 'should install local templates' do 78 | rambafile = { 79 | Generamba::TEMPLATES_KEY => [ 80 | { 81 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test', 82 | Generamba::TEMPLATE_DECLARATION_LOCAL_KEY => 'path' 83 | } 84 | ] 85 | } 86 | 87 | mock_installer = instance_double('Generamba::LocalInstaller') 88 | allow(mock_installer).to receive(:install_template) 89 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 90 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 91 | 92 | processor = Generamba::TemplateProcessor.new(nil, installer_factory) 93 | processor.install_templates(rambafile) 94 | 95 | expect(installer_factory).to have_received(:installer_for_type).with(Generamba::TemplateDeclarationType::LOCAL_TEMPLATE) 96 | expect(mock_installer).to have_received(:install_template) 97 | end 98 | 99 | it 'should install remote templates' do 100 | rambafile = { 101 | Generamba::TEMPLATES_KEY => [ 102 | { 103 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test', 104 | Generamba::TEMPLATE_DECLARATION_GIT_KEY => 'path' 105 | } 106 | ] 107 | } 108 | 109 | mock_installer = instance_double('Generamba::RemoteInstaller') 110 | allow(mock_installer).to receive(:install_template) 111 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 112 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 113 | 114 | processor = Generamba::TemplateProcessor.new(nil, installer_factory) 115 | processor.install_templates(rambafile) 116 | 117 | expect(installer_factory).to have_received(:installer_for_type).with(Generamba::TemplateDeclarationType::REMOTE_TEMPLATE) 118 | expect(mock_installer).to have_received(:install_template) 119 | end 120 | 121 | it 'should install catalog templates' do 122 | rambafile = { 123 | Generamba::TEMPLATES_KEY => [ 124 | { 125 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test' 126 | } 127 | ] 128 | } 129 | downloader = instance_double('Generamba::CatalogDownloader') 130 | allow(downloader).to receive(:download_catalog) 131 | 132 | mock_installer = instance_double('Generamba::CatalogInstaller') 133 | allow(mock_installer).to receive(:install_template) 134 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 135 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 136 | 137 | processor = Generamba::TemplateProcessor.new(downloader, installer_factory) 138 | processor.install_templates(rambafile) 139 | 140 | expect(installer_factory).to have_received(:installer_for_type).with(Generamba::TemplateDeclarationType::CATALOG_TEMPLATE) 141 | expect(mock_installer).to have_received(:install_template) 142 | end 143 | 144 | it 'should clear previously downloaded catalogs' do 145 | rambafile = { 146 | Generamba::TEMPLATES_KEY => [ 147 | { 148 | Generamba::TEMPLATE_DECLARATION_NAME_KEY => 'test' 149 | } 150 | ] 151 | } 152 | downloader = instance_double('Generamba::CatalogDownloader') 153 | allow(downloader).to receive(:download_catalog) 154 | 155 | mock_installer = instance_double('Generamba::CatalogInstaller') 156 | allow(mock_installer).to receive(:install_template) 157 | installer_factory = instance_double('Generamba::TemplateInstallerFactory') 158 | allow(installer_factory).to receive(:installer_for_type).and_return(mock_installer) 159 | 160 | FakeFS do 161 | catalogs_path = Pathname.new(ENV['HOME']) 162 | .join(Generamba::GENERAMBA_HOME_DIR) 163 | .join(Generamba::CATALOGS_DIR) 164 | custom_catalog_path = catalogs_path.join('custom-catalog') 165 | FileUtils.mkdir_p(custom_catalog_path) 166 | 167 | processor = Generamba::TemplateProcessor.new(downloader, installer_factory) 168 | processor.install_templates(rambafile) 169 | result = custom_catalog_path.exist? 170 | 171 | expect(result).to eq(false) 172 | end 173 | end 174 | end 175 | end 176 | -------------------------------------------------------------------------------- /spec/xcodeproj_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe 'XcodeprojHelper' do 4 | describe 'method add_file_to_target?' do 5 | it 'should return true for Swift file' do 6 | filename = 'Test.swift' 7 | result = Generamba::XcodeprojHelper::is_compile_source?(filename) 8 | expect(result).to eq(true) 9 | end 10 | 11 | it 'should return true for Obj-C file' do 12 | filename = 'Test.m' 13 | result = Generamba::XcodeprojHelper::is_compile_source?(filename) 14 | expect(result).to eq(true) 15 | end 16 | 17 | it 'should return true for C++ file' do 18 | filename = 'Test.mm' 19 | result = Generamba::XcodeprojHelper::is_compile_source?(filename) 20 | expect(result).to eq(true) 21 | end 22 | end 23 | 24 | describe 'method add_file_to_bundle_resources?' do 25 | it 'should return true for Xib file' do 26 | resource_name = 'Test.xib' 27 | result = Generamba::XcodeprojHelper::is_bundle_resource?(resource_name) 28 | expect(result).to eq(true) 29 | end 30 | 31 | it 'should return true for Storyboard file' do 32 | resource_name = 'Test.storyboard' 33 | result = Generamba::XcodeprojHelper::is_bundle_resource?(resource_name) 34 | expect(result).to eq(true) 35 | end 36 | end 37 | end 38 | --------------------------------------------------------------------------------