cells 3.3.10 → 3.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/CHANGES +0 -8
  2. data/Gemfile +4 -1
  3. data/README.rdoc +91 -107
  4. data/Rakefile +0 -4
  5. data/lib/cell.rb +5 -6
  6. data/lib/{cells/cell → cell}/active_helper.rb +1 -1
  7. data/lib/cell/base.rb +134 -0
  8. data/lib/cell/base_methods.rb +100 -0
  9. data/lib/cell/caching.rb +153 -0
  10. data/lib/cell/rails.rb +239 -0
  11. data/lib/cells.rb +25 -3
  12. data/lib/cells/assertions_helper.rb +1 -1
  13. data/lib/cells/helpers/capture_helper.rb +3 -3
  14. data/lib/cells/rails.rb +65 -4
  15. data/lib/cells/version.rb +3 -1
  16. data/rails_generators/cell/cell_generator.rb +47 -35
  17. data/rails_generators/cell/templates/cell.rb +1 -1
  18. data/rails_generators/cells_install/cells_install_generator.rb +5 -3
  19. data/rails_generators/erb/cell_generator.rb +20 -0
  20. data/rails_generators/{cell → erb}/templates/view.html.erb +0 -0
  21. data/test/active_helper_test.rb +1 -0
  22. data/test/app/cells/bad_guitarist_cell.rb +2 -0
  23. data/test/app/cells/bassist_cell.rb +1 -1
  24. data/test/app/controllers/musician_controller.rb +16 -0
  25. data/test/assertions_helper_test.rb +8 -18
  26. data/test/base_methods_test.rb +40 -0
  27. data/test/cell_generator_test.rb +33 -21
  28. data/test/helper_test.rb +31 -123
  29. data/test/rails/caching_test.rb +215 -0
  30. data/test/rails/capture_test.rb +52 -0
  31. data/test/rails/cells_test.rb +88 -0
  32. data/test/rails/integration_test.rb +37 -0
  33. data/test/rails/render_test.rb +140 -0
  34. data/test/rails/router_test.rb +74 -0
  35. data/test/rails/view_test.rb +24 -0
  36. data/test/test_helper.rb +30 -29
  37. metadata +68 -133
  38. data/.gitignore +0 -3
  39. data/about.yml +0 -7
  40. data/cells.gemspec +0 -26
  41. data/lib/cells/cell.rb +0 -16
  42. data/lib/cells/cell/base.rb +0 -470
  43. data/lib/cells/cell/caching.rb +0 -163
  44. data/lib/cells/cell/test_case.rb +0 -158
  45. data/lib/cells/cell/view.rb +0 -55
  46. data/lib/cells/rails/action_controller.rb +0 -37
  47. data/lib/cells/rails/action_view.rb +0 -37
  48. data/rails/init.rb +0 -44
  49. data/rails_generators/cells_install/templates/tasks.rake +0 -6
  50. data/test/app/cells/a/another_state.html.erb +0 -1
  51. data/test/app/cells/a/existing_view.html.erb +0 -1
  52. data/test/app/cells/a/inherited_view.html.erb +0 -1
  53. data/test/app/cells/a/inherited_view.js.erb +0 -1
  54. data/test/app/cells/a/view_with_locals.html.erb +0 -1
  55. data/test/app/cells/a/view_with_render_call.html.erb +0 -1
  56. data/test/app/cells/b/existing_view.html.erb +0 -1
  57. data/test/app/cells/b/existing_view.js.erb +0 -1
  58. data/test/app/cells/b/layouts/metal.html.erb +0 -1
  59. data/test/app/cells/b/view_with_render_call.html.erb +0 -1
  60. data/test/app/cells/bassist/jam.html.erb +0 -3
  61. data/test/app/cells/bassist/play.html.erb +0 -1
  62. data/test/app/cells/cells_test_one/renamed_instance_view.html.erb +0 -1
  63. data/test/app/cells/cells_test_one/super_state.html.erb +0 -1
  64. data/test/app/cells/cells_test_one_cell.rb +0 -20
  65. data/test/app/cells/cells_test_two_cell.rb +0 -4
  66. data/test/app/cells/helper_using/state_using_application_helper.html.erb +0 -3
  67. data/test/app/cells/helper_using/state_with_automatic_helper_invocation.html.erb +0 -3
  68. data/test/app/cells/helper_using/state_with_helper_invocation.html.erb +0 -3
  69. data/test/app/cells/helper_using/state_with_helper_method_invocation.html.erb +0 -3
  70. data/test/app/cells/layouts/metal.html.erb +0 -1
  71. data/test/app/cells/my_child/hello.html.erb +0 -1
  72. data/test/app/cells/my_mother/bye.html.erb +0 -1
  73. data/test/app/cells/my_mother/hello.html.erb +0 -1
  74. data/test/app/cells/my_test/_broken_partial.html.erb +0 -1
  75. data/test/app/cells/my_test/_partial.html.erb +0 -1
  76. data/test/app/cells/my_test/state_with_instance_var.html.erb +0 -1
  77. data/test/app/cells/my_test/state_with_link_to.html.erb +0 -3
  78. data/test/app/cells/my_test/state_with_not_included_helper_method.html.erb +0 -8
  79. data/test/app/cells/my_test/view_containing_broken_partial.html.erb +0 -3
  80. data/test/app/cells/my_test/view_containing_nonexistant_partial.html.erb +0 -3
  81. data/test/app/cells/my_test/view_containing_partial.html.erb +0 -3
  82. data/test/app/cells/my_test/view_containing_partial_without_cell_name.html.erb +0 -3
  83. data/test/app/cells/my_test/view_in_local_test_views_dir.html.erb +0 -1
  84. data/test/app/cells/my_test/view_with_explicit_english_translation.en.html.erb +0 -1
  85. data/test/app/cells/my_test/view_with_explicit_english_translation.html.erb +0 -1
  86. data/test/app/cells/my_test/view_with_instance_var.html.erb +0 -4
  87. data/test/app/cells/really_module/nested/happy_state.html.erb +0 -1
  88. data/test/app/cells/really_module/nested_cell.rb +0 -11
  89. data/test/app/cells/simple/two_templates_state.html.mytpl +0 -1
  90. data/test/app/cells/simple_cell.rb +0 -7
  91. data/test/app/cells/test/beep.html.erb +0 -1
  92. data/test/app/cells/test/state_invoking_capture.html.erb +0 -7
  93. data/test/app/cells/test/state_invoking_content_for.html.erb +0 -7
  94. data/test/app/cells/test/state_invoking_content_for_twice.html.erb +0 -9
  95. data/test/app/cells/test/state_with_not_included_helper_method.html.erb +0 -8
  96. data/test/app/cells/two_helpers_including/state_using_another_helper.html.erb +0 -3
  97. data/test/bugs_test.rb +0 -23
  98. data/test/caching_test.rb +0 -270
  99. data/test/capture_helper_test.rb +0 -59
  100. data/test/cells_test.rb +0 -352
  101. data/test/rails_test.rb +0 -35
  102. data/test/render_test.rb +0 -305
  103. data/test/test_case_test.rb +0 -106
