{"action":"created","issue":{"url":"https://api.github.com/repos/bbatsov/rubocop/issues/2362","labels_url":"https://api.github.com/repos/bbatsov/rubocop/issues/2362/labels{/name}","comments_url":"https://api.github.com/repos/bbatsov/rubocop/issues/2362/comments","events_url":"https://api.github.com/repos/bbatsov/rubocop/issues/2362/events","html_url":"https://github.com/bbatsov/rubocop/pull/2362","id":113905212,"number":2362,"title":"Add Pattern helper to make pattern-matching on ASTs more concise","user":{"login":"alexdowad","id":1067359,"avatar_url":"https://avatars.githubusercontent.com/u/1067359?v=3","gravatar_id":"","url":"https://api.github.com/users/alexdowad","html_url":"https://github.com/alexdowad","followers_url":"https://api.github.com/users/alexdowad/followers","following_url":"https://api.github.com/users/alexdowad/following{/other_user}","gists_url":"https://api.github.com/users/alexdowad/gists{/gist_id}","starred_url":"https://api.github.com/users/alexdowad/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexdowad/subscriptions","organizations_url":"https://api.github.com/users/alexdowad/orgs","repos_url":"https://api.github.com/users/alexdowad/repos","events_url":"https://api.github.com/users/alexdowad/events{/privacy}","received_events_url":"https://api.github.com/users/alexdowad/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":45,"created_at":"2015-10-28T19:35:09Z","updated_at":"2015-11-03T19:32:01Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/bbatsov/rubocop/pulls/2362","html_url":"https://github.com/bbatsov/rubocop/pull/2362","diff_url":"https://github.com/bbatsov/rubocop/pull/2362.diff","patch_url":"https://github.com/bbatsov/rubocop/pull/2362.patch"},"body":"While working on #2359, I used a very complex conditional with lots of calls like `node.children[1].children[0].type == ...` and so on. @bbatsov commented on this:\r\n\r\n> I don't like the usage of array references and children in general, as it really obscures what you're trying to do. Even I'm not sure about it, so people not familiar with parser will be extra confused. \r\n\r\nThis PR is an attempt to make that kind of code very concise. It introduces a pattern-matching language for AST nodes. Pass a pattern string to `RuboCop::Pattern.new`, it compiles it into a method which runs just as fast as if it was written in straight Ruby.\r\n\r\nHere are some examples of what the pattern format looks like:\r\n\r\n```ruby\r\n# Let's say we want to match this\r\nConst = Class.new\r\n# Or:\r\nConst = Module.new\r\n\r\npattern = Pattern.new '(casgn _ const (send (const _ {:Class :Module}) :new ...))'\r\npattern.match(parser.parse(src)) # => true\r\n```\r\n\r\nAlphabetic identifiers match node types, `()` destructures a node, `{}` provides alternative patterns, any of which can match. `_` is a wildcard, symbol and integer literals match themselves, `...` matches any remaining children up to the end of a node.\r\n\r\nOther features: `!` negates the next part of the pattern, `$` captures whatever it matches (returning it from `#match` if the overall match succeeds), and you can match against the last child of a node by preceding it with a `...`.\r\n\r\nIf you extend `Astrolabe::Node` with methods which have a `_type?` suffix, the prefix will become usable in `Pattern`. For example:\r\n\r\n```ruby\r\nclass Astrolabe::Node\r\n  BASIC_LITERALS = [:str, :int, :float, :sym, :regexp, :nil, :true, :false]\r\n  def basic_literal_type?\r\n    BASIC_LITERALS.include?(type)\r\n  end\r\nend\r\n\r\nPattern.new '(send basic_literal ...)' # this will now work\r\n```\r\n\r\nThoughts?"},"comment":{"url":"https://api.github.com/repos/bbatsov/rubocop/issues/comments/153463200","html_url":"https://github.com/bbatsov/rubocop/pull/2362#issuecomment-153463200","issue_url":"https://api.github.com/repos/bbatsov/rubocop/issues/2362","id":153463200,"user":{"login":"jonas054","id":336720,"avatar_url":"https://avatars.githubusercontent.com/u/336720?v=3","gravatar_id":"","url":"https://api.github.com/users/jonas054","html_url":"https://github.com/jonas054","followers_url":"https://api.github.com/users/jonas054/followers","following_url":"https://api.github.com/users/jonas054/following{/other_user}","gists_url":"https://api.github.com/users/jonas054/gists{/gist_id}","starred_url":"https://api.github.com/users/jonas054/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonas054/subscriptions","organizations_url":"https://api.github.com/users/jonas054/orgs","repos_url":"https://api.github.com/users/jonas054/repos","events_url":"https://api.github.com/users/jonas054/events{/privacy}","received_events_url":"https://api.github.com/users/jonas054/received_events","type":"User","site_admin":false},"created_at":"2015-11-03T19:32:01Z","updated_at":"2015-11-03T19:32:01Z","body":"OK. So it replaces `Util#on_node` then, I guess."}}