glimmer-dsl-opal 0.12.0 → 0.16.0

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +179 -1296
  4. data/VERSION +1 -1
  5. data/app/controllers/glimmer/application_controller.rb +4 -0
  6. data/app/controllers/glimmer/image_paths_controller.rb +46 -0
  7. data/app/views/glimmer/image_paths/index.html.erb +1 -0
  8. data/{lib/glimmer-dsl-opal/samples/hello/hello_computed/contact.rb → config/routes.rb} +2 -20
  9. data/lib/glimmer-dsl-opal.rb +9 -3
  10. data/lib/glimmer-dsl-opal/ext/file.rb +25 -0
  11. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +0 -2
  12. data/lib/glimmer-dsl-opal/samples/elaborate/weather.rb +157 -0
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +7 -7
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +16 -14
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +14 -9
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +24 -22
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +27 -9
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +15 -11
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +1 -1
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +18 -16
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +17 -12
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +29 -21
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_table/baseball_park.png +0 -0
  24. data/lib/glimmer/config.rb +11 -0
  25. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +2 -2
  26. data/lib/glimmer/engine.rb +21 -0
  27. data/lib/glimmer/swt/composite_proxy.rb +34 -0
  28. data/lib/glimmer/swt/label_proxy.rb +15 -2
  29. data/lib/glimmer/swt/shell_proxy.rb +43 -0
  30. data/lib/glimmer/swt/table_item_proxy.rb +3 -0
  31. data/lib/glimmer/ui/custom_widget.rb +11 -2
  32. data/lib/net/http.rb +15 -6
  33. metadata +9 -3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.12.0
1
+ 0.16.0
@@ -0,0 +1,4 @@
1
+ module Glimmer
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ require 'fileutils'
2
+
3
+ module Glimmer
4
+ class ImagePathsController < ApplicationController
5
+ def index
6
+ # TODO apply caching in the future to avoid recopying files on every request
7
+ Gem.loaded_specs.map(&:last).select {|s| s.name == 'glimmer-dsl-opal' || s.dependencies.detect {|dep| dep.name == 'glimmer-dsl-opal'} }
8
+ full_gem_specs = Gem.loaded_specs.map(&:last).select do |s|
9
+ s.name == 'glimmer-dsl-opal' ||
10
+ Glimmer::Config.gems_having_image_paths.to_a.include?(s.name) || # consider turning into a Glimmer::Config server-side option
11
+ s.dependencies.detect {|dep| dep.name == 'glimmer-dsl-swt'}
12
+ end
13
+ full_gem_paths = full_gem_specs.map {|gem_spec| gem_spec.full_gem_path}
14
+ full_gem_names = full_gem_paths.map {|path| File.basename(path)}
15
+ full_gem_image_path_collections = full_gem_paths.map do |gem_path|
16
+ Dir[File.join(gem_path, '**', '*')].to_a.select {|f| !!f.match(/(png|jpg|jpeg|gif)$/) }
17
+ end
18
+ download_gem_image_path_collections = full_gem_names.size.times.map do |n|
19
+ full_gem_name = full_gem_names[n]
20
+ full_gem_image_paths = full_gem_image_path_collections[n]
21
+ full_gem_image_paths.map do |image_path|
22
+ File.join(full_gem_name, image_path.split(full_gem_name).last)
23
+ end
24
+ end
25
+ download_gem_image_paths = download_gem_image_path_collections.flatten
26
+ download_gem_image_dir_names = download_gem_image_paths.map {|p| File.dirname(p)}.uniq
27
+ download_gem_image_dir_names.each do |image_dir_name|
28
+ FileUtils.mkdir_p(Rails.root.join('app', 'assets', 'images', image_dir_name))
29
+ end
30
+ full_gem_names.size.times.each do |n|
31
+ full_image_paths = full_gem_image_path_collections[n]
32
+ download_image_paths = download_gem_image_path_collections[n]
33
+ full_image_paths.each_with_index do |image_path, i|
34
+ download_image_path = download_image_paths[i]
35
+ image_dir_name = File.dirname(image_path)
36
+ FileUtils.cp_r(image_path, Rails.root.join('app', 'assets', 'images', download_image_path)) # TODO check first if files match and avoid copying if so to save time
37
+ end
38
+ end
39
+ download_gem_image_paths = download_gem_image_paths.map {|p| "/assets/#{p}"}
40
+
41
+ # TODO apply a security white list
42
+ render json: download_gem_image_paths
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1 @@
1
+ NADA
@@ -19,24 +19,6 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- class HelloComputed
23
- class Contact
24
- attr_accessor :first_name, :last_name, :year_of_birth
25
-
26
- def initialize(attribute_map)
27
- @first_name = attribute_map[:first_name]
28
- @last_name = attribute_map[:last_name]
29
- @year_of_birth = attribute_map[:year_of_birth]
30
- end
31
-
32
- def name
33
- "#{last_name}, #{first_name}"
34
- end
35
-
36
- def age
37
- Time.now.year - year_of_birth.to_i
38
- rescue
39
- 0
40
- end
41
- end
22
+ Glimmer::Engine.routes.draw do
23
+ resources :image_paths, only: [:index]
42
24
  end
@@ -37,6 +37,10 @@ if RUBY_ENGINE == 'opal'
37
37
  def include_package(package)
38
38
  # No Op (just a shim)
39
39
  end
40
+
41
+ def __dir__
42
+ '(dir)'
43
+ end
40
44
  end
41
45
 
42
46
  require 'opal-parser'
@@ -56,6 +60,7 @@ if RUBY_ENGINE == 'opal'
56
60
  # require 'glimmer-dsl-opal/vendor/jquery-ui/jquery-ui.theme.min.css'
57
61
  require 'opal-jquery'
58
62
  require 'opal/jquery/local_storage'
63
+ require 'promise'
59
64
 
60
65
  require 'facets/hash/symbolize_keys'
61
66
  require 'glimmer-dsl-opal/ext/class'
@@ -80,11 +85,12 @@ if RUBY_ENGINE == 'opal'
80
85
  require 'glimmer/config/opal_logger'
81
86
  require 'glimmer-dsl-xml'
82
87
  require 'glimmer-dsl-css'
88
+
83
89
  Element.alias_native :replace_with, :replaceWith
84
90
  Element.alias_native :select
85
91
  Element.alias_native :dialog
86
-
87
- Glimmer::Config.loop_max_count = 300 # TODO disable
92
+
93
+ Glimmer::Config.loop_max_count = 250 # TODO disable
88
94
 
89
95
  original_logger_level = Glimmer::Config.logger.level
90
96
  Glimmer::Config.logger = Glimmer::Config::OpalLogger.new(STDOUT)
@@ -95,7 +101,7 @@ if RUBY_ENGINE == 'opal'
95
101
  result ||= method == '<<'
96
102
  result ||= method == 'handle'
97
103
  end
98
-
99
104
  else
105
+ require_relative 'glimmer/config'
100
106
  require_relative 'glimmer/engine'
101
107
  end
@@ -19,11 +19,36 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'net/http'
23
+
22
24
  class File
23
25
  class << self
26
+ REGEXP_DIR_FILE = /\(dir\)|\(file\)/
27
+
28
+ attr_accessor :image_paths
29
+
24
30
  def read(*args, &block)
25
31
  # TODO implement via asset downloads in the future
26
32
  # No Op in Opal
27
33
  end
34
+
35
+ # Include special processing for images that matches them against a list of available image paths from the server
36
+ # to convert to web paths.
37
+ alias expand_path_without_glimmer expand_path
38
+ def expand_path(path, base=nil)
39
+ get_image_paths unless image_paths
40
+ path = expand_path_without_glimmer(path, base) if base
41
+ path_include_dir_or_file = !!path.match(REGEXP_DIR_FILE)
42
+ essential_path = path.split('(dir)').last.split('(file)').last.split('../').last.split('./').last
43
+ image_paths.detect do |image_path|
44
+ image_path.include?(essential_path)
45
+ end
46
+ end
47
+
48
+ def get_image_paths
49
+ image_paths_json = Net::HTTP.get(`window.location.origin`, "/glimmer/image_paths.json")
50
+ self.image_paths = JSON.parse(image_paths_json)
51
+ end
52
+
28
53
  end
29
54
  end
@@ -1,5 +1,3 @@
1
- require 'glimmer-dsl-swt'
2
-
3
1
  require_relative "contact_manager/contact_manager_presenter"
4
2
 
5
3
  class ContactManager
@@ -0,0 +1,157 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'net/http'
23
+ require 'json'
24
+ require 'facets/string/titlecase'
25
+
26
+ class Weather
27
+ include Glimmer::UI::CustomShell
28
+
29
+ DEFAULT_FONT_HEIGHT = 30
30
+ DEFAULT_FOREGROUND = :white
31
+ DEFAULT_BACKGROUND = rgb(135, 176, 235)
32
+
33
+ attr_accessor :city, :temp, :temp_min, :temp_max, :feels_like, :humidity
34
+
35
+ before_body {
36
+ @weather_mutex = Mutex.new
37
+ self.city = 'Montreal, QC, CA'
38
+ fetch_weather!
39
+ }
40
+
41
+ body {
42
+ shell(:no_resize) {
43
+ grid_layout
44
+
45
+ text 'Glimmer Weather'
46
+ minimum_size 400, 300
47
+ background DEFAULT_BACKGROUND
48
+
49
+ text {
50
+ layout_data(:center, :center, true, true)
51
+
52
+ text <=> [self, :city]
53
+
54
+ on_key_pressed {|event|
55
+ if event.keyCode == swt(:cr) # carriage return
56
+ Thread.new do
57
+ fetch_weather!
58
+ end
59
+ end
60
+ }
61
+ }
62
+
63
+ tab_folder {
64
+ layout_data(:center, :center, true, true)
65
+
66
+ ['℃', '℉'].each do |temp_unit|
67
+ tab_item {
68
+ grid_layout 2, false
69
+
70
+ text temp_unit
71
+ background DEFAULT_BACKGROUND
72
+
73
+ rectangle(0, 0, [:default, -2], [:default, -2], 15, 15) {
74
+ foreground DEFAULT_FOREGROUND
75
+ }
76
+
77
+ %w[temp temp_min temp_max feels_like].each do |field_name|
78
+ temp_field(field_name, temp_unit)
79
+ end
80
+
81
+ humidity_field
82
+ }
83
+ end
84
+ }
85
+ }
86
+ }
87
+
88
+ def temp_field(field_name, temp_unit)
89
+ name_label(field_name)
90
+ label {
91
+ layout_data(:fill, :center, true, false)
92
+ text <= [self, field_name, on_read: ->(t) { "#{kelvin_to_temp_unit(t, temp_unit).to_f.round}°" }]
93
+ font height: DEFAULT_FONT_HEIGHT
94
+ foreground DEFAULT_FOREGROUND
95
+ }
96
+ end
97
+
98
+ def humidity_field
99
+ name_label('humidity')
100
+ label {
101
+ layout_data(:fill, :center, true, false)
102
+ text <= [self, 'humidity', on_read: ->(h) { "#{h.to_f.round}%" }]
103
+ font height: DEFAULT_FONT_HEIGHT
104
+ foreground DEFAULT_FOREGROUND
105
+ }
106
+ end
107
+
108
+ def name_label(field_name)
109
+ label {
110
+ layout_data :fill, :center, false, false
111
+ text field_name.titlecase
112
+ font height: DEFAULT_FONT_HEIGHT
113
+ foreground DEFAULT_FOREGROUND
114
+ }
115
+ end
116
+
117
+ def fetch_weather!
118
+ @weather_mutex.synchronize do
119
+ self.weather_data = JSON.parse(Net::HTTP.get('api.openweathermap.org', "/data/2.5/weather?q=#{city}&appid=1d16d70a9aec3570b5cbd27e6b421330"))
120
+ end
121
+ rescue => e
122
+ Glimmer::Config.logger.error "Unable to fetch weather due to error: #{e.full_message}"
123
+ end
124
+
125
+ def weather_data=(data)
126
+ @weather_data = data
127
+ main_data = data['main']
128
+ # temps come back in Kelvin
129
+ self.temp = main_data['temp']
130
+ self.temp_min = main_data['temp_min']
131
+ self.temp_max = main_data['temp_max']
132
+ self.feels_like = main_data['feels_like']
133
+ self.humidity = main_data['humidity']
134
+ end
135
+
136
+ def kelvin_to_temp_unit(kelvin, temp_unit)
137
+ temp_unit == '℃' ? kelvin_to_celsius(kelvin) : kelvin_to_fahrenheit(kelvin)
138
+ end
139
+
140
+ def kelvin_to_celsius(kelvin)
141
+ return nil if kelvin.nil?
142
+ kelvin - 273.15
143
+ end
144
+
145
+ def celsius_to_fahrenheit(celsius)
146
+ return nil if celsius.nil?
147
+ (celsius * 9 / 5 ) + 32
148
+ end
149
+
150
+ def kelvin_to_fahrenheit(kelvin)
151
+ return nil if kelvin.nil?
152
+ celsius_to_fahrenheit(kelvin_to_celsius(kelvin))
153
+ end
154
+
155
+ end
156
+
157
+ Weather.launch
@@ -20,15 +20,15 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  class HelloButton
23
- include Glimmer
23
+ include Glimmer::UI::CustomShell
24
24
 
25
25
  attr_accessor :count
26
26
 
27
- def initialize
27
+ before_body {
28
28
  @count = 0
29
- end
29
+ }
30
30
 
31
- def launch
31
+ body {
32
32
  shell {
33
33
  text 'Hello, Button!'
34
34
 
@@ -39,8 +39,8 @@ class HelloButton
39
39
  self.count += 1
40
40
  }
41
41
  }
42
- }.open
43
- end
42
+ }
43
+ }
44
44
  end
45
45
 
46
- HelloButton.new.launch
46
+ HelloButton.launch
@@ -24,10 +24,10 @@ class HelloCheckbox
24
24
  attr_accessor :skiing, :snowboarding, :snowmobiling, :snowshoeing
25
25
 
26
26
  def initialize
27
- reset_activities
27
+ reset_activities!
28
28
  end
29
29
 
30
- def reset_activities
30
+ def reset_activities!
31
31
  self.skiing = false
32
32
  self.snowboarding = true
33
33
  self.snowmobiling = false
@@ -35,11 +35,13 @@ class HelloCheckbox
35
35
  end
36
36
  end
37
37
 
38
- include Glimmer
38
+ include Glimmer::UI::CustomShell
39
39
 
40
- def launch
41
- person = Person.new
42
-
40
+ before_body {
41
+ @person = Person.new
42
+ }
43
+
44
+ body {
43
45
  shell {
44
46
  text 'Hello, Checkbox!'
45
47
  row_layout :vertical
@@ -52,22 +54,22 @@ class HelloCheckbox
52
54
  composite {
53
55
  checkbox {
54
56
  text 'Skiing'
55
- selection <=> [person, :skiing]
57
+ selection <=> [@person, :skiing]
56
58
  }
57
59
 
58
60
  checkbox {
59
61
  text 'Snowboarding'
60
- selection <=> [person, :snowboarding]
62
+ selection <=> [@person, :snowboarding]
61
63
  }
62
64
 
63
65
  checkbox {
64
66
  text 'Snowmobiling'
65
- selection <=> [person, :snowmobiling]
67
+ selection <=> [@person, :snowmobiling]
66
68
  }
67
69
 
68
70
  checkbox {
69
71
  text 'Snowshoeing'
70
- selection <=> [person, :snowshoeing]
72
+ selection <=> [@person, :snowshoeing]
71
73
  }
72
74
  }
73
75
 
@@ -75,11 +77,11 @@ class HelloCheckbox
75
77
  text 'Reset Activities'
76
78
 
77
79
  on_widget_selected do
78
- person.reset_activities
80
+ @person.reset_activities!
79
81
  end
80
82
  }
81
- }.open
82
- end
83
+ }
84
+ }
83
85
  end
84
86
 
85
- HelloCheckbox.new.launch
87
+ HelloCheckbox.launch
@@ -19,6 +19,9 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ # This sample demonstrates the use of a `checkbox_group` (aka `check_group`) in Glimmer, which provides terser syntax
23
+ # than HelloCheckbox for representing multiple checkbox buttons (`button(:check)`) by relying on data-binding to
24
+ # automatically spawn the `checkbox` widgets (`button(:check)`) based on available options on the model.
22
25
  class HelloCheckboxGroup
23
26
  class Person
24
27
  attr_accessor :activities
@@ -36,11 +39,13 @@ class HelloCheckboxGroup
36
39
  end
37
40
  end
38
41
 
39
- include Glimmer
42
+ include Glimmer::UI::CustomShell
40
43
 
41
- def launch
42
- person = Person.new
43
-
44
+ before_body {
45
+ @person = Person.new
46
+ }
47
+
48
+ body {
44
49
  shell {
45
50
  text 'Hello, Checkbox Group!'
46
51
  row_layout :vertical
@@ -51,18 +56,18 @@ class HelloCheckboxGroup
51
56
  }
52
57
 
53
58
  checkbox_group {
54
- selection bind(person, :activities)
59
+ selection <=> [@person, :activities]
55
60
  }
56
61
 
57
62
  button {
58
63
  text 'Reset Activities'
59
64
 
60
65
  on_widget_selected do
61
- person.reset_activities
66
+ @person.reset_activities
62
67
  end
63
68
  }
64
- }.open
65
- end
69
+ }
70
+ }
66
71
  end
67
72
 
68
- HelloCheckboxGroup.new.launch
73
+ HelloCheckboxGroup.launch