{"action":"created","issue":{"url":"https://api.github.com/repos/rspec/rspec-core/issues/2036","labels_url":"https://api.github.com/repos/rspec/rspec-core/issues/2036/labels{/name}","comments_url":"https://api.github.com/repos/rspec/rspec-core/issues/2036/comments","events_url":"https://api.github.com/repos/rspec/rspec-core/issues/2036/events","html_url":"https://github.com/rspec/rspec-core/issues/2036","id":96928307,"number":2036,"title":"Cleaning up resources held by memoized objects is awkward","user":{"login":"trejkaz","id":43236,"avatar_url":"https://avatars.githubusercontent.com/u/43236?v=3","gravatar_id":"","url":"https://api.github.com/users/trejkaz","html_url":"https://github.com/trejkaz","followers_url":"https://api.github.com/users/trejkaz/followers","following_url":"https://api.github.com/users/trejkaz/following{/other_user}","gists_url":"https://api.github.com/users/trejkaz/gists{/gist_id}","starred_url":"https://api.github.com/users/trejkaz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trejkaz/subscriptions","organizations_url":"https://api.github.com/users/trejkaz/orgs","repos_url":"https://api.github.com/users/trejkaz/repos","events_url":"https://api.github.com/users/trejkaz/events{/privacy}","received_events_url":"https://api.github.com/users/trejkaz/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-07-23T23:44:26Z","updated_at":"2015-07-24T03:22:55Z","closed_at":null,"body":"Let's say I have a memoized thing.\r\n\r\n    let(:image) { DiskImage.new('temp/image.dd') }\r\n\r\nNow let's say this `DiskImage` thing holds a file handle, which is pretty likely, because you're not going to read an entire disk image into memory at the start of every test. I have to close the file handle, otherwise the temp file containing the image can't be deleted and the test fails in an after block somewhere else. So intuitively, I might write this:\r\n\r\n    after(:each) { image.close }\r\n\r\nBut now for tests which don't use the lazy object, the after block is instantiating it and performing costly initialisation, just to close it again.\r\n\r\nOne workaround is like this:\r\n\r\n    let(:image) { DiskImage.new('temp/image.dd'); @image_created = true }\r\n    after(:each) { image.close if @image_created }\r\n\r\nI find this fairly ugly because now the let block initialises two things and it's difficult to generalise.\r\n\r\nAnother workaround is to add another level of lazy wrapping:\r\n\r\n    class Lazy\r\n      def initialize(path, &create_block, &cleanup_block)\r\n        @path = path\r\n        @create_block = create_block\r\n        @cleanup_block = cleanup_block\r\n      end\r\n\r\n      # really I'd do this via method_missing and probably use BasicObject, but for illustration\r\n      def my_method\r\n        instance.my_method\r\n      end\r\n\r\n      def close\r\n        cleanup_block.call(@instance) if @instance\r\n      end\r\n\r\n    private\r\n\r\n      def instance\r\n        @instance ||= create_block.call\r\n      end\r\n    end\r\n\r\n    let(:image) { Lazy.new { DiskImage.new('temp/image.dd') } }\r\n    after(:each) { image.close }\r\n\r\nThis way at least I don't have to worry about the expensive object being created unnecessarily, at least, but it still seems inelegant because I'm reimplementing lazy initialisation, something which the framework already provides.\r\n\r\nWhat would be ideal is some kind of API on let itself, like this:\r\n\r\n    let(:image) { DiskImage.new('temp/image.dd') }.afterwards { |image| image.close }\r\n\r\nOr maybe even\r\n\r\n    let(:image) { DiskImage.new('temp/image.dd') }.afterwards(:close)\r\n\r\nIt seems like let could return an object which could chain additional methods like this.\r\n"},"comment":{"url":"https://api.github.com/repos/rspec/rspec-core/issues/comments/124307723","html_url":"https://github.com/rspec/rspec-core/issues/2036#issuecomment-124307723","issue_url":"https://api.github.com/repos/rspec/rspec-core/issues/2036","id":124307723,"user":{"login":"JonRowe","id":162976,"avatar_url":"https://avatars.githubusercontent.com/u/162976?v=3","gravatar_id":"","url":"https://api.github.com/users/JonRowe","html_url":"https://github.com/JonRowe","followers_url":"https://api.github.com/users/JonRowe/followers","following_url":"https://api.github.com/users/JonRowe/following{/other_user}","gists_url":"https://api.github.com/users/JonRowe/gists{/gist_id}","starred_url":"https://api.github.com/users/JonRowe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JonRowe/subscriptions","organizations_url":"https://api.github.com/users/JonRowe/orgs","repos_url":"https://api.github.com/users/JonRowe/repos","events_url":"https://api.github.com/users/JonRowe/events{/privacy}","received_events_url":"https://api.github.com/users/JonRowe/received_events","type":"User","site_admin":false},"created_at":"2015-07-24T03:22:55Z","updated_at":"2015-07-24T03:22:55Z","body":"> Surely if someone's specs never call afterwards, it would simply not add the stuff to run afterwards, meaning there wouldn't even be a place for the \"performance issue\" to exist.\r\n\r\nDepends on the implementation, I was merely highlighting an issue that would have to be taken into consideration. As you also pointed out we don't want to run the `afterwards` code if the let isn't used, but we don't want to check wether an `afterwards` block has been set on every single let call, hence my psuedo code suggestion.\r\n"}}