From 07b49ee7f8abaacad86a8ff5ccca6751cf4aa351 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Thu, 6 Mar 2014 10:14:59 -0700 Subject: [PATCH 1/9] adds 'BW::NetworkIndicator' --- Rakefile | 2 +- lib/bubble-wrap/all.rb | 2 +- lib/bubble-wrap/network-indicator.rb | 8 +++ motion/http/query.rb | 10 +++- motion/network-indicator/network-indicator.rb | 36 ++++++++++++ .../network_indicator_spec.rb | 58 +++++++++++++++++++ 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 lib/bubble-wrap/network-indicator.rb create mode 100644 motion/network-indicator/network-indicator.rb create mode 100644 spec/motion/network-indicator/network_indicator_spec.rb diff --git a/Rakefile b/Rakefile index d0e6d5c2..a9d2379a 100644 --- a/Rakefile +++ b/Rakefile @@ -28,7 +28,7 @@ Motion::Project::App.setup do |app| app.spec_files if Motion::Project::App.osx? app.spec_files -= Dir.glob("./spec/motion/**/ios/**.rb") - ["font", "location", "media", "ui", "mail", "sms"].each do |package| + ["font", "location", "media", "ui", "mail", "sms", "network-indicator"].each do |package| app.spec_files -= Dir.glob("./spec/motion/#{package}/**/*.rb") end else diff --git a/lib/bubble-wrap/all.rb b/lib/bubble-wrap/all.rb index 8df626af..72815bbb 100644 --- a/lib/bubble-wrap/all.rb +++ b/lib/bubble-wrap/all.rb @@ -1,4 +1,4 @@ require File.expand_path('../loader', __FILE__) -['core', 'http', 'reactor', 'rss_parser', 'ui', 'location', 'media', 'font', 'mail','sms'].each { |sub| +['core', 'http', 'reactor', 'rss_parser', 'ui', 'location', 'media', 'font', 'mail','sms', 'network-indicator'].each { |sub| require File.expand_path("../#{sub}", __FILE__) } diff --git a/lib/bubble-wrap/network-indicator.rb b/lib/bubble-wrap/network-indicator.rb new file mode 100644 index 00000000..a668ae7a --- /dev/null +++ b/lib/bubble-wrap/network-indicator.rb @@ -0,0 +1,8 @@ +require 'bubble-wrap/loader' + +BubbleWrap.require_ios("network-indicator") do + BubbleWrap.require('motion/core/app.rb') + BubbleWrap.require('motion/network-indicator/**/*.rb') do + # file('motion/network-indicator/network-indicator.rb').depends_on('motion/network-indicator/result.rb') + end +end diff --git a/motion/http/query.rb b/motion/http/query.rb index c1815d7f..f9c3233b 100644 --- a/motion/http/query.rb +++ b/motion/http/query.rb @@ -171,7 +171,15 @@ def did_receive_response(response) def show_status_indicator(show) if App.ios? - UIApplication.sharedApplication.networkActivityIndicatorVisible = show + if defined?(BW::NetworkIndicator) + if show + BW::NetworkIndicator.show + else + BW::NetworkIndicator.hide + end + else + UIApplication.sharedApplication.networkActivityIndicatorVisible = show + end end end diff --git a/motion/network-indicator/network-indicator.rb b/motion/network-indicator/network-indicator.rb new file mode 100644 index 00000000..5db21700 --- /dev/null +++ b/motion/network-indicator/network-indicator.rb @@ -0,0 +1,36 @@ +module BubbleWrap + module NetworkIndicator + + module_function + + def show + self.counter += 1 + UIApplication.sharedApplication.networkActivityIndicatorVisible = true + end + + def hide + self.counter = [self.counter - 1, 0].max + if self.counter == 0 + UIApplication.sharedApplication.networkActivityIndicatorVisible = false + end + end + + def visible? + UIApplication.sharedApplication.networkActivityIndicatorVisible? + end + + def reset! + @counter = 0 + UIApplication.sharedApplication.networkActivityIndicatorVisible = false + end + + def counter + @counter ||= 0 + end + + def counter=(value) + @counter = value + end + + end +end diff --git a/spec/motion/network-indicator/network_indicator_spec.rb b/spec/motion/network-indicator/network_indicator_spec.rb new file mode 100644 index 00000000..c89cdf02 --- /dev/null +++ b/spec/motion/network-indicator/network_indicator_spec.rb @@ -0,0 +1,58 @@ +describe BW::NetworkIndicator do + + after do + BW::NetworkIndicator.instance_variable_set(:@counter, 0) + UIApplication.sharedApplication.networkActivityIndicatorVisible = false + end + + it 'should show the indicator' do + BW::NetworkIndicator.show + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + end + + it 'should hide the indicator' do + BW::NetworkIndicator.show + BW::NetworkIndicator.hide + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end + + it 'should keep track of how many times `show` was called' do + BW::NetworkIndicator.show + BW::NetworkIndicator.show + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + BW::NetworkIndicator.hide + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + BW::NetworkIndicator.hide + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end + + it 'should allow `hide` to be called too many times' do + BW::NetworkIndicator.show + BW::NetworkIndicator.show + BW::NetworkIndicator.hide + BW::NetworkIndicator.hide + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + + BW::NetworkIndicator.hide + BW::NetworkIndicator.hide + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + + BW::NetworkIndicator.show + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + end + + it 'should reset the counter when `reset!` is called' do + BW::NetworkIndicator.show + BW::NetworkIndicator.show + BW::NetworkIndicator.reset! + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end + + it 'should have `visible?` method' do + BW::NetworkIndicator.show + BW::NetworkIndicator.visible?.should == true + BW::NetworkIndicator.hide + BW::NetworkIndicator.visible?.should == false + end + +end From 4cea7ff0353850427aa83bd9f532f26abf2dedbd Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Thu, 6 Mar 2014 10:26:52 -0700 Subject: [PATCH 2/9] updated README --- README.md | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f90172a6..30f84f6e 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,12 @@ If you wish to only include the `SMS` wrapper: require 'bubble-wrap/sms' ``` +If you wish to only include the `NetworkIndicator` wrapper: + +```ruby +require 'bubble-wrap/network-indicator' +``` + If you want to include everything (ie kitchen sink mode) you can save time and do: ```ruby @@ -452,7 +458,36 @@ Wrapper for showing an in-app message (SMS) composer view. result.canceled? # => boolean result.failed? # => boolean error # => NSError - } + } +``` + +## NetworkIndicator + +Wrapper for showing and hiding the network indicator (the status bar spinner). + +```ruby + BW::NetworkIndicator.show # starts the spinner + BW::NetworkIndicator.hide # stops it + + # the nice thing is if you call 'show' multiple times, the 'hide' method will + # not have any effect until you've called it the same number of times. + BW::NetworkIndicator.show + # ...somewhere else + BW::NetworkIndicator.show + + # ...down the line + BW::NetworkIndicator.hide + # indicator is still visible + + BW::NetworkIndicator.hide + # NOW the indicator is hidden! + + # If you *really* want to hide the indicator immediately, you can call `reset!` + # but this is in no way encouraged. + BW::NetworkIndicator.reset! + + # and for completeness, a check to see if the indicator is visible + BW::NetworkIndicator.visible? ``` ## UI From dc8eacf9dcd3d833bc718a52df13abb4489a89f8 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Thu, 6 Mar 2014 10:27:42 -0700 Subject: [PATCH 3/9] don't need this block --- lib/bubble-wrap/network-indicator.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/bubble-wrap/network-indicator.rb b/lib/bubble-wrap/network-indicator.rb index a668ae7a..3898229b 100644 --- a/lib/bubble-wrap/network-indicator.rb +++ b/lib/bubble-wrap/network-indicator.rb @@ -2,7 +2,5 @@ BubbleWrap.require_ios("network-indicator") do BubbleWrap.require('motion/core/app.rb') - BubbleWrap.require('motion/network-indicator/**/*.rb') do - # file('motion/network-indicator/network-indicator.rb').depends_on('motion/network-indicator/result.rb') - end + BubbleWrap.require('motion/network-indicator/**/*.rb') end From b09da68afe1e43ab62f81e98afce0f5b59b0e020 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 11:53:27 -0700 Subject: [PATCH 4/9] models the spinner behavior after AFNetworking; hiding the spinner is delayed (avoids flickering), thread support (all updates are on the main thread) --- motion/network-indicator/network-indicator.rb | 39 +++++++++-- .../network_indicator_spec.rb | 67 +++++++++++++++---- 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/motion/network-indicator/network-indicator.rb b/motion/network-indicator/network-indicator.rb index 5db21700..0b26fcd1 100644 --- a/motion/network-indicator/network-indicator.rb +++ b/motion/network-indicator/network-indicator.rb @@ -1,17 +1,44 @@ module BubbleWrap module NetworkIndicator + DELAY = 0.2 module_function def show - self.counter += 1 - UIApplication.sharedApplication.networkActivityIndicatorVisible = true + if Dispatch::Queue.current.to_s == 'com.apple.main-thread' + self.counter += 1 + self.update_spinner + else + Dispatch::Queue.main.async do + self.show + end + end end def hide - self.counter = [self.counter - 1, 0].max - if self.counter == 0 - UIApplication.sharedApplication.networkActivityIndicatorVisible = false + if Dispatch::Queue.current.to_s == 'com.apple.main-thread' + self.counter = [self.counter - 1, 0].max + if self.counter == 0 + if @hide_indicator_timer + @hide_indicator_timer.invalidate + end + @hide_indicator_timer = NSTimer.timerWithTimeInterval(DELAY - 0.01, target: self, selector: :update_spinner, userInfo: nil, repeats: false) + NSRunLoop.mainRunLoop.addTimer(@hide_indicator_timer, forMode:NSRunLoopCommonModes) + end + else + Dispatch::Queue.main.async do + self.hide + end + end + end + + def update_spinner + if Dispatch::Queue.current.to_s == 'com.apple.main-thread' + UIApplication.sharedApplication.networkActivityIndicatorVisible = (@counter > 0) + else + Dispatch::Queue.main.async do + self.update_spinner + end end end @@ -21,7 +48,7 @@ def visible? def reset! @counter = 0 - UIApplication.sharedApplication.networkActivityIndicatorVisible = false + self.update_spinner end def counter diff --git a/spec/motion/network-indicator/network_indicator_spec.rb b/spec/motion/network-indicator/network_indicator_spec.rb index c89cdf02..a331f83a 100644 --- a/spec/motion/network-indicator/network_indicator_spec.rb +++ b/spec/motion/network-indicator/network_indicator_spec.rb @@ -5,25 +5,62 @@ UIApplication.sharedApplication.networkActivityIndicatorVisible = false end - it 'should show the indicator' do + it 'should show the indicator immediately' do BW::NetworkIndicator.show UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true end + it 'should show the indicator from any thread' do + Dispatch::Queue.concurrent.async do + BW::NetworkIndicator.show + end + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + end + end + it 'should hide the indicator' do BW::NetworkIndicator.show BW::NetworkIndicator.hide - UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end end - it 'should keep track of how many times `show` was called' do - BW::NetworkIndicator.show + it 'should hide the indicator after a delay' do BW::NetworkIndicator.show + BW::NetworkIndicator.hide UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end + end + + it 'should not hide the indicator if show/hide/show is called quickly' do + BW::NetworkIndicator.show BW::NetworkIndicator.hide UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + wait BW::NetworkIndicator::DELAY/2 do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + BW::NetworkIndicator.show + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + end + end + end + + it 'should keep track of how many times `show` was called' do + BW::NetworkIndicator.show + BW::NetworkIndicator.show + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true BW::NetworkIndicator.hide - UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + BW::NetworkIndicator.hide + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + end + end end it 'should allow `hide` to be called too many times' do @@ -31,14 +68,18 @@ BW::NetworkIndicator.show BW::NetworkIndicator.hide BW::NetworkIndicator.hide - UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false - BW::NetworkIndicator.hide - BW::NetworkIndicator.hide - UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false + BW::NetworkIndicator.hide + BW::NetworkIndicator.hide + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false - BW::NetworkIndicator.show - UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + BW::NetworkIndicator.show + UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true + end + end end it 'should reset the counter when `reset!` is called' do @@ -52,7 +93,9 @@ BW::NetworkIndicator.show BW::NetworkIndicator.visible?.should == true BW::NetworkIndicator.hide - BW::NetworkIndicator.visible?.should == false + wait BW::NetworkIndicator::DELAY do + BW::NetworkIndicator.visible?.should == false + end end end From 679aecd3848ac2b36f2f4226645bd69fa2f21381 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 11:53:43 -0700 Subject: [PATCH 5/9] include 'network-indicator' as part of 'http' --- lib/bubble-wrap/http.rb | 1 + motion/http/query.rb | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/bubble-wrap/http.rb b/lib/bubble-wrap/http.rb index 9e4aeef6..c46fe278 100644 --- a/lib/bubble-wrap/http.rb +++ b/lib/bubble-wrap/http.rb @@ -1,5 +1,6 @@ require 'bubble-wrap/version' unless defined?(BubbleWrap::VERSION) require 'bubble-wrap/loader' +require 'bubble-wrap/network-indicator' BubbleWrap.require('motion/core/ns_url_request.rb') BubbleWrap.require('motion/core.rb') BubbleWrap.require('motion/http.rb') diff --git a/motion/http/query.rb b/motion/http/query.rb index f9c3233b..27c7ed93 100644 --- a/motion/http/query.rb +++ b/motion/http/query.rb @@ -171,14 +171,10 @@ def did_receive_response(response) def show_status_indicator(show) if App.ios? - if defined?(BW::NetworkIndicator) - if show - BW::NetworkIndicator.show - else - BW::NetworkIndicator.hide - end + if show + BW::NetworkIndicator.show else - UIApplication.sharedApplication.networkActivityIndicatorVisible = show + BW::NetworkIndicator.hide end end end From f8031f4d9e23dcdd6a6f52fa8c10360f0a78d87d Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 12:46:51 -0700 Subject: [PATCH 6/9] only alloy one 'error' --- motion/http/query.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/motion/http/query.rb b/motion/http/query.rb index 27c7ed93..e4513ecc 100644 --- a/motion/http/query.rb +++ b/motion/http/query.rb @@ -114,6 +114,9 @@ def connection(connection, willSendRequest:request, redirectResponse:redirect_re end def connection(connection, didFailWithError: error) + return if @error + + @error = error log "HTTP Connection to #{@url.absoluteString} failed #{error.localizedDescription}" show_status_indicator false @request.done_loading! From 80619bf1e5258149a321b088549fd54fb169e504 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 13:07:19 -0700 Subject: [PATCH 7/9] geez, that took a lot of 'cancel's! --- motion/http/query.rb | 28 ++++-- motion/network-indicator/network-indicator.rb | 26 +++--- spec/motion/http/query_spec.rb | 91 +++++++++++++++++-- .../network_indicator_spec.rb | 11 +++ 4 files changed, 125 insertions(+), 31 deletions(-) diff --git a/motion/http/query.rb b/motion/http/query.rb index e4513ecc..66ce204f 100644 --- a/motion/http/query.rb +++ b/motion/http/query.rb @@ -69,6 +69,10 @@ def to_s end alias description to_s + def done? + @did_fail_error || @did_finish_loading || @canceled + end + def connection(connection, didReceiveResponse:response) # On OSX, if using an FTP connection, this method will fire *immediately* after creating an # NSURLConnection, even if the connection has not yet started. The `response` @@ -99,13 +103,10 @@ def connection(connection, willSendRequest:request, redirectResponse:redirect_re log "##{@redirect_count} HTTP redirect_count: #{request.inspect} - #{self.description}" if @redirect_count >= 30 - @response.error = NSError.errorWithDomain('BubbleWrap::HTTP', code:NSURLErrorHTTPTooManyRedirects, + error = NSError.errorWithDomain('BubbleWrap::HTTP', code:NSURLErrorHTTPTooManyRedirects, userInfo:NSDictionary.dictionaryWithObject("Too many redirections", forKey: NSLocalizedDescriptionKey)) - @response.error_message = @response.error.localizedDescription - show_status_indicator false - @request.done_loading! - call_delegator_with_response + self.connection(connection, didFailWithError: error) nil else @url = request.URL if @follow_urls @@ -114,9 +115,9 @@ def connection(connection, willSendRequest:request, redirectResponse:redirect_re end def connection(connection, didFailWithError: error) - return if @error + return if done? - @error = error + @did_fail_error = error log "HTTP Connection to #{@url.absoluteString} failed #{error.localizedDescription}" show_status_indicator false @request.done_loading! @@ -132,6 +133,9 @@ def connection(connection, didSendBodyData:sending, totalBytesWritten:written, t end def connectionDidFinishLoading(connection) + return if done? + @did_finish_loading = true + show_status_indicator false @request.done_loading! response_body = NSData.dataWithData(@received_data) if @received_data @@ -159,6 +163,9 @@ def connection(connection, didReceiveAuthenticationChallenge:challenge) end def cancel + return if done? + @canceled = true + @connection.cancel show_status_indicator false @request.done_loading! @@ -173,7 +180,8 @@ def did_receive_response(response) end def show_status_indicator(show) - if App.ios? + if App.ios? && @status.nil? || @status != !!show + @status = !!show if show BW::NetworkIndicator.show else @@ -283,8 +291,8 @@ def append_auth_header def parse_file(key, value) value = {data: value} unless value.is_a?(Hash) raise(InvalidFileError, "You need to supply a `:data` entry in #{value} for file '#{key}' in your HTTP `:files`") if value[:data].nil? - { - data: value[:data], + { + data: value[:data], filename: value.fetch(:filename, key), content_type: value.fetch(:content_type, "application/octet-stream") } diff --git a/motion/network-indicator/network-indicator.rb b/motion/network-indicator/network-indicator.rb index 0b26fcd1..2c0b4ebe 100644 --- a/motion/network-indicator/network-indicator.rb +++ b/motion/network-indicator/network-indicator.rb @@ -4,9 +4,13 @@ module NetworkIndicator module_function + def counter + @counter ||= 0 + end + def show if Dispatch::Queue.current.to_s == 'com.apple.main-thread' - self.counter += 1 + @counter = self.counter + 1 self.update_spinner else Dispatch::Queue.main.async do @@ -17,12 +21,12 @@ def show def hide if Dispatch::Queue.current.to_s == 'com.apple.main-thread' - self.counter = [self.counter - 1, 0].max + @counter = [self.counter - 1, 0].max if self.counter == 0 if @hide_indicator_timer @hide_indicator_timer.invalidate end - @hide_indicator_timer = NSTimer.timerWithTimeInterval(DELAY - 0.01, target: self, selector: :update_spinner, userInfo: nil, repeats: false) + @hide_indicator_timer = NSTimer.timerWithTimeInterval(DELAY - 0.01, target: self, selector: :update_spinner_timer, userInfo: nil, repeats: false) NSRunLoop.mainRunLoop.addTimer(@hide_indicator_timer, forMode:NSRunLoopCommonModes) end else @@ -32,8 +36,16 @@ def hide end end + def update_spinner_timer + update_spinner + end + def update_spinner if Dispatch::Queue.current.to_s == 'com.apple.main-thread' + if @hide_indicator_timer + @hide_indicator_timer.invalidate + @hide_indicator_timer = nil + end UIApplication.sharedApplication.networkActivityIndicatorVisible = (@counter > 0) else Dispatch::Queue.main.async do @@ -51,13 +63,5 @@ def reset! self.update_spinner end - def counter - @counter ||= 0 - end - - def counter=(value) - @counter = value - end - end end diff --git a/spec/motion/http/query_spec.rb b/spec/motion/http/query_spec.rb index 543a763f..3933c2ca 100644 --- a/spec/motion/http/query_spec.rb +++ b/spec/motion/http/query_spec.rb @@ -11,6 +11,10 @@ @json_query = BubbleWrap::HTTP::Query.new( "http://localhost:3000" , :post, @json_options ) end + after do + @json_query.cancel + end + it "should generate json body" do BW::JSON.parse(@json_query.request.HTTPBody).should == @json_payload end @@ -21,6 +25,10 @@ @query = BubbleWrap::HTTP::Query.new("http://www.google.com", :get, {payload: {following: false}}) end + after do + @query.cancel + end + it "should have right url" do @query.request.URL.absoluteString.should == "http://www.google.com?following=false" end @@ -59,9 +67,14 @@ leftover_option: @leftover_option, format: @format } + @query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options ) end + after do + @query.cancel + end + it "has appropriate attributes" do @query.should.respond_to :request= @query.should.respond_to :connection= @@ -84,6 +97,7 @@ } query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options ) query.should.not.be.nil + query.cancel end describe "When initialized" do @@ -95,7 +109,8 @@ it "throws an error for invalid/missing URL schemes" do %w(http https file ftp).each do |scheme| lambda { - BW::HTTP::Query.new("#{scheme}://example.com", :get) { |r| p r.body.to_str } + q = BW::HTTP::Query.new("#{scheme}://example.com", :get) { |r| p r.body.to_str } + q.cancel }.should.not.raise InvalidURLError end @@ -122,6 +137,7 @@ it "should set self as the delegator if action was not passed in" do new_query = BubbleWrap::HTTP::Query.new( 'http://localhost', :get, {}) new_query.instance_variable_get(:@delegator).should.equal new_query + new_query.cancel end it "should merge :username and :password in loaded credentials" do @@ -129,6 +145,7 @@ options = { credentials: {} } new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, options) + new_query.cancel generated_credentials = { :username => nil, :password => nil } new_query.credentials.should.equal generated_credentials @@ -155,24 +172,29 @@ def sample_data it "should check if @payload is a hash before generating GET params" do query_string_payload = BubbleWrap::HTTP::Query.new( @fake_url , :get, { payload: "name=apple&model=macbook"} ) query_string_payload.instance_variable_get(:@payload).should.equal 'name=apple&model=macbook' + query_string_payload.cancel end it "should check if payload is nil" do + q = nil lambda{ - BubbleWrap::HTTP::Query.new( @fake_url , :post, {} ) + q = BubbleWrap::HTTP::Query.new( @fake_url , :post, {} ) }.should.not.raise NoMethodError + q.cancel end it "should set the payload in URL only for GET/HEAD/OPTIONS requests" do [:post, :put, :delete, :patch].each do |method| query = BubbleWrap::HTTP::Query.new( @localhost_url , method, { payload: @payload } ) query.instance_variable_get(:@url).description.should.equal @localhost_url + query.cancel end payload = {name: 'marin'} [:get, :head, :options].each do |method| query = BubbleWrap::HTTP::Query.new( @localhost_url , method, { payload: payload } ) query.instance_variable_get(:@url).description.should.equal "#{@localhost_url}?name=marin" + query.cancel end end @@ -184,6 +206,7 @@ def sample_data uuid = query.instance_variable_get(:@boundary) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) real_payload.should.equal "--#{uuid}\r\nContent-Disposition: attachment; name=\"upload\"; filename=\"test.txt\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}--\r\n" + query.cancel end it "processes filenames from file hashes, using the name when the filename is missing" do @@ -194,6 +217,7 @@ def sample_data uuid = query.instance_variable_get(:@boundary) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) real_payload.should.equal "--#{uuid}\r\nContent-Disposition: attachment; name=\"upload\"; filename=\"upload\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}--\r\n" + query.cancel end it "throws an error for invalid file parameters" do @@ -215,6 +239,7 @@ def sample_data uuid = query.instance_variable_get(:@boundary) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\napple\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nmacbook\r\n--#{uuid}\r\nContent-Disposition: attachment; name=\"twitter\"; filename=\"twitter\"\r\nContent-Type: application/octet-stream\r\n\r\ntwitter:@mneorr\r\n--#{uuid}\r\nContent-Disposition: attachment; name=\"site\"; filename=\"site\"\r\nContent-Type: application/octet-stream\r\n\r\nmneorr.com\r\n--#{uuid}--\r\n" + query.cancel end [:get, :head, :options].each do |method| @@ -222,12 +247,16 @@ def sample_data query = BubbleWrap::HTTP::Query.new( @fake_url , method, { payload: payload } ) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) real_payload.should.be.empty + query.cancel end end it "sets the payload without conversion to-from NSString if the payload was NSData" do data = sample_data - lambda { create_query(data, nil) }.should.not.raise NoMethodError + lambda { + q = create_query(data, nil) + q.cancel + }.should.not.raise NoMethodError end it "sets the payload as a string if JSON" do @@ -237,6 +266,7 @@ def sample_data query = BubbleWrap::HTTP::Query.new( @fake_url , method, { payload: json } ) set_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) set_payload.should.equal json + query.cancel end end @@ -246,6 +276,7 @@ def sample_data uuid = query.instance_variable_get(:@boundary) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:NSUTF8StringEncoding) real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[name]\"\r\n\r\napple\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[model]\"\r\n\r\nmacbook\r\n--#{uuid}--\r\n" + query.cancel end [["NSUTF8StringEncoding", NSUTF8StringEncoding], @@ -258,6 +289,7 @@ def sample_data uuid = query.instance_variable_get(:@boundary) real_payload = NSString.alloc.initWithData(query.request.HTTPBody, encoding:encoding) real_payload.should.equal "--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[name]\"\r\n\r\n#{payload[:computer][:name]}\r\n--#{uuid}\r\nContent-Disposition: form-data; name=\"computer[model]\"\r\n\r\n#{payload[:computer][:model]}\r\n--#{uuid}--\r\n" + query.cancel end end @@ -271,6 +303,7 @@ def sample_data new_query.instance_variable_get(:@timeout).should == 10 options.should.be.empty + new_query.cancel end it "should delete :headers from options and escape Line Feeds" do @@ -286,6 +319,7 @@ def sample_data new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, {}) new_query.instance_variable_get(:@cache_policy).should.equal NSURLRequestUseProtocolCachePolicy + new_query.cancel end it "should delete :credential_persistence or set NSURLCredentialPersistenceForSession" do @@ -294,6 +328,7 @@ def sample_data new_query = BubbleWrap::HTTP::Query.new( @localhost_url, :get, {}) new_query.instance_variable_get(:@credential_persistence).should.equal NSURLCredentialPersistenceForSession + new_query.cancel end it "should present base64-encoded credentials in Authorization header when provided" do @@ -307,6 +342,7 @@ def sample_data headers = query.instance_variable_get(:@headers) headers.should.equal nil + query.cancel end @@ -347,6 +383,11 @@ def sample_data @get_query = BubbleWrap::HTTP::Query.new( @url_string , :get, { headers: @headers } ) end + after do + @query.cancel + @get_query.cancel + end + it "should create a new request with HTTP method & header fields" do @query.request.HTTPMethod.should.equal @query.method @get_query.request.allHTTPHeaderFields.should.equal @headers @@ -381,9 +422,14 @@ def sample_data @post_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: @payload}) end + after do + @post_query.cancel + end + it "should add default Content Type if no payload is given" do query_without_payload = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers}) query_without_payload.request.allHTTPHeaderFields.should.include? 'Content-Type' + query_without_payload.cancel end it "should automatically provide Content-Type if a payload is provided" do @@ -393,6 +439,7 @@ def sample_data it "should use the format parameter to decide the Content-Type" do json_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, format: :json, payload: "{\"key\":\"abc1234\"}"}) json_query.request.allHTTPHeaderFields['Content-Type'].should.equal "application/json" + json_query.cancel end it "should default to multipart/form-data for payloads with a hash" do @@ -403,13 +450,15 @@ def sample_data it "should default to application/x-www-form-urlencoded for non-hash payloads" do string_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: "{\"key\":\"abc1234\"}"}) string_query.request.allHTTPHeaderFields['Content-Type'].should.equal "application/x-www-form-urlencoded" + string_query.cancel end it "should not add Content-Type if you provide one yourself" do # also ensures check is case insenstive @headers = { fake: 'headers', 'CONTENT-TYPE' => 'x-banana' } - @post_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: @payload}) - @post_query.request.allHTTPHeaderFields['CONTENT-TYPE'].should.equal @headers['CONTENT-TYPE'] + post_query = BubbleWrap::HTTP::Query.new(@url_string, :post, {headers: @headers, payload: @payload}) + post_query.request.allHTTPHeaderFields['CONTENT-TYPE'].should.equal @headers['CONTENT-TYPE'] + post_query.cancel end end @@ -500,7 +549,9 @@ def query_received_data it "should turn off network indicator" do UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == true @query.connection(nil, didFailWithError:@fake_error) - UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false + end end end @@ -540,10 +591,10 @@ def query_received_data if App.ios? it "should turn off the network indicator" do - UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == true - @query.connectionDidFinishLoading(nil) - UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should == false + end end end @@ -681,6 +732,7 @@ def query_received_data query = BubbleWrap::HTTP::Query.new( @localhost_url , :get, @options ) query.connection(nil, didReceiveAuthenticationChallenge:@challenge) @challenge.sender.continue_without_credential.should.equal true + query.cancel end end @@ -697,7 +749,9 @@ def query_received_data if App.ios? it "should turn off the network indicator" do - UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should.equal false + wait BW::NetworkIndicator::DELAY do + UIApplication.sharedApplication.isNetworkActivityIndicatorVisible.should.equal false + end end end end @@ -710,6 +764,10 @@ def query_received_data @get_query = BubbleWrap::HTTP::Query.new(@url_string, :get, :payload => @payload) end + after do + @get_query.cancel + end + it "should not append a ? to the end of the URL" do @get_query.instance_variable_get(:@url).description.should.equal('http://fake.url/method') end @@ -725,6 +783,10 @@ def query_received_data @escaped_url = "http://fake.url/method?we%20love=%23%3D%3DRock%26Roll%3D%3D%23&radio=Ga%20Ga&qual=3.0&incr=-1&RFC3986=%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D" end + after do + @get_query.cancel + end + it "should escape !*'();:@&=+$,/?%#[] characters only in keys and values" do @get_query.instance_variable_get(:@url).description.should.equal @escaped_url end @@ -738,6 +800,11 @@ def query_received_data @cookie_query = BubbleWrap::HTTP::Query.new("http://haz-cookiez.url", :get, :payload => {:something => "else"}) end + after do + @no_cookie_query.cancel + @cookie_query.cancel + end + it 'should disabled cookie-usage on nsurlrequest' do @no_cookie_query.instance_variable_get(:@request).HTTPShouldHandleCookies.should.equal false end @@ -746,7 +813,11 @@ def query_received_data @cookie_query.instance_variable_get(:@request).HTTPShouldHandleCookies.should.equal true end + end + after do + sleep(BW::NetworkIndicator::DELAY) if BW::NetworkIndicator.counter > 0 + raise "I think you forgot to 'cancel' a query (in order for BW::NetworkIndicator to be tested properly, all queries must be canceled)" if BW::NetworkIndicator.counter > 0 end class FakeSender diff --git a/spec/motion/network-indicator/network_indicator_spec.rb b/spec/motion/network-indicator/network_indicator_spec.rb index a331f83a..15c7ce5b 100644 --- a/spec/motion/network-indicator/network_indicator_spec.rb +++ b/spec/motion/network-indicator/network_indicator_spec.rb @@ -1,5 +1,9 @@ describe BW::NetworkIndicator do + before do + BW::NetworkIndicator.reset! + end + after do BW::NetworkIndicator.instance_variable_set(:@counter, 0) UIApplication.sharedApplication.networkActivityIndicatorVisible = false @@ -10,6 +14,13 @@ UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true end + it 'should have a counter' do + BW::NetworkIndicator.show + BW::NetworkIndicator.counter.should == 1 + BW::NetworkIndicator.hide + BW::NetworkIndicator.counter.should == 0 + end + it 'should show the indicator from any thread' do Dispatch::Queue.concurrent.async do BW::NetworkIndicator.show From 763226addb15a46d6af49f0faac0d25f3f660376 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 13:09:17 -0700 Subject: [PATCH 8/9] don't need this to be outside lambda --- spec/motion/http/query_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/motion/http/query_spec.rb b/spec/motion/http/query_spec.rb index 3933c2ca..3a4ca3a7 100644 --- a/spec/motion/http/query_spec.rb +++ b/spec/motion/http/query_spec.rb @@ -176,11 +176,10 @@ def sample_data end it "should check if payload is nil" do - q = nil lambda{ q = BubbleWrap::HTTP::Query.new( @fake_url , :post, {} ) + q.cancel }.should.not.raise NoMethodError - q.cancel end it "should set the payload in URL only for GET/HEAD/OPTIONS requests" do From a53a35cfcc5a96ba7dd974dd2356bb562659af18 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Fri, 7 Mar 2014 15:47:54 -0700 Subject: [PATCH 9/9] fixes for osx --- motion/http/query.rb | 2 +- spec/motion/http/query_spec.rb | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/motion/http/query.rb b/motion/http/query.rb index 66ce204f..8bb4afb3 100644 --- a/motion/http/query.rb +++ b/motion/http/query.rb @@ -180,7 +180,7 @@ def did_receive_response(response) end def show_status_indicator(show) - if App.ios? && @status.nil? || @status != !!show + if App.ios? && (@status.nil? || @status != !!show) @status = !!show if show BW::NetworkIndicator.show diff --git a/spec/motion/http/query_spec.rb b/spec/motion/http/query_spec.rb index 3a4ca3a7..ab321058 100644 --- a/spec/motion/http/query_spec.rb +++ b/spec/motion/http/query_spec.rb @@ -815,8 +815,10 @@ def query_received_data end after do - sleep(BW::NetworkIndicator::DELAY) if BW::NetworkIndicator.counter > 0 - raise "I think you forgot to 'cancel' a query (in order for BW::NetworkIndicator to be tested properly, all queries must be canceled)" if BW::NetworkIndicator.counter > 0 + if App.ios? + sleep(BW::NetworkIndicator::DELAY) if BW::NetworkIndicator.counter > 0 + raise "I think you forgot to 'cancel' a query (in order for BW::NetworkIndicator to be tested properly, all queries must be canceled)" if BW::NetworkIndicator.counter > 0 + end end class FakeSender