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.
Files changed (74) hide show
  1. data/Gemfile +10 -0
  2. data/Gemfile.lock +47 -0
  3. data/README +141 -0
  4. data/README.rdoc +141 -0
  5. data/Rakefile +78 -0
  6. data/TODO +36 -0
  7. data/app/cells/apotomo/child_switch_widget/switch.html.erb +1 -0
  8. data/app/cells/apotomo/child_switch_widget/switch.rhtml +1 -0
  9. data/app/cells/apotomo/deep_link_widget.rb +27 -0
  10. data/app/cells/apotomo/deep_link_widget/setup.html.erb +20 -0
  11. data/app/cells/apotomo/java_script_widget.rb +12 -0
  12. data/app/cells/apotomo/tab_panel_widget.rb +87 -0
  13. data/app/cells/apotomo/tab_panel_widget/display.html.erb +57 -0
  14. data/app/cells/apotomo/tab_widget.rb +18 -0
  15. data/app/cells/apotomo/tab_widget/display.html.erb +1 -0
  16. data/config/routes.rb +3 -0
  17. data/generators/widget/USAGE +15 -0
  18. data/generators/widget/templates/functional_test.rb +8 -0
  19. data/generators/widget/templates/view.html.erb +2 -0
  20. data/generators/widget/templates/view.html.haml +3 -0
  21. data/generators/widget/templates/widget.rb +8 -0
  22. data/generators/widget/widget_generator.rb +34 -0
  23. data/lib/apotomo.rb +59 -0
  24. data/lib/apotomo/caching.rb +37 -0
  25. data/lib/apotomo/container_widget.rb +10 -0
  26. data/lib/apotomo/deep_link_methods.rb +90 -0
  27. data/lib/apotomo/event.rb +9 -0
  28. data/lib/apotomo/event_handler.rb +23 -0
  29. data/lib/apotomo/event_methods.rb +102 -0
  30. data/lib/apotomo/invoke_event_handler.rb +24 -0
  31. data/lib/apotomo/javascript_generator.rb +57 -0
  32. data/lib/apotomo/persistence.rb +139 -0
  33. data/lib/apotomo/proc_event_handler.rb +18 -0
  34. data/lib/apotomo/rails/controller_methods.rb +161 -0
  35. data/lib/apotomo/rails/view_helper.rb +95 -0
  36. data/lib/apotomo/rails/view_methods.rb +7 -0
  37. data/lib/apotomo/request_processor.rb +92 -0
  38. data/lib/apotomo/stateful_widget.rb +8 -0
  39. data/lib/apotomo/transition.rb +46 -0
  40. data/lib/apotomo/tree_node.rb +186 -0
  41. data/lib/apotomo/version.rb +5 -0
  42. data/lib/apotomo/widget.rb +289 -0
  43. data/lib/apotomo/widget_shortcuts.rb +36 -0
  44. data/rails/init.rb +0 -0
  45. data/test/fixtures/application_widget_tree.rb +2 -0
  46. data/test/rails/controller_methods_test.rb +206 -0
  47. data/test/rails/rails_integration_test.rb +99 -0
  48. data/test/rails/view_helper_test.rb +77 -0
  49. data/test/rails/view_methods_test.rb +40 -0
  50. data/test/rails/widget_generator_test.rb +47 -0
  51. data/test/support/assertions_helper.rb +13 -0
  52. data/test/support/test_case_methods.rb +68 -0
  53. data/test/test_helper.rb +77 -0
  54. data/test/unit/apotomo_test.rb +20 -0
  55. data/test/unit/container_test.rb +20 -0
  56. data/test/unit/event_handler_test.rb +67 -0
  57. data/test/unit/event_methods_test.rb +83 -0
  58. data/test/unit/event_test.rb +30 -0
  59. data/test/unit/invoke_test.rb +123 -0
  60. data/test/unit/javascript_generator_test.rb +90 -0
  61. data/test/unit/onfire_integration_test.rb +19 -0
  62. data/test/unit/persistence_test.rb +240 -0
  63. data/test/unit/render_test.rb +203 -0
  64. data/test/unit/request_processor_test.rb +178 -0
  65. data/test/unit/stateful_widget_test.rb +135 -0
  66. data/test/unit/test_addressing.rb +111 -0
  67. data/test/unit/test_caching.rb +54 -0
  68. data/test/unit/test_jump_to_state.rb +89 -0
  69. data/test/unit/test_tab_panel.rb +72 -0
  70. data/test/unit/test_widget_shortcuts.rb +45 -0
  71. data/test/unit/transition_test.rb +33 -0
  72. data/test/unit/widget_shortcuts_test.rb +68 -0
  73. data/test/unit/widget_test.rb +24 -0
  74. metadata +215 -0
