rbytes 0.2.0 → 0.3.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: f8780a2ff433a3b3e11c59a05fe7bf73a1a742463c659bbf8abd835b17242d43
4
- data.tar.gz: 10fd5d4eb9271e6e3be8fe2f71262e03a743dbf077b71a925498796c20e2cafe
3
+ metadata.gz: 040b91d608de3f8f28c7e81b3b63c604370779ed8321baa8d8a26ad249365927
4
+ data.tar.gz: 8b99d75e94487d7999284caf0b6ad07fd7feb8771d4ba7ab20d85e0e7c248449
5
5
  SHA512:
6
- metadata.gz: c681d786e67cf6423e95b78b687bfec4dea5460ec3238656ed3319347240eae199a21fbf5bc05fb3d67a70fdd6862797712401085e239ce94c56ccb25f7ceea9
7
- data.tar.gz: e8fae36e2fa7daa3acfd71a760b969e6c09c23dc248f1bedd479930e7c9e064b7c6027e987fa61683250c36a65398cc784f174f2f9890ce0a3764300abe76088
6
+ metadata.gz: bfbb924533307fed9c3ba1d62f4720ee62eb5e139946829f3f1f4d2c045a365424b42258193e9e523737ddb5f37714410028f0a74a61063ec25b883d3068cedf
7
+ data.tar.gz: 506aa21f7128feb9d7f71e25b94d94f4f1fcbfc3b2fcfb53d9ee295ae4cfba46384349659a9b766a1bd1287e4b9c606ac2a27af1e5f2ca510873f8f5ee247c0c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.3.0 (2026-02-13)
6
+
7
+ - Add Charm Ruby integration.
8
+
5
9
  ## 0.2.0 (2023-04-15)
6
10
 
7
11
  - Add `#import_template` directive to inline other templates.
