reactive-wx 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/LICENSE +20 -0
  2. data/Manifest +45 -0
  3. data/README +22 -0
  4. data/Rakefile +5 -0
  5. data/lib/reactive-wx.rb +19 -0
  6. data/lib/reactive-wx/accessors.rb +15 -0
  7. data/lib/reactive-wx/accessors/control_with_items.rb +22 -0
  8. data/lib/reactive-wx/accessors/date_picker_ctrl.rb +17 -0
  9. data/lib/reactive-wx/accessors/text_ctrl.rb +17 -0
  10. data/lib/reactive-wx/binder.rb +49 -0
  11. data/lib/reactive-wx/default_handler.rb +50 -0
  12. data/lib/reactive-wx/helpers.rb +10 -0
  13. data/lib/reactive-wx/helpers/asset_helper.rb +21 -0
  14. data/lib/reactive-wx/helpers/exception_helper.rb +82 -0
  15. data/lib/reactive-wx/helpers/forms.rb +40 -0
  16. data/lib/reactive-wx/request.rb +13 -0
  17. data/lib/reactive-wx/wx_ext.rb +42 -0
  18. data/lib/reactive-wx/wx_ext/arranger.rb +33 -0
  19. data/lib/reactive-wx/wx_ext/aui_toolbar.rb +22 -0
  20. data/lib/reactive-wx/wx_ext/event_binder.rb +11 -0
  21. data/lib/reactive-wx/wx_ext/form_grid_sizer.rb +54 -0
  22. data/lib/reactive-wx/wx_ext/message_dialog.rb +62 -0
  23. data/lib/reactive-wx/wx_ext/panel_dialog.rb +14 -0
  24. data/lib/reactive-wx/wx_ext/semi_modal.rb +233 -0
  25. data/lib/reactive-wx/wx_ext/toolbar.rb +29 -0
  26. data/lib/reactive-wx/wx_ext/window.rb +16 -0
  27. data/reactive_app_generators/wx/USAGE +6 -0
  28. data/reactive_app_generators/wx/templates/application_helper.wx.rb +8 -0
  29. data/reactive_app_generators/wx/templates/layout.wx.erb +2 -0
  30. data/reactive_app_generators/wx/templates/main_controller.rb +10 -0
  31. data/reactive_app_generators/wx/templates/main_helper.wx.rb +9 -0
  32. data/reactive_app_generators/wx/templates/run.wx.erb +22 -0
  33. data/reactive_app_generators/wx/templates/show.wx.erb +2 -0
  34. data/reactive_app_generators/wx/wx_generator.rb +54 -0
  35. data/reactive_generators/view/USAGE +11 -0
  36. data/reactive_generators/view/templates/create.wx.rb +0 -0
  37. data/reactive_generators/view/templates/delete.wx.rb +0 -0
  38. data/reactive_generators/view/templates/destroy.wx.rb +0 -0
  39. data/reactive_generators/view/templates/edit.wx.rb +0 -0
  40. data/reactive_generators/view/templates/index.wx.rb +0 -0
  41. data/reactive_generators/view/templates/layout.wx.rb +0 -0
  42. data/reactive_generators/view/templates/new.wx.rb +0 -0
  43. data/reactive_generators/view/templates/show.wx.rb +0 -0
  44. data/reactive_generators/view/templates/update.wx.rb +0 -0
  45. data/reactive_generators/view/view_generator.rb +25 -0
  46. metadata +134 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Pascal Hurni
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.
data/Manifest ADDED
@@ -0,0 +1,45 @@
1
+ LICENSE
2
+ Manifest
3
+ README
4
+ Rakefile
5
+ lib/reactive-wx.rb
6
+ lib/reactive-wx/accessors.rb
7
+ lib/reactive-wx/accessors/control_with_items.rb
8
+ lib/reactive-wx/accessors/date_picker_ctrl.rb
9
+ lib/reactive-wx/accessors/text_ctrl.rb
10
+ lib/reactive-wx/binder.rb
11
+ lib/reactive-wx/default_handler.rb
12
+ lib/reactive-wx/helpers.rb
13
+ lib/reactive-wx/helpers/asset_helper.rb
14
+ lib/reactive-wx/helpers/exception_helper.rb
15
+ lib/reactive-wx/helpers/forms.rb
16
+ lib/reactive-wx/request.rb
17
+ lib/reactive-wx/wx_ext.rb
18
+ lib/reactive-wx/wx_ext/arranger.rb
19
+ lib/reactive-wx/wx_ext/aui_toolbar.rb
20
+ lib/reactive-wx/wx_ext/event_binder.rb
21
+ lib/reactive-wx/wx_ext/form_grid_sizer.rb
22
+ lib/reactive-wx/wx_ext/message_dialog.rb
23
+ lib/reactive-wx/wx_ext/panel_dialog.rb
24
+ lib/reactive-wx/wx_ext/semi_modal.rb
25
+ lib/reactive-wx/wx_ext/toolbar.rb
26
+ lib/reactive-wx/wx_ext/window.rb
27
+ reactive_app_generators/wx/USAGE
28
+ reactive_app_generators/wx/templates/application_helper.wx.rb
29
+ reactive_app_generators/wx/templates/layout.wx.erb
30
+ reactive_app_generators/wx/templates/main_controller.rb
31
+ reactive_app_generators/wx/templates/main_helper.wx.rb
32
+ reactive_app_generators/wx/templates/run.wx.erb
33
+ reactive_app_generators/wx/templates/show.wx.erb
34
+ reactive_app_generators/wx/wx_generator.rb
35
+ reactive_generators/view/USAGE
36
+ reactive_generators/view/templates/create.wx.rb
37
+ reactive_generators/view/templates/delete.wx.rb
38
+ reactive_generators/view/templates/destroy.wx.rb
39
+ reactive_generators/view/templates/edit.wx.rb
40
+ reactive_generators/view/templates/index.wx.rb
41
+ reactive_generators/view/templates/layout.wx.rb
42
+ reactive_generators/view/templates/new.wx.rb
43
+ reactive_generators/view/templates/show.wx.rb
44
+ reactive_generators/view/templates/update.wx.rb
45
+ reactive_generators/view/view_generator.rb
data/README ADDED
@@ -0,0 +1,22 @@
1
+ == reactive-wx
2
+
3
+ This plugin is an output handler for wx code.
4
+ It therefore runs code targeted for the wxWidgets toolkit using its ruby port
5
+ named wxRuby.
6
+
7
+ It is required to run on the client-side of the Reactive application.
8
+
9
+ Version:: 0.2.0
10
+ Author:: Pascal Hurni
11
+ Email:: phi@ruby-reactive.org
12
+ Homepage:: http://www.ruby-reactive.org
13
+
14
+ == Description
15
+
16
+ Adds
17
+
18
+ == Requirements
19
+
20
+ * reactive-core >=0.2.0
21
+ * wxruby >=1.9.6
22
+ * wx_sugar >= 0.1.19
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+
3
+ unless Gem.source_index.find_name('reactive-dev').empty?
4
+ require 'reactive-dev/tasks/plugin'
5
+ end
@@ -0,0 +1,19 @@
1
+ Reactive::Initializer.init :wx do
2
+ require 'wx'
3
+ require 'wx_sugar'
4
+ require 'reactive-wx/wx_ext'
5
+
6
+ require 'reactive-wx/request'
7
+ require 'reactive-wx/binder'
8
+ require 'reactive-wx/default_handler'
9
+ require 'reactive-wx/accessors'
10
+ require 'reactive-wx/helpers'
11
+
12
+ Reactive::OutputHandler::Base.register_output_handler(:wx, Reactive::WxOutput::DefaultHandler)
13
+
14
+ # every event handler may do requests
15
+ Wx::EvtHandler.send(:include, Reactive::WxOutput::Binder)
16
+
17
+ # We want the ::Wx namespace to be directly available for our handlers
18
+ Reactive::OutputHandler::Base.helper(:wx, ::Wx)
19
+ end
@@ -0,0 +1,15 @@
1
+ require 'reactive-wx/accessors/text_ctrl'
2
+ require 'reactive-wx/accessors/control_with_items'
3
+ require 'reactive-wx/accessors/date_picker_ctrl'
4
+
5
+ module Reactive::WxOutput
6
+ module Accessors # :nodoc: all
7
+ end
8
+ end
9
+
10
+ # Introduces orthogonality to access control's data.
11
+ # The majority of controls expose a #get_value method, but some don't.
12
+ # These modules defines such a method to access the data.
13
+ Wx::ControlWithItems.send(:include, Reactive::WxOutput::Accessors::ControlWithItems)
14
+ Wx::TextCtrl.send(:include, Reactive::WxOutput::Accessors::TextCtrl)
15
+ Wx::DatePickerCtrl.send(:include, Reactive::WxOutput::Accessors::DatePickerCtrl) if defined? Wx::DatePickerCtrl
@@ -0,0 +1,22 @@
1
+ module Reactive
2
+ module WxOutput
3
+ module Accessors
4
+
5
+ # The data is identified by the related item_data (Wx) filled before
6
+ module ControlWithItems
7
+ include Enumerable
8
+
9
+ def set_data(id)
10
+ if index = find {|i| get_item_data(i) == id}
11
+ set_selection(index)
12
+ end
13
+ end
14
+
15
+ def get_data
16
+ get_item_data(get_selection)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Reactive
2
+ module WxOutput
3
+ module Accessors
4
+
5
+ module DatePickerCtrl
6
+ def set_data(value)
7
+ set_value(value)
8
+ end
9
+
10
+ def get_data
11
+ get_value
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Reactive
2
+ module WxOutput
3
+ module Accessors
4
+
5
+ module TextCtrl
6
+ def set_data(value)
7
+ set_value(value.to_s)
8
+ end
9
+
10
+ def get_data
11
+ get_value
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ require 'reactive-wx/helpers/forms'
2
+
3
+ module Reactive
4
+ module WxOutput
5
+
6
+ module Binder
7
+ include Helpers::FormsHelper
8
+
9
+ # Let Reactive do the request
10
+ # The hash argument may be of three forms:
11
+ # * Direct form: a record (for CRUD actions, the action is known by asking the record: CREATE => new_record?, UPDATE => changed?, DELETE => marked_for_destruction?, SHOW => none of them)
12
+ # You may override the action by passing an :action entry in the options. A special action named :save will auto-determine the :create or :update state.
13
+ # * Simple form: a params hash with at least entries for :controller and :action
14
+ # * Complex form: a hash with a :link entry
15
+ # TODO: Refactor this new_params creation process
16
+ def do_request(*args)
17
+ hash = args.extract_options!
18
+ record = args.first
19
+ if record.class.respond_to?(:meta) && record.class.meta.is_a?(MetaModel::Model)
20
+ options = hash
21
+ new_params = hash.merge(:model => record)
22
+ else
23
+ # params may be of two forms, a simple params hash or a complex hash with embedded hashes. To distinguish: if the hash contains a :link key it is a complex one.
24
+ params, options = hash.has_key?(:link) ? [hash[:link], hash] : [hash, {}]
25
+ new_params = params.dup
26
+
27
+ # handle forms params
28
+ new_params.update(forms_to_params(options[:forms]))
29
+
30
+ # handle late update of params
31
+ # return false unless handle_late_tag(options[:late], new_params, wx_event)
32
+
33
+ end
34
+ # set up the request
35
+ request = Request.new(new_params)
36
+ request.local_assigns = options[:locals]
37
+
38
+ # let the request run
39
+ Dispatcher::Base.dispatch(request)
40
+ end
41
+
42
+ # def handle_late_tag(tag, params, wx_event)
43
+ # return true if tag.nil?
44
+ # tag.call(params, wx_event)
45
+ # end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ module Reactive
2
+ module WxOutput
3
+ # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
4
+ # bunch of intimate details and uses it to report a very precise exception message.
5
+ class HandlerError < Error #:nodoc:
6
+ include Reactive::TemplateError
7
+ end
8
+
9
+ class DefaultHandler < Reactive::OutputHandler::Base
10
+ cattr_reader :handler_name # We are the default handler, so our name is nil!
11
+
12
+ include Binder
13
+
14
+ def default_treatment
15
+ :run
16
+ end
17
+
18
+ def run
19
+ copy_ivars(response.variables)
20
+ instance_eval("#{locals_code}\n#{response.body}", request_name, 0)
21
+ rescue Exception => e
22
+ if HandlerError === e
23
+ e.sub_template_of(request_name)
24
+ raise e
25
+ else
26
+ raise HandlerError.new(request_name, response.body, e)
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ def copy_ivars(variables)
33
+ variables.each {|name, value| instance_variable_set("@#{name}", value)}
34
+ end
35
+
36
+ def locals_code
37
+ @local_assigns = {}
38
+ code = "local_assigns = @local_assigns;"
39
+ if request.respond_to? :local_assigns
40
+ if request.local_assigns
41
+ @local_assigns = request.local_assigns
42
+ code <<request.local_assigns.keys.map { |key| "#{key} = @local_assigns[:#{key}];" }.join
43
+ end
44
+ end
45
+ code
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ # Load helper modules
2
+ helpers_dir = "#{File.dirname(__FILE__)}/helpers"
3
+ Dir.entries(helpers_dir).sort.each do |file|
4
+ next unless file =~ /^([a-z][a-z_]*_helper).rb$/
5
+ require File.join(helpers_dir, file)
6
+ helper_module_name = $1.camelize
7
+ if Reactive::WxOutput::Helpers.const_defined?(helper_module_name)
8
+ Reactive::OutputHandler::Base.helper(:wx, Reactive::WxOutput::Helpers.const_get(helper_module_name))
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Reactive::WxOutput
2
+ module Helpers
3
+ module AssetHelper
4
+
5
+ class AssetArtProvider < Wx::ArtProvider
6
+ include Reactive::WxOutput::Binder
7
+
8
+ def create_bitmap(id, client, size)
9
+ unless id =~ /^wx/
10
+ begin
11
+ Wx::Bitmap.new(do_request(:asset => id, :kind => :ui), Wx::BITMAP_TYPE_ANY)
12
+ rescue Reactive::Dispatcher::AssetNotFound => e
13
+ nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,82 @@
1
+ module Reactive::WxOutput
2
+ module Helpers
3
+ module ExceptionHelper
4
+
5
+ def show_exception_dialog(exception)
6
+ send("show_#{Reactive.configuration.environment}_exception_dialog", exception)
7
+ end
8
+
9
+ def show_production_exception_dialog(exception)
10
+ # TODO
11
+ show_development_exception_dialog(exception)
12
+ end
13
+
14
+ def show_test_exception_dialog(exception)
15
+ # Tests should trap all exception, so re-raise it if it is not handled!
16
+ raise exception
17
+ end
18
+
19
+ def show_development_exception_dialog(exception)
20
+ dialog = WxExtensions::PanelDialog.new(nil, :style => Wx::DEFAULT_DIALOG_STYLE | Wx::RESIZE_BORDER) do |dialog|
21
+ html_win = Wx::HtmlWindow.new(dialog)
22
+ html_win.set_page(a=htmlize_exception(exception))
23
+ html_win
24
+ end
25
+ dialog.show_modal
26
+ dialog.destroy
27
+ end
28
+
29
+ HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
30
+ def h(html)
31
+ sanitize_latin1(html).to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| HTML_ESCAPE[special] }
32
+ end
33
+
34
+ def sanitize_latin1(text)
35
+ text.gsub(/[\x80-\xFF]/, '_')
36
+ end
37
+
38
+ def htmlize_trace(exception)
39
+ "<pre>#{exception.clean_backtrace.map{|line| line.sub(/:in (.+)/, ":in <font color=\"red\">\\1</font>")}.join("\n")}</pre>"
40
+ end
41
+
42
+ def htmlize_request(exception)
43
+ end
44
+
45
+ def htmlize_exception(exception)
46
+ if exception.respond_to? :template_error
47
+ <<EOS
48
+ <h1>
49
+ #{h exception.original_exception.class.to_s } in
50
+ #{request.params[:controller] ? h(request.params[:controller].capitalize) : ''}##{h request.params[:action] }
51
+ </h1>
52
+
53
+ <p>
54
+ Showing <i>#{h exception.file_name }</i> where line <b>##{h exception.line_number }</b> raised:
55
+ <pre><code>#{h exception.message }</code></pre>
56
+ </p>
57
+
58
+ <p>Extracted source (around line <b>##{h exception.line_number }</b>):
59
+ <pre><code>#{h exception.source_extract }</code></pre></p>
60
+
61
+ <p>#{h exception.sub_template_message }</p>
62
+
63
+ #{htmlize_trace(exception.original_exception || exception)}
64
+ #{htmlize_request(exception)}
65
+ EOS
66
+ else
67
+ <<EOS
68
+ <h1>
69
+ #{h exception.class.to_s}
70
+ #{request.params[:controller] ? h('in ' + request.params[:controller].humanize + 'Controller' + (request.params[:action] ? '#' + request.params[:action] : '')) : ''}
71
+ </h1>
72
+ <pre>#{h exception.clean_message}</pre>
73
+
74
+ #{htmlize_trace(exception)}
75
+ #{htmlize_request(exception)}
76
+ EOS
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,40 @@
1
+ module Reactive::WxOutput
2
+
3
+ class InvalidForm < Reactive::OutputHandler::Error
4
+ end
5
+
6
+ module Helpers
7
+ module FormsHelper
8
+
9
+ def forms_to_params(forms)
10
+ forms = [forms] unless forms.is_a?(Array)
11
+ forms.compact.inject({}) do |params, form|
12
+ form_name, container = form_name_and_container(form)
13
+ params.update(form_name => form_to_param(container))
14
+ end
15
+ end
16
+
17
+ def form_to_param(container)
18
+ form_param = {}
19
+ container.get_children.each {|child| next unless child.get_name =~ /^[\da-z_]+$/ ;form_param[child.get_name] = child.get_data} #TODO: Handle sub-children recursively
20
+ form_param
21
+ end
22
+
23
+ def form_name_and_container(form)
24
+ case form
25
+ when ::Wx::Window
26
+ raise InvalidForm, "Window #{form} has no name!" unless name = form.get_name
27
+ [name, form]
28
+ when String
29
+ raise InvalidForm, "No window named #{form}" unless win = ::Wx::Window.find_window_by_name(form, self)
30
+ [form, win]
31
+ when Hash
32
+ form.to_a
33
+ else
34
+ raise InvalidForm, "Unhandled param, passed form: #{form.inspect}"
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end