glimmer 0.9.2 → 0.10.1

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.2
1
+ 0.10.1
@@ -5,6 +5,10 @@
5
5
  require 'logger'
6
6
  require 'set'
7
7
 
8
+ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
9
+
10
+ require 'glimmer/config'
11
+
8
12
  # Glimmer provides a JRuby Desktop UI DSL + Data-Binding functionality
9
13
  #
10
14
  # A desktop UI application class must include Glimmer to gain access to Glimmer DSL
@@ -13,27 +17,50 @@ require 'set'
13
17
  # Glimmer DSL dynamic keywords (e.g. label, combo, etc...) are available via method_missing
14
18
  module Glimmer
15
19
  #TODO make it configurable to include or not include perhaps reverting to using included
16
- REGEX_METHODS_EXCLUDED = /^(to_|\[)/
20
+
21
+ # TODO add loop detection support to avoid infinite loops (perhaps breaks after 3 repetitions and provides an option to allow it if intentional)
22
+ class << self
23
+ attr_accessor :loop_last_data
24
+
25
+ def loop_reset!(including_loop_last_data = false)
26
+ @loop_last_data = nil if including_loop_last_data
27
+ @loop = 1
28
+ end
29
+
30
+ def loop
31
+ @loop ||= loop_reset!
32
+ end
33
+
34
+ def loop_increment!
35
+ @loop = loop + 1
36
+ end
37
+ end
17
38
 
18
39
  def method_missing(method_symbol, *args, &block)
19
- # This if statement speeds up Glimmer in girb or whenever directly including on main object
20
- if method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
21
- raise InvalidKeywordError, "Glimmer excluded keyword: #{method_symbol}"
40
+ new_loop_data = [method_symbol, args, block]
41
+ if new_loop_data == Glimmer.loop_last_data
42
+ Glimmer.loop_increment!
43
+ raise "Glimmer looped #{Config.loop_max_count} times with keyword '#{new_loop_data[0]}'! Check code for errors." if Glimmer.loop == Config.loop_max_count
44
+ else
45
+ Glimmer.loop_reset!
22
46
  end
23
- Glimmer::Config.logger&.debug "Interpreting keyword: #{method_symbol}"
47
+ Glimmer.loop_last_data = new_loop_data
48
+ # This if statement speeds up Glimmer in girb or whenever directly including on main object
49
+ is_excluded = Config.excluded_keyword_checkers.reduce(false) {|result, checker| result || instance_exec(method_symbol, *args, &checker) }
50
+ raise ExcludedKeywordError, "Glimmer excluded keyword: #{method_symbol}" if is_excluded
51
+ Glimmer::Config.logger.info {"Interpreting keyword: #{method_symbol}"}
24
52
  Glimmer::DSL::Engine.interpret(method_symbol, *args, &block)
53
+ rescue ExcludedKeywordError => e
54
+ # TODO add a feature to show excluded keywords optionally for debugging purposes
55
+ super(method_symbol, *args, &block)
25
56
  rescue InvalidKeywordError => e
26
- if !method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
27
- Glimmer::Config.logger&.error e.message
28
- end
29
- Glimmer::Config.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
57
+ Glimmer::Config.logger.error {"Encountered an invalid keyword at this object: #{self}"}
58
+ Glimmer::Config.logger.error {e.full_message}
30
59
  super(method_symbol, *args, &block)
31
60
  end
32
61
  end
33
62
 
34
- $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
35
-
36
- require 'glimmer/config'
37
63
  require 'glimmer/error'
64
+ require 'glimmer/excluded_keyword_error'
38
65
  require 'glimmer/invalid_keyword_error'
39
66
  require 'glimmer/dsl/engine'
@@ -1,22 +1,48 @@
1
1
  module Glimmer
2
2
  module Config
3
3
  class << self
4
+ LOOP_MAX_COUNT_DEFAULT = 100
5
+ REGEX_METHODS_EXCLUDED = /^(to_|\[)/
6
+
7
+ attr_writer :loop_max_count
8
+
9
+ def excluded_keyword_checkers
10
+ @excluded_keyword_checkers ||= reset_excluded_keyword_checkers!
11
+ end
12
+
13
+ def excluded_keyword_checkers=(checkers)
14
+ @excluded_keyword_checkers = checkers
15
+ end
16
+
17
+ def reset_excluded_keyword_checkers!
18
+ @excluded_keyword_checkers = [
19
+ lambda { |method_symbol, *args| method_symbol.to_s.match(REGEX_METHODS_EXCLUDED) }
20
+ ]
21
+ end
22
+
23
+ def loop_max_count
24
+ @loop_max_count ||= LOOP_MAX_COUNT_DEFAULT
25
+ end
26
+
4
27
  # Returns Glimmer logger (standard Ruby logger)
5
28
  def logger
6
- # unless defined? @@logger
7
- # @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
8
- # end
9
- @@logger if defined? @@logger
29
+ reset_logger! unless defined? @@logger
30
+ @@logger
31
+ end
32
+
33
+ def logger=(custom_logger)
34
+ @@logger = custom_logger
10
35
  end
11
36
 
12
- def enable_logging
13
- @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
37
+ def reset_logger!
38
+ self.logger = Logger.new(STDOUT).tap do |logger|
39
+ logger.level = Logger::ERROR
40
+ end
14
41
  end
15
42
  end
16
43
  end
17
44
  end
18
45
 
19
46
  if ENV['GLIMMER_LOGGER_LEVEL']
20
- Glimmer::Config.enable_logging
21
47
  Glimmer::Config.logger.level = ENV['GLIMMER_LOGGER_LEVEL'].downcase
22
48
  end
@@ -38,7 +38,7 @@ module Glimmer
38
38
  end
39
39
 
40
40
  def notify_observers
41
- property_observer_list.each {|observer| observer.call}
41
+ property_observer_list.to_a.each(&:call)
42
42
  end
43
43
 
44
44
  def <<(element)
@@ -47,7 +47,9 @@ module Glimmer
47
47
  end
48
48
 
49
49
  def notify_observers(property_name)
50
- property_observer_list(property_name).each {|observer| observer.call(send(property_name))}
50
+ property_observer_list(property_name).to_a.each do |observer|
51
+ observer.call(send(property_name))
52
+ end
51
53
  end
52
54
  #TODO upon updating values, make sure dependent observers are cleared (not added as dependents here)
53
55
 
@@ -56,9 +58,9 @@ module Glimmer
56
58
  method(property_writer_name)
57
59
  ensure_array_object_observer(property_name, send(property_name))
58
60
  begin
59
- method("__original_#{property_writer_name}")
61
+ singleton_method("__original_#{property_writer_name}")
60
62
  rescue
61
- old_method = self.class.instance_method(property_writer_name)
63
+ old_method = self.class.instance_method(property_writer_name) rescue self.method(property_writer_name)
62
64
  define_singleton_method("__original_#{property_writer_name}", old_method)
63
65
  define_singleton_method(property_writer_name) do |value|
64
66
  old_value = self.send(property_name)
@@ -69,8 +71,8 @@ module Glimmer
69
71
  end
70
72
  end
71
73
  rescue => e
72
- # ignore writing if no property writer exists
73
- Glimmer::Config.logger&.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
74
+ #ignore writing if no property writer exists
75
+ Glimmer::Config.logger.debug {"No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"}
74
76
  end
75
77
 
76
78
  def unregister_dependent_observers(property_name, old_value)
@@ -75,6 +75,7 @@ module Glimmer
75
75
  alias observe register
76
76
 
77
77
  def unregister(observable, property = nil)
78
+ return unless observable.is_a?(Observable)
78
79
  # TODO optimize performance in the future via indexing and/or making a registration official object/class
79
80
  observable.remove_observer(*[self, property].compact)
80
81
  registration = registration_for(observable, property)
@@ -90,16 +91,12 @@ module Glimmer
90
91
  thedependents = dependents_for(registration).select do |thedependent|
91
92
  thedependent.observable == dependent_observable
92
93
  end
93
- thedependents.each do |thedependent|
94
- thedependent.unregister
95
- end
94
+ thedependents.each(&:unregister)
96
95
  end
97
96
 
98
97
  # cleans up all registrations in observables
99
98
  def unregister_all_observables
100
- registrations.each do |registration|
101
- registration.unregister
102
- end
99
+ registrations.each(&:unregister)
103
100
  end
104
101
  alias unobserve_all_observables unregister_all_observables
105
102
 
@@ -11,6 +11,8 @@ module Glimmer
11
11
  # When DSL engine interprets an expression, it attempts to handle
12
12
  # with ordered expression array specified via `.expressions=` method.
13
13
  class Engine
14
+ MESSAGE_NO_DSLS = "Glimmer has no DSLs configured. Add glimmer-dsl-swt gem or visit https://github.com/AndyObtiva/glimmer#multi-dsl-support for more details.\n"
15
+
14
16
  class << self
15
17
  def dsl=(dsl_name)
16
18
  dsl_name = dsl_name&.to_sym
@@ -69,6 +71,16 @@ module Glimmer
69
71
  # Static expressions indexed by keyword and dsl
70
72
  def static_expressions
71
73
  @static_expressions ||= {}
74
+ end
75
+
76
+ # Sets dynamic expression chains of responsibility. Useful for internal testing
77
+ def dynamic_expression_chains_of_responsibility=(chains)
78
+ @dynamic_expression_chains_of_responsibility = chains
79
+ end
80
+
81
+ # Sets static expressions. Useful for internal testing
82
+ def static_expressions=(expressions)
83
+ @static_expressions = expressions
72
84
  end
73
85
 
74
86
  # Sets an ordered array of DSL expressions to support
@@ -84,7 +96,7 @@ module Glimmer
84
96
  dynamic_expression_chains_of_responsibility[dsl] = expression_names.reverse.map do |expression_name|
85
97
  expression_class(dsl_namespace, expression_name).new
86
98
  end.reduce(nil) do |last_expresion_handler, expression|
87
- Glimmer::Config.logger&.debug "Adding dynamic expression: #{expression.class.name}"
99
+ Glimmer::Config.logger.info {"Adding dynamic expression: #{expression.class.name}"}
88
100
  expression_handler = ExpressionHandler.new(expression)
89
101
  expression_handler.next = last_expresion_handler if last_expresion_handler
90
102
  expression_handler
@@ -92,21 +104,22 @@ module Glimmer
92
104
  end
93
105
 
94
106
  def add_static_expression(static_expression)
95
- Glimmer::Config.logger&.debug "Adding static expression: #{static_expression.class.name}"
107
+ Glimmer::Config.logger.info {"Adding static expression: #{static_expression.class.name}"}
96
108
  keyword = static_expression.class.keyword
97
109
  static_expression_dsl = static_expression.class.dsl
98
110
  static_expressions[keyword] ||= {}
99
111
  static_expressions[keyword][static_expression_dsl] = static_expression
100
112
  Glimmer.send(:define_method, keyword) do |*args, &block|
101
- begin
102
- retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
113
+ if Glimmer::DSL::Engine.no_dsls?
114
+ puts Glimmer::DSL::Engine::MESSAGE_NO_DSLS
115
+ else
116
+ retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
103
117
  static_expression_dsl = (Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls).first if retrieved_static_expression.nil?
104
118
  interpretation = nil
105
119
  if retrieved_static_expression.nil? && Glimmer::DSL::Engine.dsl && (static_expression_dsl.nil? || !Glimmer::DSL::Engine.static_expressions[keyword][static_expression_dsl].is_a?(TopLevelExpression))
106
120
  begin
107
121
  interpretation = Glimmer::DSL::Engine.interpret(keyword, *args, &block)
108
122
  rescue => e
109
- Glimmer::DSL::Engine.reset
110
123
  raise e if static_expression_dsl.nil? || !Glimmer::DSL::Engine.static_expressions[keyword][static_expression_dsl].is_a?(TopLevelExpression)
111
124
  end
112
125
  end
@@ -119,18 +132,14 @@ module Glimmer
119
132
  if !static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block)
120
133
  raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args} under parent #{Glimmer::DSL::Engine.parent}"
121
134
  else
122
- Glimmer::Config.logger&.debug "#{static_expression.class.name} will handle expression keyword #{keyword}"
135
+ Glimmer::Config.logger.info {"#{static_expression.class.name} will handle expression keyword #{keyword}"}
123
136
  static_expression.interpret(Glimmer::DSL::Engine.parent, keyword, *args, &block).tap do |ui_object|
124
137
  Glimmer::DSL::Engine.add_content(ui_object, static_expression, &block) unless block.nil?
125
138
  Glimmer::DSL::Engine.dsl_stack.pop
126
139
  end
127
140
  end
128
141
  end
129
- rescue StandardError => e
130
- # Glimmer::DSL::Engine.dsl_stack.pop
131
- Glimmer::DSL::Engine.reset
132
- raise e
133
- end
142
+ end
134
143
  end
135
144
  end
136
145
 
@@ -145,7 +154,7 @@ module Glimmer
145
154
  # Interprets Glimmer dynamic DSL expression consisting of keyword, args, and block (e.g. shell(:no_resize) { ... })
146
155
  def interpret(keyword, *args, &block)
147
156
  if no_dsls?
148
- puts "Glimmer has no DSLs configured. Add glimmer-dsl-swt gem or visit https://github.com/AndyObtiva/glimmer#multi-dsl-support for more details.\n"
157
+ puts MESSAGE_NO_DSLS
149
158
  return
