glimmer-dsl-web 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +712 -38
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +11 -6
- data/lib/glimmer/dsl/web/component_expression.rb +2 -1
- data/lib/glimmer/dsl/web/dsl.rb +22 -0
- data/lib/glimmer/dsl/web/observe_expression.rb +42 -0
- data/lib/glimmer/helpers/glimmer_helper.rb +30 -0
- data/lib/glimmer/web/component.rb +29 -5
- data/lib/glimmer/web/element_proxy.rb +15 -17
- data/lib/glimmer/web/event_proxy.rb +7 -4
- data/lib/glimmer/web/listener_proxy.rb +12 -11
- data/lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb +156 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_observer.rb +88 -0
- data/lib/glimmer-dsl-web/samples/regular/button_counter.rb +36 -0
- data/lib/glimmer-dsl-web.rb +1 -0
- metadata +13 -8
    
        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.0. | 
| 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.0.11 (Early Alpha)
         | 
| 2 2 | 
             
            ## Ruby in the Browser Web GUI Frontend Library
         | 
| 3 3 | 
             
            [](http://badge.fury.io/rb/glimmer-dsl-web)
         | 
| 4 4 | 
             
            [](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
         | 
| @@ -220,6 +220,86 @@ Screenshot: | |
| 220 220 |  | 
| 221 221 | 
             
            
         | 
| 222 222 |  | 
| 223 | 
            +
            **Hello, Observer!**
         | 
| 224 | 
            +
             | 
| 225 | 
            +
            [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) (covered later), 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 (covered later).
         | 
| 226 | 
            +
             | 
| 227 | 
            +
            Glimmer GUI code:
         | 
| 228 | 
            +
             | 
| 229 | 
            +
            ```ruby
         | 
| 230 | 
            +
            require 'glimmer-dsl-web'
         | 
| 231 | 
            +
             | 
| 232 | 
            +
            class NumberHolder
         | 
| 233 | 
            +
              attr_accessor :number
         | 
| 234 | 
            +
              
         | 
| 235 | 
            +
              def initialize
         | 
| 236 | 
            +
                self.number = 50
         | 
| 237 | 
            +
              end
         | 
| 238 | 
            +
            end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
            class HelloObserver
         | 
| 241 | 
            +
              include Glimmer::Web::Component
         | 
| 242 | 
            +
              
         | 
| 243 | 
            +
              before_render do
         | 
| 244 | 
            +
                @number_holder = NumberHolder.new
         | 
| 245 | 
            +
              end
         | 
| 246 | 
            +
              
         | 
| 247 | 
            +
              after_render do
         | 
| 248 | 
            +
                # observe Model attribute @number_holder.number for changes and update View
         | 
| 249 | 
            +
                observe(@number_holder, :number) do
         | 
| 250 | 
            +
                  number_string = @number_holder.number.to_s
         | 
| 251 | 
            +
                  @number_input.value = number_string unless @number_input.value == number_string
         | 
| 252 | 
            +
                  @range_input.value = number_string unless @range_input.value == number_string
         | 
| 253 | 
            +
                end
         | 
| 254 | 
            +
                # Bidirectional Data-Binding does the same thing automatically
         | 
| 255 | 
            +
                # Just disable the observe block above as well as the oninput listeners below
         | 
| 256 | 
            +
                # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
         | 
| 257 | 
            +
                # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 258 | 
            +
              end
         | 
| 259 | 
            +
              
         | 
| 260 | 
            +
              markup {
         | 
| 261 | 
            +
                div {
         | 
| 262 | 
            +
                  div {
         | 
| 263 | 
            +
                    @number_input = input(type: 'number', value: @number_holder.number, min: 0, max: 100) {
         | 
| 264 | 
            +
                      # oninput listener updates Model attribute @number_holder.number
         | 
| 265 | 
            +
                      oninput do
         | 
| 266 | 
            +
                        @number_holder.number = @number_input.value.to_i
         | 
| 267 | 
            +
                      end
         | 
| 268 | 
            +
                      
         | 
| 269 | 
            +
                      # Bidirectional Data-Binding simplifies the implementation significantly
         | 
| 270 | 
            +
                      # by enabling the following line and disabling oninput listeners as well
         | 
| 271 | 
            +
                      # as the after_body observe block observer
         | 
| 272 | 
            +
                      # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 273 | 
            +
            #           value <=> [@number_holder, :number]
         | 
| 274 | 
            +
                    }
         | 
| 275 | 
            +
                  }
         | 
| 276 | 
            +
                  div {
         | 
| 277 | 
            +
                    @range_input = input(type: 'range', value: @number_holder.number, min: 0, max: 100) {
         | 
| 278 | 
            +
                      # oninput listener updates Model attribute @number_holder.number
         | 
| 279 | 
            +
                      oninput do
         | 
| 280 | 
            +
                        @number_holder.number = @range_input.value.to_i
         | 
| 281 | 
            +
                      end
         | 
| 282 | 
            +
                      
         | 
| 283 | 
            +
                      # Bidirectional Data-Binding simplifies the implementation significantly
         | 
| 284 | 
            +
                      # by enabling the following line and disabling oninput listeners as well
         | 
| 285 | 
            +
                      # as the after_body observe block observer
         | 
| 286 | 
            +
                      # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 287 | 
            +
            #           value <=> [@number_holder, :number]
         | 
| 288 | 
            +
                    }
         | 
| 289 | 
            +
                  }
         | 
| 290 | 
            +
                }
         | 
| 291 | 
            +
              }
         | 
| 292 | 
            +
            end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
            Document.ready? do
         | 
| 295 | 
            +
              HelloObserver.render
         | 
| 296 | 
            +
            end
         | 
| 297 | 
            +
            ```
         | 
| 298 | 
            +
             | 
| 299 | 
            +
            Screenshot:
         | 
| 300 | 
            +
             | 
| 301 | 
            +
            
         | 
| 302 | 
            +
             | 
| 223 303 | 
             
            **Hello, Data-Binding!**
         | 
| 224 304 |  | 
| 225 305 | 
             
            [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.
         | 
| @@ -693,6 +773,211 @@ Screenshot: | |
| 693 773 |  | 
| 694 774 | 
             
            
         | 
| 695 775 |  | 
| 776 | 
            +
            **Hello, glimmer_component Rails Helper!**
         | 
| 777 | 
            +
             | 
| 778 | 
            +
            You may insert a Glimmer component anywhere into a Rails View using
         | 
| 779 | 
            +
            `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper`
         | 
| 780 | 
            +
            or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
         | 
| 781 | 
            +
             | 
| 782 | 
            +
            Rails `ApplicationHelper` setup code:
         | 
| 783 | 
            +
             | 
| 784 | 
            +
            ```ruby
         | 
| 785 | 
            +
            require 'glimmer/helpers/glimmer_helper'
         | 
| 786 | 
            +
             | 
| 787 | 
            +
            module ApplicationHelper
         | 
| 788 | 
            +
              # ...
         | 
| 789 | 
            +
              include GlimmerHelper
         | 
| 790 | 
            +
              # ...
         | 
| 791 | 
            +
            end
         | 
| 792 | 
            +
            ```
         | 
| 793 | 
            +
             | 
| 794 | 
            +
            Rails View code:
         | 
| 795 | 
            +
             | 
| 796 | 
            +
            ```erb
         | 
| 797 | 
            +
            <div id="address-container">
         | 
| 798 | 
            +
              <h1>Shipping Address </h1>
         | 
| 799 | 
            +
              <legend>Please enter your shipping address information (Zip Code must be a valid 5 digit number)</legend>
         | 
| 800 | 
            +
              <!-- This sample demonstrates use of glimmer_component helper with arguments -->
         | 
| 801 | 
            +
              <%= glimmer_component('address_form',
         | 
| 802 | 
            +
                    full_name: params[:full_name],
         | 
| 803 | 
            +
                    street: params[:street],
         | 
| 804 | 
            +
                    street2: params[:street2],
         | 
| 805 | 
            +
                    city: params[:city],
         | 
| 806 | 
            +
                    state: params[:state],
         | 
| 807 | 
            +
                    zip_code: params[:zip_code]
         | 
| 808 | 
            +
                  )
         | 
| 809 | 
            +
               %>
         | 
| 810 | 
            +
               <div>
         | 
| 811 | 
            +
                 <a href="/"><< Back Home</a>
         | 
| 812 | 
            +
               </div>
         | 
| 813 | 
            +
            </div>
         | 
| 814 | 
            +
            ```
         | 
| 815 | 
            +
             | 
| 816 | 
            +
            Glimmer GUI code:
         | 
| 817 | 
            +
             | 
| 818 | 
            +
            ```ruby
         | 
| 819 | 
            +
            require 'glimmer-dsl-web'
         | 
| 820 | 
            +
             | 
| 821 | 
            +
            class AddressForm
         | 
| 822 | 
            +
              Address = Struct.new(:full_name, :street, :street2, :city, :state, :zip_code, keyword_init: true) do
         | 
| 823 | 
            +
                def state_code
         | 
| 824 | 
            +
                  STATES.invert[state]
         | 
| 825 | 
            +
                end
         | 
| 826 | 
            +
                
         | 
| 827 | 
            +
                def state_code=(value)
         | 
| 828 | 
            +
                  self.state = STATES[value]
         | 
| 829 | 
            +
                end
         | 
| 830 | 
            +
              
         | 
| 831 | 
            +
                def summary
         | 
| 832 | 
            +
                  to_h.values.map(&:to_s).reject(&:empty?).join(', ')
         | 
| 833 | 
            +
                end
         | 
| 834 | 
            +
              end
         | 
| 835 | 
            +
              
         | 
| 836 | 
            +
              STATES = {
         | 
| 837 | 
            +
                "AK"=>"Alaska",
         | 
| 838 | 
            +
                "AL"=>"Alabama",
         | 
| 839 | 
            +
                "AR"=>"Arkansas",
         | 
| 840 | 
            +
                "AS"=>"American Samoa",
         | 
| 841 | 
            +
                "AZ"=>"Arizona",
         | 
| 842 | 
            +
                "CA"=>"California",
         | 
| 843 | 
            +
                "CO"=>"Colorado",
         | 
| 844 | 
            +
                "CT"=>"Connecticut",
         | 
| 845 | 
            +
                "DC"=>"District of Columbia",
         | 
| 846 | 
            +
                "DE"=>"Delaware",
         | 
| 847 | 
            +
                "FL"=>"Florida",
         | 
| 848 | 
            +
                "GA"=>"Georgia",
         | 
| 849 | 
            +
                "GU"=>"Guam",
         | 
| 850 | 
            +
                "HI"=>"Hawaii",
         | 
| 851 | 
            +
                "IA"=>"Iowa",
         | 
| 852 | 
            +
                "ID"=>"Idaho",
         | 
| 853 | 
            +
                "IL"=>"Illinois",
         | 
| 854 | 
            +
                "IN"=>"Indiana",
         | 
| 855 | 
            +
                "KS"=>"Kansas",
         | 
| 856 | 
            +
                "KY"=>"Kentucky",
         | 
| 857 | 
            +
                "LA"=>"Louisiana",
         | 
| 858 | 
            +
                "MA"=>"Massachusetts",
         | 
| 859 | 
            +
                "MD"=>"Maryland",
         | 
| 860 | 
            +
                "ME"=>"Maine",
         | 
| 861 | 
            +
                "MI"=>"Michigan",
         | 
| 862 | 
            +
                "MN"=>"Minnesota",
         | 
| 863 | 
            +
                "MO"=>"Missouri",
         | 
| 864 | 
            +
                "MS"=>"Mississippi",
         | 
| 865 | 
            +
                "MT"=>"Montana",
         | 
| 866 | 
            +
                "NC"=>"North Carolina",
         | 
| 867 | 
            +
                "ND"=>"North Dakota",
         | 
| 868 | 
            +
                "NE"=>"Nebraska",
         | 
| 869 | 
            +
                "NH"=>"New Hampshire",
         | 
| 870 | 
            +
                "NJ"=>"New Jersey",
         | 
| 871 | 
            +
                "NM"=>"New Mexico",
         | 
| 872 | 
            +
                "NV"=>"Nevada",
         | 
| 873 | 
            +
                "NY"=>"New York",
         | 
| 874 | 
            +
                "OH"=>"Ohio",
         | 
| 875 | 
            +
                "OK"=>"Oklahoma",
         | 
| 876 | 
            +
                "OR"=>"Oregon",
         | 
| 877 | 
            +
                "PA"=>"Pennsylvania",
         | 
| 878 | 
            +
                "PR"=>"Puerto Rico",
         | 
| 879 | 
            +
                "RI"=>"Rhode Island",
         | 
| 880 | 
            +
                "SC"=>"South Carolina",
         | 
| 881 | 
            +
                "SD"=>"South Dakota",
         | 
| 882 | 
            +
                "TN"=>"Tennessee",
         | 
| 883 | 
            +
                "TX"=>"Texas",
         | 
| 884 | 
            +
                "UT"=>"Utah",
         | 
| 885 | 
            +
                "VA"=>"Virginia",
         | 
| 886 | 
            +
                "VI"=>"Virgin Islands",
         | 
| 887 | 
            +
                "VT"=>"Vermont",
         | 
| 888 | 
            +
                "WA"=>"Washington",
         | 
| 889 | 
            +
                "WI"=>"Wisconsin",
         | 
| 890 | 
            +
                "WV"=>"West Virginia",
         | 
| 891 | 
            +
                "WY"=>"Wyoming"
         | 
| 892 | 
            +
              }
         | 
| 893 | 
            +
             | 
| 894 | 
            +
              include Glimmer::Web::Component
         | 
| 895 | 
            +
              
         | 
| 896 | 
            +
              option :full_name
         | 
| 897 | 
            +
              option :street
         | 
| 898 | 
            +
              option :street2
         | 
| 899 | 
            +
              option :city
         | 
| 900 | 
            +
              option :state
         | 
| 901 | 
            +
              option :zip_code
         | 
| 902 | 
            +
              
         | 
| 903 | 
            +
              attr_reader :address
         | 
| 904 | 
            +
              
         | 
| 905 | 
            +
              before_render do
         | 
| 906 | 
            +
                @address = Address.new(
         | 
| 907 | 
            +
                  full_name: full_name,
         | 
| 908 | 
            +
                  street: street,
         | 
| 909 | 
            +
                  street2: street2,
         | 
| 910 | 
            +
                  city: city,
         | 
| 911 | 
            +
                  state: state,
         | 
| 912 | 
            +
                  zip_code: zip_code,
         | 
| 913 | 
            +
                )
         | 
| 914 | 
            +
              end
         | 
| 915 | 
            +
              
         | 
| 916 | 
            +
              markup {
         | 
| 917 | 
            +
                div {
         | 
| 918 | 
            +
                  div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
         | 
| 919 | 
            +
                    label('Full Name: ', for: 'full-name-field')
         | 
| 920 | 
            +
                    input(id: 'full-name-field') {
         | 
| 921 | 
            +
                      value <=> [address, :full_name]
         | 
| 922 | 
            +
                    }
         | 
| 923 | 
            +
                    
         | 
| 924 | 
            +
                    @somelabel = label('Street: ', for: 'street-field')
         | 
| 925 | 
            +
                    input(id: 'street-field') {
         | 
| 926 | 
            +
                      value <=> [address, :street]
         | 
| 927 | 
            +
                    }
         | 
| 928 | 
            +
                    
         | 
| 929 | 
            +
                    label('Street 2: ', for: 'street2-field')
         | 
| 930 | 
            +
                    textarea(id: 'street2-field') {
         | 
| 931 | 
            +
                      value <=> [address, :street2]
         | 
| 932 | 
            +
                    }
         | 
| 933 | 
            +
                    
         | 
| 934 | 
            +
                    label('City: ', for: 'city-field')
         | 
| 935 | 
            +
                    input(id: 'city-field') {
         | 
| 936 | 
            +
                      value <=> [address, :city]
         | 
| 937 | 
            +
                    }
         | 
| 938 | 
            +
                    
         | 
| 939 | 
            +
                    label('State: ', for: 'state-field')
         | 
| 940 | 
            +
                    select(id: 'state-field') {
         | 
| 941 | 
            +
                      STATES.each do |state_code, state|
         | 
| 942 | 
            +
                        option(value: state_code) { state }
         | 
| 943 | 
            +
                      end
         | 
| 944 | 
            +
             | 
| 945 | 
            +
                      value <=> [address, :state_code]
         | 
| 946 | 
            +
                    }
         | 
| 947 | 
            +
                    
         | 
| 948 | 
            +
                    label('Zip Code: ', for: 'zip-code-field')
         | 
| 949 | 
            +
                    input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
         | 
| 950 | 
            +
                      value <=> [address, :zip_code,
         | 
| 951 | 
            +
                                  on_write: :to_s,
         | 
| 952 | 
            +
                                ]
         | 
| 953 | 
            +
                    }
         | 
| 954 | 
            +
                    
         | 
| 955 | 
            +
                    style {
         | 
| 956 | 
            +
                      <<~CSS
         | 
| 957 | 
            +
                        #{address_div.selector} * {
         | 
| 958 | 
            +
                          margin: 5px;
         | 
| 959 | 
            +
                        }
         | 
| 960 | 
            +
                        #{address_div.selector} input, #{address_div.selector} select {
         | 
| 961 | 
            +
                          grid-column: 2;
         | 
| 962 | 
            +
                        }
         | 
| 963 | 
            +
                      CSS
         | 
| 964 | 
            +
                    }
         | 
| 965 | 
            +
                  }
         | 
| 966 | 
            +
                  
         | 
| 967 | 
            +
                  div(style: 'margin: 5px') {
         | 
| 968 | 
            +
                    inner_text <= [address, :summary,
         | 
| 969 | 
            +
                                    computed_by: address.members + ['state_code'],
         | 
| 970 | 
            +
                                  ]
         | 
| 971 | 
            +
                  }
         | 
| 972 | 
            +
                }
         | 
| 973 | 
            +
              }
         | 
| 974 | 
            +
            end
         | 
| 975 | 
            +
            ```
         | 
| 976 | 
            +
             | 
| 977 | 
            +
            Screenshot:
         | 
| 978 | 
            +
             | 
| 979 | 
            +
            
         | 
| 980 | 
            +
             | 
| 696 981 | 
             
            NOTE: Glimmer DSL for Web is an Early Alpha project. If you want it developed faster, please [open an issue report](https://github.com/AndyObtiva/glimmer-dsl-web/issues/new). I have completed some GitHub project features much faster before due to [issue reports](https://github.com/AndyObtiva/glimmer-dsl-web/issues) and [pull requests](https://github.com/AndyObtiva/glimmer-dsl-web/pulls). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
         | 
| 697 982 |  | 
| 698 983 | 
             
            Learn more about the differences between various [Glimmer](https://github.com/AndyObtiva/glimmer) DSLs by looking at:
         | 
| @@ -714,9 +999,11 @@ Learn more about the differences between various [Glimmer](https://github.com/An | |
| 714 999 | 
             
                  - [Hello, World!](#hello-world)
         | 
| 715 1000 | 
             
                  - [Hello, Button!](#hello-button)
         | 
| 716 1001 | 
             
                  - [Hello, Form!](#hello-form)
         | 
| 1002 | 
            +
                  - [Hello, Observer!](#hello-observer)
         | 
| 717 1003 | 
             
                  - [Hello, Data-Binding!](#hello-data-binding)
         | 
| 718 1004 | 
             
                  - [Hello, Content Data-Binding!](#hello-content-data-binding)
         | 
| 719 1005 | 
             
                  - [Hello, Component!](#hello-content-data-binding)
         | 
| 1006 | 
            +
                  - [Hello, glimmer_component Rails Helper!](#hello-glimmer_component-rails-helper)
         | 
| 720 1007 | 
             
                  - [Hello, Input (Date/Time)!](#hello-input-datetime)
         | 
| 721 1008 | 
             
                  - [Button Counter](#button-counter)
         | 
| 722 1009 | 
             
              - [Glimmer Process](#glimmer-process)
         | 
| @@ -733,10 +1020,8 @@ Learn more about the differences between various [Glimmer](https://github.com/An | |
| 733 1020 |  | 
| 734 1021 | 
             
            [Glimmer DSL for Web](https://rubygems.org/gems/glimmer-dsl-web) will begin by supporting [Opal Ruby](https://opalrb.com/) on [Rails](https://rubyonrails.org/). [Opal](https://opalrb.com/) is a lightweight Ruby to JavaScript transpiler that results in small downloadables compared to WASM. In the future, the project might grow to support [Ruby WASM](https://github.com/ruby/ruby.wasm) as an alternative to [Opal Ruby](https://opalrb.com/) that could be switched to with a simple configuration change.
         | 
| 735 1022 |  | 
| 736 | 
            -
            - Ruby 3.0 | 
| 1023 | 
            +
            - Ruby 3.0+
         | 
| 737 1024 | 
             
            - Rails 6-7: [https://github.com/rails/rails](https://github.com/rails/rails)
         | 
| 738 | 
            -
            - Opal 1.4.1 for Rails 6-7: [https://github.com/opal/opal](https://github.com/opal/opal)
         | 
| 739 | 
            -
            - Opal-Rails 2.0.2 for Rails 6-7: [https://github.com/opal/opal-rails](https://github.com/opal/opal-rails)
         | 
| 740 1025 |  | 
| 741 1026 | 
             
            ## Setup
         | 
| 742 1027 |  | 
| @@ -763,7 +1048,7 @@ rails new glimmer_app_server | |
| 763 1048 | 
             
            Add the following to `Gemfile`:
         | 
| 764 1049 |  | 
| 765 1050 | 
             
            ```
         | 
| 766 | 
            -
            gem 'glimmer-dsl-web', '~> 0.0. | 
| 1051 | 
            +
            gem 'glimmer-dsl-web', '~> 0.0.11'
         | 
| 767 1052 | 
             
            ```
         | 
| 768 1053 |  | 
| 769 1054 | 
             
            Run:
         | 
| @@ -772,6 +1057,8 @@ Run: | |
| 772 1057 | 
             
            bundle
         | 
| 773 1058 | 
             
            ```
         | 
| 774 1059 |  | 
| 1060 | 
            +
            (run `rm -rf tmp/cache` from inside your Rails app if you upgrade your `glimmer-dsl-web` gem version from an older one to clear Opal-Rails's cache)
         | 
| 1061 | 
            +
             | 
| 775 1062 | 
             
            Follow [opal-rails](https://github.com/opal/opal-rails) instructions, basically running:
         | 
| 776 1063 |  | 
| 777 1064 | 
             
            ```
         | 
| @@ -782,6 +1069,7 @@ To enable the `glimmer-dsl-web` library in the frontend, edit `config/initialize | |
| 782 1069 |  | 
| 783 1070 | 
             
            ```ruby
         | 
| 784 1071 | 
             
            Opal.use_gem 'glimmer-dsl-web'
         | 
| 1072 | 
            +
            Opal.append_path Rails.root.join('app', 'assets', 'opal')
         | 
| 785 1073 | 
             
            ```
         | 
| 786 1074 |  | 
| 787 1075 | 
             
            To enable Opal Browser Debugging in Ruby with the [Source Maps](https://opalrb.com/docs/guides/v1.4.1/source_maps.html) feature, edit `config/initializers/opal.rb` and add the following inside the `Rails.application.configure do; end` block at the bottom of it:
         | 
| @@ -811,9 +1099,20 @@ root to: 'welcomes#index' | |
| 811 1099 |  | 
| 812 1100 | 
             
            Clear the file `app/views/welcomes/index.html.erb` completely from all content.
         | 
| 813 1101 |  | 
| 814 | 
            -
            Delete `app/javascript | 
| 1102 | 
            +
            Delete `app/javascript` directory
         | 
| 815 1103 |  | 
| 816 | 
            -
             | 
| 1104 | 
            +
            Rename `app/assets/javascript` directory to `app/assets/opal`.
         | 
| 1105 | 
            +
             | 
| 1106 | 
            +
            Add the following lines to `app/assets/config/manifest.js` (and delete their `javascript` equivalents):
         | 
| 1107 | 
            +
             | 
| 1108 | 
            +
            ```js
         | 
| 1109 | 
            +
            //= link_tree ../../opal .js
         | 
| 1110 | 
            +
            //= link_directory ../opal .js
         | 
| 1111 | 
            +
            ```
         | 
| 1112 | 
            +
             | 
| 1113 | 
            +
            Rename `app/assets/opal/application.js.rb` to `app/assets/opal/application.rb`.
         | 
| 1114 | 
            +
             | 
| 1115 | 
            +
            Edit and replace `app/assets/opal/application.rb` content with code below (optionally including a require statement for one of the [samples](#samples) below):
         | 
| 817 1116 |  | 
| 818 1117 | 
             
            ```ruby
         | 
| 819 1118 | 
             
            require 'glimmer-dsl-web' # brings opal and other dependencies automatically
         | 
| @@ -821,9 +1120,67 @@ require 'glimmer-dsl-web' # brings opal and other dependencies automatically | |
| 821 1120 | 
             
            # Add more require-statements or Glimmer GUI DSL code
         | 
| 822 1121 | 
             
            ```
         | 
| 823 1122 |  | 
| 1123 | 
            +
            ```ruby
         | 
| 1124 | 
            +
            require 'glimmer-dsl-web'
         | 
| 1125 | 
            +
             | 
| 1126 | 
            +
            require 'glimmer-dsl-web/samples/hello/hello_world.rb'
         | 
| 1127 | 
            +
            ```
         | 
| 1128 | 
            +
             | 
| 1129 | 
            +
            If the `<body></body>` element (where the Glimmer GUI DSL adds elements by default) is not available when the JS file is loading, you need to put the code inside a `Document.ready? do; end` (but, it is recommended that you load the JS file after the parent element like `<body></body>` is in the page already for faster performance, which is guaranteed automatically by using `glimmer_component`, mentioned in details below):
         | 
| 1130 | 
            +
             | 
| 1131 | 
            +
            ```ruby
         | 
| 1132 | 
            +
            require 'glimmer-dsl-web'
         | 
| 1133 | 
            +
             | 
| 1134 | 
            +
            Document.ready? do
         | 
| 1135 | 
            +
              require 'glimmer-dsl-web/samples/hello/hello_world.rb'
         | 
| 1136 | 
            +
            end
         | 
| 1137 | 
            +
            ```
         | 
| 1138 | 
            +
             | 
| 824 1139 | 
             
            Example to confirm setup is working:
         | 
| 825 1140 |  | 
| 826 | 
            -
             | 
| 1141 | 
            +
            Glimmer GUI code:
         | 
| 1142 | 
            +
             | 
| 1143 | 
            +
            ```ruby
         | 
| 1144 | 
            +
            require 'glimmer-dsl-web'
         | 
| 1145 | 
            +
             | 
| 1146 | 
            +
            include Glimmer
         | 
| 1147 | 
            +
             | 
| 1148 | 
            +
            Document.ready? do
         | 
| 1149 | 
            +
              # This will hook into element #app-container and then build HTML inside it using Ruby DSL code
         | 
| 1150 | 
            +
              div {
         | 
| 1151 | 
            +
                label(class: 'greeting') {
         | 
| 1152 | 
            +
                  'Hello, World!'
         | 
| 1153 | 
            +
                }
         | 
| 1154 | 
            +
              }.render
         | 
| 1155 | 
            +
            end
         | 
| 1156 | 
            +
            ```
         | 
| 1157 | 
            +
             | 
| 1158 | 
            +
            That produces:
         | 
| 1159 | 
            +
             | 
| 1160 | 
            +
            ```html
         | 
| 1161 | 
            +
            <body>
         | 
| 1162 | 
            +
              <div data-parent="body" class="element element-1">
         | 
| 1163 | 
            +
                <label class="greeting element element-2">
         | 
| 1164 | 
            +
                  Hello, World!
         | 
| 1165 | 
            +
                </label>
         | 
| 1166 | 
            +
              </div>
         | 
| 1167 | 
            +
            </body>
         | 
| 1168 | 
            +
            ```
         | 
| 1169 | 
            +
             | 
| 1170 | 
            +
            Start the Rails server:
         | 
| 1171 | 
            +
            ```
         | 
| 1172 | 
            +
            rails s
         | 
| 1173 | 
            +
            ```
         | 
| 1174 | 
            +
             | 
| 1175 | 
            +
            Visit `http://localhost:3000`
         | 
| 1176 | 
            +
             | 
| 1177 | 
            +
            You should see:
         | 
| 1178 | 
            +
             | 
| 1179 | 
            +
            
         | 
| 1180 | 
            +
             | 
| 1181 | 
            +
            If you want to customize where the top-level element is mounted, just pass a `parent: 'css_selector'` option.
         | 
| 1182 | 
            +
             | 
| 1183 | 
            +
            HTML:
         | 
| 827 1184 |  | 
| 828 1185 | 
             
            ```html
         | 
| 829 1186 | 
             
            ...
         | 
| @@ -854,7 +1211,7 @@ That produces: | |
| 854 1211 | 
             
            ```html
         | 
| 855 1212 | 
             
            ...
         | 
| 856 1213 | 
             
            <div id="app-container">
         | 
| 857 | 
            -
              <div data-parent=" | 
| 1214 | 
            +
              <div data-parent="app-container" class="element element-1">
         | 
| 858 1215 | 
             
                <label class="greeting element element-2">
         | 
| 859 1216 | 
             
                  Hello, World!
         | 
| 860 1217 | 
             
                </label>
         | 
| @@ -862,17 +1219,21 @@ That produces: | |
| 862 1219 | 
             
            </div>
         | 
| 863 1220 | 
             
            ...
         | 
| 864 1221 | 
             
            ```
         | 
| 1222 | 
            +
            You may insert a Glimmer component anywhere into a Rails View using `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper` or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
         | 
| 865 1223 |  | 
| 866 | 
            -
             | 
| 867 | 
            -
            ```
         | 
| 868 | 
            -
            rails s
         | 
| 869 | 
            -
            ```
         | 
| 1224 | 
            +
            To use `glimmer_component`, edit `app/helpers/application_helper.rb` in your Rails application, add `require 'glimmer/helpers/glimmer_helper'` on top and `include GlimmerHelper` inside `module`.
         | 
| 870 1225 |  | 
| 871 | 
            -
             | 
| 1226 | 
            +
            `app/helpers/application_helper.rb` should look like this after the change:
         | 
| 872 1227 |  | 
| 873 | 
            -
             | 
| 1228 | 
            +
            ```ruby
         | 
| 1229 | 
            +
            require 'glimmer/helpers/glimmer_helper'
         | 
| 874 1230 |  | 
| 875 | 
            -
             | 
| 1231 | 
            +
            module ApplicationHelper
         | 
| 1232 | 
            +
              # ...
         | 
| 1233 | 
            +
              include GlimmerHelper
         | 
| 1234 | 
            +
              # ...
         | 
| 1235 | 
            +
            end
         | 
| 1236 | 
            +
            ```
         | 
| 876 1237 |  | 
| 877 1238 | 
             
            If you run into any issues in setup, refer to the [Sample Glimmer DSL for Web Rails 7 App](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails7-app) project (in case I forgot to include some setup steps by mistake).
         | 
| 878 1239 |  | 
| @@ -903,7 +1264,7 @@ Disable the `webpacker` gem line in `Gemfile`: | |
| 903 1264 | 
             
            Add the following to `Gemfile`:
         | 
| 904 1265 |  | 
| 905 1266 | 
             
            ```ruby
         | 
| 906 | 
            -
            gem 'glimmer-dsl-web', '~> 0.0. | 
| 1267 | 
            +
            gem 'glimmer-dsl-web', '~> 0.0.11'
         | 
| 907 1268 | 
             
            ```
         | 
| 908 1269 |  | 
| 909 1270 | 
             
            Run:
         | 
| @@ -912,6 +1273,8 @@ Run: | |
| 912 1273 | 
             
            bundle
         | 
| 913 1274 | 
             
            ```
         | 
| 914 1275 |  | 
| 1276 | 
            +
            (run `rm -rf tmp/cache` from inside your Rails app if you upgrade your `glimmer-dsl-web` gem version from an older one to clear Opal-Rails's cache)
         | 
| 1277 | 
            +
             | 
| 915 1278 | 
             
            Follow [opal-rails](https://github.com/opal/opal-rails) instructions, basically running:
         | 
| 916 1279 |  | 
| 917 1280 | 
             
            ```
         | 
| @@ -922,6 +1285,7 @@ To enable the `glimmer-dsl-web` library in the frontend, edit `config/initialize | |
| 922 1285 |  | 
| 923 1286 | 
             
            ```ruby
         | 
| 924 1287 | 
             
            Opal.use_gem 'glimmer-dsl-web'
         | 
| 1288 | 
            +
            Opal.append_path Rails.root.join('app', 'assets', 'opal')
         | 
| 925 1289 | 
             
            ```
         | 
| 926 1290 |  | 
| 927 1291 | 
             
            To enable Opal Browser Debugging in Ruby with the [Source Maps](https://opalrb.com/docs/guides/v1.4.1/source_maps.html) feature, edit `config/initializers/opal.rb` and add the following inside the `Rails.application.configure do; end` block at the bottom of it:
         | 
| @@ -958,7 +1322,18 @@ Also, delete the following line: | |
| 958 1322 |  | 
| 959 1323 | 
             
            Clear the file `app/views/welcomes/index.html.erb` completely from all content.
         | 
| 960 1324 |  | 
| 961 | 
            -
             | 
| 1325 | 
            +
            Rename `app/assets/javascript` directory to `app/assets/opal`.
         | 
| 1326 | 
            +
             | 
| 1327 | 
            +
            Add the following lines to `app/assets/config/manifest.js` (and delete their `javascript` equivalents):
         | 
| 1328 | 
            +
             | 
| 1329 | 
            +
            ```js
         | 
| 1330 | 
            +
            //= link_tree ../../opal .js
         | 
| 1331 | 
            +
            //= link_directory ../opal .js
         | 
| 1332 | 
            +
            ```
         | 
| 1333 | 
            +
             | 
| 1334 | 
            +
            Rename `app/assets/opal/application.js.rb` to `app/assets/opal/application.rb`.
         | 
| 1335 | 
            +
             | 
| 1336 | 
            +
            Edit and replace `app/assets/opal/application.rb` content with code below (optionally including a require statement for one of the [samples](#samples) below inside a `Document.ready? do; end` block):
         | 
| 962 1337 |  | 
| 963 1338 | 
             
            ```ruby
         | 
| 964 1339 | 
             
            require 'glimmer-dsl-web' # brings opal and other dependencies automatically
         | 
| @@ -1019,6 +1394,22 @@ You should see: | |
| 1019 1394 |  | 
| 1020 1395 | 
             
            
         | 
| 1021 1396 |  | 
| 1397 | 
            +
            You may insert a Glimmer component anywhere into a Rails View using `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper` or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
         | 
| 1398 | 
            +
             | 
| 1399 | 
            +
            To use `glimmer_component`, edit `app/helpers/application_helper.rb` in your Rails application, add `require 'glimmer/helpers/glimmer_helper'` on top and `include GlimmerHelper` inside `module`.
         | 
| 1400 | 
            +
             | 
| 1401 | 
            +
            `app/helpers/application_helper.rb` should look like this after the change:
         | 
| 1402 | 
            +
             | 
| 1403 | 
            +
            ```ruby
         | 
| 1404 | 
            +
            require 'glimmer/helpers/glimmer_helper'
         | 
| 1405 | 
            +
             | 
| 1406 | 
            +
            module ApplicationHelper
         | 
| 1407 | 
            +
              # ...
         | 
| 1408 | 
            +
              include GlimmerHelper
         | 
| 1409 | 
            +
              # ...
         | 
| 1410 | 
            +
            end
         | 
| 1411 | 
            +
            ```
         | 
| 1412 | 
            +
             | 
| 1022 1413 | 
             
            **NOT RELEASED OR SUPPORTED YET**
         | 
| 1023 1414 |  | 
| 1024 1415 | 
             
            If you run into any issues in setup, refer to the [Sample Glimmer DSL for Web Rails 6 App](https://github.com/AndyObtiva/sample-glimmer-dsl-web-rails6-app) project (in case I forgot to include some setup steps by mistake).
         | 
| @@ -1354,6 +1745,86 @@ Screenshot: | |
| 1354 1745 |  | 
| 1355 1746 | 
             
            
         | 
| 1356 1747 |  | 
| 1748 | 
            +
            #### Hello, Observer!
         | 
| 1749 | 
            +
             | 
| 1750 | 
            +
            [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) (covered later), 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 (covered later).
         | 
| 1751 | 
            +
             | 
| 1752 | 
            +
            Glimmer GUI code:
         | 
| 1753 | 
            +
             | 
| 1754 | 
            +
            ```ruby
         | 
| 1755 | 
            +
            require 'glimmer-dsl-web'
         | 
| 1756 | 
            +
             | 
| 1757 | 
            +
            class NumberHolder
         | 
| 1758 | 
            +
              attr_accessor :number
         | 
| 1759 | 
            +
              
         | 
| 1760 | 
            +
              def initialize
         | 
| 1761 | 
            +
                self.number = 50
         | 
| 1762 | 
            +
              end
         | 
| 1763 | 
            +
            end
         | 
| 1764 | 
            +
             | 
| 1765 | 
            +
            class HelloObserver
         | 
| 1766 | 
            +
              include Glimmer::Web::Component
         | 
| 1767 | 
            +
              
         | 
| 1768 | 
            +
              before_render do
         | 
| 1769 | 
            +
                @number_holder = NumberHolder.new
         | 
| 1770 | 
            +
              end
         | 
| 1771 | 
            +
              
         | 
| 1772 | 
            +
              after_render do
         | 
| 1773 | 
            +
                # observe Model attribute @number_holder.number for changes and update View
         | 
| 1774 | 
            +
                observe(@number_holder, :number) do
         | 
| 1775 | 
            +
                  number_string = @number_holder.number.to_s
         | 
| 1776 | 
            +
                  @number_input.value = number_string unless @number_input.value == number_string
         | 
| 1777 | 
            +
                  @range_input.value = number_string unless @range_input.value == number_string
         | 
| 1778 | 
            +
                end
         | 
| 1779 | 
            +
                # Bidirectional Data-Binding does the same thing automatically
         | 
| 1780 | 
            +
                # Just disable the observe block above as well as the oninput listeners below
         | 
| 1781 | 
            +
                # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
         | 
| 1782 | 
            +
                # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 1783 | 
            +
              end
         | 
| 1784 | 
            +
              
         | 
| 1785 | 
            +
              markup {
         | 
| 1786 | 
            +
                div {
         | 
| 1787 | 
            +
                  div {
         | 
| 1788 | 
            +
                    @number_input = input(type: 'number', value: @number_holder.number, min: 0, max: 100) {
         | 
| 1789 | 
            +
                      # oninput listener updates Model attribute @number_holder.number
         | 
| 1790 | 
            +
                      oninput do
         | 
| 1791 | 
            +
                        @number_holder.number = @number_input.value.to_i
         | 
| 1792 | 
            +
                      end
         | 
| 1793 | 
            +
                      
         | 
| 1794 | 
            +
                      # Bidirectional Data-Binding simplifies the implementation significantly
         | 
| 1795 | 
            +
                      # by enabling the following line and disabling oninput listeners as well
         | 
| 1796 | 
            +
                      # as the after_body observe block observer
         | 
| 1797 | 
            +
                      # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 1798 | 
            +
            #           value <=> [@number_holder, :number]
         | 
| 1799 | 
            +
                    }
         | 
| 1800 | 
            +
                  }
         | 
| 1801 | 
            +
                  div {
         | 
| 1802 | 
            +
                    @range_input = input(type: 'range', value: @number_holder.number, min: 0, max: 100) {
         | 
| 1803 | 
            +
                      # oninput listener updates Model attribute @number_holder.number
         | 
| 1804 | 
            +
                      oninput do
         | 
| 1805 | 
            +
                        @number_holder.number = @range_input.value.to_i
         | 
| 1806 | 
            +
                      end
         | 
| 1807 | 
            +
                      
         | 
| 1808 | 
            +
                      # Bidirectional Data-Binding simplifies the implementation significantly
         | 
| 1809 | 
            +
                      # by enabling the following line and disabling oninput listeners as well
         | 
| 1810 | 
            +
                      # as the after_body observe block observer
         | 
| 1811 | 
            +
                      # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
         | 
| 1812 | 
            +
            #           value <=> [@number_holder, :number]
         | 
| 1813 | 
            +
                    }
         | 
| 1814 | 
            +
                  }
         | 
| 1815 | 
            +
                }
         | 
| 1816 | 
            +
              }
         | 
| 1817 | 
            +
            end
         | 
| 1818 | 
            +
             | 
| 1819 | 
            +
            Document.ready? do
         | 
| 1820 | 
            +
              HelloObserver.render
         | 
| 1821 | 
            +
            end
         | 
| 1822 | 
            +
            ```
         | 
| 1823 | 
            +
             | 
| 1824 | 
            +
            Screenshot:
         | 
| 1825 | 
            +
             | 
| 1826 | 
            +
            
         | 
| 1827 | 
            +
             | 
| 1357 1828 | 
             
            #### Hello, Data-Binding!
         | 
| 1358 1829 |  | 
| 1359 1830 | 
             
            [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.
         | 
| @@ -1883,6 +2354,212 @@ Screenshot: | |
| 1883 2354 |  | 
| 1884 2355 | 
             
            
         | 
| 1885 2356 |  | 
| 2357 | 
            +
            #### Hello, glimmer_component Rails Helper!
         | 
| 2358 | 
            +
             | 
| 2359 | 
            +
            You may insert a Glimmer component anywhere into a Rails View using
         | 
| 2360 | 
            +
            `glimmer_component(component_path, *args)` Rails helper. Add `include GlimmerHelper` to `ApplicationHelper`
         | 
| 2361 | 
            +
            or another Rails helper, and use `<%= glimmer_component("path/to/component", *args) %>` in Views.
         | 
| 2362 | 
            +
             | 
| 2363 | 
            +
            Rails `ApplicationHelper` setup code:
         | 
| 2364 | 
            +
             | 
| 2365 | 
            +
            ```ruby
         | 
| 2366 | 
            +
            require 'glimmer/helpers/glimmer_helper'
         | 
| 2367 | 
            +
             | 
| 2368 | 
            +
            module ApplicationHelper
         | 
| 2369 | 
            +
              # ...
         | 
| 2370 | 
            +
              include GlimmerHelper
         | 
| 2371 | 
            +
              # ...
         | 
| 2372 | 
            +
            end
         | 
| 2373 | 
            +
            ```
         | 
| 2374 | 
            +
             | 
| 2375 | 
            +
            Rails View code:
         | 
| 2376 | 
            +
             | 
| 2377 | 
            +
            ```erb
         | 
| 2378 | 
            +
            <div id="address-container">
         | 
| 2379 | 
            +
              <h1>Shipping Address </h1>
         | 
| 2380 | 
            +
              <legend>Please enter your shipping address information (Zip Code must be a valid 5 digit number)</legend>
         | 
| 2381 | 
            +
              <!-- This sample demonstrates use of glimmer_component helper with arguments -->
         | 
| 2382 | 
            +
              <%= glimmer_component('address_form',
         | 
| 2383 | 
            +
                    full_name: params[:full_name],
         | 
| 2384 | 
            +
                    street: params[:street],
         | 
| 2385 | 
            +
                    street2: params[:street2],
         | 
| 2386 | 
            +
                    city: params[:city],
         | 
| 2387 | 
            +
                    state: params[:state],
         | 
| 2388 | 
            +
                    zip_code: params[:zip_code]
         | 
| 2389 | 
            +
                  )
         | 
| 2390 | 
            +
               %>
         | 
| 2391 | 
            +
               <div>
         | 
| 2392 | 
            +
                 <a href="/"><< Back Home</a>
         | 
| 2393 | 
            +
               </div>
         | 
| 2394 | 
            +
            </div>
         | 
| 2395 | 
            +
            ```
         | 
| 2396 | 
            +
             | 
| 2397 | 
            +
            Glimmer GUI code:
         | 
| 2398 | 
            +
             | 
| 2399 | 
            +
            ```ruby
         | 
| 2400 | 
            +
            require 'glimmer-dsl-web'
         | 
| 2401 | 
            +
             | 
| 2402 | 
            +
            class AddressForm
         | 
| 2403 | 
            +
              Address = Struct.new(:full_name, :street, :street2, :city, :state, :zip_code, keyword_init: true) do
         | 
| 2404 | 
            +
                def state_code
         | 
| 2405 | 
            +
                  STATES.invert[state]
         | 
| 2406 | 
            +
                end
         | 
| 2407 | 
            +
                
         | 
| 2408 | 
            +
                def state_code=(value)
         | 
| 2409 | 
            +
                  self.state = STATES[value]
         | 
| 2410 | 
            +
                end
         | 
| 2411 | 
            +
              
         | 
| 2412 | 
            +
                def summary
         | 
| 2413 | 
            +
                  to_h.values.map(&:to_s).reject(&:empty?).join(', ')
         | 
| 2414 | 
            +
                end
         | 
| 2415 | 
            +
              end
         | 
| 2416 | 
            +
              
         | 
| 2417 | 
            +
              STATES = {
         | 
| 2418 | 
            +
                "AK"=>"Alaska",
         | 
| 2419 | 
            +
                "AL"=>"Alabama",
         | 
| 2420 | 
            +
                "AR"=>"Arkansas",
         | 
| 2421 | 
            +
                "AS"=>"American Samoa",
         | 
| 2422 | 
            +
                "AZ"=>"Arizona",
         | 
| 2423 | 
            +
                "CA"=>"California",
         | 
| 2424 | 
            +
                "CO"=>"Colorado",
         | 
| 2425 | 
            +
                "CT"=>"Connecticut",
         | 
| 2426 | 
            +
                "DC"=>"District of Columbia",
         | 
| 2427 | 
            +
                "DE"=>"Delaware",
         | 
| 2428 | 
            +
                "FL"=>"Florida",
         | 
| 2429 | 
            +
                "GA"=>"Georgia",
         | 
| 2430 | 
            +
                "GU"=>"Guam",
         | 
| 2431 | 
            +
                "HI"=>"Hawaii",
         | 
| 2432 | 
            +
                "IA"=>"Iowa",
         | 
| 2433 | 
            +
                "ID"=>"Idaho",
         | 
| 2434 | 
            +
                "IL"=>"Illinois",
         | 
| 2435 | 
            +
                "IN"=>"Indiana",
         | 
| 2436 | 
            +
                "KS"=>"Kansas",
         | 
| 2437 | 
            +
                "KY"=>"Kentucky",
         | 
| 2438 | 
            +
                "LA"=>"Louisiana",
         | 
| 2439 | 
            +
                "MA"=>"Massachusetts",
         | 
| 2440 | 
            +
                "MD"=>"Maryland",
         | 
| 2441 | 
            +
                "ME"=>"Maine",
         | 
| 2442 | 
            +
                "MI"=>"Michigan",
         | 
| 2443 | 
            +
                "MN"=>"Minnesota",
         | 
| 2444 | 
            +
                "MO"=>"Missouri",
         | 
| 2445 | 
            +
                "MS"=>"Mississippi",
         | 
| 2446 | 
            +
                "MT"=>"Montana",
         | 
| 2447 | 
            +
                "NC"=>"North Carolina",
         | 
| 2448 | 
            +
                "ND"=>"North Dakota",
         | 
| 2449 | 
            +
                "NE"=>"Nebraska",
         | 
| 2450 | 
            +
                "NH"=>"New Hampshire",
         | 
| 2451 | 
            +
                "NJ"=>"New Jersey",
         | 
| 2452 | 
            +
                "NM"=>"New Mexico",
         | 
| 2453 | 
            +
                "NV"=>"Nevada",
         | 
| 2454 | 
            +
                "NY"=>"New York",
         | 
| 2455 | 
            +
                "OH"=>"Ohio",
         | 
| 2456 | 
            +
                "OK"=>"Oklahoma",
         | 
| 2457 | 
            +
                "OR"=>"Oregon",
         | 
| 2458 | 
            +
                "PA"=>"Pennsylvania",
         | 
| 2459 | 
            +
                "PR"=>"Puerto Rico",
         | 
| 2460 | 
            +
                "RI"=>"Rhode Island",
         | 
| 2461 | 
            +
                "SC"=>"South Carolina",
         | 
| 2462 | 
            +
                "SD"=>"South Dakota",
         | 
| 2463 | 
            +
                "TN"=>"Tennessee",
         | 
| 2464 | 
            +
                "TX"=>"Texas",
         | 
| 2465 | 
            +
                "UT"=>"Utah",
         | 
| 2466 | 
            +
                "VA"=>"Virginia",
         | 
| 2467 | 
            +
                "VI"=>"Virgin Islands",
         | 
| 2468 | 
            +
                "VT"=>"Vermont",
         | 
| 2469 | 
            +
                "WA"=>"Washington",
         | 
| 2470 | 
            +
                "WI"=>"Wisconsin",
         | 
| 2471 | 
            +
                "WV"=>"West Virginia",
         | 
| 2472 | 
            +
                "WY"=>"Wyoming"
         | 
| 2473 | 
            +
              }
         | 
| 2474 | 
            +
             | 
| 2475 | 
            +
              include Glimmer::Web::Component
         | 
| 2476 | 
            +
              
         | 
| 2477 | 
            +
              option :full_name
         | 
| 2478 | 
            +
              option :street
         | 
| 2479 | 
            +
              option :street2
         | 
| 2480 | 
            +
              option :city
         | 
| 2481 | 
            +
              option :state
         | 
| 2482 | 
            +
              option :zip_code
         | 
| 2483 | 
            +
              
         | 
| 2484 | 
            +
              attr_reader :address
         | 
| 2485 | 
            +
              
         | 
| 2486 | 
            +
              before_render do
         | 
| 2487 | 
            +
                @address = Address.new(
         | 
| 2488 | 
            +
                  full_name: full_name,
         | 
| 2489 | 
            +
                  street: street,
         | 
| 2490 | 
            +
                  street2: street2,
         | 
| 2491 | 
            +
                  city: city,
         | 
| 2492 | 
            +
                  state: state,
         | 
| 2493 | 
            +
                  zip_code: zip_code,
         | 
| 2494 | 
            +
                )
         | 
| 2495 | 
            +
              end
         | 
| 2496 | 
            +
              
         | 
| 2497 | 
            +
              markup {
         | 
| 2498 | 
            +
                div {
         | 
| 2499 | 
            +
                  div(style: 'display: grid; grid-auto-columns: 80px 260px;') { |address_div|
         | 
| 2500 | 
            +
                    label('Full Name: ', for: 'full-name-field')
         | 
| 2501 | 
            +
                    input(id: 'full-name-field') {
         | 
| 2502 | 
            +
                      value <=> [address, :full_name]
         | 
| 2503 | 
            +
                    }
         | 
| 2504 | 
            +
                    
         | 
| 2505 | 
            +
                    @somelabel = label('Street: ', for: 'street-field')
         | 
| 2506 | 
            +
                    input(id: 'street-field') {
         | 
| 2507 | 
            +
                      value <=> [address, :street]
         | 
| 2508 | 
            +
                    }
         | 
| 2509 | 
            +
                    
         | 
| 2510 | 
            +
                    label('Street 2: ', for: 'street2-field')
         | 
| 2511 | 
            +
                    textarea(id: 'street2-field') {
         | 
| 2512 | 
            +
                      value <=> [address, :street2]
         | 
| 2513 | 
            +
                    }
         | 
| 2514 | 
            +
                    
         | 
| 2515 | 
            +
                    label('City: ', for: 'city-field')
         | 
| 2516 | 
            +
                    input(id: 'city-field') {
         | 
| 2517 | 
            +
                      value <=> [address, :city]
         | 
| 2518 | 
            +
                    }
         | 
| 2519 | 
            +
                    
         | 
| 2520 | 
            +
                    label('State: ', for: 'state-field')
         | 
| 2521 | 
            +
                    select(id: 'state-field') {
         | 
| 2522 | 
            +
                      STATES.each do |state_code, state|
         | 
| 2523 | 
            +
                        option(value: state_code) { state }
         | 
| 2524 | 
            +
                      end
         | 
| 2525 | 
            +
             | 
| 2526 | 
            +
                      value <=> [address, :state_code]
         | 
| 2527 | 
            +
                    }
         | 
| 2528 | 
            +
                    
         | 
| 2529 | 
            +
                    label('Zip Code: ', for: 'zip-code-field')
         | 
| 2530 | 
            +
                    input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
         | 
| 2531 | 
            +
                      value <=> [address, :zip_code,
         | 
| 2532 | 
            +
                                  on_write: :to_s,
         | 
| 2533 | 
            +
                                ]
         | 
| 2534 | 
            +
                    }
         | 
| 2535 | 
            +
                    
         | 
| 2536 | 
            +
                    style {
         | 
| 2537 | 
            +
                      <<~CSS
         | 
| 2538 | 
            +
                        #{address_div.selector} * {
         | 
| 2539 | 
            +
                          margin: 5px;
         | 
| 2540 | 
            +
                        }
         | 
| 2541 | 
            +
                        #{address_div.selector} input, #{address_div.selector} select {
         | 
| 2542 | 
            +
                          grid-column: 2;
         | 
| 2543 | 
            +
                        }
         | 
| 2544 | 
            +
                      CSS
         | 
| 2545 | 
            +
                    }
         | 
| 2546 | 
            +
                  }
         | 
| 2547 | 
            +
                  
         | 
| 2548 | 
            +
                  div(style: 'margin: 5px') {
         | 
| 2549 | 
            +
                    inner_text <= [address, :summary,
         | 
| 2550 | 
            +
                                    computed_by: address.members + ['state_code'],
         | 
| 2551 | 
            +
                                  ]
         | 
| 2552 | 
            +
                  }
         | 
| 2553 | 
            +
                }
         | 
| 2554 | 
            +
              }
         | 
| 2555 | 
            +
            end
         | 
| 2556 | 
            +
            ```
         | 
| 2557 | 
            +
             | 
| 2558 | 
            +
            Screenshot:
         | 
| 2559 | 
            +
             | 
| 2560 | 
            +
            
         | 
| 2561 | 
            +
             | 
| 2562 | 
            +
             | 
| 1886 2563 | 
             
            #### Hello, Input (Date/Time)!
         | 
| 1887 2564 |  | 
| 1888 2565 | 
             
            Glimmer GUI code:
         | 
| @@ -1992,8 +2669,6 @@ Screenshot: | |
| 1992 2669 |  | 
| 1993 2670 | 
             
            #### Button Counter
         | 
| 1994 2671 |  | 
| 1995 | 
            -
            **UPCOMING (NOT RELEASED OR SUPPORTED YET)**
         | 
| 1996 | 
            -
             | 
| 1997 2672 | 
             
            Glimmer GUI code demonstrating MVC + Glimmer Web Components (Views) + Data-Binding:
         | 
| 1998 2673 |  | 
| 1999 2674 | 
             
            ```ruby
         | 
| @@ -2005,13 +2680,9 @@ class Counter | |
| 2005 2680 | 
             
              def initialize
         | 
| 2006 2681 | 
             
                self.count = 0
         | 
| 2007 2682 | 
             
              end
         | 
| 2008 | 
            -
             | 
| 2009 | 
            -
              def increment!
         | 
| 2010 | 
            -
                self.count += 1
         | 
| 2011 | 
            -
              end
         | 
| 2012 2683 | 
             
            end
         | 
| 2013 2684 |  | 
| 2014 | 
            -
            class  | 
| 2685 | 
            +
            class ButtonCounter
         | 
| 2015 2686 | 
             
              include Glimmer::Web::Component
         | 
| 2016 2687 |  | 
| 2017 2688 | 
             
              before_render do
         | 
| @@ -2019,32 +2690,31 @@ class HelloButton | |
| 2019 2690 | 
             
              end
         | 
| 2020 2691 |  | 
| 2021 2692 | 
             
              markup {
         | 
| 2022 | 
            -
                 | 
| 2023 | 
            -
                div(parent: parent_selector) {
         | 
| 2024 | 
            -
                  text 'Button Counter'
         | 
| 2025 | 
            -
                  
         | 
| 2693 | 
            +
                div {
         | 
| 2026 2694 | 
             
                  button {
         | 
| 2027 2695 | 
             
                    # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
         | 
| 2028 2696 | 
             
                    # is read and converted to "Click To Increment: #{value}  ", and then automatically
         | 
| 2029 2697 | 
             
                    # copied to button innerText (content) to display to the user
         | 
| 2030 | 
            -
                    inner_text <= [@counter, :count, | 
| 2698 | 
            +
                    inner_text <= [@counter, :count,
         | 
| 2699 | 
            +
                                    on_read: ->(value) { "Click To Increment: #{value}  " }
         | 
| 2700 | 
            +
                                  ]
         | 
| 2031 2701 |  | 
| 2032 2702 | 
             
                    onclick {
         | 
| 2033 | 
            -
                      @counter. | 
| 2703 | 
            +
                      @counter.count += 1
         | 
| 2034 2704 | 
             
                    }
         | 
| 2035 2705 | 
             
                  }
         | 
| 2036 2706 | 
             
                }
         | 
| 2037 2707 | 
             
              }
         | 
| 2038 2708 | 
             
            end
         | 
| 2039 2709 |  | 
| 2040 | 
            -
             | 
| 2710 | 
            +
            ButtonCounter.render
         | 
| 2041 2711 | 
             
            ```
         | 
| 2042 2712 |  | 
| 2043 2713 | 
             
            That produces:
         | 
| 2044 2714 |  | 
| 2045 2715 | 
             
            ```html
         | 
| 2046 | 
            -
            <div  | 
| 2047 | 
            -
              <button>
         | 
| 2716 | 
            +
            <div data-parent="body" class="element element-1">
         | 
| 2717 | 
            +
              <button class="element element-2">
         | 
| 2048 2718 | 
             
                Click To Increment: 0
         | 
| 2049 2719 | 
             
              </button>
         | 
| 2050 2720 | 
             
            </div>
         | 
| @@ -2053,8 +2723,8 @@ That produces: | |
| 2053 2723 | 
             
            When clicked:
         | 
| 2054 2724 |  | 
| 2055 2725 | 
             
            ```html
         | 
| 2056 | 
            -
            <div  | 
| 2057 | 
            -
              <button>
         | 
| 2726 | 
            +
            <div data-parent="body" class="element element-1">
         | 
| 2727 | 
            +
              <button class="element element-2">
         | 
| 2058 2728 | 
             
                Click To Increment: 1
         | 
| 2059 2729 | 
             
              </button>
         | 
| 2060 2730 | 
             
            </div>
         | 
| @@ -2063,13 +2733,17 @@ When clicked: | |
| 2063 2733 | 
             
            When clicked 7 times:
         | 
| 2064 2734 |  | 
| 2065 2735 | 
             
            ```html
         | 
| 2066 | 
            -
            <div  | 
| 2067 | 
            -
              <button>
         | 
| 2736 | 
            +
            <div data-parent="body" class="element element-1">
         | 
| 2737 | 
            +
              <button class="element element-2">
         | 
| 2068 2738 | 
             
                Click To Increment: 7
         | 
| 2069 2739 | 
             
              </button>
         | 
| 2070 2740 | 
             
            </div>
         | 
| 2071 2741 | 
             
            ```
         | 
| 2072 2742 |  | 
| 2743 | 
            +
            Screenshot:
         | 
| 2744 | 
            +
             | 
| 2745 | 
            +
            
         | 
| 2746 | 
            +
             | 
| 2073 2747 | 
             
            ## Glimmer Supporting Libraries
         | 
| 2074 2748 |  | 
| 2075 2749 | 
             
            Here is a list of notable 3rd party gems used by Glimmer DSL for Web:
         |