apotomo 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +10 -0
- data/Gemfile.lock +47 -0
- data/README +141 -0
- data/README.rdoc +141 -0
- data/Rakefile +78 -0
- data/TODO +36 -0
- data/app/cells/apotomo/child_switch_widget/switch.html.erb +1 -0
- data/app/cells/apotomo/child_switch_widget/switch.rhtml +1 -0
- data/app/cells/apotomo/deep_link_widget.rb +27 -0
- data/app/cells/apotomo/deep_link_widget/setup.html.erb +20 -0
- data/app/cells/apotomo/java_script_widget.rb +12 -0
- data/app/cells/apotomo/tab_panel_widget.rb +87 -0
- data/app/cells/apotomo/tab_panel_widget/display.html.erb +57 -0
- data/app/cells/apotomo/tab_widget.rb +18 -0
- data/app/cells/apotomo/tab_widget/display.html.erb +1 -0
- data/config/routes.rb +3 -0
- data/generators/widget/USAGE +15 -0
- data/generators/widget/templates/functional_test.rb +8 -0
- data/generators/widget/templates/view.html.erb +2 -0
- data/generators/widget/templates/view.html.haml +3 -0
- data/generators/widget/templates/widget.rb +8 -0
- data/generators/widget/widget_generator.rb +34 -0
- data/lib/apotomo.rb +59 -0
- data/lib/apotomo/caching.rb +37 -0
- data/lib/apotomo/container_widget.rb +10 -0
- data/lib/apotomo/deep_link_methods.rb +90 -0
- data/lib/apotomo/event.rb +9 -0
- data/lib/apotomo/event_handler.rb +23 -0
- data/lib/apotomo/event_methods.rb +102 -0
- data/lib/apotomo/invoke_event_handler.rb +24 -0
- data/lib/apotomo/javascript_generator.rb +57 -0
- data/lib/apotomo/persistence.rb +139 -0
- data/lib/apotomo/proc_event_handler.rb +18 -0
- data/lib/apotomo/rails/controller_methods.rb +161 -0
- data/lib/apotomo/rails/view_helper.rb +95 -0
- data/lib/apotomo/rails/view_methods.rb +7 -0
- data/lib/apotomo/request_processor.rb +92 -0
- data/lib/apotomo/stateful_widget.rb +8 -0
- data/lib/apotomo/transition.rb +46 -0
- data/lib/apotomo/tree_node.rb +186 -0
- data/lib/apotomo/version.rb +5 -0
- data/lib/apotomo/widget.rb +289 -0
- data/lib/apotomo/widget_shortcuts.rb +36 -0
- data/rails/init.rb +0 -0
- data/test/fixtures/application_widget_tree.rb +2 -0
- data/test/rails/controller_methods_test.rb +206 -0
- data/test/rails/rails_integration_test.rb +99 -0
- data/test/rails/view_helper_test.rb +77 -0
- data/test/rails/view_methods_test.rb +40 -0
- data/test/rails/widget_generator_test.rb +47 -0
- data/test/support/assertions_helper.rb +13 -0
- data/test/support/test_case_methods.rb +68 -0
- data/test/test_helper.rb +77 -0
- data/test/unit/apotomo_test.rb +20 -0
- data/test/unit/container_test.rb +20 -0
- data/test/unit/event_handler_test.rb +67 -0
- data/test/unit/event_methods_test.rb +83 -0
- data/test/unit/event_test.rb +30 -0
- data/test/unit/invoke_test.rb +123 -0
- data/test/unit/javascript_generator_test.rb +90 -0
- data/test/unit/onfire_integration_test.rb +19 -0
- data/test/unit/persistence_test.rb +240 -0
- data/test/unit/render_test.rb +203 -0
- data/test/unit/request_processor_test.rb +178 -0
- data/test/unit/stateful_widget_test.rb +135 -0
- data/test/unit/test_addressing.rb +111 -0
- data/test/unit/test_caching.rb +54 -0
- data/test/unit/test_jump_to_state.rb +89 -0
- data/test/unit/test_tab_panel.rb +72 -0
- data/test/unit/test_widget_shortcuts.rb +45 -0
- data/test/unit/transition_test.rb +33 -0
- data/test/unit/widget_shortcuts_test.rb +68 -0
- data/test/unit/widget_test.rb +24 -0
- metadata +215 -0
@@ -0,0 +1,289 @@
|
|
1
|
+
require 'onfire'
|
2
|
+
require 'apotomo/tree_node'
|
3
|
+
|
4
|
+
|
5
|
+
require 'apotomo/event'
|
6
|
+
require 'apotomo/event_methods'
|
7
|
+
require 'apotomo/transition'
|
8
|
+
require 'apotomo/caching'
|
9
|
+
require 'apotomo/deep_link_methods'
|
10
|
+
require 'apotomo/widget_shortcuts'
|
11
|
+
require 'apotomo/rails/view_helper'
|
12
|
+
|
13
|
+
### TODO: use load_hooks when switching to rails 3.
|
14
|
+
# wycats@gmail.com: ActiveSupport.run_load_hooks(:name)
|
15
|
+
# (21:01:17) wycats@gmail.com: ActiveSupport.on_load(:name) { … }
|
16
|
+
#require 'active_support/lazy_load_hooks'
|
17
|
+
|
18
|
+
module Apotomo
|
19
|
+
class Widget < Cell::Base
|
20
|
+
|
21
|
+
class_inheritable_array :initialize_hooks, :instance_writer => false
|
22
|
+
self.initialize_hooks = []
|
23
|
+
|
24
|
+
attr_accessor :opts
|
25
|
+
attr_writer :visible
|
26
|
+
|
27
|
+
include TreeNode
|
28
|
+
|
29
|
+
|
30
|
+
include Onfire
|
31
|
+
include EventMethods
|
32
|
+
|
33
|
+
include Transition
|
34
|
+
include Caching
|
35
|
+
|
36
|
+
include DeepLinkMethods
|
37
|
+
include WidgetShortcuts
|
38
|
+
|
39
|
+
helper Apotomo::Rails::ViewHelper
|
40
|
+
|
41
|
+
|
42
|
+
attr_writer :controller
|
43
|
+
attr_accessor :version
|
44
|
+
|
45
|
+
### DISCUSS: extract to has_widgets_methods for both Widget and Controller?
|
46
|
+
#class_inheritable_array :has_widgets_blocks
|
47
|
+
|
48
|
+
class << self
|
49
|
+
include WidgetShortcuts
|
50
|
+
|
51
|
+
def has_widgets_blocks
|
52
|
+
@has_widgets_blocks ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_widgets(&block)
|
56
|
+
has_widgets_blocks << block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
self.initialize_hooks << :add_has_widgets_blocks
|
60
|
+
|
61
|
+
def add_has_widgets_blocks(*)
|
62
|
+
self.class.has_widgets_blocks.each { |block| block.call(self) }
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Constructor which needs a unique id for the widget and one or multiple start states.
|
67
|
+
# <tt>start_state</tt> may be a symbol or an array of symbols.
|
68
|
+
def initialize(id, start_state, opts={})
|
69
|
+
@opts = opts
|
70
|
+
@name = id
|
71
|
+
@start_state = start_state
|
72
|
+
|
73
|
+
@visible = true
|
74
|
+
@version = 0
|
75
|
+
|
76
|
+
@cell = self
|
77
|
+
|
78
|
+
process_initialize_hooks(id, start_state, opts)
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_initialize_hooks(*args)
|
82
|
+
self.class.initialize_hooks.each { |method| send(method, *args) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def last_state
|
86
|
+
@state_name
|
87
|
+
end
|
88
|
+
|
89
|
+
def visible?
|
90
|
+
@visible
|
91
|
+
end
|
92
|
+
|
93
|
+
# Defines the instance vars that should <em>not</em> survive between requests,
|
94
|
+
# which means they're not frozen in Apotomo::StatefulWidget#freeze.
|
95
|
+
def ivars_to_forget
|
96
|
+
unfreezable_ivars
|
97
|
+
end
|
98
|
+
|
99
|
+
def unfreezable_ivars
|
100
|
+
[:@childrenHash, :@children, :@parent, :@controller, :@cell, :@invoke_block, :@rendered_children, :@page_updates, :@opts,
|
101
|
+
:@suppress_javascript ### FIXME: implement with ActiveHelper and :locals.
|
102
|
+
|
103
|
+
]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Defines the instance vars which should <em>not</em> be copied to the view.
|
107
|
+
# Called in Cell::Base.
|
108
|
+
def ivars_to_ignore
|
109
|
+
[]
|
110
|
+
end
|
111
|
+
|
112
|
+
### FIXME:
|
113
|
+
def logger; self; end
|
114
|
+
def debug(*args); puts args; end
|
115
|
+
|
116
|
+
# Returns the rendered content for the widget by running the state method for <tt>state</tt>.
|
117
|
+
# This might lead us to some other state since the state method could call #jump_to_state.
|
118
|
+
def invoke(state=nil, &block)
|
119
|
+
@invoke_block = block ### DISCUSS: store block so we don't have to pass it 10 times?
|
120
|
+
logger.debug "\ninvoke on #{name} with #{state.inspect}"
|
121
|
+
|
122
|
+
if state.blank?
|
123
|
+
state = next_state_for(last_state) || @start_state
|
124
|
+
end
|
125
|
+
|
126
|
+
logger.debug "#{name}: transition: #{last_state} to #{state}"
|
127
|
+
logger.debug " ...#{state}"
|
128
|
+
|
129
|
+
render_state(state)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
# called in Cell::Base#render_state
|
135
|
+
def dispatch_state(state)
|
136
|
+
send(state, &@invoke_block)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# Render the view for the current state. Usually called at the end of a state method.
|
141
|
+
#
|
142
|
+
# ==== Options
|
143
|
+
# * <tt>:view</tt> - Specifies the name of the view file to render. Defaults to the current state name.
|
144
|
+
# * <tt>:template_format</tt> - Allows using a format different to <tt>:html</tt>.
|
145
|
+
# * <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>.
|
146
|
+
# * <tt>:render_children</tt> - If false, automatic rendering of child widgets is turned off. Defaults to true.
|
147
|
+
# * <tt>:invoke</tt> - Explicitly define the state to be invoked on a child when rendering.
|
148
|
+
# * see Cell::Base#render for additional options
|
149
|
+
#
|
150
|
+
# Note that <tt>:text => ...</tt> and <tt>:update => true</tt> will turn off <tt>:frame</tt>.
|
151
|
+
#
|
152
|
+
# Example:
|
153
|
+
# class MouseCell < Apotomo::StatefulWidget
|
154
|
+
# def eating
|
155
|
+
# # ... do something
|
156
|
+
# render
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# will just render the view <tt>eating.html</tt>.
|
160
|
+
#
|
161
|
+
# def eating
|
162
|
+
# # ... do something
|
163
|
+
# render :view => :bored, :layout => "metal"
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# will use the view <tt>bored.html</tt> as template and even put it in the layout
|
167
|
+
# <tt>metal</tt> that's located at <tt>$RAILS_ROOT/app/cells/layouts/metal.html.erb</tt>.
|
168
|
+
#
|
169
|
+
# render :js => "alert('SQUEAK!');"
|
170
|
+
#
|
171
|
+
# issues a squeaking alert dialog on the page.
|
172
|
+
def render(options={}, &block)
|
173
|
+
if options[:nothing]
|
174
|
+
return ""
|
175
|
+
end
|
176
|
+
|
177
|
+
if options[:text]
|
178
|
+
options.reverse_merge!(:render_children => false)
|
179
|
+
end
|
180
|
+
|
181
|
+
options.reverse_merge! :render_children => true,
|
182
|
+
:locals => {},
|
183
|
+
:invoke => {},
|
184
|
+
:suppress_js => false
|
185
|
+
|
186
|
+
|
187
|
+
rendered_children = render_children_for(options)
|
188
|
+
|
189
|
+
options[:locals].reverse_merge!(:rendered_children => rendered_children)
|
190
|
+
|
191
|
+
@controller = controller # that dependency SUCKS.
|
192
|
+
@suppress_js = options[:suppress_js] ### FIXME: implement with ActiveHelper and :locals.
|
193
|
+
|
194
|
+
|
195
|
+
render_view_for(options, @state_name) # defined in Cell::Base.
|
196
|
+
end
|
197
|
+
|
198
|
+
alias_method :emit, :render
|
199
|
+
|
200
|
+
|
201
|
+
def replace(options={})
|
202
|
+
content = render(options)
|
203
|
+
Apotomo.js_generator.replace(self.name, content)
|
204
|
+
end
|
205
|
+
|
206
|
+
def update(options={})
|
207
|
+
content = render(options)
|
208
|
+
Apotomo.js_generator.update(self.name, content)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Force the FSM to go into <tt>state</tt>, regardless whether it's a valid
|
212
|
+
# transition or not.
|
213
|
+
### TODO: document the need for return.
|
214
|
+
def jump_to_state(state)
|
215
|
+
logger.debug "STATE JUMP! to #{state}"
|
216
|
+
|
217
|
+
render_state(state)
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
def visible_children
|
222
|
+
children.find_all { |kid| kid.visible? }
|
223
|
+
end
|
224
|
+
|
225
|
+
def render_children_for(options)
|
226
|
+
return {} unless options[:render_children]
|
227
|
+
|
228
|
+
render_children(options[:invoke])
|
229
|
+
end
|
230
|
+
|
231
|
+
def render_children(invoke_options={})
|
232
|
+
returning rendered_children = ActiveSupport::OrderedHash.new do
|
233
|
+
visible_children.each do |kid|
|
234
|
+
child_state = decide_state_for(kid, invoke_options)
|
235
|
+
logger.debug " #{kid.name} -> #{child_state}"
|
236
|
+
|
237
|
+
rendered_children[kid.name] = render_child(kid, child_state)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def render_child(cell, state)
|
243
|
+
cell.invoke(state)
|
244
|
+
end
|
245
|
+
|
246
|
+
def decide_state_for(child, invoke_options)
|
247
|
+
invoke_options.stringify_keys[child.name.to_s]
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
### DISCUSS: use #param only for accessing request data.
|
252
|
+
def param(name)
|
253
|
+
params[name]
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Returns the address hash to the event controller and the targeted widget.
|
258
|
+
#
|
259
|
+
# Reserved options for <tt>way</tt>:
|
260
|
+
# :source explicitly specifies an event source.
|
261
|
+
# The default is to take the current widget as source.
|
262
|
+
# :type specifies the event type.
|
263
|
+
#
|
264
|
+
# Any other option will be directly passed into the address hash and is
|
265
|
+
# available via StatefulWidget#param in the widget.
|
266
|
+
#
|
267
|
+
# Can be passed to #url_for.
|
268
|
+
#
|
269
|
+
# Example:
|
270
|
+
# address_for_event :type => :squeak, :volume => 9
|
271
|
+
# will result in an address that triggers a <tt>:click</tt> event from the current
|
272
|
+
# widget and also provides the parameter <tt>:item_id</tt>.
|
273
|
+
def address_for_event(options)
|
274
|
+
raise "please specify the event :type" unless options[:type]
|
275
|
+
|
276
|
+
options[:source] ||= self.name
|
277
|
+
options
|
278
|
+
end
|
279
|
+
|
280
|
+
# Returns the widget named <tt>widget_id</tt> as long as it is below self or self itself.
|
281
|
+
def find_widget(widget_id)
|
282
|
+
find {|node| node.name.to_s == widget_id.to_s}
|
283
|
+
end
|
284
|
+
|
285
|
+
def controller
|
286
|
+
root? ? @controller : root.controller
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Apotomo
|
2
|
+
# Shortcut methods for creating widget trees.
|
3
|
+
module WidgetShortcuts
|
4
|
+
# Creates an instance of <tt>class_name</tt> with the id <tt>id</tt> and start state <tt>state</tt>.
|
5
|
+
# Default start state is <tt>:display</tt>.
|
6
|
+
# Yields self if a block is passed.
|
7
|
+
# Example:
|
8
|
+
# widget(:form, 'uploads', :build_form) do |form|
|
9
|
+
# form << widget(:upload_field)
|
10
|
+
def widget(class_name, id, state=:display, *args)
|
11
|
+
object = class_name.to_s.classify.constantize.new(id, state, *args)
|
12
|
+
yield object if block_given?
|
13
|
+
object
|
14
|
+
end
|
15
|
+
|
16
|
+
def container(id, *args, &block)
|
17
|
+
widget('apotomo/container_widget', id, *args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def section(*args)
|
21
|
+
container(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def cell(base_name, states, id, *args)
|
25
|
+
widget(base_name.to_s + '_cell', states, id, *args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def tab_panel(id, *args)
|
29
|
+
widget('apotomo/tab_panel_widget', :display, id, *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tab(id, *args)
|
33
|
+
widget('apotomo/tab_widget', :display, id, *args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/rails/init.rb
ADDED
File without changes
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class ControllerMethodsTest < ActionController::TestCase
|
4
|
+
context "A Rails controller" do
|
5
|
+
setup do
|
6
|
+
barn_controller!
|
7
|
+
end
|
8
|
+
|
9
|
+
context "responding to #apotomo_root" do
|
10
|
+
should "initially return a root widget" do
|
11
|
+
assert_equal 1, @controller.apotomo_root.size
|
12
|
+
end
|
13
|
+
|
14
|
+
should "allow tree modifications" do
|
15
|
+
@controller.apotomo_root << mouse_mock
|
16
|
+
assert_equal 2, @controller.apotomo_root.size
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "responding to #apotomo_request_processor" do
|
21
|
+
should "initially return the processor which has a flushed root" do
|
22
|
+
assert_kind_of Apotomo::RequestProcessor, @controller.apotomo_request_processor
|
23
|
+
assert_equal 1, @controller.apotomo_request_processor.root.size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "invoking #uses_widgets" do
|
28
|
+
setup do
|
29
|
+
@controller.class.uses_widgets do |root|
|
30
|
+
root << mouse_mock('mum')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should "add the widgets to apotomo_root" do
|
35
|
+
assert_equal 'mum', @controller.apotomo_root['mum'].name
|
36
|
+
end
|
37
|
+
|
38
|
+
should "add the widgets only once in apotomo_root" do
|
39
|
+
@controller.apotomo_root
|
40
|
+
assert @controller.apotomo_root['mum']
|
41
|
+
end
|
42
|
+
|
43
|
+
should "allow multiple calls to uses_widgets" do
|
44
|
+
@controller.class.uses_widgets do |root|
|
45
|
+
root << mouse_mock('kid')
|
46
|
+
end
|
47
|
+
|
48
|
+
assert @controller.apotomo_root['mum']
|
49
|
+
assert @controller.apotomo_root['kid']
|
50
|
+
end
|
51
|
+
|
52
|
+
should "inherit uses_widgets blocks to sub-controllers" do
|
53
|
+
berry = mouse_mock('berry')
|
54
|
+
@sub_controller = Class.new(@controller.class) do
|
55
|
+
uses_widgets { |root| root << berry }
|
56
|
+
end.new
|
57
|
+
@sub_controller.params = {}
|
58
|
+
@sub_controller.session = {}
|
59
|
+
|
60
|
+
assert @sub_controller.apotomo_root['mum']
|
61
|
+
assert @sub_controller.apotomo_root['berry']
|
62
|
+
end
|
63
|
+
|
64
|
+
should "be aliased to has_widgets" do
|
65
|
+
@controller.class.has_widgets do |root|
|
66
|
+
root << mouse_mock('kid')
|
67
|
+
end
|
68
|
+
|
69
|
+
assert @controller.apotomo_root['mum']
|
70
|
+
assert @controller.apotomo_root['kid']
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "invoking #use_widgets" do
|
75
|
+
should "have an empty apotomo_root if no call happened, yet" do
|
76
|
+
assert_equal [], @controller.bound_use_widgets_blocks
|
77
|
+
assert_equal 1, @controller.apotomo_root.size
|
78
|
+
end
|
79
|
+
|
80
|
+
should "extend the widget family and remember the block with one #use_widgets call" do
|
81
|
+
@controller.use_widgets do |root|
|
82
|
+
root << mouse_mock
|
83
|
+
end
|
84
|
+
|
85
|
+
assert_equal 1, @controller.bound_use_widgets_blocks.size
|
86
|
+
assert_equal 2, @controller.apotomo_root.size
|
87
|
+
end
|
88
|
+
|
89
|
+
should "add blocks only once" do
|
90
|
+
block = Proc.new {|root| root << mouse_mock}
|
91
|
+
|
92
|
+
@controller.use_widgets &block
|
93
|
+
@controller.use_widgets &block
|
94
|
+
|
95
|
+
assert_equal 1, @controller.bound_use_widgets_blocks.size
|
96
|
+
assert_equal 2, @controller.apotomo_root.size
|
97
|
+
end
|
98
|
+
|
99
|
+
should "allow multiple calls with different blocks" do
|
100
|
+
mum_and_kid!
|
101
|
+
@controller.use_widgets do |root|
|
102
|
+
root << @mum
|
103
|
+
end
|
104
|
+
@controller.use_widgets do |root|
|
105
|
+
root << mouse_mock('pet')
|
106
|
+
end
|
107
|
+
|
108
|
+
assert_equal 2, @controller.bound_use_widgets_blocks.size
|
109
|
+
assert_equal 4, @controller.apotomo_root.size
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "invoking #url_for_event" do
|
114
|
+
should "compute an url for any widget" do
|
115
|
+
assert_equal "/barn/render_event_response?source=mouse&type=footsteps&volume=9", @controller.url_for_event(:footsteps, :source => :mouse, :volume => 9)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
should "flush its bound_use_widgets_blocks with, guess, #flush_bound_use_widgets_blocks" do
|
120
|
+
@controller.bound_use_widgets_blocks << Proc.new {}
|
121
|
+
assert_equal 1, @controller.bound_use_widgets_blocks.size
|
122
|
+
@controller.flush_bound_use_widgets_blocks
|
123
|
+
assert_equal 0, @controller.bound_use_widgets_blocks.size
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "invoking #render_widget" do
|
128
|
+
setup do
|
129
|
+
@mum = mouse_mock('mum', 'snuggle') {def snuggle; render; end}
|
130
|
+
end
|
131
|
+
|
132
|
+
should "render the widget" do
|
133
|
+
@controller.apotomo_root << @mum
|
134
|
+
assert_equal '<div id="mum"><snuggle></snuggle></div>', @controller.render_widget('mum')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
|
140
|
+
context "invoking #apotomo_freeze" do
|
141
|
+
should "freeze the widget tree to session" do
|
142
|
+
assert_equal 0, @controller.session.size
|
143
|
+
@controller.send :apotomo_freeze
|
144
|
+
assert @controller.session[:apotomo_widget_ivars]
|
145
|
+
assert @controller.session[:apotomo_stateful_branches]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "processing an event request" do
|
150
|
+
setup do
|
151
|
+
@mum = mouse_mock('mum', :eating)
|
152
|
+
@mum << @kid = mouse_mock('kid', :squeak)
|
153
|
+
|
154
|
+
@kid.respond_to_event :doorSlam, :with => :eating, :on => 'mum'
|
155
|
+
@kid.respond_to_event :doorSlam, :with => :squeak
|
156
|
+
@mum.respond_to_event :doorSlam, :with => :squeak
|
157
|
+
|
158
|
+
@mum.instance_eval do
|
159
|
+
def squeak; render :js => 'squeak();'; end
|
160
|
+
end
|
161
|
+
@kid.instance_eval do
|
162
|
+
def squeak; render :text => 'squeak!', :update => :true; end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
### DISCUSS: needed?
|
167
|
+
context "in event mode" do
|
168
|
+
should_eventually "set the MIME type to text/javascript" do
|
169
|
+
@controller.apotomo_root << @mum
|
170
|
+
|
171
|
+
get :render_event_response, :source => :kid, :type => :doorSlam
|
172
|
+
|
173
|
+
assert_equal Mime::JS, @response.content_type
|
174
|
+
assert_equal "$(\"mum\").replace(\"<div id=\\\"mum\\\">burp!<\\/div>\")\n$(\"kid\").update(\"squeak!\")\nsqueak();", @response.body
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "The ProcHash" do
|
180
|
+
setup do
|
181
|
+
@procs = Apotomo::Rails::ControllerMethods::ProcHash.new
|
182
|
+
@b = Proc.new{}; @d = Proc.new{}
|
183
|
+
@c = Proc.new{}
|
184
|
+
@procs << @b
|
185
|
+
end
|
186
|
+
|
187
|
+
should "return true for procs it includes" do
|
188
|
+
assert @procs.include?(@b)
|
189
|
+
assert @procs.include?(@d) ### DISCUSS: line nr is id, or do YOU got a better idea?!
|
190
|
+
end
|
191
|
+
|
192
|
+
should "reject unknown procs" do
|
193
|
+
assert ! @procs.include?(@c)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
### FIXME: could somebody get that working?
|
198
|
+
context "Routing" do
|
199
|
+
should_eventually "generate routes to the render_event_response action" do
|
200
|
+
assert_generates "/barn/render_event_response?type=squeak", { :controller => "barn", :action => "render_event_response", :type => "squeak" }
|
201
|
+
|
202
|
+
assert_recognizes({ :controller => "apotomo", :action => "render_event_response", :type => "squeak" }, "/apotomo/render_event_response?type=squeak")
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|