party_resource 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # party_resource
2
+
3
+ Simple REST interface for ruby objects.
4
+
5
+ party_resource is a framework for building ruby objects which interact with a REST api. Built on top of HTTParty.
6
+
7
+ HTTParty is great for adding a couple of methods that fetch data from an HTTP api, but becomes cumbersome if you have
8
+ lots of objects that all need to connect to several routes on the api. ActiveResource doesn't give you enough control.
9
+
10
+ ## Usage
11
+
12
+ For detailed usage instructions, please see the [API Documentation](http://yardoc.org/docs/edendevelopment-party_resource).
13
+
14
+ PartyResource::Connector.add(:library, :base_uri => 'http://www.example.com/library')
15
+
16
+ class Author
17
+ include PartyResource
18
+
19
+ property :name
20
+ property :slug
21
+
22
+ connect :books, :get => '/authors/:slug/books', :as => Book, :on => :instance
23
+
24
+ def initialize(params = {})
25
+ populate_properties(params)
26
+ end
27
+ end
28
+
29
+ class Book
30
+ include PartyResource
31
+
32
+ property :title
33
+ property :author, :as => Author
34
+
35
+ connect :search, :get => '/search/:query', :with => :query
36
+
37
+ def initialize(params = {})
38
+ populate_properties(name)
39
+ end
40
+ end
41
+
42
+ book = Book.search('Lord of the Rings')
43
+
44
+ book.title #=> 'Lord of the Rings Trilogy'
45
+ book.author.class #=> Author
46
+ book.author.name #=> 'J. R. R. Tolkein'
47
+
48
+ author = book.author
49
+
50
+ author.books.map(&:title) #=> ['Lord of the Rings Trilogy', 'The Hobbit', 'The Silmarillion']
51
+
52
+ ## Issues
53
+
54
+ Please use the [github issues tracker](http://github.com/edendevelopment/party_resource/issues).
55
+
56
+ ## Note on Patches/Pull Requests
57
+
58
+ * Fork the project.
59
+ * Make your feature addition or bug fix.
60
+ * Add tests for it. This is important so I don't break it in a
61
+ future version unintentionally.
62
+ * Commit, do not mess with rakefile, version, or history.
63
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
64
+ * Send me a pull request. Bonus points for topic branches.
65
+
66
+ ## Copyright
67
+
68
+ Copyright (c) 2010 Eden Development (UK) LTD. See LICENSE for details.
@@ -13,7 +13,7 @@ module PartyResource
13
13
  def fetch(request)
14
14
  response = HTTParty.send(request.verb, request.path, request.http_data(options))
15
15
  unless (200..399).include? response.code
16
- raise PartyResource::ConnectionError.build(response)
16
+ raise PartyResource::Exceptions::ConnectionError.build(response)
17
17
  end
18
18
  response
19
19
  end
@@ -1,27 +1,29 @@
1
1
  require 'party_resource/connector/base'
2
2
  module PartyResource
3
- def self.Connector(name = nil)
4
- Connector.lookup(name)
5
- end
6
-
7
3
  module Connector
8
- def self.lookup(name)
9
- name ||= repository.default
10
- connector = repository.connectors[name]
11
- raise NoConnector.new(name) if connector.nil?
12
- connector
13
- end
4
+ class << self
5
+ # Find connector by name
6
+ # @return [Connector::Base] the requested connector
7
+ def lookup(name)
8
+ name ||= repository.default
9
+ connector = repository.connectors[name]
10
+ raise Exceptions::NoConnector.new(name) if connector.nil?
11
+ connector
12
+ end
14
13
 
15
- def self.default=(name)
16
- repository.default = name
17
- end
14
+ # Add a new named connector
15
+ def add(name, options)
16
+ repository.new_connector(name, options)
17
+ end
18
18
 
19
- def self.add(name, options)
20
- repository.new_connector(name, options)
21
- end
19
+ private
20
+ def default=(name)
21
+ repository.default = name
22
+ end
22
23
 
23
- def self.repository
24
- @repository ||= Repository.new
24
+ def repository
25
+ @repository ||= Repository.new
26
+ end
25
27
  end
26
28
 
27
29
  class Repository
@@ -1,57 +1,59 @@
1
1
  module PartyResource
2
2
 
3
- Error = Class.new(StandardError)
3
+ module Exceptions
4
+ Error = Class.new(StandardError)
4
5
 
5
- class MissingParameter < Error
6
- def initialize(parameter, context)
7
- @parameter = parameter
8
- @context = context
9
- end
6
+ class MissingParameter < Error
7
+ def initialize(parameter, context)
8
+ @parameter = parameter
9
+ @context = context
10
+ end
10
11
 
11
- def to_s
12
- "No value for #{@parameter} is available in #{@context}"
12
+ def to_s
13
+ "No value for #{@parameter} is available in #{@context}"
14
+ end
13
15
  end
14
- end
15
16
 
16
- class NoConnector < Error
17
- def initialize(name)
18
- @name = name
19
- end
17
+ class NoConnector < Error
18
+ def initialize(name)
19
+ @name = name
20
+ end
20
21
 
21
- def to_s
22
- "Connector '#{@name}' has not been defined"
22
+ def to_s
23
+ "Connector '#{@name}' has not been defined"
24
+ end
23
25
  end
24
- end
25
26
 
26
- class ConnectionError < Error;
27
- attr_reader :data
28
-
29
- def self.build(data=nil)
30
- klass = case data.code
31
- when 404: ResourceNotFound
32
- when 422: ResourceInvalid
33
- when 400..499: ClientError
34
- when 500..599: ServerError
35
- else self
27
+ class ConnectionError < Error;
28
+ attr_reader :data
29
+
30
+ def self.build(data=nil)
31
+ klass = case data.code
32
+ when 404: ResourceNotFound
33
+ when 422: ResourceInvalid
34
+ when 400..499: ClientError
35
+ when 500..599: ServerError
36
+ else self
37
+ end
38
+ klass.new(data)
36
39
  end
37
- klass.new(data)
38
- end
39
40
 
40
- def initialize(data=nil)
41
- super()
42
- @data = data
43
- end
41
+ def initialize(data=nil)
42
+ super()
43
+ @data = data
44
+ end
44
45
 
45
- def to_s
46
- code = ''
47
- code = "#{data.code} " rescue nil
48
- "A #{code}connection error occured"
46
+ def to_s
47
+ code = ''
48
+ code = "#{data.code} " rescue nil
49
+ "A #{code}connection error occured"
50
+ end
49
51
  end
50
- end
51
52
 
52
- ClientError = Class.new(ConnectionError) # 4xx
53
- ServerError = Class.new(ConnectionError) # 5xx
53
+ ClientError = Class.new(ConnectionError) # 4xx
54
+ ServerError = Class.new(ConnectionError) # 5xx
54
55
 
55
- ResourceNotFound = Class.new(ClientError) # 404
56
- ResourceInvalid = Class.new(ClientError) # 422
56
+ ResourceNotFound = Class.new(ClientError) # 404
57
+ ResourceInvalid = Class.new(ClientError) # 422
58
+ end
57
59
  end
@@ -6,6 +6,8 @@ module PartyResource
6
6
  module ClassMethods
7
7
  include MethodDefine
8
8
 
9
+ # Connect a method call to a restful uri
10
+ # @return [nil]
9
11
  def connect(name, options={})
10
12
  level = options.delete(:on)
11
13
  options = {:as => :self, :connector => @party_connector}.merge(options)
@@ -14,8 +16,35 @@ module PartyResource
14
16
  define_method_on(level, name) do |*args|
15
17
  route.call(self, *args)
16
18
  end
19
+ nil
17
20
  end
18
21
 
22
+ # Define a property
23
+ # @overload property(*names, options={})
24
+ # @param [Symbol] names list of property names
25
+ # @param [Hash] options the options to use to create the property
26
+ # @option options :as (:self) How to build property
27
+ #
28
+ # :raw - raw data
29
+ #
30
+ # :self - self.new(data)
31
+ #
32
+ # class - class.new(data)
33
+ #
34
+ # Array(class, :method) - class.method(data)
35
+ #
36
+ # lambda - lambda.call(data)
37
+ # @option options :from (property name) where to find property value in incomming data hash
38
+ #
39
+ # symbol - name
40
+ #
41
+ # array - list of nested hash keys
42
+ # @option options :to (from value) where to put property value in outgoing incomming data hash
43
+ #
44
+ # symbol - name
45
+ #
46
+ # array - list of nested hash keys
47
+ # @return [nil]
19
48
  def property(*names)
20
49
  options = names.pop if names.last.is_a?(Hash)
21
50
  names.each do |name|
@@ -26,10 +55,14 @@ module PartyResource
26
55
  @property_list ||= []
27
56
  @property_list << Property.new(name, options)
28
57
  end
58
+ nil
29
59
  end
30
60
 
61
+ # Set the name of the connector to use for this class
62
+ # @return [nil]
31
63
  def party_connector(name)
32
64
  @party_connector = name
65
+ nil
33
66
  end
34
67
 
35
68
  private
@@ -49,19 +82,22 @@ module PartyResource
49
82
  begin
50
83
  out[var] = send(var)
51
84
  rescue
52
- raise MissingParameter.new(var, self)
85
+ raise Exceptions::MissingParameter.new(var, self)
53
86
  end
54
87
  out
55
88
  end
56
89
  end
57
90
  end
58
91
 
92
+ # Converts the objects properties to a hash
93
+ # @return [Hash] a hash of all the properties
59
94
  def to_properties_hash
60
95
  self.class.send(:property_list).inject({}) do |hash, property|
61
96
  hash.merge(property.to_hash(self))
62
97
  end
63
98
  end
64
99
 
100
+ # Test if all properties are equal
65
101
  def properties_equal?(other)
66
102
  begin
67
103
  self.class.send(:property_list).all? {|property| self.send(property.name) == other.send(property.name) }
@@ -15,7 +15,7 @@ module PartyResource
15
15
  raise ArgumentError, "wrong number of arguments (#{args.size} for #{expected_args.size})" unless expected_args.size == args.size
16
16
  begin
17
17
  builder.call(connector.fetch(request(context, args, options)), context, included(context))
18
- rescue Error => e
18
+ rescue Exceptions::Error => e
19
19
  name = e.class.name.split(/::/).last
20
20
  return @options[:rescue][name] if @options[:rescue].has_key?(name)
21
21
  raise
@@ -23,7 +23,7 @@ module PartyResource
23
23
  end
24
24
 
25
25
  def connector
26
- PartyResource::Connector(@options[:connector])
26
+ PartyResource::Connector.lookup(@options[:connector])
27
27
  end
28
28
 
29
29
  private
@@ -71,22 +71,22 @@ describe TestClass do
71
71
  context 'error cases' do
72
72
  it 'raises ResourceNotFound' do
73
73
  stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 404)
74
- lambda { TestClass.destroy }.should raise_error(PartyResource::ResourceNotFound)
74
+ lambda { TestClass.destroy }.should raise_error(PartyResource::Exceptions::ResourceNotFound)
75
75
  end
76
76
 
77
77
  it 'raises ResourceInvalid' do
78
78
  stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 422)