@@ -0,0 +1,203 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. test_helper])
2
+
3
+ class RenderTest < ActionView::TestCase
4
+ context "Rendering a single widget" do
5
+ setup do
6
+ @mum = mouse_mock
7
+ end
8
+
9
+ should "per default display the state content framed in a div" do
10
+ assert_equal '<div id="mouse">burp!</div>', @mum.invoke(:eating)
11
+ end
12
+
13
+ context "with :text" do
14
+ setup do
15
+ @mum.instance_eval { def eating; render :text => "burp!!!"; end }
16
+ end
17
+
18
+ should "render the :text" do
19
+ assert_equal "burp!!!", @mum.invoke
20
+ end
21
+ end
22
+
23
+
24
+ context "with :suppress_js" do
25
+ setup do
26
+ @mum.instance_eval do
27
+ def snuggle; render; end
28
+ self.class.send :attr_reader, :suppress_js
29
+ end
30
+ end
31
+
32
+ should "per default be false" do
33
+ @mum.invoke :snuggle
34
+ assert !@mum.suppress_js
35
+ end
36
+
37
+ should "be true when set" do
38
+ @mum.instance_eval do
39
+ def snuggle; render :suppress_js => true; end
40
+ end
41
+ @mum.invoke :snuggle
42
+ assert @mum.suppress_js
43
+ end
44
+ end
45
+
46
+ should "expose its instance variables in the rendered view" do
47
+ @mum = mouse_mock('mum', :educate) do
48
+ def educate
49
+ @who = "the cat"
50
+ @what = "run away"
51
+ render
52
+ end
53
+ end
54
+ assert_equal 'If you see the cat do run away!', @mum.invoke(:educate)
55
+ end
56
+
57
+ context "with #emit" do
58
+ setup do
59
+ @kid = mouse_mock('kid', :squeak)
60
+ @kid.instance_eval do
61
+ def squeak
62
+ render :text => "squeeeeaaak"
63
+ end
64
+
65
+ def render(*)
66
+ @rendered = true
67
+ super
68
+ end
69
+ def rendered?; @rendered; end
70
+ end
71
+ end
72
+
73
+ context "and :text" do
74
+ setup do
75
+ @mum.instance_eval do
76
+ def squeak
77
+ emit :text => "squeak();"
78
+ end
79
+ end
80
+ end
81
+
82
+ should "just return the plain :text" do
83
+ assert_equal 'squeak();', @mum.invoke(:squeak)
84
+ end
85
+
86
+ should "not render children" do
87
+ @mum << @kid
88
+ @mum.invoke(:squeak)
89
+
90
+ assert_not @kid.rendered?
91
+ end
92
+
93
+ should "allow rendering children" do
94
+ @mum.instance_eval do
95
+ def squeak
96
+ emit :text => "squeak();", :render_children => true
97
+ end
98
+ end
99
+ @mum << @kid
100
+ @mum.invoke(:squeak)
101
+
102
+ assert @kid.rendered?
103
+ end
104
+ end
105
+
106
+ context "and no options" do
107
+ setup do
108
+ @mum.instance_eval do
109
+ def squeak
110
+ emit
111
+ end
112
+ end
113
+ end
114
+
115
+ should "render the view" do
116
+ assert_equal "<div id=\"mouse\">burp!</div>", @mum.invoke(:eating)
117
+ end
118
+
119
+ should "render the children, too" do
120
+ @mum << @kid
121
+ @mum.invoke(:eating)
122
+ assert @kid.rendered?
123
+ end
124
+ end
125
+
126
+ context "and :view" do
127
+ setup do
128
+ @mum.instance_eval do
129
+ def squeak
130
+ emit :view => :snuggle
131
+ end
132
+ end
133
+ end
134
+
135
+ should "render the :view" do
136
+ assert_equal "<div id=\"mouse\"><snuggle></snuggle></div>", @mum.invoke(:squeak)
137
+ end
138
+
139
+ should "render the children" do
140
+ @mum << @kid
141
+
142
+ assert_equal "<div id=\"mouse\"><snuggle>squeeeeaaak</snuggle></div>", @mum.invoke(:squeak)
143
+ assert @kid.rendered?
144
+ end
145
+ end
146
+ end
147
+
148
+ context "with #update" do
149
+ setup do
150
+ Apotomo.js_framework = :prototype
151
+ end
152
+
153
+ should "wrap the :text in an update statement" do
154
+ @mum.instance_eval do
155
+ def squeak
156
+ update :text => "squeak!"
157
+ end
158
+ end
159
+ assert_equal "$(\"mouse\").update(\"squeak!\")", @mum.invoke(:squeak)
160
+ end
161
+ end
162
+
163
+ context "with #replace" do
164
+ setup do
165
+ Apotomo.js_framework = :prototype
166
+ end
167
+
168
+ should "wrap the :text in a replace statement" do
169
+ @mum.instance_eval do
170
+ def squeak
171
+ replace :text => '<div id="mum">squeak!</div>'
172
+ end
173
+ end
174
+ assert_equal "$(\"mouse\").replace(\"<div id=\\\"mum\\\">squeak!<\\/div>\")", @mum.invoke(:squeak)
175
+ end
176
+ end
177
+ end
178
+
179
+ context "rendering a widget family" do
180
+ setup do
181
+ @mum = mouse_mock('mum', :snuggle) do
182
+ def snuggle; render; end
183
+ end
184
+
185
+ @mum << @kid = mouse_mock('kid')
186
+ end
187
+
188
+ should "per default render kid's content inside mums div with rendered_children" do
189
+ assert_equal '<div id="mum"><snuggle><div id="kid">burp!</div></snuggle></div>', @mum.invoke(:snuggle)
190
+ end
191
+
192
+ should "skip kids if :render_children=>false but still provide a rendered_children hash" do
193
+ @mum.instance_eval do
194
+ def snuggle; render :render_children => false; end
195
+ end
196
+
197
+ assert_equal '<div id="mum"><snuggle></snuggle></div>', @mum.invoke(:snuggle)
198
+ end
199
+
200
+ should_eventually "provide an ordered rendered_children hash"
201
+ end
202
+
203
+ end
@@ -0,0 +1,178 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. test_helper])
2
+
3
+ class RequestProcessorTest < Test::Unit::TestCase
4
+ context "#root" do
5
+ should "allow external modification of the tree" do
6
+ @processor = Apotomo::RequestProcessor.new({})
7
+ root = @processor.root
8
+ root << mouse_mock
9
+ assert_equal 2, @processor.root.size
10
+ end
11
+ end
12
+
13
+ context "option processing at construction time" do
14
+ context "with empty session and options" do
15
+ setup do
16
+ @processor = Apotomo::RequestProcessor.new({})
17
+ end
18
+
19
+ should "mark the tree as flushed" do
20
+ assert @processor.widgets_flushed?
21
+ end
22
+
23
+ should "provide a single root-node for #root" do
24
+ assert_equal 1, @processor.root.size
25
+ end
26
+
27
+ should "initialize version to 0" do
28
+ assert_equal 0, @processor.root.version
29
+ end
30
+ end
31
+
32
+ context "with session" do
33
+ setup do
34
+ mum_and_kid!
35
+ @mum.version = 1
36
+ @session = {:apotomo_stateful_branches => [[@mum, 'root']]}
37
+ @processor = Apotomo::RequestProcessor.new(@session)
38
+ end
39
+
40
+ should "provide a widget family for #root" do
41
+ assert_equal 3, @processor.root.size
42
+ assert_equal 1, @processor.root['mum'].version
43
+ assert_not @processor.widgets_flushed?
44
+ end
45
+
46
+ context "having a flush flag set" do
47
+ setup do
48
+ @processor = Apotomo::RequestProcessor.new(@session, :flush_widgets => true)
49
+ end
50
+
51
+ should "provide a single root for #root when :flush_widgets is set" do
52
+ assert_equal 1, @processor.root.size
53
+ assert @processor.widgets_flushed?
54
+ end
55
+
56
+ should "wipe-out our session variables" do
57
+ assert_nil @session[:apotomo_stateful_branches]
58
+ assert_nil @session[:apotomo_widget_ivars]
59
+ end
60
+
61
+ should ""
62
+ end
63
+
64
+ context "and with stateless widgets" do
65
+ setup do
66
+ @session = {:apotomo_stateful_branches => [[@mum, 'grandma']]}
67
+ @processor = Apotomo::RequestProcessor.new(@session, {}, [Proc.new { |root| root << Apotomo::Widget.new('grandma', :eating) }])
68
+ end
69
+
70
+ should "first attach passed stateless, then stateful widgets to root" do
71
+ assert_equal 4, @processor.root.size
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ context "#process_for" do
79
+ setup do
80
+ ### FIXME: what about that automatic @controller everywhere?
81
+ mum_and_kid!
82
+ @mum.controller = nil # check if controller gets connected.
83
+ @processor = Apotomo::RequestProcessor.new({:apotomo_stateful_branches => [[@mum, 'root']]}, :js_framework => :prototype)
84
+
85
+
86
+
87
+
88
+
89
+ @kid.respond_to_event :doorSlam, :with => :eating, :on => 'mum'
90
+ @kid.respond_to_event :doorSlam, :with => :squeak
91
+ @mum.respond_to_event :doorSlam, :with => :squeak
92
+
93
+ @mum.instance_eval do
94
+ def squeak; render :js => 'squeak();'; end
95
+ end
96
+ @kid.instance_eval do
97
+ def squeak; render :text => 'squeak!', :update => :true; end
98
+ end
99
+ end
100
+
101
+ should "return 2 page_updates when @kid squeaks" do
102
+ res = @processor.process_for({:type => :squeak, :source => 'kid'}, @controller)
103
+
104
+ assert_equal ["alert!", "squeak"], res
105
+ end
106
+
107
+ should "raise an exception when :source is unknown" do
108
+ assert_raises RuntimeError do
109
+ @processor.process_for({:type => :squeak, :source => 'tom'}, @controller)
110
+ end
111
+ end
112
+ end
113
+
114
+
115
+
116
+ context "#freeze!" do
117
+ should "serialize stateful branches to @session" do
118
+ @processor = Apotomo::RequestProcessor.new({})
119
+ @processor.root << mum_and_kid!
120
+ assert_equal 3, @processor.root.size
121
+ @processor.freeze!
122
+
123
+ @processor = Apotomo::RequestProcessor.new(@processor.session)
124
+ assert_equal 3, @processor.root.size
125
+ end
126
+ end
127
+
128
+ context "#render_widget_for" do
129
+ setup do
130
+ @mum = mouse_mock('mum', :snuggle) do
131
+ def snuggle; render; end
132
+ end
133
+ @mum.controller = nil
134
+
135
+ @processor = Apotomo::RequestProcessor.new({:apotomo_stateful_branches => [[@mum, 'root']]})
136
+ end
137
+
138
+ should "render the widget when passing an existing widget id" do
139
+ assert_equal '<div id="mum"><snuggle></snuggle></div>', @processor.render_widget_for('mum', {}, @controller)
140
+ end
141
+
142
+ should "render the widget when passing an existing widget instance" do
143
+ assert_equal '<div id="mum"><snuggle></snuggle></div>', @processor.render_widget_for(@mum, {}, @controller)
144
+ end
145
+
146
+ should "raise an exception when a non-existent widget id id passed" do
147
+ assert_raises RuntimeError do
148
+ @processor.render_widget_for('mummy', {}, @controller)
149
+ end
150
+ end
151
+ end
152
+
153
+ context "invoking #address_for" do
154
+ setup do
155
+ @processor = Apotomo::RequestProcessor.new({})
156
+ end
157
+
158
+ should "accept an event :type" do
159
+ assert_equal({:type => :squeak, :source => 'mum'}, @processor.address_for(:type => :squeak, :source => 'mum'))
160
+ end
161
+
162
+ should "accept arbitrary options" do
163
+ assert_equal({:type => :squeak, :volume => 'loud', :source => 'mum'}, @processor.address_for(:type => :squeak, :volume => 'loud', :source => 'mum'))
164
+ end
165
+
166
+ should "complain if no type given" do
167
+ assert_raises RuntimeError do
168
+ @processor.address_for(:source => 'mum')
169
+ end
170
+ end
171
+
172
+ should "complain if no source given" do
173
+ assert_raises RuntimeError do
174
+ @processor.address_for(:type => :footsteps)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,135 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class StatefulWidgetTest < Test::Unit::TestCase
4
+ context "The StatefulWidget" do
5
+ setup do
6
+ @mum = Apotomo::StatefulWidget.new('mum', :squeak)
7
+ end
8
+
9
+ should "accept an id as first option" do
10
+ assert_equal 'mum', @mum.name
11
+ end
12
+
13
+ should "accept a start state as second option" do
14
+ assert_equal :squeak, @mum.instance_variable_get('@start_state')
15
+ end
16
+
17
+ should "respond to #version" do
18
+ assert_equal 0, mouse_mock.version
19
+ end
20
+
21
+ should "have a version setter" do
22
+ @mum = mouse_mock
23
+ @mum.version = 1
24
+ assert_equal 1, @mum.version
25
+ end
26
+
27
+ context "responding to #address_for_event" do
28
+ should "accept an event :type" do
29
+ assert_equal({:type => :squeak, :source => 'mum'}, @mum.address_for_event(:type => :squeak))
30
+ end
31
+
32
+ should "accept a :source" do
33
+ assert_equal({:type => :squeak, :source => 'kid'}, @mum.address_for_event(:type => :squeak, :source => 'kid'))
34
+ end
35
+
36
+ should "accept arbitrary options" do
37
+ assert_equal({:type => :squeak, :volume => 'loud', :source => 'mum'}, @mum.address_for_event(:type => :squeak, :volume => 'loud'))
38
+ end
39
+
40
+ should "complain if no type given" do
41
+ assert_raises RuntimeError do
42
+ @mum.address_for_event(:source => 'mum')
43
+ end
44
+ end
45
+ end
46
+
47
+ context "implementing visibility" do
48
+ should "per default respond to #visible?" do
49
+ assert @mum.visible?
50
+ end
51
+
52
+ should "expose a setter therefore" do
53
+ @mum.visible = false
54
+ assert_not @mum.visible?
55
+ end
56
+
57
+ context "in a widget family" do
58
+ setup do
59
+ @mum << @jerry = mouse_mock('jerry')
60
+ @mum << @berry = mouse_mock('berry')
61
+ end
62
+
63
+ should "per default return all #visible_children" do
64
+ assert_equal [@jerry, @berry], @mum.visible_children
65
+ assert_equal [], @jerry.visible_children
66
+ end
67
+
68
+ should "hide berry in #visible_children if he's invisible" do
69
+ @berry.visible = false
70
+ assert_equal [@jerry], @mum.visible_children
71
+ end
72
+ end
73
+ end
74
+
75
+ should "respond to #find_widget" do
76
+ mum_and_kid!
77
+ assert_not @mum.find_widget('pet')
78
+ assert @kid, @mum.find_widget('kid')
79
+ end
80
+
81
+ should "respond to the WidgetShortcuts methods, like #widget" do
82
+ assert_respond_to @mum, :widget
83
+ end
84
+
85
+ context "with initialize_hooks" do
86
+ should "expose its class_inheritable_array with #initialize_hooks" do
87
+ @mum = mouse_class_mock.new('mum', :eating)
88
+ @mum.class.instance_eval { self.initialize_hooks << :initialize_mouse }
89
+ assert ::Apotomo::StatefulWidget.initialize_hooks.size + 1 == @mum.class.initialize_hooks.size
90
+ end
91
+
92
+ should "execute the initialize_hooks in the correct order in #process_initialize_hooks" do
93
+ @mum = mouse_class_mock.new('mum', :eating)
94
+ @mum.class.instance_eval do
95
+ define_method(:executed) { |*args| @executed ||= [] }
96
+ define_method(:setup) { |*args| executed << :setup }
97
+ define_method(:configure) { |*args| executed << :configure }
98
+ initialize_hooks << :setup
99
+ initialize_hooks << :configure
100
+ end
101
+
102
+ assert_equal [:setup, :configure], @mum.class.new('zombie', nil).executed
103
+ end
104
+ end
105
+ end
106
+
107
+ context "mum having a family" do
108
+ setup do
109
+ mum_and_kid!
110
+ @mum << @berry = mouse_mock('berry')
111
+ @berry << @pet = mouse_mock('pet')
112
+ end
113
+
114
+ context "responding to #render_children" do
115
+ should "return an OrderedHash for the rendered kids" do
116
+ kids = @mum.render_children
117
+ assert_kind_of ::ActiveSupport::OrderedHash, kids
118
+ assert_equal 2, kids.size
119
+ end
120
+
121
+ should "return an OrderedHash even if there are no kids" do
122
+ kids = @kid.render_children
123
+ assert_kind_of ::ActiveSupport::OrderedHash, kids
124
+ assert_equal 0, kids.size
125
+ end
126
+
127
+ should "return an empty OrderedHash when all kids are invisible" do
128
+ @pet.visible = false
129
+ kids = @berry.render_children
130
+ assert_kind_of ::ActiveSupport::OrderedHash, kids
131
+ assert_equal 0, kids.size
132
+ end
133
+ end
134
+ end
135
+ end