apotomo 1.0.5 → 1.1.0.rc1

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 (83) hide show
  1. data/.gitignore +5 -0
  2. data/CHANGES.textile +35 -7
  3. data/Gemfile +0 -2
  4. data/README.rdoc +9 -9
  5. data/apotomo.gemspec +3 -3
  6. data/lib/apotomo.rb +4 -47
  7. data/lib/apotomo/event.rb +2 -0
  8. data/lib/apotomo/event_handler.rb +0 -3
  9. data/lib/apotomo/event_methods.rb +6 -2
  10. data/lib/apotomo/invoke_event_handler.rb +5 -3
  11. data/lib/apotomo/javascript_generator.rb +19 -16
  12. data/lib/apotomo/rails/controller_methods.rb +84 -131
  13. data/lib/apotomo/rails/view_helper.rb +15 -31
  14. data/lib/apotomo/railtie.rb +24 -0
  15. data/lib/apotomo/request_processor.rb +17 -48
  16. data/lib/apotomo/test_case.rb +5 -5
  17. data/lib/apotomo/tree_node.rb +52 -61
  18. data/lib/apotomo/version.rb +1 -1
  19. data/lib/apotomo/widget.rb +70 -146
  20. data/lib/apotomo/widget/javascript_methods.rb +39 -0
  21. data/lib/apotomo/widget_shortcuts.rb +14 -40
  22. data/lib/generators/apotomo/widget_generator.rb +8 -9
  23. data/lib/generators/erb/widget_generator.rb +17 -0
  24. data/lib/generators/haml/widget_generator.rb +20 -0
  25. data/lib/generators/{apotomo/templates → templates}/view.erb +1 -1
  26. data/lib/generators/templates/view.haml +4 -0
  27. data/lib/generators/{apotomo/templates → templates}/widget.rb +1 -1
  28. data/lib/generators/{apotomo/templates → templates}/widget_test.rb +1 -1
  29. data/lib/generators/test_unit/widget_generator.rb +14 -0
  30. data/test/rails/caching_test.rb +10 -17
  31. data/test/rails/controller_methods_test.rb +9 -81
  32. data/test/rails/rails_integration_test.rb +76 -60
  33. data/test/rails/view_helper_test.rb +17 -28
  34. data/test/rails/widget_generator_test.rb +19 -31
  35. data/test/support/test_case_methods.rb +6 -20
  36. data/test/test_helper.rb +15 -25
  37. data/test/unit/event_handler_test.rb +1 -0
  38. data/test/unit/event_methods_test.rb +20 -8
  39. data/test/unit/event_test.rb +5 -0
  40. data/test/unit/javascript_generator_test.rb +19 -19
  41. data/test/unit/render_test.rb +17 -112
  42. data/test/unit/request_processor_test.rb +73 -111
  43. data/test/unit/test_case_test.rb +13 -7
  44. data/test/unit/widget_shortcuts_test.rb +24 -53
  45. data/test/unit/widget_test.rb +76 -36
  46. data/test/widgets/mouse/eat.erb +1 -0
  47. data/test/{fixtures → widgets}/mouse/eating.html.erb +0 -0
  48. data/test/{fixtures → widgets}/mouse/educate.html.erb +0 -0
  49. data/test/{fixtures → widgets}/mouse/feed.html.erb +0 -0
  50. data/test/{fixtures → widgets}/mouse/make_me_squeak.html.erb +0 -0
  51. data/test/{fixtures → widgets}/mouse/posing.html.erb +0 -0
  52. data/test/widgets/mouse/snuggle.html.erb +1 -0
  53. metadata +32 -50
  54. data/lib/apotomo/container_widget.rb +0 -10
  55. data/lib/apotomo/persistence.rb +0 -112
  56. data/lib/apotomo/rails/view_methods.rb +0 -7
  57. data/lib/apotomo/stateful_widget.rb +0 -29
  58. data/lib/apotomo/transition.rb +0 -46
  59. data/lib/generators/apotomo/templates/view.haml +0 -4
  60. data/test/dummy/log/production.log +0 -0
  61. data/test/dummy/log/server.log +0 -0
  62. data/test/dummy/public/javascripts/application.js +0 -2
  63. data/test/dummy/public/javascripts/controls.js +0 -965
  64. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  65. data/test/dummy/public/javascripts/effects.js +0 -1123
  66. data/test/dummy/public/javascripts/prototype.js +0 -6001
  67. data/test/dummy/public/javascripts/rails.js +0 -175
  68. data/test/dummy/script/rails +0 -6
  69. data/test/dummy/tmp/app/cells/mouse_widget.rb +0 -11
  70. data/test/dummy/tmp/app/cells/mouse_widget/snuggle.html.erb +0 -7
  71. data/test/dummy/tmp/app/cells/mouse_widget/squeak.html.erb +0 -7
  72. data/test/dummy/tmp/test/widgets/mouse_widget_test.rb +0 -12
  73. data/test/fixtures/application_widget_tree.rb +0 -2
  74. data/test/fixtures/mouse/snuggle.html.erb +0 -1
  75. data/test/rails/view_methods_test.rb +0 -38
  76. data/test/unit/container_test.rb +0 -21
  77. data/test/unit/invoke_test.rb +0 -126
  78. data/test/unit/persistence_test.rb +0 -201
  79. data/test/unit/stateful_widget_test.rb +0 -58
  80. data/test/unit/test_addressing.rb +0 -110
  81. data/test/unit/test_jump_to_state.rb +0 -89
  82. data/test/unit/test_tab_panel.rb +0 -71
  83. data/test/unit/transition_test.rb +0 -34