150
159
  end
151
160
  keyword = keyword.to_s
@@ -156,10 +165,6 @@ module Glimmer
156
165
  add_content(ui_object, expression, &block)
157
166
  dsl_stack.pop
158
167
  end
159
- rescue StandardError => e
160
- # dsl_stack.pop
161
- reset
162
- raise e
163
168
  end
164
169
 
165
170
  # Adds content block to parent UI object
@@ -171,9 +176,12 @@ module Glimmer
171
176
  if block_given? && expression.is_a?(ParentExpression)
172
177
  dsl_stack.push(expression.class.dsl)
173
178
  parent_stack.push(parent)
174
- expression.add_content(parent, &block)
175
- parent_stack.pop
176
- dsl_stack.pop
179
+ begin
180
+ expression.add_content(parent, &block)
181
+ ensure
182
+ parent_stack.pop
183
+ dsl_stack.pop
184
+ end
177
185
  end
178
186
  end
179
187
 
@@ -23,9 +23,9 @@ module Glimmer
23
23
  # Otherwise, it forwards to the next handler configured via `#next=` method
24
24
  # If there is no handler next, then it raises an error
25
25
  def handle(parent, keyword, *args, &block)
26
- Glimmer::Config.logger&.debug "Attempting to handle #{keyword} with #{@expression.class.name.split(":").last}"
26
+ Glimmer::Config.logger.info {"Attempting to handle #{keyword} with #{@expression.class.name.split(":").last}"}
27
27
  if @expression.can_interpret?(parent, keyword, *args, &block)
