weeter 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +1 -1
- data/lib/weeter/configuration/client_app_config.rb +5 -2
- data/lib/weeter/configuration/limiter_config.rb +1 -1
- data/lib/weeter/plugins/lib/oauth_http.rb +5 -5
- data/lib/weeter/plugins/notification/resque.rb +9 -6
- data/lib/weeter/plugins/subscription/http.rb +4 -3
- data/lib/weeter/plugins/subscription/redis.rb +3 -3
- data/lib/weeter/twitter/tweet_consumer.rb +51 -15
- data/lib/weeter/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/weeter/configuration/client_app_config_spec.rb +2 -6
- data/spec/weeter/configuration/twitter_config_spec.rb +15 -15
- data/spec/weeter/configuration_spec.rb +5 -5
- data/spec/weeter/limitator_spec.rb +27 -27
- data/spec/weeter/plugins/notification_plugin_spec.rb +6 -6
- data/spec/weeter/plugins/subscription/update_server_spec.rb +11 -11
- data/spec/weeter/plugins/subscription_plugin_spec.rb +7 -7
- data/spec/weeter/twitter/tweet_consumer_spec.rb +60 -52
- data/spec/weeter/twitter/tweet_item_spec.rb +27 -27
- data/weeter.gemspec +12 -11
- metadata +80 -91
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 13c5cd768b0f83d96e1a67b87a505e3e99ae4b18
         | 
| 4 | 
            +
              data.tar.gz: 47f1550576846acc61ad07c1323702e4b1ad60cd
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: cf47012d1be61abcfc9a8800d8b46229f6c62a967515dfa1477891e86243cf31329c1e05e554612441c25320d2e18070d4e4d39536ca8e17c85a5db7e7324761
         | 
| 7 | 
            +
              data.tar.gz: 959ae16e71443ff6b561240f2b8432ec3f291b472a272f7373fffc2f663dd15e635671972efedf1899dcfb4e03dbf8a7cb7fa32c96001351cbd43c530c1a7ba8
         | 
    
        data/.ruby-version
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            ruby-2.2.6
         | 
    
        data/.travis.yml
    CHANGED
    
    
| @@ -4,8 +4,11 @@ require 'hashie' | |
| 4 4 | 
             
            module Weeter
         | 
| 5 5 | 
             
              class Configuration
         | 
| 6 6 | 
             
                class ClientAppConfig < Hashie::Mash
         | 
| 7 | 
            -
                   | 
| 8 | 
            -
             | 
| 7 | 
            +
                  DEFAULT_SUBSCRIPTIONS_UPDATE_PORT = 7337
         | 
| 8 | 
            +
                  InvalidConfiguration = Class.new(StandardError)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def verify_redis_namespace_config
         | 
| 11 | 
            +
                    !!self.redis_namespace || raise(InvalidConfiguration, 'missing `redis-namespace` config')
         | 
| 9 12 | 
             
                  end
         | 
| 10 13 | 
             
                end
         | 
| 11 14 | 
             
              end
         | 
| @@ -8,7 +8,7 @@ module Weeter | |
| 8 8 | 
             
                    def self.get(config, url, params = {})
         | 
| 9 9 | 
             
                      request(config, :get, url, params)
         | 
| 10 10 | 
             
                    end
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
                    def self.put(config, url, params = {})
         | 
| 13 13 | 
             
                      request(config, :put, url, params)
         | 
| 14 14 | 
             
                    end
         | 
| @@ -16,11 +16,11 @@ module Weeter | |
| 16 16 | 
             
                    def self.post(config, url, params = {})
         | 
| 17 17 | 
             
                      request(config, :post, url, params)
         | 
| 18 18 | 
             
                    end
         | 
| 19 | 
            -
             | 
| 19 | 
            +
             | 
| 20 20 | 
             
                    def self.delete(config, url, params = {})
         | 
| 21 21 | 
             
                      request(config, :delete, url, params)
         | 
| 22 22 | 
             
                    end
         | 
| 23 | 
            -
             | 
| 23 | 
            +
             | 
| 24 24 | 
             
                    def self.request(config, method, url, params = {})
         | 
| 25 25 | 
             
                      if method == :post
         | 
| 26 26 | 
             
                        request_options = {:body => params}
         | 
| @@ -32,9 +32,9 @@ module Weeter | |
| 32 32 | 
             
                    end
         | 
| 33 33 |  | 
| 34 34 | 
             
                    def self.oauth_header(config, uri, params, http_method)
         | 
| 35 | 
            -
                      :: | 
| 35 | 
            +
                      ::SimpleOAuth::Header.new(http_method, uri, params, config.oauth).to_s
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 | 
             
                  end
         | 
| 38 38 | 
             
                end
         | 
| 39 39 | 
             
              end
         | 
| 40 | 
            -
            end
         | 
| 40 | 
            +
            end
         | 
| @@ -9,19 +9,19 @@ module Weeter | |
| 9 9 | 
             
                    end
         | 
| 10 10 |  | 
| 11 11 | 
             
                    def publish_tweet(tweet_item)
         | 
