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