79
- lambda { TestClass.destroy }.should raise_error(PartyResource::ResourceInvalid)
79
+ lambda { TestClass.destroy }.should raise_error(PartyResource::Exceptions::ResourceInvalid)
80
80
  end
81
81
 
82
82
  it 'raises ClientError' do
83
83
  stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 405)
84
- lambda { TestClass.destroy }.should raise_error(PartyResource::ClientError)
84
+ lambda { TestClass.destroy }.should raise_error(PartyResource::Exceptions::ClientError)
85
85
  end
86
86
 
87
87
  it 'raises ServerError' do
88
88
  stub_request(:delete, "http://fred:pass@myserver/path/delete").to_return(:status => 501)
89
- lambda { TestClass.destroy }.should raise_error(PartyResource::ServerError)
89
+ lambda { TestClass.destroy }.should raise_error(PartyResource::Exceptions::ServerError)
90
90
  end
91
91
 
92
92
  it 'rescues exceptions' do
@@ -74,7 +74,7 @@ describe PartyResource::Connector::Base do
74
74
  it 'raises an exception' do
75
75
  return_data = mock(:data, :code => 404)
76
76
  HTTParty.should_receive(:get).and_return(return_data)
77
- lambda{ subject.fetch(request) }.should raise_error(PartyResource::ConnectionError)
77
+ lambda{ subject.fetch(request) }.should raise_error(PartyResource::Exceptions::ConnectionError)
78
78
  end