@@ -1,7 +1,21 @@
1
1
  module Apotomo
2
2
  module Rails
3
+ # == #url_for_event
4
+ #
5
+ # = url_for_event(:paginate, :page => 2)
6
+ # #=> http://apotomo.de/mouse/process_event_request?type=paginate&source=mouse&page=2
7
+ #
8
+ # == #widget_id
9
+ #
10
+ # = widget_id
11
+ # #=> :mouse
12
+ #
13
+ # == #children
14
+ #
15
+ # - children.each do |kid|
16
+ # = render_widget kid
3
17
  module ViewHelper
4
- # needs :@controller
18
+ delegate :children, :url_for_event, :widget_id, :to => :controller
5
19
 
6
20
  # Returns the app JavaScript generator.
7
21
  def js_generator
@@ -20,31 +34,6 @@ module Apotomo
20
34
  concat('<iframe id="apotomo_iframe" name="apotomo_iframe" style="display: none;"></iframe>'.html_safe) << form_tag(url_for_event(type, options), html_options, &block)
21
35
  end
22
36
 
23
- # Returns the url to trigger a +type+ event from the currently rendered widget.
24
- # The source can be changed with +:source+. Additional +options+ will be appended to the query string.
25
- #
26
- # Note that this method will use the framework's internal routing if available (e.g. #url_for in Rails).
27
- #
28
- # Example:
29
- # url_for_event(:paginate, :page => 2)
30
- # #=> http://apotomo.de/mouse/process_event_request?type=paginate&source=mouse&page=2
31
- def url_for_event(type, options={})
32
- controller.url_for_event(type, options)
33
- end
34
-
35
- ### TODO: test me.
36
- ### DISCUSS: rename to rendered_children ?
37
- def content
38
- @rendered_children.collect{|e| e.last}.join("\n").html_safe
39
- end
40
-
41
- # needs: suppress_javascript
42
- def widget_javascript(*args, &block)
43
- return if @suppress_js ### FIXME: implement with ActiveHelper and :locals.
44
-
45
- javascript_tag(*args, &block)
46
- end
47
-
48
37
  # Wraps your content in a +div+ and sets the id. Feel free to pass additional html options.
49
38
  #
50
39
  # Example:
@@ -61,11 +50,6 @@ module Apotomo
61
50
  options.reverse_merge!(:id => widget_id)
62
51
  content_tag(:div, options, &block)
63
52
  end
64
-
65
- # Returns the widget id you passed in a has_widgets block.
66
- def widget_id
67
- controller.name
68
- end
69
53
  end
70
54
  end
71
55
  end
