glimmer 0.9.2 → 0.10.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.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: