glimmer-dsl-web 0.2.6 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +213 -12
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +30 -18
- data/lib/glimmer/dsl/web/a_expression.rb +17 -0
- data/lib/glimmer/dsl/web/dsl.rb +2 -0
- data/lib/glimmer/dsl/web/element_expression.rb +1 -1
- data/lib/glimmer/dsl/web/style_expression.rb +7 -2
- data/lib/glimmer/web/element_proxy.rb +2 -1
- data/lib/glimmer/web/formatting_element_proxy.rb +2 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb +2 -2
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/models/todo.rb +44 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/presenters/todo_presenter.rb +102 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/edit_todo_input.rb +55 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_form.rb +30 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_input.rb +41 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_filters.rb +123 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_input.rb +30 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list.rb +88 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list_item.rb +158 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_mvc_footer.rb +45 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc.rb +87 -0
- metadata +17 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9f4b7e33522df500ca481bf4b9ba138c6cb3dad3847bee24f3f506fa985ac11
|
4
|
+
data.tar.gz: fd7fd300d3519276ffb27de97049daf2b59cec4a5f1c2882b7386257dddc25d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbfcc16bf944ef8726912ee31e2ab8f045bba776a63d4377fea70c0fa28d990907b17f94b59ea0cc8e6e4855a4bba2bd965a66bea44fb53fd20ce3539cb09518
|
7
|
+
data.tar.gz: 1220943c86ee1455654901700c1bf90834b81af96e355b86ebb7a91275a6130030917ce13eaa755cd0a8388d3fb9497b57a75372f1409989b9ffe0646774df32
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.2.8
|
4
|
+
|
5
|
+
- Support Content Data-Binding to multiple model attributes via `computed_by` option (e.g. `content(@game, :scale, computed_by: [:width, :height])` or `content(@game, computed_by: [:scale, :width, :height])` will re-render content on changes to `:scale`, `:width`, or `:height`)
|
6
|
+
|
7
|
+
## 0.2.7
|
8
|
+
|
9
|
+
- Unidirectional Data-Binding of element `style` property
|
10
|
+
- Support `a` as a formatting element under `p`
|
11
|
+
- Todo MVC Sample: `require 'glimmer-dsl-web/samples/regular/todo_mvc'`
|
12
|
+
|
3
13
|
## 0.2.6
|
4
14
|
|
5
15
|
- Upgrade to `glimmer-dsl-xml` 1.4.0 to provide access to `html_to_glimmer` converter command (converts legacy HTML to Glimmer DSL syntax)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
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.
|
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.8 (Beta)
|
2
2
|
## Ruby in the Browser Web Frontend Framework
|
3
3
|
### Finally, Ruby Developer Productivity, Happiness, and Fun in the Frontend!!!
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-web.svg)](http://badge.fury.io/rb/glimmer-dsl-web)
|
@@ -8,9 +8,11 @@
|
|
8
8
|
|
9
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
10
|
|
11
|
-
|
11
|
+
[![Todo MVC](/images/glimmer-dsl-web-samples-regular-todo-mvc.gif)](/lib/glimmer-dsl-web/samples/regular/todo_mvc.rb)
|
12
12
|
|
13
|
-
|
13
|
+
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. Also, with Frontend Ruby, companies can cut their hiring budget in half by having Backend Ruby Software Engineers do Frontend Development in Ruby! [Ruby in the Browser](https://www.youtube.com/watch?v=4AdcfbI6A4c) finally fulfills every smart 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.
|
14
|
+
|
15
|
+
[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). To automatically convert legacy HTML & CSS code to Glimmer DSL Ruby code, Software Engineers could use the included [`html_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-xml#html-to-glimmer-converter) and [`css_to_glimmer`](https://github.com/AndyObtiva/glimmer-dsl-css#css-to-glimmer-converter) commands. 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
16
|
|
15
17
|
[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
18
|
|
@@ -377,7 +379,7 @@ Screenshot:
|
|
377
379
|
|
378
380
|
If you need to regenerate HTML element content dynamically, you can use Content Data-Binding to effortlessly
|
379
381
|
rebuild HTML elements based on changes in a Model attribute that provides the source data.
|
380
|
-
In this example, we generate multiple address forms based on the number of addresses the user has.
|
382
|
+
In this example, we generate multiple address forms based on the number of addresses the user has using `content(@user, :address_count)` (you can add a `computed_by: array_of_attributes` option if you want to re-render content based on changes to multiple attributes like `content(@user, computed_by: [:address_count, :street_count])`, which fires on changes to `address_count` or `street_count`) .
|
381
383
|
|
382
384
|
[lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb)
|
383
385
|
|
@@ -466,7 +468,7 @@ Document.ready? do
|
|
466
468
|
div {
|
467
469
|
# Content Data-Binding is used to dynamically (re)generate content of div
|
468
470
|
# based on changes to @user.addresses, replacing older content on every change
|
469
|
-
content(@user, :
|
471
|
+
content(@user, :address_count) do
|
470
472
|
@user.addresses.each do |address|
|
471
473
|
div {
|
472
474
|
div(style: 'display: grid; grid-auto-columns: 80px 280px;') { |address_div|
|
@@ -1148,6 +1150,102 @@ Screenshot:
|
|
1148
1150
|
|
1149
1151
|
![Hello, Observer!](/images/glimmer-dsl-web-samples-hello-hello-observer.gif)
|
1150
1152
|
|
1153
|
+
**Todo MVC**
|
1154
|
+
|
1155
|
+
[Todo MVC Ruby Edition Is the One Todo MVC To Rule Them All!!!](https://andymaleh.blogspot.com/2024/06/todo-mvc-in-ruby-is-one-todo-mvc-to.html)
|
1156
|
+
|
1157
|
+
```ruby
|
1158
|
+
require 'glimmer-dsl-web'
|
1159
|
+
|
1160
|
+
require_relative 'todo_mvc/presenters/todo_presenter'
|
1161
|
+
|
1162
|
+
require_relative 'todo_mvc/views/new_todo_form'
|
1163
|
+
require_relative 'todo_mvc/views/todo_list'
|
1164
|
+
require_relative 'todo_mvc/views/todo_filters'
|
1165
|
+
require_relative 'todo_mvc/views/todo_mvc_footer'
|
1166
|
+
|
1167
|
+
class TodoMvc
|
1168
|
+
include Glimmer::Web::Component
|
1169
|
+
|
1170
|
+
before_render do
|
1171
|
+
@presenter = TodoPresenter.new
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
after_render do
|
1175
|
+
@presenter.setup_filter_routes
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
markup {
|
1179
|
+
div(class: 'todomvc') {
|
1180
|
+
section(class: 'todoapp') {
|
1181
|
+
new_todo_form(presenter: @presenter)
|
1182
|
+
|
1183
|
+
todo_list(presenter: @presenter)
|
1184
|
+
|
1185
|
+
todo_filters(presenter: @presenter)
|
1186
|
+
|
1187
|
+
style {
|
1188
|
+
todo_mvc_styles
|
1189
|
+
}
|
1190
|
+
}
|
1191
|
+
|
1192
|
+
todo_mvc_footer
|
1193
|
+
|
1194
|
+
on_remove do
|
1195
|
+
@presenter.unsetup_filter_routes
|
1196
|
+
end
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
def todo_mvc_styles
|
1201
|
+
rule('body, button, html') {
|
1202
|
+
margin '0'
|
1203
|
+
padding '0'
|
1204
|
+
}
|
1205
|
+
|
1206
|
+
rule('button') {
|
1207
|
+
_webkit_font_smoothing 'antialiased'
|
1208
|
+
_webkit_appearance 'none'
|
1209
|
+
appearance 'none'
|
1210
|
+
background 'none'
|
1211
|
+
border '0'
|
1212
|
+
color 'inherit'
|
1213
|
+
font_family 'inherit'
|
1214
|
+
font_size '100%'
|
1215
|
+
font_weight 'inherit'
|
1216
|
+
vertical_align 'baseline'
|
1217
|
+
}
|
1218
|
+
|
1219
|
+
rule('.todoapp') {
|
1220
|
+
background '#fff'
|
1221
|
+
margin '130px 0 40px 0'
|
1222
|
+
position 'relative'
|
1223
|
+
box_shadow '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1)'
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
media('screen and (-webkit-min-device-pixel-ratio:0)') {
|
1227
|
+
rule('body') {
|
1228
|
+
font "14px 'Helvetica Neue', Helvetica, Arial, sans-serif"
|
1229
|
+
line_height '1.4em'
|
1230
|
+
background '#f5f5f5'
|
1231
|
+
color '#111111'
|
1232
|
+
min_width '230px'
|
1233
|
+
max_width '550px'
|
1234
|
+
margin '0 auto'
|
1235
|
+
_webkit_font_smoothing 'antialiased'
|
1236
|
+
font_weight '300'
|
1237
|
+
}
|
1238
|
+
}
|
1239
|
+
end
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
Document.ready? do
|
1243
|
+
TodoMvc.render
|
1244
|
+
end
|
1245
|
+
```
|
1246
|
+
|
1247
|
+
![Todo MVC](/images/glimmer-dsl-web-samples-regular-todo-mvc.gif)
|
1248
|
+
|
1151
1249
|
To get started, [Setup](#setup) Ruby gem, read [Usage](#usage) instructions, and check out more [Samples](#samples) (including playing around with a [Rails sample app](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app)).
|
1152
1250
|
|
1153
1251
|
--
|
@@ -1184,6 +1282,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
1184
1282
|
- [Hello, Paragraph!](#hello-paragraph)
|
1185
1283
|
- [Hello, Input (Date/Time)!](#hello-input-datetime)
|
1186
1284
|
- [Button Counter](#button-counter)
|
1285
|
+
- [Todo MVC](#todo-mvc)
|
1187
1286
|
- [Design Principles](#design-principles)
|
1188
1287
|
- [Supporting Libraries](#supporting-libraries)
|
1189
1288
|
- [Glimmer Process](#glimmer-process)
|
@@ -1230,7 +1329,7 @@ rails new glimmer_app_server
|
|
1230
1329
|
Add the following to `Gemfile`:
|
1231
1330
|
|
1232
1331
|
```
|
1233
|
-
gem 'glimmer-dsl-web', '~> 0.2.
|
1332
|
+
gem 'glimmer-dsl-web', '~> 0.2.8'
|
1234
1333
|
```
|
1235
1334
|
|
1236
1335
|
Run:
|
@@ -1457,7 +1556,7 @@ Disable the `webpacker` gem line in `Gemfile`:
|
|
1457
1556
|
Add the following to `Gemfile`:
|
1458
1557
|
|
1459
1558
|
```ruby
|
1460
|
-
gem 'glimmer-dsl-web', '~> 0.2.
|
1559
|
+
gem 'glimmer-dsl-web', '~> 0.2.8'
|
1461
1560
|
```
|
1462
1561
|
|
1463
1562
|
Run:
|
@@ -2315,9 +2414,9 @@ Screenshot:
|
|
2315
2414
|
|
2316
2415
|
#### Hello, Content Data-Binding!
|
2317
2416
|
|
2318
|
-
If you need to regenerate HTML element content dynamically, you can use Content Data-Binding to effortlessly
|
2319
|
-
rebuild HTML elements based on changes in a Model attribute that provides the source data.
|
2320
|
-
In this example, we generate multiple address forms based on the number of addresses the user has.
|
2417
|
+
If you need to regenerate (re-render) HTML element content dynamically, you can use Content Data-Binding to effortlessly
|
2418
|
+
rebuild (rerender) HTML elements based on changes in a Model attribute that provides the source data.
|
2419
|
+
In this example, we generate multiple address forms based on the number of addresses the user has using `content(@user, :address_count)` (you can add a `computed_by: array_of_attributes` option if you want to re-render content based on changes to multiple attributes like `content(@user, computed_by: [:address_count, :street_count])`, which fires on changes to `address_count` or `street_count`) .
|
2321
2420
|
|
2322
2421
|
[lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb)
|
2323
2422
|
|
@@ -2405,8 +2504,8 @@ Document.ready? do
|
|
2405
2504
|
|
2406
2505
|
div {
|
2407
2506
|
# Content Data-Binding is used to dynamically (re)generate content of div
|
2408
|
-
# based on changes to @user.
|
2409
|
-
content(@user, :
|
2507
|
+
# based on changes to @user.address_count, replacing older content on every change
|
2508
|
+
content(@user, :address_count) do
|
2410
2509
|
@user.addresses.each do |address|
|
2411
2510
|
div {
|
2412
2511
|
div(style: 'display: grid; grid-auto-columns: 80px 280px;') { |address_div|
|
@@ -3157,6 +3256,108 @@ Screenshot:
|
|
3157
3256
|
|
3158
3257
|
![Button Counter](/images/glimmer-dsl-web-samples-regular-button-counter.gif)
|
3159
3258
|
|
3259
|
+
#### Todo MVC
|
3260
|
+
|
3261
|
+
[Todo MVC Ruby Edition Is the One Todo MVC To Rule Them All!!!](https://andymaleh.blogspot.com/2024/06/todo-mvc-in-ruby-is-one-todo-mvc-to.html)
|
3262
|
+
|
3263
|
+
[lib/glimmer-dsl-web/samples/regular/todo_mvc.rb](/lib/glimmer-dsl-web/samples/regular/todo_mvc.rb)
|
3264
|
+
|
3265
|
+
```ruby
|
3266
|
+
require 'glimmer-dsl-web'
|
3267
|
+
|
3268
|
+
require_relative 'todo_mvc/presenters/todo_presenter'
|
3269
|
+
|
3270
|
+
require_relative 'todo_mvc/views/new_todo_form'
|
3271
|
+
require_relative 'todo_mvc/views/todo_list'
|
3272
|
+
require_relative 'todo_mvc/views/todo_filters'
|
3273
|
+
require_relative 'todo_mvc/views/todo_mvc_footer'
|
3274
|
+
|
3275
|
+
class TodoMvc
|
3276
|
+
include Glimmer::Web::Component
|
3277
|
+
|
3278
|
+
before_render do
|
3279
|
+
@presenter = TodoPresenter.new
|
3280
|
+
end
|
3281
|
+
|
3282
|
+
after_render do
|
3283
|
+
@presenter.setup_filter_routes
|
3284
|
+
end
|
3285
|
+
|
3286
|
+
markup {
|
3287
|
+
div(class: 'todomvc') {
|
3288
|
+
section(class: 'todoapp') {
|
3289
|
+
new_todo_form(presenter: @presenter)
|
3290
|
+
|
3291
|
+
todo_list(presenter: @presenter)
|
3292
|
+
|
3293
|
+
todo_filters(presenter: @presenter)
|
3294
|
+
|
3295
|
+
style {
|
3296
|
+
todo_mvc_styles
|
3297
|
+
}
|
3298
|
+
}
|
3299
|
+
|
3300
|
+
todo_mvc_footer
|
3301
|
+
|
3302
|
+
on_remove do
|
3303
|
+
@presenter.unsetup_filter_routes
|
3304
|
+
end
|
3305
|
+
}
|
3306
|
+
}
|
3307
|
+
|
3308
|
+
def todo_mvc_styles
|
3309
|
+
rule('body, button, html') {
|
3310
|
+
margin '0'
|
3311
|
+
padding '0'
|
3312
|
+
}
|
3313
|
+
|
3314
|
+
rule('button') {
|
3315
|
+
_webkit_font_smoothing 'antialiased'
|
3316
|
+
_webkit_appearance 'none'
|
3317
|
+
appearance 'none'
|
3318
|
+
background 'none'
|
3319
|
+
border '0'
|
3320
|
+
color 'inherit'
|
3321
|
+
font_family 'inherit'
|
3322
|
+
font_size '100%'
|
3323
|
+
font_weight 'inherit'
|
3324
|
+
vertical_align 'baseline'
|
3325
|
+
}
|
3326
|
+
|
3327
|
+
rule('.todoapp') {
|
3328
|
+
background '#fff'
|
3329
|
+
margin '130px 0 40px 0'
|
3330
|
+
position 'relative'
|
3331
|
+
box_shadow '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1)'
|
3332
|
+
}
|
3333
|
+
|
3334
|
+
media('screen and (-webkit-min-device-pixel-ratio:0)') {
|
3335
|
+
rule('body') {
|
3336
|
+
font "14px 'Helvetica Neue', Helvetica, Arial, sans-serif"
|
3337
|
+
line_height '1.4em'
|
3338
|
+
background '#f5f5f5'
|
3339
|
+
color '#111111'
|
3340
|
+
min_width '230px'
|
3341
|
+
max_width '550px'
|
3342
|
+
margin '0 auto'
|
3343
|
+
_webkit_font_smoothing 'antialiased'
|
3344
|
+
font_weight '300'
|
3345
|
+
}
|
3346
|
+
}
|
3347
|
+
end
|
3348
|
+
end
|
3349
|
+
|
3350
|
+
Document.ready? do
|
3351
|
+
TodoMvc.render
|
3352
|
+
end
|
3353
|
+
```
|
3354
|
+
|
3355
|
+
![Todo MVC](/images/glimmer-dsl-web-samples-regular-todo-mvc.gif)
|
3356
|
+
|
3357
|
+
The rest of the files are found at:
|
3358
|
+
|
3359
|
+
[lib/glimmer-dsl-web/samples/regular/todo_mvc](/lib/glimmer-dsl-web/samples/regular/todo_mvc)
|
3360
|
+
|
3160
3361
|
## Design Principles
|
3161
3362
|
|
3162
3363
|
- The Ruby Way (including TIMTOWTDI: There Is More Than One Way To Do It)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.8
|
data/glimmer-dsl-web.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
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.
|
5
|
+
# stub: glimmer-dsl-web 0.2.8 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer-dsl-web".freeze
|
9
|
-
s.version = "0.2.
|
9
|
+
s.version = "0.2.8"
|
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-
|
14
|
+
s.date = "2024-06-21"
|
15
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 = [
|
@@ -48,9 +48,21 @@ Gem::Specification.new do |s|
|
|
48
48
|
"lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb",
|
49
49
|
"lib/glimmer-dsl-web/samples/hello/hello_world.rb",
|
50
50
|
"lib/glimmer-dsl-web/samples/regular/button_counter.rb",
|
51
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc.rb",
|
52
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/models/todo.rb",
|
53
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/presenters/todo_presenter.rb",
|
54
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/edit_todo_input.rb",
|
55
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_form.rb",
|
56
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_input.rb",
|
57
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_filters.rb",
|
58
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_input.rb",
|
59
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list.rb",
|
60
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list_item.rb",
|
61
|
+
"lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_mvc_footer.rb",
|
51
62
|
"lib/glimmer-dsl-web/vendor/jquery.js",
|
52
63
|
"lib/glimmer/config/opal_logger.rb",
|
53
64
|
"lib/glimmer/data_binding/element_binding.rb",
|
65
|
+
"lib/glimmer/dsl/web/a_expression.rb",
|
54
66
|
"lib/glimmer/dsl/web/bind_expression.rb",
|
55
67
|
"lib/glimmer/dsl/web/component_expression.rb",
|
56
68
|
"lib/glimmer/dsl/web/content_data_binding_expression.rb",
|
@@ -76,24 +88,24 @@ Gem::Specification.new do |s|
|
|
76
88
|
]
|
77
89
|
s.homepage = "http://github.com/AndyObtiva/glimmer-dsl-web".freeze
|
78
90
|
s.licenses = ["MIT".freeze]
|
79
|
-
s.rubygems_version = "3.
|
91
|
+
s.rubygems_version = "3.4.10".freeze
|
80
92
|
s.summary = "Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework)".freeze
|
81
93
|
|
82
94
|
s.specification_version = 4
|
83
95
|
|
84
|
-
s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 2.7.6"
|
85
|
-
s.add_runtime_dependency(%q<glimmer-dsl-xml>.freeze, ["~> 1.4.0"
|
86
|
-
s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.4.0"
|
87
|
-
s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2"
|
88
|
-
s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3"
|
89
|
-
s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.1"
|
90
|
-
s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.5.1"
|
91
|
-
s.add_runtime_dependency(%q<to_collection>.freeze, [">= 2.0.1"
|
92
|
-
s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 0"
|
93
|
-
s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0"
|
94
|
-
s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"
|
95
|
-
s.add_development_dependency(%q<jeweler>.freeze, [">= 2.3.9"
|
96
|
-
s.add_development_dependency(%q<rdoc>.freeze, [">= 6.2.1"
|
97
|
-
s.add_development_dependency(%q<opal-rspec>.freeze, ["~> 0.8.0.alpha2"
|
96
|
+
s.add_runtime_dependency(%q<glimmer>.freeze, ["~> 2.7.6"])
|
97
|
+
s.add_runtime_dependency(%q<glimmer-dsl-xml>.freeze, ["~> 1.4.0"])
|
98
|
+
s.add_runtime_dependency(%q<glimmer-dsl-css>.freeze, ["~> 1.4.0"])
|
99
|
+
s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2"])
|
100
|
+
s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3"])
|
101
|
+
s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.1"])
|
102
|
+
s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.5.1"])
|
103
|
+
s.add_runtime_dependency(%q<to_collection>.freeze, [">= 2.0.1", "< 3.0.0"])
|
104
|
+
s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 1.0.0"])
|
105
|
+
s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0", "< 14.0.0"])
|
106
|
+
s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
|
107
|
+
s.add_development_dependency(%q<jeweler>.freeze, [">= 2.3.9", "< 3.0.0"])
|
108
|
+
s.add_development_dependency(%q<rdoc>.freeze, [">= 6.2.1", "< 7.0.0"])
|
109
|
+
s.add_development_dependency(%q<opal-rspec>.freeze, ["~> 0.8.0.alpha2"])
|
98
110
|
end
|
99
111
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'glimmer/dsl/static_expression'
|
2
|
+
require 'glimmer/dsl/web/general_element_expression'
|
3
|
+
|
4
|
+
module Glimmer
|
5
|
+
module DSL
|
6
|
+
module Web
|
7
|
+
class AExpression < StaticExpression
|
8
|
+
include GeneralElementExpression
|
9
|
+
|
10
|
+
def can_interpret?(parent, keyword, *args, &block)
|
11
|
+
(parent.nil? ||
|
12
|
+
(parent.respond_to?(:keyword) && parent.keyword != 'p'))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/glimmer/dsl/web/dsl.rb
CHANGED
@@ -24,6 +24,7 @@ require 'glimmer/dsl/web/element_expression'
|
|
24
24
|
require 'glimmer/dsl/web/formatting_element_expression'
|
25
25
|
require 'glimmer/dsl/web/listener_expression'
|
26
26
|
require 'glimmer/dsl/web/property_expression'
|
27
|
+
require 'glimmer/dsl/web/a_expression'
|
27
28
|
require 'glimmer/dsl/web/span_expression'
|
28
29
|
require 'glimmer/dsl/web/style_expression'
|
29
30
|
require 'glimmer/dsl/web/bind_expression'
|
@@ -45,6 +46,7 @@ module Glimmer
|
|
45
46
|
property
|
46
47
|
content_data_binding
|
47
48
|
shine_data_binding
|
49
|
+
style
|
48
50
|
formatting_element
|
49
51
|
]
|
50
52
|
)
|
@@ -24,7 +24,7 @@ end
|
|
24
24
|
module Glimmer
|
25
25
|
# Optimize performance through shortcut methods for all HTML elements that circumvent the DSL chain of responsibility
|
26
26
|
element_expression = Glimmer::DSL::Web::ElementExpression.new
|
27
|
-
(Glimmer::Web::ElementProxy::ELEMENT_KEYWORDS - ['span', 'style']).each do |keyword|
|
27
|
+
(Glimmer::Web::ElementProxy::ELEMENT_KEYWORDS - ['a', 'span', 'style']).each do |keyword|
|
28
28
|
Glimmer::DSL::Engine.static_expressions[keyword] ||= Concurrent::Hash.new
|
29
29
|
element_expression_dsl = element_expression.class.dsl
|
30
30
|
Glimmer::DSL::Engine.static_expressions[keyword][element_expression_dsl] = element_expression
|
@@ -1,13 +1,18 @@
|
|
1
|
-
require 'glimmer/dsl/
|
1
|
+
require 'glimmer/dsl/expression'
|
2
2
|
require 'glimmer/dsl/web/general_element_expression'
|
3
3
|
|
4
4
|
module Glimmer
|
5
5
|
module DSL
|
6
6
|
module Web
|
7
|
-
class StyleExpression <
|
7
|
+
class StyleExpression < Expression
|
8
8
|
include GeneralElementExpression
|
9
9
|
include Glimmer
|
10
10
|
|
11
|
+
def can_interpret?(parent, keyword, *args, &block)
|
12
|
+
keyword == 'style' &&
|
13
|
+
!block.nil?
|
14
|
+
end
|
15
|
+
|
11
16
|
def add_content(parent, keyword, *args, &block)
|
12
17
|
if parent.rendered? || parent.skip_content_on_render_blocks?
|
13
18
|
return_value = css(&block).to_s
|
@@ -526,8 +526,9 @@ module Glimmer
|
|
526
526
|
children.dup.each { |child| child.remove }
|
527
527
|
content(&content_block)
|
528
528
|
end
|
529
|
+
model_binding_observer = Glimmer::DataBinding::ModelBinding.new(*binding_args)
|
529
530
|
content_binding_observer = Glimmer::DataBinding::Observer.proc(&content_binding_work)
|
530
|
-
content_binding_observer.observe(
|
531
|
+
content_binding_observer.observe(model_binding_observer)
|
531
532
|
content_binding_work.call # TODO inspect if we need to pass args here (from observed attributes) [but it's simpler not to pass anything at first]
|
532
533
|
end
|
533
534
|
|
@@ -33,7 +33,8 @@ module Glimmer
|
|
33
33
|
keyword = keyword.to_s
|
34
34
|
(
|
35
35
|
FORMATTING_ELEMENT_KEYWORDS.include?(keyword) ||
|
36
|
-
(keyword == 'span' && parent&.keyword == 'p')
|
36
|
+
(keyword == 'span' && parent&.keyword == 'p') ||
|
37
|
+
(keyword == 'a' && parent&.keyword == 'p')
|
37
38
|
)
|
38
39
|
end
|
39
40
|
|
@@ -100,8 +100,8 @@ Document.ready? do
|
|
100
100
|
|
101
101
|
div {
|
102
102
|
# Content Data-Binding is used to dynamically (re)generate content of div
|
103
|
-
# based on changes to @user.
|
104
|
-
content(@user, :
|
103
|
+
# based on changes to @user.address_count, replacing older content on every change
|
104
|
+
content(@user, :address_count) do
|
105
105
|
@user.addresses.each do |address|
|
106
106
|
div {
|
107
107
|
div(style: 'display: grid; grid-auto-columns: 80px 280px;') { |address_div|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Todo = Struct.new(:task, :completed, :editing, keyword_init: true) do
|
2
|
+
class << self
|
3
|
+
attr_writer :all
|
4
|
+
|
5
|
+
def all
|
6
|
+
@all ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def active
|
10
|
+
all.select(&:active?)
|
11
|
+
end
|
12
|
+
|
13
|
+
def completed
|
14
|
+
all.select(&:completed?)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
FILTERS = [:all, :active, :completed]
|
19
|
+
|
20
|
+
alias completed? completed
|
21
|
+
alias editing? editing
|
22
|
+
|
23
|
+
def active
|
24
|
+
!completed
|
25
|
+
end
|
26
|
+
alias active? active
|
27
|
+
|
28
|
+
def start_editing
|
29
|
+
return if editing?
|
30
|
+
@original_task = task
|
31
|
+
self.editing = true
|
32
|
+
end
|
33
|
+
|
34
|
+
def cancel_editing
|
35
|
+
return unless editing?
|
36
|
+
self.task = @original_task
|
37
|
+
self.editing = false
|
38
|
+
end
|
39
|
+
|
40
|
+
def save_editing
|
41
|
+
return unless editing?
|
42
|
+
self.editing = false
|
43
|
+
end
|
44
|
+
end
|