gris 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +67 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +203 -0
- data/Guardfile +82 -0
- data/MIT-LICENSE +20 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/bin/gris +5 -0
- data/gris.gemspec +40 -0
- data/lib/gris.rb +37 -0
- data/lib/gris/cli.rb +71 -0
- data/lib/gris/deprecations.rb +9 -0
- data/lib/gris/deprecations/gris_setup.rb +14 -0
- data/lib/gris/generators.rb +3 -0
- data/lib/gris/generators/api_generator.rb +31 -0
- data/lib/gris/generators/migration_generator.rb +228 -0
- data/lib/gris/generators/scaffold_generator.rb +21 -0
- data/lib/gris/generators/templates/api/app/apis/%name_tableize%_endpoint.rb.tt +65 -0
- data/lib/gris/generators/templates/api/app/models/%name_underscore%.rb.tt +2 -0
- data/lib/gris/generators/templates/api/app/presenters/%name_tableize%_presenter.rb.tt +5 -0
- data/lib/gris/generators/templates/api/app/presenters/%name_underscore%_presenter.rb.tt +10 -0
- data/lib/gris/generators/templates/api/spec/apis/%name_tableize%_endpoint_spec.rb.tt +70 -0
- data/lib/gris/generators/templates/api/spec/fabricators/%name_tableize%_fabricator.rb.tt +2 -0
- data/lib/gris/generators/templates/api/spec/models/%name_underscore%_spec.rb.tt +7 -0
- data/lib/gris/generators/templates/create_table_migration/%migration_filename%.rb.tt +19 -0
- data/lib/gris/generators/templates/migration/%migration_filename%.rb.tt +39 -0
- data/lib/gris/generators/templates/scaffold/.Procfile.tt +1 -0
- data/lib/gris/generators/templates/scaffold/.env.test.tt +11 -0
- data/lib/gris/generators/templates/scaffold/.env.tt +10 -0
- data/lib/gris/generators/templates/scaffold/.gitignore.tt +11 -0
- data/lib/gris/generators/templates/scaffold/.rspec.tt +3 -0
- data/lib/gris/generators/templates/scaffold/.rubocop.yml +18 -0
- data/lib/gris/generators/templates/scaffold/.ruby-gemset.tt +1 -0
- data/lib/gris/generators/templates/scaffold/.ruby-version.tt +1 -0
- data/lib/gris/generators/templates/scaffold/Gemfile.tt +33 -0
- data/lib/gris/generators/templates/scaffold/README.md.tt +3 -0
- data/lib/gris/generators/templates/scaffold/Rakefile +9 -0
- data/lib/gris/generators/templates/scaffold/app.rb +57 -0
- data/lib/gris/generators/templates/scaffold/app/apis/application_endpoint.rb +9 -0
- data/lib/gris/generators/templates/scaffold/app/presenters/root_presenter.rb +20 -0
- data/lib/gris/generators/templates/scaffold/config.ru.tt +6 -0
- data/lib/gris/generators/templates/scaffold/config/database.yml.tt +19 -0
- data/lib/gris/generators/templates/scaffold/config/initializers/active_record.rb +4 -0
- data/lib/gris/generators/templates/scaffold/db/schema.rb +11 -0
- data/lib/gris/generators/templates/scaffold/public/errors/404.html +11 -0
- data/lib/gris/generators/templates/scaffold/public/errors/500.html +11 -0
- data/lib/gris/generators/templates/scaffold/spec/apis/cors_spec.rb +31 -0
- data/lib/gris/generators/templates/scaffold/spec/spec_helper.rb +26 -0
- data/lib/gris/generators/templates/scaffold/spec/support/app_helper.rb +3 -0
- data/lib/gris/grape_extensions/crud_helpers.rb +26 -0
- data/lib/gris/identity.rb +50 -0
- data/lib/gris/middleware/app_monitor.rb +17 -0
- data/lib/gris/output_formatters/paginated_presenter.rb +41 -0
- data/lib/gris/output_formatters/presenter.rb +15 -0
- data/lib/gris/rspec_extensions/response_helpers.rb +57 -0
- data/lib/gris/setup.rb +17 -0
- data/lib/gris/version.rb +38 -0
- data/lib/tasks/db.rake +81 -0
- data/lib/tasks/routes.rake +11 -0
- data/spec/generators/api_generator_spec.rb +76 -0
- data/spec/generators/migration_generator_spec.rb +96 -0
- data/spec/generators/scaffold_generator_spec.rb +119 -0
- data/spec/grape_extensions/crud_helpers_spec.rb +5 -0
- data/spec/identity_spec.rb +86 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/spec_generators_helper.rb +11 -0
- data/spec/version_spec.rb +40 -0
- metadata +426 -0
data/gris.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/gris/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'gris'
|
6
|
+
s.version = Gris::VERSION
|
7
|
+
s.summary = 'A simple api microservice generator.'
|
8
|
+
s.description = 'Gris is a generator for Grape, Roar, Hypermedia, PG API apps.'
|
9
|
+
s.email = 'email@dylanfareed.com'
|
10
|
+
s.homepage = 'http://github.com/dylanfareed/gris/'
|
11
|
+
s.authors = ['Dylan Fareed']
|
12
|
+
s.licenses = 'MIT-LICENSE'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
15
|
+
s.executables << 'gris'
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.name = 'gris'
|
18
|
+
s.require_paths = ['lib']
|
19
|
+
s.required_ruby_version = '>= 2.1.5'
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'thor', '~> 0.19', '>= 0.19.1'
|
22
|
+
s.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.0'
|
23
|
+
s.add_runtime_dependency 'rake', '~> 10.4', '>= 10.4.2'
|
24
|
+
s.add_runtime_dependency 'git', '~> 1.2', '>= 1.2.8'
|
25
|
+
|
26
|
+
s.add_dependency 'logging'
|
27
|
+
s.add_dependency 'dotenv', '~> 1.0.2', '>= 1.0.2'
|
28
|
+
s.add_dependency 'grape', '~> 0.10.1', '>= 0.10.1'
|
29
|
+
s.add_dependency 'grape-roar', '~> 0.3.0', '>= 0.3.0'
|
30
|
+
|
31
|
+
s.add_dependency 'grape-swagger', '~> 0.9.0', '>= 0.9.0'
|
32
|
+
s.add_dependency 'roar', '~> 1.0.0', '>= 1.0.0'
|
33
|
+
s.add_dependency 'racksh', '~> 1.0'
|
34
|
+
|
35
|
+
s.add_development_dependency 'bundler', '~> 1.7.10', '>= 1.7.10'
|
36
|
+
s.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
|
37
|
+
s.add_development_dependency 'rubocop', '~> 0.28', '>= 0.28.0'
|
38
|
+
s.add_development_dependency 'activerecord', '~> 4.2', '>= 4.2.0'
|
39
|
+
s.add_development_dependency 'acts_as_fu', '~> 0'
|
40
|
+
end
|
data/lib/gris.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# require external libraries
|
2
|
+
require 'rake'
|
3
|
+
require 'dotenv'
|
4
|
+
require 'yaml'
|
5
|
+
require 'grape'
|
6
|
+
require 'json'
|
7
|
+
require 'roar'
|
8
|
+
require 'grape-roar'
|
9
|
+
require 'roar/representer'
|
10
|
+
require 'roar/json'
|
11
|
+
require 'roar/json/hal'
|
12
|
+
|
13
|
+
# require internal files
|
14
|
+
require 'gris/deprecations'
|
15
|
+
require 'gris/grape_extensions/crud_helpers'
|
16
|
+
require 'gris/identity'
|
17
|
+
require 'gris/middleware/app_monitor'
|
18
|
+
require 'gris/output_formatters/paginated_presenter'
|
19
|
+
require 'gris/output_formatters/presenter'
|
20
|
+
require 'gris/setup'
|
21
|
+
require 'gris/version'
|
22
|
+
|
23
|
+
# load rake tasks if Rake installed
|
24
|
+
if defined?(Rake)
|
25
|
+
load 'tasks/routes.rake'
|
26
|
+
load 'tasks/db.rake'
|
27
|
+
end
|
28
|
+
|
29
|
+
module Gris
|
30
|
+
class << self
|
31
|
+
def initialize
|
32
|
+
Gris::Deprecations.initialization_checks
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Gris.initialize
|
data/lib/gris/cli.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'gris/generators'
|
3
|
+
require 'gris/version'
|
4
|
+
|
5
|
+
Gris.load_environment if defined?(Dotenv)
|
6
|
+
|
7
|
+
module Gris
|
8
|
+
class CLI
|
9
|
+
class Generate < Thor
|
10
|
+
register(
|
11
|
+
Generators::ApiGenerator,
|
12
|
+
'api',
|
13
|
+
'api <api_name>',
|
14
|
+
'Create a Grape API, Model and Presenter'
|
15
|
+
)
|
16
|
+
|
17
|
+
register(
|
18
|
+
Generators::MigrationGenerator,
|
19
|
+
'migration',
|
20
|
+
'migration <migration_name> [field[:type][:index] field[:type][:index]]',
|
21
|
+
'Create a Database Migration'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
class CLI
|
26
|
+
class Base < Thor
|
27
|
+
desc 'version', 'Returns the Gris version number'
|
28
|
+
def version
|
29
|
+
say Gris::VERSION
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'console [environment]', 'Start the Gris console'
|
33
|
+
options aliases: 'c'
|
34
|
+
def console(environment = nil)
|
35
|
+
ENV['RACK_ENV'] = environment || 'development'
|
36
|
+
|
37
|
+
require 'racksh/init'
|
38
|
+
|
39
|
+
begin
|
40
|
+
require 'pry'
|
41
|
+
interpreter = Pry
|
42
|
+
rescue LoadError
|
43
|
+
require 'irb'
|
44
|
+
require 'irb/completion'
|
45
|
+
interpreter = IRB
|
46
|
+
# IRB uses ARGV and does not expect these arguments.
|
47
|
+
ARGV.delete('console')
|
48
|
+
ARGV.delete(environment) if environment
|
49
|
+
end
|
50
|
+
|
51
|
+
Rack::Shell.init
|
52
|
+
|
53
|
+
$0 = "#{$PROGRAM_NAME} console"
|
54
|
+
interpreter.start
|
55
|
+
end
|
56
|
+
|
57
|
+
register(
|
58
|
+
Generators::ScaffoldGenerator,
|
59
|
+
'new',
|
60
|
+
'new <app_name> [app_path]',
|
61
|
+
'Creates a scaffold for a new Gris service'
|
62
|
+
)
|
63
|
+
|
64
|
+
desc 'generate api <api_name>', 'Create a Grape API, Model and Representer'
|
65
|
+
subcommand 'generate api', Gris::CLI::Generate
|
66
|
+
|
67
|
+
desc 'generate migration <migration_name> [field[:type][:index] field[:type][:index]]', 'Create a Database Migration'
|
68
|
+
subcommand 'generate', Gris::CLI::Generate
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Gris
|
2
|
+
class Deprecations
|
3
|
+
# TODO: Called when Gris appliation loads.
|
4
|
+
#
|
5
|
+
# Use this method to check that embedding applications
|
6
|
+
# follow current established best practices.
|
7
|
+
# cf. https://github.com/bellycard/napa/blob/master/lib/napa/deprecations.rb
|
8
|
+
# https://github.com/bellycard/napa/blob/master/lib/napa/deprecations/napa_setup.rb
|
9
|
+
# https://github.com/bellycard/napa/blob/master/lib/napa/deprecations/application_api.rb
|
10
|
+
#
|
11
|
+
def self.gris_setup_check
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
module Gris
|
5
|
+
module Generators
|
6
|
+
class ApiGenerator < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
argument :name
|
9
|
+
|
10
|
+
def name_underscore
|
11
|
+
name.underscore
|
12
|
+
end
|
13
|
+
|
14
|
+
def name_tableize
|
15
|
+
name.tableize
|
16
|
+
end
|
17
|
+
|
18
|
+
def output_directory
|
19
|
+
'.'
|
20
|
+
end
|
21
|
+
|
22
|
+
def api
|
23
|
+
self.class.source_root "#{File.dirname(__FILE__)}/templates/api"
|
24
|
+
say 'Generating api...'
|
25
|
+
directory '.', output_directory
|
26
|
+
say 'API files created!', :green
|
27
|
+
say 'Note that you will need to mount this new endpoint in your ApplicationEndpoint.', :green
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
module Gris
|
5
|
+
module Generators
|
6
|
+
class MigrationGenerator < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
# largely ported over, with a few differences, from
|
9
|
+
# https://github.com/rails/rails/blob/76883f92374c6395f13c16628e1d87d40b6d2399/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
|
10
|
+
argument :migration_name
|
11
|
+
argument :attributes, type: :array, default: []
|
12
|
+
|
13
|
+
attr_reader :migration_action, :join_tables, :table_name
|
14
|
+
|
15
|
+
def version
|
16
|
+
Time.now.utc.strftime('%Y%m%d%H%M%S')
|
17
|
+
end
|
18
|
+
|
19
|
+
def migration_filename
|
20
|
+
"#{version}_#{migration_name.underscore}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def output_directory
|
24
|
+
'./db/migrate'
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_attributes!
|
28
|
+
self.attributes = (attributes || []).map do |attr|
|
29
|
+
GeneratedAttribute.parse(attr)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_local_assigns!
|
34
|
+
@migration_template = 'migration'
|
35
|
+
filename = migration_name.underscore
|
36
|
+
case filename
|
37
|
+
when /^(add|remove)_.*_(?:to|from)_(.*)/
|
38
|
+
@migration_action = Regexp.last_match[1]
|
39
|
+
@table_name = Regexp.last_match[2].pluralize
|
40
|
+
when /join_table/
|
41
|
+
if attributes.length == 2
|
42
|
+
@migration_action = 'join'
|
43
|
+
@join_tables = attributes.map(&:plural_name)
|
44
|
+
|
45
|
+
set_index_names
|
46
|
+
end
|
47
|
+
when /^create_(.+)/
|
48
|
+
@table_name = Regexp.last_match[1].pluralize
|
49
|
+
@migration_template = 'create_table_migration'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def migration
|
54
|
+
self.class.source_root "#{File.dirname(__FILE__)}/templates/#{@migration_template}"
|
55
|
+
say 'Generating migration...'
|
56
|
+
directory '.', output_directory
|
57
|
+
say 'Done!', :green
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def attributes_with_index
|
63
|
+
attributes.select { |a| !a.reference? && a.has_index? }
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_index_names
|
67
|
+
attributes.each_with_index do |attr, i|
|
68
|
+
attr.index_name = [attr, attributes[i - 1]].map { |a| index_name_for(a) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def index_name_for(attribute)
|
73
|
+
if attribute.foreign_key?
|
74
|
+
attribute.name
|
75
|
+
else
|
76
|
+
attribute.name.singularize.foreign_key
|
77
|
+
end.to_sym
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# directly ported from
|
82
|
+
# https://github.com/rails/rails/blob/76883f92374c6395f13c16628e1d87d40b6d2399/railties/lib/rails/generators/generated_attribute.rb
|
83
|
+
class GeneratedAttribute # :nodoc:
|
84
|
+
INDEX_OPTIONS = %w(index uniq)
|
85
|
+
UNIQ_INDEX_OPTIONS = %w(uniq)
|
86
|
+
|
87
|
+
attr_accessor :name, :type
|
88
|
+
attr_reader :attr_options
|
89
|
+
attr_writer :index_name
|
90
|
+
|
91
|
+
class << self
|
92
|
+
def parse(column_definition)
|
93
|
+
name, type, has_index = column_definition.split(':')
|
94
|
+
|
95
|
+
# if user provided "name:index" instead of "name:string:index"
|
96
|
+
# type should be set blank so GeneratedAttribute's constructor
|
97
|
+
# could set it to :string
|
98
|
+
has_index, type = type, nil if INDEX_OPTIONS.include?(type)
|
99
|
+
|
100
|
+
type, attr_options = *parse_type_and_options(type)
|
101
|
+
type = type.to_sym if type
|
102
|
+
|
103
|
+
if type && reference?(type)
|
104
|
+
references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true
|
105
|
+
attr_options[:index] = references_index
|
106
|
+
end
|
107
|
+
|
108
|
+
new(name, type, has_index, attr_options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def reference?(type)
|
112
|
+
[:references, :belongs_to].include? type
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
# parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
|
118
|
+
# when declaring options curly brackets should be used
|
119
|
+
def parse_type_and_options(type)
|
120
|
+
case type
|
121
|
+
when /(string|text|binary|integer)\{(\d+)\}/
|
122
|
+
return Regexp.last_match[1], limit: Regexp.last_match[2].to_i
|
123
|
+
when /decimal\{(\d+)[,.-](\d+)\}/
|
124
|
+
return :decimal, precision: Regexp.last_match[1].to_i, scale: Regexp.last_match[2].to_i
|
125
|
+
when /(references|belongs_to)\{polymorphic\}/
|
126
|
+
return Regexp.last_match[1], polymorphic: true
|
127
|
+
else
|
128
|
+
return type, {}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def initialize(name, type = nil, index_type = false, attr_options = {})
|
134
|
+
@name = name
|
135
|
+
@type = type || :string
|
136
|
+
@has_index = INDEX_OPTIONS.include?(index_type)
|
137
|
+
@has_uniq_index = UNIQ_INDEX_OPTIONS.include?(index_type)
|
138
|
+
@attr_options = attr_options
|
139
|
+
end
|
140
|
+
|
141
|
+
def field_type
|
142
|
+
@field_type ||= case type
|
143
|
+
when :integer then :number_field
|
144
|
+
when :float, :decimal then :text_field
|
145
|
+
when :time then :time_select
|
146
|
+
when :datetime, :timestamp then :datetime_select
|
147
|
+
when :date then :date_select
|
148
|
+
when :text then :text_area
|
149
|
+
when :boolean then :check_box
|
150
|
+
else
|
151
|
+
:text_field
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def default
|
156
|
+
@default ||= case type
|
157
|
+
when :integer then 1
|
158
|
+
when :float then 1.5
|
159
|
+
when :decimal then '9.99'
|
160
|
+
when :datetime, :timestamp, :time then Time.now.to_s(:db)
|
161
|
+
when :date then Date.today.to_s(:db)
|
162
|
+
when :string then name == 'type' ? '' : 'MyString'
|
163
|
+
when :text then 'MyText'
|
164
|
+
when :boolean then false
|
165
|
+
when :references, :belongs_to then nil
|
166
|
+
else
|
167
|
+
''
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def plural_name
|
172
|
+
name.sub(/_id$/, '').pluralize
|
173
|
+
end
|
174
|
+
|
175
|
+
def singular_name
|
176
|
+
name.sub(/_id$/, '').singularize
|
177
|
+
end
|
178
|
+
|
179
|
+
def human_name
|
180
|
+
name.humanize
|
181
|
+
end
|
182
|
+
|
183
|
+
def index_name
|
184
|
+
@index_name ||= if polymorphic?
|
185
|
+
%w(id type).map { |t| "#{name}_#{t}" }
|
186
|
+
else
|
187
|
+
column_name
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def column_name
|
192
|
+
@column_name ||= reference? ? "#{name}_id" : name
|
193
|
+
end
|
194
|
+
|
195
|
+
def foreign_key?
|
196
|
+
!!(name =~ /_id$/)
|
197
|
+
end
|
198
|
+
|
199
|
+
def reference?
|
200
|
+
self.class.reference?(type)
|
201
|
+
end
|
202
|
+
|
203
|
+
def polymorphic?
|
204
|
+
attr_options.key?(:polymorphic)
|
205
|
+
end
|
206
|
+
|
207
|
+
def has_index?
|
208
|
+
@has_index
|
209
|
+
end
|
210
|
+
|
211
|
+
def has_uniq_index?
|
212
|
+
@has_uniq_index
|
213
|
+
end
|
214
|
+
|
215
|
+
def password_digest?
|
216
|
+
name == 'password' && type == :digest
|
217
|
+
end
|
218
|
+
|
219
|
+
def inject_options
|
220
|
+
''.tap { |s| @attr_options.each { |k, v| s << ", #{k}: #{v.inspect}" } }
|
221
|
+
end
|
222
|
+
|
223
|
+
def inject_index_options
|
224
|
+
has_uniq_index? ? ', unique: true' : ''
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
module Gris
|
5
|
+
module Generators
|
6
|
+
class ScaffoldGenerator < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
source_root "#{File.dirname(__FILE__)}/templates/scaffold"
|
10
|
+
|
11
|
+
argument :app_name
|
12
|
+
argument :app_path, optional: true
|
13
|
+
|
14
|
+
def generate
|
15
|
+
say 'Generating scaffold...'
|
16
|
+
directory '.', (app_path || app_name)
|
17
|
+
say 'Done!', :green
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|