amber_extension_generator 0.0.5
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/.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
|