79
79
  end
80
80
 
@@ -8,7 +8,7 @@ describe PartyResource::Connector do
8
8
  let(:connectors) { { :test => test_connector, :other => default_connector } }
9
9
 
10
10
  before do
11
- PartyResource::Connector.repository.stub(:connectors => connectors)
11
+ PartyResource::Connector.send(:repository).stub(:connectors => connectors)
12
12
  end
13
13
 
14
14
  it "returns the named connectors" do
@@ -16,21 +16,13 @@ describe PartyResource::Connector do
16
16
  end
17
17
 
18
18
  it "returns the default connector for nil name" do
19
- PartyResource::Connector.repository.stub(:default => :other)
19
+ PartyResource::Connector.send(:repository).stub(:default => :other)
20
20
 
21
21
  PartyResource::Connector.lookup(nil).should == default_connector
22
22
  end
23
23
 
24
24
  it 'raises a NoConnector error it the connector could not be found' do
25
- lambda { PartyResource::Connector.lookup(:missing_name) }.should raise_error(PartyResource::NoConnector)
26
- end
27
- end
28
-
29
- describe '#default=' do
30
- it 'sets the default connector name' do
31
- name = mock(:name)
32
- PartyResource::Connector.default = name
33
- PartyResource::Connector.repository.default.should == name
25
+ lambda { PartyResource::Connector.lookup(:missing_name) }.should raise_error(PartyResource::Exceptions::NoConnector)
34
26
  end
