rbexy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f380f26df4a04d8cc73a9a65b5abaad571a32670013eabb3465eaa7f08ab1de4
4
+ data.tar.gz: e729b87a8e1ec2918dce115b240bfbea991a11203d73f003a42cccb972544c93
5
+ SHA512:
6
+ metadata.gz: 795cead4247e37aed6d803f430318e8cb5f080ee431fc0f2f6820684b211a0a45d58ea9205b2cbd3c9e94dcf2021fe972596bd48109276334a2b32661813c9ef
7
+ data.tar.gz: 6ec46d0e829a2d2fb387b45c372cdd2fa5dcb9f04d5e872c8dea637ee9867af964d76eabd1f2b3f83b437db3c4da823359dba0a24bb149d0fae0f3bdfcc15204
@@ -0,0 +1 @@
1
+ spec/dummy/log/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.7
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at TODO: Write your email address. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
@@ -0,0 +1,7 @@
1
+ FROM ruby:2.7.1
2
+ RUN apt-get update -qq
3
+
4
+ WORKDIR /app
5
+
6
+ COPY . .
7
+ RUN bundle install
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rbexy.gemspec
4
+ gemspec
@@ -0,0 +1,213 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rbexy (0.1.0)
5
+ actionview (>= 5.0, < 7.0)
6
+ activesupport (>= 5.0, < 7.0)
7
+ railties (>= 5.0, < 7.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ actioncable (6.0.3.3)
13
+ actionpack (= 6.0.3.3)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailbox (6.0.3.3)
17
+ actionpack (= 6.0.3.3)
18
+ activejob (= 6.0.3.3)
19
+ activerecord (= 6.0.3.3)
20
+ activestorage (= 6.0.3.3)
21
+ activesupport (= 6.0.3.3)
22
+ mail (>= 2.7.1)
23
+ actionmailer (6.0.3.3)
24
+ actionpack (= 6.0.3.3)
25
+ actionview (= 6.0.3.3)
26
+ activejob (= 6.0.3.3)
27
+ mail (~> 2.5, >= 2.5.4)
28
+ rails-dom-testing (~> 2.0)
29
+ actionpack (6.0.3.3)
30
+ actionview (= 6.0.3.3)
31
+ activesupport (= 6.0.3.3)
32
+ rack (~> 2.0, >= 2.0.8)
33
+ rack-test (>= 0.6.3)
34
+ rails-dom-testing (~> 2.0)
35
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
36
+ actiontext (6.0.3.3)
37
+ actionpack (= 6.0.3.3)
38
+ activerecord (= 6.0.3.3)
39
+ activestorage (= 6.0.3.3)
40
+ activesupport (= 6.0.3.3)
41
+ nokogiri (>= 1.8.5)
42
+ actionview (6.0.3.3)
43
+ activesupport (= 6.0.3.3)
44
+ builder (~> 3.1)
45
+ erubi (~> 1.4)
46
+ rails-dom-testing (~> 2.0)
47
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
48
+ activejob (6.0.3.3)
49
+ activesupport (= 6.0.3.3)
50
+ globalid (>= 0.3.6)
51
+ activemodel (6.0.3.3)
52
+ activesupport (= 6.0.3.3)
53
+ activerecord (6.0.3.3)
54
+ activemodel (= 6.0.3.3)
55
+ activesupport (= 6.0.3.3)
56
+ activestorage (6.0.3.3)
57
+ actionpack (= 6.0.3.3)
58
+ activejob (= 6.0.3.3)
59
+ activerecord (= 6.0.3.3)
60
+ marcel (~> 0.3.1)
61
+ activesupport (6.0.3.3)
62
+ concurrent-ruby (~> 1.0, >= 1.0.2)
63
+ i18n (>= 0.7, < 2)
64
+ minitest (~> 5.1)
65
+ tzinfo (~> 1.1)
66
+ zeitwerk (~> 2.2, >= 2.2.2)
67
+ builder (3.2.4)
68
+ byebug (11.1.3)
69
+ coderay (1.1.3)
70
+ concurrent-ruby (1.1.7)
71
+ crass (1.0.6)
72
+ diff-lcs (1.4.4)
73
+ erubi (1.9.0)
74
+ ffi (1.13.1)
75
+ formatador (0.2.5)
76
+ globalid (0.4.2)
77
+ activesupport (>= 4.2.0)
78
+ guard (2.16.2)
79
+ formatador (>= 0.2.4)
80
+ listen (>= 2.7, < 4.0)
81
+ lumberjack (>= 1.0.12, < 2.0)
82
+ nenv (~> 0.1)
83
+ notiffany (~> 0.0)
84
+ pry (>= 0.9.12)
85
+ shellany (~> 0.0)
86
+ thor (>= 0.18.1)
87
+ guard-compat (1.2.1)
88
+ guard-rspec (4.7.3)
89
+ guard (~> 2.1)
90
+ guard-compat (~> 1.1)
91
+ rspec (>= 2.99.0, < 4.0)
92
+ i18n (1.8.5)
93
+ concurrent-ruby (~> 1.0)
94
+ listen (3.2.1)
95
+ rb-fsevent (~> 0.10, >= 0.10.3)
96
+ rb-inotify (~> 0.9, >= 0.9.10)
97
+ loofah (2.7.0)
98
+ crass (~> 1.0.2)
99
+ nokogiri (>= 1.5.9)
100
+ lumberjack (1.2.8)
101
+ mail (2.7.1)
102
+ mini_mime (>= 0.1.1)
103
+ marcel (0.3.3)
104
+ mimemagic (~> 0.3.2)
105
+ method_source (1.0.0)
106
+ mimemagic (0.3.5)
107
+ mini_mime (1.0.2)
108
+ mini_portile2 (2.4.0)
109
+ minitest (5.14.2)
110
+ nenv (0.3.0)
111
+ nio4r (2.5.4)
112
+ nokogiri (1.10.10)
113
+ mini_portile2 (~> 2.4.0)
114
+ notiffany (0.1.3)
115
+ nenv (~> 0.1)
116
+ shellany (~> 0.0)
117
+ pry (0.13.1)
118
+ coderay (~> 1.1)
119
+ method_source (~> 1.0)
120
+ pry-byebug (3.9.0)
121
+ byebug (~> 11.0)
122
+ pry (~> 0.13.0)
123
+ rack (2.2.3)
124
+ rack-test (1.1.0)
125
+ 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)
140
+ sprockets-rails (>= 2.0.0)
141
+ rails-dom-testing (2.0.3)
142
+ activesupport (>= 4.2.0)
143
+ nokogiri (>= 1.6)
144
+ rails-html-sanitizer (1.3.0)
145
+ loofah (~> 2.3)
146
+ railties (6.0.3.3)
147
+ actionpack (= 6.0.3.3)
148
+ activesupport (= 6.0.3.3)
149
+ method_source
150
+ rake (>= 0.8.7)
151
+ thor (>= 0.20.3, < 2.0)
152
+ rake (12.3.3)
153
+ rb-fsevent (0.10.4)
154
+ rb-inotify (0.10.1)
155
+ ffi (~> 1.0)
156
+ rspec (3.9.0)
157
+ rspec-core (~> 3.9.0)
158
+ rspec-expectations (~> 3.9.0)
159
+ rspec-mocks (~> 3.9.0)
160
+ rspec-core (3.9.2)
161
+ rspec-support (~> 3.9.3)
162
+ rspec-expectations (3.9.2)
163
+ diff-lcs (>= 1.2.0, < 2.0)
164
+ rspec-support (~> 3.9.0)
165
+ rspec-html-matchers (0.9.3)
166
+ nokogiri (~> 1)
167
+ rspec (>= 3.0.0.a, < 4)
168
+ rspec-mocks (3.9.1)
169
+ diff-lcs (>= 1.2.0, < 2.0)
170
+ rspec-support (~> 3.9.0)
171
+ rspec-rails (4.0.1)
172
+ actionpack (>= 4.2)
173
+ activesupport (>= 4.2)
174
+ railties (>= 4.2)
175
+ rspec-core (~> 3.9)
176
+ rspec-expectations (~> 3.9)
177
+ rspec-mocks (~> 3.9)
178
+ rspec-support (~> 3.9)
179
+ rspec-support (3.9.3)
180
+ shellany (0.0.1)
181
+ sprockets (4.0.2)
182
+ concurrent-ruby (~> 1.0)
183
+ rack (> 1, < 3)
184
+ sprockets-rails (3.2.2)
185
+ actionpack (>= 4.0)
186
+ activesupport (>= 4.0)
187
+ sprockets (>= 3.0.0)
188
+ 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)
193
+ websocket-driver (0.7.3)
194
+ websocket-extensions (>= 0.1.0)
195
+ websocket-extensions (0.1.5)
196
+ zeitwerk (2.4.0)
197
+
198
+ PLATFORMS
199
+ ruby
200
+
201
+ DEPENDENCIES
202
+ guard-rspec (~> 4.7, >= 4.7.3)
203
+ pry-byebug
204
+ rails (>= 5.0, < 7.0)
205
+ rake
206
+ rbexy!
207
+ rspec (~> 3.9)
208
+ rspec-html-matchers (~> 0.9.3)
209
+ rspec-rails (~> 4.0, >= 4.0.1)
210
+ sqlite3
211
+
212
+ BUNDLED WITH
213
+ 2.1.4
@@ -0,0 +1,70 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+
43
+ # Rails files
44
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
45
+ dsl.watch_spec_files_for(rails.app_files)
46
+ dsl.watch_spec_files_for(rails.views)
47
+
48
+ watch(rails.controllers) do |m|
49
+ [
50
+ rspec.spec.call("routing/#{m[1]}_routing"),
51
+ rspec.spec.call("controllers/#{m[1]}_controller"),
52
+ rspec.spec.call("acceptance/#{m[1]}")
53
+ ]
54
+ end
55
+
56
+ # Rails config changes
57
+ watch(rails.spec_helper) { rspec.spec_dir }
58
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
+
61
+ # Capybara features specs
62
+ watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
63
+ watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
64
+
65
+ # Turnip features and steps
66
+ watch(%r{^spec/acceptance/(.+)\.feature$})
67
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
69
+ end
70
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,452 @@
1
+ # A Ruby template language inspired by JSX
2
+
3
+ [![Build Status](https://travis-ci.org/patbenatar/rbexy.svg?branch=master)](https://travis-ci.org/patbenatar/rbexy)
4
+
5
+ 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
+
7
+ 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.
8
+
9
+ _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.
10
+
11
+ ## Example
12
+
13
+ Use your custom Ruby class components from `.rbx` templates just like you would React components in JSX:
14
+
15
+ ```jsx
16
+ <body>
17
+ <Hero size="fullscreen" {**splat_some_attributes}>
18
+ <h1>Hello {@name}</h1>
19
+ <p>Welcome to rbexy, marrying the nice parts of React templating with the development efficiency of Rails server-rendered apps.</p>
20
+ <Button to={about_path}>Learn more</Button>
21
+ </Hero>
22
+ </body>
23
+ ```
24
+
25
+ after defining them in Ruby:
26
+
27
+ ```ruby
28
+ class HeroComponent < Rbexy::Component # or use ViewComponent, or another component lib
29
+ def setup(size:)
30
+ @size = size
31
+ end
32
+ end
33
+
34
+ class ButtonComponent < Rbexy::Component
35
+ def setup(to:)
36
+ @to = to
37
+ end
38
+ end
39
+ ```
40
+
41
+ with their accompying template files (also can be `.rbx`!), scoped scss files, JS and other assets (not shown).
42
+
43
+ ## Getting Started (with Rails)
44
+
45
+ Add it to your Gemfile and `bundle install`:
46
+
47
+ ```ruby
48
+ gem "rbexy"
49
+ ```
50
+
51
+ In `config/application.rb`:
52
+
53
+ ```ruby
54
+ require "rbexy/rails/engine"
55
+ ```
56
+
57
+ _Not using Rails? See "Usage outside of Rails" below._
58
+
59
+ Create your first component at `app/components/hello_world_component.rb`:
60
+
61
+ ```ruby
62
+ class HelloWorldComponent < Rbexy::Component
63
+ def setup(name:)
64
+ @name = name
65
+ end
66
+ end
67
+ ```
68
+
69
+ With a template `app/components/hello_world_component.rbx`:
70
+
71
+ ```jsx
72
+ <div>
73
+ <h1>Hello {@name}</h1>
74
+ {content}
75
+ </div>
76
+ ```
77
+
78
+ Add a controller, action, route, and `rbx` view like `app/views/hello_worlds/index.rbx`:
79
+
80
+ ```jsx
81
+ <HelloWorld name="Nick">
82
+ <p>Welcome to the world of component-based frontend development in Rails!</p>
83
+ </HelloWorld>
84
+ ```
85
+
86
+ _Or you can render Rbexy components from ERB with `<%= HelloWorldComponent.new(self, name: "Nick").render %>`_
87
+
88
+ Fire up `rails s`, navigate to your route, and you should see Rbexy in action!
89
+
90
+ ## Template Syntax
91
+
92
+ ### Text
93
+
94
+ You can put arbitrary strings anywhere.
95
+
96
+ At the root:
97
+
98
+ ```jsx
99
+ Hello world
100
+ ```
101
+
102
+ Inside tags:
103
+
104
+ ```jsx
105
+ <p>Hello world</p>
106
+ ```
107
+
108
+ As attributes:
109
+
110
+ ```jsx
111
+ <div class="myClass"></div>
112
+ ```
113
+
114
+ ### Comments
115
+
116
+ Start a line with `#` to leave a comment:
117
+
118
+ ```jsx
119
+ # Comments can be at the root
120
+ <div>
121
+ # Or within tags
122
+ # spanning multiple lines
123
+ <h1>Hello world</h1>
124
+ </div>
125
+ ```
126
+
127
+ ### Expressions
128
+
129
+ You can put ruby code anywhere that you would put text, just wrap it in `{ ... }`
130
+
131
+ At the root:
132
+
133
+ ```jsx
134
+ {"hello world".upcase}
135
+ ```
136
+
137
+ Inside a sentence:
138
+
139
+ ```jsx
140
+ Hello {"world".upcase}
141
+ ```
142
+
143
+ Inside tags:
144
+
145
+ ```jsx
146
+ <p>{"hello world".upcase}</p>
147
+ ```
148
+
149
+ As attributes:
150
+
151
+ ```jsx
152
+ <p class={@dynamic_class}>Hello world</p>
153
+ ```
154
+
155
+ #### Tags within expressions
156
+
157
+ To conditionalize your template:
158
+
159
+ ```jsx
160
+ <div>
161
+ {some_boolean && <h1>Welcome</h1>}
162
+ {another_boolean ? <p>Option One</p> : <p>Option Two</p>}
163
+ </div>
164
+ ```
165
+
166
+ Loops:
167
+
168
+ ```jsx
169
+ <ul>
170
+ {[1, 2, 3].map { |n| <li>{n}</li> }}
171
+ </ul>
172
+ ```
173
+
174
+ As an attribute:
175
+
176
+ ```jsx
177
+ <Hero title={<h1>Hello World</h1>}>
178
+ Content here...
179
+ </Hero>
180
+ ```
181
+
182
+ Pass a lambda to a prop, that when called returns a tag:
183
+
184
+ ```jsx
185
+ <Hero title={-> { <h1>Hello World</h1> }}>
186
+ Content here...
187
+ </Hero>
188
+ ```
189
+
190
+ ### Tags
191
+
192
+ You can put standard HTML tags anywhere.
193
+
194
+ At the root:
195
+
196
+ ```jsx
197
+ <h1>Hello world</h1>
198
+ ```
199
+
200
+ As children:
201
+
202
+ ```jsx
203
+ <div>
204
+ <h1>Hello world</h1>
205
+ </div>
206
+ ```
207
+
208
+ As siblings with other tags:
209
+
210
+ ```jsx
211
+ <div>
212
+ <h1>Hello world</h1>
213
+ <p>Welcome to rbexy</p>
214
+ </div>
215
+ ```
216
+
217
+ As siblings with text and expressions:
218
+
219
+ ```jsx
220
+ <h1>Hello world</h1>
221
+ {an_expression}
222
+ Some arbitrary text
223
+ ```
224
+
225
+ Self-closing tags:
226
+
227
+ ```jsx
228
+ <input type="text" />
229
+ ```
230
+
231
+ #### Attributes
232
+
233
+ Text and expressions can be provided as attributes:
234
+
235
+ ```jsx
236
+ <div class="myClass" id={dynamic_id}></div>
237
+ ```
238
+
239
+ Value-less attributes are allowed:
240
+
241
+ ```jsx
242
+ <input type="submit" disabled>
243
+ ```
244
+
245
+ You can splat a hash into attributes:
246
+
247
+ ```jsx
248
+ <div {**{ class: "myClass" }} {**@more_attrs}></div>
249
+ ```
250
+
251
+ ## Custom components
252
+
253
+ You can use custom components alongside standard HTML tags:
254
+
255
+ ```jsx
256
+ <div>
257
+ <PageHeader title="Welcome" />
258
+ <PageBody>
259
+ <p>To the world of custom components</p>
260
+ </PageBody>
261
+ </div>
262
+ ```
263
+
264
+ ### `Rbexy::Component`
265
+
266
+ 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:
267
+
268
+ ```ruby
269
+ # app/components/page_header_component.rb
270
+ class PageHeaderComponent < Rbexy::Component
271
+ def setup(title:)
272
+ @title = title
273
+ end
274
+ end
275
+ ```
276
+
277
+ By default, we'll look for a template file in the same directory as the class and with a matching filename:
278
+
279
+ ```jsx
280
+ // app/components/page_header_component.rbx
281
+ <h1>{@title}</h1>
282
+ ```
283
+
284
+ 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`.
285
+
286
+ 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/`.
287
+
288
+ #### Template-less components
289
+
290
+ If you'd prefer to render your components entirely from Ruby, e.g. using Rails `tag` helpers, you can do so with `#call`:
291
+
292
+ ```ruby
293
+ class PageHeaderComponent < Rbexy::Component
294
+ def setup(title:)
295
+ @title = title
296
+ end
297
+
298
+ def call
299
+ tag.h1 @title
300
+ end
301
+ end
302
+ ```
303
+
304
+ #### Context
305
+
306
+ `Rbexy::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.
307
+
308
+ Given a template:
309
+
310
+ ```jsx
311
+ <Form>
312
+ <TextField field={:title} />
313
+ </Form>
314
+ ```
315
+
316
+ The form component can use Rails `form_for` and then pass the `form` builder object down to any field components using context:
317
+
318
+ ```ruby
319
+ class FormComponent < Rbexy::Component
320
+ def setup(form_object:)
321
+ @form_object = form_object
322
+ end
323
+
324
+ def call
325
+ form_for @form_object do |form|
326
+ create_context(:form, form)
327
+ content
328
+ end
329
+ end
330
+ end
331
+
332
+ class TextFieldComponent < Rbexy::Component
333
+ def setup(field:)
334
+ @field = field
335
+ @form = use_context(:form)
336
+ end
337
+
338
+ def call
339
+ @form.text_field @field
340
+ end
341
+ end
342
+ ```
343
+
344
+ ### `ViewComponent`
345
+
346
+ 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.
347
+
348
+ ```ruby
349
+ require "rbexy/component_providers/view_component_provider"
350
+
351
+ Rbexy.configure do |config|
352
+ config.component_provider = Rbexy::ComponentProviders::ViewComponentProvider.new
353
+ end
354
+ ```
355
+
356
+ ### Other types of components
357
+
358
+ 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:
359
+
360
+ ```ruby
361
+ class MyComponentProvider
362
+ def match?(name)
363
+ # Return true if the given tag name matches one of your custom components
364
+ end
365
+
366
+ def render(context, name, **attrs, &block)
367
+ # Instantiate and render your custom component for the given name, using
368
+ # the render context as needed (e.g. ActionView in Rails)
369
+ end
370
+ end
371
+
372
+ # Register your component provider with Rbexy
373
+ Rbexy.configure do |config|
374
+ config.component_provider = MyComponentProvider.new
375
+ end
376
+ ```
377
+
378
+ See `lib/rbexy/component_providers/` for example implementations.
379
+
380
+ ## Usage outside of Rails
381
+
382
+ 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:
383
+
384
+ Subclass to add methods and instance variables that you'd like to make available to your template.
385
+
386
+ ```ruby
387
+ class MyRuntime < Rbexy::Runtime
388
+ def initialize
389
+ super
390
+ @an_ivar = "Ivar value"
391
+ end
392
+
393
+ def a_method
394
+ "Method value"
395
+ end
396
+ end
397
+
398
+ Rbexy.evaluate("<p class={a_method}>{@an_ivar}</p>", MyRuntime.new)
399
+ ```
400
+
401
+ If you're using custom components, inject a ComponentProvider (see "Custom components" for an example implementation):
402
+
403
+ ```ruby
404
+ class MyRuntime < Rbexy::Runtime
405
+ def initialize(component_provider)
406
+ super(component_provider)
407
+ @ivar_val = "ivar value"
408
+ end
409
+
410
+ def splat_attrs
411
+ {
412
+ key1: "val1",
413
+ key2: "val2"
414
+ }
415
+ end
416
+ end
417
+
418
+ Rbexy.evaluate(
419
+ "<Forms.TextField /><Button prop1=\"val1\" prop2={true && \"val2\">Submit</Button>",
420
+ MyRuntime.new(MyComponentProvider.new)
421
+ )
422
+ ```
423
+
424
+ Or implement your own runtime, so long as it conforms to the API:
425
+
426
+ * `#rbexy_tag` that returns a tag builder conforming to the API of `ActionView::Helpers::TagHelpers::TagBuilder`
427
+ * `#evaluate(code)` that evals the given string of ruby code
428
+
429
+ ## Development
430
+
431
+ ```
432
+ docker-compose build
433
+ docker-compose run rbexy rspec
434
+ ```
435
+
436
+ Or auto-run tests with guard if you prefer:
437
+
438
+ ```
439
+ docker-compose run rbexy guard
440
+ ```
441
+
442
+ ## Contributing
443
+
444
+ 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).
445
+
446
+ ## License
447
+
448
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
449
+
450
+ ## Code of Conduct
451
+
452
+ 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).