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).