glimmer-dsl-web 0.2.4 → 0.2.6

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: 212cee9f2c87b145cca85b47395f541a749d0a472560a946c1f3b0061ec7b801
4
- data.tar.gz: f7cccb64081e75018c6e043e021b071d70c7b2696057a32e25b191fb2c3dd4f6
3
+ metadata.gz: 45ca1b698d2d53da63a133041ac83c77888d42b1e504bc2fa3b81b54c610ff32
4
+ data.tar.gz: 91268134dcba4d5d04435d8ee517f7a1e39e277ff0be9487aa7dd99eb1899ee2
5
5
  SHA512:
6
- metadata.gz: e590b30b6f3d54f859e168bd7220cfa51e8d3f2354836fb851ab20c21114518ddd2a502a74a262b704148561e64b65a07cbd2ed61593914a274fa7542da8e1c9
7
- data.tar.gz: 1a334ce05e730c2eef25005d5a1a9d16683b639fac910e83ba0ceff88c380230df884486f994140bce11a68e10210ff1a28266604560ecaa134859b621402cf2
6
+ metadata.gz: 68b47d95332b4156d5c04643078cf95bf9d51098b5219836bb1a25eb440d2db25fca55ade3179c64b6587b887b890d4188eb8a91b2f9a32c78ba88027c114774
7
+ data.tar.gz: efdb2f8bfb640b0fe1ce052cf867c2d0b58f4ceae6284e23178f51d42b296e53dda009794cd08cb7a46ec2cc0894f0a9054c1a903d40bc7c62dae0570eec16e9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.2.6
4
+
5
+ - Upgrade to `glimmer-dsl-xml` 1.4.0 to provide access to `html_to_glimmer` converter command (converts legacy HTML to Glimmer DSL syntax)
6
+ - Upgrade to `glimmer-dsl-css` 1.4.0 to provide access to `css_to_glimmer` converter command (converts legacy CSS to Glimmer DSL syntax)
7
+
8
+ ## 0.2.5
9
+
10
+ - Support `Element#class_name` as an alternative to `Element#class` because `class` is a reserved method in Ruby
11
+ - Handle case of data-binding an element that has no `type` attribute
12
+ - Provide `attribute`/`attributes` as aliases for `option`/`options` inside classes mixing `Glimmer::Web::Component` given that the options behave like HTML attributes
13
+ - Hello, Form MVP! Sample: `require 'glimmer-dsl-web/samples/hello/hello_form_mvp'`
14
+ - Hello, Observer (Data-Binding)! Sample: `require 'glimmer-dsl-web/samples/hello/hello_observer_data_binding'`
15
+
3
16
  ## 0.2.4
4
17
 
5
18
  - Enhance `Kernel#puts` & `Kernel#p` to enable printing any native JS object with `console.log` (without having to manually wrap with `Native`)
data/README.md CHANGED
@@ -1,14 +1,31 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.2.4 (Beta)
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.2.6 (Beta)
2
2
  ## Ruby in the Browser Web Frontend Framework
