apotomo 1.0.0.beta2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/README.rdoc +30 -12
- data/Rakefile +2 -2
- data/config/routes.rb +1 -1
- data/lib/apotomo/rails/view_helper.rb +1 -2
- data/lib/apotomo/request_processor.rb +1 -12
- data/lib/apotomo/stateful_widget.rb +22 -1
- data/lib/apotomo/test_case.rb +105 -0
- data/lib/apotomo/version.rb +1 -3
- data/lib/apotomo/widget.rb +10 -55
- data/lib/apotomo.rb +16 -2
- data/lib/generators/apotomo/templates/widget_test.rb +6 -5
- data/lib/tasks.rake +6 -0
- data/test/rails/widget_generator_test.rb +1 -0
- data/test/test_helper.rb +4 -17
- data/test/unit/request_processor_test.rb +0 -4
- data/test/unit/test_case_test.rb +95 -0
- data/test/unit/widget_test.rb +3 -9
- metadata +105 -110
- data/lib/apotomo/caching.rb +0 -37
- data/lib/apotomo/deep_link_methods.rb +0 -90
- data/test/unit/test_caching.rb +0 -53
- data/test/unit/test_widget_shortcuts.rb +0 -44
data/Gemfile
CHANGED
@@ -3,7 +3,7 @@ source "http://rubygems.org"
|
|
3
3
|
gem "rails", "~> 3.0.0"
|
4
4
|
gem "cells", "~> 3.4"
|
5
5
|
gem "onfire"
|
6
|
-
gem "hooks", "~> 0.1.
|
6
|
+
gem "hooks", "~> 0.1.3"
|
7
7
|
|
8
8
|
|
9
9
|
gem "jeweler"
|
@@ -12,3 +12,4 @@ gem "jeweler"
|
|
12
12
|
gem "shoulda"
|
13
13
|
gem "mocha"
|
14
14
|
gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3' # needed in router_test, whatever.
|
15
|
+
gem "capybara"
|
data/README.rdoc
CHANGED
@@ -22,7 +22,7 @@ Easy as hell.
|
|
22
22
|
|
23
23
|
=== Rails 3
|
24
24
|
|
25
|
-
gem install apotomo
|
25
|
+
gem install apotomo --pre
|
26
26
|
|
27
27
|
=== Rails 2.3
|
28
28
|
|
@@ -42,12 +42,12 @@ Let's wrap that comments block in a widget.
|
|
42
42
|
|
43
43
|
Go and generate a widget stub.
|
44
44
|
|
45
|
-
$ rails generate apotomo:widget CommentsWidget display
|
45
|
+
$ rails generate apotomo:widget CommentsWidget display write --haml
|
46
46
|
create app/cells/
|
47
47
|
create app/cells/comments_widget
|
48
48
|
create app/cells/comments_widget.rb
|
49
49
|
create app/cells/comments_widget/display.html.haml
|
50
|
-
create app/cells/comments_widget/
|
50
|
+
create app/cells/comments_widget/write.html.haml
|
51
51
|
create test/widgets/comments_widget_test.rb
|
52
52
|
|
53
53
|
Nothing special.
|
@@ -60,10 +60,10 @@ You now tell your controller about the new widget.
|
|
60
60
|
include Apotomo::Rails::ControllerMethods
|
61
61
|
|
62
62
|
has_widgets do |root|
|
63
|
-
root << widget('comments_widget', 'post-comments', :
|
63
|
+
root << widget('comments_widget', 'post-comments', :post => @post)
|
64
64
|
end
|
65
65
|
|
66
|
-
The widget is named <tt>post-comments</tt
|
66
|
+
The widget is named <tt>post-comments</tt>. We pass the current post into the widget - the block is executed in controller instance context, that's were <tt>@post</tt> comes from. Handy, isn't it?
|
67
67
|
|
68
68
|
== Render the widget
|
69
69
|
|
@@ -82,18 +82,18 @@ Rendering usually happens in your controller view, <tt>views/posts/show.haml</tt
|
|
82
82
|
A widget is like a cell which is like a mini-controller.
|
83
83
|
|
84
84
|
class CommentsWidget < Apotomo::Widget
|
85
|
-
responds_to_event :submit, :with => :
|
85
|
+
responds_to_event :submit, :with => :write
|
86
86
|
|
87
87
|
def display
|
88
88
|
@comments = param(:post).comments # the parameter from outside.
|
89
89
|
render
|
90
90
|
end
|
91
91
|
|
92
|
-
|
92
|
+
Having +display+ as the default state when rendering, this method collects comments to show and renders its view.
|
93
93
|
|
94
|
-
And look at line 2 - if encountering a <tt>:submit</tt> event we invoke +
|
94
|
+
And look at line 2 - if encountering a <tt>:submit</tt> event we invoke +write+, which is simply another state. How cool is that?
|
95
95
|
|
96
|
-
def
|
96
|
+
def write
|
97
97
|
@comment = Comment.new(:post => param(:post))
|
98
98
|
@comment.update_attributes param(:comment) # like params[].
|
99
99
|
|
@@ -160,7 +160,7 @@ Look, +replace+ basically generates
|
|
160
160
|
|
161
161
|
If that's not what you want, do
|
162
162
|
|
163
|
-
def
|
163
|
+
def write
|
164
164
|
if param(:comment)[:text].explicit?
|
165
165
|
render :text => 'alert("Hey, you wanted to submit a pervert comment!");'
|
166
166
|
end
|
@@ -168,6 +168,25 @@ If that's not what you want, do
|
|
168
168
|
|
169
169
|
Apotomo doesn't depend on _any_ JS framework - you choose!
|
170
170
|
|
171
|
+
== Testing
|
172
|
+
|
173
|
+
Apotomo comes with its own test case and assertions to <b>build rock-solid web components</b>.
|
174
|
+
|
175
|
+
class CommentsWidgetTest < Apotomo::TestCase
|
176
|
+
has_widgets do |root|
|
177
|
+
root << widget(:comments_widget, 'me', :post => @pervert_post)
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_render
|
181
|
+
render_widget 'me'
|
182
|
+
assert_select "li#me"
|
183
|
+
|
184
|
+
trigger :submit, :comment => {:text => "Sex on the beach"}
|
185
|
+
assert_response 'alert("Hey, you wanted to submit a pervert comment!");'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
You can render your widgets, spec the markup, trigger events and assert the event responses, so far. If you need more, let us know!
|
171
190
|
|
172
191
|
== More features
|
173
192
|
|
@@ -175,8 +194,7 @@ There's even more, too much for a simple README.
|
|
175
194
|
|
176
195
|
[Statefulness] Deriving your widget from +StatefulWidget+ gives you free statefulness.
|
177
196
|
[Composability] Widgets can range from small standalone components to nested widget trees like complex dashboards.
|
178
|
-
[Bubbling events] Events bubble up from their triggering source to root and thus can be observed, providing a way to implement loosely coupled, distributable components.
|
179
|
-
[Testing] Apotomo comes with testing assertions to build rock-solid web components.
|
197
|
+
[Bubbling events] Events bubble up from their triggering source to root and thus can be observed, providing a way to implement loosely coupled, distributable components.
|
180
198
|
[Team-friendly] Widgets encourage encapsulation and help having different developers working on different components without getting out of bounds.
|
181
199
|
|
182
200
|
|
data/Rakefile
CHANGED
@@ -25,7 +25,7 @@ Jeweler::Tasks.new do |spec|
|
|
25
25
|
spec.name = "apotomo"
|
26
26
|
spec.version = ::Apotomo::VERSION
|
27
27
|
spec.summary = %{Web components for Rails.}
|
28
|
-
spec.description = "Web
|
28
|
+
spec.description = "Web component framework for Rails providing page widgets that trigger events and know when and how to update themselves with AJAX."
|
29
29
|
spec.homepage = "http://apotomo.de"
|
30
30
|
spec.authors = ["Nick Sutterer"]
|
31
31
|
spec.email = "apotonick@gmail.com"
|
@@ -36,7 +36,7 @@ Jeweler::Tasks.new do |spec|
|
|
36
36
|
spec.add_dependency 'cells', '~> 3.4.2'
|
37
37
|
spec.add_dependency 'rails', '>= 3.0.0'
|
38
38
|
spec.add_dependency 'onfire', '>= 0.1.0'
|
39
|
-
spec.add_dependency 'hooks', '~> 0.1.
|
39
|
+
spec.add_dependency 'hooks', '~> 0.1.3'
|
40
40
|
end
|
41
41
|
|
42
42
|
Jeweler::GemcutterTasks.new
|
data/config/routes.rb
CHANGED
@@ -53,8 +53,7 @@ module Apotomo
|
|
53
53
|
# url_for_event(:paginate, :page => 2)
|
54
54
|
# #=> http://apotomo.de/mouse/process_event_request?type=paginate&source=mouse&page=2
|
55
55
|
def url_for_event(type, options={})
|
56
|
-
|
57
|
-
@cell.url_for_event(type, options) # FIXME: don't access @parent_controller but @cell.
|
56
|
+
@cell.url_for_event(type, options)
|
58
57
|
end
|
59
58
|
|
60
59
|
### TODO: test me.
|
@@ -17,8 +17,6 @@ module Apotomo
|
|
17
17
|
|
18
18
|
flushed_root ### FIXME: set internal mode to flushed
|
19
19
|
end
|
20
|
-
|
21
|
-
#handle_version!(options[:version])
|
22
20
|
end
|
23
21
|
|
24
22
|
def attach_stateless_blocks_for(blocks, root, controller)
|
@@ -30,16 +28,7 @@ module Apotomo
|
|
30
28
|
@widgets_flushed = true
|
31
29
|
#widget('apotomo/widget', 'root')
|
32
30
|
end
|
33
|
-
|
34
|
-
### DISCUSS: do we need the version feature, or should we push that into user code?
|
35
|
-
def handle_version!(version)
|
36
|
-
return if version.blank?
|
37
|
-
return if root.version == version
|
38
|
-
|
39
|
-
@root = flushed_root
|
40
|
-
@root.version = version
|
41
|
-
end
|
42
|
-
|
31
|
+
|
43
32
|
def widgets_flushed?; @widgets_flushed; end
|
44
33
|
|
45
34
|
# Fires the request event in the widget tree and collects the rendered page updates.
|
@@ -4,5 +4,26 @@ require 'apotomo/persistence'
|
|
4
4
|
module Apotomo
|
5
5
|
class StatefulWidget < Widget
|
6
6
|
include Persistence
|
7
|
+
|
8
|
+
attr_accessor :version
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@version = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Defines the instance vars that should <em>not</em> survive between requests,
|
17
|
+
# which means they're not frozen in Apotomo::StatefulWidget#freeze.
|
18
|
+
def ivars_to_forget
|
19
|
+
unfreezable_ivars
|
20
|
+
end
|
21
|
+
|
22
|
+
def unfreezable_ivars
|
23
|
+
[:@childrenHash, :@children, :@parent, :@parent_controller, :@_request, :@_config, :@cell, :@invoke_block, :@rendered_children, :@page_updates, :@opts, :@params,
|
24
|
+
:@suppress_javascript ### FIXME: implement with ActiveHelper and :locals.
|
25
|
+
|
26
|
+
]
|
27
|
+
end
|
7
28
|
end
|
8
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'cell/test_case'
|
2
|
+
|
3
|
+
module Apotomo
|
4
|
+
# Testing is fun. Test your widgets!
|
5
|
+
#
|
6
|
+
# This class helps you testing widgets where it can. It is similar as in a controller.
|
7
|
+
# A declarative test would look like
|
8
|
+
#
|
9
|
+
# class BlogWidgetTest < Apotomo::TestCase
|
10
|
+
# has_widgets do |root|
|
11
|
+
# root << widget(:comments_widget, 'post-comments')
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# it "should be rendered nicely" do
|
15
|
+
# render_widget 'post-comments'
|
16
|
+
#
|
17
|
+
# assert_select "div#post-comments", "Comments for this post"
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# it "should redraw on :update" do
|
21
|
+
# trigger :update
|
22
|
+
# assert_response "$(\"post-comments\").update ..."
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# For unit testing, you can grab an instance of your tested widget.
|
26
|
+
#
|
27
|
+
# it "should be visible" do
|
28
|
+
# assert root['post-comments'].visible?
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# See also in Cell::TestCase.
|
32
|
+
class TestCase < Cell::TestCase
|
33
|
+
class << self
|
34
|
+
def has_widgets_blocks; @has_widgets; end
|
35
|
+
|
36
|
+
# Setup a widget tree as you're used to it from your controller. Executed in test context.
|
37
|
+
def has_widgets(&block)
|
38
|
+
@has_widgets = block # DISCUSS: use ControllerMethods?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup
|
43
|
+
super
|
44
|
+
@controller.instance_eval do
|
45
|
+
def controller_name
|
46
|
+
'barn'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@controller.extend Apotomo::Rails::ControllerMethods
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
# Returns the widget tree from TestCase.has_widgets.
|
54
|
+
def root
|
55
|
+
blk = self.class.has_widgets_blocks or raise "Please setup a widget tree using TestCase.has_widgets"
|
56
|
+
@root ||= widget("apotomo/widget", "root").tap do |root|
|
57
|
+
self.instance_exec(root, &blk)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def parent_controller
|
62
|
+
@controller
|
63
|
+
end
|
64
|
+
|
65
|
+
# Renders the widget +name+.
|
66
|
+
def render_widget(name, options={})
|
67
|
+
@last_invoke = root.find_widget(name).tap { |w| w.opts = options }.invoke # DISCUSS: use ControllerMethods?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Triggers an event of +type+. You have to pass <tt>:source</tt> as options.
|
71
|
+
#
|
72
|
+
# Example:
|
73
|
+
#
|
74
|
+
# trigger :submit, :source => "post-comments"
|
75
|
+
def trigger(type, options)
|
76
|
+
source = root.find_widget(options.delete(:source))
|
77
|
+
source.instance_variable_set :@params, options # TODO: this is just a try-out (what about children?).
|
78
|
+
source.fire(type)
|
79
|
+
root.page_updates # DISCUSS: use ControllerMethods?
|
80
|
+
end
|
81
|
+
|
82
|
+
# After a #trigger this assertion compares the actually triggered page updates with the passed.
|
83
|
+
#
|
84
|
+
# Example:
|
85
|
+
#
|
86
|
+
# trigger :submit, :source => "post-comments"
|
87
|
+
# assert_response "alert(\":submit clicked!\")", /\$\("post-comments"\).update/
|
88
|
+
def assert_response(*content)
|
89
|
+
updates = root.page_updates
|
90
|
+
|
91
|
+
i = 0
|
92
|
+
content.each do |assertion|
|
93
|
+
if assertion.kind_of? Regexp
|
94
|
+
assert_match assertion, updates[i]
|
95
|
+
else
|
96
|
+
assert_equal assertion, updates[i]
|
97
|
+
end
|
98
|
+
|
99
|
+
i+=1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
include Apotomo::WidgetShortcuts
|
104
|
+
end
|
105
|
+
end
|
data/lib/apotomo/version.rb
CHANGED
data/lib/apotomo/widget.rb
CHANGED
@@ -6,7 +6,6 @@ require 'apotomo/tree_node'
|
|
6
6
|
require 'apotomo/event'
|
7
7
|
require 'apotomo/event_methods'
|
8
8
|
require 'apotomo/transition'
|
9
|
-
require 'apotomo/caching'
|
10
9
|
require 'apotomo/widget_shortcuts'
|
11
10
|
require 'apotomo/rails/view_helper'
|
12
11
|
|
@@ -31,9 +30,8 @@ module Apotomo
|
|
31
30
|
|
32
31
|
attr_accessor :opts
|
33
32
|
attr_writer :visible
|
34
|
-
|
33
|
+
|
35
34
|
attr_writer :controller
|
36
|
-
attr_accessor :version
|
37
35
|
|
38
36
|
include TreeNode
|
39
37
|
|
@@ -41,7 +39,6 @@ module Apotomo
|
|
41
39
|
include EventMethods
|
42
40
|
|
43
41
|
include Transition
|
44
|
-
include Caching
|
45
42
|
include WidgetShortcuts
|
46
43
|
|
47
44
|
helper Apotomo::Rails::ViewHelper
|
@@ -66,13 +63,11 @@ module Apotomo
|
|
66
63
|
@start_state = start_state
|
67
64
|
|
68
65
|
@visible = true
|
69
|
-
@version = 0 ### DISCUSS: neeed in stateLESS?
|
70
|
-
|
71
66
|
@cell = self ### DISCUSS: needed?
|
72
67
|
|
73
68
|
@params = parent_controller.params.dup.merge(opts)
|
74
69
|
|
75
|
-
run_hook
|
70
|
+
run_hook :after_initialize, self
|
76
71
|
end
|
77
72
|
|
78
73
|
def last_state
|
@@ -82,29 +77,6 @@ module Apotomo
|
|
82
77
|
def visible?
|
83
78
|
@visible
|
84
79
|
end
|
85
|
-
|
86
|
-
# Defines the instance vars that should <em>not</em> survive between requests,
|
87
|
-
# which means they're not frozen in Apotomo::StatefulWidget#freeze.
|
88
|
-
def ivars_to_forget
|
89
|
-
unfreezable_ivars
|
90
|
-
end
|
91
|
-
|
92
|
-
def unfreezable_ivars
|
93
|
-
[:@childrenHash, :@children, :@parent, :@parent_controller, :@_request, :@_config, :@cell, :@invoke_block, :@rendered_children, :@page_updates, :@opts, :@params,
|
94
|
-
:@suppress_javascript ### FIXME: implement with ActiveHelper and :locals.
|
95
|
-
|
96
|
-
]
|
97
|
-
end
|
98
|
-
|
99
|
-
# Defines the instance vars which should <em>not</em> be copied to the view.
|
100
|
-
# Called in Cell::Base.
|
101
|
-
def ivars_to_ignore
|
102
|
-
[]
|
103
|
-
end
|
104
|
-
|
105
|
-
### FIXME:
|
106
|
-
def logger; self; end
|
107
|
-
def debug(*args); puts args; end
|
108
80
|
|
109
81
|
# Returns the rendered content for the widget by running the state method for <tt>state</tt>.
|
110
82
|
# This might lead us to some other state since the state method could call #jump_to_state.
|
@@ -256,36 +228,19 @@ module Apotomo
|
|
256
228
|
end
|
257
229
|
|
258
230
|
|
259
|
-
# Returns the address hash to the event controller and the targeted widget.
|
260
|
-
#
|
261
|
-
# Reserved options for <tt>way</tt>:
|
262
|
-
# :source explicitly specifies an event source.
|
263
|
-
# The default is to take the current widget as source.
|
264
|
-
# :type specifies the event type.
|
265
|
-
#
|
266
|
-
# Any other option will be directly passed into the address hash and is
|
267
|
-
# available via StatefulWidget#param in the widget.
|
268
|
-
#
|
269
|
-
# Can be passed to #url_for.
|
270
|
-
#
|
271
|
-
# Example:
|
272
|
-
# address_for_event :type => :squeak, :volume => 9
|
273
|
-
# will result in an address that triggers a <tt>:click</tt> event from the current
|
274
|
-
# widget and also provides the parameter <tt>:item_id</tt>.
|
275
|
-
def address_for_event(options)
|
276
|
-
raise "please specify the event :type" unless options[:type]
|
277
|
-
|
278
|
-
options[:source] ||= self.name
|
279
|
-
options
|
280
|
-
end
|
281
|
-
|
282
231
|
# Returns the widget named <tt>widget_id</tt> as long as it is below self or self itself.
|
283
232
|
def find_widget(widget_id)
|
284
233
|
find {|node| node.name.to_s == widget_id.to_s}
|
285
234
|
end
|
286
235
|
|
287
|
-
def
|
288
|
-
|
236
|
+
def address_for_event(type, options={})
|
237
|
+
options.reverse_merge! :source => name,
|
238
|
+
:type => type,
|
239
|
+
:controller => parent_controller.controller_name # DISCUSS: dependency to parent_controller.
|
240
|
+
end
|
241
|
+
|
242
|
+
def url_for_event(type, options={})
|
243
|
+
apotomo_event_path address_for_event(type, options)
|
289
244
|
end
|
290
245
|
|
291
246
|
alias_method :widget_id, :name
|
data/lib/apotomo.rb
CHANGED
@@ -20,6 +20,9 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
+
require "rails/railtie"
|
24
|
+
require 'rails/engine'
|
25
|
+
|
23
26
|
module Apotomo
|
24
27
|
class << self
|
25
28
|
def js_framework=(js_framework)
|
@@ -43,13 +46,24 @@ module Apotomo
|
|
43
46
|
|
44
47
|
class Engine < Rails::Engine
|
45
48
|
end
|
49
|
+
|
50
|
+
class Railtie < Rails::Railtie
|
51
|
+
rake_tasks do
|
52
|
+
load "tasks.rake"
|
53
|
+
end
|
54
|
+
end
|
46
55
|
end
|
47
56
|
|
48
|
-
require 'apotomo/javascript_generator'
|
49
|
-
Apotomo.js_framework = :jquery ### DISCUSS: move to rails.rb
|
50
57
|
|
51
58
|
require 'apotomo/widget'
|
52
59
|
require 'apotomo/stateful_widget'
|
53
60
|
require 'apotomo/container_widget'
|
54
61
|
require 'apotomo/widget_shortcuts'
|
55
62
|
require 'apotomo/rails/controller_methods'
|
63
|
+
|
64
|
+
|
65
|
+
require 'apotomo/javascript_generator'
|
66
|
+
Apotomo.js_framework = :jquery ### DISCUSS: move to rails.rb
|
67
|
+
|
68
|
+
### FIXME: only load in test env.
|
69
|
+
require 'apotomo/test_case' #if defined?("Rails") and Rails.env == "test"
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class <%= class_name %>Test < Apotomo::TestCase
|
4
|
-
|
5
|
-
|
6
|
-
invoke :<%= state %>
|
7
|
-
assert_select "p"
|
4
|
+
has_widgets do |root|
|
5
|
+
root << widget(:<%= file_name %>, 'me')
|
8
6
|
end
|
9
7
|
|
10
|
-
|
8
|
+
test "display" do
|
9
|
+
render_widget 'me'
|
10
|
+
assert_select "h1"
|
11
|
+
end
|
11
12
|
end
|
data/lib/tasks.rake
ADDED
@@ -20,6 +20,7 @@ class WidgetGeneratorTest < Rails::Generators::TestCase
|
|
20
20
|
assert_file "app/cells/mouse_widget/squeak.html.erb", %r(app/cells/mouse_widget/squeak\.html\.erb)
|
21
21
|
|
22
22
|
assert_file "test/widgets/mouse_widget_test.rb", %r(class MouseWidgetTest < Apotomo::TestCase)
|
23
|
+
assert_file "test/widgets/mouse_widget_test.rb", %r(widget\(:mouse_widget, 'me'\))
|
23
24
|
end
|
24
25
|
|
25
26
|
should "create haml assets with --haml" do
|
data/test/test_helper.rb
CHANGED
@@ -3,24 +3,16 @@ require 'rubygems'
|
|
3
3
|
require 'bundler'
|
4
4
|
Bundler.setup
|
5
5
|
|
6
|
-
#require 'rubygems'
|
7
6
|
require 'shoulda'
|
8
|
-
require 'mocha'
|
9
|
-
require 'mocha/integration'
|
10
|
-
|
11
|
-
|
12
7
|
require 'cells'
|
13
|
-
Cell::Base.append_view_path File.expand_path(File.dirname(__FILE__) + "/fixtures")
|
14
|
-
|
15
|
-
require 'rails/engine'
|
16
|
-
|
17
8
|
require 'apotomo'
|
18
|
-
require 'apotomo/widget_shortcuts'
|
19
|
-
require 'apotomo/rails/controller_methods'
|
20
|
-
require 'apotomo/rails/view_methods'
|
21
9
|
|
10
|
+
ENV['RAILS_ENV'] = 'test'
|
11
|
+
require "dummy/config/environment"
|
12
|
+
require "rails/test_help" # sets up ActionController::TestCase's @routes
|
22
13
|
|
23
14
|
|
15
|
+
Cell::Base.append_view_path File.expand_path(File.dirname(__FILE__) + "/fixtures")
|
24
16
|
|
25
17
|
# Load test support files.
|
26
18
|
require File.join(File.dirname(__FILE__), "support/test_case_methods")
|
@@ -59,8 +51,3 @@ end
|
|
59
51
|
class Apotomo::Widget
|
60
52
|
def action_method?(*); true; end
|
61
53
|
end
|
62
|
-
|
63
|
-
ENV['RAILS_ENV'] = 'test'
|
64
|
-
require "dummy/config/environment"
|
65
|
-
#require File.join(File.dirname(__FILE__), '..', 'config/routes.rb') ### TODO: let rails engine handle that.
|
66
|
-
require "rails/test_help" # sets up ActionController::TestCase's @routes
|
@@ -59,10 +59,6 @@ class RequestProcessorTest < ActiveSupport::TestCase
|
|
59
59
|
should "provide a single root-node for #root" do
|
60
60
|
assert_equal 1, @processor.root.size
|
61
61
|
end
|
62
|
-
|
63
|
-
should "initialize version to 0" do
|
64
|
-
assert_equal 0, @processor.root.version
|
65
|
-
end
|
66
62
|
end
|
67
63
|
|
68
64
|
context "with controller" do
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'apotomo/test_case'
|
3
|
+
|
4
|
+
class TestCaseTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class CommentsWidgetTest < Apotomo::TestCase
|
7
|
+
end
|
8
|
+
|
9
|
+
class CommentsWidget < Apotomo::Widget
|
10
|
+
end
|
11
|
+
|
12
|
+
context "TestCase" do
|
13
|
+
|
14
|
+
context "responding to #root" do
|
15
|
+
class MouseWidgetTest < Apotomo::TestCase
|
16
|
+
end
|
17
|
+
|
18
|
+
setup do
|
19
|
+
@klass = MouseWidgetTest
|
20
|
+
@test = @klass.new(:widget).tap{ |t| t.setup }
|
21
|
+
@klass.has_widgets { |r| r << widget("mouse_cell", 'mum', :eating) }
|
22
|
+
end
|
23
|
+
|
24
|
+
should "respond to #root" do
|
25
|
+
assert_equal ['root', 'mum'], @test.root.collect { |w| w.name }
|
26
|
+
end
|
27
|
+
|
28
|
+
should "raise an error if no has_widgets block given" do
|
29
|
+
exc = assert_raises RuntimeError do
|
30
|
+
@test = Class.new(Apotomo::TestCase).new(:widget).tap{ |t| t.setup }
|
31
|
+
@test.root
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_equal "Please setup a widget tree using TestCase.has_widgets", exc.message
|
35
|
+
end
|
36
|
+
|
37
|
+
should "memorize root" do
|
38
|
+
@test.root.visible=false
|
39
|
+
assert_equal false, @test.root.visible?
|
40
|
+
end
|
41
|
+
|
42
|
+
should "respond to #render_widget" do
|
43
|
+
assert_equal "<div id=\"mum\">burp!</div>", @test.render_widget('mum')
|
44
|
+
assert_equal "<div id=\"mum\">burp!</div>", @test.last_invoke
|
45
|
+
end
|
46
|
+
|
47
|
+
should "respond to #assert_select" do
|
48
|
+
@test.render_widget('mum')
|
49
|
+
|
50
|
+
assert_nothing_raised { @test.assert_select("div#mum", "burp!") }
|
51
|
+
|
52
|
+
exc = assert_raises( MiniTest::Assertion){ @test.assert_select("div#mummy", "burp!"); }
|
53
|
+
assert_equal 'Expected at least 1 element matching "div#mummy", found 0.', exc.message
|
54
|
+
end
|
55
|
+
|
56
|
+
context "using events" do
|
57
|
+
setup do
|
58
|
+
@mum = @test.root['mum']
|
59
|
+
@mum.respond_to_event :footsteps, :with => :squeak
|
60
|
+
@mum.instance_eval do
|
61
|
+
def squeak; render :text => "squeak!"; end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
should "respond to #trigger" do
|
66
|
+
assert_equal ["squeak!"], @test.trigger(:footsteps, :source => 'mum')
|
67
|
+
end
|
68
|
+
|
69
|
+
should "provide options from #trigger to the widget" do
|
70
|
+
@test.trigger(:footsteps, :source => 'mum', :direction => :kitchen)
|
71
|
+
assert_equal :kitchen, @mum.param(:direction)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "respond to #assert_response" do
|
75
|
+
@test.trigger(:footsteps, :source => 'mum')
|
76
|
+
assert @test.assert_response("squeak!")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "responding to parent_controller" do
|
82
|
+
setup do
|
83
|
+
@test = Apotomo::TestCase.new(:widget).tap{ |t| t.setup }
|
84
|
+
end
|
85
|
+
|
86
|
+
should "provide a test controller" do
|
87
|
+
assert_kind_of ActionController::Base, @test.parent_controller
|
88
|
+
end
|
89
|
+
|
90
|
+
should "respond to #controller_name" do
|
91
|
+
assert_equal "barn", @test.parent_controller.controller_name
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/test/unit/widget_test.rb
CHANGED
@@ -65,21 +65,15 @@ class WidgetTest < ActiveSupport::TestCase
|
|
65
65
|
|
66
66
|
context "responding to #address_for_event" do
|
67
67
|
should "accept an event :type" do
|
68
|
-
assert_equal({:
|
68
|
+
assert_equal({:source=>"mum", :type=>:squeak, :controller=>"barn"}, @mum.address_for_event(:squeak))
|
69
69
|
end
|
70
70
|
|
71
71
|
should "accept a :source" do
|
72
|
-
assert_equal({:
|
72
|
+
assert_equal({:source=>"kid", :type=>:squeak, :controller=>"barn"}, @mum.address_for_event(:squeak, :source => 'kid'))
|
73
73
|
end
|
74
74
|
|
75
75
|
should "accept arbitrary options" do
|
76
|
-
assert_equal({:
|
77
|
-
end
|
78
|
-
|
79
|
-
should "complain if no type given" do
|
80
|
-
assert_raises RuntimeError do
|
81
|
-
@mum.address_for_event(:source => 'mum')
|
82
|
-
end
|
76
|
+
assert_equal({:volume=>"loud", :source=>"mum", :type=>:squeak, :controller=>"barn"}, @mum.address_for_event(:squeak, :volume => 'loud'))
|
83
77
|
end
|
84
78
|
end
|
85
79
|
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apotomo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
4
|
+
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.0.beta2
|
9
|
+
version: 1.0.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Nick Sutterer
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2010-
|
17
|
+
date: 2010-11-17 00:00:00 +01:00
|
19
18
|
default_executable:
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
@@ -74,11 +73,11 @@ dependencies:
|
|
74
73
|
segments:
|
75
74
|
- 0
|
76
75
|
- 1
|
77
|
-
-
|
78
|
-
version: 0.1.
|
76
|
+
- 3
|
77
|
+
version: 0.1.3
|
79
78
|
type: :runtime
|
80
79
|
version_requirements: *id004
|
81
|
-
description: Web
|
80
|
+
description: Web component framework for Rails providing page widgets that trigger events and know when and how to update themselves with AJAX.
|
82
81
|
email: apotonick@gmail.com
|
83
82
|
executables: []
|
84
83
|
|
@@ -94,9 +93,7 @@ files:
|
|
94
93
|
- TODO
|
95
94
|
- config/routes.rb
|
96
95
|
- lib/apotomo.rb
|
97
|
-
- lib/apotomo/caching.rb
|
98
96
|
- lib/apotomo/container_widget.rb
|
99
|
-
- lib/apotomo/deep_link_methods.rb
|
100
97
|
- lib/apotomo/event.rb
|
101
98
|
- lib/apotomo/event_handler.rb
|
102
99
|
- lib/apotomo/event_methods.rb
|
@@ -109,6 +106,7 @@ files:
|
|
109
106
|
- lib/apotomo/rails/view_methods.rb
|
110
107
|
- lib/apotomo/request_processor.rb
|
111
108
|
- lib/apotomo/stateful_widget.rb
|
109
|
+
- lib/apotomo/test_case.rb
|
112
110
|
- lib/apotomo/transition.rb
|
113
111
|
- lib/apotomo/tree_node.rb
|
114
112
|
- lib/apotomo/version.rb
|
@@ -120,72 +118,72 @@ files:
|
|
120
118
|
- lib/generators/apotomo/templates/widget.rb
|
121
119
|
- lib/generators/apotomo/templates/widget_test.rb
|
122
120
|
- lib/generators/apotomo/widget_generator.rb
|
123
|
-
-
|
124
|
-
- test/fixtures/mouse/eating.html.erb
|
121
|
+
- lib/tasks.rake
|
125
122
|
- test/fixtures/mouse/make_me_squeak.html.erb
|
126
|
-
- test/fixtures/mouse/
|
123
|
+
- test/fixtures/mouse/content.html.erb
|
127
124
|
- test/fixtures/mouse/educate.html.erb
|
125
|
+
- test/fixtures/mouse/feed.html.erb
|
128
126
|
- test/fixtures/mouse/snuggle.html.erb
|
129
|
-
- test/fixtures/mouse/
|
127
|
+
- test/fixtures/mouse/eating.html.erb
|
128
|
+
- test/fixtures/mouse/posing.html.erb
|
130
129
|
- test/fixtures/application_widget_tree.rb
|
131
|
-
- test/rails/view_methods_test.rb
|
132
|
-
- test/rails/controller_methods_test.rb
|
133
|
-
- test/rails/view_helper_test.rb
|
134
|
-
- test/rails/widget_generator_test.rb
|
135
|
-
- test/rails/rails_integration_test.rb
|
136
130
|
- test/test_helper.rb
|
137
|
-
- test/support/test_case_methods.rb
|
138
|
-
- test/dummy/config/application.rb
|
139
|
-
- test/dummy/config/initializers/session_store.rb
|
140
|
-
- test/dummy/config/initializers/mime_types.rb
|
141
|
-
- test/dummy/config/initializers/secret_token.rb
|
142
|
-
- test/dummy/config/initializers/inflections.rb
|
143
|
-
- test/dummy/config/initializers/backtrace_silencers.rb
|
144
|
-
- test/dummy/config/locales/en.yml
|
145
|
-
- test/dummy/config/routes.rb
|
146
|
-
- test/dummy/config/boot.rb
|
147
|
-
- test/dummy/config/environment.rb
|
148
|
-
- test/dummy/config/environments/production.rb
|
149
|
-
- test/dummy/config/environments/test.rb
|
150
|
-
- test/dummy/config/environments/development.rb
|
151
|
-
- test/dummy/config/database.yml
|
152
|
-
- test/dummy/script/rails
|
153
|
-
- test/dummy/config.ru
|
154
|
-
- test/dummy/db/test.sqlite3
|
155
131
|
- test/dummy/Rakefile
|
156
|
-
- test/dummy/
|
157
|
-
- test/dummy/
|
158
|
-
- test/dummy/
|
132
|
+
- test/dummy/app/controllers/application_controller.rb
|
133
|
+
- test/dummy/app/helpers/application_helper.rb
|
134
|
+
- test/dummy/app/views/layouts/application.html.erb
|
159
135
|
- test/dummy/public/404.html
|
136
|
+
- test/dummy/public/javascripts/prototype.js
|
160
137
|
- test/dummy/public/javascripts/controls.js
|
138
|
+
- test/dummy/public/javascripts/effects.js
|
139
|
+
- test/dummy/public/javascripts/dragdrop.js
|
161
140
|
- test/dummy/public/javascripts/application.js
|
162
141
|
- test/dummy/public/javascripts/rails.js
|
163
|
-
- test/dummy/public/
|
164
|
-
- test/dummy/public/
|
165
|
-
- test/dummy/public/
|
166
|
-
- test/dummy/
|
167
|
-
- test/dummy/
|
168
|
-
- test/dummy/
|
169
|
-
- test/
|
170
|
-
- test/
|
171
|
-
- test/
|
172
|
-
- test/
|
173
|
-
- test/
|
142
|
+
- test/dummy/public/422.html
|
143
|
+
- test/dummy/public/favicon.ico
|
144
|
+
- test/dummy/public/500.html
|
145
|
+
- test/dummy/config.ru
|
146
|
+
- test/dummy/config/environments/test.rb
|
147
|
+
- test/dummy/config/environments/production.rb
|
148
|
+
- test/dummy/config/environments/development.rb
|
149
|
+
- test/dummy/config/database.yml
|
150
|
+
- test/dummy/config/boot.rb
|
151
|
+
- test/dummy/config/locales/en.yml
|
152
|
+
- test/dummy/config/environment.rb
|
153
|
+
- test/dummy/config/initializers/secret_token.rb
|
154
|
+
- test/dummy/config/initializers/backtrace_silencers.rb
|
155
|
+
- test/dummy/config/initializers/session_store.rb
|
156
|
+
- test/dummy/config/initializers/mime_types.rb
|
157
|
+
- test/dummy/config/initializers/inflections.rb
|
158
|
+
- test/dummy/config/application.rb
|
159
|
+
- test/dummy/config/routes.rb
|
160
|
+
- test/dummy/db/test.sqlite3
|
161
|
+
- test/dummy/script/rails
|
162
|
+
- test/support/test_case_methods.rb
|
163
|
+
- test/rails/rails_integration_test.rb
|
164
|
+
- test/rails/view_methods_test.rb
|
165
|
+
- test/rails/controller_methods_test.rb
|
166
|
+
- test/rails/view_helper_test.rb
|
167
|
+
- test/rails/widget_generator_test.rb
|
168
|
+
- test/unit/onfire_integration_test.rb
|
174
169
|
- test/unit/invoke_test.rb
|
175
|
-
- test/unit/
|
176
|
-
- test/unit/test_tab_panel.rb
|
177
|
-
- test/unit/test_jump_to_state.rb
|
170
|
+
- test/unit/request_processor_test.rb
|
178
171
|
- test/unit/render_test.rb
|
172
|
+
- test/unit/test_case_test.rb
|
179
173
|
- test/unit/apotomo_test.rb
|
180
|
-
- test/unit/request_processor_test.rb
|
181
|
-
- test/unit/test_caching.rb
|
182
174
|
- test/unit/javascript_generator_test.rb
|
183
|
-
- test/unit/
|
175
|
+
- test/unit/stateful_widget_test.rb
|
176
|
+
- test/unit/event_methods_test.rb
|
184
177
|
- test/unit/persistence_test.rb
|
178
|
+
- test/unit/event_handler_test.rb
|
185
179
|
- test/unit/transition_test.rb
|
186
180
|
- test/unit/event_test.rb
|
187
|
-
- test/unit/event_methods_test.rb
|
188
181
|
- test/unit/widget_test.rb
|
182
|
+
- test/unit/widget_shortcuts_test.rb
|
183
|
+
- test/unit/test_tab_panel.rb
|
184
|
+
- test/unit/test_addressing.rb
|
185
|
+
- test/unit/container_test.rb
|
186
|
+
- test/unit/test_jump_to_state.rb
|
189
187
|
has_rdoc: true
|
190
188
|
homepage: http://apotomo.de
|
191
189
|
licenses: []
|
@@ -206,13 +204,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
206
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
205
|
none: false
|
208
206
|
requirements:
|
209
|
-
- - "
|
207
|
+
- - ">="
|
210
208
|
- !ruby/object:Gem::Version
|
211
209
|
segments:
|
212
|
-
-
|
213
|
-
|
214
|
-
- 1
|
215
|
-
version: 1.3.1
|
210
|
+
- 0
|
211
|
+
version: "0"
|
216
212
|
requirements: []
|
217
213
|
|
218
214
|
rubyforge_project:
|
@@ -221,69 +217,68 @@ signing_key:
|
|
221
217
|
specification_version: 3
|
222
218
|
summary: Web components for Rails.
|
223
219
|
test_files:
|
224
|
-
- test/fixtures/mouse/feed.html.erb
|
225
|
-
- test/fixtures/mouse/eating.html.erb
|
226
220
|
- test/fixtures/mouse/make_me_squeak.html.erb
|
227
|
-
- test/fixtures/mouse/
|
221
|
+
- test/fixtures/mouse/content.html.erb
|
228
222
|
- test/fixtures/mouse/educate.html.erb
|
223
|
+
- test/fixtures/mouse/feed.html.erb
|
229
224
|
- test/fixtures/mouse/snuggle.html.erb
|
230
|
-
- test/fixtures/mouse/
|
225
|
+
- test/fixtures/mouse/eating.html.erb
|
226
|
+
- test/fixtures/mouse/posing.html.erb
|
231
227
|
- test/fixtures/application_widget_tree.rb
|
232
|
-
- test/rails/view_methods_test.rb
|
233
|
-
- test/rails/controller_methods_test.rb
|
234
|
-
- test/rails/view_helper_test.rb
|
235
|
-
- test/rails/widget_generator_test.rb
|
236
|
-
- test/rails/rails_integration_test.rb
|
237
228
|
- test/test_helper.rb
|
238
|
-
- test/support/test_case_methods.rb
|
239
|
-
- test/dummy/config/application.rb
|
240
|
-
- test/dummy/config/initializers/session_store.rb
|
241
|
-
- test/dummy/config/initializers/mime_types.rb
|
242
|
-
- test/dummy/config/initializers/secret_token.rb
|
243
|
-
- test/dummy/config/initializers/inflections.rb
|
244
|
-
- test/dummy/config/initializers/backtrace_silencers.rb
|
245
|
-
- test/dummy/config/locales/en.yml
|
246
|
-
- test/dummy/config/routes.rb
|
247
|
-
- test/dummy/config/boot.rb
|
248
|
-
- test/dummy/config/environment.rb
|
249
|
-
- test/dummy/config/environments/production.rb
|
250
|
-
- test/dummy/config/environments/test.rb
|
251
|
-
- test/dummy/config/environments/development.rb
|
252
|
-
- test/dummy/config/database.yml
|
253
|
-
- test/dummy/script/rails
|
254
|
-
- test/dummy/config.ru
|
255
|
-
- test/dummy/db/test.sqlite3
|
256
229
|
- test/dummy/Rakefile
|
257
|
-
- test/dummy/
|
258
|
-
- test/dummy/
|
259
|
-
- test/dummy/
|
230
|
+
- test/dummy/app/controllers/application_controller.rb
|
231
|
+
- test/dummy/app/helpers/application_helper.rb
|
232
|
+
- test/dummy/app/views/layouts/application.html.erb
|
260
233
|
- test/dummy/public/404.html
|
234
|
+
- test/dummy/public/javascripts/prototype.js
|
261
235
|
- test/dummy/public/javascripts/controls.js
|
236
|
+
- test/dummy/public/javascripts/effects.js
|
237
|
+
- test/dummy/public/javascripts/dragdrop.js
|
262
238
|
- test/dummy/public/javascripts/application.js
|
263
239
|
- test/dummy/public/javascripts/rails.js
|
264
|
-
- test/dummy/public/
|
265
|
-
- test/dummy/public/
|
266
|
-
- test/dummy/public/
|
267
|
-
- test/dummy/
|
268
|
-
- test/dummy/
|
269
|
-
- test/dummy/
|
270
|
-
- test/
|
271
|
-
- test/
|
272
|
-
- test/
|
273
|
-
- test/
|
274
|
-
- test/
|
240
|
+
- test/dummy/public/422.html
|
241
|
+
- test/dummy/public/favicon.ico
|
242
|
+
- test/dummy/public/500.html
|
243
|
+
- test/dummy/config.ru
|
244
|
+
- test/dummy/config/environments/test.rb
|
245
|
+
- test/dummy/config/environments/production.rb
|
246
|
+
- test/dummy/config/environments/development.rb
|
247
|
+
- test/dummy/config/database.yml
|
248
|
+
- test/dummy/config/boot.rb
|
249
|
+
- test/dummy/config/locales/en.yml
|
250
|
+
- test/dummy/config/environment.rb
|
251
|
+
- test/dummy/config/initializers/secret_token.rb
|
252
|
+
- test/dummy/config/initializers/backtrace_silencers.rb
|
253
|
+
- test/dummy/config/initializers/session_store.rb
|
254
|
+
- test/dummy/config/initializers/mime_types.rb
|
255
|
+
- test/dummy/config/initializers/inflections.rb
|
256
|
+
- test/dummy/config/application.rb
|
257
|
+
- test/dummy/config/routes.rb
|
258
|
+
- test/dummy/db/test.sqlite3
|
259
|
+
- test/dummy/script/rails
|
260
|
+
- test/support/test_case_methods.rb
|
261
|
+
- test/rails/rails_integration_test.rb
|
262
|
+
- test/rails/view_methods_test.rb
|
263
|
+
- test/rails/controller_methods_test.rb
|
264
|
+
- test/rails/view_helper_test.rb
|
265
|
+
- test/rails/widget_generator_test.rb
|
266
|
+
- test/unit/onfire_integration_test.rb
|
275
267
|
- test/unit/invoke_test.rb
|
276
|
-
- test/unit/
|
277
|
-
- test/unit/test_tab_panel.rb
|
278
|
-
- test/unit/test_jump_to_state.rb
|
268
|
+
- test/unit/request_processor_test.rb
|
279
269
|
- test/unit/render_test.rb
|
270
|
+
- test/unit/test_case_test.rb
|
280
271
|
- test/unit/apotomo_test.rb
|
281
|
-
- test/unit/request_processor_test.rb
|
282
|
-
- test/unit/test_caching.rb
|
283
272
|
- test/unit/javascript_generator_test.rb
|
284
|
-
- test/unit/
|
273
|
+
- test/unit/stateful_widget_test.rb
|
274
|
+
- test/unit/event_methods_test.rb
|
285
275
|
- test/unit/persistence_test.rb
|
276
|
+
- test/unit/event_handler_test.rb
|
286
277
|
- test/unit/transition_test.rb
|
287
278
|
- test/unit/event_test.rb
|
288
|
-
- test/unit/event_methods_test.rb
|
289
279
|
- test/unit/widget_test.rb
|
280
|
+
- test/unit/widget_shortcuts_test.rb
|
281
|
+
- test/unit/test_tab_panel.rb
|
282
|
+
- test/unit/test_addressing.rb
|
283
|
+
- test/unit/container_test.rb
|
284
|
+
- test/unit/test_jump_to_state.rb
|
data/lib/apotomo/caching.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# Introduces caching of rendered state views into the StatefulWidget.
|
2
|
-
module Apotomo::Caching
|
3
|
-
|
4
|
-
def self.included(base) #:nodoc:
|
5
|
-
base.class_eval do
|
6
|
-
extend ClassMethods
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
# If <tt>version_proc</tt> is omitted, Apotomo provides some basic caching
|
13
|
-
# mechanism: the state view rendered for <tt>state</tt> will be cached as long
|
14
|
-
# as you (or e.g. an EventHandler) calls #dirty!. It will then be re-rendered
|
15
|
-
# and cached again.
|
16
|
-
# You may override that to provide fine-grained caching, with multiple cache versions
|
17
|
-
# for the same state.
|
18
|
-
def cache(state, version_proc=:cache_version)
|
19
|
-
super(state, version_proc)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def cache_version
|
24
|
-
@version ||= 0
|
25
|
-
{:v => @version}
|
26
|
-
end
|
27
|
-
|
28
|
-
def increment_version
|
29
|
-
@version += 1
|
30
|
-
end
|
31
|
-
|
32
|
-
# Instruct caching to re-render all cached state views.
|
33
|
-
def dirty!
|
34
|
-
increment_version
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Apotomo
|
2
|
-
module DeepLinkMethods
|
3
|
-
def self.included(base)
|
4
|
-
base.initialize_hooks << :initialize_deep_link_for
|
5
|
-
end
|
6
|
-
|
7
|
-
|
8
|
-
# Called in StatefulWidget's constructor.
|
9
|
-
def initialize_deep_link_for(id, start_states, opts)
|
10
|
-
#add_deep_link if opts[:is_url_listener] ### DISCUSS: remove #add_de
|
11
|
-
end
|
12
|
-
|
13
|
-
def responds_to_url_change?
|
14
|
-
evt_table.all_handlers_for(:urlChange, name).size > 0
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
### DISCUSS: private? rename to compute_url_fragment_for ?
|
19
|
-
# Computes the fragment part of the widget's url by querying all widgets up to root.
|
20
|
-
# Widgets managing a certain state will usually insert state recovery information
|
21
|
-
# via local_fragment.
|
22
|
-
def url_fragment_for(local_portion=nil, portions=[])
|
23
|
-
local_portion = local_fragment if responds_to_url_change? and local_portion.nil?
|
24
|
-
|
25
|
-
portions.unshift(local_portion) # prepend portions as we move up.
|
26
|
-
|
27
|
-
return portions.compact.join("/") if root?
|
28
|
-
|
29
|
-
parent.url_fragment_for(nil, portions)
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
# Called when widget :is_url_listener. Adds the local url fragment portion to the url.
|
34
|
-
def local_fragment
|
35
|
-
#"#{local_fragment_key}=#{state_name}"
|
36
|
-
end
|
37
|
-
|
38
|
-
# Key found in the url fragment, pointing to the local fragment.
|
39
|
-
#def local_fragment_key
|
40
|
-
# name
|
41
|
-
#end
|
42
|
-
|
43
|
-
|
44
|
-
# Called by DeepLinkWidget#process to query if we're involved in an URL change.
|
45
|
-
# Do return false if you're not interested in the change.
|
46
|
-
#
|
47
|
-
# This especially means:
|
48
|
-
# * the fragment doesn't include you or is empty
|
49
|
-
# fragment[name].blank?
|
50
|
-
# * your portion in the fragment didn't change
|
51
|
-
# tab=first/content=html vs. tab=first/content=markdown
|
52
|
-
# fragment[:tab] != @active_tab
|
53
|
-
def responds_to_url_change_for?(fragment)
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
class UrlFragment
|
59
|
-
attr_reader :fragment
|
60
|
-
|
61
|
-
def initialize(fragment)
|
62
|
-
@fragment = fragment || ""
|
63
|
-
end
|
64
|
-
|
65
|
-
def to_s
|
66
|
-
fragment.to_s
|
67
|
-
end
|
68
|
-
|
69
|
-
def blank?
|
70
|
-
fragment.blank?
|
71
|
-
end
|
72
|
-
|
73
|
-
### TODO: make path separator configurable.
|
74
|
-
def [](key)
|
75
|
-
if path_portion = fragment.split("/").find {|i| i.include?(key.to_s)}
|
76
|
-
return path_portion.sub("#{key}=", "")
|
77
|
-
end
|
78
|
-
|
79
|
-
nil
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Query object for the url fragment. Use this to retrieve state information from the
|
84
|
-
# deep link.
|
85
|
-
def url_fragment
|
86
|
-
UrlFragment.new(param(:deep_link))
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
end
|
data/test/unit/test_caching.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class ApotomoCachingTest < Test::Unit::TestCase
|
4
|
-
include Apotomo::UnitTestCase
|
5
|
-
|
6
|
-
def setup
|
7
|
-
super
|
8
|
-
@controller.session= {}
|
9
|
-
@cc = CachingCell.new('caching_cell', :start)
|
10
|
-
@cc.controller = @controller
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
def test_caching_with_instance_version_proc
|
15
|
-
unless ActionController::Base.cache_configured?
|
16
|
-
throw Exception.new "cache_configured? returned false. You may enable caching in your config/environments/test.rb to make this test pass."
|
17
|
-
return
|
18
|
-
end
|
19
|
-
c1 = @cc.invoke
|
20
|
-
c2 = @cc.invoke
|
21
|
-
assert_equal c1, c2
|
22
|
-
|
23
|
-
@cc.dirty!
|
24
|
-
|
25
|
-
c3 = @cc.invoke
|
26
|
-
assert c2 != c3
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
class CachingCell < Apotomo::StatefulWidget
|
33
|
-
|
34
|
-
cache :cached_state
|
35
|
-
|
36
|
-
transition :in => :cached_state
|
37
|
-
|
38
|
-
|
39
|
-
def start
|
40
|
-
jump_to_state :cached_state
|
41
|
-
end
|
42
|
-
|
43
|
-
def cached_state
|
44
|
-
@counter ||= 0
|
45
|
-
@counter += 1
|
46
|
-
"#{@counter}"
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
def not_cached_state
|
51
|
-
"i'm really static"
|
52
|
-
end
|
53
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
# fixture:
|
4
|
-
module My
|
5
|
-
class TestCell < Apotomo::StatefulWidget
|
6
|
-
def a_state
|
7
|
-
"a_state"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class TestWidget < Apotomo::StatefulWidget
|
12
|
-
def a_state
|
13
|
-
"a_state"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class MyTestWidgetTree < Apotomo::WidgetTree
|
19
|
-
def draw(root)
|
20
|
-
root << widget('apotomo/stateful_widget', :widget_content, 'widget_one')
|
21
|
-
root << cell(:my_test, :a_state, 'my_test_cell')
|
22
|
-
root << switch('my_switch') << widget('apotomo/stateful_widget', :widget_content, :child_widget)
|
23
|
-
root << section('my_section')
|
24
|
-
root << widget('apotomo/stateful_widget', :widget_content, :widget_three)
|
25
|
-
#root ### FIXME! find a way to return nothing by default.
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
class WidgetShortcutsTest < Test::Unit::TestCase
|
31
|
-
include Apotomo::UnitTestCase
|
32
|
-
|
33
|
-
|
34
|
-
def test_cell
|
35
|
-
assert_kind_of My::TestCell, cell("my/test", :a_state, 'my_test_cell')
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_widget
|
39
|
-
w = widget("my/test_widget", :a_state, 'my_test_cell')
|
40
|
-
assert_kind_of My::TestWidget, w
|
41
|
-
assert_equal "my_test_cell", w.name
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|