@@ -0,0 +1,24 @@
1
+ require "rails/railtie"
2
+
3
+ module Apotomo
4
+ class Railtie < ::Rails::Railtie
5
+ rake_tasks do
6
+ load "apotomo/apotomo.rake"
7
+ end
8
+
9
+ # As we are a Railtie only, the routes won't be loaded automatically. Beside that, we want our
10
+ # route to be the very first (otherwise #resources might supersede it).
11
+ initializer 'apotomo.prepend_routes', :after => :add_routing_paths do |app|
12
+ app.routes_reloader.paths.unshift(File.dirname(__FILE__) + "/../../config/routes.rb")
13
+ end
14
+
15
+ # Include a lazy loader via has_widgets.
16
+ initializer 'apotomo.add_has_widgets' do |app|
17
+ ActionController::Base.extend Apotomo::Rails::ControllerMethodsLoader
18
+ end
19
+
20
+ initializer 'apotomo.setup_view_paths', :after => 'cells.setup_view_paths' do |app|
21
+ Apotomo::Widget.setup_view_paths!
22
+ end
23
+ end
24
+ end
@@ -1,70 +1,39 @@
1
1
  module Apotomo
2
2
  class RequestProcessor
3
- attr_reader :session, :root
3
+ include Hooks
4
4
 
5
- def initialize(controller, session, options={}, has_widgets_blocks=[])
6
- @session = session
7
- @widgets_flushed = false
8
-
5
+ define_hook :after_initialize
6
+ define_hook :after_fire
7
+
8
+ attr_reader :root
9
+
10
+
11
+ def initialize(controller, options={}, has_widgets_blocks=[])
9
12
  @root = Widget.new(controller, 'root', :display)
10
13
 
11
14
  attach_stateless_blocks_for(has_widgets_blocks, @root, controller)
12
15
 
13
- if options[:flush_widgets].blank? and ::Apotomo::StatefulWidget.frozen_widget_in?(session)
14
- @root = ::Apotomo::StatefulWidget.thaw_for(controller, session, @root)
15
- else
16
- #@root = flushed_root
17
-
18
- flushed_root ### FIXME: set internal mode to flushed
19
- end
16
+ run_hook :after_initialize, self
20
17
  end
21
18
 
22
19
  def attach_stateless_blocks_for(blocks, root, controller)
23
20
  blocks.each { |blk| controller.instance_exec(root, &blk) }
24
21
  end
25
22
 
26
- def flushed_root
27
- StatefulWidget.flush_storage(session)
28
- @widgets_flushed = true
29
- #widget('apotomo/widget', 'root')
30
- end
31
-
32
- def widgets_flushed?; @widgets_flushed; end
33
-
34
- # Fires the request event in the widget tree and collects the rendered page updates.
23
+ # Called when the browser wants an url_for_event address. This fires the request event in
24
+ # the widget tree and collects the rendered page updates.
35
25
  def process_for(request_params)
36
26
  source = self.root.find_widget(request_params[:source]) or raise "Source #{request_params[:source].inspect} non-existent."
37
27
 
38
- source.fire(request_params[:type].to_sym)
28
+ source.fire(request_params[:type].to_sym, request_params) # set data to params for now.
29
+
30
+ run_hook :after_fire, self
39
31
  source.root.page_updates ### DISCUSS: that's another dependency.
40
32
  end
41
33
 
42
- ### FIXME: remove me!
43
- def render_page_updates(page_updates)
44
- page_updates.join("\n")
45
- end
46
-
47
- # Serializes the current widget tree to the storage that was passed in the constructor.
48
- # Call this at the end of a request.
49
- def freeze!
50
- Apotomo::StatefulWidget.freeze_for(@session, root)
51
- end
52
-
53
- # Renders the widget named <tt>widget_id</tt>, passing optional <tt>opts</tt> and a block to it.
54
- # Use this in your #render_widget wrapper.
55
- def render_widget_for(widget_id, opts, &block)
56
- if widget_id.kind_of?(::Apotomo::Widget)
57
- widget = widget_id
58
- else
59
- widget = root.find_widget(widget_id)
60
- raise "Couldn't render non-existent widget `#{widget_id}`" unless widget
61
- end
62
-
63
-
64
- ### TODO: pass options in invoke.
65
- widget.opts = opts unless opts.empty?
66
-
67
- widget.invoke(&block)
34
+ # Renders the widget named +widget_id+. Any additional args is passed through to Widget#invoke.
35
+ def render_widget_for(*args)
36
+ root.render_widget(*args)
68
37
  end
69
38
 
70
39
  # Computes the address hash for a +:source+ widget and an event +:type+.
@@ -42,7 +42,7 @@ module Apotomo
42
42
  def setup
43
43
  super
44
44
  @controller.instance_eval do
45
- def controller_name
45
+ def controller_path
46
46
  'barn'
47
47
  end
48
48
  end
@@ -53,7 +53,7 @@ module Apotomo
53
53
  # Returns the widget tree from TestCase.has_widgets.
54
54
  def root
55
55
  blk = self.class.has_widgets_blocks or raise "Please setup a widget tree using TestCase.has_widgets"
56
- @root ||= widget("apotomo/widget", "root").tap do |root|
56
+ @root ||= Apotomo::Widget.new(parent_controller, "root").tap do |root|
57
57
  self.instance_exec(root, &blk)
58
58
  end
59
59
  end
@@ -63,8 +63,8 @@ module Apotomo
63
63
  end
64
64
 
65
65
  # Renders the widget +name+.
66
- def render_widget(name, options={})
67
- @last_invoke = root.find_widget(name).tap { |w| w.opts = options }.invoke # DISCUSS: use ControllerMethods?
66
+ def render_widget(*args)
67
+ @last_invoke = root.render_widget(*args)
68
68
  end
69
69
 
70
70
  # Triggers an event of +type+. You have to pass <tt>:source</tt> as options.
@@ -74,7 +74,7 @@ module Apotomo
74
74
  # trigger :submit, :source => "post-comments"
75
75
  def trigger(type, options)
76
76
  source = root.find_widget(options.delete(:source))
77
- source.instance_variable_set :@params, options # TODO: this is just a try-out (what about children?).
77
+ source.options.merge!(options) # TODO: this is just a try-out (what about children?).
78
78
  source.fire(type)
79
79
  root.page_updates # DISCUSS: use ControllerMethods?
80
80
  end
@@ -4,27 +4,24 @@
4
4
  module TreeNode
5
5
  include Enumerable
6
6
 
7
- attr_reader :content, :name, :parent
8
- attr_writer :content, :parent
7
+ attr_reader :name, :childrenHash
8
+ attr_accessor :parent
9
9
 
10
10
  def self.included(base)
11
11
  base.after_initialize :initialize_tree_node
12
12
  end
13
13
 
14
14
  def initialize_tree_node(*)
15
- self.setAsRoot!
15
+ root!
16
16
 
17
- @childrenHash = Hash.new
17
+ @childrenHash = {}
18
18
  @children = []
19
19
  end
20
20
 
21
21
  # Print the string representation of this node.
22
22
  def to_s
23
- s = size()
24
- "Node ID: #{@name} Content: #{@content} Parent: " +
25
- (root?() ? "ROOT" : "#{@parent.name}") +
26
- " Children: #{@children.length}" +
27
- " Total Nodes: #{s}"
23
+ "Node ID: #{widget_id} Parent: " + (root? ? "ROOT" : "#{parent.name}") +
24
+ " Children: #{children.length}" + " Total Nodes: #{size}"
28
25
  end
29
26
 
30
27
  # Convenience synonym for Tree#add method.
@@ -32,7 +29,7 @@ module TreeNode
32
29
  # children hierarchies in the tree.
33
30
  # E.g. root << child << grand_child
34
31
  def <<(child)
35
- add(child)
32
+ add(child)
36
33
  end
37
34
 
38
35
  # Adds the specified child node to the receiver node.
@@ -40,13 +37,13 @@ module TreeNode
40
37
  # The child is added as the last child in the current
41
38
  # list of children for the receiver node.
42
39
  def add(child)
43
- raise "Child already added" if @childrenHash.has_key?(child.name)
40
+ raise "Child already added" if @childrenHash.has_key?(child.name)
44
41
 
45
- @childrenHash[child.name] = child
46
- @children << child
47
- child.parent = self
48
-
49
- child.run_widget_hook(:after_add, child, self)
42
+ @childrenHash[child.name] = child
43
+ @children << child
44
+ child.parent = self
45
+
46
+ child.run_widget_hook(:after_add, child, self)
50
47
  child
51
48
  end
52
49
 
@@ -55,59 +52,55 @@ module TreeNode
55
52
  # if an alternate reference exists.
56
53
  # Returns the child node.
57
54
  def remove!(child)
58
- @childrenHash.delete(child.name)
59
- @children.delete(child)
60
- child.setAsRoot! unless child == nil
61
- return child
55
+ @childrenHash.delete(child.name)
56
+ @children.delete(child)
57
+ child.root! unless child == nil
58
+ child
62
59
  end
63
60
 
64
61
  # Removes this node from its parent. If this is the root node,
65
62
  # then does nothing.
66
- def removeFromParent!
67
- @parent.remove!(self) unless root?
63
+ def remove_from_parent!
64
+ @parent.remove!(self) unless root?
68
65
  end
69
66
 
70
67
  # Removes all children from the receiver node.
71
68
  def remove_all!
72
- for child in @children
73
- child.setAsRoot!
74
- end
75
- @childrenHash.clear
76
- @children.clear
77
- self
69
+ for child in @children
70
+ child.root!
71
+ end
72
+ @childrenHash.clear
73
+ @children.clear
74
+ self
78
75
  end
79
76
 
80
77
 
81
78
  # Private method which sets this node as a root node.
82
- def setAsRoot!
83
- @parent = nil
84
- end
85
-
86
79
  def root!
87
- setAsRoot!
80
+ @parent = nil
88
81
  end
89
-
82
+
90
83
  # Indicates whether this node is a root node. Note that
91
84
  # orphaned children will also be reported as root nodes.
92
85
  def root?
93
- @parent == nil
86
+ @parent == nil
94
87
  end
95
88
 
96
89
  # Returns an array of all the immediate children.
97
90
  # If a block is given, yields each child node to the block.
98
91
  def children
99
- if block_given?
100
- @children.each {|child| yield child}
101
- else
102
- @children
103
- end
92
+ if block_given?
93
+ @children.each { |child| yield child }
94
+ else
95
+ @children
96
+ end
104
97
  end
105
98
 
106
99
  # Returns every node (including the receiver node) from the
107
100
  # tree to the specified block.
108
- def each &block
109
- yield self
110
- children { |child| child.each(&block) }
101
+ def each(&block)
102
+ yield self
103
+ children { |child| child.each(&block) }
111
104
  end
112
105
 
113
106
  # Returns the requested node from the set of immediate
@@ -117,44 +110,42 @@ module TreeNode
117
110
  # children is accessed (see Tree#children).
118
111
  # If the key is not _numeric_, then it is assumed to be
119
112
  # the *name* of the child node to be returned.
120
- def [](key)
121
- raise "Key needs to be provided" if key == nil
122
-
123
- if key.kind_of?(Integer)
124
- @children[key]
125
- else
126
- @childrenHash[key]
127
- end
113
+ def [](name)
114
+ if name.kind_of?(Integer)
115
+ children[name]
116
+ else
117
+ childrenHash[name]
118
+ end
128
119
  end
129
120
 
130
121
  # Returns the total number of nodes in this tree, rooted
131
122
  # at the receiver node.
132
123
  def size
133
- @children.inject(1) {|sum, node| sum + node.size}
124
+ children.inject(1) {|sum, node| sum + node.size}
134
125
  end
135
126
 
136
127
  # Pretty prints the tree starting with the receiver node.
137
128
  def printTree(tab = 0)
138
- puts((' ' * tab) + self.to_s)
139
- children {|child| child.printTree(tab + 4)}
129
+ puts((' ' * tab) + self.to_s)
130
+ children {|child| child.printTree(tab + 4)}
140
131
  end
141
132
 
