reactive-wx 0.2.0

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.
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