├── test ├── dummy │ ├── app │ │ ├── mailers │ │ │ └── .keep │ │ ├── models │ │ │ ├── .keep │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── artist.rb │ │ │ └── song.rb │ │ ├── assets │ │ │ ├── images │ │ │ │ ├── .keep │ │ │ │ └── logo.jpeg │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ └── stylesheets │ │ │ │ └── application.css.erb │ │ ├── controllers │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── index_controller.rb │ │ │ ├── application_controller.rb │ │ │ ├── songs_controller.rb │ │ │ └── musician_controller.rb │ │ ├── cells │ │ │ ├── song │ │ │ │ ├── song.css │ │ │ │ └── with_escaped.erb │ │ │ ├── form_tag_cell.rb │ │ │ ├── form_tag │ │ │ │ └── show.erb │ │ │ ├── form_for_cell.rb │ │ │ ├── form_for │ │ │ │ └── show.erb │ │ │ ├── simple_form │ │ │ │ └── show.erb │ │ │ ├── formtastic_cell.rb │ │ │ ├── formtastic │ │ │ │ └── show.erb │ │ │ ├── simple_form_cell.rb │ │ │ └── song_cell.rb │ │ ├── views │ │ │ ├── songs │ │ │ │ ├── show.html.erb │ │ │ │ └── with_escaped.html.erb │ │ │ ├── index │ │ │ │ └── index.html.erb │ │ │ └── layouts │ │ │ │ └── application.html.erb │ │ └── helpers │ │ │ └── application_helper.rb │ ├── engines │ │ └── my_engine │ │ │ ├── app │ │ │ ├── assets │ │ │ │ ├── images │ │ │ │ │ └── my_engine │ │ │ │ │ │ └── .keep │ │ │ │ └── stylesheets │ │ │ │ │ └── my_engine │ │ │ │ │ └── application.css │ │ │ ├── concepts │ │ │ │ └── user │ │ │ │ │ ├── views │ │ │ │ │ ├── user.scss │ │ │ │ │ └── show.erb │ │ │ │ │ └── cell.rb │ │ │ ├── models │ │ │ │ └── my_engine │ │ │ │ │ └── user.rb │ │ │ ├── views │ │ │ │ ├── my_engine │ │ │ │ │ └── user │ │ │ │ │ │ └── show.html.erb │ │ │ │ └── layouts │ │ │ │ │ └── my_engine │ │ │ │ │ └── application.html.erb │ │ │ └── controllers │ │ │ │ └── my_engine │ │ │ │ ├── application_controller.rb │ │ │ │ └── user_controller.rb │ │ │ ├── .gitignore │ │ │ ├── README.rdoc │ │ │ ├── lib │ │ │ ├── my_engine │ │ │ │ ├── version.rb │ │ │ │ └── engine.rb │ │ │ ├── tasks │ │ │ │ └── my_engine_tasks.rake │ │ │ └── my_engine.rb │ │ │ ├── config │ │ │ └── routes.rb │ │ │ ├── db │ │ │ └── migrate │ │ │ │ └── 20150530135920_create_my_engine_users.rb │ │ │ ├── test │ │ │ └── fixtures │ │ │ │ └── my_engine │ │ │ │ └── users.yml │ │ │ ├── bin │ │ │ └── rails │ │ │ ├── Rakefile │ │ │ ├── Gemfile │ │ │ └── my_engine.gemspec │ ├── config │ │ ├── boot.rb │ │ ├── initializers │ │ │ ├── cookies_serializer.rb │ │ │ ├── formtastic.rb │ │ │ ├── session_store.rb │ │ │ ├── mime_types.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── wrap_parameters.rb │ │ │ ├── backtrace_silencers.rb │ │ │ └── inflections.rb │ │ ├── environment.rb │ │ ├── routes.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── environments │ │ │ ├── development.rb │ │ │ └── test.rb │ │ ├── application.rb │ │ └── secrets.yml │ ├── Rakefile │ ├── test │ │ ├── integration │ │ │ ├── controller_test.rb │ │ │ ├── asset_pipeline_test.rb │ │ │ ├── form_tag_test.rb │ │ │ ├── form_for_test.rb │ │ │ ├── asset_helper_test.rb │ │ │ ├── url_helper_test.rb │ │ │ ├── simple_form_test.rb │ │ │ └── formtastic_test.rb │ │ ├── public_test.rb │ │ ├── context_test.rb │ │ ├── translation_test.rb │ │ ├── concept_generator_test.rb │ │ ├── test_helper.rb │ │ ├── rails_extensions_test.rb │ │ ├── cell_generator_test.rb │ │ └── caching_test.rb │ └── Gemfile └── test_helper.rb ├── .tool-versions ├── lib ├── cells-rails.rb ├── cells │ ├── rails │ │ └── version.rb │ └── rails.rb ├── rails │ └── generators │ │ ├── cell │ │ ├── templates │ │ │ ├── view.slim │ │ │ ├── view.haml │ │ │ ├── view.erb │ │ │ └── cell.rb.erb │ │ └── cell_generator.rb │ │ ├── concept │ │ ├── templates │ │ │ ├── view.slim │ │ │ ├── view.haml │ │ │ ├── view.erb │ │ │ └── concept.rb.erb │ │ └── concept_generator.rb │ │ └── test_unit │ │ ├── cell │ │ ├── templates │ │ │ └── unit_test.rb.erb │ │ └── cell_generator.rb │ │ └── concept │ │ ├── templates │ │ └── unit_test.rb.erb │ │ └── concept_generator.rb └── cell │ ├── rails │ ├── collection.rb │ ├── constant_for.rb │ └── testing.rb │ ├── test_case.rb │ ├── caching │ └── notification.rb │ ├── translation.rb │ ├── helper │ └── asset_helper.rb │ ├── railtie.rb │ └── rails.rb ├── .gitignore ├── Rakefile ├── Gemfile ├── gemfiles ├── 5.2.gemfile ├── 6.0.gemfile ├── 6.1.gemfile ├── 7.1.gemfile ├── 7.2.gemfile └── 8.0.gemfile ├── Appraisals ├── README.md ├── LICENSE.txt ├── CHANGES.md ├── cells-rails.gemspec └── .github └── workflows └── ci.yml /test/dummy/app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.7.5 2 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/cells/song/song.css: -------------------------------------------------------------------------------- 1 | .song { background: red; } -------------------------------------------------------------------------------- /test/dummy/app/cells/song/with_escaped.erb: -------------------------------------------------------------------------------- 1 | <%= title %> -------------------------------------------------------------------------------- /test/dummy/app/views/songs/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= cell(:form_for) %> -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/app/assets/images/my_engine/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/cells-rails.rb: -------------------------------------------------------------------------------- 1 | require "cells/rails/version" 2 | require "cells/rails" 3 | -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link logo.jpeg 2 | //= link application.css 3 | -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/README.rdoc: -------------------------------------------------------------------------------- 1 | = MyEngine 2 | 3 | This project rocks and uses MIT-LICENSE. -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/app/concepts/user/views/user.scss: -------------------------------------------------------------------------------- 1 | user { 2 | background: green; 3 | } -------------------------------------------------------------------------------- /lib/cells/rails/version.rb: -------------------------------------------------------------------------------- 1 | module Cells 2 | module Rails 3 | VERSION = "0.1.6" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/lib/my_engine/version.rb: -------------------------------------------------------------------------------- 1 | module MyEngine 2 | VERSION = "0.0.1" 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/app/models/my_engine/user.rb: -------------------------------------------------------------------------------- 1 | module MyEngine 2 | class User 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /lib/rails/generators/cell/templates/view.slim: -------------------------------------------------------------------------------- 1 | h1 <%= class_name %>#<%= @state %> 2 | p Find me in <%= @path %> 3 | -------------------------------------------------------------------------------- /lib/rails/generators/concept/templates/view.slim: -------------------------------------------------------------------------------- 1 | h1 <%= class_name %>#<%= @state %> 2 | p Find me in <%= @path %> 3 | -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/app/concepts/user/views/show.erb: -------------------------------------------------------------------------------- 1 | <%= "Just an empty view, rendered by a cell..." %> 2 | -------------------------------------------------------------------------------- /lib/rails/generators/cell/templates/view.haml: -------------------------------------------------------------------------------- 1 | %h1 2 | <%= class_name %>#<%= @state %> 3 | %p 4 | Find me in <%= @path %> -------------------------------------------------------------------------------- /test/dummy/engines/my_engine/config/routes.rb: -------------------------------------------------------------------------------- 1 | MyEngine::Engine.routes.draw do 2 | get "user" => "user#show" 3 | end 4 | -------------------------------------------------------------------------------- /lib/rails/generators/concept/templates/view.haml: -------------------------------------------------------------------------------- 1 | %h1 2 | <%= class_name %>#<%= @state %> 3 | %p 4 | Find me in <%= @path %> -------------------------------------------------------------------------------- /test/dummy/app/cells/form_tag_cell.rb: -------------------------------------------------------------------------------- 1 | class FormTagCell < Cell::ViewModel 2 | def show 3 | render 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/dummy/app/views/songs/with_escaped.html.erb: -------------------------------------------------------------------------------- 1 |
} 18 | assert_file 'app/cells/blog/latest.erb', %r{app/cells/blog/latest\.erb} 19 | end 20 | 21 | test 'create cell that inherits from custom cell class if specified' do 22 | run_generator %w(Blog --parent=ApplicationCell) 23 | assert_file 'app/cells/blog_cell.rb', /class BlogCell < ApplicationCell/ 24 | end 25 | 26 | test 'work with namespaces' do 27 | run_generator %w(blog/post latest -e erb) 28 | assert_file 'app/cells/blog/post_cell.rb', /class Blog::PostCell < Cell::ViewModel/ 29 | assert_file 'app/cells/blog/post_cell.rb', /def show/ 30 | assert_file 'app/cells/blog/post_cell.rb', /def latest/ 31 | assert_file 'app/cells/blog/post/latest.erb', %r{app/cells/blog/post/latest\.erb} 32 | end 33 | 34 | test 'work with namespaces and haml' do 35 | run_generator %w(blog/post latest -e haml) 36 | assert_file 'app/cells/blog/post_cell.rb', /class Blog::PostCell < Cell::ViewModel/ 37 | assert_file 'app/cells/blog/post/latest.haml', %r{app/cells/blog/post/latest\.haml} 38 | end 39 | 40 | test 'work with namespaces and slim' do 41 | run_generator %w(blog/post latest -e slim) 42 | 43 | assert_file 'app/cells/blog/post_cell.rb', /class Blog::PostCell < Cell::ViewModel/ 44 | assert_file 'app/cells/blog/post/latest.slim', %r{app/cells/blog/post/latest\.slim} 45 | end 46 | 47 | test 'create slim assets with -e slim' do 48 | run_generator %w(blog post latest -e slim) 49 | 50 | assert_file 'app/cells/blog_cell.rb', /class BlogCell < Cell::ViewModel/ 51 | assert_file 'app/cells/blog_cell.rb', /def post/ 52 | assert_file 'app/cells/blog_cell.rb', /def latest/ 53 | assert_file 'app/cells/blog/post.slim', %r{app/cells/blog/post\.slim} 54 | assert_file 'app/cells/blog/post.slim', %r{p} 55 | assert_file 'app/cells/blog/latest.slim', %r{app/cells/blog/latest\.slim} 56 | end 57 | 58 | test 'create haml assets with -e haml' do 59 | run_generator %w(Blog post latest -e haml) 60 | 61 | assert_file 'app/cells/blog_cell.rb', /class BlogCell < Cell::ViewModel/ 62 | assert_file 'app/cells/blog_cell.rb', /def post/ 63 | assert_file 'app/cells/blog_cell.rb', /def latest/ 64 | assert_file 'app/cells/blog/post.haml', %r{app/cells/blog/post\.haml} 65 | assert_file 'app/cells/blog/post.haml', %r{%p} 66 | assert_file 'app/cells/blog/latest.haml', %r{app/cells/blog/latest\.haml} 67 | end 68 | 69 | test 'create test_unit assets with -t test_unit' do 70 | run_generator %w(Blog post latest -t test_unit) 71 | 72 | # file is there. 73 | assert_file 'test/cells/blog_cell_test.rb' 74 | # file contains #cell(..) call. 75 | assert_file 'test/cells/blog_cell_test.rb', /cell\("blog"\).\(:post\)/ 76 | end 77 | 78 | test 'work with namespace and test_unit' do 79 | run_generator %w(blog/post latest -t test_unit) 80 | 81 | assert_file 'test/cells/blog/post_cell_test.rb', /class Blog::PostCellTest < Cell::TestCase/ 82 | end 83 | 84 | test 'create test_unit assets with -t rspec' do 85 | run_generator %w(Blog post latest -t rspec) 86 | 87 | assert_no_file 'test/cells/blog_cell_test.rb' 88 | end 89 | 90 | end 91 | -------------------------------------------------------------------------------- /lib/cell/rails.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_support/concern' 3 | module Cell 4 | def self.rails_version 5 | Gem::Version.new(ActionPack::VERSION::STRING) 6 | end 7 | 8 | # These methods are automatically added to all controllers and views. 9 | module RailsExtensions 10 | module ActionController 11 | def cell(name, model=nil, options={}, constant=::Cell::ViewModel, &block) 12 | options[:context] ||= {} 13 | options[:context][:controller] = self 14 | 15 | constant.cell(name, model, options, &block) 16 | end 17 | 18 | def concept(name, model=nil, options={}, &block) 19 | cell(name, model, options, ::Cell::Concept, &block) 20 | end 21 | end 22 | 23 | module ActionView 24 | # Returns the cell instance for +name+. You may pass arbitrary options to your 25 | # cell. 26 | # 27 | # = cell(:song, title: "Creeping Out Sara").(:show) 28 | def cell(name, *args, &block) 29 | controller.cell(name, *args, &block) 30 | end 31 | 32 | # # See Cells::Rails::ActionController#render_cell. 33 | # def render_cell(name, state, *args, &block) 34 | # ::Cell::Rails.render_cell(name, state, controller, *args, &block) 35 | # end 36 | 37 | def concept(name, *args, &block) 38 | controller.concept(name, *args, &block) 39 | end 40 | end 41 | 42 | # Gets included into Cell::ViewModel in a Rails environment. 43 | module ViewModel 44 | extend ActiveSupport::Concern 45 | 46 | # DISCUSS: who actually uses forgery protection with cells? it is not working since 4, anyway? 47 | # include ActionController::RequestForgeryProtection 48 | included do 49 | extend Uber::Delegates 50 | delegates :parent_controller, :session, :params, :request, :config, :env, :url_options, :default_url_options 51 | # forgery protection. 52 | delegates :parent_controller, :request_forgery_protection_token 53 | end 54 | 55 | def call(*) 56 | super.html_safe 57 | end 58 | 59 | def parent_controller 60 | context[:controller] 61 | end 62 | alias_method :controller, :parent_controller 63 | 64 | def perform_caching? 65 | ::ActionController::Base.perform_caching 66 | end 67 | 68 | def cache_store # we want to use DI to set a cache store in cell/rails. 69 | ::ActionController::Base.cache_store 70 | end 71 | 72 | # In Ruby 2.4.0+, Forwardable prints a warning when you delegate 73 | # to a private or protected method - so `delegates :protect_against_forgery?` 74 | # or `delegates :form_authenticity_token` will print warnings all 75 | # over the place 76 | # 77 | # This workaround prevents warnings being printed 78 | def protect_against_forgery? 79 | controller.send(:protect_against_forgery?) 80 | end 81 | 82 | def form_authenticity_token(**options) 83 | controller.send(:form_authenticity_token, **options) 84 | end 85 | 86 | module ClassMethods 87 | def expand_cache_key(key) 88 | ::ActiveSupport::Cache.expand_cache_key(key, :cells) 89 | end 90 | end 91 | end 92 | 93 | # In Rails, there are about 10 different implementations of #url_for. Rails doesn't like the idea of objects, so they 94 | # have helpers in modules. Those module are now included sequentially into other modules and/or classes. While they 95 | # get included, they might or might not include methods, depending on the including module/class 96 | # (example here: https://github.com/rails/rails/blob/cad20f720c4c6e04584253cd0a23f22b3d43ab0f/actionpack/lib/action_dispatch/routing/url_for.rb#L87). 97 | # 98 | # The outcome is that several module override #url_for, and if you're lucky, this works. If you're not, then #url_for breaks 99 | # due to a raise in one of its basic implementations, introduced in 3.x, fixed in 4.0 and then re-introduced in 4.2 100 | # 101 | # This is extremely frustrating as no one in Rails core seems to tackle this problem and introduces a url object instead 102 | # of this module madness. I have to constantly test and fix it in Cells. With the module below, I'll stop doing this. 103 | # 104 | # Either Rails works with Cells and we fix this in form of a URL object that gets passed into the cell (I'm happy with 105 | # a global object here, too! Wow!) or URL helpers will stop working in Cells and a lot of people will be unhappy. 106 | # 107 | # Anyway, this is the reason we need this patch module. If you have trouble with URLs in Cells, then please ask Rails to 108 | # fix their implementation. Thank you. 109 | module HelpersAreShit 110 | def url_for(options = nil) # from ActionDispatch:R:UrlFor. 111 | case options 112 | when nil 113 | _routes.url_for(url_options.symbolize_keys) 114 | when Hash 115 | _routes.url_for(options.symbolize_keys.reverse_merge!(url_options)) 116 | when String 117 | options 118 | when Array 119 | polymorphic_url(options, options.extract_options!) 120 | else 121 | polymorphic_url(options) 122 | end 123 | end 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /test/dummy/test/caching_test.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'test_helper' 3 | 4 | class DirectorCell < Cell::ViewModel 5 | attr_reader :count 6 | 7 | def initialize(*) 8 | super 9 | @count = 0 10 | end 11 | 12 | cache :tock 13 | 14 | def tock 15 | @count += 1 16 | end 17 | end 18 | 19 | 20 | class CachingUnitTest < Minitest::Spec 21 | before :each do 22 | ActionController::Base.cache_store.clear 23 | ActionController::Base.perform_caching = true 24 | end 25 | 26 | let (:director) { DirectorCell } 27 | let (:cellule) { DirectorCell.new(nil) } 28 | 29 | 30 | describe "::state_cache_key" do 31 | # accepts state name, only. 32 | it { _(director.state_cache_key(:count)).must_equal "cells/director/count/" } 33 | 34 | # accepts hash as key parts 35 | if Cell.rails_version >= Gem::Version.new('4.0') 36 | it { _(director.state_cache_key(:count, b: 2, a: 1)).must_equal "cells/director/count/b/2/a/1" } 37 | else 38 | it { _(director.state_cache_key(:count, b: 2, a: 1)).must_equal "cells/director/count/a=1&b=2" } 39 | end 40 | 41 | # accepts array as key parts 42 | it { _(director.state_cache_key(:count, [1, 2, 3])).must_equal "cells/director/count/1/2/3" } 43 | 44 | # accepts string as key parts 45 | it { _(director.state_cache_key(:count, "1/2")).must_equal "cells/director/count/1/2" } 46 | 47 | # accepts nil as key parts 48 | it { _(director.state_cache_key(:count, nil)).must_equal "cells/director/count/" } 49 | end 50 | 51 | 52 | describe "#state_cached?" do 53 | # true for cached 54 | it { _(cellule.send(:state_cached?, :tock)).must_equal true } 55 | 56 | # false otherwise 57 | it { _(cellule.send(:state_cached?, :sing)).must_equal false } 58 | end 59 | 60 | 61 | describe "#cache?" do 62 | # true for cached 63 | it { _(cellule.cache?(:tock)).must_equal true } 64 | 65 | # false otherwise 66 | it { _(cellule.cache?(:sing)).must_equal false } 67 | 68 | describe "perform_caching turned off" do 69 | after do 70 | ::ActionController::Base.perform_caching = true 71 | end 72 | 73 | # always false 74 | it do 75 | ::ActionController::Base.perform_caching = false 76 | _(cellule.cache?(:sing)).must_equal false 77 | _(cellule.cache?(:sing)).must_equal false 78 | end 79 | end 80 | 81 | describe "#cache_store" do 82 | # rails cache store per default. 83 | it { _(cellule.cache_store).must_equal ActionController::Base.cache_store } 84 | end 85 | end 86 | 87 | 88 | # describe ".expire_cache_key" do 89 | # before :each do 90 | # @key = @class.state_cache_key(:tock) 91 | # puts "====== key is #{@key}" 92 | # assert_equal "1", render_cell(:director, :tock) 93 | # assert_equal "1", @class.cache_store.read(@key) 94 | # end 95 | 96 | # it "delete the state from cache" do 97 | # @class.expire_cache_key(@key) 98 | # assert_not @class.cache_store.read(@key) 99 | # end 100 | 101 | # it "be available in controllers for sweepers" do 102 | # MusicianController.new.expire_cell_state(DirectorCell, :tock) 103 | # assert_not @class.cache_store.read(@key) 104 | # end 105 | 106 | # it "accept cache options" do 107 | # key = @class.state_cache_key(:tock, volume: 9) 108 | # assert Cell::Rails.cache_store.write(key, 'ONE!') 109 | 110 | # MusicianController.new.expire_cell_state(DirectorCell, :tock, volume: 9) 111 | # assert_equal "1", @class.cache_store.read(@key) 112 | # assert_not ::Cell::Rails.cache_store.read(key) 113 | # end 114 | # end 115 | end 116 | 117 | 118 | class CachingTest < Minitest::Spec 119 | class DirectorCell < Cell::ViewModel 120 | def initialize(counter=0) 121 | super 122 | @counter = counter 123 | end 124 | 125 | def show # public method. 126 | @counter 127 | end 128 | 129 | 130 | cache :utf8 131 | 132 | def utf8 133 | "æøå" # or any other UTF-8 string 134 | end 135 | end 136 | 137 | before :each do 138 | ActionController::Base.cache_store.clear 139 | ActionController::Base.perform_caching = true 140 | end 141 | 142 | # let (:cell) { DirectorCell.new(nil) } 143 | def director_cell(*args) 144 | DirectorCell.new(*args) 145 | end 146 | 147 | # no caching when turned off. 148 | it do 149 | director_cell.class.cache :show 150 | ActionController::Base.perform_caching = false 151 | 152 | _(director_cell(1).call).must_equal "1" 153 | _(director_cell(2).call).must_equal "2" 154 | end 155 | 156 | # cache forever when no options. 157 | it do 158 | director_cell.class.cache :show 159 | _(director_cell(1).call).must_equal "1" 160 | _(director_cell(2).call).must_equal "1" 161 | end 162 | 163 | # caches with string state name. 164 | it do 165 | director_cell.class.cache :show 166 | _(director_cell(1).("show")).must_equal "1" 167 | _(director_cell(2).("show")).must_equal "1" 168 | end 169 | 170 | 171 | # no caching when state not configured. 172 | it do 173 | director_cell.class.class_eval do 174 | def dictate 175 | @counter 176 | end 177 | end 178 | 179 | _(director_cell(1).call(:dictate)).must_equal "1" 180 | _(director_cell(2).call(:dictate)).must_equal "2" 181 | end 182 | 183 | # compute key with cell properties from #initialize. 184 | it do 185 | director_cell.class.cache :show do 186 | @counter < 3 ? {count: "<"} : {count: ">"} 187 | end 188 | 189 | _(director_cell(1).call).must_equal "1" 190 | _(director_cell(2).call).must_equal "1" 191 | _(director_cell(3).call).must_equal "3" 192 | _(director_cell(4).call).must_equal "3" 193 | end 194 | 195 | # compute key with instance method 196 | it do 197 | director_cell.class.cache :show, :version 198 | director_cell.class.class_eval do 199 | def version 200 | @counter < 3 ? {count: "<"} : {count: ">"} 201 | end 202 | end 203 | 204 | _(director_cell(1).call).must_equal "1" 205 | _(director_cell(2).call).must_equal "1" 206 | _(director_cell(3).call).must_equal "3" 207 | _(director_cell(4).call).must_equal "3" 208 | end 209 | 210 | # allow returning strings for key 211 | it do 212 | director_cell.class.cache :show do 213 | @counter < 3 ? "<" : ">" 214 | end 215 | 216 | _(director_cell(1).call).must_equal "1" 217 | _(director_cell(2).call).must_equal "1" 218 | _(director_cell(3).call).must_equal "3" 219 | _(director_cell(4).call).must_equal "3" 220 | end 221 | 222 | # allows conditional ifs. 223 | it do 224 | director_cell.class.cache :show, if: lambda { @counter < 3 } 225 | 226 | _(director_cell(1).call).must_equal "1" 227 | _(director_cell(2).call).must_equal "1" 228 | _(director_cell(3).call).must_equal "3" 229 | _(director_cell(4).call).must_equal "4" 230 | end 231 | 232 | # allows conditional ifs with instance method. 233 | it do 234 | director_cell.class.class_eval do 235 | cache :show, if: :smaller? 236 | 237 | def smaller? 238 | @counter < 3 239 | end 240 | end 241 | 242 | _(director_cell(1).call).must_equal "1" 243 | _(director_cell(2).call).must_equal "1" 244 | _(director_cell(3).call).must_equal "3" 245 | _(director_cell(4).call).must_equal "4" 246 | end 247 | 248 | 249 | unless ::ActionPack::VERSION::MAJOR == 3 and ::ActionPack::VERSION::MINOR >= 2 # bug in 3.2. 250 | describe "utf-8" do 251 | before do 252 | @key = director_cell.class.state_cache_key(:utf8) 253 | end 254 | 255 | it "has the correct encoding when reading from cache" do 256 | assert_equal "UTF-8", director_cell.call(:utf8).encoding.to_s 257 | assert_equal "UTF-8", director_cell.cache_store.read(@key).encoding.to_s 258 | end 259 | end 260 | end 261 | 262 | # options are passed through to cache store. 263 | # :expires_in. 264 | # tags: lambda { |one, two, three| "#{one},#{two},#{three}" } 265 | class CacheStore 266 | attr_reader :fetch_args 267 | 268 | def fetch(*args) 269 | @fetch_args = args 270 | end 271 | end 272 | 273 | it do 274 | cellule = self.director_cell 275 | 276 | cellule.instance_eval do 277 | def cache_store; 278 | @cache_store ||= CacheStore.new; 279 | end 280 | end 281 | 282 | cellule.class.cache :show, expires_in: 1.minutes, tags: lambda { self.class.to_s } 283 | cellule.call 284 | _(cellule.cache_store.fetch_args).must_equal ["cells/caching_test/director/show/", {expires_in: 60, tags: "CachingTest::DirectorCell"}] 285 | end 286 | end 287 | 288 | 289 | class CachingInheritanceTest < CachingTest 290 | class DirectorCell < ::DirectorCell 291 | cache :show, expires_in: 10.minutes do 292 | "v1" 293 | end 294 | end 295 | 296 | class DirectorsSonCell < DirectorCell 297 | end 298 | 299 | class DirectorsDaughterCell < ::DirectorCell 300 | cache :show, expires_in: 9.minutes do 301 | "v2" 302 | end 303 | end 304 | 305 | end 306 | --------------------------------------------------------------------------------