@@ -0,0 +1,283 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lipgloss"
4
+ require "gum"
5
+
6
+ class Rbytes
7
+ module Charmed
8
+ # See https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc87b2124?permalink_comment_id=3892823#gistcomment-3892823
9
+ FG_COLORS = {
10
+ black: "8",
11
+ red: "9",
12
+ green: "10",
13
+ yellow: "11",
14
+ blue: "12",
15
+ magenta: "13",
16
+ cyan: "14",
17
+ white: "15",
18
+ grey: "243"
19
+ }.freeze
20
+
21
+ BG_COLORS = {
22
+ black: "16",
23
+ red: "124",
24
+ green: "28",
25
+ yellow: "214",
26
+ blue: "25",
27
+ magenta: "95",
28
+ cyan: "45",
29
+ white: "231",
30
+ grey: "252"
31
+ }.freeze
32
+
33
+ # Print a message to the user.
34
+ # say accumulates messages to print them in a box in case more than a single message is provided.
35
+ # the empty string is used to flush the output; any other command flushes the output, too.
36
+ def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
37
+ return if quiet?
38
+
39
+ return draw_box! if message == ""
40
+
41
+ buffer = set_color(message, *Array(color))
42
+
43
+ append_to_current_box(buffer)
44
+ end
45
+
46
+ STATUS_COLORS = {
47
+ debug: :grey,
48
+ info: :blue,
49
+ warn: :yellow,
50
+ error: :red,
51
+ fatal: :red
52
+ }.freeze
53
+
54
+ # Print a status badge followed by a message (e.g. " create config/routes.rb").
55
+ # Thor default: right-aligns the status word, colors it, appends the message.
56
+ def say_status(status, message, log_status = true)
57
+ return if quiet? || log_status == false
58
+
59
+ draw_box!
60
+
61
+ color_code = log_status.is_a?(Symbol) ? FG_COLORS.fetch(log_status) : FG_COLORS.fetch(STATUS_COLORS.fetch(status, STATUS_COLORS[:info]))
62
+
63
+ status_style = Lipgloss::Style.new.bold(true).foreground(color_code).inline(true)
64
+ buffer = "#{status_style.render(status.to_s.upcase)}\t#{message}"
65
+
66
+ stdout.puts(buffer)
67
+ stdout.flush
68
+ end
69
+
70
+ # Print an error message to $stderr.
71
+ # Thor default: same as +say+ but writes to $stderr.
72
+ def say_error(message = "", color = :red, force_new_line = (message.to_s !~ /( |\t)\Z/))
73
+ return if quiet?
74
+
75
+ draw_box!
76
+
77
+ # Print error in a box
78
+ error_style = Lipgloss::Style.new
79
+ .padding(1, 1)
80
+ .align(:center)
81
+ .background(FG_COLORS.fetch(color))
82
+ buffer = error_style.render(message)
83
+ stderr.puts(buffer)
84
+ stderr.flush
85
+ end
86
+
87
+ # Print an unformatted error statement to $stderr (legacy Thor method).
88
+ # Thor default: $stderr.puts statement.
89
+ def error(statement)
90
+ say_error(statement)
91
+ end
92
+
93
+ # Print an array of strings in evenly-spaced columns.
94
+ # Thor default: calculates column widths and pads with spaces.
95
+ def print_in_columns(array)
96
+ return if quiet?
97
+
98
+ draw_box!
99
+
100
+ return if array.empty?
101
+
102
+ if array.size <= 4
103
+ stdout.puts Lipgloss::List.new.items(array.map(&:to_s)).render
104
+ stdout.flush
105
+ return
106
+ end
107
+
108
+ col_width = array.map { |e| e.to_s.size }.max + 2
109
+ cols = [terminal_width / col_width, array.size].min.clamp(4..)
110
+
111
+ # Shrink columns so the last row has at least cols-2 items
112
+ while cols > 4
113
+ remainder = array.size % cols
114
+ break if remainder.zero? || remainder >= cols - 2
115
+ cols -= 1
116
+ end
117
+
118
+ even_style = Lipgloss::Style.new.width(col_width).inline(true).align(:center).background("254").foreground("0")
119
+ odd_style = Lipgloss::Style.new.width(col_width).inline(true).align(:center).background("234")
120
+
121
+ rows = array.each_slice(cols).map.with_index do |row, ri|
122
+ Lipgloss.join_horizontal(:top, *row.map.with_index { |item, ci| ((ri + ci).even? ? even_style : odd_style).render(item.to_s) })
123
+ end
124
+
125
+ buffer = Lipgloss.join_vertical(:left, *rows)
126
+
127
+ stdout.puts(buffer)
128
+ stdout.flush
129
+ end
130
+
131
+ # Print a 2-D array as an aligned table.
132
+ # Thor default: ASCII table with optional indent, colwidth, borders.
133
+ #
134
+ # ==== Options
135
+ # indent<Integer> – indent the first column
136
+ # colwidth<Integer> – force the first column width
137
+ # borders<Boolean> – draw borders
138
+ def print_table(data, options = {})
139
+ return if quiet?
140
+
141
+ draw_box!
142
+
143
+ data = data.dup
144
+ headers = data.shift
145
+
146
+ header_style = Lipgloss::Style.new.bold(true).foreground("12")
147
+ even_style = Lipgloss::Style.new.background("254").foreground("0")
148
+ odd_style = Lipgloss::Style.new.background("234")
149
+
150
+ table = Lipgloss::Table.new
151
+ .headers(headers)
152
+ .rows(data)
153
+ .style_func(rows: data.size, columns: headers.size) do |row, column|
154
+ if row == Lipgloss::Table::HEADER_ROW
155
+ header_style
156
+ elsif row.even?
157
+ even_style
158
+ else
159
+ odd_style
160
+ end
161
+ end
162
+
163
+ buffer = table.render
164
+
165
+ stdout.puts(buffer)
166
+ stdout.flush
167
+ end
168
+
169
+ # Print a long string, word-wrapped to the terminal width.
170
+ # Thor default: wraps text at terminal_width minus indent.
171
+ #
172
+ # ==== Options
173
+ # indent<Integer> – left-indent each line
174
+ def print_wrapped(message, options = {})
175
+ return if quiet?
176
+
177
+ draw_box!
178
+
179
+ style = Lipgloss::Style.new.background("234")
180
+ buffer = style.render(message)
181
+ super(buffer)
182
+ end
183
+
184
+ # ── Input ─────────────────────────────────────────────────────────────
185
+
186
+ # Prompt the user for a single line of text.
187
+ # Thor default: readline with optional :default, :limited_to, :echo, :path.
188
+ def ask(statement, *args)
189
+ draw_box!
190
+
191
+ options = args.last.is_a?(Hash) ? args.pop : {}
192
+ # color = args.first
193
+
194
+ if options[:limited_to]
195
+ Gum.choose(options[:limited_to], header: statement, selected: options[:default])
196
+ else
197
+ Gum.input(value: options[:default], prompt: "> ", header: statement)
198
+ end.then { |v| handle_gum_result(v) }
199
+ end
200
+
201
+ # Ask a yes/no question, return true when the user answers "y" or "yes".
202
+ # Thor default: regex match on the answer string.
203
+ def yes?(statement, color = nil)
204
+ draw_box!
205
+
206
+ handle_gum_result(
207
+ Gum.choose(%w[Yes No], header: statement, selected: "Yes")
208
+ ).then { |res| res == "Yes" }
209
+ end
210
+
211
+ # Ask a yes/no question, return true when the user answers "n" or "no".
212
+ # Thor default: inverse of +yes?+.
213
+ def no?(statement, color = nil)
214
+ draw_box!
215
+
216
+ handle_gum_result(
217
+ Gum.choose(%w[Yes No], header: statement, selected: "Yes")
218
+ ).then { |res| res == "No" }
219
+ end
220
+
221
+ # ── Formatting / Utility ──────────────────────────────────────────────
222
+
223
+ # Apply color and style to a string.
224
+ # Thor default: wraps string in ANSI escape codes.
225
+ def set_color(string, *colors)
226
+ style = Lipgloss::Style.new.inline(true)
227
+ colors.each do |c|
228
+ case c
229
+ when :bold then style = style.bold(true)
230
+ when :on_black, :on_red, :on_green, :on_yellow, :on_blue, :on_magenta, :on_cyan, :on_white
231
+ style = style.background(BG_COLORS.fetch(c.to_s.sub("on_", "").to_sym))
232
+ when Symbol then style = style.foreground(FG_COLORS.fetch(c))
233
+ end
234
+ end
235
+
236
+ style.render(string.to_s)
237
+ end
238
+
239
+ # Return the width (in columns) of the current terminal.
240
+ # Thor default: detects via IO/stty (class method on Thor::Shell::Terminal).
241
+ def terminal_width
242
+ Thor::Shell::Terminal.terminal_width
243
+ end
244
+
245
+ private
246
+
247
+ def box_buffer
248
+ @box_buffer ||= []
249
+ end
250
+
251
+ def append_to_current_box(line)
252
+ box_buffer << line
253
+ end
254
+
255
+ def draw_box!
256
+ return stdout.puts if box_buffer.empty?
257
+
258
+ # Single line is printed as is
259
+ buffer =
260
+ if box_buffer.size == 1
261
+ box_buffer.first
262
+ else
263
+ Lipgloss::Style.new
264
+ .border(:normal)
265
+ .padding(1, 2)
266
+ .align(:center)
267
+ .render(box_buffer.join("\n"))
268
+ end
269
+
270
+ stdout.puts(buffer)
271
+ stdout.flush
272
+
273
+ box_buffer.clear
274
+ end
275
+
276
+ def handle_gum_result(res)
277
+ # nil means user cancelled
278
+ # https://github.com/marcoroth/gum-ruby/blob/faf9e2ccefa6457708a4eb31c992e0ccf585a998/lib/gum/command.rb#L46
279
+ Kernel.exit(130) if res.nil?
280
+ res
281
+ end
282
+ end
283
+ end
@@ -3,12 +3,12 @@
3
3
  class Rbytes < Thor