@@ -1,163 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # To improve performance rendered state views can be cached using Rails' caching
4
- # mechanism.
5
- # If this it configured (e.g. using our fast friend memcached) all you have to do is to
6
- # tell Cells which state to cache. You can further attach a proc to expire the
7
- # cached view.
8
- #
9
- # As always I stole a lot of code, this time from Lance Ivy <cainlevy@gmail.com> and
10
- # his fine components plugin at http://github.com/cainlevy/components.
11
-
12
- module Cells
13
- module Cell
14
- module Caching
15
-
16
- def self.included(base) #:nodoc:
17
- base.class_eval do
18
- # mixin ::Cell::Base#cache, setup vars and extend #render_state if caching's on.
19
- extend ClassMethods
20
-
21
- return unless cache_configured?
22
-
23
- alias_method_chain :render_state, :caching
24
- end
25
- end
26
-
27
- module ClassMethods
28
- # Activate caching for the state <tt>state</tt>. If no other options are passed
29
- # the view will be cached forever.
30
- #
31
- # You may pass a Proc or a Symbol as cache expiration <tt>version_proc</tt>.
32
- # This method is called every time the state is rendered, and is expected to return a
33
- # Hash containing the cache key ingredients.
34
- #
35
- # Additional options will be passed directly to the cache store when caching the state.
36
- # Useful for simply setting a TTL for a cached state.
37
- # Note that you may omit the <tt>version_proc</tt>.
38
- #
39
- #
40
- # Example:
41
- # class CachingCell < ::Cell::Base
42
- # cache :versioned_cached_state, Proc.new{ {:version => 0} }
43
- # would result in the complete cache key
44
- # cells/CachingCell/versioned_cached_state/version=0
45
- #
46
- # If you provide a symbol, you can access the cell instance directly in the versioning
47
- # method:
48
- #
49
- # class CachingCell < ::Cell::Base
50
- # cache :cached_state, :my_cache_version
51
- #
52
- # def my_cache_version
53
- # { :user => current_user.id,
54
- # :item_id => params[:item] }
55
- # }
56
- # end
57
- # results in a very specific cache key, for customized caching:
58
- # cells/CachingCell/cached_state/user=18/item_id=1
59
- #
60
- # You may also set a TTL only, e.g. when using the memcached store:
61
- #
62
- # cache :cached_state, :expires_in => 3.minutes
63
- #
64
- # Or use both, having a versioning proc <em>and</em> a TTL expiring the state as a fallback
65
- # after a certain amount of time.
66
- #
67
- # cache :cached_state, Proc.new { {:version => 0} }, :expires_in => 10.minutes
68
- #--
69
- ### TODO: implement for string, nil.
70
- ### DISCUSS: introduce return method #sweep ? so the Proc can explicitly
71
- ### delegate re-rendering to the outside.
72
- #--
73
- def cache(state, version_proc=nil, cache_opts={})
74
- if version_proc.is_a?(Hash)
75
- cache_opts = version_proc
76
- version_proc = nil
77
- end
78
-
79
- version_procs[state] = version_proc
80
- cache_options[state] = cache_opts
81
- end
82
-
83
- def version_procs
84
- @version_procs ||= {}
85
- end
86
-
87
- def cache_options
88
- @cache_options ||= {}
89
- end
90
-
91
- def cache_store #:nodoc:
92
- ::ActionController::Base.cache_store
93
- end
94
-
95
- def cache_key_for(cell_class, state, args = {}) #:nodoc:
96
- key_pieces = [cell_class, state]
97
-
98
- args.collect{|a,b| [a.to_s, b]}.sort.each{ |k,v| key_pieces << "#{k}=#{v}" }
99
- key = key_pieces.join('/')
100
-
101
- ::ActiveSupport::Cache.expand_cache_key(key, :cells)
102
- end
103
-
104
- def expire_cache_key(key, opts=nil)
105
- cache_store.delete(key, opts)
106
- end
107
- end
108
-
109
- def render_state_with_caching(state)
110
- return render_state_without_caching(state) unless state_cached?(state)
111
-
112
- key = cache_key(state, call_version_proc_for_state(state))
113
- ### DISCUSS: see sweep discussion at #cache.
114
-
115
- # cache hit:
116
- if content = read_fragment(key)
117
- return content
118
- end
119
- # re-render:
120
- return write_fragment(key, render_state_without_caching(state), cache_options[state])
121
- end
122
-
123
- def read_fragment(key, cache_options = nil) #:nodoc:
124
- self.class.cache_store.read(key, cache_options).tap do |content|
125
- log "Cell Cache hit: #{key}" unless content.blank?
126
- end
127
- end
128
-
129
- def write_fragment(key, content, cache_opts = nil) #:nodoc:
130
- log "Cell Cache miss: #{key}"
131
- self.class.cache_store.write(key, content, cache_opts)
132
- content
133
- end
134
-
135
- # Call the versioning Proc for the respective state.
136
- def call_version_proc_for_state(state)
137
- version_proc = version_procs[state]
138
-
139
- return {} unless version_proc # call to #cache was without any args.
140
-
141
- return version_proc.call(self) if version_proc.kind_of?(Proc)
142
- send(version_proc)
143
- end
144
-
145
- def cache_key(state, args = {}) #:nodoc:
146
- self.class.cache_key_for(self.cell_name, state, args)
147
- end
148
-
149
- def state_cached?(state)
150
- self.class.version_procs.has_key?(state)
151
- end
152
-
153
- def version_procs
154
- self.class.version_procs
155
- end
156
-
157
- def cache_options
158
- self.class.cache_options
159
- end
160
-
161
- end
162
- end
163
- end
@@ -1,158 +0,0 @@
1
- require "active_support/test_case"
2
- module Cell
3
- # Test your cells.
4
- #
5
- # This class is roughly equal to ActionController::TestCase, exposing the same semantics. It will try
6
- # to infer the tested cell name from the test name if you use declarative testing. You can also set it
7
- # with TestCase.tests.
8
- #
9
- # A declarative test would look like
10
- #
11
- # class SellOutTest < Cell::TestCase
12
- # tests ShoppingCartCell
13
- #
14
- # it "should be rendered nicely" do
15
- # invoke :order_button, :items => @fixture_items
16
- #
17
- # assert_select "button", "Order now!"
18
- # end
19
- #
20
- # You can also do stuff yourself, like
21
- #
22
- # it "should be rendered even nicer" do
23
- # html = render_cell(:shopping_cart, :order_button, , :items => @fixture_items)
24
- # assert_selector "button", "Order now!", html
25
- # end
26
- #
27
- # Or even unit test your cell:
28
- #
29
- # it "should provide #default_items" do
30
- # assert_equal [@item1, @item2], cell(:shopping_cart).default_items
31
- # end
32
- #
33
- # == Test helpers
34
- #
35
- # Basically, we got these new methods:
36
- #
37
- # +invoke+:: Renders the passed +state+ with your tested cell. You may pass options like in #render_cell.
38
- # +render_cell+:: As in your views. Will return the rendered view.
39
- # +assert_selector+:: Like #assert_select except that the last argument is the html markup you wanna test.
40
- # +cell+:: Gives you a cell instance for unit testing and stuff.
41
- class TestCase < ActiveSupport::TestCase
42
- module AssertSelect
43
- # Invokes assert_select for the last argument, the +content+ string.
44
- #
45
- # Example:
46
- # assert_selector "h1", "The latest and greatest!", "<h1>The latest and greatest!</h1>"
47
- #
48
- # would be true.
49
- def assert_selector(*args, &block)
50
- rails_assert_select(HTML::Document.new(args.pop).root, *args, &block)
51
- end
52
-
53
- # Invokes assert_select on the markup set by the last #invoke.
54
- #
55
- # Example:
56
- # invoke :latest
57
- # assert_select "h1", "The latest and greatest!"
58
- def assert_select(*args, &block)
59
- super(HTML::Document.new(last_invoke).root, *args, &block)
60
- end
61
- end
62
-
63
- module TestMethods
64
- def setup
65
- @controller = Class.new(ActionController::Base).new
66
- @request = ::ActionController::TestRequest.new
67
- @response = ::ActionController::TestResponse.new
68
- @controller.request = @request
69
- @controller.response = @response
70
- @controller.params = {}
71
- @controller.send(:initialize_current_url)
72
- end
73
-
74
- # Use this for functional tests of your application cells.
75
- #
76
- # Example:
77
- # should "spit out a h1 title" do
78
- # html = render_cell(:news, :latest)
79
- # assert_selekt html, "h1", "The latest and greatest!"
80
- def render_cell(name, state, *args)
81
- @subject_cell = ::Cell::Base.create_cell_for(@controller, name, *args)
82
- @view_assigns = extract_state_ivars_for(@subject_cell) do
83
- @last_invoke = @subject_cell.render_state(state)
84
- end
85
-
86
- @last_invoke
87
- end
88
-
89
- # Runs the block while computing the instance variables diff from before and after.
90
- def extract_state_ivars_for(cell)
91
- before = cell.instance_variables
92
- before += [:@cell, :@state_name]
93
- yield
94
- after = cell.instance_variables
95
-
96
- Hash[(after - before).collect do |var|
97
- next if var =~ /^@_/
98
- [var[1, var.length].to_sym, cell.instance_variable_get(var)]
99
- end]
100
- end
101
-
102
- # Builds an instance of <tt>name</tt>Cell for unit testing.
103
- # Passes the optional block to <tt>cell.instance_eval</tt>.
104
- #
105
- # Example:
106
- # assert_equal "Banks kill planet!" cell(:news, :topic => :terror).latest_headline
107
- def cell(name, opts={}, &block)
108
- cell = ::Cell::Base.create_cell_for(@controller, name, opts)
109
- cell.instance_eval &block if block_given?
110
- cell
111
- end
112
-
113
- def assigns(name)
114
- view_assigns[name.to_sym]
115
- end
116
- end
117
-
118
- include TestMethods
119
- include ActionController::Assertions::SelectorAssertions # imports "their" #assert_select.
120
- alias_method :rails_assert_select, :assert_select # i hate that.
121
- include AssertSelect
122
-
123
-
124
- attr_reader :last_invoke, :subject_cell, :view_assigns
125
-
126
- def invoke(state, *args)
127
- @last_invoke = self.class.controller_class.new(@controller, *args).render_state(state)
128
- end
129
-
130
-
131
- class << self
132
- # Sets the controller class name. Useful if the name can't be inferred from test class.
133
- # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
134
- def tests(controller_class)
135
- self.controller_class = controller_class
136
- end
137
-
138
- def controller_class=(new_class)
139
- write_inheritable_attribute(:controller_class, new_class)
140
- end
141
-
142
- def controller_class
143
- if current_controller_class = read_inheritable_attribute(:controller_class)
144
- current_controller_class
145
- else
146
- self.controller_class = determine_default_controller_class(name)
147
- end
148
- end
149
-
150
- def determine_default_controller_class(name)
151
- name.sub(/Test$/, '').constantize
152
- rescue NameError
153
- nil
154
- end
155
- end
156
-
157
- end
158
- end
@@ -1,55 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Cells
4
- module Cell
5
- class View < ::ActionView::Base
6
-
7
- attr_accessor :cell
8
- alias_method :render_for, :render
9
-
10
- # Tries to find the passed template in view_paths. Returns the view on success-
11
- # otherwise it will throw an ActionView::MissingTemplate exception.
12
- def try_picking_template_for_path(template_path)
13
- self.view_paths.find_template(template_path, template_format)
14
- end
15
-
16
- ### TODO: this should just be a thin helper.
17
- ### dear rails folks, could you guys please provide a helper #render and an internal #render_for
18
- ### so that we can overwrite the helper and cleanly reuse the internal method? using the same
19
- ### method both internally and externally sucks ass.
20
- def render(options = {}, local_assigns = {}, &block)
21
- ### TODO: we have to find out if this is a call to the cells #render method, or to the rails
22
- ### method (e.g. when rendering a layout). what a shit.
23
- if (options.keys & [:view, :state]).present? ### TODO: is there something like has_keys?
24
- # that's better: simply delegate render back to the cell/controller.
25
- return cell.render(options)
26
- end
27
-
28
- # rails compatibility we should get rid of:
29
- if partial_path = options[:partial]
30
- # adds the cell name to the partial name.
31
- options[:partial] = expand_view_path(partial_path)
32
- end
33
-
34
- super(options, local_assigns, &block)
35
- end
36
-
37
- def expand_view_path(path)
38
- path = "#{cell.cell_name}/#{path}" unless path.include?('/')
39
- path
40
- end
41
-
42
- # this prevents cell ivars from being overwritten by same-named
43
- # controller ivars.
44
- # we'll hopefully get a cleaner way, or an API, to handle this in rails 3.
45
- def _copy_ivars_from_controller #:nodoc:
46
- if @controller
47
- variables = @controller.instance_variable_names
48
- variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
49
- variables -= assigns.keys.collect { |key| "@#{key}" } # cell ivars override controller ivars.
50
- variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,37 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # These ControllerMethods are automatically added to all Controllers when
4
- # the cells plugin is loaded.
5
- module Cells
6
- module Rails
7
- module ActionController
8
- # Equivalent to ActionController#render_to_string, except it renders a cell
9
- # rather than a regular templates.
10
- def render_cell(name, state, opts={})
11
- cell = ::Cell::Base.create_cell_for(self, name, opts)
12
- return cell.render_state(state)
13
- end
14
- alias_method :render_cell_to_string, :render_cell # just for backward compatibility.
15
-
16
- # Expires the cached cell state view, similar to ActionController::expire_fragment.
17
- # Usually, this method is used in Sweepers.
18
- # Beside the obvious first two args <tt>cell_name</tt> and <tt>state</tt> you can pass
19
- # in additional cache key <tt>args</tt> and cache store specific <tt>opts</tt>.
20
- #
21
- # Example:
22
- #
23
- # class ListSweeper < ActionController::Caching::Sweeper
24
- # observe List, Item
25
- #
26
- # def after_save(record)
27
- # expire_cell_state :my_listing, :display_list
28
- # end
29
- #
30
- # will expire the view for state <tt>:display_list</tt> in the cell <tt>MyListingCell</tt>.
31
- def expire_cell_state(cell_name, state, args={}, opts=nil)
32
- key = ::Cell::Base.cache_key_for(cell_name, state, args)
33
- ::Cell::Base.expire_cache_key(key, opts)
34
- end
35
- end
36
- end
37
- end
@@ -1,37 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # The Cells plugin defines a number of new methods for ActionView::Base. These allow
4
- # you to render cells from within normal controller views as well as from Cell state views.
5
- module Cells
6
- module Rails
7
- module ActionView
8
- # Call a cell state and return its rendered view.
9
- #
10
- # ERB example:
11
- # <div id="login">
12
- # <%= render_cell :user, :login_prompt, :message => "Please login" %>
13
- # </div>
14
- #
15
- # If you have a <tt>UserCell</tt> cell in <tt>app/cells/user_cell.rb</tt>, which has a
16
- # <tt>UserCell#login_prompt</tt> method, this will call that method and then will
17
- # find the view <tt>app/cells/user/login_prompt.html.erb</tt> and render it. This is
18
- # called the <tt>:login_prompt</tt> <em>state</em> in Cells terminology.
19
- #
20
- # If this view file looks like this:
21
- # <h1><%= @opts[:message] %></h1>
22
- # <label>name: <input name="user[name]" /></label>
23
- # <label>password: <input name="user[password]" /></label>
24
- #
25
- # The resulting view in the controller will be roughly equivalent to:
26
- # <div id="login">
27
- # <h1><%= "Please login" %></h1>
28
- # <label>name: <input name="user[name]" /></label>
29
- # <label>password: <input name="user[password]" /></label>
30
- # </div>
31
- def render_cell(name, state, opts = {})
32
- cell = ::Cell::Base.create_cell_for(@controller, name, opts)
33
- cell.render_state(state)
34
- end
35
- end
36
- end
37
- end