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