Skip to content

Commit

Permalink
feature(xcode): added support for exporting template sets as xcode te…
Browse files Browse the repository at this point in the history
…mplates
  • Loading branch information
MarcoCabazal committed Feb 22, 2018
1 parent e0e9ba7 commit c04869c
Show file tree
Hide file tree
Showing 31 changed files with 306 additions and 186 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
molt (0.1.4)
molt (0.2.0)
commander
liquid (~> 4.0.0)
terminal-table
Expand Down
225 changes: 129 additions & 96 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions bundle/partials/_header.liquid
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// {{ project.name }}
// {{ module_name }}{{ filename_suffix }}
//
// Created by {{ developer.name }} on {{ date }}.
// Copyright © {{ year }} {{ developer.company }}. All rights reserved.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// {{ module_name }}LocalDataManager.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "LocalDataManager.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import CoreData
import Hydra

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// {{ module_name }}RemoteDataManager.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "RemoteDataManager.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___ {% endif %}

import Alamofire
import AlamofireObjectMapper
import Hydra
Expand All @@ -10,10 +9,9 @@ class {{ module_name }}RemoteDataManager: {{ module_name }}RemoteDataManagerProt

func retrieveDataFromAPI() -> Promise<[{{ model }}]> {
return Promise<[{{ model }}]>(in: .background) { resolve, reject, _ in
Alamofire.request(APIRouter.{{ model | downcase }})
Alamofire.request(APIRouter.<# Enum Key #>)
.validate()
.responseArray(keyPath: "{{ model | downcase }}") { (response: DataResponse<[{{ model }}]>) in

.responseArray(keyPath: "<# JSON KeyPath #>") { (response: DataResponse<[{{ model }}]>) in
switch response.result {
case .success(let data):
resolve(data)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// {{ module_name }}Interactor.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Interactor.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

final class {{ module_name }}Interactor {
weak var presenter: {{ module_name }}InteractorOutputProtocol?
var localDataManager: {{ module_name }}LocalDataManagerProtocol?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//
// {{ module_name }}Presenter.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Presenter.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

final class {{ module_name }}Presenter {

weak var view: {{ module_name }}ViewProtocol?
var wireFrame: {{ module_name }}WireFrameProtocol?
var wireframe: {{ module_name }}WireframeProtocol?
var interactor: {{ module_name }}InteractorProtocol?
var dataSource: [{{ model }}] = []
}
Expand All @@ -29,7 +28,7 @@ extension {{ module_name }}Presenter: {{ module_name }}PresenterProtocol {

func process(object: {{ model }}) {
guard let view = view else { return }
wireFrame?.navigate(to: object, from: view)
wireframe?.navigate(to: object, from: view)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
//
// {{ module_name }}Protocols.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Protocols.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit
import Hydra

// MARK: Called by VIEW -> Implemented BY PRESENTER
protocol {{ module_name }}PresenterProtocol: class {
var view: {{ module_name }}ViewProtocol? { get set }
var wireFrame: {{ module_name }}WireFrameProtocol? { get set }
var wireframe: {{ module_name }}WireframeProtocol? { get set }
var interactor: {{ module_name }}InteractorProtocol? { get set }

func viewDidLoad()

// datasource
var numberOfSection: Int { get }
func numberOfRows(in section: Int) -> Int
func content(at row: Int) -> <{{ model }}>?
func content(at row: Int) -> {{ model }}?

func process(object: {{ model }})
}
Expand Down Expand Up @@ -52,7 +51,7 @@ protocol {{ module_name }}ViewProtocol: Loadable {
}

// MARK: PRESENTER -> WIREFRAME
protocol {{ module_name }}WireFrameProtocol: class {
protocol {{ module_name }}WireframeProtocol: class {
static func prepareModule() -> UIViewController
func navigate(to object: {{ model }}, from view: {{ module_name }}ViewProtocol)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// {{ module_name }}View.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "View.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit

class {{ module_name }}View: UIViewController {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//
// {{ module_name }}WireFrame.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Wireframe.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit

final class {{ module_name }}WireFrame {
final class {{ module_name }}Wireframe {

static func prepareModule() -> UIViewController {
let navController = UINavigationController.instantiate(from: .main, identifier: "mainNavigationController")
Expand All @@ -14,14 +13,14 @@ final class {{ module_name }}WireFrame {

let presenter: {{ module_name }}PresenterProtocol & {{ module_name }}InteractorOutputProtocol = {{ module_name }}Presenter()
let interactor: {{ module_name }}InteractorProtocol = {{ module_name }}Interactor()
let wireFrame: {{ module_name }}WireFrameProtocol = {{ module_name }}WireFrame()
let wireframe: {{ module_name }}WireframeProtocol = {{ module_name }}Wireframe()
let localDataManager: {{ module_name }}LocalDataManagerProtocol = {{ module_name }}LocalDataManager()
let remoteDataManager: {{ module_name }}RemoteDataManagerProtocol = {{ module_name }}RemoteDataManager()

// INTEGRATION
view.presenter = presenter
presenter.view = view
presenter.wireFrame = wireFrame
presenter.wireframe = wireframe
presenter.interactor = interactor
interactor.presenter = presenter
interactor.localDataManager = localDataManager
Expand All @@ -32,12 +31,12 @@ final class {{ module_name }}WireFrame {
}

// MARK: NAVIGATION
extension {{ module_name }}WireFrame: {{ module_name }}WireFrameProtocol {
extension {{ module_name }}Wireframe: {{ module_name }}WireframeProtocol {

func navigate(to object: {{ model }}, from view: {{ module_name }}ViewProtocol) {
guard let sourceView = view as? UIViewController else { return }

let detailVC = {{ module_name }}DetailWireFrame.prepareModule(with: object)
sourceView.navigationController?.pushViewController(detailVC, animated: true)
// let detailVC = {{ module_name }}DetailWireframe.prepareModule(with: object)
// sourceView.navigationController?.pushViewController(detailVC, animated: true)
}
}
7 changes: 3 additions & 4 deletions bundle/template_sets/viper_detail/Presenter.swift.liquid
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//
// {{ module_name }}Interactor.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Presenter.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

final class {{ module_name }}Presenter {

weak var view: {{ module_name }}ViewProtocol?
var wireFrame: {{ module_name }}WireFrameProtocol?
var wireframe: {{ module_name }}WireframeProtocol?
var dataSource: {{ model }}?
}

Expand Down
9 changes: 4 additions & 5 deletions bundle/template_sets/viper_detail/Protocols.swift.liquid
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//
// {{ module_name }}Protocols.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Protocols.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit

// MARK: Called by VIEW -> Implemented BY PRESENTER
protocol {{ module_name }}PresenterProtocol: class {
weak var view: {{ module_name }}ViewProtocol? { get set }
weak var wireFrame: {{ module_name }}WireFrameProtocol? { get set }
weak var wireframe: {{ module_name }}WireframeProtocol? { get set }
var dataSource: {{ model }}? { get set }

func viewDidLoad()
Expand All @@ -18,6 +17,6 @@ protocol {{ module_name }}ViewProtocol: Loadable {
}

// MARK: PRESENTER -> WIREFRAME
protocol {{ module_name }}WireFrameProtocol: class {
protocol {{ module_name }}WireframeProtocol: class {
static func prepareModule(with object: {{ model }}) -> UIViewController
}
5 changes: 2 additions & 3 deletions bundle/template_sets/viper_detail/View.swift.liquid
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// {{ module_name }}View.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "View.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit

class {{ module_name }}View: UIViewController {
Expand Down
11 changes: 5 additions & 6 deletions bundle/template_sets/viper_detail/WireFrame.swift.liquid
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
//
// {{ module_name }}WireFrame.swift
{% include 'header' %}
{% if target != "xcode" %}{% assign filename_suffix = "Wireframe.swift" %}{% include 'header' %}{% else %}//___FILEHEADER___{% endif %}

import UIKit

final class {{ module_name }}WireFrame: {{ module_name }}WireFrameProtocol {
final class {{ module_name }}Wireframe: {{ module_name }}WireframeProtocol {

static func prepareModule(with object: {{ model }}) -> UIViewController {
let view = {{ module_name }}View.instantiate(from: .main)

let presenter: {{ module_name }}PresenterProtocol = {{ module_name }}Presenter()
let wireFrame = {{ module_name }}WireFrame()
let wireframe = {{ module_name }}Wireframe()

view.presenter = presenter
presenter.view = view
presenter.dataSource = object
presenter.wireFrame = wireFrame
presenter.wireframe = wireframe

return view
}
Expand Down
13 changes: 12 additions & 1 deletion lib/molt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@
require "generamba/string-colorize"

module Molt
ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))
MOLT_GLOBAL = "#{Dir.home}/.molt"
MOLT_PROJECT = "./.molt"
CONFIG_GLOBAL = "#{MOLT_GLOBAL}/config.yml"
CONFIG_PROJECT = "#{MOLT_PROJECT}/config.yml"
LONG_DESC = <<-TAG
LONG_DESC_CREATE_MODULE = <<-TAG
This generates a scaffold for a new VIPER module all contained in a folder. You will then have onto drag this folder onto Xcode.
This command runs in dry-run mode by default (without using -f). That means this will only print the project/module configuration, list of templates, and eventual output files.
When you're happy with what you see, just add --do-it or -f to exit dry-run mode.
* Note that the default template set "viper_table" uses several Swift helper extensions all of which could be found in either ~/.molt or your project's ./.molt folder. These were all copied when you ran "molt setup" or "molt setup_project". Feel free to copy them or modify the template files to suit your preference.
TAG
LONG_DESC_XCODE = <<-TAG
This command basically exports a template set and makes it available as an Xcode template, which makes your often-used template instantly available from within Xcode itself. When you do create a new file from within Xcode, you will be prompted for the module name and the model. Other meta details normally available in create_module won't be necessary during the export.
This command runs in dry-run mode by default (without using -f). That means this will only print the project/module configuration, list of templates, and eventual output files.
When you're happy with what you see, just add --do-it or -f to exit dry-run mode.
* Note that the default template set "viper_table" uses several Swift helper extensions all of which could be found in either ~/.molt or your project's ./.molt folder. These were all copied when you ran "molt setup" or "molt setup_project". Feel free to copy them or modify the template files to suit your preference.
TAG
end
Expand Down
32 changes: 20 additions & 12 deletions lib/molt/cli/create_module.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
module Molt::CLI
class Generator

def self.create_module(module_name, template_set, options)
puts "OPTIONS #{options.inspect}".blue
def self.create_module(module_name, template_set, options, xcode = false)
config = Molt::Configuration.load_or_initialize
config = Molt::Configuration.apply_cli_overrides config, options, module_name, template_set
rows = table_rows_for config
if !xcode
config = Molt::Configuration.apply_cli_overrides config, options, module_name, template_set
else
config = Molt::Configuration.config_for_xcode config, options, module_name, template_set
end
rows = table_rows_for config, xcode

sets = ""
models = ""
Expand All @@ -20,14 +23,17 @@ def self.create_module(module_name, template_set, options)

Dir["#{sets}/#{template_set}/**/*swift.liquid"].each do |template_file|
template_base_folder = File.dirname(template_file).gsub(/#{sets}\/#{template_set}/, "")
destination_folder = "#{options.output_folder}/#{module_name}#{template_base_folder}"
destination_folder = "#{options.output_folder}/#{module_name}#{template_base_folder}" if !xcode
destination_folder = File.expand_path("~/Library/Developer/Xcode/Templates/File Templates/Molt/#{template_set.gsub(/_/, " ")}.xctemplate") if xcode

base_file = File.basename(template_file.gsub(/.liquid$/, ""))
output_file = "#{destination_folder}/#{module_name}#{base_file}"
output_file = "#{destination_folder}/#{module_name}#{base_file}" if !xcode
output_file = "#{destination_folder}/___FILEBASENAME___#{base_file}" if xcode
rows << [template_file.gsub(/#{sets}\//, ""), output_file.blue]

if options.do_it
FileUtils.mkdir_p destination_folder
FileUtils.cp Dir.glob("#{Molt::ROOT}/sample_configs/Template*"), destination_folder
Molt::Template.liquify(template: template_file, output_file: output_file, config: config)
end
end
Expand Down Expand Up @@ -55,14 +61,16 @@ def self.create_module(module_name, template_set, options)
end

private
def self.table_rows_for config
def self.table_rows_for config, xcode
rows = []
rows << ["Developer", config["developer"]["name"].blue]
rows << ["Email", config["developer"]["email"].blue]
rows << ["Company", config["developer"]["company"].blue]
rows << ["Project", config["project"]["name"].blue]

rows << [" ", " "]
if !xcode
rows << ["Developer", config["developer"]["name"].blue]
rows << ["Email", config["developer"]["email"].blue]
rows << ["Company", config["developer"]["company"].blue]
rows << ["Project", config["project"]["name"].blue]
rows << [" ", " "]
end
rows << ["Template Set", config["template_set"].yellow]
rows << [" ", " "]
rows << ["Source".green, "Destination".green]
Expand Down
15 changes: 13 additions & 2 deletions lib/molt/cli/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def self.start
command :create_module do |c|
c.syntax = 'molt create_module MODULE_NAME TEMPLATE_SET'
c.summary = "Generate component files for a VIPER module"
c.description = Molt::LONG_DESC
c.description = Molt::LONG_DESC_CREATE_MODULE
c.option '--name NAME', String, "Author name"
c.option '--email EMAIL', String, "Author email"
c.option '--company COMPANY', String, "Author company"
Expand All @@ -62,12 +62,23 @@ def self.start
c.option '--do-it', false, "Release the Kraken and write the files"
c.action do |args, options|
options.default :output_folder => "."
puts "ARGS #{args}\nOPTS #{options.inspect}"
Molt::CLI::Generator.create_module(args[0], args[1], options)
end
end
alias_command :'c', :create_module

command :xcode do |c|
c.syntax = 'molt xcode TEMPLATE_SET'
c.summary = "Export template set as Xcode template"
c.description = Molt::LONG_DESC_XCODE
c.option '--output-folder DIRECTORY', String, "Create module folder in this directory. Defaults to \"./\""
c.option '--do-it', false, "Release the Kraken and write the files"
c.action do |args, options|
options.default :output_folder => "."
Molt::CLI::Generator.create_module("", args[0], options, true)
end
end
alias_command :'c', :create_module
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/molt/cli/setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ class Setup < Thor
include Thor::Actions

def self.source_root
gem_root = File.join(File.dirname(__FILE__), "../../..")
File.expand_path(gem_root)
Molt::ROOT
end

desc "perform", "perform"
Expand Down
Loading

0 comments on commit c04869c

Please sign in to comment.