| 12 | 
            -
                      resque_job = %Q|{"class":"WeeterPublishTweetJob","args":[#{tweet_item.to_json}]}|
         | 
| 12 | 
            +
                      resque_job = %Q|{"class":"WeeterPublishTweetJob","args":[#{tweet_item.to_json}],"jid": "#{SecureRandom.hex(12)}"}|
         | 
| 13 13 | 
             
                      Weeter.logger.info("Publishing tweet #{tweet_item['id']} from user #{tweet_item['user']['id_str']}: #{tweet_item['text']}")
         | 
| 14 14 | 
             
                      enqueue(resque_job)
         | 
| 15 15 | 
             
                    end
         | 
| 16 16 |  | 
| 17 17 | 
             
                    def delete_tweet(tweet_item)
         | 
| 18 | 
            -
                      resque_job = %Q|{"class":"WeeterDeleteTweetJob","args":[#{tweet_item.to_json}]}|
         | 
| 18 | 
            +
                      resque_job = %Q|{"class":"WeeterDeleteTweetJob","args":[#{tweet_item.to_json}],"jid": "#{SecureRandom.hex(12)}"}|
         | 
| 19 19 | 
             
                      Weeter.logger.info("Deleting tweet #{tweet_item['id']} for user #{tweet_item['user']['id_str']}")
         | 
| 20 20 | 
             
                      enqueue(resque_job)
         | 
| 21 21 | 
             
                    end
         | 
| 22 22 |  | 
| 23 23 | 
             
                    def notify_missed_tweets(tweet_item)
         | 
| 24 | 
            -
                      resque_job = %Q|{"class":"WeeterMissedTweetsJob","args":[#{tweet_item.to_json}]}|
         | 
| 24 | 
            +
                      resque_job = %Q|{"class":"WeeterMissedTweetsJob","args":[#{tweet_item.to_json}],"jid": "#{SecureRandom.hex(12)}"}|
         | 
| 25 25 | 
             
                      Weeter.logger.info("Notifying of missed tweets (#{tweet_item.missed_tweets_count}).")
         | 
| 26 26 | 
             
                      enqueue(resque_job)
         | 
| 27 27 | 
             
                    end
         | 
| @@ -29,7 +29,7 @@ module Weeter | |
| 29 29 | 
             
                    def notify_rate_limiting_initiated(tweet_item, limited_keys)
         | 
| 30 30 | 
             
                      payload = tweet_item.to_hash.merge(:limited_keys => limited_keys)
         | 
| 31 31 | 
             
                      payload_json = MultiJson.encode(payload)
         | 
| 32 | 
            -
                      resque_job = %Q|{"class":"WeeterRateLimitingInitiatedJob","args":[#{payload_json}]}|
         | 
| 32 | 
            +
                      resque_job = %Q|{"class":"WeeterRateLimitingInitiatedJob","args":[#{payload_json}],"jid": "#{SecureRandom.hex(12)}"}|
         | 
| 33 33 | 
             
                      Weeter.logger.info("Initiated rate limiting with tweet: #{payload_json}")
         | 
| 34 34 | 
             
                      enqueue(resque_job)
         | 
| 35 35 | 
             
                    end
         | 
| @@ -37,7 +37,10 @@ module Weeter | |
| 37 37 | 
             
                  protected
         | 
| 38 38 |  | 
| 39 39 | 
             
                    def redis
         | 
| 40 | 
            -
                      @redis ||=  | 
| 40 | 
            +
                      @redis ||= begin
         | 
| 41 | 
            +
                        @config.verify_redis_namespace_config
         | 
| 42 | 
            +
                        create_redis_client
         | 
| 43 | 
            +
                      end
         | 
| 41 44 | 
             
                    end
         | 
| 42 45 |  | 
| 43 46 | 
             
                    def enqueue(job)
         | 
| @@ -45,7 +48,7 @@ module Weeter | |
| 45 48 | 
             
                    end
         | 
| 46 49 |  | 
| 47 50 | 
             
                    def queue_key
         | 
| 48 | 
            -
                      " | 
| 51 | 
            +
                      "#{@config.redis_namespace}:#{@config.queue}"
         | 
| 49 52 | 
             
                    end
         | 
| 50 53 | 
             
                  end
         | 
| 51 54 | 
             
                end
         | 
| @@ -23,11 +23,12 @@ module Weeter | |
| 23 23 | 
             
                    end
         | 
| 24 24 |  | 
| 25 25 | 
             
                    def listen_for_filter_update(tweet_consumer)
         | 
| 26 | 
            -
                       | 
| 26 | 
            +
                      port = @config.subscription_updates_port || Weeter::Configuration::ClientAppConfig::DEFAULT_SUBSCRIPTIONS_UPDATE_PORT
         | 
| 27 | 
            +
                      EM.start_server('localhost', port, UpdateServer) do |conn|
         | 
| 27 28 | 
             
                        conn.tweet_consumer = tweet_consumer
         | 
| 28 29 | 
             
                      end
         | 
| 29 30 | 
             
                    end
         | 
| 30 | 
            -
             | 
| 31 | 
            +
             | 
| 31 32 | 
             
                    class UpdateServer < EM::Connection
         | 
| 32 33 | 
             
                      include EM::HttpServer
         | 
| 33 34 | 
             
                      attr_accessor :tweet_consumer
         | 
| @@ -42,4 +43,4 @@ module Weeter | |
| 42 43 | 
             
                  end
         | 
| 43 44 | 
             
                end
         | 
| 44 45 | 
             
              end
         | 
| 45 | 
            -
            end
         | 
| 46 | 
            +
            end
         | 
| @@ -23,8 +23,8 @@ module Weeter | |
| 23 23 | 
             
                    end
         | 
| 24 24 |  | 
| 25 25 | 
             
                    def listen_for_filter_update(tweet_consumer)
         | 
| 26 | 
            -
                       | 
| 27 | 
            -
                      pub_sub_redis. | 
| 26 | 
            +
                      channel = @config.subscriptions_changed_channel
         | 
| 27 | 
            +
                      pub_sub_redis.subscribe(channel) do |message|
         | 
| 28 28 | 
             
                        Weeter.logger.info [:message, channel, message]
         | 
| 29 29 | 
             
                        Weeter.logger.info("Retrieving updated filters from redis")
         | 
| 30 30 | 
             
                        get_initial_filters do |filter_params|
         | 
| @@ -41,7 +41,7 @@ module Weeter | |
| 41 41 | 
             
                    end
         | 
| 42 42 |  | 
| 43 43 | 
             
                    def pub_sub_redis
         | 
| 44 | 
            -
                      @pub_sub_redis ||= create_redis_client
         | 
| 44 | 
            +
                      @pub_sub_redis ||= create_redis_client.pubsub
         | 
| 45 45 | 
             
                    end
         | 
| 46 46 |  | 
| 47 47 | 
             
                  end
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            require 'twitter | 
| 1 | 
            +
            require 'em-twitter'
         | 
| 2 2 | 
             
            require 'multi_json'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module Weeter
         | 
| @@ -23,18 +23,22 @@ module Weeter | |
| 23 23 | 
             
                  def connect(filter_params)
         | 
| 24 24 | 
             
                    filter_params = limit_filter_params(filter_params)
         | 
| 25 25 | 
             
                    filter_params = clean_filter_params(filter_params)
         | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
                     | 
| 29 | 
            -
                       | 
| 30 | 
            -
                      params | 
| 31 | 
            -
                       | 
| 32 | 
            -
             | 
| 26 | 
            +
                    oauth_options = @config.auth_options[:oauth]
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    options = {
         | 
| 29 | 
            +
                      :path   => '/1.1/statuses/filter.json',
         | 
| 30 | 
            +
                      :params => filter_params,
         | 
| 31 | 
            +
                      :oauth  => {
         | 
| 32 | 
            +
                        :consumer_key     => oauth_options[:consumer_key],
         | 
| 33 | 
            +
                        :consumer_secret  => oauth_options[:consumer_secret],
         | 
| 34 | 
            +
                        :token            => oauth_options[:access_key],
         | 
| 35 | 
            +
                        :token_secret     => oauth_options[:access_secret]
         | 
| 36 | 
            +
                      }
         | 
| 37 | 
            +
                    }
         | 
| 33 38 |  | 
| 34 39 | 
             
                    Weeter.logger.info("Connecting to Twitter stream...")
         | 
| 35 | 
            -
                    @ | 
| 36 | 
            -
             | 
| 37 | 
            -
                    @stream.each_item do |item|
         | 
| 40 | 
            +
                    @client = EM::Twitter::Client.connect(options)
         | 
| 41 | 
            +
                    @client.each do |item|
         | 
| 38 42 | 
             
                      begin
         | 
| 39 43 | 
             
                        tweet_item = TweetItem.new(MultiJson.decode(item))
         | 
| 40 44 |  | 
| @@ -52,18 +56,50 @@ module Weeter | |
| 52 56 | 
             
                      end
         | 
| 53 57 | 
             
                    end
         | 
| 54 58 |  | 
| 55 | 
            -
                    @ | 
| 59 | 
            +
                    @client.on_unauthorized do |msg|
         | 
| 60 | 
            +
                      Weeter.logger.debug("on_unauthorized: #{msg}")
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    @client.on_forbidden do |msg|
         | 
| 64 | 
            +
                      Weeter.logger.debug("on_forbidden: #{msg}")
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    @client.on_not_found do |msg|
         | 
| 68 | 
            +
                      Weeter.logger.debug("on_not_found: #{msg}")
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    @client.on_not_acceptable do |msg|
         | 
| 72 | 
            +
                      Weeter.logger.debug("on_not_acceptable: #{msg}")
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    @client.on_too_long do |msg|
         | 
| 76 | 
            +
                      Weeter.logger.debug("on_too_long: #{msg}")
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    @client.on_range_unacceptable do |msg|
         | 
| 80 | 
            +
                      Weeter.logger.debug("on_range_unacceptable: #{msg}")
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    @client.on_enhance_your_calm do |msg| # rate-limited
         | 
| 84 | 
            +
                      Weeter.logger.debug("on_enhance_your_calm: #{msg}")
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    @client.on_error do |msg|
         | 
| 56 88 | 
             
                      Weeter.logger.error("Twitter stream error: #{msg}. Connect options were #{connect_options.inspect}")
         | 
| 57 89 | 
             
                    end
         | 
| 58 90 |  | 
| 59 | 
            -
                    @ | 
| 91 | 
            +
                    @client.on_reconnect do |msg|
         | 
| 92 | 
            +
                      Weeter.logger.debug("on_reconnect: #{msg}")
         | 
| 93 | 
            +
                    end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    @client.on_max_reconnects do |timeout, retries|
         | 
| 60 96 | 
             
                      Weeter.logger.error("Twitter stream max-reconnects reached: timeout=#{timeout}, retries=#{retries}")
         | 
| 61 97 | 
             
                    end
         | 
| 62 98 | 
             
                  end
         | 
| 63 99 |  | 
| 64 100 | 
             
                  def reconnect(filter_params)
         | 
| 65 | 
            -
                    @ | 
| 66 | 
            -
                    @ | 
| 101 | 
            +
                    @client.stop
         | 
| 102 | 
            +
                    @client.unbind
         | 
| 67 103 | 
             
                    connect(filter_params)
         | 
| 68 104 | 
             
                  end
         | 
| 69 105 |  | 
    
        data/lib/weeter/version.rb
    CHANGED
    
    
    
        data/spec/spec_helper.rb
    CHANGED
    
    
| @@ -2,17 +2,13 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe Weeter::Configuration::ClientAppConfig do
         | 
| 4 4 | 
             
              %w{delete_url subscriptions_url oauth}.each do |setting|
         | 
| 5 | 
            -
                it " | 
| 5 | 
            +
                it "accepts setting for #{setting}" do
         | 
| 6 6 | 
             
                  Weeter.configure do |conf|
         | 
| 7 7 | 
             
                    conf.client_app do |app|
         | 
| 8 8 | 
             
                      app.send("#{setting}=", "testvalue")
         | 
| 9 9 | 
             
                    end
         | 
| 10 10 | 
             
                  end
         | 
| 11 | 
            -
                  Weeter::Configuration.instance.client_app.send(setting). | 
| 11 | 
            +
                  expect(Weeter::Configuration.instance.client_app.send(setting)).to eq("testvalue")
         | 
| 12 12 | 
             
                end
         | 
| 13 13 | 
             
              end
         | 
| 14 | 
            -
              
         | 
| 15 | 
            -
              it "should default subscription_updates_port" do
         | 
| 16 | 
            -
                Weeter::Configuration.instance.client_app.subscription_updates_port.should == 7337
         | 
| 17 | 
            -
              end
         | 
| 18 14 | 
             
            end
         | 
| @@ -2,38 +2,38 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe Weeter::Configuration::TwitterConfig do
         | 
| 4 4 | 
             
              %w{basic_auth oauth}.each do |setting|
         | 
| 5 | 
            -
                it " | 
| 5 | 
            +
                it "accepts setting for #{setting}" do
         | 
| 6 6 | 
             
                  Weeter.configure do |conf|
         | 
| 7 7 | 
             
                    conf.twitter do |app|
         | 
| 8 8 | 
             
                      app.send("#{setting}=", "testvalue")
         | 
| 9 9 | 
             
                    end
         | 
| 10 10 | 
             
                  end
         | 
| 11 | 
            -
                  Weeter::Configuration::TwitterConfig.instance.send(setting). | 
| 11 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance.send(setting)).to eq("testvalue")
         | 
| 12 12 | 
             
                end
         | 
| 13 13 | 
             
              end
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
              describe "auth_options" do
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             | 
| 17 17 | 
             
                before do
         | 
| 18 18 | 
             
                  Weeter::Configuration::TwitterConfig.instance.oauth = nil
         | 
| 19 19 | 
             
                  Weeter::Configuration::TwitterConfig.instance.basic_auth = nil
         | 
| 20 20 | 
             
                end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                it " | 
| 21 | 
            +
             | 
| 22 | 
            +
                it "returns the oauth settings with a oauth credentials" do
         | 
| 23 23 | 
             
                  Weeter::Configuration::TwitterConfig.instance.oauth = {:consumer_key => 'consumer_key', :consumer_secret => 'consumer_secret', :access_key => 'acces_key', :access_secret => 'access_secret'}
         | 
| 24 | 
            -
                  Weeter::Configuration::TwitterConfig.instance.auth_options. | 
| 24 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance.auth_options).to eq({:oauth => {:consumer_key => 'consumer_key', :consumer_secret => 'consumer_secret', :access_key => 'acces_key', :access_secret => 'access_secret'}})
         | 
| 25 25 | 
             
                end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                it " | 
| 26 | 
            +
             | 
| 27 | 
            +
                it "returns the basic auth settings separated by a colon" do
         | 
| 28 28 | 
             
                  Weeter::Configuration::TwitterConfig.instance.basic_auth = {:username => "bob", :password => "s3cr3t"}
         | 
| 29 | 
            -
                  Weeter::Configuration::TwitterConfig.instance.auth_options. | 
| 29 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance.auth_options).to eq({:auth => "bob:s3cr3t"})
         | 
| 30 30 | 
             
                end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                it " | 
| 31 | 
            +
             | 
| 32 | 
            +
                it "prefers oauth over basic auth" do
         | 
| 33 33 | 
             
                  Weeter::Configuration::TwitterConfig.instance.basic_auth = {:username => "bob", :password => "s3cr3t"}
         | 
| 34 34 | 
             
                  Weeter::Configuration::TwitterConfig.instance.oauth = {:consumer_key => 'consumer_key', :consumer_secret => 'consumer_secret', :access_key => 'acces_key', :access_secret => 'access_secret'}
         | 
| 35 | 
            -
                  Weeter::Configuration::TwitterConfig.instance.auth_options. | 
| 35 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance.auth_options).to eq({:oauth => {:consumer_key => 'consumer_key', :consumer_secret => 'consumer_secret', :access_key => 'acces_key', :access_secret => 'access_secret'}})
         | 
| 36 36 | 
             
                end
         | 
| 37 37 | 
             
              end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            end
         | 
| @@ -2,14 +2,14 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe Weeter::Configuration do
         | 
| 4 4 | 
             
              describe "#twitter" do
         | 
| 5 | 
            -
                it " | 
| 6 | 
            -
                  Weeter::Configuration.instance.twitter. | 
| 5 | 
            +
                it "returns the instance" do
         | 
| 6 | 
            +
                  expect(Weeter::Configuration.instance.twitter).to eq(Weeter::Configuration::TwitterConfig.instance)
         | 
| 7 7 | 
             
                end
         | 
| 8 8 |  | 
| 9 | 
            -
                it " | 
| 9 | 
            +
                it "yields the instance when a block is provided" do
         | 
| 10 10 | 
             
                  Weeter::Configuration.instance.twitter do |twitter_config|
         | 
| 11 | 
            -
                    twitter_config. | 
| 11 | 
            +
                    expect(twitter_config).to eq(Weeter::Configuration::TwitterConfig.instance)
         | 
| 12 12 | 
             
                  end
         | 
| 13 13 | 
             
                end
         | 
| 14 14 | 
             
              end
         | 
| 15 | 
            -
            end
         | 
| 15 | 
            +
            end
         | 
| @@ -14,7 +14,7 @@ describe Weeter::Limitator do | |
| 14 14 | 
             
              let(:keys) { ['key'] }
         | 
| 15 15 |  | 
| 16 16 | 
             
              describe '.new' do
         | 
| 17 | 
            -
                it { limitator. | 
| 17 | 
            +
                it { expect(limitator).to be }
         | 
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 20 | 
             
              describe '#limit_status' do
         | 
| @@ -25,38 +25,38 @@ describe Weeter::Limitator do | |
| 25 25 |  | 
| 26 26 | 
             
                context 'max: 0' do
         | 
| 27 27 | 
             
                  let(:max) { 0 }
         | 
| 28 | 
            -
                   | 
| 29 | 
            -
                   | 
| 28 | 
            +
                  it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 29 | 
            +
                  it { expect(subject.limited_keys).to eq(keys) }
         | 
| 30 30 |  | 
| 31 31 | 
             
                  context 'no keys' do
         | 
| 32 32 | 
             
                    let(:keys) { [] }
         | 
| 33 | 
            -
                     | 
| 34 | 
            -
                     | 
| 33 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::DO_NOT_LIMIT) }
         | 
| 34 | 
            +
                    it { expect(subject.limited_keys).to be_nil }
         | 
| 35 35 | 
             
                  end
         | 
| 36 36 |  | 
| 37 37 | 
             
                  context 'two keys' do
         | 
| 38 38 | 
             
                    let(:keys) { ['key', 'key2'] }
         | 
| 39 | 
            -
                     | 
| 40 | 
            -
                     | 
| 39 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 40 | 
            +
                    it { expect(subject.limited_keys).to eq(keys) }
         | 
| 41 41 | 
             
                  end
         | 
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| 44 44 | 
             
                context 'max: 1' do
         | 
| 45 45 | 
             
                  let(:max) { 1 }
         | 
| 46 46 |  | 
| 47 | 
            -
                   | 
| 48 | 
            -
                   | 
| 47 | 
            +
                  it { expect(subject.status).to eq(Weeter::Limitator::DO_NOT_LIMIT) }
         | 
| 48 | 
            +
                  it { expect(subject.limited_keys).to be_nil }
         | 
| 49 49 |  | 
| 50 50 | 
             
                  context 'two keys within max' do
         | 
| 51 51 | 
             
                    let(:keys) { ['key', 'key2'] }
         | 
| 52 52 |  | 
| 53 | 
            -
                     | 
| 53 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::DO_NOT_LIMIT) }
         | 
| 54 54 | 
             
                  end
         | 
| 55 55 |  | 
| 56 56 | 
             
                  context 'no keys' do
         | 
| 57 57 | 
             
                    let(:keys) { [] }
         | 
| 58 | 
            -
                     | 
| 59 | 
            -
                     | 
| 58 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::DO_NOT_LIMIT) }
         | 
| 59 | 
            +
                    it { expect(subject.limited_keys).to be_nil }
         | 
| 60 60 | 
             
                  end
         | 
| 61 61 |  | 
| 62 62 | 
             
                  context 'one key just outside max' do
         | 
| @@ -66,8 +66,8 @@ describe Weeter::Limitator do | |
| 66 66 | 
             
                      end
         | 
| 67 67 | 
             
                    end
         | 
| 68 68 |  | 
| 69 | 
            -
                     | 
| 70 | 
            -
                     | 
| 69 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 70 | 
            +
                    it { expect(subject.limited_keys).to eq(keys) }
         | 
| 71 71 |  | 
| 72 72 | 
             
                    context 'outside duration' do
         | 
| 73 73 | 
             
                      let(:some_time_after_duration) do
         | 
| @@ -75,11 +75,11 @@ describe Weeter::Limitator do | |
| 75 75 | 
             
                      end
         | 
| 76 76 |  | 
| 77 77 | 
             
                      before do
         | 
| 78 | 
            -
                        limitator. | 
| 78 | 
            +
                        expect(limitator).to receive(:now).and_return(some_time_after_duration).at_least(:once)
         | 
| 79 79 | 
             
                      end
         | 
| 80 80 |  | 
| 81 | 
            -
                       | 
| 82 | 
            -
                       | 
| 81 | 
            +
                      it { expect(subject.status).to eq(Weeter::Limitator::DO_NOT_LIMIT) }
         | 
| 82 | 
            +
                      it { expect(subject.limited_keys).to be_nil }
         | 
| 83 83 | 
             
                    end
         | 
| 84 84 | 
             
                  end
         | 
| 85 85 |  | 
| @@ -90,8 +90,8 @@ describe Weeter::Limitator do | |
| 90 90 | 
             
                      limitator.process(*keys)
         | 
| 91 91 | 
             
                    end
         | 
| 92 92 |  | 
| 93 | 
            -
                     | 
| 94 | 
            -
                     | 
| 93 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 94 | 
            +
                    it { expect(subject.limited_keys).to eq(keys) }
         | 
| 95 95 | 
             
                  end
         | 
| 96 96 |  | 
| 97 97 | 
             
                  context 'two keys past max' do
         | 
| @@ -102,8 +102,8 @@ describe Weeter::Limitator do | |
| 102 102 | 
             
                      limitator.process(*keys)
         | 
| 103 103 | 
             
                    end
         | 
| 104 104 |  | 
| 105 | 
            -
                     | 
| 106 | 
            -
                     | 
| 105 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::CONTINUE_LIMITING) }
         | 
| 106 | 
            +
                    it { expect(subject.limited_keys).to eq(keys) }
         | 
| 107 107 | 
             
                  end
         | 
| 108 108 |  | 
| 109 109 | 
             
                  context 'one key just past max: 1, one key within max: 1' do
         | 
| @@ -114,8 +114,8 @@ describe Weeter::Limitator do | |
| 114 114 | 
             
                      limitator.process(keys.first)
         | 
| 115 115 | 
             
                    end
         | 
| 116 116 |  | 
| 117 | 
            -
                     | 
| 118 | 
            -
                     | 
| 117 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 118 | 
            +
                    it { expect(subject.limited_keys).to eq([keys.first]) }
         | 
| 119 119 | 
             
                  end
         | 
| 120 120 |  | 
| 121 121 | 
             
                  context 'one key past max: 1, one key within max: 1' do
         | 
| @@ -127,8 +127,8 @@ describe Weeter::Limitator do | |
| 127 127 | 
             
                      limitator.process(keys.first)
         | 
| 128 128 | 
             
                    end
         | 
| 129 129 |  | 
| 130 | 
            -
                     | 
| 131 | 
            -
                     | 
| 130 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::CONTINUE_LIMITING) }
         | 
| 131 | 
            +
                    it { expect(subject.limited_keys).to eq([keys.first]) }
         | 
| 132 132 | 
             
                  end
         | 
| 133 133 |  | 
| 134 134 | 
             
                  context 'one key past max: 1, one key just past max: 1' do
         | 
| @@ -140,8 +140,8 @@ describe Weeter::Limitator do | |
| 140 140 | 
             
                      limitator.process(*[keys.first, keys.last])
         | 
| 141 141 | 
             
                    end
         | 
| 142 142 |  | 
| 143 | 
            -
                     | 
| 144 | 
            -
                     | 
| 143 | 
            +
                    it { expect(subject.status).to eq(Weeter::Limitator::INITIATE_LIMITING) }
         | 
| 144 | 
            +
                    it { expect(subject.limited_keys).to eq(keys) }
         | 
| 145 145 | 
             
                  end
         | 
| 146 146 | 
             
                end
         | 
| 147 147 | 
             
              end
         | 
| @@ -4,14 +4,14 @@ module Weeter | |
| 4 4 | 
             
              module Plugins
         | 
| 5 5 | 
             
                describe NotificationPlugin do
         | 
| 6 6 | 
             
                  describe "#publish_tweet" do
         | 
| 7 | 
            -
                    it " | 
| 7 | 
            +
                    it "delegates to the configured plugin" do
         | 
| 8 8 | 
             
                      client_app_config = Hashie::Mash.new(:notification_plugin => :http)
         | 
| 9 9 | 
             
                      tweet_item = TweetItem.new({})
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                      mock_plugin =  | 
| 12 | 
            -
                      Notification::Http. | 
| 13 | 
            -
             | 
| 14 | 
            -
                      mock_plugin. | 
| 10 | 
            +
             | 
| 11 | 
            +
                      mock_plugin = double(Notification::Http)
         | 
| 12 | 
            +
                      expect(Notification::Http).to receive(:new).and_return(mock_plugin)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      expect(mock_plugin).to receive(:publish_tweet).with(tweet_item)
         | 
| 15 15 |  | 
| 16 16 | 
             
                      plugin = NotificationPlugin.new(client_app_config)
         | 
| 17 17 | 
             
                      plugin.publish_tweet(tweet_item)
         | 
| @@ -5,26 +5,26 @@ module Weeter | |
| 5 5 | 
             
                  describe Http::UpdateServer do
         | 
| 6 6 | 
             
                    before(:each) do
         | 
| 7 7 | 
             
                      @new_ids = [1,2,3]
         | 
| 8 | 
            -
                      @tweet_consumer =  | 
| 8 | 
            +
                      @tweet_consumer = double('TweetConsumer', :reconnect => nil)
         | 
| 9 9 | 
             
                      @tweet_server = Http::UpdateServer.new(nil)
         | 
| 10 10 | 
             
                      @tweet_server.instance_variable_set('@http_post_content', MultiJson.encode(@new_ids))
         | 
| 11 11 | 
             
                      @tweet_server.tweet_consumer = @tweet_consumer
         | 
| 12 | 
            -
                      @response =  | 
| 13 | 
            -
                      EM::DelegatedHttpResponse. | 
| 12 | 
            +
                      @response = double('DelegatedHttpResponse', :send_response => nil)
         | 
| 13 | 
            +
                      expect(EM::DelegatedHttpResponse).to receive(:new).and_return(@response)
         | 
| 14 14 | 
             
                    end
         | 
| 15 | 
            -
             | 
| 15 | 
            +
             | 
| 16 16 | 
             
                    after(:each) do
         | 
| 17 17 | 
             
                      @tweet_server.process_http_request
         | 
| 18 18 | 
             
                    end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    it " | 
| 21 | 
            -
                      @tweet_consumer. | 
| 19 | 
            +
             | 
| 20 | 
            +
                    it "processes http request" do
         | 
| 21 | 
            +
                      expect(@tweet_consumer).to receive(:reconnect).with(@new_ids)
         | 
| 22 22 | 
             
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    it " | 
| 25 | 
            -
                      @response. | 
| 23 | 
            +
             | 
| 24 | 
            +
                    it "sends the response" do
         | 
| 25 | 
            +
                      expect(@response). to receive(:send_response)
         | 
| 26 26 | 
             
                    end
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 | 
             
                end
         | 
| 29 29 | 
             
              end
         | 
| 30 | 
            -
            end
         | 
| 30 | 
            +
            end
         | 
| @@ -4,17 +4,17 @@ module Weeter | |
| 4 4 | 
             
              module Plugins
         | 
| 5 5 | 
             
                describe SubscriptionPlugin do
         | 
| 6 6 | 
             
                  describe "#get_initial_filters" do
         | 
| 7 | 
            -
                    it " | 
| 7 | 
            +
                    it "delegates to the configured plugin" do
         | 
| 8 8 | 
             
                      client_app_config = Hashie::Mash.new(:subscription_plugin => :http)
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                      mock_plugin =  | 
| 11 | 
            -
                      Subscription::Http. | 
| 12 | 
            -
             | 
| 13 | 
            -
                      mock_plugin. | 
| 9 | 
            +
             | 
| 10 | 
            +
                      mock_plugin = double(Subscription::Http)
         | 
| 11 | 
            +
                      expect(Subscription::Http).to receive(:new).and_return(mock_plugin)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      expect(mock_plugin).to receive(:get_initial_filters).and_yield([{'foo' => 'bar'}])
         | 
| 14 14 |  | 
| 15 15 | 
             
                      plugin = SubscriptionPlugin.new(client_app_config)
         | 
| 16 16 | 
             
                      plugin.get_initial_filters do |filter_params|
         | 
| 17 | 
            -
                        filter_params. | 
| 17 | 
            +
                        expect(filter_params).to eq([{'foo' => 'bar'}])
         | 
| 18 18 | 
             
                      end
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 | 
             
                  end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require 'spec_helper'
         | 
| 2 | 
            +
            require 'em-twitter'
         | 
| 2 3 |  | 
| 3 4 | 
             
            describe Weeter::Twitter::TweetConsumer do
         | 
| 4 5 | 
             
              let(:limiter) do
         | 
| @@ -9,20 +10,25 @@ describe Weeter::Twitter::TweetConsumer do | |
| 9 10 | 
             
              end
         | 
| 10 11 |  | 
| 11 12 | 
             
              describe "auth" do
         | 
| 12 | 
            -
                it ' | 
| 13 | 
            -
                   | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                   | 
| 18 | 
            -
                  Twitter:: | 
| 13 | 
            +
                it 'connects to JSON stream with auth options for the configuration' do
         | 
| 14 | 
            +
                  mock_client = double('EM::Twitter::Client', on_error: nil, on_unauthorized: nil, on_forbidden: nil, on_not_found: nil,
         | 
| 15 | 
            +
                    on_not_acceptable: nil, on_range_unacceptable: nil, on_too_long: nil, on_enhance_your_calm: nil, on_reconnect: nil,
         | 
| 16 | 
            +
                    on_max_reconnects: nil,
         | 
| 17 | 
            +
                    each: nil)
         | 
| 18 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance).to receive(:auth_options).and_return(:oauth => { :foo => :bar }).at_least(:once)
         | 
| 19 | 
            +
                  expect(EM::Twitter::Client).to receive(:connect).with(hash_including(
         | 
| 20 | 
            +
                    :path => "/1.1/statuses/filter.json",
         | 
| 21 | 
            +
                    :params => { 'follow' => [1, 2] }
         | 
| 22 | 
            +
                  )).and_return(mock_client)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  consumer = Weeter::Twitter::TweetConsumer.new(Weeter::Configuration::TwitterConfig.instance, double('NotificationPlugin'), limiter)
         | 
| 19 25 | 
             
                  consumer.connect({'follow' => ['1','2']})
         | 
| 20 26 | 
             
                end
         | 
| 21 27 | 
             
              end
         | 
| 22 28 |  | 
| 23 29 | 
             
              describe '#limit_filter_params' do
         | 
| 24 30 |  | 
| 25 | 
            -
                let(:client_proxy) {  | 
| 31 | 
            +
                let(:client_proxy) { double('NotificationPlugin', :publish_tweet => nil) }
         | 
| 26 32 | 
             
                let(:consumer) do
         | 
| 27 33 | 
             
                  Weeter::Twitter::TweetConsumer.new(Weeter::Configuration::TwitterConfig.instance, client_proxy, limiter)
         | 
| 28 34 | 
             
                end
         | 
| @@ -40,8 +46,8 @@ describe Weeter::Twitter::TweetConsumer do | |
| 40 46 | 
             
                context 'limit not reached' do
         | 
| 41 47 | 
             
                  it 'leaves the values alone' do
         | 
| 42 48 | 
             
                    result = consumer.send(:limit_filter_params, params)
         | 
| 43 | 
            -
                    result.fetch('track').length. | 
| 44 | 
            -
                    result.fetch('follow').length. | 
| 49 | 
            +
                    expect(result.fetch('track').length).to eq(0)
         | 
| 50 | 
            +
                    expect(result.fetch('follow').length).to eq(0)
         | 
| 45 51 | 
             
                  end
         | 
| 46 52 | 
             
                end
         | 
| 47 53 |  | 
| @@ -50,8 +56,8 @@ describe Weeter::Twitter::TweetConsumer do | |
| 50 56 |  | 
| 51 57 | 
             
                  it 'it limits follows, but not tracks' do
         | 
| 52 58 | 
             
                    result = consumer.send(:limit_filter_params, params)
         | 
| 53 | 
            -
                    result.fetch('follow').length. | 
| 54 | 
            -
                    result.fetch('track').length. | 
| 59 | 
            +
                    expect(result.fetch('follow').length).to eq(5000)
         | 
| 60 | 
            +
                    expect(result.fetch('track').length).to eq(0)
         | 
| 55 61 | 
             
                  end
         | 
| 56 62 | 
             
                end
         | 
| 57 63 |  | 
| @@ -60,8 +66,8 @@ describe Weeter::Twitter::TweetConsumer do | |
| 60 66 |  | 
| 61 67 | 
             
                  it 'limits tracks, but not follows' do
         | 
| 62 68 | 
             
                    result = consumer.send(:limit_filter_params, params)
         | 
| 63 | 
            -
                    result.fetch('track').length. | 
| 64 | 
            -
                    result.fetch('follow').length. | 
| 69 | 
            +
                    expect(result.fetch('track').length).to eq(400)
         | 
| 70 | 
            +
                    expect(result.fetch('follow').length).to eq(0)
         | 
| 65 71 | 
             
                  end
         | 
| 66 72 | 
             
                end
         | 
| 67 73 |  | 
| @@ -72,8 +78,8 @@ describe Weeter::Twitter::TweetConsumer do | |
| 72 78 | 
             
                  it 'limits both' do
         | 
| 73 79 |  | 
| 74 80 | 
             
                    result = consumer.send(:limit_filter_params, params)
         | 
| 75 | 
            -
                    result.fetch('track').length. | 
| 76 | 
            -
                    result.fetch('follow').length. | 
| 81 | 
            +
                    expect(result.fetch('track').length).to eq(400)
         | 
| 82 | 
            +
                    expect(result.fetch('follow').length).to eq(5000)
         | 
| 77 83 | 
             
                  end
         | 
| 78 84 | 
             
                end
         | 
| 79 85 | 
             
              end
         | 
| @@ -83,20 +89,27 @@ describe Weeter::Twitter::TweetConsumer do | |
| 83 89 | 
             
                let(:tweet_values) {
         | 
| 84 90 | 
             
                  [@tweet_hash]
         | 
| 85 91 | 
             
                }
         | 
| 86 | 
            -
                let(: | 
| 87 | 
            -
                   | 
| 88 | 
            -
             | 
| 92 | 
            +
                let(:mock_client) {
         | 
| 93 | 
            +
                  client = double('EM::Twitter::Client', on_error: nil, on_unauthorized: nil, on_forbidden: nil, on_not_found: nil,
         | 
| 94 | 
            +
                    on_not_acceptable: nil, on_range_unacceptable: nil, on_too_long: nil, on_enhance_your_calm: nil, on_reconnect: nil,
         | 
| 95 | 
            +
                    on_max_reconnects: nil,
         | 
| 96 | 
            +
                    each: nil)
         | 
| 97 | 
            +
                  client_expectation = expect(client).to receive(:each)
         | 
| 89 98 | 
             
                  tweet_values.each do |t|
         | 
| 90 | 
            -
                     | 
| 99 | 
            +
                    client_expectation.and_yield(MultiJson.encode(t))
         | 
| 91 100 | 
             
                  end
         | 
| 92 | 
            -
                   | 
| 101 | 
            +
                  client
         | 
| 93 102 | 
             
                }
         | 
| 94 103 | 
             
                before(:each) do
         | 
| 95 | 
            -
                  @filter_params = {'follow' => ['1','2','3']}
         | 
| 96 | 
            -
                  Weeter::Configuration::TwitterConfig.instance.stub!(:auth_options).and_return(:foo => :bar)
         | 
| 97 104 | 
             
                  @tweet_hash = {'text' => "Hey", 'id_str' => "123", 'user' => {'id_str' => "1"}}
         | 
| 98 | 
            -
                  Twitter:: | 
| 99 | 
            -
             | 
| 105 | 
            +
                  expect(EM::Twitter::Client).to receive(:connect).with(hash_including(
         | 
| 106 | 
            +
                    :path => "/1.1/statuses/filter.json",
         | 
| 107 | 
            +
                    :params => { 'follow' => [1, 2, 3] }
         | 
| 108 | 
            +
                  )).and_return(mock_client)
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  @filter_params = {'follow' => ['1','2','3']}
         | 
| 111 | 
            +
                  expect(Weeter::Configuration::TwitterConfig.instance).to receive(:auth_options).and_return(:oauth => { :foo => :bar }).at_least(:once)
         | 
| 112 | 
            +
                  @client_proxy = double('NotificationPlugin', :publish_tweet => nil)
         | 
| 100 113 | 
             
                  @consumer = Weeter::Twitter::TweetConsumer.new(Weeter::Configuration::TwitterConfig.instance, @client_proxy, limiter)
         | 
| 101 114 | 
             
                end
         | 
| 102 115 |  | 
| @@ -104,50 +117,45 @@ describe Weeter::Twitter::TweetConsumer do | |
| 104 117 | 
             
                  @consumer.connect(@filter_params)
         | 
| 105 118 | 
             
                end
         | 
| 106 119 |  | 
| 107 | 
            -
                it " | 
| 120 | 
            +
                it "instantiates a TweetItem" do
         | 
| 108 121 | 
             
                  tweet_item = Weeter::TweetItem.new(@tweet_hash)
         | 
| 109 | 
            -
                  Weeter::TweetItem. | 
| 110 | 
            -
                end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                it "should connect to a Twitter JSON stream" do
         | 
| 113 | 
            -
                  Twitter::JSONStream.should_receive(:connect).
         | 
| 114 | 
            -
                    with(:ssl => true, :foo => :bar, :params => {'follow' => [1,2,3]}, :method => 'POST')
         | 
| 122 | 
            +
                  expect(Weeter::TweetItem).to receive(:new).with({'text' => "Hey", 'id_str' => "123", 'user' => {'id_str' => "1"}}).and_return(tweet_item)
         | 
| 115 123 | 
             
                end
         | 
| 116 124 |  | 
| 117 | 
            -
                it " | 
| 118 | 
            -
                  mock_tweet =  | 
| 119 | 
            -
                  Weeter::TweetItem. | 
| 120 | 
            -
                  @client_proxy. | 
| 125 | 
            +
                it "publishes new tweet if publishable" do
         | 
| 126 | 
            +
                  mock_tweet = double('tweet', :deletion? => false, :publishable? => true, :limit_notice? => false, :limiting_facets => [])
         | 
| 127 | 
            +
                  expect(Weeter::TweetItem).to receive(:new).and_return(mock_tweet)
         | 
| 128 | 
            +
                  expect(@client_proxy).to receive(:publish_tweet).with(mock_tweet)
         | 
| 121 129 | 
             
                end
         | 
| 122 130 |  | 
| 123 | 
            -
                it " | 
| 124 | 
            -
                  mock_tweet =  | 
| 125 | 
            -
                  Weeter::TweetItem. | 
| 126 | 
            -
                  @client_proxy. | 
| 131 | 
            +
                it "does not publish unpublishable tweets" do
         | 
| 132 | 
            +
                  mock_tweet = double('tweet', :deletion? => false, :publishable? => false, :limit_notice? => false, :[] => '', :limiting_facets => [], :disconnect_notice? => false)
         | 
| 133 | 
            +
                  expect(Weeter::TweetItem).to receive(:new).and_return mock_tweet
         | 
| 134 | 
            +
                  expect(@client_proxy).to_not receive(:publish_tweet).with(mock_tweet)
         | 
| 127 135 | 
             
                end
         | 
| 128 136 |  | 
| 129 | 
            -
                it " | 
| 130 | 
            -
                  mock_tweet =  | 
| 131 | 
            -
                  Weeter::TweetItem. | 
| 132 | 
            -
                  @client_proxy. | 
| 137 | 
            +
                it "deletes deletion tweets" do
         | 
| 138 | 
            +
                  mock_tweet = double('tweet', :deletion? => true, :publishable? => false, :limit_notice? => false, :limiting_facets => [])
         | 
| 139 | 
            +
                  expect(Weeter::TweetItem).to receive(:new).and_return mock_tweet
         | 
| 140 | 
            +
                  expect(@client_proxy).to receive(:delete_tweet).with(mock_tweet)
         | 
| 133 141 | 
             
                end
         | 
| 134 142 |  | 
| 135 | 
            -
                it " | 
| 143 | 
            +
                it "notifies when stream is limited by Twitter" do
         | 
| 136 144 | 
             
                  tweet_item = Weeter::TweetItem.new({'limit' => { 'track' => 65 } })
         | 
| 137 | 
            -
                  Weeter::TweetItem. | 
| 138 | 
            -
                  @client_proxy. | 
| 145 | 
            +
                  expect(Weeter::TweetItem).to receive(:new).and_return(tweet_item)
         | 
| 146 | 
            +
                  expect(@client_proxy).to receive(:notify_missed_tweets).with(tweet_item)
         | 
| 139 147 | 
             
                end
         | 
| 140 148 |  | 
| 141 149 | 
             
                context "when weeter is initiating rate-limiting on a facet" do
         | 
| 142 150 | 
             
                  let(:tweet_values) {
         | 
| 143 151 | 
             
                    [@tweet_hash, @tweet_hash]
         | 
| 144 152 | 
             
                  }
         | 
| 145 | 
            -
                  it " | 
| 146 | 
            -
                    tweet_item1 =  | 
| 147 | 
            -
                    tweet_item2 =  | 
| 148 | 
            -
                    Weeter::TweetItem. | 
| 153 | 
            +
                  it "notifies that rate limiting is being initiated" do
         | 
| 154 | 
            +
                    tweet_item1 = double('tweet', :deletion? => false, :publishable? => true, :limit_notice? => false, :limiting_facets => ['key'], :[] => '1')
         | 
| 155 | 
            +
                    tweet_item2 = double('tweet', :deletion? => false, :publishable? => true, :limit_notice? => false, :limiting_facets => ['key'], :[] => '2')
         | 
| 156 | 
            +
                    expect(Weeter::TweetItem).to receive(:new).and_return(tweet_item1, tweet_item2)
         | 
| 149 157 |  | 
| 150 | 
            -
                    @client_proxy. | 
| 158 | 
            +
                    expect(@client_proxy).to receive(:notify_rate_limiting_initiated).with(tweet_item2, ['key'])
         | 
| 151 159 | 
             
                  end
         | 
| 152 160 | 
             
                end
         | 
| 153 161 | 
             
              end
         | 
| @@ -6,77 +6,77 @@ describe Weeter::TweetItem do | |
| 6 6 | 
             
              }
         | 
| 7 7 |  | 
| 8 8 | 
             
              describe "deletion?" do
         | 
| 9 | 
            -
                it " | 
| 9 | 
            +
                it "is true if it is a deletion request" do
         | 
| 10 10 | 
             
                  item = Weeter::TweetItem.new({"delete"=>{"status"=>{"id"=>234, "user_id"=>34555}}})
         | 
| 11 | 
            -
                  item. | 
| 11 | 
            +
                  expect(item).to be_deletion
         | 
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 | 
            -
                it " | 
| 14 | 
            +
                it "is false if it is not a deletion request" do
         | 
| 15 15 | 
             
                  item = Weeter::TweetItem.new({'text' => "Hey", 'id_str' => "123", 'user' => {'id_str' => "1"}})
         | 
| 16 | 
            -
                  item. | 
| 16 | 
            +
                  expect(item).to_not be_deletion
         | 
| 17 17 | 
             
                end
         | 
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 20 | 
             
              describe "publishable" do
         | 
| 21 21 |  | 
| 22 22 |  | 
| 23 | 
            -
                it " | 
| 23 | 
            +
                it "is publishable if not a reply or a retweet" do
         | 
| 24 24 | 
             
                  item = Weeter::TweetItem.new(tweet_json)
         | 
| 25 | 
            -
                  item. | 
| 25 | 
            +
                  expect(item).to be_publishable
         | 
| 26 26 | 
             
                end
         | 
| 27 27 |  | 
| 28 | 
            -
                it " | 
| 28 | 
            +
                it "is not publishable if implicitly retweeted" do
         | 
| 29 29 | 
             
                  item = Weeter::TweetItem.new(tweet_json.merge({'text' => 'RT @joe Hey'}))
         | 
| 30 | 
            -
                  item. | 
| 30 | 
            +
                  expect(item).to_not be_publishable
         | 
| 31 31 | 
             
                end
         | 
| 32 32 |  | 
| 33 | 
            -
                it " | 
| 33 | 
            +
                it "is not publishable if explicitly retweeted" do
         | 
| 34 34 | 
             
                  item = Weeter::TweetItem.new(tweet_json.merge('retweeted_status' => {'id_str' => '111', 'text' => 'Hey', 'user' => {'id_str' => "1"}}))
         | 
| 35 | 
            -
                  item. | 
| 35 | 
            +
                  expect(item).to_not be_publishable
         | 
| 36 36 | 
             
                end
         | 
| 37 37 |  | 
| 38 | 
            -
                it " | 
| 38 | 
            +
                it "is not publishable if implicit reply" do
         | 
| 39 39 | 
             
                  item = Weeter::TweetItem.new(tweet_json.merge('text' => '@joe Hey'))
         | 
| 40 | 
            -
                  item. | 
| 40 | 
            +
                  expect(item).to_not be_publishable
         | 
| 41 41 | 
             
                end
         | 
| 42 42 |  | 
| 43 | 
            -
                it " | 
| 43 | 
            +
                it "is not publishable if explicit reply" do
         | 
| 44 44 | 
             
                  item = Weeter::TweetItem.new(tweet_json.merge('text' => '@joe Hey', 'in_reply_to_user_id_str' => '1'))
         | 
| 45 | 
            -
                  item. | 
| 45 | 
            +
                  expect(item).to_not be_publishable
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| 48 | 
            -
                it " | 
| 48 | 
            +
                it "is not publishable if disconnect message" do
         | 
| 49 49 | 
             
                  item = Weeter::TweetItem.new({"disconnect" => {"code" => 7,"stream_name" => "YappBox-statuses668638","reason" => "admin logout"}})
         | 
| 50 | 
            -
                  item. | 
| 50 | 
            +
                  expect(item).to_not be_publishable
         | 
| 51 51 | 
             
                end
         | 
| 52 52 |  | 
| 53 53 | 
             
              end
         | 
| 54 54 |  | 
| 55 55 | 
             
              describe "limit_notice?" do
         | 
| 56 | 
            -
                it " | 
| 56 | 
            +
                it "is true if it's a limit notice" do
         | 
| 57 57 | 
             
                  item = Weeter::TweetItem.new({ 'limit' => { 'track' => 65 }})
         | 
| 58 | 
            -
                  item. | 
| 59 | 
            -
                  item.missed_tweets_count. | 
| 58 | 
            +
                  expect(item).to be_limit_notice
         | 
| 59 | 
            +
                  expect(item.missed_tweets_count).to eq(65)
         | 
| 60 60 | 
             
                end
         | 
| 61 | 
            -
                it " | 
| 61 | 
            +
                it "is not be true if it's a limit notice" do
         | 
| 62 62 | 
             
                  item = Weeter::TweetItem.new(tweet_json)
         | 
| 63 | 
            -
                  item. | 
| 64 | 
            -
                  lambda {
         | 
| 63 | 
            +
                  expect(item).to_not be_limit_notice
         | 
| 64 | 
            +
                  expect(lambda {
         | 
| 65 65 | 
             
                    item.missed_tweets_count
         | 
| 66 | 
            -
                  }. | 
| 66 | 
            +
                  }).to_not raise_error
         | 
| 67 67 | 
             
                end
         | 
| 68 68 | 
             
              end
         | 
| 69 69 |  | 
| 70 70 | 
             
              describe "json attributes" do
         | 
| 71 71 |  | 
| 72 | 
            -
                it " | 
| 72 | 
            +
                it "delegates hash calls to its json" do
         | 
| 73 73 | 
             
                  item = Weeter::TweetItem.new({'text' => "Hey"})
         | 
| 74 | 
            -
                  item['text']. | 
| 74 | 
            +
                  expect(item['text']).to eq("Hey")
         | 
| 75 75 | 
             
                end
         | 
| 76 76 |  | 
| 77 | 
            -
                it " | 
| 77 | 
            +
                it "retrieves nested attributes" do
         | 
| 78 78 | 
             
                  item = Weeter::TweetItem.new({'text' => "Hey", 'id_str' => "123", 'user' => {'id_str' => '1'}})
         | 
| 79 | 
            -
                  item['user']['id_str']. | 
| 79 | 
            +
                  expect(item['user']['id_str']).to eq("1")
         | 
| 80 80 | 
             
                end
         | 
| 81 81 |  | 
| 82 82 | 
             
              end
         | 
    
        data/weeter.gemspec
    CHANGED
    
    | @@ -19,17 +19,18 @@ Gem::Specification.new do |s| | |
| 19 19 | 
             
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 20 20 | 
             
              s.require_paths = ["lib"]
         | 
| 21 21 |  | 
| 22 | 
            -
              s.add_dependency('eventmachine')
         | 
| 23 | 
            -
              s.add_dependency('eventmachine_httpserver', ' | 
| 24 | 
            -
              s.add_dependency('em-hiredis', ' | 
| 25 | 
            -
              s.add_dependency('multi_json', ' | 
| 26 | 
            -
              s.add_dependency('hashie', '>=  | 
| 27 | 
            -
              s.add_dependency('em-http-request', ' | 
| 28 | 
            -
              s.add_dependency('i18n', "~> 0.6. | 
| 29 | 
            -
              s.add_dependency('activesupport', ">= 3. | 
| 30 | 
            -
              s.add_dependency("simple_oauth", ' | 
| 31 | 
            -
              s.add_dependency(' | 
| 22 | 
            +
              s.add_dependency('eventmachine', '~> 1.2.0')
         | 
| 23 | 
            +
              s.add_dependency('eventmachine_httpserver', '~> 0.2.1')
         | 
| 24 | 
            +
              s.add_dependency('em-hiredis', '~> 0.3.1')
         | 
| 25 | 
            +
              s.add_dependency('multi_json', '~> 1.3.0')
         | 
| 26 | 
            +
              s.add_dependency('hashie', '>= 2.0.5')
         | 
| 27 | 
            +
              s.add_dependency('em-http-request', '~> 1.1.5')
         | 
| 28 | 
            +
              s.add_dependency('i18n', "~> 0.6.11")
         | 
| 29 | 
            +
              s.add_dependency('activesupport', ">= 3.2.22")
         | 
| 30 | 
            +
              s.add_dependency("simple_oauth", '~> 0.3.1')
         | 
| 31 | 
            +
              s.add_dependency('em-twitter', '~> 0.3.5')
         | 
| 32 32 |  | 
| 33 | 
            -
              s.add_development_dependency 'rspec', '~>  | 
| 33 | 
            +
              s.add_development_dependency 'rspec', '~> 3.4.0'
         | 
| 34 | 
            +
              s.add_development_dependency 'byebug', '~> 2.4.1'
         | 
| 34 35 | 
             
              s.add_development_dependency 'ZenTest'
         | 
| 35 36 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,8 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: weeter
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 5 | 
            -
              prerelease: 
         | 
| 4 | 
            +
              version: 0.17.0
         | 
| 6 5 | 
             
            platform: ruby
         | 
| 7 6 | 
             
            authors:
         | 
| 8 7 | 
             
            - Luke Melia
         | 
| @@ -11,200 +10,190 @@ authors: | |
| 11 10 | 
             
            autorequire: 
         | 
| 12 11 | 
             
            bindir: bin
         | 
| 13 12 | 
             
            cert_chain: []
         | 
| 14 | 
            -
            date:  | 
| 13 | 
            +
            date: 2017-07-21 00:00:00.000000000 Z
         | 
| 15 14 | 
             
            dependencies:
         | 
| 16 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 16 | 
             
              name: eventmachine
         | 
| 18 | 
            -
              prerelease: false
         | 
| 19 17 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 20 18 | 
             
                requirements:
         | 
| 21 | 
            -
                - -  | 
| 19 | 
            +
                - - "~>"
         | 
| 22 20 | 
             
                  - !ruby/object:Gem::Version
         | 
| 23 | 
            -
                    version:  | 
| 24 | 
            -
                none: false
         | 
| 21 | 
            +
                    version: 1.2.0
         | 
| 25 22 | 
             
              type: :runtime
         | 
| 23 | 
            +
              prerelease: false
         | 
| 26 24 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 27 25 | 
             
                requirements:
         | 
| 28 | 
            -
                - -  | 
| 26 | 
            +
                - - "~>"
         | 
| 29 27 | 
             
                  - !ruby/object:Gem::Version
         | 
| 30 | 
            -
                    version:  | 
| 31 | 
            -
                none: false
         | 
| 28 | 
            +
                    version: 1.2.0
         | 
| 32 29 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 33 30 | 
             
              name: eventmachine_httpserver
         | 
| 34 | 
            -
              prerelease: false
         | 
| 35 31 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 36 32 | 
             
                requirements:
         | 
| 37 | 
            -
                - -  | 
| 33 | 
            +
                - - "~>"
         | 
| 38 34 | 
             
                  - !ruby/object:Gem::Version
         | 
| 39 35 | 
             
                    version: 0.2.1
         | 
| 40 | 
            -
                none: false
         | 
| 41 36 | 
             
              type: :runtime
         | 
| 37 | 
            +
              prerelease: false
         | 
| 42 38 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 43 39 | 
             
                requirements:
         | 
| 44 | 
            -
                - -  | 
| 40 | 
            +
                - - "~>"
         | 
| 45 41 | 
             
                  - !ruby/object:Gem::Version
         | 
| 46 42 | 
             
                    version: 0.2.1
         | 
| 47 | 
            -
                none: false
         | 
| 48 43 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 49 44 | 
             
              name: em-hiredis
         | 
| 50 | 
            -
              prerelease: false
         | 
| 51 45 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 52 46 | 
             
                requirements:
         | 
| 53 | 
            -
                - -  | 
| 47 | 
            +
                - - "~>"
         | 
| 54 48 | 
             
                  - !ruby/object:Gem::Version
         | 
| 55 | 
            -
                    version: 0.1 | 
| 56 | 
            -
                none: false
         | 
| 49 | 
            +
                    version: 0.3.1
         | 
| 57 50 | 
             
              type: :runtime
         | 
| 51 | 
            +
              prerelease: false
         | 
| 58 52 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 59 53 | 
             
                requirements:
         | 
| 60 | 
            -
                - -  | 
| 54 | 
            +
                - - "~>"
         | 
| 61 55 | 
             
                  - !ruby/object:Gem::Version
         | 
| 62 | 
            -
                    version: 0.1 | 
| 63 | 
            -
                none: false
         | 
| 56 | 
            +
                    version: 0.3.1
         | 
| 64 57 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 65 58 | 
             
              name: multi_json
         | 
| 66 | 
            -
              prerelease: false
         | 
| 67 59 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 68 60 | 
             
                requirements:
         | 
| 69 | 
            -
                - -  | 
| 61 | 
            +
                - - "~>"
         | 
| 70 62 | 
             
                  - !ruby/object:Gem::Version
         | 
| 71 | 
            -
                    version: 1.0 | 
| 72 | 
            -
                none: false
         | 
| 63 | 
            +
                    version: 1.3.0
         | 
| 73 64 | 
             
              type: :runtime
         | 
| 65 | 
            +
              prerelease: false
         | 
| 74 66 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 75 67 | 
             
                requirements:
         | 
| 76 | 
            -
                - -  | 
| 68 | 
            +
                - - "~>"
         | 
| 77 69 | 
             
                  - !ruby/object:Gem::Version
         | 
| 78 | 
            -
                    version: 1.0 | 
| 79 | 
            -
                none: false
         | 
| 70 | 
            +
                    version: 1.3.0
         | 
| 80 71 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 81 72 | 
             
              name: hashie
         | 
| 82 | 
            -
              prerelease: false
         | 
| 83 73 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 84 74 | 
             
                requirements:
         | 
| 85 | 
            -
                - -  | 
| 75 | 
            +
                - - ">="
         | 
| 86 76 | 
             
                  - !ruby/object:Gem::Version
         | 
| 87 | 
            -
                    version:  | 
| 88 | 
            -
                none: false
         | 
| 77 | 
            +
                    version: 2.0.5
         | 
| 89 78 | 
             
              type: :runtime
         | 
| 79 | 
            +
              prerelease: false
         | 
| 90 80 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 91 81 | 
             
                requirements:
         | 
| 92 | 
            -
                - -  | 
| 82 | 
            +
                - - ">="
         | 
| 93 83 | 
             
                  - !ruby/object:Gem::Version
         | 
| 94 | 
            -
                    version:  | 
| 95 | 
            -
                none: false
         | 
| 84 | 
            +
                    version: 2.0.5
         | 
| 96 85 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 97 86 | 
             
              name: em-http-request
         | 
| 98 | 
            -
              prerelease: false
         | 
| 99 87 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 88 | 
             
                requirements:
         | 
| 101 | 
            -
                - -  | 
| 89 | 
            +
                - - "~>"
         | 
| 102 90 | 
             
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            -
                    version: 1. | 
| 104 | 
            -
                none: false
         | 
| 91 | 
            +
                    version: 1.1.5
         | 
| 105 92 | 
             
              type: :runtime
         | 
| 93 | 
            +
              prerelease: false
         | 
| 106 94 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 95 | 
             
                requirements:
         | 
| 108 | 
            -
                - -  | 
| 96 | 
            +
                - - "~>"
         | 
| 109 97 | 
             
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            -
                    version: 1. | 
| 111 | 
            -
                none: false
         | 
| 98 | 
            +
                    version: 1.1.5
         | 
| 112 99 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 113 100 | 
             
              name: i18n
         | 
| 114 | 
            -
              prerelease: false
         | 
| 115 101 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 116 102 | 
             
                requirements:
         | 
| 117 | 
            -
                - - ~>
         | 
| 103 | 
            +
                - - "~>"
         | 
| 118 104 | 
             
                  - !ruby/object:Gem::Version
         | 
| 119 | 
            -
                    version: 0.6. | 
| 120 | 
            -
                none: false
         | 
| 105 | 
            +
                    version: 0.6.11
         | 
| 121 106 | 
             
              type: :runtime
         | 
| 107 | 
            +
              prerelease: false
         | 
| 122 108 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 123 109 | 
             
                requirements:
         | 
| 124 | 
            -
                - - ~>
         | 
| 110 | 
            +
                - - "~>"
         | 
| 125 111 | 
             
                  - !ruby/object:Gem::Version
         | 
| 126 | 
            -
                    version: 0.6. | 
| 127 | 
            -
                none: false
         | 
| 112 | 
            +
                    version: 0.6.11
         | 
| 128 113 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 129 114 | 
             
              name: activesupport
         | 
| 130 | 
            -
              prerelease: false
         | 
| 131 115 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 132 116 | 
             
                requirements:
         | 
| 133 | 
            -
                - -  | 
| 117 | 
            +
                - - ">="
         | 
| 134 118 | 
             
                  - !ruby/object:Gem::Version
         | 
| 135 | 
            -
                    version: 3. | 
| 136 | 
            -
                none: false
         | 
| 119 | 
            +
                    version: 3.2.22
         | 
| 137 120 | 
             
              type: :runtime
         | 
| 121 | 
            +
              prerelease: false
         | 
| 138 122 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 139 123 | 
             
                requirements:
         | 
| 140 | 
            -
                - -  | 
| 124 | 
            +
                - - ">="
         | 
| 141 125 | 
             
                  - !ruby/object:Gem::Version
         | 
| 142 | 
            -
                    version: 3. | 
| 143 | 
            -
                none: false
         | 
| 126 | 
            +
                    version: 3.2.22
         | 
| 144 127 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 145 128 | 
             
              name: simple_oauth
         | 
| 146 | 
            -
              prerelease: false
         | 
| 147 129 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 148 130 | 
             
                requirements:
         | 
| 149 | 
            -
                - -  | 
| 131 | 
            +
                - - "~>"
         | 
| 150 132 | 
             
                  - !ruby/object:Gem::Version
         | 
| 151 | 
            -
                    version: 0.1 | 
| 152 | 
            -
                none: false
         | 
| 133 | 
            +
                    version: 0.3.1
         | 
| 153 134 | 
             
              type: :runtime
         | 
| 135 | 
            +
              prerelease: false
         | 
| 154 136 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 155 137 | 
             
                requirements:
         | 
| 156 | 
            -
                - -  | 
| 138 | 
            +
                - - "~>"
         | 
| 157 139 | 
             
                  - !ruby/object:Gem::Version
         | 
| 158 | 
            -
                    version: 0.1 | 
| 159 | 
            -
                none: false
         | 
| 140 | 
            +
                    version: 0.3.1
         | 
| 160 141 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 161 | 
            -
              name:  | 
| 162 | 
            -
              prerelease: false
         | 
| 142 | 
            +
              name: em-twitter
         | 
| 163 143 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 164 144 | 
             
                requirements:
         | 
| 165 | 
            -
                - - ~>
         | 
| 145 | 
            +
                - - "~>"
         | 
| 166 146 | 
             
                  - !ruby/object:Gem::Version
         | 
| 167 | 
            -
                    version: 0. | 
| 168 | 
            -
                none: false
         | 
| 147 | 
            +
                    version: 0.3.5
         | 
| 169 148 | 
             
              type: :runtime
         | 
| 149 | 
            +
              prerelease: false
         | 
| 170 150 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 171 151 | 
             
                requirements:
         | 
| 172 | 
            -
                - - ~>
         | 
| 152 | 
            +
                - - "~>"
         | 
| 173 153 | 
             
                  - !ruby/object:Gem::Version
         | 
| 174 | 
            -
                    version: 0. | 
| 175 | 
            -
                none: false
         | 
| 154 | 
            +
                    version: 0.3.5
         | 
| 176 155 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 177 156 | 
             
              name: rspec
         | 
| 157 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 158 | 
            +
                requirements:
         | 
| 159 | 
            +
                - - "~>"
         | 
| 160 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 161 | 
            +
                    version: 3.4.0
         | 
| 162 | 
            +
              type: :development
         | 
| 178 163 | 
             
              prerelease: false
         | 
| 164 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 165 | 
            +
                requirements:
         | 
| 166 | 
            +
                - - "~>"
         | 
| 167 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 168 | 
            +
                    version: 3.4.0
         | 
| 169 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 170 | 
            +
              name: byebug
         | 
| 179 171 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 180 172 | 
             
                requirements:
         | 
| 181 | 
            -
                - - ~>
         | 
| 173 | 
            +
                - - "~>"
         | 
| 182 174 | 
             
                  - !ruby/object:Gem::Version
         | 
| 183 | 
            -
                    version: 2. | 
| 184 | 
            -
                none: false
         | 
| 175 | 
            +
                    version: 2.4.1
         | 
| 185 176 | 
             
              type: :development
         | 
| 177 | 
            +
              prerelease: false
         | 
| 186 178 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 187 179 | 
             
                requirements:
         | 
| 188 | 
            -
                - - ~>
         | 
| 180 | 
            +
                - - "~>"
         | 
| 189 181 | 
             
                  - !ruby/object:Gem::Version
         | 
| 190 | 
            -
                    version: 2. | 
| 191 | 
            -
                none: false
         | 
| 182 | 
            +
                    version: 2.4.1
         | 
| 192 183 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 193 184 | 
             
              name: ZenTest
         | 
| 194 | 
            -
              prerelease: false
         | 
| 195 185 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 196 186 | 
             
                requirements:
         | 
| 197 | 
            -
                - -  | 
| 187 | 
            +
                - - ">="
         | 
| 198 188 | 
             
                  - !ruby/object:Gem::Version
         | 
| 199 189 | 
             
                    version: '0'
         | 
| 200 | 
            -
                none: false
         | 
| 201 190 | 
             
              type: :development
         | 
| 191 | 
            +
              prerelease: false
         | 
| 202 192 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 203 193 | 
             
                requirements:
         | 
| 204 | 
            -
                - -  | 
| 194 | 
            +
                - - ">="
         | 
| 205 195 | 
             
                  - !ruby/object:Gem::Version
         | 
| 206 196 | 
             
                    version: '0'
         | 
| 207 | 
            -
                none: false
         | 
| 208 197 | 
             
            description: Weeter subscribes to a set of twitter users or search terms using Twitter's
         | 
| 209 198 | 
             
              streaming API, and notifies your app with each new tweet.
         | 
| 210 199 | 
             
            email:
         | 
| @@ -215,8 +204,9 @@ executables: | |
| 215 204 | 
             
            extensions: []
         | 
| 216 205 | 
             
            extra_rdoc_files: []
         | 
| 217 206 | 
             
            files:
         | 
| 218 | 
            -
            - .gitignore
         | 
| 219 | 
            -
            - . | 
| 207 | 
            +
            - ".gitignore"
         | 
| 208 | 
            +
            - ".ruby-version"
         | 
| 209 | 
            +
            - ".travis.yml"
         | 
| 220 210 | 
             
            - Gemfile
         | 
| 221 211 | 
             
            - LICENSE
         | 
| 222 212 | 
             
            - README.md
         | 
| @@ -263,27 +253,26 @@ files: | |
| 263 253 | 
             
            - weeter.gemspec
         | 
| 264 254 | 
             
            homepage: http://github.com/lukemelia/weeter
         | 
| 265 255 | 
             
            licenses: []
         | 
| 256 | 
            +
            metadata: {}
         | 
| 266 257 | 
             
            post_install_message: 
         | 
| 267 258 | 
             
            rdoc_options: []
         | 
| 268 259 | 
             
            require_paths:
         | 
| 269 260 | 
             
            - lib
         | 
| 270 261 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 271 262 | 
             
              requirements:
         | 
| 272 | 
            -
              - -  | 
| 263 | 
            +
              - - ">="
         | 
| 273 264 | 
             
                - !ruby/object:Gem::Version
         | 
| 274 265 | 
             
                  version: '0'
         | 
| 275 | 
            -
              none: false
         | 
| 276 266 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 277 267 | 
             
              requirements:
         | 
| 278 | 
            -
              - -  | 
| 268 | 
            +
              - - ">="
         | 
| 279 269 | 
             
                - !ruby/object:Gem::Version
         | 
| 280 270 | 
             
                  version: '0'
         | 
| 281 | 
            -
              none: false
         | 
| 282 271 | 
             
            requirements: []
         | 
| 283 272 | 
             
            rubyforge_project: weeter
         | 
| 284 | 
            -
            rubygems_version:  | 
| 273 | 
            +
            rubygems_version: 2.4.8
         | 
| 285 274 | 
             
            signing_key: 
         | 
| 286 | 
            -
            specification_version:  | 
| 275 | 
            +
            specification_version: 4
         | 
| 287 276 | 
             
            summary: Consume the Twitter stream and notify your app
         | 
| 288 277 | 
             
            test_files:
         | 
| 289 278 | 
             
            - spec/spec_helper.rb
         |