4
4
  desc "template", "Load and run generator from RailsBytes"
5
5
  def template(url)
6
- puts "Run template from: #{url}"
6
+ say_status :info, "Run template from: #{url}"
7
7
 
8
8
  # require gems typically used in templates
9
9
  require "bundler"
10
10
 
11
- Base.new.apply(url)
11
+ Base.new.apply(url, verbose: false)
12
12
  end
13
13
 
14
14
  # Rails core extensions can be used in templates.
@@ -449,6 +449,15 @@ class Rbytes < Thor
449
449
  include Thor::Actions
450
450
  include Rails::Actions
451
451
 
452
+ unless ENV["RBYTES_DISABLE_GUM"] == "1"
453
+ begin
454
+ require "ruby_bytes/charmed"
455
+ Thor::Shell::Color.prepend Charmed
456
+ rescue LoadError
457
+ # No gum available
458
+ end
459
+ end
460
+
452
461
  # Custom methods defined on AppGenerator
453
462
  # https://github.com/rails/rails/blob/38275763257221e381cd4e37958ce1413fd0433c/railties/lib/rails/generators/rails/app/app_generator.rb#L558
454
463
  def file(*args, &block)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyBytes # :nodoc:
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -3,12 +3,12 @@
3
3
  class Rbytes < Thor
