Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Garner: Anatomy of a Ruby Gem

Garner: Anatomy of a Ruby Gem

Originally presented on May 21, 2014 at the "Anatomy of a Gem" Meetup: http://www.meetup.com/Anatomy-of-a-Ruby-Gem/events/176176352/

Find me on Twitter: https://twitter.com/fancyremarker

Frank Macreery

May 21, 2014
Tweet

More Decks by Frank Macreery

Other Decks in Programming

Transcript

  1. FRAGMENT CACHING!!! -# app/views/posts/show.html.haml = cache [ "v1", @post ]

    do %h1 = @post.title %section = @post.body %section = render @post.comments
  2. -# app/views/posts/index.html.haml - @posts.each do |post| = cache [ "v1",

    post ] do %section = @post.summary -# app/views/posts/show.html.haml = cache [ "v1", @post ] do %h1 = @post.title %section = @post.body %section = render @post.comments
  3. -# app/views/posts/index.html.haml - @posts.each do |post| = cache [ "v1",

    post ] do %section = @post.summary -# app/views/posts/show.html.haml = cache [ "v1", @post ] do %h1 = @post.title %section = @post.body %section = render @post.comments
  4. There are only two hard things in Computer Science: cache

    invalidation and naming things. — Phil Karlton
  5. Inside GARNER “Context” A code location from which Garner is

    invoked (e.g., Rack/Grape request context)
  6. “Context” get '/artwork/:id' garner.bind(Artwork.identify(params[:id])) do # One long operation... end

    garner.bind(Artwork.identify(params[:id])) do # Another long operation... Same binding! end end
  7. “Context” artwork = garner.bind(Artwork.identify(params[:id]) do Artwork.find(params[:id]) end error!('Artwork Not Found',

    404) unless artwork error!('Forbidden', 403) unless artwork.can_be?(:read, current_user) garner.bind(artwork).different_for(:partners) do if params[:format].to_s == "txt" error!('Artwork Image Not Found', 404) unless artwork.has_image? artwork.default_image.to_ascii elsif cdn_request? artwork.as_json(properties: :public) else artwork.as_json_for(current_user) end end
  8. “Context” artwork = garner.bind(Artwork.identify(params[:id]) do Artwork.find(params[:id]) end error!('Artwork Not Found',

    404) unless artwork error!('Forbidden', 403) unless artwork.can_be?(:read, current_user) garner.bind(artwork).different_for(:partners) do if params[:format].to_s == "txt" error!('Artwork Image Not Found', 404) unless artwork.has_image? artwork.default_image.to_ascii elsif cdn_request? artwork.as_json(properties: :public) else artwork.as_json_for(current_user) end end
  9. “Context” artwork = garner.bind(Artwork.identify(params[:id]) do Artwork.find(params[:id]) end error!('Artwork Not Found',

    404) unless artwork error!('Forbidden', 403) unless artwork.can_be?(:read, current_user) garner.bind(artwork).different_for(:partners) do if params[:format].to_s == "txt" error!('Artwork Image Not Found', 404) unless artwork.has_image? artwork.default_image.to_ascii elsif cdn_request? artwork.as_json(properties: :public) else artwork.as_json_for(current_user) end end
  10. “Context” artwork = garner.bind(Artwork.identify(params[:id]) do Artwork.find(params[:id]) end error!('Artwork Not Found',

    404) unless artwork error!('Forbidden', 403) unless artwork.can_be?(:read, current_user) garner.bind(artwork).different_for(:partners) do if params[:format].to_s == "txt" error!('Artwork Image Not Found', 404) unless artwork.has_image? artwork.default_image.to_ascii elsif cdn_request? artwork.as_json(properties: :public) else artwork.as_json_for(current_user) end end
  11. RequestGet Strategy module Garner::Strategies::Context::Key class RequestGet < Base def self.apply(identity,

    ruby_context = nil) return identity unless (ruby_context.respond_to?(:request)) request = ruby_context.request if request && ["GET", "HEAD"].include?(request.request_method) identity.key(field => request.GET.dup) end identity end end end
  12. Jsonp Strategy module Garner::Strategies::Context::Key class Jsonp < Base def self.apply(identity,

    ruby_context = nil) key_hash = identity.key_hash return identity unless key_hash[field] key_hash[field].delete("callback") key_hash[field].delete("_") identity end end end
  13. Caller Strategy module Garner::Strategies::Context::Key class Caller < Base def self.apply(identity,

    ruby_context = nil) value = nil if ruby_context.send(:caller) ruby_context.send(:caller).compact.each do |line| parts = line.match(/(?<filename>[^:]+)\:(?<lineno>[^:]+)/) file = (Pathname.new(parts[:filename]).realpath.to_s rescue nil) next if file.nil? || file == "" next if file.include?(File.join("lib", "garner")) if (root = Garner.config.caller_root) root += File::SEPARATOR unless root[-1] == File::SEPARATOR next unless file =~ /^#{root}/ value = "#{file.gsub(root || "", "")}:#{parts[:lineno]}" else value = "#{file}:#{parts[:lineno]}" end break end end value ? identity.key(field => value) : identity end end end
  14. Configuration Garner.configure do |config| config.context_key_strategies = [ Garner::Strategies::Context::Key::Caller, Garner::Strategies::Context::Key::RequestGet, Garner::Strategies::Context::Key::RequestPost,

    Garner::Strategies::Context::Key::RequestPath, Garner::Strategies::Context::Key::TrackedGrapeHeaders, Garner::Strategies::Context::Key::Auth ] end
  15. Unit Testing describe Garner::Strategies::Context::Key::Caller do before(:each) do @cache_identity = Garner::Cache::Identity.new

    @mock_context = double("object") end subject { Garner::Strategies::Context::Key::Caller } it_behaves_like "Garner::Strategies::Context::Key strategy" it "ignores blank caller location" do @mock_context.stub(:caller) { [""] } subject.apply(@cache_identity, @mock_context) @cache_identity.key_hash[:caller].should be_nil end # ... end
  16. Unit Testing describe Garner::Strategies::Context::Key::Caller do before(:each) do @cache_identity = Garner::Cache::Identity.new

    @mock_context = double("object") end subject { Garner::Strategies::Context::Key::Caller } it_behaves_like "Garner::Strategies::Context::Key strategy" it "ignores blank caller location" do @mock_context.stub(:caller) { [""] } subject.apply(@cache_identity, @mock_context) @cache_identity.key_hash[:caller].should be_nil end # ... end
  17. Unit Testing describe Garner::Strategies::Context::Key::Caller do before(:each) do @cache_identity = Garner::Cache::Identity.new

    @mock_context = double("object") end subject { Garner::Strategies::Context::Key::Caller } it_behaves_like "Garner::Strategies::Context::Key strategy" it "ignores blank caller location" do @mock_context.stub(:caller) { [""] } subject.apply(@cache_identity, @mock_context) @cache_identity.key_hash[:caller].should be_nil end # ... end
  18. Unit Testing describe Garner::Strategies::Context::Key::Caller do before(:each) do @cache_identity = Garner::Cache::Identity.new

    @mock_context = double("object") end subject { Garner::Strategies::Context::Key::Caller } it_behaves_like "Garner::Strategies::Context::Key strategy" it "ignores blank caller location" do @mock_context.stub(:caller) { [""] } subject.apply(@cache_identity, @mock_context) @cache_identity.key_hash[:caller].should be_nil end # ... end
  19. Unit Testing describe Garner::Strategies::Context::Key::Caller do before(:each) do @cache_identity = Garner::Cache::Identity.new

    @mock_context = double("object") end subject { Garner::Strategies::Context::Key::Caller } it_behaves_like "Garner::Strategies::Context::Key strategy" it "ignores blank caller location" do @mock_context.stub(:caller) { [""] } subject.apply(@cache_identity, @mock_context) @cache_identity.key_hash[:caller].should be_nil end # ... end