{"action":"created","issue":{"url":"https://api.github.com/repos/rails/rails/issues/19693","labels_url":"https://api.github.com/repos/rails/rails/issues/19693/labels{/name}","comments_url":"https://api.github.com/repos/rails/rails/issues/19693/comments","events_url":"https://api.github.com/repos/rails/rails/issues/19693/events","html_url":"https://github.com/rails/rails/issues/19693","id":67132721,"number":19693,"title":"Add thread_mattr_accessor for convenient use of per-thread module attributes","user":{"login":"dhh","id":2741,"avatar_url":"https://avatars.githubusercontent.com/u/2741?v=3","gravatar_id":"","url":"https://api.github.com/users/dhh","html_url":"https://github.com/dhh","followers_url":"https://api.github.com/users/dhh/followers","following_url":"https://api.github.com/users/dhh/following{/other_user}","gists_url":"https://api.github.com/users/dhh/gists{/gist_id}","starred_url":"https://api.github.com/users/dhh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dhh/subscriptions","organizations_url":"https://api.github.com/users/dhh/orgs","repos_url":"https://api.github.com/users/dhh/repos","events_url":"https://api.github.com/users/dhh/events{/privacy}","received_events_url":"https://api.github.com/users/dhh/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/rails/rails/labels/activesupport","name":"activesupport","color":"FC9300"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/rails/rails/milestones/34","html_url":"https://github.com/rails/rails/milestones/5.0.0","labels_url":"https://api.github.com/repos/rails/rails/milestones/34/labels","id":665192,"number":34,"title":"5.0.0","description":null,"creator":{"login":"rafaelfranca","id":47848,"avatar_url":"https://avatars.githubusercontent.com/u/47848?v=3","gravatar_id":"","url":"https://api.github.com/users/rafaelfranca","html_url":"https://github.com/rafaelfranca","followers_url":"https://api.github.com/users/rafaelfranca/followers","following_url":"https://api.github.com/users/rafaelfranca/following{/other_user}","gists_url":"https://api.github.com/users/rafaelfranca/gists{/gist_id}","starred_url":"https://api.github.com/users/rafaelfranca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rafaelfranca/subscriptions","organizations_url":"https://api.github.com/users/rafaelfranca/orgs","repos_url":"https://api.github.com/users/rafaelfranca/repos","events_url":"https://api.github.com/users/rafaelfranca/events{/privacy}","received_events_url":"https://api.github.com/users/rafaelfranca/received_events","type":"User","site_admin":false},"open_issues":97,"closed_issues":93,"state":"open","created_at":"2014-05-21T00:58:22Z","updated_at":"2015-04-08T17:31:56Z","due_on":null,"closed_at":null},"comments":7,"created_at":"2015-04-08T13:40:06Z","updated_at":"2015-04-08T18:18:52Z","closed_at":null,"body":"Intended to work like mattr_accessor, but be scoped on a per-thread basis. IMO, this is a simpler way about the problem than PerThreadRegistry, and we should replace that with this:\r\n\r\n```ruby\r\nclass Module\r\n  def thread_mattr_reader(*syms)\r\n    options = syms.extract_options!\r\n\r\n    syms.each do |sym|\r\n      raise NameError.new(\"invalid attribute name: #{sym}\") unless sym =~ /^[_A-Za-z]\\w*$/\r\n      class_eval(<<-EOS, __FILE__, __LINE__ + 1)\r\n        def self.#{sym}\r\n          Thread.current[:\"attr_#{name}_#{sym}\"]\r\n        end\r\n      EOS\r\n\r\n      unless options[:instance_reader] == false || options[:instance_accessor] == false\r\n        class_eval(<<-EOS, __FILE__, __LINE__ + 1)\r\n          def #{sym}\r\n            Thread.current[:\"attr_#{self.class.name}_#{sym}\"]\r\n          end\r\n        EOS\r\n      end\r\n\r\n      if block_given?\r\n        Thread.current[:\"attr_#{name}_#{sym}\"] = yield\r\n      end\r\n    end\r\n  end\r\n  alias :thread_cattr_reader :thread_mattr_reader\r\n\r\n  def thread_mattr_writer(*syms)\r\n    options = syms.extract_options!\r\n    syms.each do |sym|\r\n      raise NameError.new(\"invalid attribute name: #{sym}\") unless sym =~ /^[_A-Za-z]\\w*$/\r\n      class_eval(<<-EOS, __FILE__, __LINE__ + 1)\r\n        def self.#{sym}=(obj)\r\n          Thread.current[:\"attr_#{name}_#{sym}\"] = obj\r\n        end\r\n      EOS\r\n\r\n      unless options[:instance_writer] == false || options[:instance_accessor] == false\r\n        class_eval(<<-EOS, __FILE__, __LINE__ + 1)\r\n          def #{sym}=(obj)\r\n            Thread.current[:\"attr_#{self.class.name}_#{sym}\"] = obj\r\n          end\r\n        EOS\r\n      end\r\n\r\n      send(\"#{sym}=\", yield) if block_given?\r\n    end\r\n  end\r\n  alias :thread_cattr_writer :thread_mattr_writer\r\n\r\n  def thread_mattr_accessor(*syms, &blk)\r\n    thread_mattr_reader(*syms, &blk)\r\n    thread_mattr_writer(*syms, &blk)\r\n  end\r\n  alias :thread_cattr_accessor :thread_mattr_accessor\r\nend\r\n```"},"comment":{"url":"https://api.github.com/repos/rails/rails/issues/comments/90993886","html_url":"https://github.com/rails/rails/issues/19693#issuecomment-90993886","issue_url":"https://api.github.com/repos/rails/rails/issues/19693","id":90993886,"user":{"login":"pixeltrix","id":6321,"avatar_url":"https://avatars.githubusercontent.com/u/6321?v=3","gravatar_id":"","url":"https://api.github.com/users/pixeltrix","html_url":"https://github.com/pixeltrix","followers_url":"https://api.github.com/users/pixeltrix/followers","following_url":"https://api.github.com/users/pixeltrix/following{/other_user}","gists_url":"https://api.github.com/users/pixeltrix/gists{/gist_id}","starred_url":"https://api.github.com/users/pixeltrix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pixeltrix/subscriptions","organizations_url":"https://api.github.com/users/pixeltrix/orgs","repos_url":"https://api.github.com/users/pixeltrix/repos","events_url":"https://api.github.com/users/pixeltrix/events{/privacy}","received_events_url":"https://api.github.com/users/pixeltrix/received_events","type":"User","site_admin":false},"created_at":"2015-04-08T18:18:52Z","updated_at":"2015-04-08T18:18:52Z","body":"This is what Aaron discovered awhile ago: http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html\r\n\r\nWe should test whether this still holds true for Ruby 2.2"}}