grsx 0.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 +7 -0
- data/.github/workflows/main.yml +20 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Appraisals +17 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +8 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +274 -0
- data/Guardfile +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +437 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/test +43 -0
- data/docker-compose.yml +29 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_6_1.gemfile +8 -0
- data/gemfiles/rails_6_1.gemfile.lock +260 -0
- data/gemfiles/rails_7_0.gemfile +7 -0
- data/gemfiles/rails_7_0.gemfile.lock +265 -0
- data/gemfiles/rails_7_1.gemfile +7 -0
- data/gemfiles/rails_7_1.gemfile.lock +295 -0
- data/gemfiles/rails_7_2.gemfile +7 -0
- data/gemfiles/rails_7_2.gemfile.lock +290 -0
- data/gemfiles/rails_8_0.gemfile +8 -0
- data/gemfiles/rails_8_0.gemfile.lock +344 -0
- data/gemfiles/rails_8_1.gemfile +8 -0
- data/gemfiles/rails_8_1.gemfile.lock +313 -0
- data/gemfiles/rails_master.gemfile +7 -0
- data/gemfiles/rails_master.gemfile.lock +296 -0
- data/grsx.gemspec +43 -0
- data/lib/generators/grsx/phlex_component/phlex_component_generator.rb +40 -0
- data/lib/generators/grsx/phlex_component/templates/component.rb.tt +12 -0
- data/lib/generators/grsx/phlex_component/templates/component.rbx.tt +6 -0
- data/lib/grsx/component_resolver.rb +64 -0
- data/lib/grsx/configuration.rb +14 -0
- data/lib/grsx/lexer.rb +325 -0
- data/lib/grsx/nodes/abstract_attr.rb +12 -0
- data/lib/grsx/nodes/abstract_element.rb +13 -0
- data/lib/grsx/nodes/abstract_node.rb +31 -0
- data/lib/grsx/nodes/component_element.rb +69 -0
- data/lib/grsx/nodes/component_prop.rb +29 -0
- data/lib/grsx/nodes/declaration.rb +15 -0
- data/lib/grsx/nodes/expression.rb +15 -0
- data/lib/grsx/nodes/expression_group.rb +15 -0
- data/lib/grsx/nodes/fragment.rb +30 -0
- data/lib/grsx/nodes/html_attr.rb +13 -0
- data/lib/grsx/nodes/html_element.rb +49 -0
- data/lib/grsx/nodes/newline.rb +9 -0
- data/lib/grsx/nodes/raw.rb +23 -0
- data/lib/grsx/nodes/root.rb +19 -0
- data/lib/grsx/nodes/text.rb +15 -0
- data/lib/grsx/nodes/util.rb +9 -0
- data/lib/grsx/nodes.rb +20 -0
- data/lib/grsx/parser.rb +238 -0
- data/lib/grsx/phlex_compiler.rb +223 -0
- data/lib/grsx/phlex_component.rb +361 -0
- data/lib/grsx/phlex_runtime.rb +70 -0
- data/lib/grsx/prop_inspector.rb +52 -0
- data/lib/grsx/rails/engine.rb +24 -0
- data/lib/grsx/rails/phlex_reloader.rb +25 -0
- data/lib/grsx/template.rb +12 -0
- data/lib/grsx/version.rb +3 -0
- data/lib/grsx.rb +35 -0
- metadata +324 -0
data/README.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# A Ruby template language inspired by JSX
|
|
2
|
+
|
|
3
|
+
[](https://github.com/patbenatar/grsx/actions?query=branch%3Amaster)
|
|
4
|
+
|
|
5
|
+
* [Getting Started](#getting-started-with-rails)
|
|
6
|
+
* [Template Syntax](#template-syntax)
|
|
7
|
+
* [Components](#components)
|
|
8
|
+
* [`Grsx::Component`](#grsxcomponent)
|
|
9
|
+
* [Usage with any component library](#usage-with-any-component-library)
|
|
10
|
+
* [Fragment caching in Rails](#fragment-caching-in-rails)
|
|
11
|
+
* [Advanced](#advanced)
|
|
12
|
+
* [Component resolution](#component-resolution)
|
|
13
|
+
* [AST Transforms](#ast-transforms)
|
|
14
|
+
* [Usage outside of Rails](#usage-outside-of-rails)
|
|
15
|
+
|
|
16
|
+
## Manifesto
|
|
17
|
+
|
|
18
|
+
Love JSX and component-based frontends, but sick of paying the costs of SPA development? Grsx brings the elegance of JSX—operating on HTML elements and custom components with an interchangeable syntax—to the world of Rails server-rendered apps.
|
|
19
|
+
|
|
20
|
+
Combine this with CSS Modules in your Webpacker PostCSS pipeline and you'll have a first-class frontend development experience while maintaining the development efficiency of Rails.
|
|
21
|
+
|
|
22
|
+
_But what about Javascript and client-side behavior?_ You probably don't need as much of it as you think you do. See how far you can get with layering RailsUJS, vanilla JS, Turbolinks, and/or StimulusJS onto your server-rendered components. I think you'll be pleasantly surprised with the modern UX you're able to build while writing and maintaining less code.
|
|
23
|
+
|
|
24
|
+
## Example
|
|
25
|
+
|
|
26
|
+
Use your custom Ruby class components from `.rbx` templates just like you would React components in JSX:
|
|
27
|
+
|
|
28
|
+
```jsx
|
|
29
|
+
<body>
|
|
30
|
+
<Hero size="fullscreen" {**splat_some_attributes}>
|
|
31
|
+
<h1>Hello {@name}</h1>
|
|
32
|
+
<p>Welcome to grsx, marrying the nice parts of React templating with the development efficiency of Rails server-rendered apps.</p>
|
|
33
|
+
<Button to={about_path}>Learn more</Button>
|
|
34
|
+
</Hero>
|
|
35
|
+
</body>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
after defining them in Ruby:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
class HeroComponent < Grsx::Component # or use ViewComponent, or another component lib
|
|
42
|
+
def setup(size:)
|
|
43
|
+
@size = size
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ButtonComponent < Grsx::Component
|
|
48
|
+
def setup(to:)
|
|
49
|
+
@to = to
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
with their accompying template files (also can be `.rbx`!), scoped scss files, JS and other assets (not shown).
|
|
55
|
+
|
|
56
|
+
## Getting Started (with Rails)
|
|
57
|
+
|
|
58
|
+
Add it to your Gemfile and `bundle install`:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
gem "grsx"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
_From 1.0 onward, we only support Rails 6. If you're using Rails 5, use the 0.x releases._
|
|
65
|
+
|
|
66
|
+
_Not using Rails? See "Usage outside of Rails" below._
|
|
67
|
+
|
|
68
|
+
Create your first component at `app/components/hello_world_component.rb`:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
class HelloWorldComponent < Grsx::Component
|
|
72
|
+
def setup(name:)
|
|
73
|
+
@name = name
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
With a template `app/components/hello_world_component.rbx`:
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
<div>
|
|
82
|
+
<h1>Hello {@name}</h1>
|
|
83
|
+
{content}
|
|
84
|
+
</div>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Add a controller, action, route, and `rbx` view like `app/views/hello_worlds/index.rbx`:
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
<HelloWorld name="Nick">
|
|
91
|
+
<p>Welcome to the world of component-based frontend development in Rails!</p>
|
|
92
|
+
</HelloWorld>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Fire up `rails s`, navigate to your route, and you should see Grsx in action!
|
|
96
|
+
|
|
97
|
+
## Template Syntax
|
|
98
|
+
|
|
99
|
+
You can use Ruby code within brackets:
|
|
100
|
+
|
|
101
|
+
```jsx
|
|
102
|
+
<p class={@dynamic_class}>
|
|
103
|
+
Hello {"world".upcase}
|
|
104
|
+
</p>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
You can splat a hash into attributes:
|
|
108
|
+
|
|
109
|
+
```jsx
|
|
110
|
+
<div {**{class: "myClass"}} {**@more_attrs}></div>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
You can use HTML or component tags within expressions. e.g. to conditionalize a template:
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
<div>
|
|
117
|
+
{some_boolean && <h1>Welcome</h1>}
|
|
118
|
+
{another_boolean ? <p>Option One</p> : <p>Option Two</p>}
|
|
119
|
+
</div>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Or in loops:
|
|
123
|
+
|
|
124
|
+
```jsx
|
|
125
|
+
<ul>
|
|
126
|
+
{[1, 2, 3].map { |n| <li>{n}</li> }}
|
|
127
|
+
</ul>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Blocks:
|
|
131
|
+
|
|
132
|
+
```jsx
|
|
133
|
+
{link_to "/" do
|
|
134
|
+
<span>Click me</span>
|
|
135
|
+
end}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Pass a tag to a component as an attribute:
|
|
139
|
+
|
|
140
|
+
```jsx
|
|
141
|
+
<Hero title={<h1>Hello World</h1>}>
|
|
142
|
+
Content here...
|
|
143
|
+
</Hero>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Or pass a lambda as an attribute, that when called returns a tag:
|
|
147
|
+
|
|
148
|
+
```jsx
|
|
149
|
+
<Hero title={-> { <h1>Hello World</h1> }}>
|
|
150
|
+
Content here...
|
|
151
|
+
</Hero>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
_Note that when using tags inside blocks, the block must evaluate to a single root element. Grsx behaves similar to JSX in this way. E.g.:_
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
# Do
|
|
158
|
+
-> { <span><i>Hello</i> World</span> }
|
|
159
|
+
|
|
160
|
+
# Don't
|
|
161
|
+
-> { <i>Hello</i> World }
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Start a line with `#` to leave a comment:
|
|
165
|
+
|
|
166
|
+
```jsx
|
|
167
|
+
# Private note to self that won't be rendered in the final HTML
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Components
|
|
171
|
+
|
|
172
|
+
You can use Ruby classes as components alongside standard HTML tags:
|
|
173
|
+
|
|
174
|
+
```jsx
|
|
175
|
+
<div>
|
|
176
|
+
<PageHeader title="Welcome" />
|
|
177
|
+
<PageBody>
|
|
178
|
+
<p>To the world of custom components</p>
|
|
179
|
+
</PageBody>
|
|
180
|
+
</div>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
By default, Grsx will resolve `PageHeader` to a Ruby class called `PageHeaderComponent` and render it with the view context, attributes, and its children: `PageHeaderComponent.new(self, title: "Welcome").render_in(self, &block)`. This behavior is customizable, see "Component resolution" below.
|
|
184
|
+
|
|
185
|
+
### `Grsx::Component`
|
|
186
|
+
|
|
187
|
+
We ship with a component superclass that integrates nicely with Rails' ActionView and the controller rendering context. You can use it to easily implement custom components in your Rails app:
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
# app/components/page_header_component.rb
|
|
191
|
+
class PageHeaderComponent < Grsx::Component
|
|
192
|
+
def setup(title:)
|
|
193
|
+
@title = title
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
By default, we'll look for a template file in the same directory as the class and with a matching filename:
|
|
199
|
+
|
|
200
|
+
```jsx
|
|
201
|
+
// app/components/page_header_component.rbx
|
|
202
|
+
<h1>{@title}</h1>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Your components and their templates run in the same context as traditional Rails views, so you have access to all of the view helpers you're used to as well as any custom helpers you've defined in `app/helpers/` or via `helper_method` in your controller.
|
|
206
|
+
|
|
207
|
+
#### Template-less components
|
|
208
|
+
|
|
209
|
+
If you'd prefer to render your components entirely from Ruby, you can do so by implementing `#call`:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class PageHeaderComponent < Grsx::Component
|
|
213
|
+
def setup(title:)
|
|
214
|
+
@title = title
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def call
|
|
218
|
+
tag.h1 @title
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Context
|
|
224
|
+
|
|
225
|
+
`Grsx::Component` implements a similar notion to React's Context API, allowing you to pass data through the component tree without having to pass props down manually.
|
|
226
|
+
|
|
227
|
+
Given a template:
|
|
228
|
+
|
|
229
|
+
```jsx
|
|
230
|
+
<Form>
|
|
231
|
+
<TextField field={:title} />
|
|
232
|
+
</Form>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The form component can use Rails `form_for` and then pass the `form` builder object down to any field components using context:
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
class FormComponent < Grsx::Component
|
|
239
|
+
def setup(form_object:)
|
|
240
|
+
@form_object = form_object
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def call
|
|
244
|
+
form_for @form_object do |form|
|
|
245
|
+
create_context(:form, form)
|
|
246
|
+
content
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
class TextFieldComponent < Grsx::Component
|
|
252
|
+
def setup(field:)
|
|
253
|
+
@field = field
|
|
254
|
+
@form = use_context(:form)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def call
|
|
258
|
+
@form.text_field @field
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Usage with ERB
|
|
264
|
+
|
|
265
|
+
We recommend using `Grsx::Component` with the rbx template language, but if you prefer ERB... a component's template can be `.html.erb` and you can render a component from ERB like so:
|
|
266
|
+
|
|
267
|
+
Rails 6.1:
|
|
268
|
+
|
|
269
|
+
```erb
|
|
270
|
+
<%= render PageHeaderComponent.new(self, title: "Welcome") do %>
|
|
271
|
+
<p>Children...</p>
|
|
272
|
+
<% end >
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Rails 6.0 or earlier:
|
|
276
|
+
|
|
277
|
+
```erb
|
|
278
|
+
<%= PageHeaderComponent.new(self, title: "Welcome").render_in(self) %>
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Usage with any component library
|
|
282
|
+
|
|
283
|
+
You can use the rbx template language with other component libraries like Github's view_component. You just need to tell Grsx how to render the component:
|
|
284
|
+
|
|
285
|
+
```ruby
|
|
286
|
+
# config/initializers/grsx.rb
|
|
287
|
+
Grsx.configure do |config|
|
|
288
|
+
config.component_rendering_templates = {
|
|
289
|
+
children: "{capture{%{children}}}",
|
|
290
|
+
component: "::%{component_class}.new(%{view_context},%{kwargs}).render_in%{children_block}"
|
|
291
|
+
}
|
|
292
|
+
end
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Fragment caching in Rails
|
|
296
|
+
|
|
297
|
+
`.rbx` templates integrate with Rails fragment caching, automatically cachebusting when the template or its render dependencies change.
|
|
298
|
+
|
|
299
|
+
If you're using `Grsx::Component`, you can further benefit from component cachebusting where the fragment cache will be busted if any dependent component's template _or_ class definition changes.
|
|
300
|
+
|
|
301
|
+
And you can use `<Grsx.Cache>`, a convenient wrapper for the Rails fragment cache:
|
|
302
|
+
|
|
303
|
+
```rbx
|
|
304
|
+
<Grsx.Cache key={...}>
|
|
305
|
+
<p>Fragment here...</p>
|
|
306
|
+
<MyButton />
|
|
307
|
+
</Grsx.Cache>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Advanced
|
|
311
|
+
|
|
312
|
+
### Component resolution
|
|
313
|
+
|
|
314
|
+
By default, Grsx resolves component tags to Ruby classes named `#{tag}Component`, e.g.:
|
|
315
|
+
|
|
316
|
+
* `<PageHeader />` => `PageHeaderComponent`
|
|
317
|
+
* `<Admin.Button />` => `Admin::ButtonComponent`
|
|
318
|
+
|
|
319
|
+
You can customize this behavior by providing a custom resolver:
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
# config/initializers/grsx.rb
|
|
323
|
+
Grsx.configure do |config|
|
|
324
|
+
config.element_resolver = MyResolver.new
|
|
325
|
+
end
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Where `MyResolver` implements the following API:
|
|
329
|
+
|
|
330
|
+
* `component?(name: string, template: Grsx::Template) => Boolean`
|
|
331
|
+
* `component_class(name: string, template: Grsx::Template) => T`
|
|
332
|
+
|
|
333
|
+
See `lib/grsx/component_resolver.rb` for an example.
|
|
334
|
+
|
|
335
|
+
#### Auto-namespacing
|
|
336
|
+
|
|
337
|
+
Want to namespace your components but sick of typing `Admin.` in front of every component call? Grsx's default `ComponentResolver` implementation has an option for that:
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
# config/initializers/grsx.rb
|
|
341
|
+
Grsx.configure do |config|
|
|
342
|
+
config.element_resolver.component_namespaces = {
|
|
343
|
+
Rails.root.join("app", "views", "admin") => %w[Admin],
|
|
344
|
+
Rails.root.join("app", "components", "admin") => %w[Admin]
|
|
345
|
+
}
|
|
346
|
+
end
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Now any calls to `<Button>` made from `.rbx` views within `app/views/admin/` or from component templates within `app/components/admin/` will first check for `Admin::ButtonComponent` before `ButtonComponent`.
|
|
350
|
+
|
|
351
|
+
### AST Transforms
|
|
352
|
+
|
|
353
|
+
You can hook into Grsx's compilation process to mutate the abstract syntax tree. This is both useful and dangerous, so use with caution.
|
|
354
|
+
|
|
355
|
+
An example use case is automatically scoping CSS class names if you're using something like CSS Modules. Here's an oversimplified example of this:
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
# config/initializers/grsx.rb
|
|
359
|
+
Grsx.configure do |config|
|
|
360
|
+
config.transforms.register(Grsx::Nodes::HTMLAttr) do |node, context|
|
|
361
|
+
if node.name == "class"
|
|
362
|
+
class_list = node.value.split(" ")
|
|
363
|
+
node.value.content = scope_names(class_list, scope: context.template.identifier)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Usage outside of Rails
|
|
370
|
+
|
|
371
|
+
Grsx compiles your template into ruby code, which you can then execute in any context you like. Subclass `Grsx::Runtime` to add methods and instance variables that you'd like to make available to your template.
|
|
372
|
+
|
|
373
|
+
```ruby
|
|
374
|
+
class MyRuntime < Grsx::Runtime
|
|
375
|
+
def initialize
|
|
376
|
+
super
|
|
377
|
+
@an_ivar = "Ivar value"
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def a_method
|
|
381
|
+
"Method value"
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
Grsx.evaluate("<p class={a_method}>{@an_ivar}</p>", MyRuntime.new)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Development
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
docker-compose build
|
|
392
|
+
docker-compose run grsx bin/test
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Or auto-run tests with guard if you prefer:
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
docker-compose run grsx guard
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
If you want to run against the supported versions of Rails, use
|
|
402
|
+
Appraisal:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
docker-compose run grsx bundle exec appraisal bin/test
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
When updating dependency versions in gemspec, you also need to regenerate the appraisal gemspecs with:
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
docker-compose run grsx bundle exec appraisal install
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Debugging TemplatePath methods being called
|
|
415
|
+
When a new version of Rails is released, we need to check what methods are being
|
|
416
|
+
called on `Grsx::Component::TemplatePath` to make sure we always return
|
|
417
|
+
a TemplatePath, not a string due to how we handle `TemplatePath`s
|
|
418
|
+
internally.
|
|
419
|
+
|
|
420
|
+
To list all methods being called, enable `GRSX_TEMPLATE_PATH_DEBUG` and
|
|
421
|
+
run tests:
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
docker-compose run -e GRSX_TEMPLATE_PATH_DEBUG=1 grsx bundle exec appraisal bin/test
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## Contributing
|
|
428
|
+
|
|
429
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/patbenatar/grsx. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/patbenatar/grsx/blob/master/CODE_OF_CONDUCT.md).
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
434
|
+
|
|
435
|
+
## Code of Conduct
|
|
436
|
+
|
|
437
|
+
Everyone interacting in the Grsx project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/patbenatar/grsx/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "rbexy"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/bin/test
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
declare -i RESULT=0
|
|
4
|
+
|
|
5
|
+
echo "Running main suite..."
|
|
6
|
+
|
|
7
|
+
bundle exec rspec
|
|
8
|
+
RESULT+=$?
|
|
9
|
+
|
|
10
|
+
echo "Running initial caching specs to test cold cache behavior..."
|
|
11
|
+
|
|
12
|
+
bundle exec rspec spec/integration/caching/before_changes_spec.rb
|
|
13
|
+
RESULT+=$?
|
|
14
|
+
|
|
15
|
+
echo "Making template source changes to allow testing of cache-busting..."
|
|
16
|
+
|
|
17
|
+
templates=(
|
|
18
|
+
"spec/dummy/app/views/caching/inline.rbx"
|
|
19
|
+
"spec/dummy/app/components/cached_thing_component.rbx"
|
|
20
|
+
"spec/dummy/app/components/cached_class_thing_component.rb"
|
|
21
|
+
"spec/dummy/app/components/cached_thing_call_component.rb"
|
|
22
|
+
"spec/dummy/app/views/caching/_partial_render_partial.rbx"
|
|
23
|
+
)
|
|
24
|
+
for i in "${templates[@]}"
|
|
25
|
+
do
|
|
26
|
+
mv $i $i.original
|
|
27
|
+
mv $i.changed $i
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
echo "Running subsequent caching specs to test cache-busting of warm cache..."
|
|
31
|
+
|
|
32
|
+
bundle exec rspec spec/integration/caching/after_changes_spec.rb
|
|
33
|
+
RESULT+=$?
|
|
34
|
+
|
|
35
|
+
echo "Cleaning up..."
|
|
36
|
+
|
|
37
|
+
for i in "${templates[@]}"
|
|
38
|
+
do
|
|
39
|
+
mv $i $i.changed
|
|
40
|
+
mv $i.original $i
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
exit $RESULT
|
data/docker-compose.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
version: '3'
|
|
2
|
+
|
|
3
|
+
volumes:
|
|
4
|
+
bundle:
|
|
5
|
+
|
|
6
|
+
services:
|
|
7
|
+
rbexy:
|
|
8
|
+
build: .
|
|
9
|
+
image: rbexy
|
|
10
|
+
volumes:
|
|
11
|
+
- .:/app
|
|
12
|
+
- bundle:/usr/local/bundle
|
|
13
|
+
- $HOME/.ssh:/root/.ssh:ro
|
|
14
|
+
- $HOME/.gitconfig:/root/.gitconfig:ro
|
|
15
|
+
- $HOME/.gem/credentials:/root/.gem/credentials
|
|
16
|
+
working_dir: /app
|
|
17
|
+
dummy:
|
|
18
|
+
image: rbexy
|
|
19
|
+
volumes:
|
|
20
|
+
- .:/app
|
|
21
|
+
- bundle:/usr/local/bundle
|
|
22
|
+
working_dir: /app/spec/dummy/
|
|
23
|
+
command: ./start.sh
|
|
24
|
+
ports:
|
|
25
|
+
- 3000:3000
|
|
26
|
+
environment:
|
|
27
|
+
- RAILS_LOG_STDOUT=1
|
|
28
|
+
tty: true
|
|
29
|
+
stdin_open: true
|