{"action":"created","comment":{"url":"https://api.github.com/repos/rackerlabs/mimic/pulls/comments/32485985","id":32485985,"diff_hunk":"@@ -312,3 +323,72 @@ def delete_behavior(kl_self, request, behavior_id):\n                 delete_behavior)\n \n     return BehaviorAPI\n+\n+\n+sequence_docstring = \"\"\"\n+    Sometimes a sequence of behaviors occur when you try to trigger an\n+    event in a predictable pattern.\n+\n+    Takes one parameter, ``behaviors``, which is a list of specifications\n+    of other behaviors, similar to those specified in the request to\n+    create a behavior, with the addition of a behavior with a name of\n+    \"default\" that means default success.\n+\n+    Each time the criterion for this behavior is matched, the next\n+    behavior is executed, looping back to the beginning when the list of\n+    behaviors is exhausted.  In other words, this creation behavior is\n+    stateful.\n+\n+    Note that the behavior specifications here do not need a criterion,\n+    since the criterion is specified for the behavior overall, and each\n+    behavior is unconditionally executed in sequence.\n+\n+    For example, to specify an alternating sequence of success and then\n+    failure when the criterion for the ``sequence`` behavior is matched::\n+\n+        {\n+            \"behaviors\": [\n+                {\n+                    \"name\": \"default\"\n+                },\n+                {\n+                    \"name\": \"fail\",\n+                    \"parameters\": {\n+                        \"code\": 500,\n+                        \"message\": \"synthetic error\"\n+                    }\n+                }\n+            ]\n+        }\n+\"\"\"\n+\n+\n+def _sequence_behavior(event):\n+    \"\"\"\n+    A convenience function that should for :class:`EventDescription` that,\n+    given an event, produces a generic behavior-creator that provides this\n+    sequence behavior, which is named \"sequence\".\n+\n+    :param event: an instance of :class:`EventDescription`\n+    :return: a callable behavior-creator as described above\n+    \"\"\"\n+    @event.declare_behavior_creator(\"sequence\")\n+    def sequence(parameters):\n+        behavior_specification = parameters[\"behaviors\"]\n+        behavior_objects = cycle([\n+            (\n+                event.create_behavior(behavior[\"name\"],\n+                                      behavior[\"parameters\"])\n+                if behavior[\"name\"] != \"default\"\n+                else event.default_behavior\n+            )\n+            for behavior in behavior_specification\n+        ])\n+\n+        def rotating_behavior(*args, **kwargs):\n+            current = next(behavior_objects)\n+            return current(*args, **kwargs)\n+        return rotating_behavior\n+\n+    sequence.__doc__ = sequence_docstring","path":"mimic/model/behaviors.py","position":110,"original_position":110,"commit_id":"35a8236174bf94be93f5f31b1b9fadd26ba919aa","original_commit_id":"35a8236174bf94be93f5f31b1b9fadd26ba919aa","user":{"login":"glyph","id":716529,"avatar_url":"https://avatars.githubusercontent.com/u/716529?v=3","gravatar_id":"","url":"https://api.github.com/users/glyph","html_url":"https://github.com/glyph","followers_url":"https://api.github.com/users/glyph/followers","following_url":"https://api.github.com/users/glyph/following{/other_user}","gists_url":"https://api.github.com/users/glyph/gists{/gist_id}","starred_url":"https://api.github.com/users/glyph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/glyph/subscriptions","organizations_url":"https://api.github.com/users/glyph/orgs","repos_url":"https://api.github.com/users/glyph/repos","events_url":"https://api.github.com/users/glyph/events{/privacy}","received_events_url":"https://api.github.com/users/glyph/received_events","type":"User","site_admin":false},"body":"Ironically, the reason that this needed to be moved to an external attribute was that it was calling `.format`.  Now that you've changed it to be the same docstring everywhere, you could put it back inline.  I'll leave it up to you which way you prefer it though.","created_at":"2015-06-16T02:10:13Z","updated_at":"2015-06-16T02:10:13Z","html_url":"https://github.com/rackerlabs/mimic/pull/316#discussion_r32485985","pull_request_url":"https://api.github.com/repos/rackerlabs/mimic/pulls/316","_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/comments/32485985"},"html":{"href":"https://github.com/rackerlabs/mimic/pull/316#discussion_r32485985"},"pull_request":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/316"}}},"pull_request":{"url":"https://api.github.com/repos/rackerlabs/mimic/pulls/316","id":37612061,"html_url":"https://github.com/rackerlabs/mimic/pull/316","diff_url":"https://github.com/rackerlabs/mimic/pull/316.diff","patch_url":"https://github.com/rackerlabs/mimic/pull/316.patch","issue_url":"https://api.github.com/repos/rackerlabs/mimic/issues/316","number":316,"state":"open","locked":false,"title":"Provide a general utility for adding and testing sequence behavior.","user":{"login":"cyli","id":223200,"avatar_url":"https://avatars.githubusercontent.com/u/223200?v=3","gravatar_id":"","url":"https://api.github.com/users/cyli","html_url":"https://github.com/cyli","followers_url":"https://api.github.com/users/cyli/followers","following_url":"https://api.github.com/users/cyli/following{/other_user}","gists_url":"https://api.github.com/users/cyli/gists{/gist_id}","starred_url":"https://api.github.com/users/cyli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cyli/subscriptions","organizations_url":"https://api.github.com/users/cyli/orgs","repos_url":"https://api.github.com/users/cyli/repos","events_url":"https://api.github.com/users/cyli/events{/privacy}","received_events_url":"https://api.github.com/users/cyli/received_events","type":"User","site_admin":false},"body":"So that anything that provides behaviors can support sequence behavior.  Also, add it to identity control behaviors.\r\n\r\nQuestion for the Reviewer:  I want to know your thoughts:  I originally wanted to support sequence behaviors by default.\r\n\r\nPros:\r\n- all `EventDescription` instances would have a sequence behavior by default, no need to declare one - it always seems applicable\r\n- we wouldn't have to generate tests for sequence behavior for each plugin, we could just test the sequence behavior tests for `EventDescription`\r\n\r\nCons:\r\n- seems to be more explicit to require a plugin author to declare a sequence behavior for their plugin\r\n- there wouldn't be two magic behavior names: \"default\" AND \"sequence\" - just \"default\"\r\n\r\nThere's also the question, in both cases, of where to put the docstring for sequence behaviors... or how to make it obvious at least.","created_at":"2015-06-12T23:04:09Z","updated_at":"2015-06-16T02:10:13Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f0849f088aa4576fde58140a2a9af2b97eae1bff","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/rackerlabs/mimic/pulls/316/commits","review_comments_url":"https://api.github.com/repos/rackerlabs/mimic/pulls/316/comments","review_comment_url":"https://api.github.com/repos/rackerlabs/mimic/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rackerlabs/mimic/issues/316/comments","statuses_url":"https://api.github.com/repos/rackerlabs/mimic/statuses/35a8236174bf94be93f5f31b1b9fadd26ba919aa","head":{"label":"rackerlabs:generic-sequence-behavior","ref":"generic-sequence-behavior","sha":"35a8236174bf94be93f5f31b1b9fadd26ba919aa","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":13158707,"name":"mimic","full_name":"rackerlabs/mimic","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/mimic","description":"An API mock service","fork":false,"url":"https://api.github.com/repos/rackerlabs/mimic","forks_url":"https://api.github.com/repos/rackerlabs/mimic/forks","keys_url":"https://api.github.com/repos/rackerlabs/mimic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/mimic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/mimic/teams","hooks_url":"https://api.github.com/repos/rackerlabs/mimic/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/mimic/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/mimic/events","assignees_url":"https://api.github.com/repos/rackerlabs/mimic/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/mimic/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/mimic/tags","blobs_url":"https://api.github.com/repos/rackerlabs/mimic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/mimic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/mimic/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/mimic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/mimic/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/mimic/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/mimic/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/mimic/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/mimic/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/mimic/subscription","commits_url":"https://api.github.com/repos/rackerlabs/mimic/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/mimic/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/mimic/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/mimic/issues/comments{/number}","contents_url":"https://api.github.com/repos/rackerlabs/mimic/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/mimic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/mimic/merges","archive_url":"https://api.github.com/repos/rackerlabs/mimic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/mimic/downloads","issues_url":"https://api.github.com/repos/rackerlabs/mimic/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/mimic/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/mimic/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/mimic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/mimic/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/mimic/releases{/id}","created_at":"2013-09-27T19:01:16Z","updated_at":"2015-06-13T01:28:39Z","pushed_at":"2015-06-16T02:09:34Z","git_url":"git://github.com/rackerlabs/mimic.git","ssh_url":"git@github.com:rackerlabs/mimic.git","clone_url":"https://github.com/rackerlabs/mimic.git","svn_url":"https://github.com/rackerlabs/mimic","homepage":"","size":4091,"stargazers_count":83,"watchers_count":83,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":43,"mirror_url":null,"open_issues_count":78,"forks":43,"open_issues":78,"watchers":83,"default_branch":"master"}},"base":{"label":"rackerlabs:master","ref":"master","sha":"240732a9e5996d54d699c003fff4813adf27b759","user":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"repo":{"id":13158707,"name":"mimic","full_name":"rackerlabs/mimic","owner":{"login":"rackerlabs","id":84293,"avatar_url":"https://avatars.githubusercontent.com/u/84293?v=3","gravatar_id":"","url":"https://api.github.com/users/rackerlabs","html_url":"https://github.com/rackerlabs","followers_url":"https://api.github.com/users/rackerlabs/followers","following_url":"https://api.github.com/users/rackerlabs/following{/other_user}","gists_url":"https://api.github.com/users/rackerlabs/gists{/gist_id}","starred_url":"https://api.github.com/users/rackerlabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rackerlabs/subscriptions","organizations_url":"https://api.github.com/users/rackerlabs/orgs","repos_url":"https://api.github.com/users/rackerlabs/repos","events_url":"https://api.github.com/users/rackerlabs/events{/privacy}","received_events_url":"https://api.github.com/users/rackerlabs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rackerlabs/mimic","description":"An API mock service","fork":false,"url":"https://api.github.com/repos/rackerlabs/mimic","forks_url":"https://api.github.com/repos/rackerlabs/mimic/forks","keys_url":"https://api.github.com/repos/rackerlabs/mimic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rackerlabs/mimic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rackerlabs/mimic/teams","hooks_url":"https://api.github.com/repos/rackerlabs/mimic/hooks","issue_events_url":"https://api.github.com/repos/rackerlabs/mimic/issues/events{/number}","events_url":"https://api.github.com/repos/rackerlabs/mimic/events","assignees_url":"https://api.github.com/repos/rackerlabs/mimic/assignees{/user}","branches_url":"https://api.github.com/repos/rackerlabs/mimic/branches{/branch}","tags_url":"https://api.github.com/repos/rackerlabs/mimic/tags","blobs_url":"https://api.github.com/repos/rackerlabs/mimic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rackerlabs/mimic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rackerlabs/mimic/git/refs{/sha}","trees_url":"https://api.github.com/repos/rackerlabs/mimic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rackerlabs/mimic/statuses/{sha}","languages_url":"https://api.github.com/repos/rackerlabs/mimic/languages","stargazers_url":"https://api.github.com/repos/rackerlabs/mimic/stargazers","contributors_url":"https://api.github.com/repos/rackerlabs/mimic/contributors","subscribers_url":"https://api.github.com/repos/rackerlabs/mimic/subscribers","subscription_url":"https://api.github.com/repos/rackerlabs/mimic/subscription","commits_url":"https://api.github.com/repos/rackerlabs/mimic/commits{/sha}","git_commits_url":"https://api.github.com/repos/rackerlabs/mimic/git/commits{/sha}","comments_url":"https://api.github.com/repos/rackerlabs/mimic/comments{/number}","issue_comment_url":"https://api.github.com/repos/rackerlabs/mimic/issues/comments{/number}","contents_url":"https://api.github.com/repos/rackerlabs/mimic/contents/{+path}","compare_url":"https://api.github.com/repos/rackerlabs/mimic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rackerlabs/mimic/merges","archive_url":"https://api.github.com/repos/rackerlabs/mimic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rackerlabs/mimic/downloads","issues_url":"https://api.github.com/repos/rackerlabs/mimic/issues{/number}","pulls_url":"https://api.github.com/repos/rackerlabs/mimic/pulls{/number}","milestones_url":"https://api.github.com/repos/rackerlabs/mimic/milestones{/number}","notifications_url":"https://api.github.com/repos/rackerlabs/mimic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rackerlabs/mimic/labels{/name}","releases_url":"https://api.github.com/repos/rackerlabs/mimic/releases{/id}","created_at":"2013-09-27T19:01:16Z","updated_at":"2015-06-13T01:28:39Z","pushed_at":"2015-06-16T02:09:34Z","git_url":"git://github.com/rackerlabs/mimic.git","ssh_url":"git@github.com:rackerlabs/mimic.git","clone_url":"https://github.com/rackerlabs/mimic.git","svn_url":"https://github.com/rackerlabs/mimic","homepage":"","size":4091,"stargazers_count":83,"watchers_count":83,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":43,"mirror_url":null,"open_issues_count":78,"forks":43,"open_issues":78,"watchers":83,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/316"},"html":{"href":"https://github.com/rackerlabs/mimic/pull/316"},"issue":{"href":"https://api.github.com/repos/rackerlabs/mimic/issues/316"},"comments":{"href":"https://api.github.com/repos/rackerlabs/mimic/issues/316/comments"},"review_comments":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/316/comments"},"review_comment":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rackerlabs/mimic/pulls/316/commits"},"statuses":{"href":"https://api.github.com/repos/rackerlabs/mimic/statuses/35a8236174bf94be93f5f31b1b9fadd26ba919aa"}}}}