Glimmer 1.0.0

Gem Version Travis CI Coverage Status Maintainability Join the chat at https://gitter.im/AndyObtiva/glimmer

Contributors Wanted! (Submit a Glimmer App Sample to Get Started)

(The Original Glimmer Library Since 2007. Beware of Imitators!)

Glimmer is a Ruby DSL engine with support for the following DSLs:

Glimmer and/or Glimmer DSLs receive two updates per month. You can trust Glimmer with your Ruby development needs.


Featured in
JRuby Cookbook

Table of contents

Glimmer DSL for SWT (JRuby Desktop Development GUI Library)

Glimmer DSL for SWT is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust Eclipse SWT library. Glimmer DSL for SWT additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns. To get started quickly, Glimmer DSL for SWT offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer DSL for SWT also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store and MSI/EXE files on Windows.

Glimmer DSL for SWT Samples

Hello, World!

Glimmer code (from samples/hello/hello_world.rb):

include Glimmer

shell {
  text "Glimmer"
  label {
    text "Hello, World!"
  }
}.open

Run:

glimmer sample:run[hello_world]

Glimmer app:

Hello World

Tic Tac Toe

Glimmer code (from samples/elaborate/tic_tac_toe.rb):

# ...
    @tic_tac_toe_board = Board.new

    @shell = shell {
      text "Tic-Tac-Toe"
      minimum_size 150, 178
      composite {
        grid_layout 3, true
        (1..3).each { |row|
          (1..3).each { |column|
            button {
              layout_data :fill, :fill, true, true
              text        bind(@tic_tac_toe_board[row, column], :sign)
              enabled     bind(@tic_tac_toe_board[row, column], :empty)
              font        style: :bold, height: 20
              on_widget_selected {
                @tic_tac_toe_board.mark(row, column)
              }
            }
          }
        }
      }
    }

    observe(@tic_tac_toe_board, :game_status) { |game_status|
      display_win_message if game_status == Board::WIN
      display_draw_message if game_status == Board::DRAW
    }
# ...

Run:

glimmer sample:run[tic_tac_toe]

Glimmer app:

Tic Tac Toe

Contact Manager

Glimmer code (from samples/elaborate/contact_manager.rb):

# ...
    shell {
      text "Contact Manager"
      composite {
        group {
          grid_layout(2, false) {
            margin_width 0
            margin_height 0
          }
          layout_data :fill, :center, true, false
          text 'Lookup Contacts'
          font height: 24

          label {
            layout_data :right, :center, false, false
            text "First &Name: "
            font height: 16
          }
          text {
            layout_data :fill, :center, true, false
            text bind(@contact_manager_presenter, :first_name)
            on_key_pressed {|key_event|
              @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
            }
          }

          label {
            layout_data :right, :center, false, false
            text "&Last Name: "
            font height: 16
          }
          text {
            layout_data :fill, :center, true, false
            text bind(@contact_manager_presenter, :last_name)
            on_key_pressed {|key_event|
              @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
            }
          }

          label {
            layout_data :right, :center, false, false
            text "&Email: "
            font height: 16
          }
          text {
            layout_data :fill, :center, true, false
            text bind(@contact_manager_presenter, :email)
            on_key_pressed {|key_event|
              @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
            }
          }

          composite {
            row_layout {
              margin_width 0
              margin_height 0
            }
            layout_data(:right, :center, false, false) {
              horizontal_span 2
            }

            button {
              text "&Find"
              on_widget_selected { @contact_manager_presenter.find }
              on_key_pressed {|key_event|
                @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
              }
            }

            button {
              text "&List All"
              on_widget_selected { @contact_manager_presenter.list }
              on_key_pressed {|key_event|
                @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
              }
            }
          }
        }

        table(:multi) { |table_proxy|
          layout_data {
            horizontal_alignment :fill
            vertical_alignment :fill
            grab_excess_horizontal_space true
            grab_excess_vertical_space true
            height_hint 200
          }
          table_column {
            text "First Name"
            width 80
          }
          table_column {
            text "Last Name"
            width 80
          }
          table_column {
            text "Email"
            width 200
          }
          items bind(@contact_manager_presenter, :results),
          column_properties(:first_name, :last_name, :email)
          on_mouse_up { |event|
            table_proxy.edit_table_item(event.table_item, event.column_index)
          }
        }
      }
    }.open
# ...

Run:

glimmer sample:run[contact_manager]

Glimmer App:

Contact Manager

Production Desktop Apps Built with Glimmer DSL for SWT

Are We There Yet LogoAre We There Yet? - Small Project Tracking App

Are We There Yet? App Screenshot

Math Bowling LogoMath Bowling - Elementary Level Math Game Featuring Bowling Rules

Math Bowling App Screenshot

Glimmer DSL for Tk (Ruby Desktop Development GUI Library)

Tcl/Tk has evolved into a practical desktop GUI toolkit due to gaining true native widgets on Mac, Windows, and Linux in Tk version 8.5.

Additionally, Ruby 3.0 Ractor (formerly known as Guilds) supports truly parallel multi-threading, making both MRI and Tk finally viable for support in Glimmer (Ruby Desktop Development GUI Library) as an alternative to JRuby on SWT.

The trade-off is that while SWT provides a plethora of high quality reusable widgets for the Enterprise (such as Nebula), Tk enables very fast app startup time via MRI Ruby.

Glimmer DSL for Tk aims to provide a DSL similar to the Glimmer DSL for SWT to enable more productive desktop development in Ruby with:

  • Declarative DSL syntax that visually maps to the GUI widget hierarchy
  • Convention over configuration via smart defaults and automation of low-level details
  • Requiring the least amount of syntax possible to build GUI
  • Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
  • Custom Widget support
  • Scaffolding for new custom widgets, apps, and gems
  • Native-Executable packaging on Mac, Windows, and Linux

Glimmer DSL for Tk Samples

Hello, World!

Glimmer code (from samples/hello/hello_world.rb):

include Glimmer

root {
  label {
    text 'Hello, World!'
  }
}.open

Run (with the glimmer-dsl-tk gem installed):

ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"

Glimmer app:

glimmer dsl tk screenshot sample hello world

Hello, Tab!

Glimmer code (from samples/hello/hello_tab.rb):

include Glimmer

root {      
  title 'Hello, Tab!'

  notebook {
    frame(text: 'English') {
      label {
        text 'Hello, World!'
      }
    }

    frame(text: 'French') {
      label {
        text 'Bonjour, Univers!'
      }
    }
  }
}.open

Run (with the glimmer-dsl-tk gem installed):

ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_tab.rb'"

Glimmer app:

glimmer dsl tk screenshot sample hello tab English glimmer dsl tk screenshot sample hello tab French

Hello, Combo!

Glimmer code (from samples/hello/hello_combo.rb):

# ... more code precedes
root {
  title 'Hello, Combo!'

  combobox { |proxy|
    state 'readonly'       
    text bind(person, :country)
  }

  button { |proxy|
    text "Reset Selection"
    command {
      person.reset_country
    }
  }
}.open
# ... more code follows

Run (with the glimmer-dsl-tk gem installed):

ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combo.rb'"

Glimmer app:

glimmer dsl tk screenshot sample hello combo glimmer dsl tk screenshot sample hello combo dropdown

Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)

Glimmer DSL for Opal is an experimental proof-of-concept web GUI adapter for Glimmer desktop apps (i.e. apps built with Glimmer DSL for SWT). It webifies them via Rails, allowing Ruby desktop apps to run on the web via Opal Ruby without changing a line of code. Apps may then be custom-styled for the web with standard CSS.

Glimmer DSL for Opal webifier successfully reuses the entire Glimmer core DSL engine in Opal Ruby inside a web browser, and as such inherits the full range of powerful Glimmer desktop data-binding capabilities for the web.

Glimmer DSL for Opal Samples

Hello, Computed!

Add the following require statement to app/assets/javascripts/application.rb

require 'samples/hello/hello_computed'

Or add the Glimmer code directly if you prefer to play around with it:

class HelloComputed
  class Contact
    attr_accessor :first_name, :last_name, :year_of_birth

    def initialize(attribute_map)
      @first_name = attribute_map[:first_name]
      @last_name = attribute_map[:last_name]
      @year_of_birth = attribute_map[:year_of_birth]
    end

    def name
      "#{last_name}, #{first_name}"
    end

    def age
      Time.now.year - year_of_birth.to_i
    rescue
      0
    end
  end
end

class HelloComputed
  include Glimmer

  def initialize
    @contact = Contact.new(
      first_name: 'Barry',
      last_name: 'McKibbin',
      year_of_birth: 1985
    )
  end

  def launch
    shell {
      text 'Hello, Computed!'
      composite {
        grid_layout {
          num_columns 2
          make_columns_equal_width true
          horizontal_spacing 20
          vertical_spacing 10
        }
        label {text 'First &Name: '}
        text {
          text bind(@contact, :first_name)
          layout_data {
            horizontal_alignment :fill
            grab_excess_horizontal_space true
          }
        }
        label {text '&Last Name: '}
        text {
          text bind(@contact, :last_name)
          layout_data {
            horizontal_alignment :fill
            grab_excess_horizontal_space true
          }
        }
        label {text '&Year of Birth: '}
        text {
          text bind(@contact, :year_of_birth)
          layout_data {
            horizontal_alignment :fill
            grab_excess_horizontal_space true
          }
        }
        label {text 'Name: '}
        label {
          text bind(@contact, :name, computed_by: [:first_name, :last_name])
          layout_data {
            horizontal_alignment :fill
            grab_excess_horizontal_space true
          }
        }
        label {text 'Age: '}
        label {
          text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
          layout_data {
            horizontal_alignment :fill
            grab_excess_horizontal_space true
          }
        }
      }
    }.open
  end
end

HelloComputed.new.launch

Glimmer app on the desktop (using glimmer-dsl-swt gem):

Glimmer DSL for SWT Hello Computed

Glimmer app on the web (using glimmer-dsl-opal gem):

Start the Rails server:

rails s

Visit http://localhost:3000

You should see "Hello, Computed!"

Glimmer DSL for Opal Hello Computed

Hello, List Single Selection!

Add the following require statement to app/assets/javascripts/application.rb

require 'samples/hello/hello_list_single_selection'

Or add the Glimmer code directly if you prefer to play around with it:

class Person 
  attr_accessor :country, :country_options

  def initialize
    self.country_options=["", "Canada", "US", "Mexico"]
    self.country = "Canada"
  end

  def reset_country
    self.country = "Canada"
  end
end

class HelloListSingleSelection
  include Glimmer
  def launch
    person = Person.new
    shell {
      composite {
        list {
          selection bind(person, :country)
        }
        button {
          text "Reset"
          on_widget_selected do
            person.reset_country
          end
        }
      }
    }.open
  end
end

HelloListSingleSelection.new.launch

Glimmer app on the desktop (using glimmer-dsl-swt gem):

Glimmer DSL for SWT Hello List Single Selection

Glimmer app on the web (using glimmer-dsl-opal gem):

Start the Rails server:

rails s

Visit http://localhost:3000

You should see "Hello, List Single Selection!"

Glimmer DSL for Opal Hello List Single Selection

Hello, List Multi Selection!

Add the following require statement to app/assets/javascripts/application.rb

require 'samples/hello/hello_list_multi_selection'

Or add the Glimmer code directly if you prefer to play around with it:

class Person
  attr_accessor :provinces, :provinces_options

  def initialize
    self.provinces_options=[
      "",
      "Quebec",
      "Ontario",
      "Manitoba",
      "Saskatchewan",
      "Alberta",
      "British Columbia",
      "Nova Skotia",
      "Newfoundland"
    ]
    self.provinces = ["Quebec", "Manitoba", "Alberta"]
  end

  def reset_provinces
    self.provinces = ["Quebec", "Manitoba", "Alberta"]
  end
end

class HelloListMultiSelection
  include Glimmer
  def launch
    person = Person.new
    shell {
      composite {
        list(:multi) {
          selection bind(person, :provinces)
        }
        button {
          text "Reset"
          on_widget_selected do
            person.reset_provinces
          end
        }
      }
    }.open
  end
end

HelloListMultiSelection.new.launch

Glimmer app on the desktop (using glimmer-dsl-swt gem):

Glimmer DSL for SWT Hello List Multi Selection

Glimmer app on the web (using glimmer-dsl-opal gem):

Start the Rails server:

rails s

Visit http://localhost:3000

You should see "Hello, List Multi Selection!"

Glimmer DSL for Opal Hello List Multi Selection

Glimmer DSL for XML (& HTML)

Glimmer DSL for XML provides Ruby syntax for building XML (eXtensible Markup Language) documents.

Within the context of desktop development, Glimmer DSL for XML is useful in providing XML data for the SWT Browser widget.

XML DSL

Simply start with html keyword and add HTML inside its block using Glimmer DSL syntax. Once done, you may call to_s, to_xml, or to_html to get the formatted HTML output.

Here are all the Glimmer XML DSL top-level keywords:

  • html
  • tag: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
  • name_space: enables namespacing html tags

Element properties are typically passed as a key/value hash (e.g. section(id: 'main', class: 'accordion')) . However, for properties like "selected" or "checked", you must leave value nil or otherwise pass in front of the hash (e.g. input(:checked, type: 'checkbox') )

Example (basic HTML):

@xml = html {
  head {
    meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
  }
  body {
    h1 { "Hello, World!" }
  }
}
puts @xml

Output:

<html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>

Glimmer DSL for CSS

Glimmer DSL for CSS provides Ruby syntax for building CSS (Cascading Style Sheets).

Within the context of Glimmer app development, Glimmer DSL for CSS is useful in providing CSS for the SWT Browser widget.

CSS DSL

Simply start with css keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax. Once done, you may call to_s or to_css to get the formatted CSS output.

css is the only top-level keyword in the Glimmer CSS DSL

Selectors may be specified by s keyword or HTML element keyword directly (e.g. body) Rule property values may be specified by pv keyword or underscored property name directly (e.g. font_size)

Example:

@css = css {
  body {
    font_size '1.1em'
    pv 'background', 'white'
  }

  s('body > h1') {
    background_color :red
    pv 'font-size', '2em'
  }
}
puts @css

Output:

body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}

Multi-DSL Support

Glimmer allows mixing DSLs, which comes in handy when doing things like using a desktop Browser widget with HTML and CSS.

Glimmer DSL syntax consists mainly of:

  • keywords (e.g. table for a table widget)
  • style/args (e.g. :multi as in table(:multi) for a multi-line selection table widget)
  • content (e.g. { table_column { text 'Name'} } as in table(:multi) { table_column { text 'name'} } for a multi-line selection table widget with a table column having header text property 'Name' as content)

DSLs are activated by specific keywords. For example, the html keyword activates the Glimmer DSL for XML. Glimmer automatically recognizes top-level keywords in each DSL and activates the DSL accordingly. Once done processing a nested DSL top-level keyword, Glimmer switches back to the prior DSL automatically.

Glimmer Supporting Libraries

Here is a list of notable 3rd party gems used by Glimmer and Glimmer DSLs:

  • jeweler: generates app gems during Glimmer Scaffolding
  • logging: provides extra logging capabilities not available in Ruby Logger such as multi-threaded buffered asynchronous logging (to avoid affecting app performance) and support for multiple appenders such as stdout, syslog, and log files (the last one is needed on Windows where syslog is not supported)
  • nested_inherited_jruby_include_package: makes included SWT/Java packages available to all classes/modules that mix in the Glimmer module without having to manually reimport
  • os: provides OS detection capabilities (e.g. OS.mac? or OS.windows?) to write cross-platform code inexpensively
  • puts_debuggerer: helps in troubleshooting when adding require 'pd' and using the pd command instead of puts or p (also #pd_inspect or #pdi instead of #inspect)
  • rake: used to implement and execute glimmer commands
  • rake-tui: Rake Text-based User Interface. Allows navigating rake tasks with arrow keys and filtering task list by typing to quickly find an run a rake task.
  • super_module: used to cleanly write the Glimmer::UI:CustomWidget and Glimmer::UI::CustomShell modules
  • text-table: renders textual data in a textual table for the command-line interface of Glimmer
  • warbler: converts a Glimmer app into a Java JAR file during packaging

Glimmer Process

Glimmer Process is the lightweight software development process used for building Glimmer libraries and Glimmer apps, which goes beyond Agile, rendering all Agile processes obsolete. Glimmer Process is simply made up of 7 guidelines to pick and choose as necessary until software development needs are satisfied.

Learn more by reading the GPG (Glimmer Process Guidelines)

Resources

Help

Issues

You may submit issues on GitHub.

Click here to submit an issue.

Chat

If you need live help, try to Join the chat at https://gitter.im/AndyObtiva/glimmer

Feature Suggestions

These features have been suggested. You might see them in a future version of Glimmer. You are welcome to contribute more feature suggestions.

glimmer-dsl-swt/TODO.md

Glimmer DSL Engine specific tasks are at:

TODO.md

Change Log

glimmer-dsl-swt/CHANGELOG.md

CHANGELOG.md

Contributing

Contributors Wanted!

If you would like to contribute to Glimmer, please study up on Glimmer and SWT, run all Glimmer samples, and build a small sample app (perhaps from this TODO list) to add to glimmer-dsl-swt Hello or Elaborate samples via a Pull Request. Once done, contact me on Chat.

You may apply for contributing to any of these Glimmer DSL gems whether you prefer to focus on the desktop or web:

CONTRIBUTING.md

Contributors

Click here to view contributor commits.

Hire Me

If your company would like to invest fulltime in further development of the Glimmer open-source project, hire me.

License

MIT

Copyright (c) 2007-2020 - Andy Maleh.

--

Glimmer logo was made by Freepik from www.flaticon.com