35
27
  end
36
28
 
@@ -49,16 +41,16 @@ describe PartyResource::Connector do
49
41
  it 'creates a new connector' do
50
42
  PartyResource::Connector::Base.should_receive(:new).with(name, options).and_return(connector)
51
43
  PartyResource::Connector.add(name, options)
52
- PartyResource::Connector(name).should == connector
44
+ PartyResource::Connector.lookup(name).should == connector
53
45
  end
54
46
 
55
47
  it 'sets the default if it is currently unset' do
56
48
  name2 = mock(:name2)
57
49
  PartyResource::Connector.add(name, options)
58
- PartyResource::Connector.repository.default.should == name
50
+ PartyResource::Connector.send(:repository).default.should == name
59
51
 
60
52
  PartyResource::Connector.add(name2, options)
61
- PartyResource::Connector.repository.default.should == name
53
+ PartyResource::Connector.send(:repository).default.should == name
62
54
  end
63
55
 
64
56
  it 'sets the default if the default option is set' do
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe 'Exceptions' do
4
4
 
5
- describe PartyResource::ConnectionError do
6
- subject { PartyResource::ConnectionError }
5
+ describe PartyResource::Exceptions::ConnectionError do
6
+ subject { PartyResource::Exceptions::ConnectionError }
7
7
 
8
8
  describe 'build' do
9
9
  let(:built) { subject.build(data) }
@@ -11,22 +11,22 @@ describe 'Exceptions' do
11
11
 
12
12
  it 'builds ResourceNotFound' do
13
13
  data.stub(:code => 404)
14
- built.should be_a(PartyResource::ResourceNotFound)
14
+ built.should be_a(PartyResource::Exceptions::ResourceNotFound)
15
15
  end
16
16
 
17
17
  it 'builds ResourceInvalid' do
18
18
  data.stub(:code => 422)
19
- built.should be_a(PartyResource::ResourceInvalid)
19
+ built.should be_a(PartyResource::Exceptions::ResourceInvalid)
20
20
  end
21
21
 
22
22
  it 'builds ClientError' do
23
23
  data.stub(:code => 400)
24
- built.should be_a(PartyResource::ClientError)
24
+ built.should be_a(PartyResource::Exceptions::ClientError)
25
25
  end
26
26
 
27
27
  it 'builds ServerError' do
28
28
  data.stub(:code => 500)
29
- built.should be_a(PartyResource::ServerError)
29
+ built.should be_a(PartyResource::Exceptions::ServerError)
30
30
  end
31
31
  end
32
32
  end
@@ -84,25 +84,6 @@ describe "PartyResource" do
84
84
  end
85
85
  end
86
86
 
87
- describe '.Connector' do
88
- context 'with name == nil' do
89
- it 'returns the default connector' do
90
- connector = mock(:connector)
91
- PartyResource::Connector.should_receive(:lookup).with(nil).and_return(connector)
92
- PartyResource::Connector(nil).should == connector
93
- end
94
- end
95
-
96
- context 'with a name given' do
97
- it 'returns the requested connector' do
98
- connector = mock(:connector)
99
- name = mock(:name)
100
- PartyResource::Connector.should_receive(:lookup).with(name).and_return(connector)
101
- PartyResource::Connector(name).should == connector
102
- end
103
- end
104
- end
105
-
106
87
  describe '.parameter_values' do
107
88
  let_mock(:v1)
108
89
  let_mock(:v2)
@@ -114,7 +95,7 @@ describe "PartyResource" do
114
95
 
115
96
  it 'raises a MissingParameter error' do
