[hotcocoa] http wrapper, what do you think?
In my free time, I've been working on a http://lighthouseapp.com client app which mixes API calls with webkit views (I'll release the source code later on). The app is 100% HotCocoa (meaning that I didn't use any nib/xibs). For people who are not used to work with delegation, having to deal with file download, URL queries can be a bit challenging if you want to start using the underlying obj-c methods. That's why I wrote a small HTTP wrapper to clean up my code and make download/queries easier. The library is divided in 2 major parts, a helper and the Query/Response classes. Here is how the helper looks like so far: Download helper making file download trivial Pass an url, and a block or a delegator A block takes precedence over a delegator, meaning that you can't pass a delegator and use a block, you have to choose which approach to pick. ==== Parameters url<String>:: url of the resource to download options<Hash>:: optional options used for the query &block<block>:: block which is called when the file is downloaded ==== Options :method<String, Symbol>:: An optional value which represents the HTTP method to use :payload<String>:: - data to pass to a POST, PUT, DELETE query. :delegation<Object>:: - class or instance to call when the file is downloaded. the handle_query_response method will be called on the passed object :save_to<String>:: - path to save the response to :credential<Hash>:: - should contains the :user key and :password key By default the credential will be saved for the entire session TODO :progress <Proc>:: Proc to call everytime the downloader makes some progress :cache_storage :custom headers Error handling ==== Examples download("http://www.macruby.org/files/MacRuby%200.4.zip", {:save_to => '~/tmp/macruby.zip'.stringByStandardizingPath}) do |macruby| NSLog("file downloaded!") end download "http://macruby.org" do |mr| # The response object has 3 accessors: status_code, headers and body NSLog("status: #{mr.status_code}, Headers: #{mr.headers.inspect}") end path = File.expand_path('~/macruby_tmp.html') window :frame => [100, 100, 500, 500], :title => "HotCocoa" do |win| download("http://macruby.org", {:save_to => path}) do |homepage| win << label(:text => "status code: #{homepage.status_code}", :layout => {:start => false}) win << label(:text => "Headers: #{homepage.headers.inspect}", :layout => {:start => false}) end end download "http://macruby.org/users/matt", :delegation => @downloaded_file, :method => 'PUT', :payload => {:name => 'matt aimonetti'} download "http://macruby.org/roadmap.xml", :delegation => self download "http://localhost:5984/couchrest-test/", :method => 'POST', :payload => '{"user":"mattaimonetti@gmail.com","zip":92129}', :delegation => self download("http://mysite.com/login", {:credential => {:user => 'me', :password => 's3krit'}}) do |test| NSLog("response received: #{test.headers} #{test.status_code}") end As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back. The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done. So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available? - Matt
Hi Matt, On Mar 30, 2009, at 11:08 AM, Matt Aimonetti wrote:
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
Good stuff. I think it is worth iterating on as a separate lib for now, and integrating it with HotCocoa later. For comparison, you may also want to look at how "Shoes" handles downloads (below). -- Ernie P. http://help.shoooes.net/App.html download(url: a string, styles) Starts a download thread (much like XMLHttpRequest, if you're familiar with JavaScript.) This method returns immediately and runs the download in the background. Each download thread also fires start, progress and finish events. You can send the download to a file or just get back a string (in the finish event.) If you attach a block to a download, it'll get called as the finish event. Shoes.app do stack do title "Searching Google", :size => 16 @status = para "One moment..." # Search Google for 'shoes' and print the HTTP headers download "http://www.google.com/search?q=shoes" do |goog| @status.text = "Headers: " + goog.response.headers.inspect end end end And, if we wanted to use the downloaded data, we'd get it using goog.response.body. This example is truly the simplest form of download: pulling some web data down into memory and handling it once it's done. Another simple use of download is to save some web data to a file, using the :save style. Shoes.app do stack do title "Downloading Google image", :size => 16 @status = para "One moment..." download "http://www.google.com/logos/nasa50th.gif", :save => "nasa50th.gif" do @status.text = "Okay, is downloaded." end end end In this case, you can still get the headers for the downloaded file, but response.body will be nil, since the data wasn't saved to memory. You will need to open the file to get the downloaded goods. If you need to send certain headers or actions to the web server, you can use the :method, :headers and :body styles to customize the HTTP request. (And, if you need to go beyond these, you can always break out Ruby's OpenURI class.) Shoes.app do stack do title "POSTing to Google", :size => 16 @status = para "One moment..." download "http://www.stevex.net/dump.php", :method => "POST", :body => "v=1.0&q=shoes" do |dump| require 'hpricot' @status.text = Hpricot(dump.response.body).inner_text end end end As you can see from the above example, Shoes includes the Hpricot library for parsing HTML.
Ernie, Pretty cool, my library does(will do) what Shoes does but with more flexibility (you can assign any class/objects as a delegates and it supports more options (including using your keychain for auth)). Did/do you use Shoes, if so, I'd be interested in knowing what you think we are missing or what we should look at. Thanks, - Matt 2009/3/30 Ernest N. Prabhakar, Ph.D. <prabhaka@apple.com>
Hi Matt, On Mar 30, 2009, at 11:08 AM, Matt Aimonetti wrote:
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
Good stuff. I think it is worth iterating on as a separate lib for now, and integrating it with HotCocoa later. For comparison, you may also want to look at how "Shoes" handles downloads (below).
-- Ernie P.
http://help.shoooes.net/App.html
*download(url: a string, styles)*<http://help.shoooes.net/App.html#download>
Starts a download thread (much like XMLHttpRequest, if you're familiar with JavaScript.) This method returns immediately and runs the download in the background. Each download thread also fires start, progress and finishevents. You can send the download to a file or just get back a string (in the finish event.)
If you attach a block to a download, it'll get called as the finish event. *Shoes*.app do stack do title "Searching Google", :size => 16 @status = para "One moment..." # Search Google for 'shoes' and print the HTTP headers download "http://www.google.com/search?q=shoes" do |goog| @status.text = "Headers: " + goog.response.headers.inspect end end end
And, if we wanted to use the downloaded data, we'd get it using goog.response.body. This example is truly the simplest form of download: pulling some web data down into memory and handling it once it's done.
Another simple use of download is to save some web data to a file, using the :save style. *Shoes*.app do stack do title "Downloading Google image", :size => 16 @status = para "One moment..." download "http://www.google.com/logos/nasa50th.gif", :save => "nasa50th.gif" do @status.text = "Okay, is downloaded." end end end
In this case, you can still get the headers for the downloaded file, but response.body will be nil, since the data wasn't saved to memory. You will need to open the file to get the downloaded goods.
If you need to send certain headers or actions to the web server, you can use the :method, :headers and :body styles to customize the HTTP request. (And, if you need to go beyond these, you can always break out Ruby's OpenURI class.) *Shoes*.app do stack do title "POSTing to Google", :size => 16 @status = para "One moment..." download "http://www.stevex.net/dump.php", :method => "POST", :body => "v=1.0&q=shoes" do |dump| require 'hpricot' @status.text = *Hpricot*(dump.response.body).inner_text end end end
As you can see from the above example, Shoes includes the Hpricot library for parsing HTML.
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
Hi Matt, On Mar 30, 2009, at 2:37 PM, Matt Aimonetti wrote:
Pretty cool, my library does(will do) what Shoes does but with more flexibility (you can assign any class/objects as a delegates and it supports more options (including using your keychain for auth)). Did/do you use Shoes, if so, I'd be interested in knowing what you think we are missing or what we should look at.
The one thing I didn't notice in your API was having explicit mappings for "start" and "finish", (though I see "progress" on your TODO). -- Ernie P.
Oh right, I don't have a mapping for "start" but that's pretty easy to add. The "finish" mapping is the optional block you pass to the helper method. Once the request is processed, the block is being called. I'm wondering if these callbacks, shouldn't be defined using accessors instead. passing too many options via a hash is kinda nasty, don't you think? The problem with adding accessors to the helper is that now I need to use a kicker method and we end up with almost the same API as the default obj-c api :p - Matt On Mon, Mar 30, 2009 at 3:06 PM, Ernest N. Prabhakar, Ph.D. < prabhaka@apple.com> wrote:
Hi Matt,
On Mar 30, 2009, at 2:37 PM, Matt Aimonetti wrote:
Pretty cool, my library does(will do) what Shoes does but with more flexibility (you can assign any class/objects as a delegates and it supports more options (including using your keychain for auth)). Did/do you use Shoes, if so, I'd be interested in knowing what you think we are missing or what we should look at.
The one thing I didn't notice in your API was having explicit mappings for "start" and "finish", (though I see "progress" on your TODO).
-- Ernie P.
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
Seems like a good idea that would be useful even outside of HotCocoa. Especially for simple cases, I think if Obj-C had blocks, this would be a good place to use them. For more control, the delegate pattern actually works really well. It's good that you have a way to do both. Does the delegate object also receive messages that would be sent to an NSURLDownload? That might be a nice way to allow easy integration in cases where an object that responds to the delegate methods is defined in Obj-C. Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue. Since your #download method already wraps the underlying process, you could should able to generate NSOperations to perform the downloads and add them to a queue. Those classes handle creating threads (which have their own runloops) and performing a task on the thread. That said, maybe this approach would not work with the current IO implementation? Brian 2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
In my free time, I've been working on a http://lighthouseapp.com client app which mixes API calls with webkit views (I'll release the source code later on). The app is 100% HotCocoa (meaning that I didn't use any nib/xibs). For people who are not used to work with delegation, having to deal with file download, URL queries can be a bit challenging if you want to start using the underlying obj-c methods.
That's why I wrote a small HTTP wrapper to clean up my code and make download/queries easier. The library is divided in 2 major parts, a helper and the Query/Response classes.
Here is how the helper looks like so far:
Download helper making file download trivial Pass an url, and a block or a delegator A block takes precedence over a delegator, meaning that you can't pass a delegator and use a block, you have to choose which approach to pick.
==== Parameters url<String>:: url of the resource to download options<Hash>:: optional options used for the query &block<block>:: block which is called when the file is downloaded
==== Options :method<String, Symbol>:: An optional value which represents the HTTP method to use :payload<String>:: - data to pass to a POST, PUT, DELETE query. :delegation<Object>:: - class or instance to call when the file is downloaded. the handle_query_response method will be called on the passed object :save_to<String>:: - path to save the response to :credential<Hash>:: - should contains the :user key and :password key By default the credential will be saved for the entire session
TODO :progress <Proc>:: Proc to call everytime the downloader makes some progress :cache_storage :custom headers Error handling
==== Examples
download("http://www.macruby.org/files/MacRuby%200.4.zip", {:save_to => '~/tmp/macruby.zip'.stringByStandardizingPath}) do |macruby| NSLog("file downloaded!") end
download "http://macruby.org" do |mr| # The response object has 3 accessors: status_code, headers and body NSLog("status: #{mr.status_code}, Headers: #{mr.headers.inspect}") end
path = File.expand_path('~/macruby_tmp.html') window :frame => [100, 100, 500, 500], :title => "HotCocoa" do |win| download("http://macruby.org", {:save_to => path}) do |homepage| win << label(:text => "status code: #{homepage.status_code}", :layout => {:start => false}) win << label(:text => "Headers: #{homepage.headers.inspect}", :layout => {:start => false}) end end
download "http://macruby.org/users/matt", :delegation => @downloaded_file, :method => 'PUT', :payload => {:name => 'matt aimonetti'}
download "http://macruby.org/roadmap.xml", :delegation => self
download "http://localhost:5984/couchrest-test/", :method => 'POST', :payload => '{"user":"mattaimonetti@gmail.com","zip":92129}', :delegation => self
download("http://mysite.com/login", {:credential => {:user => 'me', :password => 's3krit'}}) do |test| NSLog("response received: #{test.headers} #{test.status_code}") end
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
- Matt
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
Does the delegate object also receive messages that would be sent to an NSURLDownload?
I'm actually using NSURLConnection and not NSURLDownload which seemed a bit too simple when I looked at it. If the delegator is a proc, then it currently only gets called once the request finished properly. (I still need to write the error handling etc...) If your delegator is an object or a class, all messages should be passed directly. Also, regarding multi-threading, couldn't you just use NSURLDownload (which
you're probably already using underneath) + NSOperation/NSOperationQueue.
I need to look into NSOperation/NSOperationQueue to see how it works, I'm still pretty new to Cocoa and I don't know all the tricks cool kids use ;) I'll do what Ernie and put what I have so far on GitHub and let people hack on it until we have something nice that could be used in most use cases. Since I'm going on vacation, I'll add you (Brian) to my repo so you can deal with pull requests and push your own stuff there ;) Reharding using the class outside of HotCocoa, I agree, but you should be able to only require the file and include the module. What do you think? - Matt On Mon, Mar 30, 2009 at 2:46 PM, Brian Chapados <chapbr@gmail.com> wrote:
Seems like a good idea that would be useful even outside of HotCocoa. Especially for simple cases, I think if Obj-C had blocks, this would be a good place to use them. For more control, the delegate pattern actually works really well. It's good that you have a way to do both. Does the delegate object also receive messages that would be sent to an NSURLDownload? That might be a nice way to allow easy integration in cases where an object that responds to the delegate methods is defined in Obj-C.
Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue. Since your #download method already wraps the underlying process, you could should able to generate NSOperations to perform the downloads and add them to a queue. Those classes handle creating threads (which have their own runloops) and performing a task on the thread. That said, maybe this approach would not work with the current IO implementation?
Brian
2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
In my free time, I've been working on a http://lighthouseapp.com client app which mixes API calls with webkit views (I'll release the source code later on). The app is 100% HotCocoa (meaning that I didn't use any nib/xibs). For people who are not used to work with delegation, having to deal with file download, URL queries can be a bit challenging if you want to start using the underlying obj-c methods.
That's why I wrote a small HTTP wrapper to clean up my code and make download/queries easier. The library is divided in 2 major parts, a helper and the Query/Response classes.
Here is how the helper looks like so far:
Download helper making file download trivial Pass an url, and a block or a delegator A block takes precedence over a delegator, meaning that you can't pass a delegator and use a block, you have to choose which approach to pick.
==== Parameters url<String>:: url of the resource to download options<Hash>:: optional options used for the query &block<block>:: block which is called when the file is downloaded
==== Options :method<String, Symbol>:: An optional value which represents the HTTP method to use :payload<String>:: - data to pass to a POST, PUT, DELETE query. :delegation<Object>:: - class or instance to call when the file is downloaded. the handle_query_response method will be called on the passed object :save_to<String>:: - path to save the response to :credential<Hash>:: - should contains the :user key and :password key By default the credential will be saved for the entire session
TODO :progress <Proc>:: Proc to call everytime the downloader makes some progress :cache_storage :custom headers Error handling
==== Examples
download("http://www.macruby.org/files/MacRuby%200.4.zip", {:save_to => '~/tmp/macruby.zip'.stringByStandardizingPath}) do |macruby| NSLog("file downloaded!") end
download "http://macruby.org" do |mr| # The response object has 3 accessors: status_code, headers and body NSLog("status: #{mr.status_code}, Headers: #{mr.headers.inspect}") end
path = File.expand_path('~/macruby_tmp.html') window :frame => [100, 100, 500, 500], :title => "HotCocoa" do |win| download("http://macruby.org", {:save_to => path}) do |homepage| win << label(:text => "status code: #{homepage.status_code}", :layout => {:start => false}) win << label(:text => "Headers: #{homepage.headers.inspect}", :layout => {:start => false}) end end
download "http://macruby.org/users/matt", :delegation => @downloaded_file, :method => 'PUT', :payload => {:name => 'matt aimonetti'}
download "http://macruby.org/roadmap.xml", :delegation => self
download "http://localhost:5984/couchrest-test/", :method => 'POST', :payload => '{"user":"mattaimonetti@gmail.com","zip":92129}', :delegation => self
download("http://mysite.com/login", {:credential => {:user => 'me', :password => 's3krit'}}) do |test| NSLog("response received: #{test.headers} #{test.status_code}") end
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
- Matt
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
Does the delegate object also receive messages that would be sent to an NSURLDownload?
I'm actually using NSURLConnection and not NSURLDownload which seemed a bit too simple when I looked at it. If the delegator is a proc, then it currently only gets called once the request finished properly. (I still need to write the error handling etc...) If your delegator is an object or a class, all messages should be passed directly.
Right... NSURLConnection is more flexible.
Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue.
I need to look into NSOperation/NSOperationQueue to see how it works, I'm still pretty new to Cocoa and I don't know all the tricks cool kids use ;)
The libraries are vast, and the NSOperation stuff is new in 10.5, so there aren't as many examples floating around the net. Just throwing it out there as a possibility. I'm not sure how hard it would be to implement. I've only used NSOperation for really simple stuff.
I'll do what Ernie and put what I have so far on GitHub and let people hack on it until we have something nice that could be used in most use cases. Since I'm going on vacation, I'll add you (Brian) to my repo so you can deal with pull requests and push your own stuff there ;)
So, you are assigning me as the delegate? ;-)
Reharding using the class outside of HotCocoa, I agree, but you should be able to only require the file and include the module. What do you think?
Yes, that makes sense. It sounds like you've got a good start. I agree with your goal of just making the standard use cases easy. It is nice to have a block right next to the download call, as it is easy to understand the logic. If something requires more complicated handling, then delegate option is always available. Anyway, I'll see if I can make any progress on NSOperation and error-handling. Brian
On Mon, Mar 30, 2009 at 2:46 PM, Brian Chapados <chapbr@gmail.com> wrote:
Seems like a good idea that would be useful even outside of HotCocoa. Especially for simple cases, I think if Obj-C had blocks, this would be a good place to use them. For more control, the delegate pattern actually works really well. It's good that you have a way to do both. Does the delegate object also receive messages that would be sent to an NSURLDownload? That might be a nice way to allow easy integration in cases where an object that responds to the delegate methods is defined in Obj-C.
Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue. Since your #download method already wraps the underlying process, you could should able to generate NSOperations to perform the downloads and add them to a queue. Those classes handle creating threads (which have their own runloops) and performing a task on the thread. That said, maybe this approach would not work with the current IO implementation?
Brian
2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
In my free time, I've been working on a http://lighthouseapp.com client app which mixes API calls with webkit views (I'll release the source code later on). The app is 100% HotCocoa (meaning that I didn't use any nib/xibs). For people who are not used to work with delegation, having to deal with file download, URL queries can be a bit challenging if you want to start using the underlying obj-c methods.
That's why I wrote a small HTTP wrapper to clean up my code and make download/queries easier. The library is divided in 2 major parts, a helper and the Query/Response classes.
Here is how the helper looks like so far:
Download helper making file download trivial Pass an url, and a block or a delegator A block takes precedence over a delegator, meaning that you can't pass a delegator and use a block, you have to choose which approach to pick.
==== Parameters url<String>:: url of the resource to download options<Hash>:: optional options used for the query &block<block>:: block which is called when the file is downloaded
==== Options :method<String, Symbol>:: An optional value which represents the HTTP method to use :payload<String>:: - data to pass to a POST, PUT, DELETE query. :delegation<Object>:: - class or instance to call when the file is downloaded. the handle_query_response method will be called on the passed object :save_to<String>:: - path to save the response to :credential<Hash>:: - should contains the :user key and :password key By default the credential will be saved for the entire session
TODO :progress <Proc>:: Proc to call everytime the downloader makes some progress :cache_storage :custom headers Error handling
==== Examples
download("http://www.macruby.org/files/MacRuby%200.4.zip", {:save_to => '~/tmp/macruby.zip'.stringByStandardizingPath}) do |macruby| NSLog("file downloaded!") end
download "http://macruby.org" do |mr| # The response object has 3 accessors: status_code, headers and body NSLog("status: #{mr.status_code}, Headers: #{mr.headers.inspect}") end
path = File.expand_path('~/macruby_tmp.html') window :frame => [100, 100, 500, 500], :title => "HotCocoa" do |win| download("http://macruby.org", {:save_to => path}) do |homepage| win << label(:text => "status code: #{homepage.status_code}", :layout => {:start => false}) win << label(:text => "Headers: #{homepage.headers.inspect}", :layout => {:start => false}) end end
download "http://macruby.org/users/matt", :delegation => @downloaded_file, :method => 'PUT', :payload => {:name => 'matt aimonetti'}
download "http://macruby.org/roadmap.xml", :delegation => self
download "http://localhost:5984/couchrest-test/", :method => 'POST', :payload => '{"user":"mattaimonetti@gmail.com","zip":92129}', :delegation => self
download("http://mysite.com/login", {:credential => {:user => 'me', :password => 's3krit'}}) do |test| NSLog("response received: #{test.headers} #{test.status_code}") end
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
- Matt
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
Here is the file: http://github.com/mattetti/macruby-httpwrapper/tree/master I noticed I gave you some wrong info, currently the delegator doesn't receive all delegated messages. Instead it gets called with a custom method and the response gets passed to it. This currently only happens when the download is successful http://github.com/mattetti/macruby-httpwrapper/blob/93230af840d1cdc5124f3639... That's something we probably should fix. I tried to make the delegation process easier than what obj-c does but maybe we need to support a more advanced mode. Anyways, I added Brian to the repo and I hope to a see a fully finished version when I come back from vacation ;) - Matt p.s: adding tests would be totally awesome, I'm just not sure how to address that yet. On Mon, Mar 30, 2009 at 4:36 PM, Brian Chapados <chapbr@gmail.com> wrote:
2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
Does the delegate object also receive messages that would be sent to an NSURLDownload?
I'm actually using NSURLConnection and not NSURLDownload which seemed a bit too simple when I looked at it. If the delegator is a proc, then it currently only gets called once the request finished properly. (I still need to write the error handling etc...) If your delegator is an object or a class, all messages should be passed directly.
Right... NSURLConnection is more flexible.
Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue.
I need to look into NSOperation/NSOperationQueue to see how it works, I'm still pretty new to Cocoa and I don't know all the tricks cool kids use ;)
The libraries are vast, and the NSOperation stuff is new in 10.5, so there aren't as many examples floating around the net. Just throwing it out there as a possibility. I'm not sure how hard it would be to implement. I've only used NSOperation for really simple stuff.
I'll do what Ernie and put what I have so far on GitHub and let people hack on it until we have something nice that could be used in most use cases. Since I'm going on vacation, I'll add you (Brian) to my repo so you can deal with pull requests and push your own stuff there ;)
So, you are assigning me as the delegate? ;-)
Reharding using the class outside of HotCocoa, I agree, but you should be able to only require the file and include the module. What do you think?
Yes, that makes sense. It sounds like you've got a good start. I agree with your goal of just making the standard use cases easy. It is nice to have a block right next to the download call, as it is easy to understand the logic. If something requires more complicated handling, then delegate option is always available. Anyway, I'll see if I can make any progress on NSOperation and error-handling.
Brian
On Mon, Mar 30, 2009 at 2:46 PM, Brian Chapados <chapbr@gmail.com>
wrote:
Seems like a good idea that would be useful even outside of HotCocoa. Especially for simple cases, I think if Obj-C had blocks, this would be a good place to use them. For more control, the delegate pattern actually works really well. It's good that you have a way to do both. Does the delegate object also receive messages that would be sent to an NSURLDownload? That might be a nice way to allow easy integration in cases where an object that responds to the delegate methods is defined in Obj-C.
Also, regarding multi-threading, couldn't you just use NSURLDownload (which you're probably already using underneath) + NSOperation/NSOperationQueue. Since your #download method already wraps the underlying process, you could should able to generate NSOperations to perform the downloads and add them to a queue. Those classes handle creating threads (which have their own runloops) and performing a task on the thread. That said, maybe this approach would not work with the current IO implementation?
Brian
2009/3/30 Matt Aimonetti <mattaimonetti@gmail.com>:
In my free time, I've been working on a http://lighthouseapp.comclient app which mixes API calls with webkit views (I'll release the source code later on). The app is 100% HotCocoa (meaning that I didn't use any nib/xibs). For people who are not used to work with delegation, having to deal
with
file download, URL queries can be a bit challenging if you want to start using the underlying obj-c methods.
That's why I wrote a small HTTP wrapper to clean up my code and make download/queries easier. The library is divided in 2 major parts, a helper and the Query/Response classes.
Here is how the helper looks like so far:
Download helper making file download trivial Pass an url, and a block or a delegator A block takes precedence over a delegator, meaning that you can't pass a delegator and use a block, you have to choose which approach to pick.
==== Parameters url<String>:: url of the resource to download options<Hash>:: optional options used for the query &block<block>:: block which is called when the file is downloaded
==== Options :method<String, Symbol>:: An optional value which represents the HTTP method to use :payload<String>:: - data to pass to a POST, PUT, DELETE query. :delegation<Object>:: - class or instance to call when the file is downloaded. the handle_query_response method will be called on the passed object :save_to<String>:: - path to save the response to :credential<Hash>:: - should contains the :user key and :password key By default the credential will be saved for the entire session
TODO :progress <Proc>:: Proc to call everytime the downloader makes some progress :cache_storage :custom headers Error handling
==== Examples
download("http://www.macruby.org/files/MacRuby%200.4.zip", {:save_to => '~/tmp/macruby.zip'.stringByStandardizingPath}) do |macruby| NSLog("file downloaded!") end
download "http://macruby.org" do |mr| # The response object has 3 accessors: status_code, headers and body NSLog("status: #{mr.status_code}, Headers: #{mr.headers.inspect}") end
path = File.expand_path('~/macruby_tmp.html') window :frame => [100, 100, 500, 500], :title => "HotCocoa" do |win| download("http://macruby.org", {:save_to => path}) do |homepage| win << label(:text => "status code: #{homepage.status_code}", :layout => {:start => false}) win << label(:text => "Headers: #{homepage.headers.inspect}", :layout => {:start => false}) end end
download "http://macruby.org/users/matt", :delegation => @downloaded_file, :method => 'PUT', :payload => {:name => 'matt aimonetti'}
download "http://macruby.org/roadmap.xml", :delegation => self
download "http://localhost:5984/couchrest-test/", :method => 'POST', :payload => '{"user":"mattaimonetti@gmail.com","zip":92129}', :delegation => self
download("http://mysite.com/login", {:credential => {:user => 'me', :password => 's3krit'}}) do |test| NSLog("response received: #{test.headers} #{test.status_code}") end
As you can see from the example list, the method name download isn't always appropriate and maybe I should give it the http_query alias. What do you think? What's nice with this approach, is that on top of being simple, the query and the response handling are async so the rest of your code can be run and you get notified only when the response comes back.
The underlying API looks like that: MacRubyHTTP::Query.new( url, http_method='GET', options={} ) Queries don't run in a custom runloop yet but that's something I want to look at once IO work on the experimental branch will be done.
So, here is my question, do you guys think that this is something that should be part of HotCocoa or it's better if it stays its own separate lib? Also, what do you think about having a default vendor folder for hotcocoa apps and autoload the vendor files if available?
- Matt
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
participants (3)
-
Brian Chapados
-
Ernest N. Prabhakar, Ph.D.
-
Matt Aimonetti