28
- Glimmer::Config.logger&.debug "#{@expression.class.name} will handle expression keyword #{keyword}"
28
+ Glimmer::Config.logger.info {"#{@expression.class.name} will handle expression keyword #{keyword}"}
29
29
  return @expression
30
30
  elsif @next_expression_handler
31
31
  return @next_expression_handler.handle(parent, keyword, *args, &block)
@@ -34,7 +34,6 @@ module Glimmer
34
34
  message = "Glimmer keyword #{keyword} with args #{args} cannot be handled"
35
35
  message += " inside parent #{parent}" if parent
36
36
  message += "! Check the validity of the code."
37
- # Glimmer::Config.logger&.error message
38
37
  raise InvalidKeywordError, message
39
38
  end
40
39
  end
@@ -0,0 +1,5 @@
1
+ module Glimmer
2
+ # Represents Glimmer errors that occur due to excluded keywords passing through method_missing
3
+ class ExcludedKeywordError < RuntimeError
4
+ end
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-17 00:00:00.000000000 Z
11
+ date: 2020-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -132,6 +132,48 @@ dependencies:
132
132
  - - "<"
133
133
  - !ruby/object:Gem::Version
134
134
  version: 7.0.0
135
+ - !ruby/object:Gem::Dependency
136
+ requirement: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - '='
139
+ - !ruby/object:Gem::Version
140
+ version: 0.8.23
141
+ name: coveralls
142
+ type: :development
143
+ prerelease: false
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - '='
147
+ - !ruby/object:Gem::Version
148
+ version: 0.8.23
149
+ - !ruby/object:Gem::Dependency
150
+ requirement: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - "~>"
153
+ - !ruby/object:Gem::Version
154
+ version: 0.16.1
155
+ name: simplecov
156
+ type: :development
157
+ prerelease: false
158
+ version_requirements: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: 0.16.1
163
+ - !ruby/object:Gem::Dependency
164
+ requirement: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
168
+ version: 0.7.0
169
+ name: simplecov-lcov
170
+ type: :development
171
+ prerelease: false
172
+ version_requirements: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: 0.7.0
135
177
  description: Ruby Desktop Development GUI Library (JRuby on SWT)
136
178
  email: andy.am@gmail.com
137
179
  executables: []
@@ -157,6 +199,7 @@ files:
157
199
  - lib/glimmer/dsl/static_expression.rb
158
200
  - lib/glimmer/dsl/top_level_expression.rb
159
201
  - lib/glimmer/error.rb
202
+ - lib/glimmer/excluded_keyword_error.rb
160
203
  - lib/glimmer/invalid_keyword_error.rb
161
204
  homepage: http://github.com/AndyObtiva/glimmer
162
205
  licenses: