glimmer-dsl-web 0.2.4 → 0.2.5

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: cefe3f3c8cf1e4e34eb1703d601e57a99e000ac5691542f275e8c998c6467914
4
+ data.tar.gz: f70430fa7d665432a870e01bf69a6e42b328c5b8def261cd1b3fb330451dd54f
5
5
  SHA512:
6
- metadata.gz: e590b30b6f3d54f859e168bd7220cfa51e8d3f2354836fb851ab20c21114518ddd2a502a74a262b704148561e64b65a07cbd2ed61593914a274fa7542da8e1c9
7
- data.tar.gz: 1a334ce05e730c2eef25005d5a1a9d16683b639fac910e83ba0ceff88c380230df884486f994140bce11a68e10210ff1a28266604560ecaa134859b621402cf2
6
+ metadata.gz: 1610a4360edd9019218e7d483e97b45d636da381250edba3616503d5677c4109818ee81d1b9ae2715688632214edadc96762c81c2fde2706ace9d7bae9a0dbc2
7
+ data.tar.gz: f35f136f78c03b53c264392cb1ee1c9bae16601b2992b721bea0a3748a6cbcff2e67012ef86d3e8cd4c2cdbfccec577921a4322833d5223bd27bbc0a114c16a4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.2.5
4
+
5
+ - Support `Element#class_name` as an alternative to `Element#class` because `class` is a reserved method in Ruby
6
+ - Handle case of data-binding an element that has no `type` attribute
7
+ - Provide `attribute`/`attributes` as aliases for `option`/`options` inside classes mixing `Glimmer::Web::Component` given that the options behave like HTML attributes
8
+ - Hello, Form MVP! Sample: `require 'glimmer-dsl-web/samples/hello/hello_form_mvp'`
9
+ - Hello, Observer (Data-Binding)! Sample: `require 'glimmer-dsl-web/samples/hello/hello_observer_data_binding'`
10
+
3
11
  ## 0.2.4
4
12
 
5
13
  - 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.5 (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)
@@ -1152,7 +1228,7 @@ rails new glimmer_app_server
1152
1228
  Add the following to `Gemfile`:
1153
1229
 
1154
1230
  ```
1155
- gem 'glimmer-dsl-web', '~> 0.2.4'
1231
+ gem 'glimmer-dsl-web', '~> 0.2.5'
1156
1232
  ```
1157
1233
 
1158
1234
  Run:
@@ -1377,7 +1453,7 @@ Disable the `webpacker` gem line in `Gemfile`:
1377
1453
  Add the following to `Gemfile`:
1378
1454
 
1379
1455
  ```ruby
1380
- gem 'glimmer-dsl-web', '~> 0.2.4'
1456
+ gem 'glimmer-dsl-web', '~> 0.2.5'
1381
1457
  ```
1382
1458
 
1383
1459
  Run:
