{"id":13858656,"url":"https://github.com/kematzy/sinatra-cache","last_synced_at":"2025-07-14T01:31:16.916Z","repository":{"id":418244,"uuid":"37970","full_name":"kematzy/sinatra-cache","owner":"kematzy","description":"A Sinatra Extension that makes Page and Fragment Caching easy","archived":false,"fork":false,"pushed_at":"2012-05-11T06:10:59.000Z","size":311,"stargazers_count":161,"open_issues_count":5,"forks_count":21,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-10-28T22:10:59.032Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kematzy.png","metadata":{"files":{"readme":"README.rdoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2008-07-28T19:35:03.000Z","updated_at":"2023-05-21T22:57:38.000Z","dependencies_parsed_at":"2022-08-16T10:25:10.214Z","dependency_job_id":null,"html_url":"https://github.com/kematzy/sinatra-cache","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kematzy%2Fsinatra-cache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kematzy%2Fsinatra-cache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kematzy%2Fsinatra-cache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kematzy%2Fsinatra-cache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kematzy","download_url":"https://codeload.github.com/kematzy/sinatra-cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225938637,"owners_count":17548524,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-05T03:02:16.450Z","updated_at":"2024-11-22T17:30:19.228Z","avatar_url":"https://github.com/kematzy.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"= Sinatra::Cache\n\nA Sinatra Extension that makes Page and Fragment Caching easy.\n\n\n== IMPORTANT INFORMATION\n\n\u003cb\u003eThis is a completely rewritten extension that basically breaks all previous versions of it.\u003c/b\u003e\n\nSo use with care!  You have been warned ;-)\n\n---- \n\n\n\nWith that said, on to the real stuff.\n\n\n== Installation\n  \n  #  Add RubyGems.org (former Gemcutter) to your RubyGems sources \n  $  gem sources -a http://rubygems.org\n\n  $  (sudo)? gem install sinatra-cache \n\n== Dependencies\n\nThis Gem depends upon the following:\n\n=== Runtime:\n\n* sinatra ( \u003e= 1.0.a )\n* sinatra-outputbuffer[http://github.com/kematzy/sinatra-outputbuffer] (\u003e= 0.1.0)\n\nOptionals:\n\n* sinatra-settings[http://github.com/kematzy/sinatra-settings] (\u003e= 0.1.1) # to view default settings in a browser display.\n\n=== Development \u0026 Tests:\n\n* sinatra-tests (\u003e= 0.1.6)\n* rspec (\u003e= 1.3.0 )\n* rack-test (\u003e= 0.5.3)\n* rspec_hpricot_matchers (\u003e= 0.1.0)\n* fileutils\n* sass\n* ostruct\n* yaml\n* json\n\n\n== Getting Started\n\nTo start caching your app's ouput, just require and register \nthe extension in your sub-classed Sinatra app:\n\n  require 'sinatra/cache'\n  \n  class YourApp \u003c Sinatra::Base\n    \n    # NB! you need to set the root of the app first\n    set :root, '/path/2/the/root/of/your/app'\n    \n    register(Sinatra::Cache)\n    \n    set :cache_enabled, true  # turn it on\n    \n    \u003csnip...\u003e\n    \n  end\n  \n  \nIn your \"classic\" Sinatra app, you just require the extension and set some key settings, like this:\n\n  require 'rubygems'\n  require 'sinatra'\n  require 'sinatra/cache'\n  \n  # NB! you need to set the root of the app first\n  set :root, '/path/2/the/root/of/your/app'\n  set :public, '/path/2/public'\n  \n  set :cache_enabled, true  # turn it on\n  \n  \u003csnip...\u003e\n  \n\n\nThat's more or less it. \n\nYou should now be caching your output by default, in \u003ctt\u003e:production\u003c/tt\u003e mode, as long as you use \none of Sinatra's render methods:\n\n  erb(),  erubis(), haml(), sass(), builder(), etc..\n  \n...or any render method that uses \u003ctt\u003eSinatra::Templates#render()\u003c/tt\u003e as its base.\n\n\n\n== Configuration Settings\n\nThe default settings should help you get moving quickly, and are fairly common sense based.\n\n\n==== \u003ctt\u003e:cache_enabled\u003c/tt\u003e\n\nThis setting toggles the cache functionality On / Off. \nDefault is: \u003ctt\u003efalse\u003c/tt\u003e\n\n\n==== \u003ctt\u003e:cache_environment\u003c/tt\u003e\n\nSets the environment during which the cache functionality is active. \nDefault is: \u003ctt\u003e:production\u003c/tt\u003e\n\n\n==== \u003ctt\u003e:cache_page_extension\u003c/tt\u003e+ \n\nSets the default file extension for cached files. \nDefault is: \u003ctt\u003e.html\u003c/tt\u003e\n\n\n==== \u003ctt\u003e:cache_output_dir\u003c/tt\u003e\n\nSets cache directory where the cached files are stored. \nDefault is:  == \"/path/2/your/app/public\"\n\nAlthough you can set it to the more ideal '\u003ctt\u003e..public/system/cache/\u003c/tt\u003e' \nif you can get that to work with your webserver setup.\n\n\n==== \u003ctt\u003e:cache_fragments_output_dir\u003c/tt\u003e\n\nSets the directory where cached fragments are stored. \nDefault is the '../tmp/cache_fragments/' directory at the root of your app.\n\nThis is for security reasons since you don't really want your cached fragments publically available.\n\n\n==== \u003ctt\u003e:cache_fragments_wrap_with_html_comments\u003c/tt\u003e\n\nThis setting toggles the wrapping of cached fragments in HTML comments. (see below)\nDefault is: \u003ctt\u003etrue\u003c/tt\u003e\n\n\n==== \u003ctt\u003e:cache_logging\u003c/tt\u003e\n\nThis setting toggles the logging of various cache calls. If the app has access to the \u003ctt\u003e#logger\u003c/tt\u003e method,\ncurtesy of Sinatra::Logger[http://github.com/kematzy/sinatra-logger] then it will log there, otherwise logging \nis silent.\n\nDefault is: \u003ctt\u003etrue\u003c/tt\u003e\n\n\n==== \u003ctt\u003e:cache_logging_level\u003c/tt\u003e\n\nSets the level at which the cache logger should log it's messages. \nDefault is: \u003ctt\u003e:info\u003c/tt\u003e\n\nAvailable options are: [:fatal, :error, :warn, :info, :debug]\n\n\n== Basic Page Caching\n\nBy default caching only happens in \u003ctt\u003e:production\u003c/tt\u003e mode, and via the Sinatra render methods, erb(), etc,\n\nSo asuming we have the following setup (continued from above)\n\n\n  class YourApp\n    \n    \u003csnip...\u003e\n    \n    set :cache_output_dir, \"/full/path/2/app/root/public/system/cache\"\n    \n    \u003csnip...\u003e\n    \n    get('/') { erb(:index) }            # =\u003e cached as '../index.html'\n    \n    get('/contact') { erb(:contact) }   # =\u003e cached as '../contact.html'\n    \n    # NB! the trailing slash on the URL\n    get('/about/') { erb(:about) }      # =\u003e cached as '../about/index.html'\n    \n    get('/feed.rss') { builder(:feed) }  # =\u003e cached as '../feed.rss' \n    # NB! uses the extension of the passed URL, \n    # but DOES NOT ensure the format of the content based on the extension provided.\n    \n    # complex URL with multiple possible params  \n    get %r{/articles/?([\\s\\w-]+)?/?([\\w-]+)?/?([\\w-]+)?/?([\\w-]+)?/?([\\w-]+)?/?([\\w-]+)?}  do\n      erb(:articles)\n    end\n    # with the '/articles/a/b/c  =\u003e cached as ../articles/a/b/c.html\n    \n    # NB! the trailing slash on the URL\n    # with the '/articles/a/b/c/  =\u003e cached as ../articles/a/b/c/index.html\n    \n    # CSS caching via Sass  # =\u003e cached as '.../css/screen.css'\n    get '/css/screen.css' do \n      content_type 'text/css'\n      sass(:'css/screen')\n    end\n    \n    # to turn off caching on certain pages.\n    get('/dont/cache/this/page') { erb(:aview, :cache =\u003e false) }   # =\u003e is NOT cached\n    \n    \n    # NB! any query string params - [ /?page=X\u0026id=y ] - are stripped off and TOTALLY IGNORED \n    # during the caching process.\n    \n  end\n\nOK, that's about all you need to know about basic Page Caching right there. Read the above example\ncarefully until you understand all the variations.\n\n\n== Fragment Caching \n\nIf you just need to cache a fragment of a page, then you'd do as follows:\n\n  class YourApp\n    \n    set :cache_fragments_output_dir, \"/full/path/2/fragments/store/location\"\n    \n  end\n  \nThen in your views / layouts add the following:\n\n  \u003c% cache_fragment(:name_of_fragment) do %\u003e\n   # do something worth caching\n  \u003c% end %\u003e\n \n\nEach fragment is stored in the same directory structure as your request\nso, if you have a request like this:\n  \n  get '/articles/2010/02' ...\n \n...the cached fragment will be stored as:\n\n  ../tmp/cache_fragments/articles/2010/02/\u003c name_of_fragment \u003e.html\n \nThis enables you to use similar names for your fragments or have \nmultiple URLs use the same view / layout.\n\n\n=== An important limitation\n\nThe fragment caching is dependent upon the final URL, so in the case of \na blog, where each article uses the same view, but through different URLs,\neach of the articles would cache it's own fragment, which is ineffecient.\n\nTo sort-of deal with this limitation I have temporarily added a very hackish \n'fix' through adding a 2nd parameter (see example below), which will remove the \nlast part of the URL and use the rest of the URL as the stored fragment path.\n\nSo given the URL:\n\n  get '/articles/2010/02/fragment-caching-with-sinatra-cache' ...\n\nand the following \u003ctt\u003e#cache_fragment\u003c/tt\u003e declaration in your view\n\n  \u003c% cache_fragment(:name_of_fragment, :shared) do %\u003e\n    # do something worth caching\n  \u003c% end %\u003e\n\n...the cached fragment would be stored as:\n\n  ../tmp/cache_fragments/articles/2010/02/\u003c name_of_fragment \u003e.html\n\nAny other URLs with the same URL root, like...\n\n  get '/articles/2010/02/writing-sinatra-extensions' ...\n\n... would use the same cached fragment.\n\n\n\u003cb\u003eNB!\u003c/b\u003e currently only supports one level, but Your fork might fix that ;-)\n  \n\n== Cache Expiration\n\n\u003cb\u003eUnder development, and not entirely final.\u003c/b\u003e See Todo's below for more info.\n\n\nTo expire a cached item - file or fragment you use the :cache_expire() method.\n\n  \n  cache_expire('/contact')  =\u003e  expires ../contact.html\n  \n  \n  # NB! notice the trailing slash\n  cache_expire('/contact/')  =\u003e  expires ../contact/index.html\n  \n  \n  cache_expire('/feed.rss')  =\u003e  expires ../feed.rss\n  \n\nTo expire a cached fragment:\n  \n  cache_expire('/some/path', :fragment =\u003e :name_of_fragment )  \n    \n    =\u003e  expires ../some/path/:name_of_fragment.html\n\n\n\n== A few important points to consider \n\n\n=== The DANGERS of URL query string params\n\nBy default the caching ignores the query string params, but that's not the only problem with query params.\n\nLet's say you have a URL like this:\n\n  /products/?product_id=111\n\nand then inside that template [ .../views/products.erb ], you use the \u003ctt\u003eparams[:product_id]\u003c/tt\u003e \nparam passed in for some purpose.\n\n  \u003cul\u003e\n    \u003cli\u003eProduct ID: \u003c%= params[:product_id] %\u003e\u003c/li\u003e  # =\u003e 111\n    ...\n  \u003c/ul\u003e\n  \nIf you cache this URL, then the cached file [ ../cache/products.html ] will be stored with that\nvalue embedded. Obviously not ideal for any other similar URLs with different \u003ctt\u003eproduct_id\u003c/tt\u003e's\n\nTo overcome this issue, use either of these two methods.\n\n  # in your_app.rb\n  \n  # turning off caching on this page\n  \n  get '/products/' do\n    ...\n    erb(:products, :cache =\u003e false)\n  end\n  \n  # or\n  \n  # rework the URLs to something like '/products/111 '\n  \n  get '/products/:product_id' do\n    ...\n    erb(:products)\n  end\n  \n  \n\nThats's about all the information you need to know.\n\n\n== RTFM\n\nIf the above is not clear enough, please check the Specs for a better understanding.\n\n\n== Errors / Bugs\n\nIf something is not behaving intuitively, it is a bug, and should be reported.\nReport it here: http://github.com/kematzy/sinatra-cache/issues \n\n\n== TODOs\n\n* Improve the fragment caching functionality\n  \n  * Decide on how to handle site-wide shared fragments.\n  \n  * Make the shared fragments more dynamic or usable\n\n* Work out how to use the \u003ctt\u003ecache_expire()\u003c/tt\u003e functionality in a logical way.\n\n* Work out and include instructions on how to use a '../public/custom/cache/dir' with Passenger.\n\n* Enable time-based / date-based cache expiry and regeneration of the cached-pages. [ht oakleafs]\n\n* Enable .gz version of the cached file, further reducing the processing on the server. [ht oakleafs]\n  It would be killer to have \u003cb\u003ean extra .gz file next to the cached file\u003c/b\u003e. That way, in Apache, you set it up like that:\n    \n      RewriteCond %{HTTP:Accept-Encoding} gzip\n      RewriteCond %{REQUEST_FILENAME}.gz$ -f\n      RewriteRule ^(.*)$ $1.gz [L,QSA]\n  \n  And it should serve the compressed file if available.\n\n* Write more tests to ensure everything is very solid.\n\n* Any other improvements you or I can think of.\n\n\n== Note on Patches/Pull Requests\n \n* Fork the project.\n* Make your feature addition or bug fix.\n* Add tests for it. This is important so I don't break it in a future version unintentionally.\n* Commit, do not mess with rakefile, version, or history.\n  * (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)\n* Send me a pull request. Bonus points for topic branches.\n\n== Copyright\n\nCopyright (c) 2009-2010 kematzy. Released under the MIT License.\n\nSee LICENSE for details.\n\n=== Credits\n\nA big \u003cb\u003eThank You!\u003c/b\u003e goes to rtomayko[http://github/rtomayko], blakemizerany[http://github.com/blakemizerany/] \nand others working on the Sinatra framework.\n\n=== Inspirations\n\nInspired by code from Rails[http://rubyonrails.com/] \u0026 Merb[http://merbivore.com/]\nand other sources\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkematzy%2Fsinatra-cache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkematzy%2Fsinatra-cache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkematzy%2Fsinatra-cache/lists"}