singer 1.0.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d116f657ac1b8d3b57e540ae37bf0d74c21fe357938fbb6aa33309663b9add5
4
- data.tar.gz: 7854c981adfd45a2989647f6302022ff0b40e66b47960c2fc536cd428dc99c93
3
+ metadata.gz: cd871af4e10b86d0362c5bef72223344e86d5f81cb6a2871e901ec588f2d97b8
4
+ data.tar.gz: 5bbab829554eee4d7da41e43f07fec57784269906da6a7f866a466a73f1a9e92
5
5
  SHA512:
6
- metadata.gz: 933cc4b306f621d57598f0b1d5f72993fd6c25afea2496aa89f088847b42374b8a01bf68d709a2c3f154455b02b6158f2072677ec23b55ff1b359020c24cfbe5
7
- data.tar.gz: 0ed47b046d03641227f28faa499d9ea50cd80901ddf7efa75f5c1da4df07a54b5ad435bb338158047438175862c17b310e92d19ee94ecfafdf135d9810dce468
6
+ metadata.gz: ba2fecb7f5b4a230e5eb26d7e8fef5566e1753fec82952b431779b7720fc3c525beb26a9e9e977c2d328648b11531e1bc4e760c1edbde219f1f025abb0be72d9
7
+ data.tar.gz: 3f121f9b2383ef48b822e1dac292e5a9f8455ded6cb9816b4fcd79b8139e64d46709c5fd2f9473cb200a1f8fc4808586e16bcfad574d91c86c8892bb67aba96a
data/README.md CHANGED
@@ -17,6 +17,43 @@ Run `singer --list-templates` to see available templates' names, both shipped wi
17
17
 
18
18
  For `PROJECT_NAME`, using `snake_case` is best, although an attempt will be made to understand `CamelCase`, too.
19
19
 
20
+ ## Writing your own templates
21
+
22
+ Many features of Singer will become clearer when seeing actual usage - please feel encouraged to examine the templates shipped with the gem (you can find them in `templates_from_gem` shown by the `singer --show-paths`).
23
+
24
+ ### Template location
25
+
26
+ User-provided templates should be placed in the directory named `templates_from_user` in the `singer --show-paths` output.
27
+ Each template resides in a subdirectory - the subdirectory's name becomes the template's name, and is not included in the generated paths (Singer will only replicate the directory structure _inside_ it).
28
+
29
+ ### Variables available to templates
30
+
31
+ Templates written in ERB or other templating mechanisms that execute Ruby code will have access to Singer::CONFIGURATION object and its accessors. For example, the following code in a `my_template`'s file:
32
+ ```ruby
33
+ class <%= Singer::CONFIGURATION.project_name_camelcase %>
34
+ ```
35
+ will, when Singer is called with `singer my_template foo_bar_baz`, result in this written to output file:
36
+ ```ruby
37
+ class FooBarBaz
38
+ ```
39
+
40
+ To see an up-to-date list of available variables, run `singer --list-variables`.
41
+
42
+ ### Special segments in template paths
43
+
44
+ Any of the variables above can be also used in paths/filenames of template files, capitalized and surrounded by double underscores.
45
+ So, if `my_template` has a file at this path:
46
+ ```
47
+ docs_for___PROJECT_NAME_SNAKECASE__/__PROJECT_NAME_CAMELCASE__ - humble beginnings.txt
48
+ ```
49
+ and you run `singer my_template alpha_beta`, a file named like this will be created:
50
+ ```
51
+ docs_for_alpha_beta/AlphaBeta - humble beginnings.txt
52
+ ```
53
+
54
+ Tiny caveat for completeness - while `template_file_name_actual` can be used inside template files, the `__TEMPLATE_FILE_NAME_ACTUAL__` will not be substituted in paths.
55
+ "Stack level too deep" and all that ;)
56
+
20
57
  ## Development
21
58
 
22
59
  ```bash
@@ -1,4 +1,9 @@
1
1
  module Singer
2
2
  # holds settings
3
- CONFIGURATION = Struct.new(:template_name, :camelcase_name, :snakecase_name).new
3
+ CONFIGURATION = Struct.new(
4
+ :template_name,
5
+ :project_name_original, :project_name_camelcase, :project_name_snakecase,
6
+ :template_file_name_original, :template_file_name_actual,
7
+ ).new
8
+ CONFIGURATION_VARIABLES_FORBIDDEN_IN_PATHS = %w[template_file_name_actual].freeze
4
9
  end
@@ -3,7 +3,7 @@ module Singer
3
3
  class OptionParsing
4
4
  DEFAULT_TEMPLATE_NAME = 'tdd'.freeze
5
5
 
6
- def self.configure(argvies) # rubocop:disable Metrics/MethodLength
6
+ def self.configure(argvies) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
7
7
  options = {}
8
8
  OptionParser.new do |parser|
9
9
  parser.banner = <<~USAGE
@@ -17,6 +17,14 @@ module Singer
17
17
  list_all_templates
18
18
  Kernel.exit
19
19
  end
20
+ parser.on('--show-paths', 'show paths used to load files') do
21
+ show_paths
22
+ Kernel.exit
23
+ end
24
+ parser.on('--list-variables', 'list all variables available to templates') do
25
+ list_variables
26
+ Kernel.exit
27
+ end
20
28
  end.parse!(argvies, into: options)
21
29
 
22
30
  raise NameMissingError, 'Missing mandatory argument PROJECT_NAME' if argvies.empty?
@@ -26,15 +34,28 @@ module Singer
26
34
  end
27
35
 
28
36
  def self.list_all_templates
29
- name_column_width = Template.all.keys.map(&:length).max
30
- Template.all.each do |name, template|
31
- puts "#{name.to_s.ljust(name_column_width)} #{template.path}"
37
+ columnize_hash(Template.all.transform_values(&:path))
38
+ end
39
+
40
+ def self.show_paths
41
+ columnize_hash(%w[singer_config_dir templates_from_user templates_from_gem].to_h{ [_1, Paths.send(_1)] })
42
+ end
43
+
44
+ def self.columnize_hash(hash)
45
+ first_column_width = hash.keys.map(&:length).max
46
+ hash.each do |k, v|
47
+ puts "#{k.to_s.ljust(first_column_width)} #{v}"
32
48
  end
33
49
  end
34
50
 
51
+ def self.list_variables
52
+ puts CONFIGURATION.members
53
+ end
54
+
35
55
  def self.encase_name(name)
36
- CONFIGURATION.snakecase_name = name.gsub(/(\w)([A-Z])/){ "#{$1}_#{$2}" }.gsub(/_+/, '_').downcase
37
- CONFIGURATION.camelcase_name = CONFIGURATION.snakecase_name.split('_').map(&:capitalize).join
56
+ CONFIGURATION.project_name_original = name
57
+ CONFIGURATION.project_name_snakecase = name.gsub(/(\w)([A-Z])/){ "#{$1}_#{$2}" }.gsub(/_+/, '_').downcase
58
+ CONFIGURATION.project_name_camelcase = CONFIGURATION.project_name_snakecase.split('_').map(&:capitalize).join
38
59
  end
39
60
  end
40
61
  end
data/lib/singer/paths.rb CHANGED
@@ -21,12 +21,16 @@ module Singer
21
21
  File.join(...).gsub(/__([[:alpha:]][[:word:]]+)__/) do |match|
22
22
  potential_config = $1.downcase
23
23
 
24
- if CONFIGURATION.respond_to?(potential_config)
24
+ if variable_name_can_be_substituted?(potential_config)
25
25
  CONFIGURATION.send(potential_config)
26
26
  else
27
27
  match
28
28
  end
29
29
  end
30
30
  end
31
+
32
+ def self.variable_name_can_be_substituted?(name)
33
+ CONFIGURATION.respond_to?(name) && !CONFIGURATION_VARIABLES_FORBIDDEN_IN_PATHS.include?(name)
34
+ end
31
35
  end
32
36
  end
@@ -31,9 +31,12 @@ module Singer
31
31
 
32
32
  def generate(output_path)
33
33
  files.each do |file|
34
- source_file = File.join(path, file)
34
+ CONFIGURATION.template_file_name_original = file
35
35
  target_file = Paths.output_path(output_path, file)
36
+ CONFIGURATION.template_file_name_actual = target_file
37
+
36
38
  FileUtils.mkdir_p(File.dirname(target_file))
39
+ source_file = File.join(path, file)
37
40
  TemplatingMethods.send(:erb, source_file, target_file)
38
41
  end
39
42
  end
@@ -1,3 +1,3 @@
1
1
  module Singer
2
- VERSION = '1.0.0'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -1,3 +1,3 @@
1
1
  require_relative '../lib/zeitwerk_setup'
2
2
 
3
- run <%= Singer::CONFIGURATION.camelcase_name %>::Server
3
+ run <%= Singer::CONFIGURATION.project_name_camelcase %>::Server
@@ -1,3 +1,3 @@
1
1
  require 'zeitwerk_setup'
2
2
 
3
- <%= Singer::CONFIGURATION.camelcase_name %>::Server.run!
3
+ <%= Singer::CONFIGURATION.project_name_camelcase %>::Server.run!
@@ -1,4 +1,4 @@
1
- module <%= Singer::CONFIGURATION.camelcase_name %>
1
+ module <%= Singer::CONFIGURATION.project_name_camelcase %>
2
2
  # demo class - TODO: remove
3
3
  class Hello
4
4
  def self.greet(target)
@@ -3,7 +3,7 @@ require 'haml'
3
3
  require 'sassc'
4
4
  require 'colorize'
5
5
 
6
- module <%= Singer::CONFIGURATION.camelcase_name %>
6
+ module <%= Singer::CONFIGURATION.project_name_camelcase %>
7
7
  # Sinatra app with routes
8
8
  class Server < Sinatra::Base
9
9
  set :root, '.' # to make directory structure with views/ work
@@ -4,7 +4,7 @@ class TestServer < Minitest::Test
4
4
  include Rack::Test::Methods
5
5
 
6
6
  def app
7
- <%= Singer::CONFIGURATION.camelcase_name %>::Server
7
+ <%= Singer::CONFIGURATION.project_name_camelcase %>::Server
8
8
  end
9
9
 
10
10
  def test_top_level_redirects
@@ -10,7 +10,7 @@ class TestServerErrorDumping < Minitest::Test
10
10
  refute message.lines.any?{ _1.include?('/vendor/') } # now you don't
11
11
  end
12
12
 
13
- app = <%= Singer::CONFIGURATION.camelcase_name %>::Server.new! # .new! returns real instance without Sinatra::Wrapper
13
+ app = <%= Singer::CONFIGURATION.project_name_camelcase %>::Server.new! # .new! returns real instance without Sinatra::Wrapper
14
14
  app.dump_errors!(exc, log)
15
15
  end
16
16
 
@@ -3,6 +3,6 @@ require 'minitest_helper'
3
3
  # TODO: remove
4
4
  class TestHello < Minitest::Test
5
5
  def test_greet_with_a_param_generates_a_greeting_string
6
- assert_equal 'Hello, stranger.', <%= Singer::CONFIGURATION.camelcase_name %>::Hello.greet(:stranger)
6
+ assert_equal 'Hello, stranger.', <%= Singer::CONFIGURATION.project_name_camelcase %>::Hello.greet(:stranger)
7
7
  end
8
8
  end
@@ -1,7 +1,7 @@
1
1
  !!!
2
2
  %html
3
3
  %head
4
- %title <%= Singer::CONFIGURATION.camelcase_name %>
4
+ %title <%= Singer::CONFIGURATION.project_name_camelcase %>
5
5
  %meta{charset: 'UTF-8'}
6
6
  %style!= COMPILED_STYLE
7
7
  %body!= yield
@@ -1,13 +1,13 @@
1
1
  require 'colorize'
2
2
  # require 'awesome_print'
3
3
 
4
- class <%= Singer::CONFIGURATION.camelcase_name %>
4
+ class <%= Singer::CONFIGURATION.project_name_camelcase %>
5
5
  def hello(who:)
6
6
  "Hello, #{who}."
7
7
  end
8
8
  end
9
9
 
10
10
  if File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
11
- ciastka = <%= Singer::CONFIGURATION.camelcase_name %>.new
11
+ ciastka = <%= Singer::CONFIGURATION.project_name_camelcase %>.new
12
12
  puts "Testing testing: #{ciastka.hello(who: :world).green}"
13
13
  end
@@ -0,0 +1,16 @@
1
+ require 'minitest/rg'
2
+ require 'minitest/autorun' if $PROGRAM_NAME == __FILE__
3
+
4
+ require_relative '../lib/<%= Singer::CONFIGURATION.project_name_snakecase %>'
5
+
6
+ class Test<%= Singer::CONFIGURATION.project_name_camelcase %> < Minitest::Test
7
+ # TEST_INPUT = File.read('input_mini.txt').lines(chomp: true)
8
+
9
+ def setup
10
+ @<%= Singer::CONFIGURATION.project_name_snakecase %> = <%= Singer::CONFIGURATION.project_name_camelcase %>.new
11
+ end
12
+
13
+ def test_hello_interpolates_into_string
14
+ assert_equal 'Hello, world.', @<%= Singer::CONFIGURATION.project_name_snakecase %>.hello(who: :world)
15
+ end
16
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: singer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emil Chludziński
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-29 00:00:00.000000000 Z
10
+ date: 2025-02-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: erb
@@ -48,8 +48,8 @@ files:
48
48
  - templates/sinatra_app/config/config.ru
49
49
  - templates/sinatra_app/config/puma.rb
50
50
  - templates/sinatra_app/exe/server.rb
51
- - templates/sinatra_app/lib/__SNAKECASE_NAME__/hello.rb
52
- - templates/sinatra_app/lib/__SNAKECASE_NAME__/server.rb
51
+ - templates/sinatra_app/lib/__PROJECT_NAME_SNAKECASE__/hello.rb
52
+ - templates/sinatra_app/lib/__PROJECT_NAME_SNAKECASE__/server.rb
53
53
  - templates/sinatra_app/lib/zeitwerk_setup.rb
54
54
  - templates/sinatra_app/test/minitest_helper.rb
55
55
  - templates/sinatra_app/test/server/test_server.rb
@@ -60,8 +60,8 @@ files:
60
60
  - templates/sinatra_app/views/layout.haml
61
61
  - templates/sinatra_app/views/style.sass
62
62
  - templates/tdd/Gemfile
63
- - templates/tdd/lib/__SNAKECASE_NAME__.rb
64
- - templates/tdd/test/test___SNAKECASE_NAME__.rb
63
+ - templates/tdd/lib/__PROJECT_NAME_SNAKECASE__.rb
64
+ - templates/tdd/test/test___PROJECT_NAME_SNAKECASE__.rb
65
65
  homepage: https://gitlab.com/tanstaafl/singer/
66
66
  licenses:
67
67
  - MIT
@@ -1,16 +0,0 @@
1
- require 'minitest/rg'
2
- require 'minitest/autorun' if $PROGRAM_NAME == __FILE__
3
-
4
- require_relative '../lib/<%= Singer::CONFIGURATION.snakecase_name %>'
5
-
6
- class Test<%= Singer::CONFIGURATION.camelcase_name %> < Minitest::Test
7
- # TEST_INPUT = File.read('input_mini.txt').lines(chomp: true)
8
-
9
- def setup
10
- @<%= Singer::CONFIGURATION.snakecase_name %> = <%= Singer::CONFIGURATION.camelcase_name %>.new
11
- end
12
-
13
- def test_hello_interpolates_into_string
14
- assert_equal 'Hello, world.', @<%= Singer::CONFIGURATION.snakecase_name %>.hello(who: :world)
15
- end
16
- end