{"action":"opened","number":332,"pull_request":{"url":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332","id":27146371,"html_url":"https://github.com/symfony/AsseticBundle/pull/332","diff_url":"https://github.com/symfony/AsseticBundle/pull/332.diff","patch_url":"https://github.com/symfony/AsseticBundle/pull/332.patch","issue_url":"https://api.github.com/repos/symfony/AsseticBundle/issues/332","number":332,"state":"open","locked":false,"title":"Use the etag in the internal AssetCache cache key","user":{"login":"EvanPurkhiser","id":1421724,"avatar_url":"https://avatars.githubusercontent.com/u/1421724?v=3","gravatar_id":"","url":"https://api.github.com/users/EvanPurkhiser","html_url":"https://github.com/EvanPurkhiser","followers_url":"https://api.github.com/users/EvanPurkhiser/followers","following_url":"https://api.github.com/users/EvanPurkhiser/following{/other_user}","gists_url":"https://api.github.com/users/EvanPurkhiser/gists{/gist_id}","starred_url":"https://api.github.com/users/EvanPurkhiser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/EvanPurkhiser/subscriptions","organizations_url":"https://api.github.com/users/EvanPurkhiser/orgs","repos_url":"https://api.github.com/users/EvanPurkhiser/repos","events_url":"https://api.github.com/users/EvanPurkhiser/events{/privacy}","received_events_url":"https://api.github.com/users/EvanPurkhiser/received_events","type":"User","site_admin":false},"body":"When using the AsseticController to serve assets that have a filter applied to them implementing the [`DependencyExtractorInterface`](https://github.com/kriswallsmith/assetic/blob/master/src/Assetic/Filter/DependencyExtractorInterface.php) the etag will be correctly computed using `AssetFactory::getLastModified`, which properly takes into account filters marked with this interface.\r\n\r\nHowever. The asset is then cached using the `AssetCache`, which only takes into consideration the modification time of the asset itself, and _does not_ take into account the modification time of dependencies. The effect is that when a dependency of an asset is changed (for example a SCSS file that imports another SCSS file) the etag will correctly differ and the response will be considered modified, but the `AssetCache` will incorrectly assume that the asset has not changed. The root cause is that assets derive their last modified independently from any other asset, so `AssetCache` can only do the same.\r\n\r\nAfter spending some time understanding the problem we came up with what we think is an appropriate solution. This doesn't alter any APIs so I think it may be merged in as a patch.\r\n\r\nThe AssetCache will compute the cache key based upon metadata about the asset, among these things are the filters that were applied to the asset. In particular, [the serialization of the filter](https://github.com/kriswallsmith/assetic/blob/50a30b23ce4229b408d99d580d1f056908dfb64f/src/Assetic/Asset/AssetCache.php#L159-L165). If the filter also implements the `HashableInterface` then the `hash` method will be used to add to the cache key. Originally this was simply meant for [when a filter can't be serialized](https://github.com/kriswallsmith/assetic/pull/228), but we can take advantage of this and use it to add additional metadata to the cache key.\r\n\r\nIn this implementation we create a custom filter implementing `HashableInterface` that is simply used to add an additional component to the cache key and not modify the content in load or dump. This then instantiated with the etag as the cache key (since this includes the proper modified time) and is passed along to the AssetCache.","created_at":"2015-01-10T08:43:46Z","updated_at":"2015-01-10T08:43:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332/commits","review_comments_url":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332/comments","review_comment_url":"https://api.github.com/repos/symfony/AsseticBundle/pulls/comments/{number}","comments_url":"https://api.github.com/repos/symfony/AsseticBundle/issues/332/comments","statuses_url":"https://api.github.com/repos/symfony/AsseticBundle/statuses/5067a03c88896f818e1cc8e096e19384f1bc421b","head":{"label":"crunchyroll:fixup/use-etag-in-asset-cache","ref":"fixup/use-etag-in-asset-cache","sha":"5067a03c88896f818e1cc8e096e19384f1bc421b","user":{"login":"crunchyroll","id":3979933,"avatar_url":"https://avatars.githubusercontent.com/u/3979933?v=3","gravatar_id":"","url":"https://api.github.com/users/crunchyroll","html_url":"https://github.com/crunchyroll","followers_url":"https://api.github.com/users/crunchyroll/followers","following_url":"https://api.github.com/users/crunchyroll/following{/other_user}","gists_url":"https://api.github.com/users/crunchyroll/gists{/gist_id}","starred_url":"https://api.github.com/users/crunchyroll/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/crunchyroll/subscriptions","organizations_url":"https://api.github.com/users/crunchyroll/orgs","repos_url":"https://api.github.com/users/crunchyroll/repos","events_url":"https://api.github.com/users/crunchyroll/events{/privacy}","received_events_url":"https://api.github.com/users/crunchyroll/received_events","type":"Organization","site_admin":false},"repo":{"id":29050483,"name":"AsseticBundle","full_name":"crunchyroll/AsseticBundle","owner":{"login":"crunchyroll","id":3979933,"avatar_url":"https://avatars.githubusercontent.com/u/3979933?v=3","gravatar_id":"","url":"https://api.github.com/users/crunchyroll","html_url":"https://github.com/crunchyroll","followers_url":"https://api.github.com/users/crunchyroll/followers","following_url":"https://api.github.com/users/crunchyroll/following{/other_user}","gists_url":"https://api.github.com/users/crunchyroll/gists{/gist_id}","starred_url":"https://api.github.com/users/crunchyroll/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/crunchyroll/subscriptions","organizations_url":"https://api.github.com/users/crunchyroll/orgs","repos_url":"https://api.github.com/users/crunchyroll/repos","events_url":"https://api.github.com/users/crunchyroll/events{/privacy}","received_events_url":"https://api.github.com/users/crunchyroll/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/crunchyroll/AsseticBundle","description":"","fork":true,"url":"https://api.github.com/repos/crunchyroll/AsseticBundle","forks_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/forks","keys_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/teams","hooks_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/hooks","issue_events_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/issues/events{/number}","events_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/events","assignees_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/assignees{/user}","branches_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/branches{/branch}","tags_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/tags","blobs_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/git/refs{/sha}","trees_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/statuses/{sha}","languages_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/languages","stargazers_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/stargazers","contributors_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/contributors","subscribers_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/subscribers","subscription_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/subscription","commits_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/commits{/sha}","git_commits_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/git/commits{/sha}","comments_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/comments{/number}","issue_comment_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/issues/comments/{number}","contents_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/contents/{+path}","compare_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/merges","archive_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/downloads","issues_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/issues{/number}","pulls_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/pulls{/number}","milestones_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/milestones{/number}","notifications_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/labels{/name}","releases_url":"https://api.github.com/repos/crunchyroll/AsseticBundle/releases{/id}","created_at":"2015-01-10T07:23:09Z","updated_at":"2015-01-10T08:16:56Z","pushed_at":"2015-01-10T08:21:15Z","git_url":"git://github.com/crunchyroll/AsseticBundle.git","ssh_url":"git@github.com:crunchyroll/AsseticBundle.git","clone_url":"https://github.com/crunchyroll/AsseticBundle.git","svn_url":"https://github.com/crunchyroll/AsseticBundle","homepage":"","size":1850,"stargazers_count":0,"watchers_count":0,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"symfony:master","ref":"master","sha":"ab3d73839939a77592f4c56fb1f6ce6b6d0eef1e","user":{"login":"symfony","id":143937,"avatar_url":"https://avatars.githubusercontent.com/u/143937?v=3","gravatar_id":"","url":"https://api.github.com/users/symfony","html_url":"https://github.com/symfony","followers_url":"https://api.github.com/users/symfony/followers","following_url":"https://api.github.com/users/symfony/following{/other_user}","gists_url":"https://api.github.com/users/symfony/gists{/gist_id}","starred_url":"https://api.github.com/users/symfony/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/symfony/subscriptions","organizations_url":"https://api.github.com/users/symfony/orgs","repos_url":"https://api.github.com/users/symfony/repos","events_url":"https://api.github.com/users/symfony/events{/privacy}","received_events_url":"https://api.github.com/users/symfony/received_events","type":"Organization","site_admin":false},"repo":{"id":1902260,"name":"AsseticBundle","full_name":"symfony/AsseticBundle","owner":{"login":"symfony","id":143937,"avatar_url":"https://avatars.githubusercontent.com/u/143937?v=3","gravatar_id":"","url":"https://api.github.com/users/symfony","html_url":"https://github.com/symfony","followers_url":"https://api.github.com/users/symfony/followers","following_url":"https://api.github.com/users/symfony/following{/other_user}","gists_url":"https://api.github.com/users/symfony/gists{/gist_id}","starred_url":"https://api.github.com/users/symfony/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/symfony/subscriptions","organizations_url":"https://api.github.com/users/symfony/orgs","repos_url":"https://api.github.com/users/symfony/repos","events_url":"https://api.github.com/users/symfony/events{/privacy}","received_events_url":"https://api.github.com/users/symfony/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/symfony/AsseticBundle","description":"","fork":false,"url":"https://api.github.com/repos/symfony/AsseticBundle","forks_url":"https://api.github.com/repos/symfony/AsseticBundle/forks","keys_url":"https://api.github.com/repos/symfony/AsseticBundle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/symfony/AsseticBundle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/symfony/AsseticBundle/teams","hooks_url":"https://api.github.com/repos/symfony/AsseticBundle/hooks","issue_events_url":"https://api.github.com/repos/symfony/AsseticBundle/issues/events{/number}","events_url":"https://api.github.com/repos/symfony/AsseticBundle/events","assignees_url":"https://api.github.com/repos/symfony/AsseticBundle/assignees{/user}","branches_url":"https://api.github.com/repos/symfony/AsseticBundle/branches{/branch}","tags_url":"https://api.github.com/repos/symfony/AsseticBundle/tags","blobs_url":"https://api.github.com/repos/symfony/AsseticBundle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/symfony/AsseticBundle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/symfony/AsseticBundle/git/refs{/sha}","trees_url":"https://api.github.com/repos/symfony/AsseticBundle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/symfony/AsseticBundle/statuses/{sha}","languages_url":"https://api.github.com/repos/symfony/AsseticBundle/languages","stargazers_url":"https://api.github.com/repos/symfony/AsseticBundle/stargazers","contributors_url":"https://api.github.com/repos/symfony/AsseticBundle/contributors","subscribers_url":"https://api.github.com/repos/symfony/AsseticBundle/subscribers","subscription_url":"https://api.github.com/repos/symfony/AsseticBundle/subscription","commits_url":"https://api.github.com/repos/symfony/AsseticBundle/commits{/sha}","git_commits_url":"https://api.github.com/repos/symfony/AsseticBundle/git/commits{/sha}","comments_url":"https://api.github.com/repos/symfony/AsseticBundle/comments{/number}","issue_comment_url":"https://api.github.com/repos/symfony/AsseticBundle/issues/comments/{number}","contents_url":"https://api.github.com/repos/symfony/AsseticBundle/contents/{+path}","compare_url":"https://api.github.com/repos/symfony/AsseticBundle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/symfony/AsseticBundle/merges","archive_url":"https://api.github.com/repos/symfony/AsseticBundle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/symfony/AsseticBundle/downloads","issues_url":"https://api.github.com/repos/symfony/AsseticBundle/issues{/number}","pulls_url":"https://api.github.com/repos/symfony/AsseticBundle/pulls{/number}","milestones_url":"https://api.github.com/repos/symfony/AsseticBundle/milestones{/number}","notifications_url":"https://api.github.com/repos/symfony/AsseticBundle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/symfony/AsseticBundle/labels{/name}","releases_url":"https://api.github.com/repos/symfony/AsseticBundle/releases{/id}","created_at":"2011-06-15T19:59:14Z","updated_at":"2015-01-09T20:06:38Z","pushed_at":"2015-01-02T21:27:11Z","git_url":"git://github.com/symfony/AsseticBundle.git","ssh_url":"git@github.com:symfony/AsseticBundle.git","clone_url":"https://github.com/symfony/AsseticBundle.git","svn_url":"https://github.com/symfony/AsseticBundle","homepage":"","size":1850,"stargazers_count":163,"watchers_count":163,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":161,"mirror_url":null,"open_issues_count":88,"forks":161,"open_issues":88,"watchers":163,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332"},"html":{"href":"https://github.com/symfony/AsseticBundle/pull/332"},"issue":{"href":"https://api.github.com/repos/symfony/AsseticBundle/issues/332"},"comments":{"href":"https://api.github.com/repos/symfony/AsseticBundle/issues/332/comments"},"review_comments":{"href":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332/comments"},"review_comment":{"href":"https://api.github.com/repos/symfony/AsseticBundle/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/symfony/AsseticBundle/pulls/332/commits"},"statuses":{"href":"https://api.github.com/repos/symfony/AsseticBundle/statuses/5067a03c88896f818e1cc8e096e19384f1bc421b"}},"merged":false,"mergeable":null,"mergeable_state":"unknown","merged_by":null,"comments":0,"review_comments":0,"commits":1,"additions":53,"deletions":1,"changed_files":2}}