4
4
  desc "template", "Load and run generator from RailsBytes"
5
5
  def template(url)
6
- puts "Run template from: #{url}"
6
+ say_status :info, "Run template from: #{url}"
7
7
 
8
8
  # require gems typically used in templates
9
9
  require "bundler"
10
10
 
11
- Base.new.apply(url)
11
+ Base.new.apply(url, verbose: false)
12
12
  end
13
13
 
14
14
  <%= include "core_ext", indent: 2 %>
@@ -20,6 +20,15 @@ class Rbytes < Thor
20
20
  include Thor::Actions
21
21
  include Rails::Actions
22
22
 
23
+ unless ENV["RBYTES_DISABLE_GUM"] == "1"
24
+ begin
25
+ require "ruby_bytes/charmed"
26
+ Thor::Shell::Color.prepend Charmed
27
+ rescue LoadError
28
+ # No gum available
29
+ end
30
+ end
31
+
23
32
  # Custom methods defined on AppGenerator
24
33
  # https://github.com/rails/rails/blob/38275763257221e381cd4e37958ce1413fd0433c/railties/lib/rails/generators/rails/app/app_generator.rb#L558
25
34
  def file(*args, &block)
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbytes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-08-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: thor
@@ -80,6 +79,7 @@ files:
80
79
  - README.md
81
80
  - bin/rbytes
82
81
  - lib/rbytes.rb
82
+ - lib/ruby_bytes/charmed.rb
83
83
  - lib/ruby_bytes/cli.rb
84
84
  - lib/ruby_bytes/compiler.rb
85
85
  - lib/ruby_bytes/publisher.rb
@@ -99,7 +99,6 @@ metadata:
99
99
  documentation_uri: http://github.com/palkan/rbytes
100
100
  homepage_uri: http://github.com/palkan/rbytes
101
101
  source_code_uri: http://github.com/palkan/rbytes
102
- post_install_message:
103
102
  rdoc_options: []
104
103
  require_paths:
105
104
  - lib
@@ -114,8 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
113
  - !ruby/object:Gem::Version
115
114
  version: '0'
116
115
  requirements: []
117
- rubygems_version: 3.4.8
118
- signing_key:
116
+ rubygems_version: 3.6.9
119
117
  specification_version: 4
120
118
  summary: Ruby Bytes is a tool to build application templates for Ruby and Rails applications
121
119
  test_files: []