142
133
  # Returns the root for this node.
143
134
  def root
144
- root = self
145
- root = root.parent while !root.root?
146
- root
135
+ root = self
136
+ root = root.parent while !root.root?
137
+ root
147
138
  end
148
139
 
149
140
  # Provides a comparision operation for the nodes. Comparision
150
141
  # is based on the natural character-set ordering for the
151
142
  # node names.
152
143
  def <=>(other)
153
- return +1 if other == nil
154
- self.name <=> other.name
144
+ return +1 if other == nil
145
+ self.name <=> other.name
155
146
  end
156
147
 
157
- protected :parent=, :setAsRoot!
148
+ protected :parent=, :root!
158
149
 
159
150
  def find_by_path(selector)
160
151
  next_node = self
@@ -165,7 +156,7 @@ module TreeNode
165
156
  }
166
157
  end
167
158
 
168
- return next_node
159
+ next_node
169
160
  end
170
161
 
171
162
 
@@ -1,3 +1,3 @@
1
1
  module Apotomo
2
- VERSION = '1.0.5'
2
+ VERSION = '1.1.0.rc1'
3
3
  end
@@ -5,12 +5,42 @@ require 'hooks'
5
5
  require 'apotomo/tree_node'
6
6
  require 'apotomo/event'
7
7
  require 'apotomo/event_methods'
8
- require 'apotomo/transition'
9
8
  require 'apotomo/widget_shortcuts'
10
9
  require 'apotomo/rails/view_helper'
10
+ require 'apotomo/rails/controller_methods' # FIXME.
11
+
12
+ require 'apotomo/widget/javascript_methods'
13
+
11
14
 
12
15
  module Apotomo
16
+ # == Accessing Parameters
17
+ #
18
+ # Apotomo tries to prevent you from having to access the global #params hash. We have the following
19
+ # concepts to retrieve input data.
20
+ #
21
+ # 1. Configuration values are available both in render and triggered states. Pass those in #widget
22
+ # when creating the widget tree. Use #options for reading.
23
+ #
24
+ # has_widgets do |root|
25
+ # root << widget(:mouse_widget, 'mum', :favorites => ["Gouda", "Chedar"])
26
+ #
27
+ # and read in your widget state
28
+ #
29
+ # def display
30
+ # @cheese = options[:favorites].first
31
+ #
32
+ # 2. Request data from forms etc. is available through <tt>event.data</tt> in the triggered states.
33
+ # Use the <tt>#[]</tt> shortcut to access values directly.
34
+ #
35
+ # def update(evt)
36
+ # @cheese = Cheese.find evt[:cheese_id]
13
37
  class Widget < Cell::Base
38
+
39
+ DEFAULT_VIEW_PATHS = [
40
+ File.join('app', 'widgets'),
41
+ File.join('app', 'widgets', 'layouts')
42
+ ]
43
+
14
44
  include Hooks
15
45
 
16
46
  # Use this for setup code you're calling in every state. Almost like a +before_filter+ except that it's
@@ -23,27 +53,31 @@ module Apotomo
23
53
  #
24
54
  # # we need @cheese in every state:
25
55
  # def setup_cheese(*)
26
- # @cheese = Cheese.find @opts[:cheese_id]
56
+ # @cheese = Cheese.find options[:cheese_id]
27
57
  define_hook :after_initialize
28
58
  define_hook :has_widgets
29
59
  define_hook :after_add
30
60
 
31
- attr_accessor :opts
32
- attr_writer :visible
33
-
61
+ attr_writer :visible
62
+
34
63
  include TreeNode
35
64
 
36
65
  include Onfire
37
- include EventMethods
38
66
 
39
- include Transition
67
+ include EventMethods
40
68
  include WidgetShortcuts
69
+ include JavascriptMethods
41
70
 
42
71
  helper Apotomo::Rails::ViewHelper
72
+ helper Apotomo::Rails::ActionViewMethods
43
73
 
44
74
  abstract!
75
+
45
76
  undef :display # We don't want #display to be listed in #internal_methods.
46
77
 
78
+ alias_method :widget_id, :name
79
+
80
+
47
81
  # Runs callbacks for +name+ hook in instance context.