@@ -1632,14 +1708,18 @@ This project is inspired by [Glimmer DSL for Opal](https://github.com/AndyObtiva
1632
1708
 
1633
1709
  ## Samples
1634
1710
 
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:
1711
+ 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
1712
 
1637
1713
  https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app
1638
1714
 
1715
+ ![Sample Selector](https://raw.githubusercontent.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app/master/sample-glimmer-dsl-web-rails7-app.png)
1716
+
1639
1717
  ### Hello Samples
1640
1718
 
1641
1719
  #### Hello, World!
1642
1720
 
1721
+ [lib/glimmer-dsl-web/samples/hello/hello_world.rb](/lib/glimmer-dsl-web/samples/hello/hello_world.rb)
1722
+
1643
1723
  Glimmer HTML DSL Ruby code in the frontend:
1644
1724
 
1645
1725
  ```ruby
@@ -1692,6 +1772,8 @@ You can also mount the `div` elsewhere by passing the `parent: parent_css_select
1692
1772
 
1693
1773
  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
1774
 
1775
+ [lib/glimmer-dsl-web/samples/hello/hello_button.rb](/lib/glimmer-dsl-web/samples/hello/hello_button.rb)
1776
+
1695
1777
  Glimmer HTML DSL Ruby code in the frontend:
1696
1778
 
1697
1779
  ```ruby
@@ -1726,6 +1808,8 @@ Screenshot:
1726
1808
 
1727
1809
  [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
1810
 
1811
+ [lib/glimmer-dsl-web/samples/hello/hello_form.rb](/lib/glimmer-dsl-web/samples/hello/hello_form.rb)
1812
+
1729
1813
  Glimmer HTML DSL Ruby code in the frontend:
1730
1814
 
1731
1815
  ```ruby
@@ -1878,10 +1962,63 @@ Screenshot:
1878
1962
 
1879
1963
  ![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
1880
1964
 
1965
+ #### Hello, Form (MVP)!
1966
+
1967
+ This is the MVP (Model-View-Presenter) edition of Hello, Form! leveraging Glimmer Web Components and the MVP Architectural Pattern.
1968
+
1969
+ Main file:
1970
+
1971
+ [lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb](/lib/glimmer-dsl-web/samples/hello/hello_form_mvp.rb)
1972
+
1973
+ Other files:
1974
+
1975
+ [lib/glimmer-dsl-web/samples/hello/hello_form_mvp](/lib/glimmer-dsl-web/samples/hello/hello_form_mvp)
1976
+
1977
+ Glimmer HTML DSL Ruby code in the frontend:
1978
+
1979
+ ```ruby
1980
+ require 'glimmer-dsl-web'
1981
+
1982
+ require_relative 'hello_form_mvp/presenters/hello_form_mvp_presenter'
1983
+
1984
+ require_relative 'hello_form_mvp/views/contact_form'
1985
+ require_relative 'hello_form_mvp/views/contact_table'
1986
+
1987
+ class HelloFormMvp
1988
+ include Glimmer::Web::Component
1989
+
1990
+ before_render do
1991
+ @presenter = HelloFormMvpPresenter.new
1992
+ end
1993
+
1994
+ markup {
1995
+ div {
1996
+ h1('Contact Form')
1997
+
1998
+ contact_form(presenter: @presenter)
1999
+
2000
+ h1('Contacts Table')
2001
+
2002
+ contact_table(presenter: @presenter)
2003
+ }
2004
+ }
2005
+ end
2006
+
2007
+ Document.ready? do
2008
+ HelloFormMvp.render
2009
+ end
2010
+ ```
2011
+
2012
+ Screenshot:
2013
+
2014
+ ![Hello, Form!](/images/glimmer-dsl-web-samples-hello-hello-form.gif)
2015
+
1881
2016
  #### Hello, Observer!
1882
2017
 
1883
2018
  [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
2019
 
2020
+ [lib/glimmer-dsl-web/samples/hello/hello_observer.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer.rb)
2021
+
1885
2022
  Glimmer HTML DSL Ruby code in the frontend:
1886
2023
 
1887
2024
  ```ruby
@@ -1905,48 +2042,85 @@ class HelloObserver
1905
2042
  after_render do
1906
2043
  @number_input.value = @number_holder.number
1907
2044
  @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
2045
+
2046
+ # Observe Model attribute @number_holder.number for changes and update View elements.
2047
+ # Observer is automatically cleaned up when `remove` method is called on rendered
2048
+ # HelloObserver web component or its top-level markup element (div)
1911
2049
  observe(@number_holder, :number) do
1912
2050
  number_string = @number_holder.number.to_s
1913
2051
  @number_input.value = number_string unless @number_input.value == number_string
1914
2052
  @range_input.value = number_string unless @range_input.value == number_string
1915
2053
  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
2054
+ # Bidirectional Data-Binding does the same thing automatically as per alternative sample: Hello, Observer (Data-Binding)!
1920
2055
  end
1921
2056
 
1922
2057
  markup {
1923
2058
  div {
1924
2059
  div {
1925
2060
  @number_input = input(type: 'number', min: 0, max: 100) {
1926
- # oninput listener updates Model attribute @number_holder.number
2061
+ # oninput listener (observer) updates Model attribute @number_holder.number
1927
2062
  oninput do
1928
2063
  @number_holder.number = @number_input.value.to_i
1929
2064
  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
2065
  }
1937
2066
  }
1938
2067
  div {
1939
2068
  @range_input = input(type: 'range', min: 0, max: 100) {
1940
- # oninput listener updates Model attribute @number_holder.number
2069
+ # oninput listener (observer) updates Model attribute @number_holder.number
1941
2070
  oninput do
1942
2071
  @number_holder.number = @range_input.value.to_i
1943
2072
  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]
2073
+ }
2074
+ }
2075
+ }
2076
+ }
2077
+ end
2078
+
2079
+ Document.ready? do
2080
+ HelloObserver.render
2081
+ end
2082
+ ```
2083
+
2084
+ Screenshot:
2085
+
2086
+ ![Hello, Observer!](/images/glimmer-dsl-web-samples-hello-hello-observer.gif)
2087
+
2088
+ #### Hello, Observer (Data-Binding)!
2089
+
2090
+ 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.
2091
+
2092
+ [lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb)
2093
+
2094
+ Glimmer HTML DSL Ruby code in the frontend:
2095
+
2096
+ ```ruby
2097
+ require 'glimmer-dsl-web'
2098
+
2099
+ class NumberHolder
2100
+ attr_accessor :number
2101
+
2102
+ def initialize
2103
+ self.number = 50
2104
+ end
2105
+ end
2106
+
2107
+ class HelloObserver
2108
+ include Glimmer::Web::Component
2109
+
2110
+ before_render do
2111
+ @number_holder = NumberHolder.new
2112
+ end
2113
+
2114
+ markup {
2115
+ div {
2116
+ div {
2117
+ input(type: 'number', min: 0, max: 100) {
2118
+ value <=> [@number_holder, :number]
2119
+ }
2120
+ }
2121
+ div {
2122
+ input(type: 'range', min: 0, max: 100) {
2123
+ value <=> [@number_holder, :number]
1950
2124
  }
1951
2125
  }
1952
2126
  }
@@ -1966,6 +2140,8 @@ Screenshot:
1966
2140
 
1967
2141
  [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
2142
 
2143
+ [lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb)
2144
+
1969
2145
  Glimmer HTML DSL Ruby code in the frontend:
1970
2146
 
1971
2147
  ```ruby
@@ -2139,6 +2315,8 @@ If you need to regenerate HTML element content dynamically, you can use Content
2139
2315
  rebuild HTML elements based on changes in a Model attribute that provides the source data.
2140
2316
  In this example, we generate multiple address forms based on the number of addresses the user has.
2141
2317
 
2318
+ [lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb)
2319
+
2142
2320
  Glimmer HTML DSL Ruby code in the frontend:
2143
2321
 
2144
2322
  ```ruby
@@ -2278,6 +2456,8 @@ Rails helper (more about it in [Hello, glimmer_component Rails Helper!](#hello-g
2278
2456
  Below, we define an `AddressForm` component that generates an `address_form` keyword, and then we
2279
2457
  reuse it twice inside an `AddressPage` component displaying a Shipping Address and a Billing Address.
2280
2458
 
2459
+ [lib/glimmer-dsl-web/samples/hello/hello_component.rb](/lib/glimmer-dsl-web/samples/hello/hello_component.rb)
2460
+
2281
2461
  Glimmer HTML DSL Ruby code in the frontend:
2282
2462
 
2283
2463
  ```ruby
@@ -2493,6 +2673,8 @@ You may insert a Glimmer component anywhere into a Rails View using
2493
2673
  `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper`
2494
2674
  or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
2495
2675
 
2676
+ [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)
2677
+
2496
2678
  Rails `ApplicationHelper` setup code:
2497
2679
 
2498
2680
  ```ruby
@@ -2724,6 +2906,8 @@ it returns a `String` to enable code like this:
2724
2906
 
2725
2907
  In any case, below is a full example leveraging the Glimmer HTML DSL alternative approach when utilizing formatting elements underneath a paragraph.
2726
2908
 
2909
+ [lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb](/lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb)
2910
+
2727
2911
  Glimmer HTML DSL Ruby code in the frontend:
2728
2912
 
2729
2913
  ```ruby
@@ -2785,6 +2969,8 @@ Screenshot:
2785
2969
 
2786
2970
  #### Hello, Input (Date/Time)!
2787
2971
 
2972
+ [lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb](/lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb)
2973
+
2788
2974
  Glimmer HTML DSL Ruby code in the frontend:
2789
2975
 
2790
2976
  ```ruby
@@ -2890,6 +3076,8 @@ Screenshot:
2890
3076
 
2891
3077
  #### Button Counter
2892
3078
 
3079
+ [lib/glimmer-dsl-web/samples/regular/button_counter.rb](/lib/glimmer-dsl-web/samples/regular/button_counter.rb)
3080
+
2893
3081
  Glimmer HTML DSL Ruby code in the frontend demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
2894
3082
 
2895
3083
  ```ruby
@@ -2992,11 +3180,35 @@ Learn more by reading the [GPG](https://github.com/AndyObtiva/glimmer/blob/maste
2992
3180
 
2993
3181
  ## Help
2994
3182
 
3183
+ ### FAQ
3184
+
3185
+ F.A.Q. (Frequently Asked Questions):
3186
+
3187
+ **How does Glimmer DSL for Web compare to Rails Hotwire (Turbo)?**
3188
+
3189
+ 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 of View code with higher readability than ERB. 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 brings a big improvement over the Hotwire approach.
3190
+
3191
+ **How does Glimmer DSL for Web compare to Phlex or ViewComponent?**
3192
+
3193
+ 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.
3194
+
3195
+ **How does Glimmer DSL for Web compare to Angular, React, Vue, Svelte, or other JS frameworks?**
3196
+
3197
+ 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.
3198
+
3199
+ **How do I have a Glimmer Web Component re-render in a similar way to how React components re-render?**
3200
+
3201
+ [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.
3202
+
3203
+ **How do I reuse React components from Glimmer DSL for Web?**
3204
+
3205
+ 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).
3206
+
2995
3207
  ### Issues
2996
3208
 
2997
- You may submit [issues](https://github.com/AndyObtiva/glimmer-dsl-web /issues) on [GitHub](https://github.com/AndyObtiva/glimmer-dsl-web /issues).
3209
+ You may submit [issues](https://github.com/AndyObtiva/glimmer-dsl-web/issues) on [GitHub](https://github.com/AndyObtiva/glimmer-dsl-web/issues).
2998
3210
 
2999
- [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer-dsl-web /issues)
3211
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer-dsl-web/issues)
3000
3212
 
3001
3213
  ### Chat
3002
3214
 
@@ -3020,7 +3232,7 @@ These features have been suggested. You might see them in a future version of Gl
3020
3232
 
3021
3233
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
3022
3234
 
3023
- [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer-dsl-web /graphs/contributors)
3235
+ [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer-dsl-web/graphs/contributors)
3024
3236
 
3025
3237
  ## License
3026
3238
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.2.5
@@ -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.5 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.5"
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-12"
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",
@@ -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.5
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-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -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