glimmer-dsl-opal 0.13.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.13.0
1
+ 0.16.1
@@ -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,10 +85,11 @@ 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
-
92
+
87
93
  Glimmer::Config.loop_max_count = 250 # TODO disable
88
94
 
89
95
  original_logger_level = Glimmer::Config.logger.level
@@ -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
@@ -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
@@ -19,45 +19,47 @@
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 Person
23
- attr_accessor :country, :country_options
24
-
25
- def initialize
26
- self.country_options = ['', 'Canada', 'US', 'Mexico']
27
- reset_country
28
- end
29
-
30
- def reset_country
31
- self.country = 'Canada'
22
+ class HelloCombo
23
+ class Person
24
+ attr_accessor :country, :country_options
25
+
26
+ def initialize
27
+ self.country_options = ['', 'Canada', 'US', 'Mexico']
28
+ reset_country!
29
+ end
30
+
31
+ def reset_country!
32
+ self.country = 'Canada'
33
+ end
32
34
  end
33
- end
34
35
 
35
- class HelloCombo
36
- include Glimmer
36
+ include Glimmer::UI::CustomShell
37
37
 
38
- def launch
39
- person = Person.new
40
-
38
+ before_body {
39
+ @person = Person.new
40
+ }
41
+
42
+ body {
41
43
  shell {
42
44
  row_layout(:vertical) {
43
- pack false
45
+ fill true
44
46
  }
45
47
 
46
48
  text 'Hello, Combo!'
47
49
 
48
50
  combo(:read_only) {
49
- selection <=> [person, :country]
51
+ selection <=> [@person, :country] # also binds to country_options by convention
50
52
  }
51
53
 
52
54
  button {
53
55
  text 'Reset Selection'
54
56
 
55
57
  on_widget_selected do
56
- person.reset_country
58
+ @person.reset_country!
57
59
  end
58
60
  }
59
- }.open
60
- end
61
+ }
62
+ }
61
63
  end
62
64
 
63
- HelloCombo.new.launch
65
+ HelloCombo.launch
@@ -0,0 +1,69 @@
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
+ class HelloComposite
23
+ include Glimmer::UI::CustomShell
24
+
25
+ body {
26
+ shell {
27
+ # shell (which is a composite) has fill_layout(:horizontal) by default with no margins
28
+ # we override below
29
+ fill_layout(:vertical)
30
+ text 'Hello, Composite!'
31
+
32
+ composite { # composite simply contains widgets for visual organization via a layout
33
+ # it has grid_layout(1, false) as its default layout
34
+ label {
35
+ text "Field is above its text widget"
36
+ }
37
+ text {
38
+ layout_data :fill, :center, true, false # fill horizontally, align center vertically, grab remaining horizontal space, but not vertical
39
+ }
40
+ }
41
+
42
+ composite { # composite simply contains widgets for visual organization via a layout
43
+ grid_layout 2, true
44
+
45
+ label {
46
+ text "Field has equal width to its text widget's"
47
+ }
48
+ text {
49
+ layout_data :fill, :center, true, false # fill horizontally, align center vertically, grab remaining horizontal space, but not vertical
50
+ }
51
+ }
52
+
53
+ composite { # composite simply contains widgets for visual organization via a layout
54
+ grid_layout 2, false
55
+
56
+ label {
57
+ text "Field has inequal width"
58
+ }
59
+
60
+ text {
61
+ layout_data :fill, :center, true, false # fill horizontally, align center vertically, grab remaining horizontal space, but not vertical
62
+ }
63
+ }
64
+ }
65
+
66
+ }
67
+ end
68
+
69
+ HelloComposite.launch