README.md in weary-0.7.2 vs README.md in weary-1.0.0.rc1

- old
+ new

@@ -1,218 +1,144 @@ # Weary -Weary is a tiny DSL for making the consumption of RESTful web services simple. It has evolved from the ideas put forth by libraries like [HTTParty](http://github.com/jnunemaker/httparty/ "JNunemaker's HTTParty") and [Typhoeus](http://github.com/pauldix/typhoeus "Paul Dix's Typhoeus"). It provides some sweet syntactic sugar over the Net/HTTP standard library. +_Weary is a framework and DSL for building clients for (preferably RESTful) web service APIs._ -What does it do: +At its most minimal, Weary is simply some nice syntactic sugar around Net/HTTP. -+ Quickly build an interface to your favorite REST API. -+ Parse XML and JSON with the [Crack](http://github.com/jnunemaker/crack) library. -+ Authentication with Basic Authentication and [OAuth](http://oauth.net/). -+ Asynchronous, multi-threaded requests. +If you dig a bit deeper, it's a suite of tools built around the [Rack](http://rack.rubyforge.org/) ecosystem. As you build a client, remember that just about every class in Weary is a piece of Rack middleware or a Rack application underneath the covers. -[RDoc](http://rdoc.info/projects/mwunsch/weary) | [Gem](http://rubygems.org/gems/weary) | [Wiki](http://wiki.github.com/mwunsch/weary) | [Metrics](http://getcaliper.com/caliper/project?repo=git://github.com/mwunsch/weary.git) +It features: -## Requirements +* Full Rack integration: -+ [Crack](http://github.com/jnunemaker/crack) >= 0.1.7 -+ [OAuth](http://github.com/mojodna/oauth) >= 0.3.5 + There are points in the stack to hook in Rack middleware and just about every class in Weary is a Rack application in its own right. -## Installation +* Asynchronous: - gem install weary - -If you're interested in doing development on Weary, clone the repository and run `bundle install` to get the development dependencies. - + `Weary::Request#perform`, the thing that performs the request, returns a [future](http://en.wikipedia.org/wiki/Futures_and_promises) and only blocks when accessed. + +It takes its inspiration from [HTTParty](https://github.com/jnunemaker/httparty) and [Faraday](https://github.com/technoweenie/faraday). + ## Quick Start - - # http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-users%C2%A0show - class TwitterUser < Weary::Base - - domain "http://twitter.com/users/" - - get "show" do |resource| - resource.with = [:id, :user_id, :screen_name] - end - end - - user = TwitterUser.new - me = user.show(:id => "markwunsch").perform - puts me["name"] - -Hey, that's me! -## The Base API/DSL +```ruby +# http://developer.github.com/v3/repos/ +class GithubRepo < Weary::Client + domain "https://api.github.com" -Create a class that inherits from `Weary::Base` to give it methods to craft a resource request: + use Rack::Lint - class Foo < Weary::Base - - declare "foo" do |resource| - resource.url = "http://path/to/foo" - end - end - -If you instantiate this class, you'll get an instance method named `foo` that crafts a GET request to "http://path/to/foo" + get :list_user_repos, "/users/{user}/repos" do |resource| + resource.optional :type + end -Besides the name of the resource, you can also give `declare` a block like: + get :get, "/repos/{user}/{repo}" +end - declare "foo" do |r| - r.url = "path/to/foo" - r.via = :post # defaults to :get - r.requires = [:id, :bar] # an array of params that the resource requires to be in the query/body - r.with = [:blah] # an array of params that you can optionally send to the resource - r.authenticates = false # does the method require authentication? defaults to false - r.follows = false # if this is set to false, the formed request will not follow redirects. - r.headers = {'Accept' => 'text/html'} # send custom headers. defaults to nil. - end - -So this would form a method: - - x = Foo.new - x.foo :id => "mwunsch", :bar => 123 - -That method would return a `Weary::Request` object. Use the `perform` method and get a `Weary::Response` that you could parse and/or examine. +client = GithubRepo.new +client.list_user_repos(:user => "mwunsch").perform do |response| + puts response.body if response.success? +end +``` -### Parsing the Body +This is a basic example of a client you will build using the Weary framework. If you're coming from a previous version of Weary, you would have created a subclass of `Weary::Base`. That's one of the many changes in the **big rewrite**. -Once you make your request with the fancy method that Weary created for you, you can do stuff with what it returns...which could be a good reason you're using Weary in the first place. Let's look at the above example: +### Weary::Client - x = Foo.new - y = x.foo(:id => "mwunsch", :bar => 123).perform.parse - y["foos"]["user"] - -Weary parses with Crack, but you're not beholden to it. You can get the raw Request body to have your way with: +Inherit from `Weary::Client` for a set of class methods that craft "Resources" (more on that later). - x = Foo.new - y = x.foo(:id => "mwunsch", :bar => 123).perform - Nokogiri.parse(y.body) - -*note: Weary used to have Nokogiri built in, using the `#search` method, but that was dropped.* +```ruby +MyClass < Weary::Client + get :resource, "http://host.com/path/to/resource" do |resource| + resource.optional :optional_parameter + end +end +``` -### Shortcuts +The DSL provides methods for all of the HTTP verbs (See `Weary::Client::REQUEST_METHODS`). When you instantiate this class, the object will have an instance method named "resource" that will return a `Weary::Request` object set up to perform a "GET" request on "http://host.com/path/to/resource". -Of course, you don't always have to use `declare`; that is a little too ambiguous. You can also use `get`, `post`, `delete`, etc. Those do the obvious. +You can pass a block these methods for access to the `Weary::Resource`. -### Forming URLs +Further methods in the DSL include: -There are many ways to form URLs in Weary. You can define URLs for the entire class by typing: + domain - This will be prepended to every path when resources are defined + (Particularly useful when using Client's Rack integration, discussed below). + optional - See Resource section below. + required - See Resource section below. + defaults - See Resource section below. + headers - See Resource section below. + use - A Rack::Middleware to place in the Request stack. + (See Rack integration further down) - class Foo < Weary::Base - - domain "http://foo.bar/" - format :xml - - get "show_users" - end - -If you don't supply a url when declaring the Resource, Weary will look to see if you've defined a domain, and will make a url for you. The above `get` declaration creates a url that looks like: *http://foo.bar/show_users.xml*. I think it's better to write the whole URL out. That's unambiguous. - -### Weary DSL -You can create some defaults for all of our resources easily: +#### Weary::Resource - class Foo < Weary::Base - - def initialize(username,password) - self.credentials username,password #basic authentication - self.defaults = {:user => username} #parameters that will be passed in every request - end +The resource is a building block used in `Client` to describe the requirements of a request. - domain "http://foo.bar/" - format :xml - headers {'Accept' => 'text/html'} # set headers - - post "update" {|r| r.authenticates = true} # uses the defaults defined above! - end - -Then you can do something like this: + optional - A group of keys for parameters that the request expects. + required - Keys that the request needs in order to be performed. + defaults - Default parameters to be sent in every request. + headers - Headers to send in the request. + user_agent - A convenience method to set the User Agent header. + basic_auth! - Prepare the request to accept a username and password for basic authentication. + oauth! - Prepare the request to accept the consumer key and access token in the request. - f = Foo.new('me','secretz') - f.update - -Which will create a POST Request for *http://foo.bar/update.xml* that will authenticate you, using basic authentication, with the username/password of "me"/"secrets" and will send the parameter `{:user => "me"}`. Easy. +Finally, the `request` method of the Resource takes a set of parameters to verify that requirements are met and returns a `Weary::Request` object. It should all look something like this once all is said and done. -## Weary Class Methods +```ruby +# https://dev.twitter.com/docs/api/1/post/statuses/update +post :update, "http://api.twitter.com/1/statuses/update.json" do |resource| + resource.required :status + resource.optional :in_reply_to_status_id, :lat, :long, :place_id, + :display_coordinates, :trim_user, :include_entities + resource.oauth! +end -Maybe you don't want the baggage that comes with `Weary::Base`. That's okay, Weary provides some basic class-level methods to Easily build a `Weary::Request`: +# After instantiating the client: +# (This calls the "update" resource's `request` method) +client.update :status => "I'm tweeting from Weary", + :consumer_key => "an_oauth_consumer_key", + :token => "my_oauth_access_token" - # See examples/repo.rb to see this in practice - class Repository +``` - def show(user, repo) - Weary.get "http://github.com/api/v2/yaml/repos/show/#{user}/#{repo}" - end +If a `required` parameter is missing, a `Weary::Resource::UnmetRequirementsError` exception is raised. - end - - Repository.new.show 'mwunsch', 'weary' - -That will build the Get request to fetch the YAML info about this repository. +URL's for these methods can also be dynamic. If we alter the above example: -Pass a block to `Weary.get` to dive further into the Request: + post :update, "http://api.twitter.com/1/statuses/update.{format}" do |resource| - Weary.get "http://twitter.com/statuses/user_timeline" do |req| - req.follows = false - req.with = {:id => 'markwunsch'} - req.credentials = {:username => 'markwunsch', :password => 'secret'} - req.headers = {"User-Agent" => Weary::UserAgents["Safari 4.0.2 - Mac"]} - end - -## Request Callbacks +Then a key `:format` will be expected to be passed with the other parameters. -A `Weary::Request` has a couple of callbacks you can do: +The method that the Client defines (in the above example, the `client.update` method), can take an optional block that allows you to manipulate the underlying `Weary::Request` object. - status = Weary.get("http://twitter.com/statuses/user_timeline") do |r| - r.with = {:id => 'markwunsch'} - end - - status.before_send do |request| - puts "Sending a request to #{request.uri}" - end - - status.on_complete do |response| - if response.success? - puts response.body - else - puts "Something went wrong: #{response.code}: #{response.message}" - end - end - -`before_send` is sent just before the request is made, and `on_complete` is triggered immediately following. `before_send` passes the Request object to the block and `on_complete` passes the Response object. +### Weary::Request -You don't need to define `on_complete`, though. Passing a block to the `perform` method of the Request also defines this callback or will overwrite what you had previously defined: +No matter how you get there, you'll end up with a Weary::Request object. Call the `perform` method to actually make the request and get back a `Weary::Response`. That's not entirely true `Weary::Request#perform` is asynchronous and non-blocking. It returns a future and will only block once you call a method on the response. You can optionally pass a block that's executed once the response has returned. - status.perform do |response| - puts "Request to #{response.url}, complete. Got a #{response.code}." - end - -## Multiple Asynchronous Requests with Batch +By default, the request is performed through [Net::HTTP](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/net/http/rdoc/Net/HTTP.html). This is done through `Weary::Adapter::NetHttp`. A `Weary::Adapter` is just a special kind of Rack application. `Request#adapter` allows you to hook up your own. -Requests, along with the `perform` method, also has a `perform!` method, which spins off a Thread to actually perform the Net::HTTP Request. This method returns a Thread object, and is encapsulated by the `perform` method. +## Rack -Weary::Batch allows you to make a group of `perform!` requests, firing at will. It takes a group of Requests. +To maximize the utility of Weary, it's important to remember that driving everything is Rack. Almost every class is built to provide a Rack interface. - # see examples/batch.rb - resources = %w[http://twitter.com http://github.com http://vimeo.com http://tumblr.com] - requests = [] - - ## build the group of requests: - resources.each do |url| - requests << Weary.get(url) do |req| - req.on_complete {|res| puts "Hello from #{res.url}"} - end - end - - ## And fire them off: - Weary.batch(requests).perform - -Batch has callbacks, just like the Request: +Every class that inherits from `Weary::Client` is a Rack application. - Weary.batch(requests).perform do - puts 'All done.' - end +A `Weary::Request` is a Rack application. When you call `Request#call` it creates its own special Rack environment. In order to preserve your Rack middleware, you can add your middleware to the stack using `Request#use`. -You can investigate the pool of threads once you've called `perform` with `Batch#pool` or look at all the returned responses with `Batch#responses`. +When using `Weary::Client` the `use` method will add the passed middleware to every Request stack. -## And more... - -There's more to discover in the Wiki. +Authentication, by default is done by either `Weary::Middleware::BasicAuth` or `Weary::Middleware::OAuth`. Both are just Rack middleware, and can be used in any Rack stack. + +The point is, **it's just Rack**. + +## Release Candidate + +At this point, I need _your_ help to further Weary along. I'd love to see more examples that utilize the Rackness of Weary: using Devise, Warden, or mounted in a Rails application. + +## Examples + +[**Gilt**](https://github.com/mwunsch/gilt) is a Ruby client to the [Gilt public API](https://dev.gilt.com/). Notice how much of the code is dedicated to the Gilt domain model, and very little to actually interacting with the web service. That's the idea. + +## Copyright + +Copyright (c) 2009 - 2012 Mark Wunsch. Licensed under the [MIT License](http://opensource.org/licenses/mit-license.php).