rbytes 0.0.1 → 0.1.1
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +205 -4
- data/bin/rbytes +13 -0
- data/lib/rbytes.rb +3 -1
- data/lib/ruby_bytes/cli.rb +117 -0
- data/lib/ruby_bytes/compiler.rb +60 -0
- data/lib/ruby_bytes/publisher.rb +38 -0
- data/lib/ruby_bytes/test_case.rb +187 -0
- data/lib/ruby_bytes/thor.rb +458 -0
- data/lib/ruby_bytes/version.rb +5 -0
- data/templates/rbytes/core_ext.rb +21 -0
- data/templates/rbytes/rails_actions.rb +387 -0
- data/templates/rbytes/rails_application_stub.rb +21 -0
- data/templates/rbytes/rbytes.rb +29 -0
- metadata +27 -3
- data/lib/rbytes/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53595c57bee23eca0389d923a2b7fec3c67e570c07a4405cc705afe658081a3a
|
4
|
+
data.tar.gz: d96f2d9536487f2432a0f56c9d9407c6caa7f04a2d9e98ecea72111e3e88e9b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bee2dcc4ecf593b7aa7d7289955eb4518dadf74087bafa523880647e193bd2b8d16af2542c27e73f2a6d05fcbd2a73ea5de2fef02bfdd202dc013d4024c147a
|
7
|
+
data.tar.gz: 8f5928ca9ba174fab8e830825f2bad113fbb295cbd84b106cd0364d0519037750c1343acc19b40f1d1152f7ec1d92760180d594730247ff4c16d4133560c6376
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -6,24 +6,186 @@ Ruby Bytes is a tool to build application templates for Ruby and Rails applicati
|
|
6
6
|
|
7
7
|
- Build complex templates consisting of multiple independent components.
|
8
8
|
- Test templates with ease.
|
9
|
+
- Install application templates without Rails.
|
10
|
+
- Publish templates to [RailsBytes][].
|
9
11
|
|
10
|
-
We also provide a GitHub action to deploy
|
12
|
+
We also provide a [GitHub action](#github-action) to compile and deploy templates continuously.
|
11
13
|
|
12
|
-
|
14
|
+
## Examples
|
15
|
+
|
16
|
+
- [ruby-on-whales][]
|
17
|
+
- [view_component-contrib][]
|
18
|
+
|
19
|
+
See also examples in the [templates](https://github.com/palkan/rbytes/tree/master/templates) folder.
|
13
20
|
|
14
21
|
## Installation
|
15
22
|
|
16
|
-
|
23
|
+
To install templates, install the `rbytes` executable via the gem:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
gem install rbytes
|
27
|
+
```
|
28
|
+
|
29
|
+
For templates development, add `rbytes` to your Gemfile or gemspec:
|
17
30
|
|
18
31
|
```ruby
|
19
32
|
# Gemfile
|
20
33
|
gem "rbytes"
|
21
34
|
```
|
22
35
|
|
23
|
-
##
|
36
|
+
## Installing templates
|
37
|
+
|
38
|
+
You can use `rbytes install <url>` similarly to `rails app:template` but without needing to install Rails. It's useful if you want to use a template in a Rails-less environment.
|
39
|
+
|
40
|
+
Usage example:
|
41
|
+
|
42
|
+
```sh
|
43
|
+
$ rbytes install https://railsbytes.com/script/x7msKX
|
44
|
+
|
45
|
+
Run template from: https://railsbytes.com/script/x7msKX
|
46
|
+
apply https://railsbytes.com/script/x7msKX
|
47
|
+
hello world from https://railsbytes.com 👋
|
48
|
+
```
|
49
|
+
|
50
|
+
**IMPORTANT**: Not all templates from RailsBytes may be supported as of yet. Please, let us know if you find incompatibilities with `rails app:template`, so we can fix them.
|
51
|
+
|
52
|
+
You can also install Ruby Bytes as a plugin for Thor (see [Thor integration](#thor-integration)).
|
53
|
+
|
54
|
+
## Writing templates
|
55
|
+
|
56
|
+
> The quickes way to get started with using Ruby Bytes to build templates is to use [our generator](templates/generator/).
|
57
|
+
|
58
|
+
Ruby Bytes adds partial support to Thor/Rails templates. For that, you can use `#include` and `#render` methods:
|
59
|
+
|
60
|
+
```erb
|
61
|
+
say "Welcome to a custom Rails template!"
|
62
|
+
|
63
|
+
<%= include "setup_gems" %>
|
64
|
+
|
65
|
+
file "config/initializers/my-gem.rb", <%= code("initializer.rb") %>
|
66
|
+
```
|
67
|
+
|
68
|
+
The `#include` helper simply injects the contents of the partial into the resulting file.
|
69
|
+
|
70
|
+
The `#code` method allows you to inject dynamic contents depending on the local variables defined. For example, given the following template and a partial:
|
71
|
+
|
72
|
+
```erb
|
73
|
+
# _anycable.yml.tt
|
74
|
+
development:
|
75
|
+
broadcast_adapter: <%= cable_adapter %>
|
76
|
+
|
77
|
+
# template.rb
|
78
|
+
cable_adapter = ask? "Which AnyCable pub/sub adapter do you want to use?"
|
79
|
+
|
80
|
+
file "config/anycable.yml", <%= code("anycable.yml") %>
|
81
|
+
```
|
82
|
+
|
83
|
+
The compiled template will like like this:
|
84
|
+
|
85
|
+
```erb
|
86
|
+
cable_adapter = ask? "Which AnyCable pub/sub adapter do you want to use?"
|
87
|
+
|
88
|
+
file "config/anycable.yml", ERB.new(
|
89
|
+
*[
|
90
|
+
<<~'CODE'
|
91
|
+
development:
|
92
|
+
broadcast_adapter: <%= cable_adapter %>
|
93
|
+
CODE
|
94
|
+
], trim_mode: "<>").result(binding)
|
95
|
+
```
|
96
|
+
|
97
|
+
**NOTE:** By default, we assume that partials are stored next to the template's entrypoint. Partials may have "_" prefix and ".rb"/".tt" suffixes.
|
98
|
+
|
99
|
+
### Compiling templates
|
100
|
+
|
101
|
+
You can compile a template by using the `rbytes` executable:
|
102
|
+
|
103
|
+
```sh
|
104
|
+
$ rbytes compile path/to/template
|
105
|
+
|
106
|
+
<compiled template>
|
107
|
+
```
|
108
|
+
|
109
|
+
You can also specify a custom partials directory:
|
110
|
+
|
111
|
+
```sh
|
112
|
+
rbytes compile path/to/template --root=path/to/partials
|
113
|
+
```
|
24
114
|
|
25
115
|
### Testing
|
26
116
|
|
117
|
+
We provide a Minitest integration to test your templates.
|
118
|
+
|
119
|
+
Here is an example usage:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
require "ruby_bytes/test_case"
|
123
|
+
|
124
|
+
class TemplateTest < RubyBytes::TestCasee
|
125
|
+
# Specify root path for your template (for partials lookup)
|
126
|
+
root File.join(__dir__, "../template")
|
127
|
+
|
128
|
+
# You can test partials in isolation by declaring a custom template
|
129
|
+
template <<~RUBY
|
130
|
+
say "Hello from some partial"
|
131
|
+
<%= include "some_partial" %>
|
132
|
+
RUBY
|
133
|
+
|
134
|
+
def test_some_partial
|
135
|
+
run_generator do |output|
|
136
|
+
assert_file "application.rb"
|
137
|
+
|
138
|
+
assert_file_contains(
|
139
|
+
"application.rb",
|
140
|
+
<<~CODE
|
141
|
+
module Rails
|
142
|
+
class << self
|
143
|
+
def application
|
144
|
+
CODE
|
145
|
+
)
|
146
|
+
|
147
|
+
refute_file_contains(
|
148
|
+
"application.rb",
|
149
|
+
"Nothing"
|
150
|
+
)
|
151
|
+
|
152
|
+
assert_line_printed output, "Hello from some partial"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
If you use prompt in your templates, you can prepopulate standard input:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
class TemplateTest < RubyBytes::TestCasee
|
162
|
+
# Specify root path for your template (for partials lookup)
|
163
|
+
root File.join(__dir__, "../template")
|
164
|
+
|
165
|
+
# You can test partials in isolation by declaring a custom template
|
166
|
+
template <<~RUBY
|
167
|
+
say "Hello from some partial"
|
168
|
+
if yes?("Do you write tests?")
|
169
|
+
say "Gut"
|
170
|
+
else
|
171
|
+
say "Why not?"
|
172
|
+
end
|
173
|
+
RUBY
|
174
|
+
|
175
|
+
def test_prompt_yes
|
176
|
+
run_generator(input: ["y"]) do |output|
|
177
|
+
assert_line_printed output, "Gut"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_prompt_no
|
182
|
+
run_generator(input: ["n"]) do |output|
|
183
|
+
assert_line_printed output, "Why not?"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
27
189
|
## Thor integration
|
28
190
|
|
29
191
|
We provide a custom Thor command, which can be used to apply templates (similar to `rails app:template`).
|
@@ -46,6 +208,43 @@ Run template from: https://railsbytes.com/script/x7msKX
|
|
46
208
|
hello world from https://railsbytes.com 👋
|
47
209
|
```
|
48
210
|
|
211
|
+
## GitHub action
|
212
|
+
|
213
|
+
You can use our GitHub action to deploy your templates to RailsBytes.
|
214
|
+
|
215
|
+
Here is an example:
|
216
|
+
|
217
|
+
```yml
|
218
|
+
name: Publish
|
219
|
+
|
220
|
+
on:
|
221
|
+
push:
|
222
|
+
tags:
|
223
|
+
- v*
|
224
|
+
workflow_dispatch:
|
225
|
+
|
226
|
+
jobs:
|
227
|
+
publish:
|
228
|
+
uses: palkan/rbytes/.github/workflows/railsbytes.yml@master
|
229
|
+
with:
|
230
|
+
template: templates/my-template.rb
|
231
|
+
secrets:
|
232
|
+
RAILS_BYTES_ACCOUNT_ID: "${{ secrets.RAILS_BYTES_ACCOUNT_ID }}"
|
233
|
+
RAILS_BYTES_TOKEN: "${{ secrets.RAILS_BYTES_TOKEN }}"
|
234
|
+
RAILS_BYTES_TEMPLATE_ID: "${{ secrets.RAILS_TEMPLATE_ID }}"
|
235
|
+
```
|
236
|
+
|
237
|
+
## Publishing manually
|
238
|
+
|
239
|
+
You can use the `rbytes publish` command to compile and publish a template to RailsBytes:
|
240
|
+
|
241
|
+
```sh
|
242
|
+
RAILS_BYTES_ACCOUNT_ID=aaa \
|
243
|
+
RAILS_BYTES_TOKEN=bbb \
|
244
|
+
RAILS_BYTES_TEMPLATE_ID=ccc \
|
245
|
+
rbytes publish path/to/template
|
246
|
+
```
|
247
|
+
|
49
248
|
## Contributing
|
50
249
|
|
51
250
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/rbytes](https://github.com/palkan/rbytes).
|
@@ -59,3 +258,5 @@ This gem is generated via [new-gem-generator](https://github.com/palkan/new-gem-
|
|
59
258
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
60
259
|
|
61
260
|
[RailsBytes]: https://railsbytes.com
|
261
|
+
[ruby-on-whales]: https://github.com/evilmartians/ruby-on-whales
|
262
|
+
[view_component-contrib]: https://github.com/palkan/view_component-contrib
|
data/bin/rbytes
ADDED
data/lib/rbytes.rb
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbytes"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
module RubyBytes
|
7
|
+
class CLI
|
8
|
+
COMMANDS = %w[
|
9
|
+
compile
|
10
|
+
publish
|
11
|
+
install
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
def run(command, *args)
|
15
|
+
raise ArgumentError, "Unknown command: #{command}\nAvailable commands are: #{COMMANDS.join(", ")}\nRuby Bytes: v#{RubyBytes::VERSION}" unless COMMANDS.include?(command)
|
16
|
+
|
17
|
+
public_send(command, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile(*args)
|
21
|
+
root = nil # rubocop:disable Lint/UselessAssignment
|
22
|
+
|
23
|
+
path, args = *args
|
24
|
+
|
25
|
+
OptionParser.new do |o|
|
26
|
+
o.on "-v", "--version", "Print version and exit" do |_arg|
|
27
|
+
$stdout.puts "Ruby Bytes: v#{RubyBytes::VERSION}"
|
28
|
+
exit(0)
|
29
|
+
end
|
30
|
+
|
31
|
+
o.on "--root [DIR]", "Location of partial template files" do
|
32
|
+
raise ArgumentError, "Directory not found: #{_1}" unless File.directory?(_1)
|
33
|
+
root = _1
|
34
|
+
end
|
35
|
+
|
36
|
+
o.on_tail "-h", "--help", "Show help" do
|
37
|
+
$stdout.puts <<~USAGE
|
38
|
+
rbytes compile PATH [options]
|
39
|
+
|
40
|
+
Options:
|
41
|
+
--root DIR Location of partial template files
|
42
|
+
USAGE
|
43
|
+
|
44
|
+
exit(0)
|
45
|
+
end
|
46
|
+
end.parse!(args || [])
|
47
|
+
|
48
|
+
raise ArgumentError, "File not found: #{path}" unless File.file?(path)
|
49
|
+
|
50
|
+
$stdout.puts Compiler.new(path, root: root).render
|
51
|
+
end
|
52
|
+
|
53
|
+
def publish(*args)
|
54
|
+
root = nil # rubocop:disable Lint/UselessAssignment
|
55
|
+
|
56
|
+
path, args = *args
|
57
|
+
|
58
|
+
OptionParser.new do |o|
|
59
|
+
o.on "-v", "--version", "Print version and exit" do |_arg|
|
60
|
+
$stdout.puts "Ruby Bytes: v#{RubyBytes::VERSION}"
|
61
|
+
exit(0)
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on "--root [DIR]", "Location of partial template files" do
|
65
|
+
raise ArgumentError, "Directory not found: #{_1}" unless File.directory?(_1)
|
66
|
+
root = _1
|
67
|
+
end
|
68
|
+
|
69
|
+
o.on_tail "-h", "--help", "Show help" do
|
70
|
+
$stdout.puts <<~USAGE
|
71
|
+
rbytes publish PATH [options]
|
72
|
+
|
73
|
+
Options:
|
74
|
+
--root DIR Location of partial template files
|
75
|
+
USAGE
|
76
|
+
|
77
|
+
exit(0)
|
78
|
+
end
|
79
|
+
end.parse!(args || [])
|
80
|
+
|
81
|
+
raise ArgumentError, "File not found: #{path}" unless File.file?(path)
|
82
|
+
|
83
|
+
contents = Compiler.new(path, root: root).render
|
84
|
+
|
85
|
+
Publisher.new.call(contents)
|
86
|
+
|
87
|
+
$stdout.puts "Published successfully ✅"
|
88
|
+
end
|
89
|
+
|
90
|
+
def install(*args)
|
91
|
+
url, args = *args
|
92
|
+
|
93
|
+
OptionParser.new do |o|
|
94
|
+
o.on "-v", "--version", "Print version and exit" do |_arg|
|
95
|
+
$stdout.puts "Ruby Bytes: v#{RubyBytes::VERSION}"
|
96
|
+
exit(0)
|
97
|
+
end
|
98
|
+
|
99
|
+
o.on_tail "-h", "--help", "Show help" do
|
100
|
+
$stdout.puts <<~USAGE
|
101
|
+
rbytes install URL
|
102
|
+
USAGE
|
103
|
+
|
104
|
+
exit(0)
|
105
|
+
end
|
106
|
+
end.parse!(args || [])
|
107
|
+
|
108
|
+
raise ArgumentError, "Template URL or location must be provided" unless url
|
109
|
+
|
110
|
+
require "thor"
|
111
|
+
require "ruby_bytes/thor"
|
112
|
+
|
113
|
+
Rbytes::Base.source_paths << Dir.pwd
|
114
|
+
Rbytes.new.template(url)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module RubyBytes
|
6
|
+
class Compiler
|
7
|
+
attr_reader :path, :template, :root
|
8
|
+
|
9
|
+
def initialize(path, root: nil)
|
10
|
+
@path = path
|
11
|
+
raise ArgumentError, "There is no file at the path: #{path}" unless File.file?(path)
|
12
|
+
|
13
|
+
@template = File.read(path)
|
14
|
+
@root = root || File.dirname(File.expand_path(path))
|
15
|
+
end
|
16
|
+
|
17
|
+
def render(contents = template)
|
18
|
+
ERB.new(contents, trim_mode: "<>").result(binding)
|
19
|
+
end
|
20
|
+
|
21
|
+
def code(path)
|
22
|
+
contents = File.read(resolve_path(path))
|
23
|
+
%(ERB.new(
|
24
|
+
*[
|
25
|
+
<<~'TCODE'
|
26
|
+
#{contents}
|
27
|
+
TCODE
|
28
|
+
], trim_mode: "<>").result(binding))
|
29
|
+
end
|
30
|
+
|
31
|
+
def include(path, indent: 0)
|
32
|
+
indented(render(File.read(resolve_path(path))), indent)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
PATH_CANDIDATES = [
|
38
|
+
"%{path}",
|
39
|
+
"_%{path}",
|
40
|
+
"%{path}.rb",
|
41
|
+
"_%{path}.rb",
|
42
|
+
"%{path}.tt",
|
43
|
+
"_%{path}.tt"
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
def resolve_path(path)
|
47
|
+
PATH_CANDIDATES.each do |pattern|
|
48
|
+
resolved = File.join(root, pattern % {path: path})
|
49
|
+
return resolved if File.file?(resolved)
|
50
|
+
end
|
51
|
+
|
52
|
+
raise "File not found: #{path}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def indented(content, multiplier = 2) # :doc:
|
56
|
+
spaces = " " * multiplier
|
57
|
+
content.each_line.map { |line| (!line.match?(/\S/)) ? line : "#{spaces}#{line}" }.join
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyBytes
|
4
|
+
class Publisher
|
5
|
+
attr_reader :account_id, :token, :template_id
|
6
|
+
|
7
|
+
def initialize(
|
8
|
+
account_id: ENV.fetch("RAILS_BYTES_ACCOUNT_ID"),
|
9
|
+
token: ENV.fetch("RAILS_BYTES_TOKEN"),
|
10
|
+
template_id: ENV.fetch("RAILS_BYTES_TEMPLATE_ID")
|
11
|
+
)
|
12
|
+
@account_id = account_id
|
13
|
+
@token = token
|
14
|
+
@template_id = template_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(template)
|
18
|
+
require "net/http"
|
19
|
+
require "json"
|
20
|
+
|
21
|
+
path = "/api/v1/accounts/#{account_id}/templates/#{template_id}.json"
|
22
|
+
data = JSON.dump(script: template)
|
23
|
+
|
24
|
+
Net::HTTP.start("railsbytes.com", 443, use_ssl: true) do |http|
|
25
|
+
http.patch(
|
26
|
+
path,
|
27
|
+
data,
|
28
|
+
{
|
29
|
+
"Content-Type" => "application/json",
|
30
|
+
"Authorization" => "Bearer GGpxArFDSa2x3MT4BQNcyxG6"
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end.then do |response|
|
34
|
+
raise "Failed to publish template: #{response.code} — #{response.message}" unless response.code == "200"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "minitest"
|
4
|
+
require "fileutils"
|
5
|
+
require "stringio"
|
6
|
+
require "thor"
|
7
|
+
|
8
|
+
require "ruby_bytes/thor"
|
9
|
+
|
10
|
+
module RubyBytes
|
11
|
+
class TestCase < Minitest::Test
|
12
|
+
TMP_DIR = File.join(Dir.pwd, "tmp", "rbytes_test")
|
13
|
+
|
14
|
+
Rbytes::Base.source_paths << TMP_DIR
|
15
|
+
|
16
|
+
# Patch Thor::LineEditor to use Basic in tests
|
17
|
+
$rbytes_testing = false
|
18
|
+
|
19
|
+
Thor::LineEditor.singleton_class.prepend(Module.new do
|
20
|
+
def best_available
|
21
|
+
return super unless $rbytes_testing
|
22
|
+
|
23
|
+
Thor::LineEditor::Basic
|
24
|
+
end
|
25
|
+
end)
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_reader :template_contents
|
29
|
+
|
30
|
+
def destination_root(val = nil)
|
31
|
+
if val
|
32
|
+
@destination_root = val
|
33
|
+
end
|
34
|
+
|
35
|
+
return @destination_root if instance_variable_defined?(:@destination_root)
|
36
|
+
|
37
|
+
@destination_root =
|
38
|
+
if superclass.respond_to?(:destination_root)
|
39
|
+
superclass.destination_root
|
40
|
+
else
|
41
|
+
TMP_DIR
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def root(val = nil)
|
46
|
+
if val
|
47
|
+
@root = val
|
48
|
+
end
|
49
|
+
|
50
|
+
return @root if instance_variable_defined?(:@root)
|
51
|
+
|
52
|
+
@root =
|
53
|
+
if superclass.respond_to?(:root)
|
54
|
+
superclass.root
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Set the path to dummy app.
|
59
|
+
# Dummy app is copied to the temporary directory for every run
|
60
|
+
# and set as a destination root.
|
61
|
+
def dummy_app(val = nil)
|
62
|
+
if val
|
63
|
+
@dummy_app = val
|
64
|
+
end
|
65
|
+
|
66
|
+
return @dummy_app if instance_variable_defined?(:@dummy_app)
|
67
|
+
|
68
|
+
@dummy_app =
|
69
|
+
if superclass.respond_to?(:dummy_app)
|
70
|
+
superclass.dummy_app
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def template(contents)
|
75
|
+
@template_contents = contents
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
attr_accessor :destination
|
80
|
+
|
81
|
+
def setup
|
82
|
+
FileUtils.rm_rf(TMP_DIR) if File.directory?(TMP_DIR)
|
83
|
+
FileUtils.mkdir_p(TMP_DIR)
|
84
|
+
end
|
85
|
+
|
86
|
+
def prepare_dummy
|
87
|
+
# Then, copy the dummy app if any
|
88
|
+
dummy = self.class.dummy_app
|
89
|
+
return unless dummy
|
90
|
+
|
91
|
+
return if @dummy_prepared
|
92
|
+
|
93
|
+
raise ArgumentError, "Dummy app must be a directory" unless File.directory?(dummy)
|
94
|
+
|
95
|
+
tmp_dummy_path = File.join(TMP_DIR, "dummy")
|
96
|
+
FileUtils.rm_rf(tmp_dummy_path) if File.directory?(tmp_dummy_path)
|
97
|
+
FileUtils.cp_r(dummy, tmp_dummy_path)
|
98
|
+
self.destination = tmp_dummy_path
|
99
|
+
|
100
|
+
if block_given?
|
101
|
+
Dir.chdir(tmp_dummy_path) { yield }
|
102
|
+
end
|
103
|
+
|
104
|
+
@dummy_prepared = true
|
105
|
+
end
|
106
|
+
|
107
|
+
def run_generator(input: [])
|
108
|
+
# First, compile the template (if not yet)
|
109
|
+
path = File.join(TMP_DIR, "current_template.rb")
|
110
|
+
|
111
|
+
if File.file?(path)
|
112
|
+
File.delete(path)
|
113
|
+
end
|
114
|
+
|
115
|
+
File.write(path, "")
|
116
|
+
|
117
|
+
rendered = Compiler.new(path, root: self.class.root).render(self.class.template_contents)
|
118
|
+
File.write(path, rendered)
|
119
|
+
|
120
|
+
self.destination = self.class.destination_root
|
121
|
+
|
122
|
+
prepare_dummy
|
123
|
+
|
124
|
+
original_stdout = $stdout
|
125
|
+
original_stdin = $stdin
|
126
|
+
|
127
|
+
if input.size > 0
|
128
|
+
$rbytes_testing = true
|
129
|
+
|
130
|
+
io = StringIO.new
|
131
|
+
input.each { io.puts(_1) }
|
132
|
+
io.rewind
|
133
|
+
$stdin = io
|
134
|
+
$stdin.sync = true
|
135
|
+
end
|
136
|
+
|
137
|
+
$stdout = StringIO.new
|
138
|
+
$stdout.sync = true
|
139
|
+
|
140
|
+
begin
|
141
|
+
Dir.chdir(destination) do
|
142
|
+
Rbytes::Base.new(
|
143
|
+
[destination], {}, {destination_root: destination}
|
144
|
+
).apply("current_template.rb")
|
145
|
+
end
|
146
|
+
yield $stdout.string if block_given?
|
147
|
+
ensure
|
148
|
+
$stdout = original_stdout
|
149
|
+
$stdin = original_stdin
|
150
|
+
$rbytes_testing = false
|
151
|
+
@dummy_prepared = false
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def assert_line_printed(io, line)
|
156
|
+
lines = io.lines
|
157
|
+
|
158
|
+
assert lines.any? { _1.include?(line) }, "Expected to print line: #{line}. Got: #{io}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def assert_file_contains(path, body)
|
162
|
+
fullpath = File.join(destination, path)
|
163
|
+
assert File.file?(fullpath), "File not found: #{path}"
|
164
|
+
|
165
|
+
actual = File.read(fullpath)
|
166
|
+
assert_includes actual, body
|
167
|
+
end
|
168
|
+
|
169
|
+
def assert_file(path)
|
170
|
+
fullpath = File.join(destination, path)
|
171
|
+
assert File.file?(fullpath), "File not found: #{path}"
|
172
|
+
end
|
173
|
+
|
174
|
+
def refute_file(path)
|
175
|
+
fullpath = File.join(destination, path)
|
176
|
+
refute File.file?(fullpath), "File must not exist: #{path}"
|
177
|
+
end
|
178
|
+
|
179
|
+
def refute_file_contains(path, body)
|
180
|
+
fullpath = File.join(destination, path)
|
181
|
+
assert File.file?(fullpath), "File not found: #{path}"
|
182
|
+
|
183
|
+
actual = File.read(fullpath)
|
184
|
+
refute_includes actual, body
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|