rbexy 2.0.0.beta8 → 2.0.0.beta9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e6c7e84344a63e1d1992827aedcafdd968ce9372e35bb6a8d01e66b46be34e0
4
- data.tar.gz: 497be0f074210d5f43efe9a38e72d41972b309fc8294fa413db097afdf0cb8e5
3
+ metadata.gz: df2f31c9d3f08e035f5c3d2a7cb7673f29abdb69d17805073df4f615d2000c81
4
+ data.tar.gz: cd5c8a28d438f1ce73b0aeb50434cf308dedd17d8e9604e67354686d064f699a
5
5
  SHA512:
6
- metadata.gz: 8c18d3a259f7ce7ad630e2a39a8241ad0b6ce6bde8ea9ac55bd601cd6378b39e3201c011f8b6348bd4aaf86b780817a6b52db45cfa86a851e854b7c62cfb4285
7
- data.tar.gz: dcf0db849b73ef8c817247c53812359273ed23f4016325288f91d066c449e41ddfc98cb94a924c2dc06ddf1f3d4e56f1998cc91728619c8ddd66c30f2f4faab2
6
+ metadata.gz: d4b9919d6e47c54a587bcc3be0c8f763b064f84544ac76c2e3b88b16870fe85100bbb597edeb11465aaa70b03ee92189fb7b8222865b0546455693d73b671bfb
7
+ data.tar.gz: 2f540c4199674536e55c9dce96cb03812c73251b349e1d222952c6eb12ce4d544a0c5e69e1f32fcedb770fa03aa50bce86f9848003d7da522d9f890e184a6dc2
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --require spec_helper
2
+ --exclude-pattern "integration/caching/**/*.rb"
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.7
4
+ - 3.0
5
+ script: bin/test
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:2.7.1
1
+ FROM ruby:3.0.0
2
2
  RUN apt-get update -qq
3
3
 
4
4
  WORKDIR /app
data/Dockerfile-ruby2 ADDED
@@ -0,0 +1,7 @@
1
+ FROM ruby:2.7.2
2
+ RUN apt-get update -qq
3
+
4
+ WORKDIR /app
5
+
6
+ COPY . .
7
+ RUN bundle install
data/Gemfile.lock CHANGED
@@ -1,76 +1,80 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbexy (2.0.0.beta8)
4
+ rbexy (2.0.0.beta9)
5
5
  actionview (>= 6.0, < 7.0)
