Permalink
Browse files

First commit since renaming from Librarian

  • Loading branch information...
0 parents commit ed4e359ab19ec12ac5d99f2cc31d09d0ecbc4818 Roger Leite committed Apr 20, 2012
@@ -0,0 +1,6 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
+tags
+.rvmrc*
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in librarian.gemspec
+gemspec
@@ -0,0 +1,20 @@
+Copyright (c) 2012 Roger Leite http://1up4dev.org
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,11 @@
+require "bundler/gem_tasks"
+require "rake/testtask"
+
+Rake::TestTask.new do |t|
+ t.libs.push "lib"
+ t.libs.push "test"
+ t.test_files = FileList['test/**/*_test.rb']
+ t.verbose = true
+end
+
+task :default => :test
@@ -0,0 +1,85 @@
+h1. Restfolia: a REST client to consume and interact with Hypermedia API.
+
+h2. Description
+
+p. Restfolia is a REST client and it's main goal is help you *consume and interact* with Hypermedia APIs.
+
+Against the grain, Restfolia is very opinionated about some REST's concepts:
+* Aims only *JSON Media Type*.
+* All responses are parsed and returned as Restfolia::Resource.
+* Less is more. Restfolia is very proud to be small, easy to maintain and evolve. You can compare Restfolia's code with "Similar Projects" at page's bottom.
+* Restfolia::Resource is Ruby object with attributes from JSON and can optionally contains *hypermedia links* which have to be a specific format. See the examples below.
+* All code is very well documented, using "TomDoc":http://tomdoc.org style.
+
+p. Obs: This is a draft version. Not ready for production (yet!).
+
+h2. References
+
+You can find more information about arquitecture REST below:
+* "Roy Fielding's":http://roy.gbiv.com/untangled see this post for example: "REST APIs must be hypertext-driven":http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
+* "Rest in Practice":http://restinpractice.com, especially the chapter titled "Hypermedia Formats".
+* "Mike Amundsen's Blog":http://amundsen.com/blog/
+* ROAR - "Resource Oriented Arquitectures in Ruby":https://github.com/apotonick/roar it seems really good to build a hypermedia API, of course you can go with Sinatra+rabl solutions too.
+
+
+h2. Examples of use
+
+<pre><code>
+ # GET http://localhost:9292/recursos/busca
+ { "itens_por_pagina" : 10,
+ "paginal_atual" : 1,
+ "paginas_totais" : 1,
+ "query" : "",
+ "total_resultado" : 100,
+ "resultado" : [ { "id" : 1,
+ "name" : "Test1",
+ "links" : [ { "href" : "http://localhost:9292/recursos/id/1",
+ "rel" : "recurso",
+ "type" : "application/json"
+ } ]
+ },
+ { "id" : 2,
+ "name" : "Test2",
+ "links" : [ { "href" : "http://localhost:9292/recursos/id/2",
+ "rel" : "recurso",
+ "type" : "application/json"
+ } ]
+ }
+ ],
+ "links" : { "href" : "http://localhost:9292/recursos/busca",
+ "rel" : "self",
+ "type" : "application/json"
+ },
+ }
+
+ # GET http://localhost:9292/recursos/id/1
+ { "id" : 1,
+ "name" : "Test1",
+ "links" : { "href" : "http://localhost:9292/recursos/id/1",
+ "rel" : "self",
+ "type" : "application/json"
+ }
+ }
+
+ # getting a resource
+ resource = Restfolia.at('http://localhost:9292/recursos/busca').get
+ resource.pagina_atual # => 1
+ resource.resultado # => [#<Resource ...>, #<Resource ...>]
+
+ # example of hypermedia navigation
+ r1 = resource.resultado.first
+ r1 = r1.links("recurso").get # => #<Resource ...>
+ r1.name # => "Test1"
+</code></pre>
+
+h2. Similar Projects
+
+* "ROAR":https://github.com/apotonick/roar
+* "Leadlight":https://github.com/avdi/leadlight
+* "Frenetic":https://github.com/dlindahl/frenetic
+* "Restfulie":https://github.com/caelum/restfulie
+
+h2. License
+
+p. Restfolia is copyright 2012 Roger Leite and contributors. It is licensed under the MIT license. See the include MIT-LICENSE file for details.
+
13 TODO
@@ -0,0 +1,13 @@
+EntryPoint
+* post!, put! and delete!
+* maybe remove bang from get, post, put and delete methods
+* allow cookies in request
+* allow headers in request
+
+Resource & EntryPoint
+* facilitate cache (Cache Control, ETag, Last-Modified)
+
+HTTP Behaviours
+* follow redirects
+* improve 404 and 500 errors
+* create librarian-wrappers project
@@ -0,0 +1,95 @@
+require "net/http"
+require "uri"
+
+require "rubygems"
+require "multi_json"
+
+require "restfolia/version"
+require "restfolia/http_behaviour"
+require "restfolia/entry_point"
+require "restfolia/resource"
+
+# Public: Restfolia: a REST client to consume and interact with Hypermedia API.
+#
+# Against the grain, Restfolia is very opinionated about some REST's concepts:
+# * Aims only *JSON Media Type*.
+# * All responses are parsed and returned as Restfolia::Resource.
+# * Less is more. Restfolia is very proud to be small, easy to maintain and evolve.
+# * Restfolia::Resource is Ruby object with attributes from JSON and can optionally contains *hypermedia links* which have to be a specific format. See the examples below.
+# * All code is very well documented, using "TomDoc":http://tomdoc.org style.
+#
+# Obs: This is a draft version. Not ready for production (yet!).
+#
+# References
+#
+# You can find more information about arquitecture REST below:
+# * "Roy Fielding's":http://roy.gbiv.com/untangled see this post for example: "REST APIs must be hypertext-driven":http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
+# * "Rest in Practice":http://restinpractice.com, especially the chapter titled "Hypermedia Formats".
+# * "Mike Amundsen's Blog":http://amundsen.com/blog/
+# * ROAR - "Resource Oriented Arquitectures in Ruby":https://github.com/apotonick/roar it seems really good to build a hypermedia API, of course you can go with Sinatra+rabl solutions too.
+#
+# Examples
+#
+# # GET http://localhost:9292/recursos/busca
+# { "itens_por_pagina" : 10,
+# "paginal_atual" : 1,
+# "paginas_totais" : 1,
+# "query" : "",
+# "total_resultado" : 100,
+# "resultado" : [ { "id" : 1,
+# "name" : "Test1",
+# "links" : [ { "href" : "http://localhost:9292/recursos/id/1",
+# "rel" : "recurso",
+# "type" : "application/json"
+# } ]
+# },
+# { "id" : 2,
+# "name" : "Test2",
+# "links" : [ { "href" : "http://localhost:9292/recursos/id/2",
+# "rel" : "recurso",
+# "type" : "application/json"
+# } ]
+# }
+# ],
+# "links" : { "href" : "http://localhost:9292/recursos/busca",
+# "rel" : "self",
+# "type" : "application/json"
+# },
+# }
+#
+# # GET http://localhost:9292/recursos/id/1
+# { "id" : 1,
+# "name" : "Test1",
+# "links" : { "href" : "http://localhost:9292/recursos/id/1",
+# "rel" : "self",
+# "type" : "application/json"
+# }
+# }
+#
+# # getting a resource
+# resource = Restfolia.at('http://localhost:9292/recursos/busca').get
+# resource.pagina_atual # => 1
+# resource.resultado # => [#<Resource ...>, #<Resource ...>]
+#
+# # example of hypermedia navigation
+# r1 = resource.resultado.first
+# r1 = r1.links("recurso").get # => #<Resource ...>
+# r1.name # => "Test1"
+#
+module Restfolia
+
+ # Public: Start point for getting the first Resource.
+ #
+ # url - String with the address of service to be accessed.
+ #
+ # Examples
+ #
+ # entry_point = Restfolia.at("http://localhost:9292/recursos/busca")
+ # entry_point.get # => #<Resource ...>
+ #
+ # Returns Restfolia::EntryPoint object.
+ def self.at(url)
+ EntryPoint.new(url)
+ end
+
+end
@@ -0,0 +1,104 @@
+module Restfolia
+
+ # Public: Responsible for request and validate a Resource.
+ # Abstract details of how to deal with HTTP, like headers,
+ # cookies, auth etc.
+ # The correct form to create an EntryPoint, is using Restfolia.at
+ # or links from some instance of Restfolia::Resource. See the
+ # examples for more details.
+ #
+ # Examples
+ #
+ # ep = Restfolia.at("http://fakeurl.com/some/service")
+ # # => #<EntryPoint ...>
+ #
+ # resource = Restfolia.at("http://fakeurl.com/service/id/1").get
+ # resource.links("contacts")
+ # # => #<EntryPoint ...> to "contacts" from this resource
+ class EntryPoint
+
+ include Restfolia::HTTPBehaviour
+
+ # Public: Returns the String url of EntryPoint.
+ attr_reader :url
+
+ # Public: Returns String that represents the relation of EntryPoint.
+ attr_reader :rel
+
+ # Public: Creates an EntryPoint.
+ #
+ # url - A String address of some API service.
+ # rel - An optional String that represents the relation of EntryPoint.
+ def initialize(url, rel = nil)
+ @url = url
+ @rel = rel
+ end
+
+ # Public: Get the Resource from this EntryPoint's url.
+ #
+ # params - an optional query String or Hash object. String parameter
+ # is passed direct as query. Hash object, before mounting query,
+ # URI.encode is used on values.
+ #
+ # Examples
+ #
+ # # GET on http://service.com/search
+ # resource = Restfolia.at("http://service.com/search").get
+ #
+ # # GET on http://service.com/search?q=test
+ # resource = Restfolia.at("http://service.com/search").get(:q => "test")
+ # # or if you want to control your query, you can send a String
+ # resource = Restfolia.at("http://service.com/search").get("q=test")
+ #
+ # Returns depends on http code from response. For each "range"
+ # (ex. 2xx, 3xx ... etc) you can have a different return value.
+ # For 2xx range, you can expect an instance of Restfolia::Resource.
+ # You can see Restfolia::HttpBehaviour for more details.
+ #
+ # Raises Restfolia::ResponseError for unexpected conditions. See
+ # Restfolia::HTTPBehaviour methods for more details.
+ # Raises URI::InvalidURIError if url attribute is invalid.
+ def get(params = nil)
+ query = if params && params.is_a?(String)
+ params
+ elsif params && params.is_a?(Hash)
+ params.map { |k, v| "#{k}=#{URI.encode(v)}" }.join
+ end
+ http_resp = do_request(:get, self.url, :query => query)
+ response_by_status_code(http_resp)
+ end
+
+ # Public: Post data to EntryPoint's url.
+ #
+ # params - Hash object with data to be encoded as JSON and send as
+ # request body.
+ #
+ # Examples
+ #
+ # # Expecting API respond with 201 and Location header
+ # data = {:name => "Test"}
+ # resource = Restfolia.at("http://service.com/resource/1").post(data)
+ #
+ # Returns depends on http code from response. For each "range"
+ # (ex. 2xx, 3xx ... etc) you can have a different return value.
+ # For 2xx range, you can expect an instance of Restfolia::Resource.
+ # You can see Restfolia::HttpBehaviour for more details.
+ #
+ # Raises Restfolia::ResponseError for unexpected conditions. See
+ # Restfolia::HTTPBehaviour methods for more details.
+ # Raises URI::InvalidURIError if url attribute is invalid.
+ def post(params)
+ body = MultiJson.encode(params)
+
+ http_resp = do_request(:post, self.url, :body => body)
+ response_by_status_code(http_resp)
+ end
+
+ # Returns url and rel for inspecting.
+ def inspect
+ "#<#{self.class} @url=\"#{@url}\" @rel=\"#{@rel}\">"
+ end
+
+ end
+
+end
Oops, something went wrong.

0 comments on commit ed4e359

Please sign in to comment.