48
82
  def run_widget_hook(name, *args)
49
83
  self.class.callbacks_for_hook(name).each { |blk| instance_exec(*args, &blk) }
@@ -55,180 +89,55 @@ module Apotomo
55
89
  after_initialize :add_has_widgets_blocks
56
90
 
57
91
 
58
- # Constructor which needs a unique id for the widget and one or multiple start states.
59
- def initialize(parent_controller, id, start_state, opts={})
60
- super(parent_controller, opts) # do that as long as cells do need a parent_controller.
92
+ def initialize(parent_controller, id, options={})
93
+ super(parent_controller, options) # do that as long as cells do need a parent_controller.
61
94
 
62
95
  @name = id
63
- @start_state = start_state
64
-
65
96
  @visible = true
66
- @cell = self ### DISCUSS: needed?
67
-
68
- @params = parent_controller.params.dup.merge(opts)
69
97
 
70
98
  run_hook :after_initialize, self
71
99
  end
72
100
 
73
- def last_state
74
- action_name
75
- end
76
-
77
101
  def visible?
78
102
  @visible
79
103
  end
80
104
 
81
- # Returns the rendered content for the widget by running the state method for <tt>state</tt>.
82
- # This might lead us to some other state since the state method could call #jump_to_state.
83
- def invoke(state=nil, event=nil)
84
- logger.debug "\ninvoke on #{name} with #{state.inspect}"
85
-
86
- if state.blank?
87
- state = next_state_for(last_state) || @start_state
88
- end
89
-
90
- logger.debug "#{name}: transition: #{last_state} to #{state}"
91
- logger.debug " ...#{state}"
92
-
93
- #render_state(state)
94
-
95
- return render_state(state, event) if method(state).arity == 1
96
-
97
- opts[:event] = event
105
+ # Invokes +state+ and hopefully returns the rendered content.
106
+ def invoke(state, *args)
107
+ return render_state(state, *args) if state_accepts_args?(state)
98
108
  render_state(state)
99
109
  end
100
110
 
101
-
102
111
  # Render the view for the current state. Usually called at the end of a state method.
103
112
  #
104
113
  # ==== Options
105
114
  # * <tt>:view</tt> - Specifies the name of the view file to render. Defaults to the current state name.
106
115
  # * <tt>:template_format</tt> - Allows using a format different to <tt>:html</tt>.
107
116
  # * <tt>:layout</tt> - If set to a valid filename inside your cell's view_paths, the current state view will be rendered inside the layout (as known from controller actions). Layouts should reside in <tt>app/cells/layouts</tt>.
108
- # * <tt>:render_children</tt> - If false, automatic rendering of child widgets is turned off. Defaults to true.
109
- # * <tt>:invoke</tt> - Explicitly define the state to be invoked on a child when rendering.
110
117
  # * see Cell::Base#render for additional options
111
118
  #
112
- # Note that <tt>:text => ...</tt> and <tt>:update => true</tt> will turn off <tt>:frame</tt>.
113
- #
114
119
  # Example:
115
- # class MouseCell < Apotomo::StatefulWidget
116
- # def eating
120
+ # class MouseWidget < Apotomo::StatefulWidget
121
+ # def eat
117
122
  # # ... do something
118
123
  # render
119
124
  # end
120
125
  #
121
- # will just render the view <tt>eating.html</tt>.
122
- #
123
- # def eating
124
- # # ... do something
125
- # render :view => :bored, :layout => "metal"
126
- # end
127
- #
128
- # will use the view <tt>bored.html</tt> as template and even put it in the layout
129
- # <tt>metal</tt> that's located at <tt>$RAILS_ROOT/app/cells/layouts/metal.html.erb</tt>.
126
+ # will just render the view <tt>eat.haml</tt>.
130
127
  #
131
128
  # render :js => "alert('SQUEAK!');"
132
129
  #
133
130
  # issues a squeaking alert dialog on the page.
134
- def render(options={}, &block)
135
- if options[:nothing]
136
- return ""
137
- end
138
-
139
- if options[:text]
140
- options.reverse_merge!(:render_children => false)
141
- end
142
-
143
- options.reverse_merge! :render_children => true,
144
- :locals => {},
145
- :invoke => {},
146
- :suppress_js => false
147
-
148
-
149
- rendered_children = render_children_for(options)
150
-
151
- options[:locals].reverse_merge!(:rendered_children => rendered_children)
152
-
153
- @suppress_js = options[:suppress_js] ### FIXME: implement with ActiveHelper and :locals.
154
-
155
-
156
- render_view_for(options, action_name) # defined in Cell::Base.
131
+ def render(*args, &block)
132
+ super
157
133
  end
158
134
 
159
135
  alias_method :emit, :render
160
136
 
161
- # Wraps the rendered content in a replace statement targeted at your +Apotomo.js_framework+ setting.
162
- # Use +:selector+ to change the selector.
163
- #
164
- # Example:
165
- #
166
- # Assuming you set
167
- # Apotomo.js_framework = :jquery
168
- #
169
- # and call replace in a state
170
- #
171
- # replace :view => :squeak, :selector => "div#mouse"
172
- # #=> "$(\"div#mouse\").replaceWith(\"<div id=\\\"mum\\\">squeak!<\\/div>\")"
173
- def replace(options={})
174
- content = render(options)
175
- Apotomo.js_generator.replace(options[:selector] || self.name, content)
176
- end
177
-
178
- # Same as replace except that the content is wrapped in an update statement.
179
- #
180
- # Example for +:jquery+:
181
- #
182
- # update :view => :squeak
183
- # #=> "$(\"mum\").html(\"<div id=\\\"mum\\\">squeak!<\\/div>\")"
184
- def update(options={})
185
- content = render(options)
186
- Apotomo.js_generator.update(options[:selector] || self.name, content)
187
- end
188
-
189
- # Force the FSM to go into <tt>state</tt>, regardless whether it's a valid
190
- # transition or not.
191
- ### TODO: document the need for return.
192
- def jump_to_state(state)
193
- logger.debug "STATE JUMP! to #{state}"
194
-
195
- render_state(state)
196
- end
197
-
198
-
199
- def visible_children
200
- children.find_all { |kid| kid.visible? }
201
- end
202
-
203
- def render_children_for(options)
204
- return {} unless options[:render_children]
205
-
206
- render_children(options[:invoke])
207
- end
208
-
209
- def render_children(invoke_options={})
210
- ActiveSupport::OrderedHash.new.tap do |rendered_children|
211
- visible_children.each do |kid|
212
- child_state = decide_state_for(kid, invoke_options)
213
- logger.debug " #{kid.name} -> #{child_state}"
214
-
215
- rendered_children[kid.name] = render_child(kid, child_state)
216
- end
217
- end
218
- end
219
-
220
- def render_child(cell, state)
221
- cell.invoke(state)
222
- end
223
-
224
- def decide_state_for(child, invoke_options)
225
- invoke_options.stringify_keys[child.name.to_s]
226
- end
227
-
228
-
229
- ### DISCUSS: use #param only for accessing request data.
230
137
  def param(name)
231
- @params[name]
138
+ msg = "Deprecated. Use #options for widget constructor options or #params for request data."
139
+ ActiveSupport::Deprecation.warn(msg)
140
+ raise msg
232
141
  end
233
142
 
234
143
 
@@ -247,6 +156,21 @@ module Apotomo
247
156
  apotomo_event_path address_for_event(type, options)
248
157
  end
249
158
 
250
- alias_method :widget_id, :name
159
+
160
+ def self.controller_path
161
+ @controller_path ||= name.sub(/Widget$/, '').underscore unless anonymous?
162
+ end
163
+
164
+ # Renders the +widget+ (instance or id).
165
+ def render_widget(widget_id, state=:display, *args)
166
+ if widget_id.kind_of?(Widget)
167
+ widget = widget_id
168
+ else
169
+ widget = find_widget(widget_id) or raise "Couldn't render non-existent widget `#{widget_id}`"
170
+ end
171
+
172
+ widget.invoke(state, *args)
173
+ end
251
174
  end
252
175
  end
176
+