6
6
  activesupport (>= 6.0, < 7.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- actioncable (6.0.3.3)
12
- actionpack (= 6.0.3.3)
11
+ actioncable (6.1.3)
12
+ actionpack (= 6.1.3)
13
+ activesupport (= 6.1.3)
13
14
  nio4r (~> 2.0)
14
15
  websocket-driver (>= 0.6.1)
15
- actionmailbox (6.0.3.3)
16
- actionpack (= 6.0.3.3)
17
- activejob (= 6.0.3.3)
18
- activerecord (= 6.0.3.3)
19
- activestorage (= 6.0.3.3)
20
- activesupport (= 6.0.3.3)
16
+ actionmailbox (6.1.3)
17
+ actionpack (= 6.1.3)
18
+ activejob (= 6.1.3)
19
+ activerecord (= 6.1.3)
20
+ activestorage (= 6.1.3)
21
+ activesupport (= 6.1.3)
21
22
  mail (>= 2.7.1)
22
- actionmailer (6.0.3.3)
23
- actionpack (= 6.0.3.3)
24
- actionview (= 6.0.3.3)
25
- activejob (= 6.0.3.3)
23
+ actionmailer (6.1.3)
24
+ actionpack (= 6.1.3)
25
+ actionview (= 6.1.3)
26
+ activejob (= 6.1.3)
27
+ activesupport (= 6.1.3)
26
28
  mail (~> 2.5, >= 2.5.4)
27
29
  rails-dom-testing (~> 2.0)
28
- actionpack (6.0.3.3)
29
- actionview (= 6.0.3.3)
30
- activesupport (= 6.0.3.3)
31
- rack (~> 2.0, >= 2.0.8)
30
+ actionpack (6.1.3)
31
+ actionview (= 6.1.3)
32
+ activesupport (= 6.1.3)
33
+ rack (~> 2.0, >= 2.0.9)
32
34
  rack-test (>= 0.6.3)
33
35
  rails-dom-testing (~> 2.0)
34
36
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
- actiontext (6.0.3.3)
36
- actionpack (= 6.0.3.3)
37
- activerecord (= 6.0.3.3)
38
- activestorage (= 6.0.3.3)
39
- activesupport (= 6.0.3.3)
37
+ actiontext (6.1.3)
38
+ actionpack (= 6.1.3)
39
+ activerecord (= 6.1.3)
40
+ activestorage (= 6.1.3)
41
+ activesupport (= 6.1.3)
40
42
  nokogiri (>= 1.8.5)
41
- actionview (6.0.3.3)
42
- activesupport (= 6.0.3.3)
43
+ actionview (6.1.3)
44
+ activesupport (= 6.1.3)
43
45
  builder (~> 3.1)
44
46
  erubi (~> 1.4)
45
47
  rails-dom-testing (~> 2.0)
46
48
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
- activejob (6.0.3.3)
48
- activesupport (= 6.0.3.3)
49
+ activejob (6.1.3)
50
+ activesupport (= 6.1.3)
49
51
  globalid (>= 0.3.6)
50
- activemodel (6.0.3.3)
51
- activesupport (= 6.0.3.3)
52
- activerecord (6.0.3.3)
53
- activemodel (= 6.0.3.3)
54
- activesupport (= 6.0.3.3)
55
- activestorage (6.0.3.3)
56
- actionpack (= 6.0.3.3)
57
- activejob (= 6.0.3.3)
58
- activerecord (= 6.0.3.3)
52
+ activemodel (6.1.3)
53
+ activesupport (= 6.1.3)
54
+ activerecord (6.1.3)
55
+ activemodel (= 6.1.3)
56
+ activesupport (= 6.1.3)
57
+ activestorage (6.1.3)
58
+ actionpack (= 6.1.3)
59
+ activejob (= 6.1.3)
60
+ activerecord (= 6.1.3)
61
+ activesupport (= 6.1.3)
59
62
  marcel (~> 0.3.1)
60
- activesupport (6.0.3.3)
63
+ mimemagic (~> 0.3.2)
64
+ activesupport (6.1.3)
61
65
  concurrent-ruby (~> 1.0, >= 1.0.2)
62
- i18n (>= 0.7, < 2)
63
- minitest (~> 5.1)
64
- tzinfo (~> 1.1)
65
- zeitwerk (~> 2.2, >= 2.2.2)
66
+ i18n (>= 1.6, < 2)
67
+ minitest (>= 5.1)
68
+ tzinfo (~> 2.0)
69
+ zeitwerk (~> 2.3)
66
70
  builder (3.2.4)
67
71
  byebug (11.1.3)
68
72
  coderay (1.1.3)
69
- concurrent-ruby (1.1.7)
73
+ concurrent-ruby (1.1.8)
70
74
  crass (1.0.6)
71
75
  diff-lcs (1.4.4)
72
- erubi (1.9.0)
73
- ffi (1.13.1)
76
+ erubi (1.10.0)
77
+ ffi (1.15.0)
74
78
  formatador (0.2.5)
75
79
  globalid (0.4.2)
76
80
  activesupport (>= 4.2.0)
@@ -88,12 +92,12 @@ GEM
88
92
  guard (~> 2.1)
89
93
  guard-compat (~> 1.1)
90
94
  rspec (>= 2.99.0, < 4.0)
91
- i18n (1.8.5)
95
+ i18n (1.8.9)
92
96
  concurrent-ruby (~> 1.0)
93
- listen (3.2.1)
97
+ listen (3.4.1)
94
98
  rb-fsevent (~> 0.10, >= 0.10.3)
95
99
  rb-inotify (~> 0.9, >= 0.9.10)
96
- loofah (2.7.0)
100
+ loofah (2.9.0)
97
101
  crass (~> 1.0.2)
98
102
  nokogiri (>= 1.5.9)
99
103
  lumberjack (1.2.8)
@@ -105,12 +109,13 @@ GEM
105
109
  method_source (1.0.0)
106
110
  mimemagic (0.3.5)
107
111
  mini_mime (1.0.2)
108
- mini_portile2 (2.4.0)
109
- minitest (5.14.2)
112
+ mini_portile2 (2.5.0)
113
+ minitest (5.14.4)
110
114
  nenv (0.3.0)
111
- nio4r (2.5.4)
112
- nokogiri (1.10.10)
113
- mini_portile2 (~> 2.4.0)
115
+ nio4r (2.5.7)
116
+ nokogiri (1.11.1)
117
+ mini_portile2 (~> 2.5.0)
118
+ racc (~> 1.4)
114
119
  notiffany (0.1.3)
115
120
  nenv (~> 0.1)
116
121
  shellany (~> 0.0)
@@ -120,36 +125,37 @@ GEM
120
125
  pry-byebug (3.9.0)
121
126
  byebug (~> 11.0)
122
127
  pry (~> 0.13.0)
128
+ racc (1.5.2)
123
129
  rack (2.2.3)
124
130
  rack-test (1.1.0)
125
131
  rack (>= 1.0, < 3)
126
- rails (6.0.3.3)
127
- actioncable (= 6.0.3.3)
128
- actionmailbox (= 6.0.3.3)
129
- actionmailer (= 6.0.3.3)
130
- actionpack (= 6.0.3.3)
131
- actiontext (= 6.0.3.3)
132
- actionview (= 6.0.3.3)
133
- activejob (= 6.0.3.3)
134
- activemodel (= 6.0.3.3)
135
- activerecord (= 6.0.3.3)
136
- activestorage (= 6.0.3.3)
137
- activesupport (= 6.0.3.3)
138
- bundler (>= 1.3.0)
139
- railties (= 6.0.3.3)
132
+ rails (6.1.3)
133
+ actioncable (= 6.1.3)
134
+ actionmailbox (= 6.1.3)
135
+ actionmailer (= 6.1.3)
136
+ actionpack (= 6.1.3)
137
+ actiontext (= 6.1.3)
138
+ actionview (= 6.1.3)
139
+ activejob (= 6.1.3)
140
+ activemodel (= 6.1.3)
141
+ activerecord (= 6.1.3)
142
+ activestorage (= 6.1.3)
143
+ activesupport (= 6.1.3)
144
+ bundler (>= 1.15.0)
145
+ railties (= 6.1.3)
140
146
  sprockets-rails (>= 2.0.0)
141
147
  rails-dom-testing (2.0.3)
142
148
  activesupport (>= 4.2.0)
143
149
  nokogiri (>= 1.6)
144
150
  rails-html-sanitizer (1.3.0)
145
151
  loofah (~> 2.3)
146
- railties (6.0.3.3)
147
- actionpack (= 6.0.3.3)
148
- activesupport (= 6.0.3.3)
152
+ railties (6.1.3)
153
+ actionpack (= 6.1.3)
154
+ activesupport (= 6.1.3)
149
155
  method_source
150
156
  rake (>= 0.8.7)
151
- thor (>= 0.20.3, < 2.0)
152
- rake (12.3.3)
157
+ thor (~> 1.0)
158
+ rake (13.0.3)
153
159
  rb-fsevent (0.10.4)
154
160
  rb-inotify (0.10.1)
155
161
  ffi (~> 1.0)
@@ -186,14 +192,13 @@ GEM
186
192
  activesupport (>= 4.0)
187
193
  sprockets (>= 3.0.0)
188
194
  sqlite3 (1.4.2)
189
- thor (1.0.1)
190
- thread_safe (0.3.6)
191
- tzinfo (1.2.7)
192
- thread_safe (~> 0.1)
195
+ thor (1.1.0)
196
+ tzinfo (2.0.4)
197
+ concurrent-ruby (~> 1.0)
193
198
  websocket-driver (0.7.3)
194
199
  websocket-extensions (>= 0.1.0)
195
200
  websocket-extensions (0.1.5)
196
- zeitwerk (2.4.0)
201
+ zeitwerk (2.4.2)
197
202
 
198
203
  PLATFORMS
199
204
  ruby
@@ -211,4 +216,4 @@ DEPENDENCIES
211
216
  sqlite3
212
217
 
213
218
  BUNDLED WITH
214
- 2.1.4
219
+ 2.2.3
data/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/patbenatar/rbexy.svg?branch=master)](https://travis-ci.org/patbenatar/rbexy)
4
4
 
5
+ * [Getting Started](#getting-started-with-rails)
6
+ * [Template Syntax](#template-syntax)
7
+ * [Components](#components)
8
+ * [`Rbexy::Component`](#rbexycomponent)
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
+
5
18
  Love JSX and component-based frontends, but sick of paying the costs of SPA development? Rbexy brings the elegance of JSX—operating on HTML elements and custom components with an interchangeable syntax—to the world of Rails server-rendered apps.
6
19
 
7
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.
@@ -85,78 +98,25 @@ Add a controller, action, route, and `rbx` view like `app/views/hello_worlds/ind
85
98
  </HelloWorld>
86
99
  ```
87
100
 
88
- _Or you can render Rbexy components from ERB with `<%= HelloWorldComponent.new(self, name: "Nick").render %>`_
89
-
90
101
  Fire up `rails s`, navigate to your route, and you should see Rbexy in action!
91
102
 
92
103
  ## Template Syntax
93
104
 
94
- ### Text
95
-
96
- You can put arbitrary strings anywhere.
97
-
98
- At the root:
99
-
100
- ```jsx
101
- Hello world
102
- ```
103
-
104
- Inside tags:
105
-
106
- ```jsx
107
- <p>Hello world</p>
108
- ```
109
-
110
- As attributes:
111
-
112
- ```jsx
113
- <div class="myClass"></div>
114
- ```
115
-
116
- ### Comments
117
-
118
- Start a line with `#` to leave a comment:
119
-
120
- ```jsx
121
- # Comments can be at the root
122
- <div>
123
- # Or within tags
124
- # spanning multiple lines
125
- <h1>Hello world</h1>
126
- </div>
127
- ```
128
-
129
- ### Expressions
130
-
131
- You can put ruby code anywhere that you would put text, just wrap it in `{ ... }`
132
-
133
- At the root:
134
-
135
- ```jsx
136
- {"hello world".upcase}
137
- ```
138
-
139
- Inside a sentence:
140
-
141
- ```jsx
142
- Hello {"world".upcase}
143
- ```
144
-
145
- Inside tags:
105
+ You can use Ruby code within brackets:
146
106
 
147
107
  ```jsx
148
- <p>{"hello world".upcase}</p>
108
+ <p class={@dynamic_class}>
109
+ Hello {"world".upcase}
110
+ </p>
149
111
  ```
150
112
 
151
- As attributes:
113
+ You can splat a hash into attributes:
152
114
 
153
115
  ```jsx
154
- <p class={@dynamic_class}>Hello world</p>
116
+ <div {**{class: "myClass"}} {**@more_attrs}></div>
155
117
  ```
156
118
 
157
- #### Tags within expressions
158
-
159
- To conditionalize your template:
119
+ You can use HTML or component tags within expressions. e.g. to conditionalize a template:
160
120
 
161
121
  ```jsx
162
122
  <div>
@@ -165,7 +125,7 @@ To conditionalize your template:
165
125
  </div>
166
126
  ```
167
127
 
168
- Loops:
128
+ Or in loops:
169
129
 
170
130
  ```jsx
171
131
  <ul>
@@ -181,7 +141,7 @@ Blocks:
181
141
  end}
182
142
  ```
183
143
 
184
- As an attribute:
144
+ Pass a tag to a component as an attribute:
185
145
 
186
146
  ```jsx
187
147
  <Hero title={<h1>Hello World</h1>}>
@@ -189,7 +149,7 @@ As an attribute:
189
149
  </Hero>
190
150
  ```
191
151
 
192
- Pass a lambda to a prop, that when called returns a tag:
152
+ Or pass a lambda as an attribute, that when called returns a tag:
193
153
 
194
154
  ```jsx
195
155
  <Hero title={-> { <h1>Hello World</h1> }}>
@@ -207,70 +167,15 @@ _Note that when using tags inside blocks, the block must evaluate to a single ro
207
167
  -> { <i>Hello</i> World }
208
168
  ```
209
169
 
210
- ### Tags
211
-
212
- You can put standard HTML tags anywhere.
213
-
214
- At the root:
215
-
216
- ```jsx
217
- <h1>Hello world</h1>
218
- ```
219
-
220
- As children:
221
-
222
- ```jsx
223
- <div>
224
- <h1>Hello world</h1>
225
- </div>
226
- ```
227
-
228
- As siblings with other tags:
229
-
230
- ```jsx
231
- <div>
232
- <h1>Hello world</h1>
233
- <p>Welcome to rbexy</p>
234
- </div>
235
- ```
236
-
237
- As siblings with text and expressions:
238
-
239
- ```jsx
240
- <h1>Hello world</h1>
241
- {an_expression}
242
- Some arbitrary text
243
- ```
244
-
245
- Self-closing tags:
246
-
247
- ```jsx
248
- <input type="text" />
249
- ```
250
-
251
- #### Attributes
252
-
253
- Text and expressions can be provided as attributes:
254
-
255
- ```jsx
256
- <div class="myClass" id={dynamic_id}></div>
257
- ```
258
-
259
- Value-less attributes are allowed:
260
-
261
- ```jsx
262
- <input type="submit" disabled>
263
- ```
264
-
265
- You can splat a hash into attributes:
170
+ Start a line with `#` to leave a comment:
266
171
 
267
172
  ```jsx
268
- <div {**{ class: "myClass" }} {**@more_attrs}></div>
173
+ # Private note to self that won't be rendered in the final HTML
269
174
  ```
270
175
 
271
- ## Custom components
176
+ ## Components
272
177
 
273
- You can use custom components alongside standard HTML tags:
178
+ You can use Ruby classes as components alongside standard HTML tags:
274
179
 
275
180
  ```jsx
276
181
  <div>
@@ -281,6 +186,8 @@ You can use custom components alongside standard HTML tags:
281
186
  </div>
282
187
  ```
283
188
 
189
+ By default, Rbexy 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.
190
+
284
191
  ### `Rbexy::Component`
285
192
 
286
193
  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:
@@ -301,13 +208,11 @@ By default, we'll look for a template file in the same directory as the class an
301
208
  <h1>{@title}</h1>
302
209
  ```
303
210
 
304
- You can call this component from another `.rbx` template file (`<PageHeader title="Hello" />`)—either one rendered by another component class or a Rails view file like `app/views/products/index.rbx`. Or you can call it from ERB (or any other template language) like `PageHeaderComponent.new(self, title: "Hello").render_in`.
305
-
306
- 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/`.
211
+ 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.
307
212
 
308
213
  #### Template-less components
309
214
 
310
- If you'd prefer to render your components entirely from Ruby, e.g. using Rails `tag` helpers, you can do so with `#call`:
215
+ If you'd prefer to render your components entirely from Ruby, you can do so by implementing `#call`:
311
216
 
312
217
  ```ruby
313
218
  class PageHeaderComponent < Rbexy::Component
@@ -361,106 +266,136 @@ class TextFieldComponent < Rbexy::Component
361
266
  end
362
267
  ```
363
268
 
364
- ### `ViewComponent`
269
+ #### Usage with ERB
365
270
 
366
- Using Github's view_component library? Rbexy ships with a provider that'll resolve your RBX tags like `<Button />` to their corresponding `ButtonComponent < ViewComponent::Base` components.
271
+ We recommend using `Rbexy::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:
367
272
 
368
- ```ruby
369
- require "rbexy/component_providers/view_component_provider"
273
+ Rails 6.1:
274
+
275
+ ```erb
276
+ <%= render PageHeaderComponent.new(self, title: "Welcome") do %>
277
+ <p>Children...</p>
278
+ <% end >
279
+ ```
370
280
 
281
+ Rails 6.0 or earlier:
282
+
283
+ ```erb
284
+ <%= PageHeaderComponent.new(self, title: "Welcome").render_in(self) %>
285
+ ```
286
+
287
+ ### Usage with any component library
288
+
289
+ You can use the rbx template language with other component libraries like Github's view_component. You just need to tell Rbexy how to render the component:
290
+
291
+ ```ruby
292
+ # config/initializers/rbexy.rb
371
293
  Rbexy.configure do |config|
372
- config.component_provider = Rbexy::ComponentProviders::ViewComponentProvider.new
294
+ config.component_rendering_templates = {
295
+ children: "{capture{%{children}}}",
296
+ component: "::%{component_class}.new(%{view_context},%{kwargs}).render_in%{children_block}"
297
+ }
373
298
  end
374
299
  ```
375
300
 
376
- ### Other types of components
301
+ ## Fragment caching in Rails
377
302
 
378
- You just need to tell rbexy how to resolve your custom component classes as it encounters them while evaluating your template by implementing a ComponentProvider:
303
+ `.rbx` templates integrate with Rails fragment caching, automatically cachebusting when the template or its render dependencies change.
379
304
 
380
- ```ruby
381
- class MyComponentProvider
382
- def match?(name)
383
- # Return true if the given tag name matches one of your custom components
384
- end
305
+ If you're using `Rbexy::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.
385
306
 
386
- def render(context, name, **attrs, &block)
387
- # Instantiate and render your custom component for the given name, using
388
- # the render context as needed (e.g. ActionView in Rails)
389
- end
390
- end
307
+ And you can use `<Rbexy.Cache>`, a convenient wrapper for the Rails fragment cache:
308
+
309
+ ```rbx
310
+ <Rbexy.Cache key={...}>
311
+ <p>Fragment here...</p>
312
+ <MyButton />
313
+ </Rbexy.Cache>
314
+ ```
315
+
316
+ ## Advanced
317
+
318
+ ### Component resolution
319
+
320
+ By default, Rbexy resolves component tags to Ruby classes named `#{tag}Component`, e.g.:
391
321
 
392
- # Register your component provider with Rbexy
322
+ * `<PageHeader />` => `PageHeaderComponent`
323
+ * `<Admin.Button />` => `Admin::ButtonComponent`
324
+
325
+ You can customize this behavior by providing a custom resolver:
326
+
327
+ ```ruby
328
+ # config/initializers/rbexy.rb
393
329
  Rbexy.configure do |config|
394
- config.component_provider = MyComponentProvider.new
330
+ config.element_resolver = MyResolver.new
395
331
  end
396
332
  ```
397
333
 
398
- Or in Rails you can customize the component provider just for a controller:
334
+ Where `MyResolver` implements the following API:
335
+
336
+ * `component?(name: string, template: Rbexy::Template) => Boolean`
337
+ * `component_class(name: string, template: Rbexy::Template) => T`
338
+
339
+ See `lib/rbexy/component_resolver.rb` for an example.
340
+
341
+ #### Auto-namespacing
342
+
343
+ Want to namespace your components but sick of typing `Admin.` in front of every component call? Rbexy's default `ComponentResolver` implementation has an option for that:
399
344
 
400
345
  ```ruby
401
- class ThingsController < ApplicationController
402
- def rbexy_component_provider
403
- MyComponentProvider.new
404
- end
346
+ # config/initializers/rbexy.rb
347
+ Rbexy.configure do |config|
348
+ config.element_resolver.component_namespaces = {
349
+ Rails.root.join("app", "views", "admin") => %w[Admin],
350
+ Rails.root.join("app", "components", "admin") => %w[Admin]
351
+ }
405
352
  end
406
353
  ```
407
354
 
408
- See `lib/rbexy/component_providers/` for example implementations.
355
+ 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`.
409
356
 
410
- ## Usage outside of Rails
357
+ ### AST Transforms
411
358
 
412
- Rbexy compiles your template into ruby code, which you can then execute in any context you like, so long as a tag builder is available at `#rbexy_tag`. We provide a built-in runtime leveraging ActionView's `tag` helper that you can extend from or build your own:
359
+ You can hook into Rbexy's compilation process to mutate the abstract syntax tree. This is both useful and dangerous, so use with caution.
413
360
 
414
- Subclass to add methods and instance variables that you'd like to make available to your template.
361
+ 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:
415
362
 
416
363
  ```ruby
417
- class MyRuntime < Rbexy::Runtime
418
- def initialize
419
- super
420
- @an_ivar = "Ivar value"
421
- end
422
-
423
- def a_method
424
- "Method value"
364
+ # config/initializers/rbexy.rb
365
+ Rbexy.configure do |config|
366
+ config.transforms.register(Rbexy::Nodes::HTMLAttr) do |node, context|
367
+ if node.name == "class"
368
+ class_list = node.value.split(" ")
369
+ node.value.content = scope_names(class_list, scope: context.template.identifier)
370
+ end
425
371
  end
426
372
  end
427
-
428
- Rbexy.evaluate("<p class={a_method}>{@an_ivar}</p>", MyRuntime.new)
429
373
  ```
430
374
 
431
- If you're using custom components, inject a ComponentProvider (see "Custom components" for an example implementation):
375
+ ### Usage outside of Rails
376
+
377
+ Rbexy compiles your template into ruby code, which you can then execute in any context you like. Subclass `Rbexy::Runtime` to add methods and instance variables that you'd like to make available to your template.
432
378
 
433
379
  ```ruby
434
380
  class MyRuntime < Rbexy::Runtime
435
- def initialize(component_provider)
436
- super(component_provider)
437
- @ivar_val = "ivar value"
381
+ def initialize
382
+ super
383
+ @an_ivar = "Ivar value"
438
384
  end
439
385
 
440
- def splat_attrs
441
- {
442
- key1: "val1",
443
- key2: "val2"
444
- }
386
+ def a_method
387
+ "Method value"
445
388
  end
446
389
  end
447
390
 
448
- Rbexy.evaluate(
449
- "<Forms.TextField /><Button prop1=\"val1\" prop2={true && \"val2\">Submit</Button>",
450
- MyRuntime.new(MyComponentProvider.new)
451
- )
391
+ Rbexy.evaluate("<p class={a_method}>{@an_ivar}</p>", MyRuntime.new)
452
392
  ```
453
393
 
454
- Or implement your own runtime, so long as it conforms to the API:
455
-
456
- * `#rbexy_tag` that returns a tag builder conforming to the API of `ActionView::Helpers::TagHelpers::TagBuilder`
457
- * `#evaluate(code)` that evals the given string of ruby code
458
-
459
394
  ## Development
460
395
 
461
396
  ```
462
397
  docker-compose build
463
- docker-compose run rbexy rspec
398
+ docker-compose run rbexy bin/test
464
399
  ```
465
400
 
466
401
  Or auto-run tests with guard if you prefer:
@@ -471,7 +406,7 @@ docker-compose run rbexy guard
471
406
 
472
407
  ## Contributing
473
408
 
474
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rbexy. 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/[USERNAME]/rbexy/blob/master/CODE_OF_CONDUCT.md).
409
+ Bug reports and pull requests are welcome on GitHub at https://github.com/patbenatar/rbexy. 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/rbexy/blob/master/CODE_OF_CONDUCT.md).
475
410
 
476
411
  ## License
477
412
 
@@ -479,4 +414,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
479
414
 
480
415
  ## Code of Conduct
481
416
 
482
- Everyone interacting in the Rbexy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rbexy/blob/master/CODE_OF_CONDUCT.md).
417
+ Everyone interacting in the Rbexy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/patbenatar/rbexy/blob/master/CODE_OF_CONDUCT.md).
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 CHANGED
@@ -2,6 +2,7 @@ version: '3'
2
2
 
3
3
  volumes:
4
4
  bundle:
5
+ bundle_ruby2:
5
6
 
6
7
  services:
7
8
  rbexy:
@@ -14,6 +15,14 @@ services:
14
15
  - $HOME/.gitconfig:/root/.gitconfig:ro
15
16
  - $HOME/.gem/credentials:/root/.gem/credentials
16
17
  working_dir: /app
18
+ rbexy_ruby2:
19
+ build:
20
+ context: .
21
+ dockerfile: Dockerfile-ruby2
22
+ volumes:
23
+ - .:/app
24
+ - bundle_ruby2:/usr/local/bundle
25
+ working_dir: /app
17
26
  dummy:
18
27
  image: rbexy
19
28
  volumes:
data/lib/rbexy.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "rbexy/version"
2
2
  require "active_support/inflector"
3
3
  require "active_support/concern"
4
+ require "active_support/core_ext/enumerable"
4
5
  require "action_view/helpers/output_safety_helper"
5
6
  require "action_view/helpers/capture_helper"
6
7
  require "action_view/helpers/tag_helper"
@@ -9,6 +9,14 @@ module Rbexy
9
9
  def to_s
10
10
  self
11
11
  end
12
+
13
+ def split(*args)
14
+ super.map { |s| TemplatePath.new(s) }
15
+ end
16
+
17
+ def gsub(*args)
18
+ super.tap { |s| break TemplatePath.new(s) }
19
+ end
12
20
  end
13
21
 
14
22
  class_attribute :component_file_location
@@ -78,8 +86,8 @@ module Rbexy
78
86
  raise error
79
87
  end
80
88
 
81
- def method_missing(meth, *args, &block)
82
- view_context.send(meth, *args, &block)
89
+ def method_missing(meth, *args, **kwargs, &block)
90
+ view_context.send(meth, *args, **kwargs, &block)
83
91
  end
84
92
 
85
93
  def respond_to_missing?(method_name, include_all)
@@ -17,6 +17,7 @@ module Rbexy
17
17
  @compile_context = context
18
18
  children.each { |c| c.inject_compile_context(context) } if respond_to?(:children)
19
19
  members.each { |c| c.inject_compile_context(context) } if respond_to?(:members)
20
+ value.inject_compile_context(context) if respond_to?(:value)
20
21
  end
21
22
 
22
23
  def transform!
@@ -24,6 +25,7 @@ module Rbexy
24
25
  ast_transformer.transform(self, compile_context)
25
26
  children.each(&:transform!) if respond_to?(:children)
26
27
  members.each(&:transform!) if respond_to?(:members)
28
+ value.transform! if respond_to?(:value)
27
29
  end
28
30
 
29
31
  private
data/lib/rbexy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rbexy
2
- VERSION = "2.0.0.beta8"
2
+ VERSION = "2.0.0.beta9"
3
3
  end
data/rbexy.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.summary = "A Ruby template language inspired by JSX"
10
10
  spec.homepage = "https://github.com/patbenatar/rbexy"
11
11
  spec.license = "MIT"
12
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
13
 
14
14
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbexy
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta8
4
+ version: 2.0.0.beta9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Giancola
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-23 00:00:00.000000000 Z
11
+ date: 2021-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -206,6 +206,7 @@ files:
206
206
  - ".travis.yml"
207
207
  - CODE_OF_CONDUCT.md
208
208
  - Dockerfile
209
+ - Dockerfile-ruby2
209
210
  - Gemfile
210
211
  - Gemfile.lock
211
212
  - Guardfile
@@ -214,6 +215,7 @@ files:
214
215
  - Rakefile
215
216
  - bin/console
216
217
  - bin/setup
218
+ - bin/test
217
219
  - docker-compose.yml
218
220
  - lib/rbexy.rb
219
221
  - lib/rbexy/ast_transformer.rb
@@ -270,14 +272,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
270
272
  requirements:
271
273
  - - ">="
272
274
  - !ruby/object:Gem::Version
273
- version: 2.3.0
275
+ version: 2.7.0
274
276
  required_rubygems_version: !ruby/object:Gem::Requirement
275
277
  requirements:
276
278
  - - ">"
277
279
  - !ruby/object:Gem::Version
278
280
  version: 1.3.1
279
281
  requirements: []
280
- rubygems_version: 3.1.2
282
+ rubygems_version: 3.2.3
281
283
  signing_key:
282
284
  specification_version: 4
283
285
  summary: A Ruby template language inspired by JSX