cells 3.3.10 → 3.4.0.beta1

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