amber_extension_generator 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +185 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.solargraph.yml +20 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +116 -0
- data/LICENSE +21 -0
- data/README.md +37 -0
- data/Rakefile +16 -0
- data/amber_extension_generator.gemspec +46 -0
- data/exe/amber_extension_generator +6 -0
- data/lib/amber_extension_generator/cli/args.rb +47 -0
- data/lib/amber_extension_generator/cli/gem_generator.rb +406 -0
- data/lib/amber_extension_generator/cli.rb +17 -0
- data/lib/amber_extension_generator/gem_name.rb +6 -0
- data/lib/amber_extension_generator/templates/assets/stylesheets/components.scss +2 -0
- data/lib/amber_extension_generator/templates/assets/stylesheets/main.scss.erb +6 -0
- data/lib/amber_extension_generator/templates/bin/dev.erb +10 -0
- data/lib/amber_extension_generator/templates/bin/generate.erb +142 -0
- data/lib/amber_extension_generator/templates/components/base_component.rb.erb +11 -0
- data/lib/amber_extension_generator/templates/components.rb +4 -0
- data/lib/amber_extension_generator/templates/rails_dummy/Gemfile.erb +33 -0
- data/lib/amber_extension_generator/templates/railtie.rb.erb +13 -0
- data/lib/amber_extension_generator/templates/templates/component.rb.tt +11 -0
- data/lib/amber_extension_generator/templates/templates/component_test.rb.tt +17 -0
- data/lib/amber_extension_generator/templates/templates/style.scss.tt +5 -0
- data/lib/amber_extension_generator/templates/templates/view.html.erb.tt +8 -0
- data/lib/amber_extension_generator/templates/test/component_test_case.rb +7 -0
- data/lib/amber_extension_generator/version.rb +6 -0
- data/lib/amber_extension_generator.rb +16 -0
- data/lib/dummy_rails_app_template.rb +61 -0
- metadata +127 -0
@@ -0,0 +1,406 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
require 'rainbow/refinement'
|
6
|
+
require 'cli/ui'
|
7
|
+
require 'tty-command'
|
8
|
+
require 'erb'
|
9
|
+
|
10
|
+
using ::Rainbow
|
11
|
+
|
12
|
+
module ::AmberExtensionGenerator
|
13
|
+
module CLI
|
14
|
+
# Generates a new extension gem
|
15
|
+
class GemGenerator
|
16
|
+
class << self
|
17
|
+
# @param args [::AmberExtensionGenerator::CLI::Args]
|
18
|
+
# @return [void]
|
19
|
+
def call(args)
|
20
|
+
new(args).call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param args [::AmberExtensionGenerator::CLI::Args]
|
25
|
+
def initialize(args)
|
26
|
+
@args = args
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [void]
|
30
|
+
def call
|
31
|
+
::CLI::UI::StdoutRouter.enable
|
32
|
+
generate_amber_gem
|
33
|
+
puts
|
34
|
+
generate_rails_dummy_app
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @return [void]
|
40
|
+
def generate_amber_gem
|
41
|
+
::CLI::UI::Frame.open 'Create gem', color: :green do # rubocop:disable Metrics/BlockLength
|
42
|
+
::CLI::UI::Frame.open 'Generate gem with bundler' do
|
43
|
+
syscall "bundle gem #{root_path} --linter=rubocop --ci=github --test=minitest", input: "y\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
::CLI::UI::Frame.open 'Patch gem' do
|
47
|
+
template 'components/base_component.rb.erb', gem_entry_folder_path / 'components' / 'base_component.rb'
|
48
|
+
|
49
|
+
copy 'components.rb', gem_entry_folder_path / 'components.rb'
|
50
|
+
template 'railtie.rb.erb', gem_entry_folder_path / 'railtie.rb'
|
51
|
+
|
52
|
+
substitute gem_entry_file_path, /^end/, <<~RUBY.chomp
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'pathname'
|
56
|
+
require_relative '#{gem_entry_folder_path.basename}/railtie' if defined?(::Rails::Railtie)
|
57
|
+
require_relative '#{gem_entry_folder_path.basename}/components'
|
58
|
+
|
59
|
+
# Override this if you want to have a different name for the
|
60
|
+
# base component of your gem
|
61
|
+
#{root_module_name}::ABSTRACT_COMPONENT = #{root_module_name}::BaseComponent
|
62
|
+
#{root_module_name}::ROOT_PATH = ::Pathname.new ::File.expand_path('#{relative_path_to_root}', __dir__)
|
63
|
+
RUBY
|
64
|
+
|
65
|
+
create '.rubocop.yml', ::File.read(ROOT_GEM_PATH / '.rubocop.yml')
|
66
|
+
|
67
|
+
template 'bin/generate.erb', 'bin/generate'
|
68
|
+
template 'bin/dev.erb', 'bin/dev'
|
69
|
+
make_executable 'bin/generate'
|
70
|
+
make_executable 'bin/dev'
|
71
|
+
|
72
|
+
make_dir 'assets/stylesheets'
|
73
|
+
template 'assets/stylesheets/main.scss.erb', main_stylesheet_path
|
74
|
+
copy 'assets/stylesheets/components.scss', stylesheet_dir_path / 'components.scss'
|
75
|
+
|
76
|
+
make_dir 'templates'
|
77
|
+
copy 'templates/component.rb.tt'
|
78
|
+
copy 'templates/style.scss.tt'
|
79
|
+
copy 'templates/view.html.erb.tt'
|
80
|
+
copy 'templates/component_test.rb.tt'
|
81
|
+
|
82
|
+
substitute "#{gem_name}.gemspec", /^end/, <<~RUBY.chomp
|
83
|
+
# ignore the dummy Rails app when building the gem
|
84
|
+
spec.files.reject! { _1.match(/^#{rails_dummy_path}/) }
|
85
|
+
spec.add_dependency 'amber_component'
|
86
|
+
spec.add_development_dependency 'thor'
|
87
|
+
spec.add_development_dependency 'sassc'
|
88
|
+
end
|
89
|
+
RUBY
|
90
|
+
|
91
|
+
copy 'test/component_test_case.rb'
|
92
|
+
substitute 'Rakefile', /test_\*\.rb/, '*_test.rb'
|
93
|
+
inner_module_name = gem_name.split('-').last
|
94
|
+
move gem_test_folder_path.parent / "test_#{inner_module_name}.rb",
|
95
|
+
gem_test_folder_path.parent / "#{inner_module_name}_test.rb"
|
96
|
+
|
97
|
+
append 'test/test_helper.rb',
|
98
|
+
"require_relative 'component_test_case'\n"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [void]
|
104
|
+
def generate_rails_dummy_app
|
105
|
+
::CLI::UI::Frame.open 'Rails dummy app', color: :magenta do
|
106
|
+
unless syscall? 'gem list -i rails'
|
107
|
+
::CLI::UI::Frame.open 'Install Rails' do
|
108
|
+
syscall 'gem install rails'
|
109
|
+
syscall 'gem install sqlite3'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
::CLI::UI::Frame.open 'Generate app' do
|
114
|
+
syscall "rails new #{root_path / rails_dummy_path} -m #{rails_template_path}",
|
115
|
+
env: { GEM_NAME: gem_name },
|
116
|
+
input: "y\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
::CLI::UI::Frame.open 'Patch app' do
|
120
|
+
append rails_dummy_path / 'Gemfile', template_content('rails_dummy/Gemfile.erb')
|
121
|
+
|
122
|
+
if exist?(rails_dummy_path / 'app' / 'assets' / 'stylesheets' / 'application.css')
|
123
|
+
move rails_dummy_path / 'app' / 'assets' / 'stylesheets' / 'application.css',
|
124
|
+
rails_dummy_path / 'app' / 'assets' / 'stylesheets' / 'application.scss'
|
125
|
+
|
126
|
+
append rails_dummy_path / 'app' / 'assets' / 'stylesheets' / 'application.scss', <<~SCSS
|
127
|
+
@import "#{gem_name_path}";
|
128
|
+
SCSS
|
129
|
+
else
|
130
|
+
append rails_dummy_path / 'app' / 'assets' / 'stylesheets' / 'application.sass.scss', <<~SCSS
|
131
|
+
@import "#{gem_name_path}";
|
132
|
+
SCSS
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [String]
|
139
|
+
def relative_path_to_root
|
140
|
+
(['..'] * root_module_name.split('::').length).join('/')
|
141
|
+
end
|
142
|
+
|
143
|
+
# Performs a shell command with a PTY,
|
144
|
+
# captures its output and logs it to this process's STDOUT.
|
145
|
+
#
|
146
|
+
# @param command [String]
|
147
|
+
# @param env [Hash{Symbol => String}] Environment variables
|
148
|
+
# @param input [String, nil] Input to the process
|
149
|
+
# @return [String] STDOUT
|
150
|
+
def syscall(command, env: {}, input: nil)
|
151
|
+
cmd = ::TTY::Command.new(color: true, printer: :quiet)
|
152
|
+
cmd.run!(command, pty: true, input: input, env: env)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Performs a quiet shell command (without logging to STDOUT)
|
156
|
+
# and returns the process's exit status as a `Boolean`.
|
157
|
+
#
|
158
|
+
# @param command [String]
|
159
|
+
# @param env [Hash{Symbol => String}] Environment variables
|
160
|
+
# @param input [String, nil] Input to the process
|
161
|
+
# @return [Boolean] whether the command was successful
|
162
|
+
def syscall?(command, env: {}, input: nil)
|
163
|
+
cmd = ::TTY::Command.new(printer: :null)
|
164
|
+
!cmd.run!(command, input: input, env: env).failure?
|
165
|
+
end
|
166
|
+
|
167
|
+
# Make a file in the newly generated gem executable.
|
168
|
+
#
|
169
|
+
# @param path [String, Pathname]
|
170
|
+
# @return [void]
|
171
|
+
def make_executable(path)
|
172
|
+
::FileUtils.chmod 'ugo+x', root_path / path
|
173
|
+
end
|
174
|
+
|
175
|
+
# Relative path to the main entry file of the generated gem.
|
176
|
+
#
|
177
|
+
# @return [Pathname]
|
178
|
+
def gem_entry_file_path
|
179
|
+
gem_entry_folder_path.sub_ext('.rb')
|
180
|
+
end
|
181
|
+
|
182
|
+
# Relative path to the main folder of the generated gem.
|
183
|
+
#
|
184
|
+
# @return [Pathname]
|
185
|
+
def gem_entry_folder_path
|
186
|
+
::Pathname.new('lib') / gem_name_path
|
187
|
+
end
|
188
|
+
|
189
|
+
# Relative path to the test folder of the generated gem.
|
190
|
+
#
|
191
|
+
# @return [Pathname]
|
192
|
+
def gem_test_folder_path
|
193
|
+
::Pathname.new('test') / gem_name_path
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [Pathname]
|
197
|
+
def gem_name_path
|
198
|
+
@gem_name_path ||= ::Pathname.new gem_name.gsub('-', '/')
|
199
|
+
end
|
200
|
+
|
201
|
+
# Name of the generated gem.
|
202
|
+
#
|
203
|
+
# @return [String]
|
204
|
+
def gem_name
|
205
|
+
@gem_name ||= root_path.basename.to_s
|
206
|
+
end
|
207
|
+
|
208
|
+
# Relative path to the stylesheet directory of the generated gem.
|
209
|
+
#
|
210
|
+
# @return [Pathname]
|
211
|
+
def stylesheet_dir_path
|
212
|
+
::Pathname.new('assets') / 'stylesheets' / gem_name_path
|
213
|
+
end
|
214
|
+
|
215
|
+
# Relative path to the main stylesheet file of the generated gem.
|
216
|
+
#
|
217
|
+
# @return [Pathname]
|
218
|
+
def main_stylesheet_path
|
219
|
+
stylesheet_dir_path.sub_ext '.scss'
|
220
|
+
end
|
221
|
+
|
222
|
+
# @return [Pathname]
|
223
|
+
def rails_dummy_path
|
224
|
+
::Pathname.new 'dummy_app'
|
225
|
+
end
|
226
|
+
|
227
|
+
# Path to the root folder of the generated gem.
|
228
|
+
#
|
229
|
+
# @return [Pathname]
|
230
|
+
def root_path
|
231
|
+
@args.gem_path
|
232
|
+
end
|
233
|
+
|
234
|
+
# @return [Pathname]
|
235
|
+
def rails_template_path
|
236
|
+
ROOT_GEM_PATH / 'lib' / 'dummy_rails_app_template.rb'
|
237
|
+
end
|
238
|
+
|
239
|
+
# Check whether the given file/directory exists
|
240
|
+
# in the generated gem.
|
241
|
+
#
|
242
|
+
# @param path [String, Pathname]
|
243
|
+
# @return [Boolean]
|
244
|
+
def exist?(path)
|
245
|
+
(root_path / path).exist?
|
246
|
+
end
|
247
|
+
|
248
|
+
# Create a directory in the generated gem
|
249
|
+
# if it doesn't exist already.
|
250
|
+
#
|
251
|
+
# @param path [String, Pathname]
|
252
|
+
# @return [void]
|
253
|
+
def make_dir(path)
|
254
|
+
dir_path = root_path / path
|
255
|
+
::FileUtils.mkdir_p dir_path unless dir_path.exist?
|
256
|
+
end
|
257
|
+
alias mkdir make_dir
|
258
|
+
|
259
|
+
# @param string [String, Symbol]
|
260
|
+
# @return [String]
|
261
|
+
def action_message(string)
|
262
|
+
"#{string.to_s.rjust(12, ' ')} "
|
263
|
+
end
|
264
|
+
|
265
|
+
# Parse a template file from this gem using ERB and
|
266
|
+
# and copy it to the newly generated gem.
|
267
|
+
#
|
268
|
+
# @param template_path [String, Pathname]
|
269
|
+
# @param target_path [String, Pathname]
|
270
|
+
# @return [void]
|
271
|
+
def template(template_path, target_path)
|
272
|
+
create target_path, template_content(template_path)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Copy a file from this gem's template folder to
|
276
|
+
# the newly generated gem.
|
277
|
+
#
|
278
|
+
# @param source_path [String, Pathname]
|
279
|
+
# @param target_path [String, Pathname]
|
280
|
+
# @return [void]
|
281
|
+
def copy(source_path, target_path = source_path, recursive: false)
|
282
|
+
print action_message(__method__).green
|
283
|
+
puts target_path
|
284
|
+
|
285
|
+
source = TEMPLATES_FOLDER_PATH / source_path
|
286
|
+
target = root_path / target_path
|
287
|
+
::FileUtils.mkdir_p(target.dirname) unless target.dirname.directory?
|
288
|
+
return ::FileUtils.cp_r source, target if recursive
|
289
|
+
|
290
|
+
::FileUtils.cp source, target
|
291
|
+
end
|
292
|
+
|
293
|
+
# Move a file inside the generated gem.
|
294
|
+
#
|
295
|
+
# @param source_path [String, Pathname]
|
296
|
+
# @param target_path [String, Pathname]
|
297
|
+
# @return [void]
|
298
|
+
def move(source_path, target_path)
|
299
|
+
print action_message(__method__).yellow
|
300
|
+
puts "#{source_path} -> #{target_path}"
|
301
|
+
|
302
|
+
source = root_path / source_path
|
303
|
+
target = root_path / target_path
|
304
|
+
::FileUtils.move source, target
|
305
|
+
end
|
306
|
+
|
307
|
+
# Read and parse a template with ERB and
|
308
|
+
# return the result as a `String`.
|
309
|
+
#
|
310
|
+
# @param path [String, Pathname]
|
311
|
+
# @return [String] Parsed content of the template
|
312
|
+
def template_content(path)
|
313
|
+
template_path = TEMPLATES_FOLDER_PATH / path
|
314
|
+
::ERB.new(template_path.read).result(binding)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Create a new file with the specified content
|
318
|
+
# in the newly generated gem.
|
319
|
+
#
|
320
|
+
# @param file_path [String, Pathname]
|
321
|
+
# @param content [String]
|
322
|
+
def create(file_path, content)
|
323
|
+
print action_message(__method__).green
|
324
|
+
puts file_path
|
325
|
+
|
326
|
+
path = root_path / file_path
|
327
|
+
::FileUtils.mkdir_p(path.dirname) unless path.dirname.directory?
|
328
|
+
|
329
|
+
path.write(content)
|
330
|
+
end
|
331
|
+
|
332
|
+
# Substitute a part of a certain file in the generated gem.
|
333
|
+
#
|
334
|
+
# @param file_path [String, Pathname]
|
335
|
+
# @param regexp [Regexp]
|
336
|
+
# @param replacement [String]
|
337
|
+
# @return [void]
|
338
|
+
def substitute(file_path, regexp, replacement)
|
339
|
+
print action_message(:gsub).yellow
|
340
|
+
puts file_path
|
341
|
+
|
342
|
+
path = root_path / file_path
|
343
|
+
file_content = path.read
|
344
|
+
raise "Cannot substitute #{path} because #{regexp.inspect} was not found" unless file_content.match?(regexp)
|
345
|
+
|
346
|
+
path.write file_content.sub(regexp, replacement)
|
347
|
+
end
|
348
|
+
|
349
|
+
# Prepend some content to a file in the generated gem.
|
350
|
+
#
|
351
|
+
# @param file_path [String, Pathname]
|
352
|
+
# @param content [String]
|
353
|
+
# @return [void]
|
354
|
+
def prepend(file_path, content)
|
355
|
+
print action_message(__method__).yellow
|
356
|
+
puts file_path
|
357
|
+
|
358
|
+
# @type [Pathname]
|
359
|
+
path = root_path / file_path
|
360
|
+
current_content = ::File.read path
|
361
|
+
::File.open(path, 'w') do |f|
|
362
|
+
f.write(content)
|
363
|
+
f.write(current_content)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Append some content to a file in the generated gem.
|
368
|
+
#
|
369
|
+
# @param file_path [String, Pathname]
|
370
|
+
# @param content[String]
|
371
|
+
# @return [void]
|
372
|
+
def append(file_path, content)
|
373
|
+
print action_message(__method__).yellow
|
374
|
+
puts file_path
|
375
|
+
|
376
|
+
# @type [Pathname]
|
377
|
+
path = root_path / file_path
|
378
|
+
::File.open(path, 'a') { _1.write(content) }
|
379
|
+
end
|
380
|
+
|
381
|
+
# Name of the root module of the generated gem.
|
382
|
+
#
|
383
|
+
# @return [String]
|
384
|
+
def root_module_name
|
385
|
+
camelize(gem_name_path)
|
386
|
+
end
|
387
|
+
|
388
|
+
# @param string [String]
|
389
|
+
# @param uppercase_first_letter [Boolean]
|
390
|
+
# @return [String]
|
391
|
+
def camelize(string, uppercase_first_letter: true)
|
392
|
+
string = string.to_s
|
393
|
+
string = if uppercase_first_letter
|
394
|
+
string.sub(/^[a-z\d]*/, &:capitalize)
|
395
|
+
else
|
396
|
+
string.sub(/^(?:(?=\b|[A-Z_])|\w)/, &:downcase)
|
397
|
+
end
|
398
|
+
|
399
|
+
string.gsub!(%r{(?:_|(/))([a-z\d]*)}) do
|
400
|
+
"#{::Regexp.last_match(1)}#{::Regexp.last_match(2).capitalize}"
|
401
|
+
end
|
402
|
+
string.gsub('/', '::')
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module ::AmberExtensionGenerator
|
6
|
+
# Contains all code which interacts with the terminal.
|
7
|
+
module CLI
|
8
|
+
class << self
|
9
|
+
def run
|
10
|
+
GemGenerator.call Args.parse
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require_relative 'cli/args'
|
17
|
+
require_relative 'cli/gem_generator'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
BIN_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
4
|
+
|
5
|
+
echo "╔════════════════════════════════════╗"
|
6
|
+
echo "║ Starting the development Rails App ║"
|
7
|
+
echo "╚════════════════════════════════════╝"
|
8
|
+
echo
|
9
|
+
|
10
|
+
$BIN_DIR/../<%= rails_dummy_path %>/bin/rails server
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'thor'
|
5
|
+
require 'pathname'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
require_relative '../<%= gem_entry_folder_path %>'
|
9
|
+
|
10
|
+
class ::GenerateCommand < ::Thor
|
11
|
+
include ::Thor::Actions
|
12
|
+
|
13
|
+
# @return [Module]
|
14
|
+
LIBRARY_MODULE = <%= root_module_name %>
|
15
|
+
# @return [String]
|
16
|
+
LIBRARY_NAME = '<%= gem_name %>'
|
17
|
+
# @return [Pathname]
|
18
|
+
ROOT_PATH = ::Pathname.new ::File.expand_path('..', __dir__)
|
19
|
+
# @return [Pathname]
|
20
|
+
GEM_ENTRY_FOLDER_PATH = ::Pathname.new '<%= gem_entry_folder_path %>'
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Define the generator's root folder
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def source_root
|
27
|
+
::File.expand_path('..', __dir__)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'component NAME', 'Generate a new component with the specified NAME'
|
32
|
+
|
33
|
+
# @param name [String]
|
34
|
+
# @return [void]
|
35
|
+
def component(name)
|
36
|
+
name = snake_case(name)
|
37
|
+
name = "#{name}_component" unless name.end_with? '_component'
|
38
|
+
@name = name
|
39
|
+
|
40
|
+
template 'templates/component.rb.tt', component_path
|
41
|
+
component_assets_path.mkdir unless component_assets_path.exist?
|
42
|
+
|
43
|
+
template 'templates/view.html.erb.tt', component_assets_path / 'view.html.erb'
|
44
|
+
template 'templates/style.scss.tt', component_assets_path / 'style.scss'
|
45
|
+
template 'templates/component_test.rb.tt', component_test_file_path
|
46
|
+
|
47
|
+
append_file main_components_stylesheet_path, %(\n@import "../../../#{component_assets_path}/style.scss";)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @return [Pathname]
|
53
|
+
def component_test_file_path
|
54
|
+
components_test_folder_path / "#{@name}_test.rb"
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Pathname]
|
58
|
+
def main_components_stylesheet_path
|
59
|
+
ROOT_PATH / 'assets' / 'stylesheets' / library_relative_path / 'components.scss'
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Pathname]
|
63
|
+
def components_test_folder_path
|
64
|
+
ROOT_PATH / 'test' / library_relative_path / 'components'
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Pathname]
|
68
|
+
def library_relative_path
|
69
|
+
@library_relative_path ||= ::Pathname.new LIBRARY_NAME.gsub('-', '/')
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [Pathname]
|
73
|
+
def component_assets_path
|
74
|
+
@component_assets_path ||= GEM_ENTRY_FOLDER_PATH / 'components' / @name
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Pathname]
|
78
|
+
def component_path
|
79
|
+
@component_path ||= GEM_ENTRY_FOLDER_PATH / 'components' / file_name
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [String]
|
83
|
+
def full_class_name
|
84
|
+
"#{LIBRARY_MODULE}::#{class_name}"
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [String]
|
88
|
+
def component_css_class
|
89
|
+
"#{LIBRARY_NAME}--#{@name}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [String]
|
93
|
+
def class_name
|
94
|
+
@class_name ||= camelize(@name)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [String]
|
98
|
+
def file_name
|
99
|
+
@file_name ||= "#{@name}.rb"
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Class]
|
103
|
+
def abstract_component
|
104
|
+
LIBRARY_MODULE::ABSTRACT_COMPONENT
|
105
|
+
end
|
106
|
+
|
107
|
+
# Converts a string to camel/Pascal Case.
|
108
|
+
#
|
109
|
+
# camelize('some_snake_case') #=> "SomeSnakeCase"
|
110
|
+
# camelize('some/snake_case') #=> "Some::SnakeCase"
|
111
|
+
#
|
112
|
+
# @param string [String]
|
113
|
+
# @param uppercase_first_letter [Boolean]
|
114
|
+
# @return [String]
|
115
|
+
def camelize(string, uppercase_first_letter: true)
|
116
|
+
string = if uppercase_first_letter
|
117
|
+
string.sub(/^[a-z\d]*/, &:capitalize)
|
118
|
+
else
|
119
|
+
string.sub(/^(?:(?=\b|[A-Z_])|\w)/, &:downcase)
|
120
|
+
end
|
121
|
+
|
122
|
+
string.gsub!(%r{(?:_|(/))([a-z\d]*)}) do
|
123
|
+
"#{::Regexp.last_match(1)}#{::Regexp.last_match(2).capitalize}"
|
124
|
+
end
|
125
|
+
string.gsub('/', '::')
|
126
|
+
end
|
127
|
+
|
128
|
+
# Converts a string in PascalCase or camelCase to snake_case.
|
129
|
+
#
|
130
|
+
# snake_case('SomePascalCase') => "some_pascal_case"
|
131
|
+
# snake_case('Some::PascalCase') => "some/pascal_case"
|
132
|
+
#
|
133
|
+
# @param string [String]
|
134
|
+
# @return [String]
|
135
|
+
def snake_case(string)
|
136
|
+
string.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
|
137
|
+
.gsub(%r{::_|/_}, '/')
|
138
|
+
.downcase
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
::GenerateCommand.start
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'amber_component'
|
4
|
+
|
5
|
+
module <%= root_module_name %>
|
6
|
+
# Abstract class which should serve as a superclass
|
7
|
+
# for all components.
|
8
|
+
#
|
9
|
+
# @abstract Subclass to create a new component.
|
10
|
+
class BaseComponent < ::AmberComponent::Base; end
|
11
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# === GEM AUTO-RELOADING ===
|
2
|
+
|
3
|
+
class ::ExtensionGem
|
4
|
+
# @return [String] Name of the gem.
|
5
|
+
attr_reader :name
|
6
|
+
# @return [Pathname] Path to the gem.
|
7
|
+
attr_reader :path
|
8
|
+
# @return [Array<Symbol>] Top-level constants defined by the gem.
|
9
|
+
attr_reader :constants
|
10
|
+
# @return [String]
|
11
|
+
attr_reader :require_path
|
12
|
+
|
13
|
+
# @param name [String]
|
14
|
+
# @param path [String, Pathname]
|
15
|
+
# @param constants [Array<Symbol>]
|
16
|
+
# @param require_path [String, nil]
|
17
|
+
def initialize(name:, path:, constants:, require_path: nil)
|
18
|
+
@name = name
|
19
|
+
@path = ::File.expand_path(path, __dir__)
|
20
|
+
@constants = constants
|
21
|
+
@require_path = require_path || @name.gsub('-', '/')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [ExtensionGem]
|
26
|
+
::AMBER_EXTENSION_GEM = ::ExtensionGem.new(
|
27
|
+
name: "<%= gem_name %>",
|
28
|
+
path: '..',
|
29
|
+
constants: %i[<%= root_module_name %>].freeze
|
30
|
+
)
|
31
|
+
gem ::AMBER_EXTENSION_GEM.name, path: ::AMBER_EXTENSION_GEM.path
|
32
|
+
|
33
|
+
# === END GEM AUTO-RELOADING ===
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module <%= root_module_name %>
|
4
|
+
# Class which hooks into Rails
|
5
|
+
# and configures the application.
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
initializer '<%= gem_name %>.assets' do |app|
|
8
|
+
(ROOT_PATH / 'assets').each_child do |asset_dir|
|
9
|
+
app.config.assets.paths << asset_dir.to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= full_class_name %> < <%= abstract_component %>
|
4
|
+
# Props that your component accepts
|
5
|
+
prop :description, type: ::String, default: -> { 'Default Description' }
|
6
|
+
|
7
|
+
after_initialize do
|
8
|
+
# some initialization
|
9
|
+
@time = ::Time.now
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class <%= full_class_name %>Test < ::ComponentTestCase
|
6
|
+
# For a full list of available assertions see
|
7
|
+
# https://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Matchers
|
8
|
+
|
9
|
+
# def test_returns_correct_html
|
10
|
+
# render do
|
11
|
+
# <%= full_class_name %>.call
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# assert_text 'Hello from <%= full_class_name %>'
|
15
|
+
# assert_selector "div.<%= component_css_class %> p", text: 'Default Description'
|
16
|
+
# end
|
17
|
+
end
|