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,20 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class ContainerTest < Test::Unit::TestCase
|
4
|
+
context "Rendering a container" do
|
5
|
+
setup do
|
6
|
+
@family = container('family')
|
7
|
+
@family.controller = @controller
|
8
|
+
end
|
9
|
+
|
10
|
+
should "return an empty view if childless" do
|
11
|
+
assert_equal "<div id=\"family\"></div>", @family.invoke
|
12
|
+
end
|
13
|
+
|
14
|
+
should "provide a family picture" do
|
15
|
+
@family << mouse_mock('mum')
|
16
|
+
@family << mouse_mock('kid')
|
17
|
+
assert_equal "<div id=\"family\"><div id=\"mum\">burp!</div>\n<div id=\"kid\">burp!</div></div>", @family.invoke
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
|
4
|
+
class EventHandlerTest < Test::Unit::TestCase
|
5
|
+
context "an abstract EventHandler" do
|
6
|
+
should "push nil to root's ordered page_updates when #call'ed" do
|
7
|
+
@mum = mouse_mock('mum')
|
8
|
+
@mum << @kid = mouse_mock('kid')
|
9
|
+
|
10
|
+
assert_equal 0, @mum.page_updates.size
|
11
|
+
|
12
|
+
[@mum, @kid, @mum].each do |source|
|
13
|
+
Apotomo::EventHandler.new.call(Apotomo::Event.new(:squeak, source))
|
14
|
+
end
|
15
|
+
|
16
|
+
# order matters:
|
17
|
+
assert_equal 3, @mum.page_updates.size
|
18
|
+
assert_equal 0, @kid.page_updates.size
|
19
|
+
assert_equal(nil, @mum.page_updates[0])
|
20
|
+
assert_equal(nil, @mum.page_updates[1])
|
21
|
+
assert_equal(nil, @mum.page_updates[2])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
def test_invoke_to_s
|
28
|
+
h = Apotomo::InvokeEventHandler.new
|
29
|
+
h.widget_id = :widget_id
|
30
|
+
h.state = :my_state
|
31
|
+
assert_equal "InvokeEventHandler:widget_id#my_state", h.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_proc_to_s
|
35
|
+
h = Apotomo::ProcEventHandler.new
|
36
|
+
h.proc = :my_method
|
37
|
+
assert_equal "ProcEventHandler:my_method", h.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_constructor_for_proc
|
41
|
+
h = Apotomo::ProcEventHandler.new
|
42
|
+
assert_nil h.proc
|
43
|
+
h = Apotomo::ProcEventHandler.new(:proc => :method)
|
44
|
+
assert_equal :method, h.proc
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_constructor_for_invoke
|
48
|
+
h = Apotomo::InvokeEventHandler.new
|
49
|
+
assert_nil h.widget_id
|
50
|
+
assert_nil h.state
|
51
|
+
h = Apotomo::InvokeEventHandler.new(:widget_id => :widget, :state => :state)
|
52
|
+
assert_equal :widget, h.widget_id
|
53
|
+
assert_equal :state, h.state
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_equal
|
57
|
+
h1 = Apotomo::ProcEventHandler.new(:proc => :run)
|
58
|
+
h2 = Apotomo::ProcEventHandler.new(:proc => :run)
|
59
|
+
h3 = Apotomo::ProcEventHandler.new(:proc => :walk)
|
60
|
+
|
61
|
+
assert h1 == h2
|
62
|
+
assert h1 != h3
|
63
|
+
end
|
64
|
+
|
65
|
+
### TODO: test #call
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w(.. test_helper))
|
2
|
+
|
3
|
+
class EventMethodsTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "#respond_to_event and #fire" do
|
6
|
+
setup do
|
7
|
+
mum_and_kid!
|
8
|
+
end
|
9
|
+
|
10
|
+
should "alert @mum first, then make her squeak when @kid squeaks" do
|
11
|
+
@kid.fire :squeak
|
12
|
+
assert_equal ['be alerted', 'answer squeak'], @mum.list
|
13
|
+
end
|
14
|
+
|
15
|
+
should "make @mum just squeak back when @jerry squeaks" do
|
16
|
+
@mum << @jerry = mouse_mock('jerry')
|
17
|
+
@jerry.fire :squeak
|
18
|
+
assert_equal ['answer squeak'], @mum.list
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
should "make @mum run away while @kid keeps watching" do
|
23
|
+
@kid.fire :footsteps
|
24
|
+
assert_equal ['peek', 'escape'], @mum.list
|
25
|
+
end
|
26
|
+
|
27
|
+
should "by default add a handler only once" do
|
28
|
+
@mum.respond_to_event :peep, :with => :answer_squeak
|
29
|
+
@mum.respond_to_event :peep, :with => :answer_squeak
|
30
|
+
@mum.fire :peep
|
31
|
+
assert_equal ['answer squeak'], @mum.list
|
32
|
+
end
|
33
|
+
|
34
|
+
should "squeak back twice when using the :once => false option" do
|
35
|
+
@mum.respond_to_event :peep, :with => :answer_squeak
|
36
|
+
@mum.respond_to_event :peep, :with => :answer_squeak, :once => false
|
37
|
+
@mum.fire :peep
|
38
|
+
assert_equal ['answer squeak', 'answer squeak'], @mum.list
|
39
|
+
end
|
40
|
+
|
41
|
+
context "#responds_to_event in class context" do
|
42
|
+
setup do
|
43
|
+
class AdultMouseCell < MouseCell
|
44
|
+
responds_to_event :peep, :with => :answer_squeak
|
45
|
+
end
|
46
|
+
class BabyMouseCell < AdultMouseCell
|
47
|
+
responds_to_event :footsteps, :with => :squeak
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
should "add the handlers at creation time" do
|
52
|
+
assert_equal [Apotomo::InvokeEventHandler.new(:widget_id => 'mum', :state => :answer_squeak)], AdultMouseCell.new('mum', :show).event_table.all_handlers_for(:peep, 'mum')
|
53
|
+
end
|
54
|
+
|
55
|
+
should "not inherit handlers for now" do
|
56
|
+
assert_equal [], BabyMouseCell.new('kid', :show).event_table.all_handlers_for(:peep, 'kid')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
context "#trigger" do
|
62
|
+
should "be an alias for #fire" do
|
63
|
+
@kid.trigger :footsteps
|
64
|
+
assert_equal ['peek', 'escape'], @mum.list
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
context "page_updates" do
|
70
|
+
should "expose a simple Array for now" do
|
71
|
+
assert_kind_of Array, @mum.page_updates
|
72
|
+
assert_equal 0, @mum.page_updates.size
|
73
|
+
end
|
74
|
+
|
75
|
+
should "be queued in root#page_updates after #fire" do
|
76
|
+
@mum.fire :footsteps
|
77
|
+
assert_equal ["escape"], @mum.page_updates
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class EventTest < Test::Unit::TestCase
|
4
|
+
context "An Event" do
|
5
|
+
should "require type and respond to #type" do
|
6
|
+
assert_equal :footsteps, Apotomo::Event.new(:footsteps).type
|
7
|
+
end
|
8
|
+
|
9
|
+
should "require source and respond to #source" do
|
10
|
+
@event = Apotomo::Event.new(:footsteps, 'mum')
|
11
|
+
assert_equal :footsteps, @event.type
|
12
|
+
assert_equal 'mum', @event.source
|
13
|
+
end
|
14
|
+
|
15
|
+
should "accept an additional data object and respond to #data" do
|
16
|
+
@event = Apotomo::Event.new(:footsteps, 'mum', {:volume => :loud})
|
17
|
+
assert_equal({:volume => :loud}, @event.data)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "complain when serialized" do
|
21
|
+
assert_raises RuntimeError do
|
22
|
+
Marshal.dump(Apotomo::Event.new(:footsteps, 'mum'))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
should "respond to #stopped?" do
|
27
|
+
assert_not Apotomo::Event.new(:footsteps, 'mum').stopped?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class InvokeTest < Test::Unit::TestCase
|
4
|
+
class LocalMouse < MouseCell
|
5
|
+
def snuggle; render; end
|
6
|
+
def educate; render :view => :snuggle; end
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Invoking a single widget" do
|
10
|
+
setup do
|
11
|
+
@mum = LocalMouse.new('mum', :snuggle)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "implicitely" do
|
15
|
+
should "always enter the given state" do
|
16
|
+
@mum.invoke :snuggle
|
17
|
+
assert_equal :snuggle, @mum.last_state
|
18
|
+
|
19
|
+
@mum.invoke :educate
|
20
|
+
assert_equal :educate, @mum.last_state
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "explicitely" do
|
25
|
+
should "per default enter the start state" do
|
26
|
+
@mum.invoke
|
27
|
+
assert_equal :snuggle, @mum.last_state
|
28
|
+
|
29
|
+
@mum.invoke
|
30
|
+
assert_equal :snuggle, @mum.last_state
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with defined transitions" do
|
34
|
+
setup do
|
35
|
+
@mum.instance_eval do
|
36
|
+
self.class.transition :from => :snuggle, :to => :educate
|
37
|
+
end
|
38
|
+
|
39
|
+
@mum.invoke
|
40
|
+
assert_equal :snuggle, @mum.last_state
|
41
|
+
end
|
42
|
+
|
43
|
+
should "automatically follow the transitions if defined" do
|
44
|
+
@mum.invoke
|
45
|
+
assert_equal :educate, @mum.last_state
|
46
|
+
end
|
47
|
+
|
48
|
+
should "nevertheless allow undefined implicit invokes" do
|
49
|
+
@mum.invoke :snuggle
|
50
|
+
assert_equal :snuggle, @mum.last_state
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "Invoking a widget family" do
|
57
|
+
setup do
|
58
|
+
@mum = LocalMouse.new('mum', :snuggle)
|
59
|
+
|
60
|
+
# create an anonym class for @kid so we don't pollute with #transition's.
|
61
|
+
@mum << @kid = mouse_class_mock.new('kid', :snooze)
|
62
|
+
@kid.instance_eval do
|
63
|
+
def snooze; render :nothing => true; end
|
64
|
+
def listen; render :nothing => true; end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "implicitely" do
|
69
|
+
should "per default send kid to its start state" do
|
70
|
+
@mum.invoke :snuggle
|
71
|
+
assert_equal :snuggle, @mum.last_state
|
72
|
+
assert_equal :snooze, @kid.last_state
|
73
|
+
|
74
|
+
@mum.invoke :educate
|
75
|
+
assert_equal :educate, @mum.last_state
|
76
|
+
assert_equal :snooze, @kid.last_state
|
77
|
+
end
|
78
|
+
|
79
|
+
should "follow the kid's transition if defined" do
|
80
|
+
@kid.instance_eval do
|
81
|
+
self.class.transition :from => :snooze, :to => :listen
|
82
|
+
end
|
83
|
+
|
84
|
+
@mum.invoke :snuggle
|
85
|
+
@mum.invoke :educate
|
86
|
+
assert_equal :educate, @mum.last_state
|
87
|
+
assert_equal :listen, @kid.last_state
|
88
|
+
end
|
89
|
+
|
90
|
+
should "send kid to the given state passed to #render" do
|
91
|
+
@mum.instance_eval do
|
92
|
+
def snuggle
|
93
|
+
render :invoke => {'kid' => :listen}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@mum.invoke :snuggle
|
98
|
+
assert_equal :snuggle, @mum.last_state
|
99
|
+
assert_equal :listen, @kid.last_state
|
100
|
+
end
|
101
|
+
|
102
|
+
should "send kid to the :invoke state as it overrides #transition" do
|
103
|
+
@kid.instance_eval do
|
104
|
+
self.class.transition :from => :snooze, :to => :listen
|
105
|
+
end
|
106
|
+
|
107
|
+
@mum.instance_eval do
|
108
|
+
def educate
|
109
|
+
render :nothing => true, :invoke => {'kid' => :snooze}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@mum.invoke :snuggle
|
114
|
+
assert_equal :snuggle, @mum.last_state
|
115
|
+
assert_equal :snooze, @kid.last_state
|
116
|
+
|
117
|
+
@mum.invoke :educate
|
118
|
+
assert_equal :educate, @mum.last_state
|
119
|
+
assert_equal :snooze, @kid.last_state
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class JavascriptGeneratorTest < Test::Unit::TestCase
|
5
|
+
context "The JavascriptGenerator" do
|
6
|
+
should "raise an error if no framework passed" do
|
7
|
+
assert_raises RuntimeError do
|
8
|
+
Apotomo::JavascriptGenerator.new(nil)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "in prototype mode" do
|
13
|
+
setup do
|
14
|
+
@gen = Apotomo::JavascriptGenerator.new(:prototype)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "respond to prototype" do
|
18
|
+
assert_respond_to @gen, :prototype
|
19
|
+
end
|
20
|
+
|
21
|
+
should "respond to xhr" do
|
22
|
+
assert_equal "new Ajax.Request(\"/drink/beer?source=nick\")", @gen.xhr('/drink/beer?source=nick')
|
23
|
+
end
|
24
|
+
|
25
|
+
should "respond to replace" do
|
26
|
+
assert_equal "$(\"drinks\").replace(\"EMPTY!\")", @gen.replace(:drinks, 'EMPTY!')
|
27
|
+
end
|
28
|
+
|
29
|
+
should "respond to update" do
|
30
|
+
assert_equal "$(\"drinks\").update(\"<li id=\\\"beer\\\"><\\/li>\")", @gen.update(:drinks, '<li id="beer"></li>')
|
31
|
+
end
|
32
|
+
|
33
|
+
should "respond to <<" do
|
34
|
+
assert_equal "alert(\"Beer!\")", @gen << 'alert("Beer!")'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "in right mode" do
|
39
|
+
setup do
|
40
|
+
@gen = Apotomo::JavascriptGenerator.new(:right)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "respond to right" do
|
44
|
+
assert_respond_to @gen, :right
|
45
|
+
end
|
46
|
+
|
47
|
+
should "respond to xhr" do
|
48
|
+
assert_equal "new Xhr(\"/drink/beer?source=nick\", {evalScripts:true}).send()", @gen.xhr('/drink/beer?source=nick')
|
49
|
+
end
|
50
|
+
|
51
|
+
should "respond to replace" do
|
52
|
+
assert_equal "$(\"drinks\").replace(\"EMPTY!\")", @gen.replace(:drinks, 'EMPTY!')
|
53
|
+
end
|
54
|
+
|
55
|
+
should "respond to update" do
|
56
|
+
assert_equal "$(\"drinks\").update(\"<li id=\\\"beer\\\"><\\/li>\")", @gen.update(:drinks, '<li id="beer"></li>')
|
57
|
+
end
|
58
|
+
|
59
|
+
should "respond to <<" do
|
60
|
+
assert_equal "alert(\"Beer!\")", @gen << 'alert("Beer!")'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "in jquery mode" do
|
65
|
+
setup do
|
66
|
+
@gen = Apotomo::JavascriptGenerator.new(:jquery)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "respond to jquery" do
|
70
|
+
assert_respond_to @gen, :jquery
|
71
|
+
end
|
72
|
+
|
73
|
+
should "respond to xhr" do
|
74
|
+
assert_equal "$.ajax({url: \"/drink/beer?source=nick\"})", @gen.xhr('/drink/beer?source=nick')
|
75
|
+
end
|
76
|
+
|
77
|
+
should "respond to replace" do
|
78
|
+
assert_equal "$(\"#drinks\").replaceWith(\"EMPTY!\")", @gen.replace(:drinks, 'EMPTY!')
|
79
|
+
end
|
80
|
+
|
81
|
+
should "respond to update" do
|
82
|
+
assert_equal "$(\"#drinks\").html(\"<li id=\\\"beer\\\"><\\/li>\")", @gen.update(:drinks, '<li id="beer"></li>')
|
83
|
+
end
|
84
|
+
|
85
|
+
should "respond to <<" do
|
86
|
+
assert_equal "alert(\"Beer!\")", @gen << 'alert("Beer!")'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class OnfireIntegrationTest < Test::Unit::TestCase
|
4
|
+
context "including Onfire into the StatefulWidget it" do
|
5
|
+
setup do
|
6
|
+
@mum = mouse_mock('mum')
|
7
|
+
@mum << @kid = mouse_mock('kid')
|
8
|
+
end
|
9
|
+
|
10
|
+
should "respond to #root" do
|
11
|
+
assert @mum.root?
|
12
|
+
assert ! @kid.root?
|
13
|
+
end
|
14
|
+
|
15
|
+
should "respond to #parent" do
|
16
|
+
assert_equal @mum, @kid.parent
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. test_helper])
|
2
|
+
|
3
|
+
class PersistenceTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
class PersistentMouse < Apotomo::StatefulWidget # we need a named class for marshalling.
|
6
|
+
attr_reader :who, :what
|
7
|
+
|
8
|
+
def educate
|
9
|
+
@who = "the cat"
|
10
|
+
@what = "run away"
|
11
|
+
render :nothing => true
|
12
|
+
end
|
13
|
+
|
14
|
+
def recap; render :nothing => true; end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "StatefulWidget" do
|
18
|
+
|
19
|
+
context ".stateful_branches_for" do
|
20
|
+
should "provide all stateful branch-roots seen from root" do
|
21
|
+
@root = Apotomo::Widget.new('root', :eat)
|
22
|
+
@root << mum_and_kid!
|
23
|
+
@root << Apotomo::Widget.new('berry', :eat) << @jerry = mouse_mock('jerry', :eat)
|
24
|
+
|
25
|
+
assert_equal ['mum', 'jerry'], Apotomo::StatefulWidget.stateful_branches_for(@root).collect {|n| n.name}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "After #hibernate_widget (request) the widget" do
|
31
|
+
should "still have the same ivars" do
|
32
|
+
@mum = PersistentMouse.new('mum', :educate)
|
33
|
+
@mum.controller = @controller ### FIXME: remove that dependency
|
34
|
+
|
35
|
+
@mum.invoke(:educate)
|
36
|
+
|
37
|
+
assert_equal @mum.last_state, :educate
|
38
|
+
assert_equal @mum.who, "the cat"
|
39
|
+
assert_equal @mum.what, "run away"
|
40
|
+
|
41
|
+
@mum = hibernate_widget(@mum)
|
42
|
+
@mum.controller = @controller ### FIXME: remove that dependency
|
43
|
+
|
44
|
+
@mum.invoke(:recap)
|
45
|
+
|
46
|
+
assert_equal @mum.last_state, :recap
|
47
|
+
assert_equal @mum.who, "the cat"
|
48
|
+
assert_equal @mum.what, "run away"
|
49
|
+
end
|
50
|
+
|
51
|
+
should "still have its event_table" do
|
52
|
+
@mum = PersistentMouse.new('mum', :educate)
|
53
|
+
@event = Apotomo::Event.new(:squeak, @mum)
|
54
|
+
@mum.respond_to_event :squeak, :with => :educate
|
55
|
+
|
56
|
+
assert_equal 1, @mum.send(:local_event_handlers, @event).size
|
57
|
+
@mum = hibernate_widget(@mum)
|
58
|
+
assert_equal 1, @mum.send(:local_event_handlers, @event).size
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "freezing and thawing a widget family" do
|
63
|
+
setup do
|
64
|
+
mum_and_kid!
|
65
|
+
@storage = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
context "and calling #flush_storage" do
|
69
|
+
should "clear the storage from frozen data" do
|
70
|
+
@root = Apotomo::Widget.new('root', :eat)
|
71
|
+
@root << @mum
|
72
|
+
|
73
|
+
Apotomo::StatefulWidget.freeze_for(@storage, @root)
|
74
|
+
|
75
|
+
assert @storage[:apotomo_stateful_branches]
|
76
|
+
assert @storage[:apotomo_widget_ivars]
|
77
|
+
|
78
|
+
Apotomo::StatefulWidget.flush_storage(@storage)
|
79
|
+
|
80
|
+
assert_nil @storage[:apotomo_stateful_branches]
|
81
|
+
assert_nil @storage[:apotomo_widget_ivars]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
should "push @mum's freezable ivars to the storage when calling #freeze_ivars_to" do
|
86
|
+
@mum.freeze_ivars_to(@storage)
|
87
|
+
|
88
|
+
assert_equal 1, @storage.size
|
89
|
+
assert_equal 5, @storage['mum'].size
|
90
|
+
end
|
91
|
+
|
92
|
+
should "push family's freezable ivars to the storage when calling #freeze_data_to" do
|
93
|
+
@kid << mouse_mock('pet')
|
94
|
+
@mum.freeze_data_to(@storage)
|
95
|
+
|
96
|
+
assert_equal 3, @storage.size
|
97
|
+
assert_equal 5, @storage['mum'].size
|
98
|
+
assert_equal 5, @storage['mum/kid'].size
|
99
|
+
assert_equal 4, @storage['mum/kid/pet'].size
|
100
|
+
end
|
101
|
+
|
102
|
+
should "push ivars and structure to the storage when calling #freeze_to" do
|
103
|
+
@mum.freeze_to(@storage)
|
104
|
+
assert_equal 2, @storage[:apotomo_widget_ivars].size
|
105
|
+
assert_kind_of Apotomo::StatefulWidget, @storage[:apotomo_root]
|
106
|
+
end
|
107
|
+
|
108
|
+
context "that has also stateless widgets" do
|
109
|
+
setup do
|
110
|
+
@root = Apotomo::Widget.new('root', :eat)
|
111
|
+
@root << mum_and_kid!
|
112
|
+
@root << Apotomo::Widget.new('berry', :eat) << @jerry = mouse_mock('jerry', :eat)
|
113
|
+
@root << Apotomo::Widget.new('tom', :eating)
|
114
|
+
|
115
|
+
Apotomo::StatefulWidget.freeze_for(@storage, @root)
|
116
|
+
end
|
117
|
+
|
118
|
+
should "ignore stateless widgets when calling #freeze_for" do
|
119
|
+
assert_equal(['root/mum', 'root/mum/kid', "root/berry/jerry"], @storage[:apotomo_widget_ivars].keys)
|
120
|
+
end
|
121
|
+
|
122
|
+
should "save stateful branches only" do
|
123
|
+
#@mum.root!
|
124
|
+
#@jerry.root! # disconnect stateful branches.
|
125
|
+
|
126
|
+
assert_equal([[@mum, 'root'], [@jerry, 'berry']], @storage[:apotomo_stateful_branches])
|
127
|
+
assert @storage[:apotomo_stateful_branches].first.first.root?, "mum not disconnected from root"
|
128
|
+
assert @storage[:apotomo_stateful_branches].last.first.root?, "jerry not disconnected from berry"
|
129
|
+
end
|
130
|
+
|
131
|
+
should "attach stateful branches to the tree in thaw_for" do
|
132
|
+
@new_root = Apotomo::Widget.new('root', :eat)
|
133
|
+
@new_root << Apotomo::Widget.new('berry', :eat)
|
134
|
+
assert_equal @new_root, Apotomo::StatefulWidget.thaw_for(@storage, @new_root)
|
135
|
+
|
136
|
+
assert_equal 5, @new_root.size # without tom.
|
137
|
+
end
|
138
|
+
|
139
|
+
should "re-establish ivars recursivly when calling #thaw_for" do
|
140
|
+
@storage[:apotomo_stateful_branches] = Marshal.load(Marshal.dump(@storage[:apotomo_stateful_branches]))
|
141
|
+
|
142
|
+
@new_root = Apotomo::Widget.new('root', :eat)
|
143
|
+
@new_root << Apotomo::Widget.new('berry', :eat)
|
144
|
+
@new_root = Apotomo::StatefulWidget.thaw_for(@storage, @new_root)
|
145
|
+
|
146
|
+
assert_equal :answer_squeak, @new_root['mum'].instance_variable_get(:@start_state)
|
147
|
+
assert_equal :peek, @new_root['mum']['kid'].instance_variable_get(:@start_state)
|
148
|
+
end
|
149
|
+
|
150
|
+
should "raise an exception when thaw_for can't find the branch's parent" do
|
151
|
+
@new_root = Apotomo::Widget.new('dad', :eat)
|
152
|
+
|
153
|
+
assert_raises RuntimeError do
|
154
|
+
Apotomo::StatefulWidget.thaw_for(@storage, @new_root)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
should "clear the fields in the storage when fetching in #thaw_for" do
|
159
|
+
@new_root = Apotomo::Widget.new('root', :eat)
|
160
|
+
@new_root << Apotomo::Widget.new('berry', :eat)
|
161
|
+
|
162
|
+
Apotomo::StatefulWidget.thaw_for(@storage, @new_root)
|
163
|
+
|
164
|
+
assert_nil @storage[:apotomo_stateful_branches]
|
165
|
+
assert_nil @storage[:apotomo_widget_ivars]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
should "update @mum's ivars when calling #thaw_ivars_from" do
|
170
|
+
@mum.instance_variable_set(:@name, "zombie mum")
|
171
|
+
assert_equal 'zombie mum', @mum.name
|
172
|
+
|
173
|
+
@mum.thaw_ivars_from({'zombie mum' => {'@name' => 'mum'}})
|
174
|
+
assert_equal 'mum', @mum.name
|
175
|
+
end
|
176
|
+
|
177
|
+
should "update family's ivars when calling #thaw_data_from" do
|
178
|
+
@kid << @pet = mouse_mock('pet')
|
179
|
+
@kid.instance_variable_set(:@name, "paranoid kid")
|
180
|
+
@pet.instance_variable_set(:@name, "mad dog")
|
181
|
+
assert_equal "paranoid kid", @kid.name
|
182
|
+
|
183
|
+
@mum.thaw_data_from({ "mum/paranoid kid" => {'@name' => 'kid'},
|
184
|
+
"mum/kid/mad dog" => {'@name' => 'pet'}})
|
185
|
+
assert_equal 'kid', @kid.name
|
186
|
+
assert_equal 'pet', @pet.name
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
context "dumping and loading" do
|
193
|
+
setup do
|
194
|
+
@mum = PersistentMouse.new('mum', :eating)
|
195
|
+
@mum << @kid = PersistentMouse.new('kid', :eating)
|
196
|
+
end
|
197
|
+
|
198
|
+
context "a single stateful widget" do
|
199
|
+
should "provide a serialized widget on #node_dump" do
|
200
|
+
assert_equal "mum|PersistenceTest::PersistentMouse|mum", @mum.dump_node
|
201
|
+
assert_equal "kid|PersistenceTest::PersistentMouse|mum", @kid.dump_node
|
202
|
+
end
|
203
|
+
|
204
|
+
should "recover the widget skeleton when invoking self.node_load" do
|
205
|
+
@mum, parent = ::Apotomo::StatefulWidget.load_node(@mum.dump_node)
|
206
|
+
assert_kind_of PersistentMouse, @mum
|
207
|
+
assert_equal 'mum', @mum.name
|
208
|
+
assert_equal 1, @mum.size
|
209
|
+
assert_equal 'mum', parent
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "a stateful widget family" do
|
214
|
+
should "provide the serialized tree on _dump" do
|
215
|
+
assert_equal "mum|PersistenceTest::PersistentMouse|mum\nkid|PersistenceTest::PersistentMouse|mum\n", @mum._dump(10)
|
216
|
+
end
|
217
|
+
|
218
|
+
should "recover the widget tree on _load" do
|
219
|
+
@mum = ::Apotomo::StatefulWidget._load(@mum._dump(10))
|
220
|
+
assert_equal 2, @mum.size
|
221
|
+
assert_equal @mum, @mum['kid'].parent
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "#frozen_widget_in?" do
|
227
|
+
should "return true if a valid widget is passed" do
|
228
|
+
assert_not Apotomo::StatefulWidget.frozen_widget_in?({})
|
229
|
+
assert Apotomo::StatefulWidget.frozen_widget_in?({:apotomo_stateful_branches => [[mouse_mock, 'root']]})
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "#symbolized_instance_variables?" do
|
234
|
+
should "return instance_variables as symbols" do
|
235
|
+
@mum = mouse_mock
|
236
|
+
assert_equal @mum.instance_variables.size, @mum.symbolized_instance_variables.size
|
237
|
+
assert_not @mum.symbolized_instance_variables.find { |ivar| ivar.kind_of? String }
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|