swift-boiler 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +67 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/swift-boil +5 -0
- data/lib/swift/boiler/builder.rb +26 -0
- data/lib/swift/boiler/parser.rb +172 -0
- data/lib/swift/boiler/scanner.rb +23 -0
- data/lib/swift/boiler/template.rb +12 -0
- data/lib/swift/boiler/templates/controller.mustache +59 -0
- data/lib/swift/boiler/templates/model.mustache +31 -0
- data/lib/swift/boiler/templates/singleton.mustache +17 -0
- data/lib/swift/boiler/templates/table_view_cell.mustache +135 -0
- data/lib/swift/boiler/templates/view.mustache +126 -0
- data/lib/swift/boiler/templates/viewmodel.mustache +16 -0
- data/lib/swift/boiler/token.rb +15 -0
- data/lib/swift/boiler/token_factory.rb +56 -0
- data/lib/swift/boiler/token_pattern_validator.rb +73 -0
- data/lib/swift/boiler/version.rb +5 -0
- data/lib/swift/boiler.rb +37 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ae6e8acdb47998ccba7f7d369e45246852ac134d
|
4
|
+
data.tar.gz: 62dc57b257198487389f4ca88f7b80c191345f9d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d28b9e6f66707174357fab342d4cb97e47d5c7f6069fec4e20c1951f438ec5dd9c0639815af870a6d32b08ab4a0b2ed58a5b803ee35e2412f6b3ea1dbd36e574
|
7
|
+
data.tar.gz: 190b8965ab0fbc9c39dd033f42dda7287cb4c9a0e7582f6f5165cf6dffaa272aeae72ee7a73bf0859466cea225d366a036031f3c0d731f145c71edba5c01fd98
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Swift-Boiler
|
2
|
+
|
3
|
+
Swift-Boiler is a tool developed for iOS developers to speed up the process of implementing views, models, controllers, and many other common patterns found in an iOS project. Inspired by the swift syntax and the simplicity of the Rails generate tool, Swift-Boiler is a simple to use boiler plate code generator that will get you up running in no time.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
### Install Swift-Boiler via [Homebrew](http://brew.sh):
|
8
|
+
|
9
|
+
```sh
|
10
|
+
$ brew update
|
11
|
+
$ brew install swift-boiler
|
12
|
+
```
|
13
|
+
|
14
|
+
<!-- ### Install it with [Homebrew](http://brew.sh):
|
15
|
+
|
16
|
+
```sh
|
17
|
+
$ gem install swift-boiler
|
18
|
+
``` -->
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Swift-Boiler has only one swift-boil binary, and the command follow this specific syntax:
|
23
|
+
<summary>swi
|
24
|
+
ft-boil \<option\></summary>
|
25
|
+
```sh
|
26
|
+
$ swift-boil --help
|
27
|
+
```
|
28
|
+
|
29
|
+
<summary>swift-boil \<option\> \<template_path\> \<class_name\> \<options\> \<properties\></summary>
|
30
|
+
```sh
|
31
|
+
$ swift-boil -t /Desktop/mytemplate.mustache MyView toplabel:UILabel bottomlabel:UILabel
|
32
|
+
$ swift-boil -t /Desktop/mytemplate.mustache MyView -d toplabel:UILabel bottomlabel:UILabel
|
33
|
+
```
|
34
|
+
|
35
|
+
<summary>swift-boil \<command\> \<class_name\> \<options\> \<properties\></summary>
|
36
|
+
```sh
|
37
|
+
$ swift-boil v MyView mainlabel:UILabel firstView:UIView secondView:CustomView
|
38
|
+
$ swift-boil view MyView -d mainlabel:UILabel firstView:UIView secondView:CustomView
|
39
|
+
```
|
40
|
+
|
41
|
+
### Commands:
|
42
|
+
- `view or v`: command to generate the boiler plate code for a view
|
43
|
+
- `model or m`: command to generate the boiler plate code for a model
|
44
|
+
- `controller or c`: command to generate the boiler plate code for a controller
|
45
|
+
- `tableViewCell or tvc`: command to generate the boiler plate code for a table view cell
|
46
|
+
- `singleton or s`: command to generate the boiler plate code for a singleton
|
47
|
+
|
48
|
+
### Options:
|
49
|
+
- `--help or -h`: shows usage details
|
50
|
+
- `--delegate or -d`: it indicates that Swift-Boiler should add a delegate to the class being generated
|
51
|
+
- `--template or -t`: allows user to specify a custom template to be used to generate code
|
52
|
+
|
53
|
+
## Development
|
54
|
+
|
55
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
56
|
+
|
57
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
Bug reports and pull requests are welcome on GitHub at our [Bug Section](https://github.com/Boilerplate-Factory/swift-boiler/issues).
|
62
|
+
|
63
|
+
|
64
|
+
## License
|
65
|
+
|
66
|
+
The gem is available as open source under the terms of the [MIT License](https://github.com/Boilerplate-Factory/swift-boiler/blob/master/LICENSE.txt).
|
67
|
+
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "swift/boiler"
|
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
|
data/bin/setup
ADDED
data/bin/swift-boil
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Swift
|
2
|
+
module Boiler
|
3
|
+
class Builder
|
4
|
+
|
5
|
+
def build_template(template)
|
6
|
+
Mustache.template_file = template.template_path
|
7
|
+
mustache_template = Mustache.new
|
8
|
+
mustache_template[:class_name] = template.class_name
|
9
|
+
#mustache_template[:dev_name] = template.developer_name
|
10
|
+
mustache_template[:date] = template.date
|
11
|
+
mustache_template[:options] = template.options
|
12
|
+
mustache_template[:properties] = template.properties
|
13
|
+
# puts template.inspect
|
14
|
+
create_file(mustache_template.render.to_s, mustache_template[:class_name])
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_file(text, class_name)
|
18
|
+
puts text
|
19
|
+
out_file = File.new(class_name + ".swift", "w")
|
20
|
+
out_file.puts(text)
|
21
|
+
out_file.close
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
require 'swift/boiler/template'
|
3
|
+
require 'swift/boiler/token'
|
4
|
+
|
5
|
+
module Swift
|
6
|
+
module Boiler
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
TYPE = :type
|
10
|
+
NAME = :name
|
11
|
+
PROPERTY_NAME_INDEX = 0
|
12
|
+
PROPERTY_TYPE_INDEX = 1
|
13
|
+
CAPITALIZED_NAME = :capitalized_name
|
14
|
+
LABEL_TYPE = :ui_label
|
15
|
+
VIEW_TYPE = :ui_view
|
16
|
+
UNKNOWN_TYPE = :unknow_type
|
17
|
+
SHORTCUTS = { tableviewcell: 'table_view_cell', m: 'model', v: 'view', c: 'controller', s: 'singleton', tvc: 'table_view_cell' }
|
18
|
+
|
19
|
+
def create_template_from_tokens(tokens)
|
20
|
+
template = Template.new
|
21
|
+
template.template_path = get_template_path(tokens)
|
22
|
+
template.class_name = get_class_name(tokens)
|
23
|
+
template.options = get_options(tokens)
|
24
|
+
template.properties = get_properties(tokens)
|
25
|
+
template
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_template_path(tokens)
|
29
|
+
tokens.each do |token|
|
30
|
+
if is_path_generation_possible_from_token(token)
|
31
|
+
return get_template_path_from_token(token)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def is_path_generation_possible_from_token(token)
|
37
|
+
is_template_path_token(token) || is_template_name_token(token)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_template_path_from_token(token)
|
41
|
+
if is_template_path_token(token)
|
42
|
+
return get_template_path_from_path_token(token)
|
43
|
+
elsif is_template_name_token(token)
|
44
|
+
return get_template_path_from_name_token(token)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_class_name(tokens)
|
49
|
+
tokens.each do |token|
|
50
|
+
if is_class_name_token(token)
|
51
|
+
return token.content
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_options(tokens)
|
57
|
+
options = {}
|
58
|
+
tokens.each do |token|
|
59
|
+
options[:protocol] = is_protocol_token(token)
|
60
|
+
end
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_properties(tokens)
|
65
|
+
properties = {}
|
66
|
+
properties[LABEL_TYPE] = Array.new
|
67
|
+
properties[VIEW_TYPE] = Array.new
|
68
|
+
properties[UNKNOWN_TYPE] = Array.new
|
69
|
+
tokens.each do |token|
|
70
|
+
if token.type == Token::PROPERTY
|
71
|
+
new_property = get_property_from_token(token)
|
72
|
+
case new_property[TYPE].to_s
|
73
|
+
when "UILabel"
|
74
|
+
properties[LABEL_TYPE] << new_property
|
75
|
+
when "UIView"
|
76
|
+
properties[VIEW_TYPE] << new_property
|
77
|
+
else
|
78
|
+
properties[UNKNOWN_TYPE] << new_property
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
properties
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_template_path_from_path_token(token)
|
86
|
+
token.content
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_template_path_from_name_token(token)
|
90
|
+
template_name = get_template_name_from_token(token)
|
91
|
+
File.dirname(__FILE__) + '/templates/' + template_name + ".mustache"
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_template_name_from_token(template_name_token)
|
95
|
+
if is_abreviation(template_name_token)
|
96
|
+
get_name_from_abreviation(template_name_token.content.downcase)
|
97
|
+
else
|
98
|
+
get_name_from_name_token(template_name_token)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_property_from_token(token)
|
103
|
+
name = get_property_name_from_token(token)
|
104
|
+
type = get_property_type_from_token(token)
|
105
|
+
capitalized_name = capitalize_name(name)
|
106
|
+
create_property_dictionary(name, capitalized_name, type)
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_property_dictionary(name, capitalized_name, type)
|
110
|
+
property = {}
|
111
|
+
property[NAME] = name
|
112
|
+
property[TYPE] = type
|
113
|
+
property[CAPITALIZED_NAME] = capitalized_name
|
114
|
+
property
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_property_name_from_token(token)
|
118
|
+
argument_list = token.content.split(/:/)
|
119
|
+
name = argument_list[PROPERTY_NAME_INDEX]
|
120
|
+
downcase_name(name)
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_property_type_from_token(token)
|
124
|
+
argument_list = token.content.split(/:/)
|
125
|
+
argument_list[PROPERTY_TYPE_INDEX]
|
126
|
+
end
|
127
|
+
|
128
|
+
def capitalize_name(name)
|
129
|
+
name.split.map(&:capitalize).join(' ')
|
130
|
+
end
|
131
|
+
|
132
|
+
def downcase_name(name)
|
133
|
+
place_holder = name.dup
|
134
|
+
first_letter = place_holder.slice!(0).downcase
|
135
|
+
first_letter + place_holder
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_name_from_abreviation(abreviation)
|
139
|
+
print SHORTCUTS[abreviation.to_sym]
|
140
|
+
SHORTCUTS[abreviation.to_sym]
|
141
|
+
end
|
142
|
+
|
143
|
+
def get_name_from_name_token(template_name_token)
|
144
|
+
template_name_token.content.downcase
|
145
|
+
end
|
146
|
+
|
147
|
+
def is_abreviation(template_name_token)
|
148
|
+
template_name = template_name_token.content.downcase
|
149
|
+
template_name_token.type == Token::TEMPLATE_NAME && SHORTCUTS.has_key?(template_name.to_sym)
|
150
|
+
end
|
151
|
+
|
152
|
+
def is_protocol_token(token)
|
153
|
+
token.type == Token::OPTION && (token.content == "-p" || token.content == "--protocol")
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def is_template_path_token(token)
|
159
|
+
token.type == Token::TEMPLATE_PATH
|
160
|
+
end
|
161
|
+
|
162
|
+
def is_template_name_token(token)
|
163
|
+
token.type == Token::TEMPLATE_NAME
|
164
|
+
end
|
165
|
+
|
166
|
+
def is_class_name_token(token)
|
167
|
+
token.type == Token::CLASS_NAME
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'swift/boiler/token'
|
3
|
+
require 'swift/boiler/token_factory'
|
4
|
+
require 'swift/boiler/token_pattern_validator'
|
5
|
+
|
6
|
+
module Swift
|
7
|
+
module Boiler
|
8
|
+
class Scanner
|
9
|
+
|
10
|
+
def create_valid_token_pattern_from_arguments(arguments)
|
11
|
+
token_factory = Swift::Boiler::TokenFactory.new
|
12
|
+
token_pattern_validator = Swift::Boiler::TokenPatternValidator.new
|
13
|
+
tokens = token_factory.create_tokens_from_arguments(arguments)
|
14
|
+
if token_pattern_validator.validate(tokens)
|
15
|
+
return tokens
|
16
|
+
else
|
17
|
+
raise ArgumentError.new("The following sequence of arguments was not valid #{arguments}.")
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
//
|
2
|
+
// {{class_name}}.swift
|
3
|
+
//
|
4
|
+
// Created by <# Developer Name #> on {{date}}.
|
5
|
+
//
|
6
|
+
//
|
7
|
+
|
8
|
+
import UIKit
|
9
|
+
|
10
|
+
class {{class_name}}: UIViewController {
|
11
|
+
private let mainView = {{class_name}}View()
|
12
|
+
{{#properties.ui_label}}
|
13
|
+
|
14
|
+
private let {{name}}:UILabel = {
|
15
|
+
let {{name}} = UILabel()
|
16
|
+
{{name}}.textColor = <# UIColor #>
|
17
|
+
{{name}}.font = <# UIFont #>
|
18
|
+
return {{name}}
|
19
|
+
}()
|
20
|
+
{{/properties.ui_label}}
|
21
|
+
{{#properties.ui_view}}
|
22
|
+
|
23
|
+
private let {{name}}:UIView = {
|
24
|
+
let {{name}} = UIView()
|
25
|
+
{{name}}.backgroundColor = <# UIColor #>
|
26
|
+
{{name}}.clipBounds = <# true or false #>
|
27
|
+
return {{name}}
|
28
|
+
}()
|
29
|
+
{{/properties.ui_view}}
|
30
|
+
{{#properties.unknow_type}}
|
31
|
+
|
32
|
+
private let {{name}} = {{type}}()
|
33
|
+
|
34
|
+
{{/properties.unknow_type}}
|
35
|
+
|
36
|
+
override func loadView() {
|
37
|
+
view = mainView
|
38
|
+
}
|
39
|
+
|
40
|
+
init() {
|
41
|
+
super.init(nibName: nil, bundle: nil)
|
42
|
+
}
|
43
|
+
|
44
|
+
required init?(coder aDecoder: NSCoder) {
|
45
|
+
fatalError("init(coder:) has not been implemented for {{class_name}}.swift")
|
46
|
+
}
|
47
|
+
|
48
|
+
override func viewDidLoad() {
|
49
|
+
super.viewDidLoad()
|
50
|
+
}
|
51
|
+
|
52
|
+
override func viewWillAppear(_ animated: Bool) {
|
53
|
+
super.viewWillAppear(animated)
|
54
|
+
}
|
55
|
+
|
56
|
+
override func viewDidAppear(_ animated: Bool) {
|
57
|
+
super.viewDidAppear(animated)
|
58
|
+
}
|
59
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
//
|
2
|
+
// {{class_name}}.swift
|
3
|
+
//
|
4
|
+
// Created by {{dev_name}} on {{date}}.
|
5
|
+
//
|
6
|
+
//
|
7
|
+
|
8
|
+
struct {{class_name}} {
|
9
|
+
|
10
|
+
{{#properties.ui_label}}
|
11
|
+
let {{name}}: {{type}} = {{type}}()
|
12
|
+
{{/properties.ui_label}}
|
13
|
+
{{#properties.ui_view}}
|
14
|
+
let {{name}}: {{type}} = {{type}}()
|
15
|
+
{{/properties.ui_view}}
|
16
|
+
{{#properties.unknow_type}}
|
17
|
+
let {{name}}: {{type}} = {{type}}()
|
18
|
+
{{/properties.unknow_type}}
|
19
|
+
|
20
|
+
public init({{#properties.unknow_type}}, {{name}}:{{type}}{{/properties.unknow_type}}{{#properties.ui_label}}, {{name}}:{{type}}{{/properties.ui_label}}{{#properties.ui_view}}, {{name}}:{{type}}{{/properties.ui_view}}) {
|
21
|
+
{{#properties.ui_label}}
|
22
|
+
self.{{name}} = {{name}}
|
23
|
+
{{/properties.ui_label}}
|
24
|
+
{{#properties.ui_view}}
|
25
|
+
self.{{name}} = {{name}}
|
26
|
+
{{/properties.ui_view}}
|
27
|
+
{{#properties.unknow_type}}
|
28
|
+
self.{{name}} = {{name}}
|
29
|
+
{{/properties.unknow_type}}
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
//
|
2
|
+
// {{class_name}}.swift
|
3
|
+
//
|
4
|
+
// Created by <# Developer Name #> on {{date}}.
|
5
|
+
//
|
6
|
+
//
|
7
|
+
|
8
|
+
|
9
|
+
class {{class_name}} {
|
10
|
+
|
11
|
+
static let shared: {{class_name}}
|
12
|
+
|
13
|
+
private init(_ shared: {{class_name}} = {{class_name}}()) {
|
14
|
+
self.shared = shared
|
15
|
+
}
|
16
|
+
|
17
|
+
}
|
@@ -0,0 +1,135 @@
|
|
1
|
+
//
|
2
|
+
// {{class_name}}.swift
|
3
|
+
//
|
4
|
+
// Created by <# Developer Name #> on {{date}}.
|
5
|
+
//
|
6
|
+
//
|
7
|
+
|
8
|
+
import UIKit
|
9
|
+
{{#options.protocol}}
|
10
|
+
|
11
|
+
protocol {{class_name}}Delegate: class {
|
12
|
+
|
13
|
+
}
|
14
|
+
|
15
|
+
protocol {{class_name}}DataSouce: class {
|
16
|
+
|
17
|
+
}
|
18
|
+
{{/options.protocol}}
|
19
|
+
|
20
|
+
class {{class_name}}: UITableViewCell {
|
21
|
+
{{#options.protocol}}
|
22
|
+
weak private var delegate: {{class_name}}Delegate?
|
23
|
+
weak private var datasource: {{class_name}}DataSource?
|
24
|
+
{{/options.protocol}}
|
25
|
+
{{#properties.ui_label}}
|
26
|
+
|
27
|
+
private let {{name}}:UILabel = {
|
28
|
+
let {{name}} = UILabel()
|
29
|
+
{{name}}.textColor = <# UIColor #>
|
30
|
+
{{name}}.font = <# UIFont #>
|
31
|
+
return {{name}}
|
32
|
+
}()
|
33
|
+
{{/properties.ui_label}}
|
34
|
+
{{#properties.ui_view}}
|
35
|
+
|
36
|
+
private let {{name}}:UIView = {
|
37
|
+
let {{name}} = UIView()
|
38
|
+
{{name}}.backgroundColor = <# UIColor #>
|
39
|
+
{{name}}.clipBounds = <# true or false #>
|
40
|
+
return {{name}}
|
41
|
+
}()
|
42
|
+
{{/properties.ui_view}}
|
43
|
+
{{#properties.unknow_type}}
|
44
|
+
|
45
|
+
private let {{name}}:{{type}} = {
|
46
|
+
return {{type}}()
|
47
|
+
}()
|
48
|
+
{{/properties.unknow_type}}
|
49
|
+
|
50
|
+
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
|
51
|
+
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
|
52
|
+
setupView()
|
53
|
+
}
|
54
|
+
|
55
|
+
required public init?(coder aDecoder: NSCoder) {
|
56
|
+
super.init(coder: aDecoder)
|
57
|
+
}
|
58
|
+
|
59
|
+
override func prepareForReuse() {
|
60
|
+
super.prepareForReuse()
|
61
|
+
{{#properties.ui_label}}
|
62
|
+
{{name}}.text = ""
|
63
|
+
{{/properties.ui_label}}
|
64
|
+
}
|
65
|
+
|
66
|
+
// MARK: - Identifier
|
67
|
+
|
68
|
+
class func identifier() -> String {
|
69
|
+
return String(describing: self)
|
70
|
+
}
|
71
|
+
|
72
|
+
// MARK: - Setup
|
73
|
+
|
74
|
+
private func setupView() {
|
75
|
+
addViewComponents()
|
76
|
+
setupConstraints()
|
77
|
+
}
|
78
|
+
|
79
|
+
private func addViewComponents() {
|
80
|
+
{{#properties.ui_label}}
|
81
|
+
contentView.addSubview({{name}})
|
82
|
+
{{/properties.ui_label}}
|
83
|
+
{{#properties.ui_view}}
|
84
|
+
contentView.addSubview({{name}})
|
85
|
+
{{/properties.ui_view}}
|
86
|
+
{{#properties.unknow_type}}
|
87
|
+
contentView.addSubview({{name}})
|
88
|
+
{{/properties.unknow_type}}
|
89
|
+
}
|
90
|
+
|
91
|
+
// MARK: - Constraints
|
92
|
+
|
93
|
+
private func setupConstraints() {
|
94
|
+
{{#properties.ui_label}}
|
95
|
+
setupConstraintsFor{{capitalized_name}}()
|
96
|
+
{{/properties.ui_label}}
|
97
|
+
{{#properties.ui_view}}
|
98
|
+
setupConstraintsFor{{capitalized_name}}()
|
99
|
+
{{/properties.ui_view}}
|
100
|
+
{{#properties.unknow_type}}
|
101
|
+
setupConstraintsFor{{capitalized_name}}()
|
102
|
+
{{/properties.unknow_type}}
|
103
|
+
}
|
104
|
+
|
105
|
+
{{#properties.ui_label}}
|
106
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
107
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
108
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
109
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
110
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
111
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
112
|
+
}
|
113
|
+
|
114
|
+
{{/properties.ui_label}}
|
115
|
+
{{#properties.ui_view}}
|
116
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
117
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
118
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
119
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
120
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
121
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
122
|
+
}
|
123
|
+
|
124
|
+
{{/properties.ui_view}}
|
125
|
+
{{#properties.unknow_type}}
|
126
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
127
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
128
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
129
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
130
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
131
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
132
|
+
}
|
133
|
+
|
134
|
+
{{/properties.unknow_type}}
|
135
|
+
}
|
@@ -0,0 +1,126 @@
|
|
1
|
+
//
|
2
|
+
// {{class_name}}.swift
|
3
|
+
//
|
4
|
+
// Created by <# Developer Name #> on {{date}}.
|
5
|
+
//
|
6
|
+
//
|
7
|
+
|
8
|
+
import UIKit
|
9
|
+
{{#options.protocol}}
|
10
|
+
|
11
|
+
protocol {{class_name}}Delegate: class {
|
12
|
+
|
13
|
+
}
|
14
|
+
|
15
|
+
protocol {{class_name}}DataSouce: class {
|
16
|
+
|
17
|
+
}
|
18
|
+
{{/options.protocol}}
|
19
|
+
|
20
|
+
class {{class_name}}: UIView {
|
21
|
+
{{#options.protocol}}
|
22
|
+
weak private var delegate: {{class_name}}Delegate?
|
23
|
+
weak private var datasource: {{class_name}}DataSource?
|
24
|
+
{{/options.protocol}}
|
25
|
+
{{#properties.ui_label}}
|
26
|
+
|
27
|
+
private let {{name}}:UILabel = {
|
28
|
+
let {{name}} = UILabel()
|
29
|
+
{{name}}.textColor = <# UIColor #>
|
30
|
+
{{name}}.font = <# UIFont #>
|
31
|
+
return {{name}}
|
32
|
+
}()
|
33
|
+
{{/properties.ui_label}}
|
34
|
+
{{#properties.ui_view}}
|
35
|
+
|
36
|
+
private let {{name}}:UIView = {
|
37
|
+
let {{name}} = UIView()
|
38
|
+
{{name}}.backgroundColor = <# UIColor #>
|
39
|
+
{{name}}.clipBounds = <# true or false #>
|
40
|
+
return {{name}}
|
41
|
+
}()
|
42
|
+
{{/properties.ui_view}}
|
43
|
+
{{#properties.unknow_type}}
|
44
|
+
|
45
|
+
private let {{name}}:{{type}} = {
|
46
|
+
return {{type}}()
|
47
|
+
}()
|
48
|
+
{{/properties.unknow_type}}
|
49
|
+
|
50
|
+
override init(frame: CGRect) {
|
51
|
+
super.init(frame: frame)
|
52
|
+
self.setupView()
|
53
|
+
}
|
54
|
+
|
55
|
+
convenience init() {
|
56
|
+
self.init(frame: CGRect.zero)
|
57
|
+
}
|
58
|
+
|
59
|
+
required init?(coder aDecoder: NSCoder) {
|
60
|
+
fatalError("init(coder:) has not been implemented for {{class_name}}.swift")
|
61
|
+
}
|
62
|
+
|
63
|
+
// MARK: - Setup
|
64
|
+
|
65
|
+
private func setupView() {
|
66
|
+
addViewComponents()
|
67
|
+
setupConstraints()
|
68
|
+
}
|
69
|
+
|
70
|
+
private func addViewComponents() {
|
71
|
+
{{#properties.ui_label}}
|
72
|
+
addSubview({{name}})
|
73
|
+
{{/properties.ui_label}}
|
74
|
+
{{#properties.ui_view}}
|
75
|
+
addSubview({{name}})
|
76
|
+
{{/properties.ui_view}}
|
77
|
+
{{#properties.unknow_type}}
|
78
|
+
addSubview({{name}})
|
79
|
+
{{/properties.unknow_type}}
|
80
|
+
}
|
81
|
+
|
82
|
+
// MARK: - Constraints
|
83
|
+
|
84
|
+
private func setupConstraints() {
|
85
|
+
{{#properties.ui_label}}
|
86
|
+
setupConstraintsFor{{capitalized_name}}()
|
87
|
+
{{/properties.ui_label}}
|
88
|
+
{{#properties.ui_view}}
|
89
|
+
setupConstraintsFor{{capitalized_name}}()
|
90
|
+
{{/properties.ui_view}}
|
91
|
+
{{#properties.unknow_type}}
|
92
|
+
setupConstraintsFor{{capitalized_name}}()
|
93
|
+
{{/properties.unknow_type}}
|
94
|
+
}
|
95
|
+
|
96
|
+
{{#properties.ui_label}}
|
97
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
98
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
99
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
100
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
101
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
102
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
103
|
+
}
|
104
|
+
|
105
|
+
{{/properties.ui_label}}
|
106
|
+
{{#properties.ui_view}}
|
107
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
108
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
109
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
110
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
111
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
112
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
113
|
+
}
|
114
|
+
|
115
|
+
{{/properties.ui_view}}
|
116
|
+
{{#properties.unknow_type}}
|
117
|
+
private func setupConstraintsFor{{capitalized_name}}() {
|
118
|
+
{{name}}.translatesAutoresizingMaskIntoConstraints = false
|
119
|
+
{{name}}.topAnchor.constraint(equalTo: <# topAnchor #>, constant: <# constant #>).isActive = true
|
120
|
+
{{name}}.leftAnchor.constraint(equalTo: <# leftAnchor #>, constant: <# constant #>).isActive = true
|
121
|
+
{{name}}.rightAnchor.constraint(equalTo: <# rightAnchor #>, constant: <# constant #>).isActive = true
|
122
|
+
{{name}}.bottomAnchor.constraint(equalTo: <# bottomAnchor #>, constant: <# constant #>).isActive = true
|
123
|
+
}
|
124
|
+
|
125
|
+
{{/properties.unknow_type}}
|
126
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Swift
|
2
|
+
module Boiler
|
3
|
+
class Token
|
4
|
+
attr_accessor :type, :content
|
5
|
+
|
6
|
+
TYPE = [ OPTION = :option_type, PROPERTY = :property_type, TEMPLATE_NAME = :template_name_type, TEMPLATE_PATH = :template_path_type, CLASS_NAME = :class_name_type ]
|
7
|
+
|
8
|
+
def initialize (type, content)
|
9
|
+
@type = type
|
10
|
+
@content = content
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'swift/boiler/token'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
module Boiler
|
5
|
+
class TokenFactory
|
6
|
+
|
7
|
+
def create_tokens_from_arguments(arguments)
|
8
|
+
tokens = Array.new
|
9
|
+
|
10
|
+
arguments.each do |argument|
|
11
|
+
|
12
|
+
if is_valid_option(argument)
|
13
|
+
tokens << Token.new(Token::OPTION, argument)
|
14
|
+
|
15
|
+
elsif is_valid_property(argument)
|
16
|
+
tokens << Token.new(Token::PROPERTY, argument)
|
17
|
+
|
18
|
+
elsif is_valid_template_name(argument)
|
19
|
+
tokens << Token.new(Token::TEMPLATE_NAME, argument)
|
20
|
+
|
21
|
+
elsif is_valid_template_path(argument)
|
22
|
+
tokens << Token.new(Token::TEMPLATE_PATH, argument)
|
23
|
+
|
24
|
+
elsif is_valid_class_name(argument)
|
25
|
+
tokens << Token.new(Token::CLASS_NAME, argument)
|
26
|
+
|
27
|
+
else
|
28
|
+
raise ArgumentError.new("Argument was not recognized: #{argument}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
tokens
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_valid_option(argument)
|
36
|
+
argument =~ /\A--[a-z]{3,}\z|\A-[a-z]\z/
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_valid_property(argument)
|
40
|
+
argument =~ /\A[a-zA-Z_][a-zA-Z_\d\-]*:[a-zA-Z][a-zA-Z_.\d\-]{2,}\z/
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_valid_template_name(argument)
|
44
|
+
["controller", "c", "model", "m" "singleton", "s", "table_view_cell", "tableviewcell", "tvc", "view", "v", "viewmodel"].include?(argument)
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_valid_class_name(argument)
|
48
|
+
argument =~ /\A[a-zA-Z][a-zA-Z\d_]+\z/
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_valid_template_path(argument)
|
52
|
+
argument =~ /\A[a-zA-Z\-.\/\\~\s_:]*[\/\\][\w\-\s]+.mustache\z|\A[\w\-\s]+.mustache\z/
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'swift/boiler/token'
|
2
|
+
|
3
|
+
module Swift
|
4
|
+
module Boiler
|
5
|
+
class TokenPatternValidator
|
6
|
+
|
7
|
+
def validate(tokens)
|
8
|
+
tokens.count > 0 && is_token_pattern_valid(tokens)
|
9
|
+
end
|
10
|
+
|
11
|
+
def is_token_pattern_valid(tokens)
|
12
|
+
is_help_pattern(tokens) || is_template_path_pattern(tokens) || is_template_name_pattern(tokens)
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_template_path_pattern(tokens)
|
16
|
+
has_one_template_name = contains_unique_instace_of_type(tokens, Token::TEMPLATE_NAME)
|
17
|
+
has_one_template_path = contains_unique_instace_of_type(tokens, Token::TEMPLATE_PATH)
|
18
|
+
has_one_class_name = contains_unique_instace_of_type(tokens, Token::CLASS_NAME)
|
19
|
+
!has_one_template_name && has_one_template_path && has_one_class_name && is_first_token_template_option(tokens)
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_template_name_pattern(tokens)
|
23
|
+
has_one_template_name = contains_unique_instace_of_type(tokens, Token::TEMPLATE_NAME)
|
24
|
+
has_one_template_path = contains_unique_instace_of_type(tokens, Token::TEMPLATE_PATH)
|
25
|
+
has_one_class_name = contains_unique_instace_of_type(tokens, Token::CLASS_NAME)
|
26
|
+
!has_one_template_path && has_one_class_name && has_one_template_name && is_first_token_template_name_type(tokens)
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_help_pattern(tokens)
|
30
|
+
tokens.count == 1 && is_first_token_help_option(tokens)
|
31
|
+
end
|
32
|
+
|
33
|
+
def contains_unique_instace_of_type(tokens, type)
|
34
|
+
instance_counter = 0
|
35
|
+
tokens.each do |token|
|
36
|
+
if token.type == type
|
37
|
+
instance_counter += 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
instance_counter == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_first_token_template_name_type(tokens)
|
44
|
+
tokens.count > 0 && is_template_name_token(tokens[0])
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_first_token_help_option(tokens)
|
48
|
+
tokens.count > 0 && is_help_token(tokens[0])
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_first_token_help_option(tokens)
|
52
|
+
tokens.count > 0 && is_help_token(tokens[0])
|
53
|
+
end
|
54
|
+
|
55
|
+
def is_first_token_template_option(tokens)
|
56
|
+
tokens.count > 0 && is_template_token(tokens[0])
|
57
|
+
end
|
58
|
+
|
59
|
+
def is_template_name_token(token)
|
60
|
+
token.type == Token::TEMPLATE_NAME
|
61
|
+
end
|
62
|
+
|
63
|
+
def is_help_token(token)
|
64
|
+
token.type == Token::OPTION && (token.content == '-h' || token.content == '--help')
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_template_token(token)
|
68
|
+
token.type == Token::OPTION && (token.content == '-t' || token.content == '--template')
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/swift/boiler.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'swift/boiler/builder'
|
2
|
+
require 'swift/boiler/scanner'
|
3
|
+
require 'swift/boiler/parser'
|
4
|
+
require 'swift/boiler/template'
|
5
|
+
require 'swift/boiler/token'
|
6
|
+
|
7
|
+
module Swift
|
8
|
+
module Boiler
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def boil(arguments)
|
12
|
+
begin
|
13
|
+
template = build_template_from_arguments(arguments)
|
14
|
+
create_file_from_template(template)
|
15
|
+
rescue ArgumentError => argumentError
|
16
|
+
print "swift-boiler: #{argumentError.message}. Please see 'swift-boil --help'."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_file_from_template(template)
|
23
|
+
builder = Swift::Boiler::Builder.new
|
24
|
+
builder.build_template(template)
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_template_from_arguments(arguments)
|
28
|
+
scanner = Swift::Boiler::Scanner.new
|
29
|
+
parser = Swift::Boiler::Parser.new
|
30
|
+
tokens = scanner.create_valid_token_pattern_from_arguments(arguments)
|
31
|
+
template = parser.create_template_from_tokens(tokens)
|
32
|
+
template
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swift-boiler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pedro Peres
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
description: This gem generate swift boilter plate code
|
56
|
+
email:
|
57
|
+
- pedro.peres@weightwatchers.com
|
58
|
+
executables:
|
59
|
+
- swift-boil
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- bin/console
|
64
|
+
- bin/setup
|
65
|
+
- bin/swift-boil
|
66
|
+
- lib/swift/boiler/builder.rb
|
67
|
+
- lib/swift/boiler/parser.rb
|
68
|
+
- lib/swift/boiler/scanner.rb
|
69
|
+
- lib/swift/boiler/template.rb
|
70
|
+
- lib/swift/boiler/templates/controller.mustache
|
71
|
+
- lib/swift/boiler/templates/model.mustache
|
72
|
+
- lib/swift/boiler/templates/singleton.mustache
|
73
|
+
- lib/swift/boiler/templates/table_view_cell.mustache
|
74
|
+
- lib/swift/boiler/templates/view.mustache
|
75
|
+
- lib/swift/boiler/templates/viewmodel.mustache
|
76
|
+
- lib/swift/boiler/token.rb
|
77
|
+
- lib/swift/boiler/token_factory.rb
|
78
|
+
- lib/swift/boiler/token_pattern_validator.rb
|
79
|
+
- lib/swift/boiler/version.rb
|
80
|
+
- lib/swift/boiler.rb
|
81
|
+
- README.md
|
82
|
+
homepage: https://github.com/Boilerplate-Factory/swift-boiler
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata:
|
86
|
+
allowed_push_host: Set to 'http://mygemserver.com'
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 2.0.14.1
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: This gem generate swift boilter plate code
|
107
|
+
test_files: []
|