Permalink
Browse files

Improve support for custom methods

  • Loading branch information...
1 parent 9038a23 commit 17d968ca1c3f20eb9a2543e5219defba504a3a20 @remiprev committed Apr 9, 2012
Showing with 148 additions and 42 deletions.
  1. +18 −3 README.md
  2. +2 −2 lib/her/api.rb
  3. +79 −16 lib/her/model/http.rb
  4. +9 −0 lib/her/model/orm.rb
  5. +3 −3 lib/her/model/relationships.rb
  6. +37 −18 spec/model_spec.rb
View
@@ -55,8 +55,23 @@ user.comments # => [#<Comment id=1>, #<Comment id=2>]
## Custom requests
-TBD.
+You can easily add custom methods for your models. You can either use `get_collection` (which maps the returned data to a collection of resources), `get_resource` (which maps the returned data to a single resource) or `get_raw` (which yields the parsed data return from the HTTP request).
-## Hooks
+```ruby
+class User
+ include Her::Model
+
+ def self.popular
+ get_collection("/users/popular")
+ end
-TBD.
+ def self.total
+ get_raw("/users/stats") do |parsed_data|
+ parsed_data[:data][:total_users]
+ end
+ end
+end
+
+User.popular # => [#<User id=1>, #<User id=2>]
+User.total # => 42
+```
View
@@ -26,7 +26,7 @@ def setup(attrs={}) # {{{
@parse_with = lambda do |response|
json = JSON.parse(response.body, :symbolize_names => true)
{
- :resource => json[:data],
+ :data => json[:data],
:errors => json[:errors],
:metadata => json[:metadata],
}
@@ -38,7 +38,7 @@ def setup(attrs={}) # {{{
end # }}}
# Define a custom parsing procedure. The procedure is passed the response object and is
- # expected to return a hash with three keys: a main resource Hash, an errors Array
+ # expected to return a hash with three keys: a main data Hash, an errors Array
# and a metadata Hash.
#
# @example
View
@@ -28,36 +28,99 @@ def request(attrs={}, &block) # {{{
end # }}}
# Make a GET request and return the parsed JSON response (not mapped to objects)
- #
- # @example
- # User.get "/users/1"
- def get(path, attrs={}, &block) # {{{
+ def get_raw(path, attrs={}, &block) # {{{
request(attrs.merge(:_method => :get, :_path => path), &block)
end # }}}
+ # Make a GET request and return a collection of resources
+ def get_collection(path, attrs={}) # {{{
+ get_raw(path, attrs) do |parsed_data|
+ new_collection(parsed_data)
+ end
+ end # }}}
+
+ # Make a GET request and return a collection of resources
+ def get_resource(path, attrs={}) # {{{
+ get_raw(path, attrs) do |parsed_data|
+ new(parsed_data[:resource])
+ end
+ end # }}}
+
# Make a POST request and return the parsed JSON response (not mapped to objects)
- #
- # @example
- # User.post "/users", :fullname => "G.O.B. Bluth"
- def post(path, attrs={}, &block) # {{{
+ def post_raw(path, attrs={}, &block) # {{{
request(attrs.merge(:_method => :post, :_path => path), &block)
end # }}}
+ # Make a POST request and return a collection of resources
+ def post_collection(path, attrs={}) # {{{
+ post_raw(path, attrs) do |parsed_data|
+ new_collection(parsed_data)
+ end
+ end # }}}
+
+ # Make a POST request and return a collection of resources
+ def post_resource(path, attrs={}) # {{{
+ post_raw(path, attrs) do |parsed_data|
+ new(parsed_data[:resource])
+ end
+ end # }}}
+
# Make a PUT request and return the parsed JSON response (not mapped to objects)
- #
- # @example
- # User.put "/users/1", :email => "gob@bluthcompany.com"
- def put(path, attrs={}, &block) # {{{
+ def put_raw(path, attrs={}, &block) # {{{
request(attrs.merge(:_method => :put, :_path => path), &block)
end # }}}
+ # Make a PUT request and return a collection of resources
+ def put_collection(path, attrs={}) # {{{
+ put_raw(path, attrs) do |parsed_data|
+ new_collection(parsed_data)
+ end
+ end # }}}
+
+ # Make a PUT request and return a collection of resources
+ def put_resource(path, attrs={}) # {{{
+ put_raw(path, attrs) do |parsed_data|
+ new(parsed_data[:resource])
+ end
+ end # }}}
+
+ # Make a PATCH request and return the parsed JSON response (not mapped to objects)
+ def patch_raw(path, attrs={}, &block) # {{{
+ request(attrs.merge(:_method => :patch, :_path => path), &block)
+ end # }}}
+
+ # Make a PATCH request and return a collection of resources
+ def patch_collection(path, attrs={}) # {{{
+ patch_raw(path, attrs) do |parsed_data|
+ new_collection(parsed_data)
+ end
+ end # }}}
+
+ # Make a PATCH request and return a collection of resources
+ def patch_resource(path, attrs={}) # {{{
+ patch_raw(path, attrs) do |parsed_data|
+ new(parsed_data[:resource])
+ end
+ end # }}}
+
# Make a DELETE request and return the parsed JSON response (not mapped to objects)
- #
- # @example
- # User.delete "/users/1"
- def delete(path, attrs={}, &block) # {{{
+ def delete_raw(path, attrs={}, &block) # {{{
request(attrs.merge(:_method => :delete, :_path => path), &block)
end # }}}
+
+ # Make a DELETE request and return a collection of resources
+ def delete_collection(path, attrs={}) # {{{
+ delete_raw(path, attrs) do |parsed_data|
+ new_collection(parsed_data)
+ end
+ end # }}}
+
+ # Make a DELETE request and return a collection of resources
+ def delete_resource(path, attrs={}) # {{{
+ delete_raw(path, attrs) do |parsed_data|
+ new(parsed_data[:resource])
+ end
+ end # }}}
end
end
end
@@ -9,6 +9,15 @@ def initialize(single_data) # {{{
@data = self.class.parse_relationships(@data)
end # }}}
+ # Initialize a collection of resources with raw data from an HTTP request
+ #
+ # @example
+ # User.get("/users/popular") { |data| User.new_collection(data) }
+ def new_collection(parsed_data) # {{{
+ collection_data = parsed_data[:resource]
+ Her::Model::ORM.initialize_collection(self.to_s.downcase.to_sym, collection_data)
+ end # }}}
+
# Initialize a collection of resources
# @private
def self.initialize_collection(name, collection_data) # {{{
@@ -34,9 +34,7 @@ def has_many(name, attrs={}) # {{{
define_method(name) do
return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
- self.class.get("#{collection_path}/#{id}/#{Object.const_get(name.to_s.classify).collection_path}") do |parsed_data|
- @data[name] = Her::Model::ORM.initialize_collection(name, parsed_data[:resource])
- end
+ self.class.get_collection("#{collection_path}/#{id}/#{Object.const_get(name.to_s.classify).collection_path}")
end
end # }}}
@@ -50,6 +48,8 @@ def has_many(name, attrs={}) # {{{
def belongs_to(name, attrs={}) # {{{
@her_relationships ||= {}
(@her_relationships[:belongs_to] ||= []) << attrs.merge(:name => name)
+
+ # TODO Write some code here
end # }}}
end
end
View
@@ -3,7 +3,7 @@
describe Her::Model do
describe Her::Model::HTTP do
- context "binding a model with an API" do # {{{
+ context "binding a model with an API" do
it "binds a model to an instance of Her::API" do # {{{
@api = Her::API.new
@api.setup :base_uri => "https://api.example.com"
@@ -78,16 +78,19 @@ class Comment; include Her::Model; end
@her_api.base_uri.should == "https://api2.example.com"
end
end # }}}
- end # }}}
+ end
context "making HTTP requests" do
before do # {{{
@api = Her::API.new
@api.setup :base_uri => "https://api.example.com"
FakeWeb.register_uri(:get, "https://api.example.com/users", :body => { :data => [{ :id => 1 }] }.to_json)
FakeWeb.register_uri(:get, "https://api.example.com/users?page=2", :body => { :data => [{ :id => 2 }] }.to_json)
+ FakeWeb.register_uri(:get, "https://api.example.com/users/popular", :body => { :data => [{ :id => 1 }, { :id => 2 }] }.to_json)
+ FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1 } }.to_json)
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => [{ :id => 3 }] }.to_json)
FakeWeb.register_uri(:put, "https://api.example.com/users/4", :body => { :data => [{ :id => 4 }] }.to_json)
+ FakeWeb.register_uri(:patch, "https://api.example.com/users/6", :body => { :data => [{ :id => 6 }] }.to_json)
FakeWeb.register_uri(:delete, "https://api.example.com/users/5", :body => { :data => [{ :id => 5 }] }.to_json)
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
@@ -97,40 +100,57 @@ class User
User.uses_api @api
end # }}}
- it "handle GET" do # {{{
- User.get("/users") do |parsed_data|
+ it "handle raw GET" do # {{{
+ User.get_raw("/users") do |parsed_data|
parsed_data[:resource].should == [{ :id => 1 }]
end
end # }}}
- it "handle POST" do # {{{
- User.post("/users") do |parsed_data|
+ it "handle raw POST" do # {{{
+ User.post_raw("/users") do |parsed_data|
parsed_data[:resource].should == [{ :id => 3 }]
end
end # }}}
- it "handle PUT" do # {{{
- User.put("/users/4") do |parsed_data|
+ it "handle raw PUT" do # {{{
+ User.put_raw("/users/4") do |parsed_data|
parsed_data[:resource].should == [{ :id => 4 }]
end
end # }}}
- it "handle DELETE" do # {{{
- User.delete("/users/5") do |parsed_data|
+ it "handle raw PATCH" do # {{{
+ User.patch_raw("/users/6") do |parsed_data|
+ parsed_data[:resource].should == [{ :id => 6 }]
+ end
+ end # }}}
+
+ it "handle raw DELETE" do # {{{
+ User.delete_raw("/users/5") do |parsed_data|
parsed_data[:resource].should == [{ :id => 5 }]
end
end # }}}
it "handle querystring parameters" do # {{{
- User.get("/users", :page => 2) do |parsed_data|
+ User.get_raw("/users", :page => 2) do |parsed_data|
parsed_data[:resource].should == [{ :id => 2 }]
end
end # }}}
+
+ it "handle GET collection" do # {{{
+ @users = User.get_collection("/users/popular")
+ @users.length.should == 2
+ @users.first.id.should == 1
+ end # }}}
+
+ it "handle GET resource" do # {{{
+ @user = User.get_resource("/users/1")
+ @user.id.should == 1
+ end # }}}
end
end
describe Her::Model::ORM do
- context "mapping data to Ruby objects" do # {{{
+ context "mapping data to Ruby objects" do
before do # {{{
@api = Her::API.new
@api.setup :base_uri => "https://api.example.com"
@@ -155,7 +175,7 @@ class User
@users.length.should == 2
@users.first.name.should == "Tobias Fünke"
end # }}}
- end # }}}
+ end
context "creating resources" do
before do # {{{
@@ -173,7 +193,7 @@ class User
end
describe Her::Model::Relationships do
- context "setting relationships" do # {{{
+ context "setting relationships" do
before do # {{{
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
class User
@@ -202,9 +222,9 @@ class User
User.belongs_to :family
User.relationships[:belongs_to].should == [{ :name => :organization }, { :name => :family }]
end # }}}
- end # }}}
+ end
- context "handling relationships" do # {{{
+ context "handling relationships" do
before do # {{{
Her::API.setup :base_uri => "https://api.example.com"
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke", :comments => [{ :id => 2, :body => "Tobias, you blow hard!" }, { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak" }] } }.to_json)
@@ -236,7 +256,6 @@ class Comment
@user.comments.first.id.should == 4
@user.comments.first.body.should == "They're having a FIRESALE?"
end # }}}
-
- end # }}}
+ end
end
end

0 comments on commit 17d968c

Please sign in to comment.