3
+ ### Finally, Ruby Developer Productivity, Happiness, and Fun in the Frontend!!!
3
4
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-web.svg)](http://badge.fury.io/rb/glimmer-dsl-web)
4
5
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
6
 
6
7
  **(Based on Original [Glimmer](https://github.com/AndyObtiva/glimmer) Library Handling World’s Ruby GUI Needs Since 2007. Beware of Imitators!)**
7
8
 
8
- [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web enables building Web Frontends using [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c), as per [Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby](https://youtu.be/knutsgHTrfQ?t=789). It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework follows the Ruby way (with [DSLs](https://martinfowler.com/books/dsl.html) and [TIMTOWTDI](https://en.wiktionary.org/wiki/TMTOWTDI#English)) and the Rails way ([Convention over Configuration](https://rubyonrails.org/doctrine)) in building Isomorphic Ruby on Rails Applications. It provides a Ruby [HTML DSL](#usage), which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) [Data-Binding](#hello-data-binding) (using `<=`) and Bidirectional (Two-Way) [Data-Binding](#hello-data-binding) (using `<=>`). Dynamic rendering (and re-rendering) of HTML content is also supported via [Content Data-Binding](#hello-content-data-binding). And, modular design is supported with [Glimmer Web Components](#hello-component). Many [samples](#samples) are demonstrated in the [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) (there is a very minimal [Standalone [No Rails] sample app](https://github.com/Largo/glimmer-dsl-web-standalone-demo) too). You can finally live in pure Rubyland on the Web in both the frontend and backend with [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web)!
9
+ **(Talk Videos: [Intro to Ruby in the Browser](https://youtu.be/4AdcfbI6A4c?si=MmxOrkhIXTDHQoYi) & [Frontend Ruby with Glimmer DSL for Web](https://youtu.be/rIZ-ILUv9ME?si=raygUXVPd_7ypWuE))**
10
+
11
+ You can finally have Ruby developer happiness and productivity in the Frontend! No more wasting time splitting your resources across multiple languages, using badly engineered, over-engineered, or premature-optimization-obsessed JavaScript libraries, fighting JavaScript build issues (e.g. webpack), or rewriting Ruby Backend code in Frontend JavaScript. With [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c), you can have an exponential jump in development productivity (2x or higher), time-to-release (1/2 or less time), cost (1/2 or cheaper), and maintainability (~50% the code that is simpler and more readable) over JavaScript libraries like React, Angular, Ember, Vue, and Svelte, while being able to reuse Backend Ruby code as is in the Frontend for faster interactions when needed. [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c) finally fulfills every highly-productive Rubyist's dream by bringing Ruby productivity fun to Frontend Development, the same productivity fun you had for years and decades in Backend Development.
12
+
13
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for Web enables building Web Frontends using [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c), as per [Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby](https://youtu.be/knutsgHTrfQ?t=789). It supports Rails' principle of the One Person Framework by not requiring any extra developers with JavaScript expertise, yet enabling Ruby (Backend) Software Engineers to develop the Frontend with Ruby code that is better than any JavaScript code produced by JS developers. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework follows the Ruby way (with [DSLs](https://martinfowler.com/books/dsl.html) and [TIMTOWTDI](https://en.wiktionary.org/wiki/TMTOWTDI#English)) and the Rails way ([Convention over Configuration](https://rubyonrails.org/doctrine)) in building Isomorphic Ruby on Rails Applications. It provides a Ruby [HTML DSL](#usage), which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) [Data-Binding](#hello-data-binding) (using `<=`) and Bidirectional (Two-Way) [Data-Binding](#hello-data-binding) (using `<=>`). Dynamic rendering (and re-rendering) of HTML content is also supported via [Content Data-Binding](#hello-content-data-binding). Modular design is supported with [Glimmer Web Components](#hello-component). And, a Ruby CSS DSL is supported with the included [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css). Many [samples](#samples) are demonstrated in the [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) (there is a very minimal [Standalone [No Rails] static site sample app](https://github.com/Largo/glimmer-dsl-web-standalone-demo) too). You can finally live in pure Rubyland on the Web in both the frontend and backend with [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web)!
14
+
15
+ [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) aims to be a very simple Ruby-based drop-in replacement for your existing JavaScript Frontend library (e.g. React, Angular, Vue, Ember, Svelte) or your JavaScript Frontend layer in general. It does not change how your Frontend interacts with the Backend, meaning you can continue to write Rails Backend API endpoints as needed and make Ajax HTTP requests or read data embedded in elements, but from [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c). Whatever is possible in JavaScript is possible when using Glimmer DSL for Web as it integrates with any existing JavaScript library. The [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) demonstrates how to [make Ajax HTTP calls](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/assets/opal/sample_selector/models/sample_selector_presenter.rb) and how to [integrate with a JavaScript library](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/views/layouts/application.html.erb) (highlightjs) that performs [code syntax highlighting](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/blob/master/app/assets/opal/sample_selector.rb).
16
+
17
+ After looking through the [samples](#samples) below, read the [FAQ (Frequently Asked Questions)](#faq) to learn more about how Glimmer DSL for Web compares to other approaches/libraries like Hotwire (Turbo), Phlex, ViewComponent, Angular, Vue, React, Svelte, and other JS frameworks.
18
+
19
+ Anyone not considering this kind of technology in 2024 is like someone stuck in the dark ages riding horse carriage (e.g. JavaScript developers using frameworks like React) despite flying cars having been invented already and providing exponential jumps in productivity (way more than small linear jumps provided by some JavaScript libraries). Obviously, those who do make this jump will end up winning their work over from customers and beating the competition while delivering the best Frontend value possible to customers.
20
+
21
+ (Attention Software Engineers, Bloggers, and Contributors: Please use Glimmer DSL for Web in web projects, blog about it, and submit a PR with your article, project, and/or open-source-repo added to the README. Also, I give everyone permission to present this project at their local Ruby user group, local Software Engineering meetup, or Software Conferences outside of North America (e.g. Europe). I am willing to present at Software Conferences in North America and Japan (the birthplace of Ruby) only. If you want to have this project presented elsewhere, like in Europe or South America, feel free to prepare and give your own presentations of the project, and if needed, hit me up for help on the [Glimmer Gitter chat](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge))
9
22
 
10
23
  **Hello, World! Sample**
11
24
 
25
+ (Note: in real app development, we build [Glimmer Web Components](#hello-component), but this sample is just introducing basic building blocks towards building [components](#hello-component))
26
+
27
+ [lib/glimmer-dsl-web/samples/hello/hello_world.rb](/lib/glimmer-dsl-web/samples/hello/hello_world.rb)
28
+
12
29
  Glimmer HTML DSL Ruby code in the frontend:
13
30
 
14
31
  ```ruby
@@ -37,8 +54,12 @@ You can also mount the `div` elsewhere by passing the `parent: parent_css_select
37
54
 
38
55
  **Hello, Button!**
39
56
 
57
+ (Note: in real app development, we build [Glimmer Web Components](#hello-component), but this sample is just introducing basic building blocks towards building [components](#hello-component))
58
+
40
59
  Event listeners can be setup on any element using the same event names used in HTML (e.g. `onclick`) while passing in a standard Ruby block to handle behavior. `$$` gives access to JS global scope from Ruby to invoke functions like `alert`.
41
60
 
61
+ [lib/glimmer-dsl-web/samples/hello/hello_button.rb](/lib/glimmer-dsl-web/samples/hello/hello_button.rb)
62
+
42
63
  Glimmer HTML DSL Ruby code in the frontend:
43
64
 
44
65
  ```ruby
@@ -71,8 +92,12 @@ Screenshot:
71
92
 
72
93
  **Hello, Form!**
73
94
 
95
+ (Note: in real app development, we build [Glimmer Web Components](#hello-component), but this sample is just introducing basic building blocks towards building [components](#hello-component))
96
+
74
97
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) gives access to all Web Browser built-in features like HTML form validations, input focus, events, and element functions from a very terse and productive Ruby HTML DSL. Also, you can apply CSS styles by including directly in Ruby code as a string, using [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css), or managing CSS completely separately using something like [SCSS](https://sass-lang.com/). The CSS techniques could be combined as well, like by managing common reusable CSS styles separately in SCSS, but adding component specific CSS styles in Ruby when it is more convenient.
75
98
 
99
+ [lib/glimmer-dsl-web/samples/hello/hello_form.rb](/lib/glimmer-dsl-web/samples/hello/hello_form.rb)
100
+
76
101
  Glimmer HTML DSL Ruby code in the frontend:
77
102
 
78
103
  ```ruby
@@ -227,8 +252,12 @@ Screenshot:
227
252
 
228
253
  **Hello, Data-Binding!**
229
254
 
255
+ (Note: in real app development, we build [Glimmer Web Components](#hello-component), but this sample is just introducing basic building blocks towards building [components](#hello-component))
256
+
230
257
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) intuitively supports both Unidirectional (One-Way) Data-Binding via the `<=` operator and Bidirectional (Two-Way) Data-Binding via the `<=>` operator, incredibly simplifying how to sync View properties with Model attributes with the simplest code to reason about.
231
258
 
259
+ [lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb)
260
+
232
261
  Glimmer HTML DSL Ruby code in the frontend:
233
262
 
234
263
  ```ruby
@@ -344,10 +373,14 @@ Screenshot:
344
373
 
345
374
  **Hello, Content Data-Binding!**
346
375
 
376
+ (Note: in real app development, we build [Glimmer Web Components](#hello-component), but this sample is just introducing basic building blocks towards building [components](#hello-component))
377
+
347
378
  If you need to regenerate HTML element content dynamically, you can use Content Data-Binding to effortlessly
348
379
  rebuild HTML elements based on changes in a Model attribute that provides the source data.
349
380
  In this example, we generate multiple address forms based on the number of addresses the user has.
350
381
 
382
+ [lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb)
383
+
351
384
  Glimmer HTML DSL Ruby code in the frontend:
352
385
 
353
386
  ```ruby
@@ -487,6 +520,8 @@ Rails helper (more about it in [Hello, glimmer_component Rails Helper!](#hello-g
487
520
  Below, we define an `AddressForm` component that generates an `address_form` keyword, and then we
488
521
  reuse it twice inside an `AddressPage` component displaying a Shipping Address and a Billing Address.
489
522
 
523
+ [lib/glimmer-dsl-web/samples/hello/hello_component.rb](/lib/glimmer-dsl-web/samples/hello/hello_component.rb)
524
+
490
525
  Glimmer HTML DSL Ruby code in the frontend:
491
526
 
492
527
  ```ruby
@@ -702,6 +737,8 @@ You may insert a Glimmer component anywhere into a Rails View using
702
737
  `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper`
703
738
  or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
704
739
 
740
+ [lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb](/lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb)
741
+
705
742
  Rails `ApplicationHelper` setup code:
706
743
 
707
744
  ```ruby
@@ -933,6 +970,8 @@ it returns a `String` to enable code like this:
933
970
 
934
971
  In any case, below is a full example leveraging the Glimmer HTML DSL alternative approach when utilizing formatting elements underneath a paragraph.
935
972
 
973
+ [lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb](/lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb)
974
+
936
975
  Glimmer HTML DSL Ruby code in the frontend:
937
976
 
938
977
  ```ruby
@@ -994,6 +1033,8 @@ Screenshot:
994
1033
 
995
1034
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) provides the `observe(model, attribute) { ... }` keyword to employ the [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) as per [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (Model View Controller), enabling Views to observe Models and update themselves in response to changes. If the `observe` keyword is used from inside a [Component](#hello-component), when the Component is removed or its top-level element is removed, the observer is automatically cleaned up. The need for such explicit observers is significantly diminished by the availablility of the more advanced Unidirectional [Data-Binding](#hello-data-binding) Support and Bidirectional [Data-Binding](#hello-data-binding) Support.
996
1035
 
1036
+ [lib/glimmer-dsl-web/samples/hello/hello_observer.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer.rb)
1037
+
997
1038
  Glimmer HTML DSL Ruby code in the frontend:
998
1039
 
999
1040
  ```ruby
@@ -1017,48 +1058,81 @@ class HelloObserver
1017
1058
  after_render do
1018
1059
  @number_input.value = @number_holder.number
1019
1060
  @range_input.value = @number_holder.number
1020
- # Observe Model attribute @number_holder.number for changes and update View
1021
- # Observer is automatically cleaned up if remove method is called on rendered HelloObserver
1022
- # or its top-level element
1061
+
1062
+ # Observe Model attribute @number_holder.number for changes and update View elements.
1063
+ # Observer is automatically cleaned up when `remove` method is called on rendered
1064
+ # HelloObserver web component or its top-level markup element (div)
1023
1065
  observe(@number_holder, :number) do
1024
1066
  number_string = @number_holder.number.to_s
1025
1067
  @number_input.value = number_string unless @number_input.value == number_string
1026
1068
  @range_input.value = number_string unless @range_input.value == number_string
1027
1069
  end
1028
- # Bidirectional Data-Binding does the same thing automatically
1029
- # Just disable the observe block above as well as the oninput listeners below
1030
- # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
1031
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
1070
+ # Bidirectional Data-Binding does the same thing automatically as per alternative sample: Hello, Observer (Data-Binding)!
1032
1071
  end
1033
1072
 
1034
1073
  markup {
1035
1074
  div {
1036
1075
  div {
1037
1076
  @number_input = input(type: 'number', min: 0, max: 100) {
1038
- # oninput listener updates Model attribute @number_holder.number
1077
+ # oninput listener (observer) updates Model attribute @number_holder.number
1039
1078
  oninput do
1040
1079
  @number_holder.number = @number_input.value.to_i
1041
1080
  end
1042
-
1043
- # Bidirectional Data-Binding simplifies the implementation significantly
1044
- # by enabling the following line and disabling oninput listeners as well
1045
- # as the after_body observe block observer
1046
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
1047
- # value <=> [@number_holder, :number]
1048
1081
  }
1049
1082
  }
1050
1083
  div {
1051
1084
  @range_input = input(type: 'range', min: 0, max: 100) {
1052
- # oninput listener updates Model attribute @number_holder.number
1085
+ # oninput listener (observer) updates Model attribute @number_holder.number
1053
1086
  oninput do
1054
1087
  @number_holder.number = @range_input.value.to_i
1055
1088
  end
1056
-
1057
- # Bidirectional Data-Binding simplifies the implementation significantly
1058
- # by enabling the following line and disabling oninput listeners as well
1059
- # as the after_body observe block observer
1060
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
1061
- # value <=> [@number_holder, :number]
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+ end
1094
+ ```
1095
+
1096
+ Screenshot:
1097
+
1098
+ ![Hello, Observer!](/images/glimmer-dsl-web-samples-hello-hello-observer.gif)
1099
+
1100
+ **Hello, Observer (Data-Binding)!**
1101
+
1102
+ This is the data-binding edition of Hello, Observer!, which uses the `<=>` operator to perform bidirectional data-binding between a View property and a Model attribute, thus yield a lot less code that is declarative and is the most minimal code possible to express the requirements.
1103
+
1104
+ [lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb)
1105
+
1106
+ Glimmer HTML DSL Ruby code in the frontend:
1107
+
1108
+ ```ruby
1109
+ require 'glimmer-dsl-web'
1110
+
1111
+ class NumberHolder
1112
+ attr_accessor :number
1113
+
1114
+ def initialize
1115
+ self.number = 50
1116
+ end
1117
+ end
1118
+
1119
+ class HelloObserver
1120
+ include Glimmer::Web::Component
1121
+
1122
+ before_render do
1123
+ @number_holder = NumberHolder.new
1124
+ end
1125
+
1126
+ markup {
1127
+ div {
1128
+ div {
1129
+ input(type: 'number', min: 0, max: 100) {
1130
+ value <=> [@number_holder, :number]
1131
+ }
1132
+ }
1133
+ div {
1134
+ input(type: 'range', min: 0, max: 100) {
1135
+ value <=> [@number_holder, :number]
1062
1136
  }
1063
1137
  }
1064
1138
  }
@@ -1100,7 +1174,9 @@ Learn more about the differences between various [Glimmer](https://github.com/An
1100
1174
  - [Hello, World!](#hello-world)
1101
1175
  - [Hello, Button!](#hello-button)
1102
1176
  - [Hello, Form!](#hello-form)
1177
+ - [Hello, Form (MVP)!](#hello-form-mvp)
1103
1178
  - [Hello, Observer!](#hello-observer)
1179
+ - [Hello, Observer (Data-Binding)!](#hello-observer)
1104
1180
  - [Hello, Data-Binding!](#hello-data-binding)
1105
1181
  - [Hello, Content Data-Binding!](#hello-content-data-binding)
1106
1182
  - [Hello, Component!](#hello-content-data-binding)
@@ -1135,6 +1211,8 @@ You can setup Glimmer DSL for Web in [Rails 7](#rails-7), [Rails 6](#rails-6), o
1135
1211
 
1136
1212
  ### Rails 7
1137
1213
 
1214
+ (NOTE: In the future, we plan to automate the setup steps below. If you would like to help contribute that to the project, please do so and open a Pull Request.)
1215
+
1138
1216
  Please follow these steps to setup.
1139
1217
 
1140
1218
  Install a Rails 7 gem:
@@ -1152,7 +1230,7 @@ rails new glimmer_app_server
1152
1230
  Add the following to `Gemfile`:
1153
1231
 
1154
1232
  ```
1155
- gem 'glimmer-dsl-web', '~> 0.2.4'
1233
+ gem 'glimmer-dsl-web', '~> 0.2.6'
1156
1234
  ```
1157
1235
 
1158
1236
  Run:
@@ -1354,6 +1432,8 @@ Next, read [Usage](#usage) instructions, and check out [Samples](#samples).
1354
1432
 
1355
1433
  ### Rails 6
1356
1434
 
1435
+ (NOTE: In the future, we plan to automate the setup steps below. If you would like to help contribute that to the project, please do so and open a Pull Request.)
1436
+
1357
1437
  Please follow these steps to setup.
1358
1438
 
1359
1439
  Install a Rails 6 gem:
@@ -1377,7 +1457,7 @@ Disable the `webpacker` gem line in `Gemfile`:
1377
1457
  Add the following to `Gemfile`:
1378
1458
 
1379
1459
  ```ruby
1380
- gem 'glimmer-dsl-web', '~> 0.2.4'
1460
+ gem 'glimmer-dsl-web', '~> 0.2.6'
1381
1461
  ```
1382
1462
 
1383
1463
  Run:
@@ -1632,14 +1712,18 @@ This project is inspired by [Glimmer DSL for Opal](https://github.com/AndyObtiva
1632
1712
 
1633
1713
  ## Samples
1634
1714
 
1635
- This external sample app contains all the samples mentioned below configured inside a Rails [Opal](https://opalrb.com/) app with all the prerequisites ready to go for convenience:
1715
+ This external Sample Selector app is built using Rails and Glimmer DSL for Web, including all the samples mentioned below configured inside a Rails [Opal](https://opalrb.com/) web app with all the prerequisites ready to go for convenience (there is a very minimal [Standalone [No Rails] static site sample app](https://github.com/Largo/glimmer-dsl-web-standalone-demo) too):
1636
1716
 
1637
1717
  https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app
1638
1718
 
1719
+ ![Sample Selector](https://raw.githubusercontent.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/master/sample-glimmer-dsl-web-rails7-app.png)
1720
+
1639
1721
  ### Hello Samples
1640
1722
 
1641
1723
  #### Hello, World!
1642
1724
 
1725
+ [lib/glimmer-dsl-web/samples/hello/hello_world.rb](/lib/glimmer-dsl-web/samples/hello/hello_world.rb)
1726
+
1643
1727
  Glimmer HTML DSL Ruby code in the frontend:
1644
1728
 
1645
1729
  ```ruby
@@ -1692,6 +1776,8 @@ You can also mount the `div` elsewhere by passing the `parent: parent_css_select
1692
1776
 
1693
1777
  Event listeners can be setup on any element using the same event names used in HTML (e.g. `onclick`) while passing in a standard Ruby block to handle behavior. `$$` gives access to JS global scope from Ruby to invoke functions like `alert`.
1694
1778
 
1779
+ [lib/glimmer-dsl-web/samples/hello/hello_button.rb](/lib/glimmer-dsl-web/samples/hello/hello_button.rb)
1780
+
1695
1781
  Glimmer HTML DSL Ruby code in the frontend:
1696
1782
 
1697
1783
  ```ruby
@@ -1726,6 +1812,8 @@ Screenshot:
1726
1812
 
1727
1813
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) gives access to all Web Browser built-in features like HTML form validations, input focus, events, and element functions from a very terse and productive Ruby HTML DSL. Also, you can apply CSS styles by including directly in Ruby code as a string, using [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css), or managing CSS completely separately using something like [SCSS](https://sass-lang.com/). The CSS techniques could be combined as well, like by managing common reusable CSS styles separately in SCSS, but adding component specific CSS styles in Ruby when it is more convenient.
1728
1814
 
1815
+ [lib/glimmer-dsl-web/samples/hello/hello_form.rb](/lib/glimmer-dsl-web/samples/hello/hello_form.rb)
1816
+
1729
1817
  Glimmer HTML DSL Ruby code in the frontend:
1730
1818
 
1731
1819
  ```ruby
@@ -1878,10 +1966,63 @@ Screenshot:
1878
1966
 
1879
1967
  ![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
1880
1968
 
1969
+ #### Hello, Form (MVP)!
1970
+
1971
+ This is the MVP (Model-View-Presenter) edition of Hello, Form! leveraging Glimmer Web Components and the MVP Architectural Pattern.
1972
+
1973
+ Main file:
1974
+
1975
+ [lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb](/lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb)
1976
+
1977
+ Other files:
1978
+
1979
+ [lib/glimmer-dsl-web/samples/hello/hello_form_mvp](/lib/glimmer-dsl-web/samples/hello/hello_form_mvp)
1980
+
1981
+ Glimmer HTML DSL Ruby code in the frontend:
1982
+
1983
+ ```ruby
1984
+ require 'glimmer-dsl-web'
1985
+
1986
+ require_relative 'hello_form_mvp/presenters/hello_form_mvp_presenter'
1987
+
1988
+ require_relative 'hello_form_mvp/views/contact_form'
1989
+ require_relative 'hello_form_mvp/views/contact_table'
1990
+
1991
+ class HelloFormMvp
1992
+ include Glimmer::Web::Component
1993
+
1994
+ before_render do
1995
+ @presenter = HelloFormMvpPresenter.new
1996
+ end
1997
+
1998
+ markup {
1999
+ div {
2000
+ h1('Contact Form')
2001
+
2002
+ contact_form(presenter: @presenter)
2003
+
2004
+ h1('Contacts Table')
2005
+
2006
+ contact_table(presenter: @presenter)
2007
+ }
2008
+ }
2009
+ end
2010
+
2011
+ Document.ready? do
2012
+ HelloFormMvp.render
2013
+ end
2014
+ ```
2015
+
2016
+ Screenshot:
2017
+
2018
+ ![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
2019
+
1881
2020
  #### Hello, Observer!
1882
2021
 
1883
2022
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) provides the `observe(model, attribute) { ... }` keyword to employ the [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) as per [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (Model View Controller), enabling Views to observe Models and update themselves in response to changes. If the `observe` keyword is used from inside a [Component](#hello-component), when the Component is removed or its top-level element is removed, the observer is automatically cleaned up. The need for such explicit observers is significantly diminished by the availablility of the more advanced Unidirectional [Data-Binding](#hello-data-binding) Support and Bidirectional [Data-Binding](#hello-data-binding) Support.
1884
2023
 
2024
+ [lib/glimmer-dsl-web/samples/hello/hello_observer.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer.rb)
2025
+
1885
2026
  Glimmer HTML DSL Ruby code in the frontend:
1886
2027
 
1887
2028
  ```ruby
@@ -1905,48 +2046,85 @@ class HelloObserver
1905
2046
  after_render do
1906
2047
  @number_input.value = @number_holder.number
1907
2048
  @range_input.value = @number_holder.number
1908
- # Observe Model attribute @number_holder.number for changes and update View
1909
- # Observer is automatically cleaned up if remove method is called on rendered HelloObserver
1910
- # or its top-level element
2049
+
2050
+ # Observe Model attribute @number_holder.number for changes and update View elements.
2051
+ # Observer is automatically cleaned up when `remove` method is called on rendered
2052
+ # HelloObserver web component or its top-level markup element (div)
1911
2053
  observe(@number_holder, :number) do
1912
2054
  number_string = @number_holder.number.to_s
1913
2055
  @number_input.value = number_string unless @number_input.value == number_string
1914
2056
  @range_input.value = number_string unless @range_input.value == number_string
1915
2057
  end
1916
- # Bidirectional Data-Binding does the same thing automatically
1917
- # Just disable the observe block above as well as the oninput listeners below
1918
- # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
1919
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
2058
+ # Bidirectional Data-Binding does the same thing automatically as per alternative sample: Hello, Observer (Data-Binding)!
1920
2059
  end
1921
2060
 
1922
2061
  markup {
1923
2062
  div {
1924
2063
  div {
1925
2064
  @number_input = input(type: 'number', min: 0, max: 100) {
1926
- # oninput listener updates Model attribute @number_holder.number
2065
+ # oninput listener (observer) updates Model attribute @number_holder.number
1927
2066
  oninput do
1928
2067
  @number_holder.number = @number_input.value.to_i
1929
2068
  end
1930
-
1931
- # Bidirectional Data-Binding simplifies the implementation significantly
1932
- # by enabling the following line and disabling oninput listeners as well
1933
- # as the after_body observe block observer
1934
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
1935
- # value <=> [@number_holder, :number]
1936
2069
  }
1937
2070
  }
1938
2071
  div {
1939
2072
  @range_input = input(type: 'range', min: 0, max: 100) {
1940
- # oninput listener updates Model attribute @number_holder.number
2073
+ # oninput listener (observer) updates Model attribute @number_holder.number
1941
2074
  oninput do
1942
2075
  @number_holder.number = @range_input.value.to_i
1943
2076
  end
1944
-
1945
- # Bidirectional Data-Binding simplifies the implementation significantly
1946
- # by enabling the following line and disabling oninput listeners as well
1947
- # as the after_body observe block observer
1948
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
1949
- # value <=> [@number_holder, :number]
2077
+ }
2078
+ }
2079
+ }
2080
+ }
2081
+ end
2082
+
2083
+ Document.ready? do
2084
+ HelloObserver.render
2085
+ end
2086
+ ```
2087
+
2088
+ Screenshot:
2089
+
2090
+ ![Hello, Observer!](/images/glimmer-dsl-web-samples-hello-hello-observer.gif)
2091
+
2092
+ #### Hello, Observer (Data-Binding)!
2093
+
2094
+ This is the data-binding edition of Hello, Observer!, which uses the `<=>` operator to perform bidirectional data-binding between a View property and a Model attribute, thus yield a lot less code that is declarative and is the most minimal code possible to express the requirements.
2095
+
2096
+ [lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb)
2097
+
2098
+ Glimmer HTML DSL Ruby code in the frontend:
2099
+
2100
+ ```ruby
2101
+ require 'glimmer-dsl-web'
2102
+
2103
+ class NumberHolder
2104
+ attr_accessor :number
2105
+
2106
+ def initialize
2107
+ self.number = 50
2108
+ end
2109
+ end
2110
+
2111
+ class HelloObserver
2112
+ include Glimmer::Web::Component
2113
+
2114
+ before_render do
2115
+ @number_holder = NumberHolder.new
2116
+ end
2117
+
2118
+ markup {
2119
+ div {
2120
+ div {
2121
+ input(type: 'number', min: 0, max: 100) {
2122
+ value <=> [@number_holder, :number]
2123
+ }
2124
+ }
2125
+ div {
2126
+ input(type: 'range', min: 0, max: 100) {
2127
+ value <=> [@number_holder, :number]
1950
2128
  }
1951
2129
  }
1952
2130
  }
@@ -1966,6 +2144,8 @@ Screenshot:
1966
2144
 
1967
2145
  [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) intuitively supports both Unidirectional (One-Way) Data-Binding via the `<=` operator and Bidirectional (Two-Way) Data-Binding via the `<=>` operator, incredibly simplifying how to sync View properties with Model attributes with the simplest code to reason about.
1968
2146
 
2147
+ [lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb)
2148
+
1969
2149
  Glimmer HTML DSL Ruby code in the frontend:
1970
2150
 
1971
2151
  ```ruby
@@ -2139,6 +2319,8 @@ If you need to regenerate HTML element content dynamically, you can use Content
2139
2319
  rebuild HTML elements based on changes in a Model attribute that provides the source data.
2140
2320
  In this example, we generate multiple address forms based on the number of addresses the user has.
2141
2321
 
2322
+ [lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb)
2323
+
2142
2324
  Glimmer HTML DSL Ruby code in the frontend:
2143
2325
 
2144
2326
  ```ruby
@@ -2278,6 +2460,8 @@ Rails helper (more about it in [Hello, glimmer_component Rails Helper!](#hello-g
2278
2460
  Below, we define an `AddressForm` component that generates an `address_form` keyword, and then we
2279
2461
  reuse it twice inside an `AddressPage` component displaying a Shipping Address and a Billing Address.
2280
2462
 
2463
+ [lib/glimmer-dsl-web/samples/hello/hello_component.rb](/lib/glimmer-dsl-web/samples/hello/hello_component.rb)
2464
+
2281
2465
  Glimmer HTML DSL Ruby code in the frontend:
2282
2466
 
2283
2467
  ```ruby
@@ -2493,6 +2677,8 @@ You may insert a Glimmer component anywhere into a Rails View using
2493
2677
  `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper`
2494
2678
  or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
2495
2679
 
2680
+ [lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb](/lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb)
2681
+
2496
2682
  Rails `ApplicationHelper` setup code:
2497
2683
 
2498
2684
  ```ruby
@@ -2724,6 +2910,8 @@ it returns a `String` to enable code like this:
2724
2910
 
2725
2911
  In any case, below is a full example leveraging the Glimmer HTML DSL alternative approach when utilizing formatting elements underneath a paragraph.
2726
2912
 
2913
+ [lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb](/lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb)
2914
+
2727
2915
  Glimmer HTML DSL Ruby code in the frontend:
2728
2916
 
2729
2917
  ```ruby
@@ -2785,6 +2973,8 @@ Screenshot:
2785
2973
 
2786
2974
  #### Hello, Input (Date/Time)!
2787
2975
 
2976
+ [lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb](/lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb)
2977
+
2788
2978
  Glimmer HTML DSL Ruby code in the frontend:
2789
2979
 
2790
2980
  ```ruby
@@ -2890,6 +3080,8 @@ Screenshot:
2890
3080
 
2891
3081
  #### Button Counter
2892
3082
 
3083
+ [lib/glimmer-dsl-web/samples/regular/button_counter.rb](/lib/glimmer-dsl-web/samples/regular/button_counter.rb)
3084
+
2893
3085
  Glimmer HTML DSL Ruby code in the frontend demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
2894
3086
 
2895
3087
  ```ruby
@@ -2992,11 +3184,40 @@ Learn more by reading the [GPG](https://github.com/AndyObtiva/glimmer/blob/maste
2992
3184
 
2993
3185
  ## Help
2994
3186
 
3187
+ ### FAQ
3188
+
3189
+ F.A.Q. (Frequently Asked Questions):
3190
+
3191
+ **Can I reuse JavaScript libraries from Glimmer DSL for Web in Ruby?**
3192
+
3193
+ Absolutely. Glimmer DSL for Web can integrate with any JavaScript libraries. You can either load the JavaScript libraries in advance by linking to them in the Rails View/Layout (e.g. linking to JS library CDN URLs) or by including JavaScript files in the lookup directories of Opal Ruby, and adding a Ruby `require('path_to_js_lib')` call in the code. In Ruby, the `$$` global variable gives access to the top-level JavaScript global scope, which enables invocations on any JavaScript objects. For example, `$$.hljs` gives access to the loaded `window.hljs` object for the Highlight.js library, and that enables invoking any functions from that library as needed, like `$$.hljs.highlightAll` to activate code syntax highlighting.
3194
+
3195
+ **How does Glimmer DSL for Web compare to Rails Hotwire (Turbo)?**
3196
+
3197
+ Glimmer DSL for Web is a Frontend library, meaning it replaces the JavaScript layer in a web application (e.g. Rails app) with Ruby code. On the other hand, Rails Hotwire (Turbo) is mostly a Backend-driven technology that enables automatically replacing HTML DOM elements with HTML markup sent over the wire from a Rails Backend. So, the two technologies are mostly orthogonal, but can be used in the same Rails web application, albeit exclusively on separate web pages at the moment. In the future, we might explore supporting the ability to combine both technologies in the same pages, but until then, use on separate pages. Glimmer DSL for Web can handle any sort of Frontend interactions even without making HTTP calls to the Backend, so it can implement more use-cases than Hotwire. Using Glimmer DSL for Web with Rails API end-points is simpler than using Hotwire overall. That is because Glimmer Content Data-Binding is a simpler version of Turbo Frames that does not require worrying about setting and using element IDs (as that is handled automatically) and that operates at a more correct abstraction level for how we think about View component updates in relation to Model changes (we make updates at the Model layer, and they automatically get reflected in the View through data-binding). Also, Glimmer DSL for Web supports Glimmer Web Components, which enable better division and organization of View code into modular components than ERB plus having higher readability. It is true that Hotwire is an improvement over using JavaScript frameworks like React when there is not much logic beyond updating elements with Server-Side rendered HTML. And, Glimmer DSL for Web is the next big advancement that provides an even simpler approach while also giving you full Frontend Development capabilities using Ruby in the Browser.
3198
+
3199
+ **How does Glimmer DSL for Web compare to Phlex or ViewComponent?**
3200
+
3201
+ Glimmer DSL for Web is a Frontend library, meaning it replaces the JavaScript layer in a web application (e.g. Rails app) with Ruby code while providing its own View-layer component support using [`Glimmer::Web::Component`](#hello-component). On the other hand, Phlex and ViewComponent are Backend View-layer component libraries, so they serve 100% orthogonal needs, and can be used side by side with Glimmer DSL for Web if needed (e.g. Phlex components can consist of Backend HTML elements + Glimmer components that are rendered in the Frontend). That said, Phlex came out years after previous Glimmer libraries like Glimmer DSL for SWT, and Phlex's component system is very similar to Glimmer's component system in Glimmer DSL for SWT (perhaps inspired by it unconsciously or consciously). So, if you like Phlex in the Backend, you'll love Glimmer DSL for Web in the Frontend.
3202
+
3203
+ **How does Glimmer DSL for Web compare to Angular, React, Vue, Svelte, or other JS frameworks?**
3204
+
3205
+ Without delving into details, Glimmer DSL for Web is meant to be a Ruby-based drop-in replacement for Angular, React, Vue, Svelte, and other JS frameworks. Additionally, it enables writing both structure code and logic code in the same language (Ruby), greatly simplifying maintainability and improving productivity by eliminating multi-language dissonance and friction that drags down productivity as there is no need anymore to think in multiple languages unnecessarily, use XML based solutions (e.g. JSX), or use templating solutions (e.g. Mustache). Lastly, Glimmer DSL for Web supports familiar Software Engineering architectural patterns like Model-View-Controller and Model-View-Presenter, enabling Software Engineers to write the lightest and simplest code possible for building Web frontends in Ruby, with the best separation of concerns. Software Engineers can finally become happy Ruby developers by writing Ruby code in the Frontend too now in addition to the Backend.
3206
+
3207
+ **How do I have a Glimmer Web Component re-render in a similar way to how React components re-render?**
3208
+
3209
+ [Content Data-Binding](#hello-content-data-binding) supports re-rendering dynamic parts of a Glimmer Web Component (or the full component if all of it is dynamic). Glimmer DSL for Web simplifies Frontend Development significantly over React by not re-rendering everything if not needed (regardless of use of Virtual DOM) yet only re-rendering the parts of a component that do change dynamically. As a result, Software Engineers do not have to worry about the ripple effect of full re-renders or the possibility of breaking some parts of a page when making small changes to a single component in a hierarchy of page components. And, if only an element property changes, [Content Data-Binding](#hello-content-data-binding) is not even needed. It is much simpler to rely on simple [Property Data-Binding](#hello-data-binding) in that case. This makes reasoning about Glimmer DSL for Web Ruby code a lot simpler than reasoning about React component JavaScript code.
3210
+
3211
+ **How do I reuse React components from Glimmer DSL for Web?**
3212
+
3213
+ In the future, support for HTML Web Components will be added, and that will enable reuse of React components by using a library that converts them to HTML Web Components first like [react-to-web-component](https://github.com/bitovi/react-to-web-component) or [react-webcomponent](https://github.com/adobe/react-webcomponent).
3214
+
3215
+
2995
3216
  ### Issues
2996
3217
 
2997
- You may submit [issues](https://github.com/AndyObtiva/glimmer-dsl-web /issues) on [GitHub](https://github.com/AndyObtiva/glimmer-dsl-web /issues).
3218
+ You may submit [issues](https://github.com/AndyObtiva/glimmer-dsl-web/issues) on [GitHub](https://github.com/AndyObtiva/glimmer-dsl-web/issues).
2998
3219
 
2999
- [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer-dsl-web /issues)
3220
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer-dsl-web/issues)
3000
3221
 
3001
3222
  ### Chat
3002
3223
 
@@ -3020,7 +3241,7 @@ These features have been suggested. You might see them in a future version of Gl
3020
3241
 
3021
3242
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
3022
3243
 
3023
- [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer-dsl-web /graphs/contributors)
3244
+ [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer-dsl-web/graphs/contributors)
3024
3245
 
3025
3246
  ## License
3026
3247
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.2.6
@@ -2,17 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-web 0.2.4 ruby lib
5
+ # stub: glimmer-dsl-web 0.2.6 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-web".freeze
9
- s.version = "0.2.4"
9
+ s.version = "0.2.6".freeze
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2024-02-04"
15
- s.description = "Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework) enables building Web Frontends using Ruby in the Browser, as per Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework follows the Ruby way (with DSLs and TIMTOWTDI) and the Rails way (Convention over Configuration) in building Isomorphic Ruby on Rails Applications. It provides a Ruby HTML DSL, which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) Data-Binding (using <=) and Bidirectional (Two-Way) Data-Binding (using <=>). Dynamic rendering (and re-rendering) of HTML content is also supported via Content Data-Binding. And, modular design is supported with Glimmer Web Components. Many samples are demonstrated in the Rails sample app (there is a very minimal Standalone [No Rails] sample app too). You can finally live in pure Rubyland on the Web in both the frontend and backend with Glimmer DSL for Web! This gem relies on Opal Ruby.".freeze
14
+ s.date = "2024-05-26"
15
+ s.description = "Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework) enables building Web Frontends using Ruby in the Browser, as per Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend framework in existence. The framework follows the Ruby way (with DSLs and TIMTOWTDI) and the Rails way (Convention over Configuration) in building Isomorphic Ruby on Rails Applications. It provides a Ruby HTML DSL, which uniquely enables writing both structure code and logic code in one language. It supports both Unidirectional (One-Way) Data-Binding (using <=) and Bidirectional (Two-Way) Data-Binding (using <=>). Dynamic rendering (and re-rendering) of HTML content is also supported via Content Data-Binding. Modular design is supported with Glimmer Web Components. And, a Ruby CSS DSL is supported with the included Glimmer DSL for CSS. Many samples are demonstrated in the Rails sample app (there is a very minimal Standalone [No Rails] sample app too). You can finally live in pure Rubyland on the Web in both the frontend and backend with Glimmer DSL for Web! This gem relies on Opal Ruby.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
18
18
  "CHANGELOG.md",
@@ -36,9 +36,15 @@ Gem::Specification.new do |s|
36
36
  "lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb",
37
37
  "lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb",
38
38
  "lib/glimmer-dsl-web/samples/hello/hello_form.rb",
39
+ "lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb",
40
+ "lib/glimmer-dsl-web/samples/hello/hello_form_mvp/models/contact.rb",
41
+ "lib/glimmer-dsl-web/samples/hello/hello_form_mvp/presenters/hello_form_mvp_presenter.rb",
42
+ "lib/glimmer-dsl-web/samples/hello/hello_form_mvp/views/contact_form.rb",
43
+ "lib/glimmer-dsl-web/samples/hello/hello_form_mvp/views/contact_table.rb",
39
44
  "lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb",
40
45
  "lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb",
41
46
  "lib/glimmer-dsl-web/samples/hello/hello_observer.rb",
47
+ "lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb",
42
48
  "lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb",
43
49
  "lib/glimmer-dsl-web/samples/hello/hello_world.rb",
44
50
  "lib/glimmer-dsl-web/samples/regular/button_counter.rb",
@@ -70,24 +76,24 @@ Gem::Specification.new do |s|
70
76
  ]
71
77
  s.homepage = "http://github.com/AndyObtiva/glimmer-dsl-web".freeze
72
78
  s.licenses = ["MIT".freeze]
73
- s.rubygems_version = "3.4.10".freeze
79
+ s.rubygems_version = "3.5.3".freeze
74
80
  s.summary = "Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework)".freeze
75
81
 
76
82
  s.specification_version = 4
77
83
 
78
- s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 2.7.6"])
79
- s.add_runtime_dependency(%q<glimmer-dsl-xml>.freeze, ["~> 1.3.2"])
80
- s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.2.3"])
81
- s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2"])
82
- s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3"])
83
- s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.1"])
84
- s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.5.1"])
85
- s.add_runtime_dependency(%q<to_collection>.freeze, [">= 2.0.1", "< 3.0.0"])
86
- s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 0"])
87
- s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0", "< 14.0.0"])
88
- s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
89
- s.add_development_dependency(%q<jeweler>.freeze, [">= 2.3.9", "< 3.0.0"])
90
- s.add_development_dependency(%q<rdoc>.freeze, [">= 6.2.1", "< 7.0.0"])
91
- s.add_development_dependency(%q<opal-rspec>.freeze, ["~> 0.8.0.alpha2"])
84
+ s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 2.7.6".freeze])
85
+ s.add_runtime_dependency(%q<glimmer-dsl-xml>.freeze, ["~> 1.4.0".freeze])
86
+ s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.4.0".freeze])
87
+ s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2".freeze])
88
+ s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3".freeze])
89
+ s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.1".freeze])
90
+ s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.5.1".freeze])
91
+ s.add_runtime_dependency(%q<to_collection>.freeze, [">= 2.0.1".freeze, "< 3.0.0".freeze])
92
+ s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 0".freeze])
93
+ s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0".freeze, "< 14.0.0".freeze])
94
+ s.add_development_dependency(%q<rake-tui>.freeze, [">= 0".freeze])
95
+ s.add_development_dependency(%q<jeweler>.freeze, [">= 2.3.9".freeze, "< 3.0.0".freeze])
96
+ s.add_development_dependency(%q<rdoc>.freeze, [">= 6.2.1".freeze, "< 7.0.0".freeze])
97
+ s.add_development_dependency(%q<opal-rspec>.freeze, ["~> 0.8.0.alpha2".freeze])
92
98
  end
93
99
 
@@ -49,6 +49,7 @@ module Glimmer
49
49
  def_option_attr_accessors(new_options)
50
50
  end
51
51
  end
52
+ alias attributes options
52
53
 
53
54
  def option(new_option, default: nil)
54
55
  new_option = new_option.to_s.to_sym
@@ -58,6 +59,7 @@ module Glimmer
58
59
  'def_option_attr_accessors(new_options)'
59
60
  def_option_attr_accessors(new_options)
60
61
  end
62
+ alias attribute option
61
63
 
62
64
  def def_option_attr_accessors(new_options)
63
65
  new_options.each do |option, default|
@@ -22,6 +22,7 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  module ::Kernel
25
+ # TODO contribute to Opal
25
26
  alias puts_without_glimmer puts
26
27
  def puts(*strs)
27
28
  puts_without_glimmer(*strs)
@@ -31,6 +32,7 @@ module ::Kernel
31
32
  end
32
33
  end
33
34
 
35
+ # TODO contribute to Opal
34
36
  alias p_without_glimmer p
35
37
  def p(*args)
36
38
  p_without_glimmer(*args)
@@ -23,6 +23,8 @@ require 'glimmer-dsl-web'
23
23
 
24
24
  include Glimmer
25
25
 
26
+ # This is an introductory version of Hello, Form!
27
+ # Check out Hello, Form (MVP)! for a more serious advanced version built with Glimmer Web Components.
26
28
  Document.ready? do
27
29
  div {
28
30
  h1('Contact Form')
@@ -0,0 +1 @@
1
+ Contact = Struct.new(:name, :email, keyword_init: true)
@@ -0,0 +1,25 @@
1
+ require_relative '../models/contact'
2
+
3
+ class HelloFormMvpPresenter
4
+ attr_accessor :contacts
5
+
6
+ def initialize
7
+ @contacts = fetch_contacts
8
+ end
9
+
10
+ def fetch_contacts
11
+ [
12
+ Contact.new(name: 'John Doe', email: 'johndoe@example.com'),
13
+ Contact.new(name: 'Jane Doe', email: 'janedoe@example.com'),
14
+ ]
15
+ end
16
+
17
+ def new_contact
18
+ @new_contact ||= Contact.new
19
+ end
20
+
21
+ def add_contact
22
+ contacts << Contact.new(name: new_contact.name, email: new_contact.email)
23
+ new_contact.name = new_contact.email = ''
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ class ContactForm
2
+ include Glimmer::Web::Component
3
+
4
+ option :presenter
5
+
6
+ markup {
7
+ form {
8
+ div {
9
+ label('Name: ', for: 'name-field')
10
+ @name_input = input(type: 'text', id: 'name-field', required: true, autofocus: true) {
11
+ value <=> [presenter.new_contact, :name]
12
+ }
13
+ }
14
+
15
+ div {
16
+ label('Email: ', for: 'email-field')
17
+ @email_input = input(type: 'email', id: 'email-field', required: true) {
18
+ value <=> [presenter.new_contact, :email]
19
+ }
20
+ }
21
+
22
+ div {
23
+ input(type: 'submit', value: 'Add Contact') {
24
+ onclick do |event|
25
+ if [@name_input, @email_input].all?(&:check_validity)
26
+ event.prevent_default
27
+ # adding contact model to presenter contacts array indirectly updates contacts table
28
+ presenter.add_contact
29
+ @name_input.focus
30
+ end
31
+ end
32
+ }
33
+ }
34
+
35
+ style {
36
+ # CSS can be included as Glimmer DSL for CSS syntax (Ruby code)
37
+ r('input') {
38
+ margin '5px'
39
+ }
40
+ r('input[type=submit]') {
41
+ margin '5px 0'
42
+ }
43
+ }
44
+ }
45
+ }
46
+ end
@@ -0,0 +1,42 @@
1
+ class ContactTable
2
+ include Glimmer::Web::Component
3
+
4
+ option :presenter
5
+
6
+ markup {
7
+ table {
8
+ thead {
9
+ tr {
10
+ th('Name')
11
+ th('Email')
12
+ }
13
+ }
14
+
15
+ tbody {
16
+ content(presenter, :contacts) {
17
+ presenter.contacts.each do |contact|
18
+ tr {
19
+ td(contact.name)
20
+ td(contact.email)
21
+ }
22
+ end
23
+ }
24
+ }
25
+
26
+ style {
27
+ # CSS can be included as Glimmer DSL for CSS syntax (Ruby code)
28
+ r('table') {
29
+ border '1px solid grey'
30
+ border_spacing '0'
31
+ }
32
+ r('table tr td, table tr th') {
33
+ padding '5px'
34
+ }
35
+ r('table tr:nth-child(even)') {
36
+ background '#ccc'
37
+ }
38
+ }
39
+ }
40
+ }
41
+ end
42
+
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-web'
23
+
24
+ require_relative 'hello_form_mvp/presenters/hello_form_mvp_presenter'
25
+
26
+ require_relative 'hello_form_mvp/views/contact_form'
27
+ require_relative 'hello_form_mvp/views/contact_table'
28
+
29
+ class HelloFormMvp
30
+ include Glimmer::Web::Component
31
+
32
+ before_render do
33
+ @presenter = HelloFormMvpPresenter.new
34
+ end
35
+
36
+ markup {
37
+ div {
38
+ h1('Contact Form')
39
+
40
+ contact_form(presenter: @presenter)
41
+
42
+ h1('Contacts Table')
43
+
44
+ contact_table(presenter: @presenter)
45
+ }
46
+ }
47
+ end
48
+
49
+ Document.ready? do
50
+ HelloFormMvp.render
51
+ end
@@ -39,48 +39,34 @@ class HelloObserver
39
39
  after_render do
40
40
  @number_input.value = @number_holder.number
41
41
  @range_input.value = @number_holder.number
42
- # Observe Model attribute @number_holder.number for changes and update View
43
- # Observer is automatically cleaned up if remove method is called on rendered HelloObserver
44
- # or its top-level element
42
+
43
+ # Observe Model attribute @number_holder.number for changes and update View elements.
44
+ # Observer is automatically cleaned up when `remove` method is called on rendered
45
+ # HelloObserver web component or its top-level markup element (div)
45
46
  observe(@number_holder, :number) do
46
47
  number_string = @number_holder.number.to_s
47
48
  @number_input.value = number_string unless @number_input.value == number_string
48
49
  @range_input.value = number_string unless @range_input.value == number_string
49
50
  end
50
- # Bidirectional Data-Binding does the same thing automatically
51
- # Just disable the observe block above as well as the oninput listeners below
52
- # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
53
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
51
+ # Bidirectional Data-Binding does the same thing automatically as per alternative sample: Hello, Observer (Data-Binding)!
54
52
  end
55
53
 
56
54
  markup {
57
55
  div {
58
56
  div {
59
57
  @number_input = input(type: 'number', min: 0, max: 100) {
60
- # oninput listener updates Model attribute @number_holder.number
58
+ # oninput listener (observer) updates Model attribute @number_holder.number
61
59
  oninput do
62
60
  @number_holder.number = @number_input.value.to_i
63
61
  end
64
-
65
- # Bidirectional Data-Binding simplifies the implementation significantly
66
- # by enabling the following line and disabling oninput listeners as well
67
- # as the after_body observe block observer
68
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
69
- # value <=> [@number_holder, :number]
70
62
  }
71
63
  }
72
64
  div {
73
65
  @range_input = input(type: 'range', min: 0, max: 100) {
74
- # oninput listener updates Model attribute @number_holder.number
66
+ # oninput listener (observer) updates Model attribute @number_holder.number
75
67
  oninput do
76
68
  @number_holder.number = @range_input.value.to_i
77
69
  end
78
-
79
- # Bidirectional Data-Binding simplifies the implementation significantly
80
- # by enabling the following line and disabling oninput listeners as well
81
- # as the after_body observe block observer
82
- # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
83
- # value <=> [@number_holder, :number]
84
70
  }
85
71
  }
86
72
  }
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-web'
23
+
24
+ class NumberHolder
25
+ attr_accessor :number
26
+
27
+ def initialize
28
+ self.number = 50
29
+ end
30
+ end
31
+
32
+ class HelloObserver
33
+ include Glimmer::Web::Component
34
+
35
+ before_render do
36
+ @number_holder = NumberHolder.new
37
+ end
38
+
39
+ markup {
40
+ div {
41
+ div {
42
+ input(type: 'number', min: 0, max: 100) {
43
+ value <=> [@number_holder, :number]
44
+ }
45
+ }
46
+ div {
47
+ input(type: 'range', min: 0, max: 100) {
48
+ value <=> [@number_holder, :number]
49
+ }
50
+ }
51
+ }
52
+ }
53
+ end
54
+
55
+ Document.ready? do
56
+ HelloObserver.render
57
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-04 00:00:00.000000000 Z
11
+ date: 2024-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.3.2
33
+ version: 1.4.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.3.2
40
+ version: 1.4.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: glimmer-dsl-css
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.2.3
47
+ version: 1.4.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.2.3
54
+ version: 1.4.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: opal
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -240,11 +240,11 @@ description: Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework) en
240
240
  both structure code and logic code in one language. It supports both Unidirectional
241
241
  (One-Way) Data-Binding (using <=) and Bidirectional (Two-Way) Data-Binding (using
242
242
  <=>). Dynamic rendering (and re-rendering) of HTML content is also supported via
243
- Content Data-Binding. And, modular design is supported with Glimmer Web Components.
244
- Many samples are demonstrated in the Rails sample app (there is a very minimal Standalone
245
- [No Rails] sample app too). You can finally live in pure Rubyland on the Web in
246
- both the frontend and backend with Glimmer DSL for Web! This gem relies on Opal
247
- Ruby.
243
+ Content Data-Binding. Modular design is supported with Glimmer Web Components. And,
244
+ a Ruby CSS DSL is supported with the included Glimmer DSL for CSS. Many samples
245
+ are demonstrated in the Rails sample app (there is a very minimal Standalone [No
246
+ Rails] sample app too). You can finally live in pure Rubyland on the Web in both
247
+ the frontend and backend with Glimmer DSL for Web! This gem relies on Opal Ruby.
248
248
  email: andy.am@gmail.com
249
249
  executables: []
250
250
  extensions: []
@@ -269,9 +269,15 @@ files:
269
269
  - lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb
270
270
  - lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb
271
271
  - lib/glimmer-dsl-web/samples/hello/hello_form.rb
272
+ - lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb
273
+ - lib/glimmer-dsl-web/samples/hello/hello_form_mvp/models/contact.rb
274
+ - lib/glimmer-dsl-web/samples/hello/hello_form_mvp/presenters/hello_form_mvp_presenter.rb
275
+ - lib/glimmer-dsl-web/samples/hello/hello_form_mvp/views/contact_form.rb
276
+ - lib/glimmer-dsl-web/samples/hello/hello_form_mvp/views/contact_table.rb
272
277
  - lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb
273
278
  - lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb
274
279
  - lib/glimmer-dsl-web/samples/hello/hello_observer.rb
280
+ - lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb
275
281
  - lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb
276
282
  - lib/glimmer-dsl-web/samples/hello/hello_world.rb
277
283
  - lib/glimmer-dsl-web/samples/regular/button_counter.rb
@@ -319,7 +325,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
319
325
  - !ruby/object:Gem::Version
320
326
  version: '0'
321
327
  requirements: []
322
- rubygems_version: 3.4.10
328
+ rubygems_version: 3.5.3
323
329
  signing_key:
324
330
  specification_version: 4
325
331
  summary: Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework)