glimmer-dsl-opal 0.12.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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