Open zw963 opened 1 month ago
I've never encountered any issues running JRuby though one of its dependencies is the JVM (Java Virtual Machine), and it has to be installed first before using JRuby. I'd be happy to troubleshoot your JRuby installation if needed. You oughta be able to run java --version
successfully first before installing JRuby.
That said, Glimmer DSL for LibUI already ships with Tetris too as one of its Advanced examples, and it doesn't even need a web server like Glimmer DSL for Web, nor a JVM. It just runs on pure Ruby and it won a Fukuoka Ruby 2022 Special Award after getting presented to Matz the creator of Ruby.
Just install the gem in standard Ruby (MRI CRuby):
gem install glimmer-dsl-libui
And, then run this command to list all available samples:
glimmer examples
You will find Tetris under the Advanced examples tab.
You can select Tetris and click the Launch button to start Tetris from there.
Or, you can run this direct command to start Tetris right away:
ruby -r glimmer-dsl-libui -e "require 'examples/tetris'"
The version of Tetris above can be run much more easily without using a web browser. Desktop apps in general are way simpler than web apps. That's why I love them and prefer them over web app development when they are sufficient. People who try to build everything as a web app miss out on that advantage. It is better to use the simplest tool available for every job instead of trying to force everything into being a web app if not needed, which is a bad habit many developers suffer from recently, causing extreme over-engineering and writing a lot more code than needed that is unnecessary.
That said, if you want Tetris in Glimmer DSL for Web, you can implement/port it and submit a Pull Request. I am busy with other priorities at the moment, including some related to implementing features needed for version 1.0 of Glimmer DSL for Web.
BTW: i port standalone to use opal-browser instead of opal-jquery. https://github.com/zw963/glimmer-dsl-web_example
Thank you for providing that. I starred/forked it.
One thing to note is the last time I checked out opal-browser, it had limitations and issues that made it unreliable for my usage. That is why I rely on opal-jquery instead. I have not checked out the latest version of opal-browser though, so things might have changed since the last time I looked at it.
Okay, thanks, let me try again.
I am current running Arch Linux.
╰─ $ java --version
java 22.0.1 2024-04-16
Java(TM) SE Runtime Environment (build 22.0.1+8-16)
Java HotSpot(TM) 64-Bit Server VM (build 22.0.1+8-16, mixed mode, sharing)
╰─ $ rvm current
jruby-9.2.19.0@glimmer_tetris
It get following error message when bundle install.
Then, i switch to Ruby 3.3.4 with new rvmset ruby-3.3.4@glimmer-dsl-libui
, and ran gem install glimmer-dsl-libui
successful, but failed again when try to run glimmer examples
.
Anyway, I think JRuby is a niche thing in the Ruby world, in my decade of working with Ruby, I haven't even seen anyone use it, if we could run Tetris on a very small web server (e.g. Roda, Sinatra, even webrick), it would actually be easier to reproduce.
One thing to note is the last time I checked out opal-browser, it had limitations and issues that made it unreliable for my usage. That is why I rely on opal-jquery instead. I have not checked out the latest version of opal-browser though, so things might have changed since the last time I looked at it.
I don't know if this reason, when i run the data-binding
example code in README, i get following error.
╰─ $ ruby app.rb
./script.js.rb:4:16: error: unexpected token tRCURLY
./script.js.rb:4: STATES = {...} # contains US States
./script.js.rb:4: ^
./script.js.rb:4:in ` STATES = {...} # contains US States': unexpected token tRCURLY (Opal::SyntaxError)
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/diagnostic/engine.rb:72:in `process'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:286:in `on_error'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/racc-1.8.0/lib/racc/parser.rb:263:in `_racc_do_parse_c'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/racc-1.8.0/lib/racc/parser.rb:263:in `do_parse'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:190:in `parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/parser/default_config.rb:32:in `parse'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:238:in `tokenize'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:312:in `block in parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:393:in `re_raise_with_location'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:312:in `parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:298:in `compile'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:88:in `block in compiled'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/cache.rb:43:in `fetch'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:86:in `compiled'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:102:in `requires'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder.rb:105:in `build_str'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder.rb:92:in `build'
from app.rb:11:in `<main>'
./script.js.rb:4:in ` STATES = {...} # contains US States': unexpected token tRCURLY (Parser::SyntaxError)
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/diagnostic/engine.rb:72:in `process'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:286:in `on_error'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/racc-1.8.0/lib/racc/parser.rb:263:in `_racc_do_parse_c'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/racc-1.8.0/lib/racc/parser.rb:263:in `do_parse'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:190:in `parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/parser/default_config.rb:32:in `parse'
from /home/zw963/Dropbox/linux/utils/ruby_tools/app/gems/parser-3.3.0.5/lib/parser/base.rb:238:in `tokenize'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:312:in `block in parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:393:in `re_raise_with_location'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:312:in `parse'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/compiler.rb:298:in `compile'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:88:in `block in compiled'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/cache.rb:43:in `fetch'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:86:in `compiled'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder_processors.rb:102:in `requires'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder.rb:105:in `build_str'
from /home/common/.rvm/gems/ruby-3.3.0@glimmer-dsl-web_example/gems/opal-1.8.2/lib/opal/builder.rb:92:in `build'
from app.rb:11:in `<main>'
I don't know if it caused by opal-browser, you can check data-binding branch of my standalone example repo to reproduce.
It might be caused by opal-browser. I don't recommend using opal-browser. Everything works for me without opal-browser.
One thing to improve in your example is to make it use a Glimmer::Web::Component
. For serious app usage, web UI should always be built using Glimmer::Web::Component
(and embedded using the glimmer_component helper in a Rails view).
(Update: I found the issue (below). Try to fix the issue as per my solution below first. You can upgrade the code to use Glimmer::Web::Component
and the glimmer_component helper separately in a different example if desired)
Actually, your main problem though is you copied code from the GitHub page that is intentionally shortened (to avoid distracting readers with unimportant details) by not putting all states in:
STATES = {...} # contains US States
You need to copy the actual full version of it in the project files (with the STATES filled in): https://github.com/AndyObtiva/glimmer-dsl-web/blob/master/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb
# Copyright (c) 2023-2024 Andy Maleh
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
require 'glimmer-dsl-web'
Address = Struct.new(:street, :street2, :city, :state, :zip_code, :billing_and_shipping, keyword_init: true) do
STATES = {
"AK"=>"Alaska",
"AL"=>"Alabama",
"AR"=>"Arkansas",
"AS"=>"American Samoa",
"AZ"=>"Arizona",
"CA"=>"California",
"CO"=>"Colorado",
"CT"=>"Connecticut",
"DC"=>"District of Columbia",
"DE"=>"Delaware",
"FL"=>"Florida",
"GA"=>"Georgia",
"GU"=>"Guam",
"HI"=>"Hawaii",
"IA"=>"Iowa",
"ID"=>"Idaho",
"IL"=>"Illinois",
"IN"=>"Indiana",
"KS"=>"Kansas",
"KY"=>"Kentucky",
"LA"=>"Louisiana",
"MA"=>"Massachusetts",
"MD"=>"Maryland",
"ME"=>"Maine",
"MI"=>"Michigan",
"MN"=>"Minnesota",
"MO"=>"Missouri",
"MS"=>"Mississippi",
"MT"=>"Montana",
"NC"=>"North Carolina",
"ND"=>"North Dakota",
"NE"=>"Nebraska",
"NH"=>"New Hampshire",
"NJ"=>"New Jersey",
"NM"=>"New Mexico",
"NV"=>"Nevada",
"NY"=>"New York",
"OH"=>"Ohio",
"OK"=>"Oklahoma",
"OR"=>"Oregon",
"PA"=>"Pennsylvania",
"PR"=>"Puerto Rico",
"RI"=>"Rhode Island",
"SC"=>"South Carolina",
"SD"=>"South Dakota",
"TN"=>"Tennessee",
"TX"=>"Texas",
"UT"=>"Utah",
"VA"=>"Virginia",
"VI"=>"Virgin Islands",
"VT"=>"Vermont",
"WA"=>"Washington",
"WI"=>"Wisconsin",
"WV"=>"West Virginia",
"WY"=>"Wyoming"
}
def state_code
STATES.invert[state]
end
def state_code=(value)
self.state = STATES[value]
end
def summary
string_attributes = to_h.except(:billing_and_shipping)
summary = string_attributes.values.map(&:to_s).reject(&:empty?).join(', ')
summary += " (Billing & Shipping)" if billing_and_shipping
summary
end
end
@address = Address.new(
street: '123 Main St',
street2: 'Apartment 3C, 2nd door to the right',
city: 'San Diego',
state: 'California',
zip_code: '91911',
billing_and_shipping: true,
)
include Glimmer
Document.ready? do
div {
div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
label('Street: ', for: 'street-field')
input(id: 'street-field') {
# Bidirectional Data-Binding with <=> ensures input.value and @address.street
# automatically stay in sync when either side changes
value <=> [@address, :street]
}
label('Street 2: ', for: 'street2-field')
textarea(id: 'street2-field') {
value <=> [@address, :street2]
}
label('City: ', for: 'city-field')
input(id: 'city-field') {
value <=> [@address, :city]
}
label('State: ', for: 'state-field')
select(id: 'state-field') {
Address::STATES.each do |state_code, state|
option(value: state_code) { state }
end
value <=> [@address, :state_code]
}
label('Zip Code: ', for: 'zip-code-field')
input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
# Bidirectional Data-Binding with <=> ensures input.value and @address.zip_code
# automatically stay in sync when either side changes
# on_write option specifies :to_s method to invoke on value before writing to model attribute
# to ensure the numeric zip code value is stored as a String
value <=> [@address, :zip_code,
on_write: :to_s,
]
}
div(style: 'grid-column: 1 / span 2') {
input(id: 'billing-and-shipping-field', type: 'checkbox') {
checked <=> [@address, :billing_and_shipping]
}
label(for: 'billing-and-shipping-field') {
'Use this address for both Billing & Shipping'
}
}
# Programmable CSS using Glimmer DSL for CSS
style {
# `r` is an alias for `rule`, generating a CSS rule
r("#{address_div.selector} *") {
margin '5px'
}
r("#{address_div.selector} input, #{address_div.selector} select") {
grid_column '2'
}
}
}
div(style: 'margin: 5px') {
# Unidirectional Data-Binding is done with <= to ensure @address.summary changes
# automatically update div.inner_text
# (computed by changes to address attributes, meaning if street changes,
# @address.summary is automatically recomputed.)
inner_text <= [@address, :summary,
computed_by: @address.members + ['state_code'],
]
}
}
end
I just put the STATES back in the main README to avoid confusing users who miss this detail.
It might be caused by opal-browser. I don't recommend using opal-browser. Everything works for me without opal-browser.
Hi, i test on the full version new data-binding code, although ruby app.rb
work now without any error whatever use opal-jquery
or opal-browser
, but still get error when open index.html
.
i test on both Firefox/Chrome browser.
One thing to improve in your example is to make it use a Glimmer::Web::Component. For serious app usage, web UI should always be built using Glimmer::Web::Component (and embedded using the glimmer_component helper in a Rails view).
Thanks, I will discover how to use it latter. in fact, what i expected is to use glimmer-dsl-web
with lucky, it write web view use Crystal code instead traditional template for speed and safety and support component too.
II told you to go to the source: https://github.com/AndyObtiva/glimmer-dsl-web/blob/master/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb
The README version which I shared with you still has issues apparently (I forgot to add the billing_and_shipping attribute given I added it to the source). I need to fix it. Ignore it please given I gave you the source.
The source works 100% in the original Rails Sample App: https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app
When in doubt, go to that sample app and try its code.
Also, in general, do not try extra libraries like opal-browser until you mastered all the basic samples first and got comfortable with Glimmer DSL for Web as is. It is not a good software engineering technique to add more moving pieces until you mastered the basics or else you would be confusing yourself every time an issue occurs as you would not know if it's the extra moving piece (opal-browser) causing the issue or something else. Use good software engineering techniques by starting small and adding extra things only after mastering the basics.
And, if you want my opinion about opal-browser, you do not need opal-browser. I don't recommend it whether it works or not whatsoever. I don't believe it offers any important benefits at all on top of Glimmer DSL for Web.
I understand you would like to use glimmer-dsl-web with Crystal and Lucky, but you would be defeating part of the point of using a Ruby library in the Frontend if you do that, which is matching the language of the backend, assuming Ruby on Rails.
Although I do like Crystal for the purpose of performance optimization of some algorithms only and some large scale processing of data, I do not think Crystal is a good software engineering decision in general in 80-90% of business apps. I would generally use Ruby because it's a lot simpler due to dynamic typing. The compilation benefits of Crystal are overblown and are over-engineering for most business apps. Many new developers fall for the trap of statically typed languages like TypeScript among others. I actually have work experience with statically typed languages for 5-6 years and about 15 years of Ruby on Rails and I know for a fact most business apps don't need the benefits of compilation. Developers would be more productive without the heavy weight of a compiler that adds extra an extra step to building software that is not needed most of the time.
I would only use Crystal for performance optimizations in 1-10% of use cases.
Otherwise, Glimmer DSL for Web eventually is going to do server-side rendering of components in Rails so that you can write your components once and they run on both the backend and frontend. If you use Crystal in the backend, you won't have that benefit, let alone Crystal is over-engineering that slows people down over Ruby in productivity as mentioned above. It is only useful in 1-10% of the performance optimization cases. That said, I know for sure my current employer wouldn't need Crystal at all in 100% of our use-cases.
Also, in general, do not try extra libraries like opal-browser until you mastered all the basic samples first and got comfortable with Glimmer DSL for Web as is. It is not a good software engineering technique to add more moving pieces until you mastered the basics or else you would be confusing yourself every time an issue occurs as you would not know if it's the extra moving piece (opal-browser) causing the issue or something else. Use good software engineering techniques by starting small and adding extra things only after mastering the basics.
Can't agree more, i revert to use Jquery.
but you would be defeating part of the point of using a Ruby library in the Frontend if you do that, which is matching the language of the backend, assuming Ruby on Rails.
This is acceptable anyway.
After switch to Crystal 2 years, I really don't want to use Ruby to write web server code anymore. you mentioned performance improvement, but on the contrary, although it was the reason attracted me, but it became the least important factor when I started using it, it fixed many design errors in Matz's eyes, the most important reason let me can't leave is type safety
, if you have ever used a language like Dart, you know what I am talking about. I used to do very very well in using TDD with Ruby (Or DDD, Data Driven development), but now I write tests lesser, because I am very happy with compiler driven development, no error, robust.
Following is some highlight in Crystal:
Null Safety (as Dart, Swift, Kotlin) make code sound,
Union Type + with the type_inference, make write Crystal almost like write Ruby.
Multiple dispatch allows you to have different versions of a method with the same name,
Concurrent Fiber as golang's goroutine/channel,
Plus the most wonderful compile time macro feature(which possible stolen from lisp), which make many of feature in ruby (method missing, Module.included etc) is possible in Crystal.
The compilation benefits of Crystal are overblown and are over-engineering for most business apps.
This is the first time I have heard this viewpoint, I actually think Ruby made some mistakes at the beginning and has been patching up now, but Crystal fixed most of those issues perfectly from the beginning.
BTW: Like you, @jgaskins is also a senior development in the Opal/Ruby area, but very active in the Crystal community nowadays, maybe we can hear something from him.
EDIT:
I am current write web use htmx, a server-side rendering tech i guess similar to hotwire and Turbo in Rails, but not tightly bound to Rails, this project can as a replacement if need write page more dynamically, e.g. SPA.
I don't buy the "type safety" benefits either. I worked in type safe languages for 6 years without using Ruby, and after I got used to Ruby on Rails and writing automated tests, I found there is no big need for type safety. After all, even while using type safe languages like Java in the past, I found I still had to write automated tests to ensure business requirement correctness, and those tests always caught type safety issues anyways, including when I write them in Ruby, so it would be more productive to avoid an extra compilation step in software development. That is not just my opinion. I worked for a US company in the past with 55 people who all believed that the benefits of "type safety" are overblown. That's why DHH abandoned TypeScript in favor of JavaScript recently. It's the opinion of other people too not just me.
I appreciate and respect other people's opinions on the matter, but my opinion comes from my deep experience with both type safe and dynamically typed languages. Someone else's opinion won't change mine on this matter given my deep experience with it. What I recommended above (using 99% Ruby and 1% type safe languages for performance optimization only) is what I believe yields the most productiveness in software development.
I found I still had to write automated tests to ensure business requirement correctness,
Yes, this is true, but write Crystal spec just like old fashion RSpec (???.should ??), it did not lose efficiency (compare to Ruby), on the contrary, many spec don't need anymore because compiler help us a lot.
I worked for a US company in the past with 55 people who all believed that the benefits of "type safety" are overblown. That's why DHH abandoned TypeScript in favor of JavaScript recently. It's the opinion of other people too not just me.
I don't know why DHH make this decision, From my point of view, introduce type in typescript is great idea, maybe other reasons? so i checked a blog, it seem like involved with writing types was creating a bad developer experience for him and his team. but this is not true for Crystal, TS is a dynamic language disguised as a static language, but Crystal is a static language disguised to a dynamic language, you don't need write any type in Crystal if you prefer this(just like Ruby), But beneath the surface, the hidden union types are ten times better than TS. Although I only have a basic understanding of TS, I believe the union types in TypeScripts's is quite primitive ,Crystal's type inference is TOO powerful compare to TS, this is the guarantee of experience, just not like JAVA types is everywhere and I hate it! type safety
in JAVA and Crystal is totally different, AFAIK, There are some similar concepts present in Dart, Swift, Kotlin, and Julia, if you have used one of them, of course, TS should be in this list, but as I mentioned before, its very primitive,
Anyway, just like DHH abandoned the Mac and use Ubuntu instead in recent days, i use Linux years, never used a Mac for more than six months, but i still think MBP may be one of the best development laptop ever. It just personal preferences, I never heard of him use Opal either.
Anyway, I don't know if you have used Crystal formally, i feel try it out is a good choice, considering that you are a Ruby expert and expressed your love for Ruby on Twitter, as well as used static language before. :smile:
As https://github.com/AndyObtiva/glimmer_tetris
But, because it use JRuby, it is hard to make it work on my laptop because of various issues related to JRuby.
I expect it can be port to gimmer-dsl-web, use standalone demo
Thanks.
BTW: i port standalone to use
opal-browser
instead ofopal-jquery
.https://github.com/zw963/glimmer-dsl-web_example