116
97
  subject.stub(:v1 => v1)
117
- lambda { subject.parameter_values([:v1, :vx]) }.should raise_error(PartyResource::MissingParameter)
98
+ lambda { subject.parameter_values([:v1, :vx]) }.should raise_error(PartyResource::Exceptions::MissingParameter)
118
99
  end
119
100
  end
120
101
 
@@ -12,7 +12,7 @@ describe PartyResource::Route do
12
12
  let(:options) { { :get => path, :as => klass } }
13
13
 
14
14
  before do
15
- PartyResource.stub(:Connector => connector)
15
+ PartyResource::Connector.stub(:lookup => connector)
16
16
  end
17
17
 
18
18
  describe ".call" do
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 2
9
+ version: 0.0.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Tristan Harris
@@ -19,8 +19,6 @@ date: 2010-05-26 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: httparty
23
- prerelease: false
24
22
  requirement: &id001 !ruby/object:Gem::Requirement
25
23
  requirements:
26
24
  - - ">="
@@ -30,11 +28,11 @@ dependencies:
30
28
  - 5
31
29
  - 2
32
30
  version: 0.5.2
31
+ name: httparty
32
+ prerelease: false
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
35
  - !ruby/object:Gem::Dependency
36
- name: activesupport
37
- prerelease: false
38
36
  requirement: &id002 !ruby/object:Gem::Requirement
39
37
  requirements:
40
38
  - - ">="
@@ -44,11 +42,11 @@ dependencies:
44
42
  - 3
45
43
  - 5
46
44
  version: 2.3.5
45
+ name: activesupport
46
+ prerelease: false
47
47
  type: :runtime
48
48
  version_requirements: *id002
49
49
  - !ruby/object:Gem::Dependency
50
- name: rspec
51
- prerelease: false
52
50
  requirement: &id003 !ruby/object:Gem::Requirement
53
51
  requirements:
54
52
  - - ">="
@@ -58,11 +56,11 @@ dependencies:
58
56
  - 2
59
57
  - 9
60
58
  version: 1.2.9
59
+ name: rspec
60
+ prerelease: false
61
61
  type: :development
62
62
  version_requirements: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: yard
65
- prerelease: false
66
64
  requirement: &id004 !ruby/object:Gem::Requirement
67
65
  requirements:
68
66
  - - ">="
@@ -70,11 +68,11 @@ dependencies:
70
68
  segments:
71
69
  - 0
72
70
  version: "0"
71
+ name: yard
72
+ prerelease: false
73
73
  type: :development
74
74
  version_requirements: *id004
75
75
  - !ruby/object:Gem::Dependency
76
- name: webmock
77
- prerelease: false
78
76
  requirement: &id005 !ruby/object:Gem::Requirement
79
77
  requirements:
80
78
  - - ">="
@@ -82,6 +80,8 @@ dependencies:
82
80
  segments:
83
81
  - 0
84
82
  version: "0"
83
+ name: webmock
84
+ prerelease: false
85
85
  type: :development
86
86
  version_requirements: *id005
87
87
  description: party_resource is a framework for building ruby objects which interact with a REST api. Built on top of HTTParty.
@@ -92,7 +92,7 @@ extensions: []
92
92
 
93
93
  extra_rdoc_files:
94
94
  - LICENSE
95
- - README.rdoc
95
+ - README.md
96
96
  files:
97
97
  - lib/party_resource.rb
98
98
  - lib/party_resource/buildable.rb
@@ -105,7 +105,7 @@ files:
105
105
  - lib/party_resource/request.rb
106
106
  - lib/party_resource/route.rb
107
107
  - LICENSE
108
- - README.rdoc
108
+ - README.md
109
109
  has_rdoc: true
110
110
  homepage: http://github.com/edendevelopment/party_resource.git
111
111
  licenses: []
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = party_resource
2
-
3
- Simple REST interface for ruby objects.
4
-
5
- party_resource is a framework for building ruby objects which interact with a REST api. Built on top of HTTParty.
6
-
7
- == Note on Patches/Pull Requests
8
-
9
- * Fork the project.
10
- * Make your feature addition or bug fix.
11
- * Add tests for it. This is important so I don't break it in a
12
- future version unintentionally.
13
- * Commit, do not mess with rakefile, version, or history.
14
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
15
- * Send me a pull request. Bonus points for topic branches.
16
-
17
- == Copyright
18
-
19
- Copyright (c) 2010 Eden Development (UK) LTD. See LICENSE for details.