activeresource-five 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a090060069d258cd94729d4b260529d9e16c8943
4
+ data.tar.gz: 210bacf920a99fd19e34092b4c27a60e4a9798bf
5
+ SHA512:
6
+ metadata.gz: 77f3941f609d8f612b17c0ee76e182475c8610bd9ef9d0fcddb96338409b3a26fbe08fc6a39270dea394ff9f9af4e833110de2016f7997d53ff7227835e1a6c8
7
+ data.tar.gz: 1c20287839e4b664aa6b73ac5a4e4fbd46ac2459bf4d991114a83c279f0284ea4e17e31949692d6708fa3daf2a8c48d1afefd3722d8c336bf94b95dd853912e0
@@ -0,0 +1,249 @@
1
+ = Active Resource Five
2
+
3
+ Active Resource (ARes) connects business objects and Representational State Transfer (REST)
4
+ web services. It implements object-relational mapping for REST web services to provide transparent
5
+ proxying capabilities between a client (ActiveResource) and a RESTful service (which is provided by Simply RESTful routing
6
+ in ActionController::Resources).
7
+
8
+ There is currently no version 5 release in Ruby gems, so as an interim solution, this release brings 5 functionality to RubyGems so that it can be included in a dependency of other gems without causing dependency conflicts with ActiveResource/ActiveModel 5 etc.
9
+
10
+ == Philosophy
11
+
12
+ Active Resource attempts to provide a coherent wrapper object-relational mapping for REST
13
+ web services. It follows the same philosophy as Active Record, in that one of its prime aims
14
+ is to reduce the amount of code needed to map to these resources. This is made possible
15
+ by relying on a number of code- and protocol-based conventions that make it easy for Active Resource
16
+ to infer complex relations and structures. These conventions are outlined in detail in the documentation
17
+ for ActiveResource::Base.
18
+
19
+ == Overview
20
+
21
+ Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database
22
+ tables. When a request is made to a remote resource, a REST JSON request is generated, transmitted, and the result
23
+ received and serialized into a usable Ruby object.
24
+
25
+ == Download and installation
26
+
27
+ The latest version of Active Resource can be installed with RubyGems:
28
+
29
+ % [sudo] gem install activeresource-five
30
+
31
+ Or added to a Gemfile:
32
+
33
+ gem 'activeresource-five'
34
+
35
+ For compatibility with Rails 5, use:
36
+
37
+ gem 'activeresource-five', github: 'rails/activeresource', branch: 'master'
38
+
39
+ Source code can be downloaded on GitHub
40
+
41
+ * https://github.com/rails/activeresource/tree/master/activeresource
42
+
43
+ === Configuration and Usage
44
+
45
+ Putting Active Resource to use is very similar to Active Record. It's as simple as creating a model class
46
+ that inherits from ActiveResource::Base and providing a <tt>site</tt> class variable to it:
47
+
48
+ class Person < ActiveResource::Base
49
+ self.site = "http://api.people.com:3000"
50
+ end
51
+
52
+ Now the Person class is REST enabled and can invoke REST services very similarly to how Active Record invokes
53
+ life cycle methods that operate against a persistent store.
54
+
55
+ # Find a person with id = 1
56
+ tyler = Person.find(1)
57
+ Person.exists?(1) # => true
58
+
59
+ As you can see, the methods are quite similar to Active Record's methods for dealing with database
60
+ records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records).
61
+
62
+ ==== Authentication
63
+
64
+ Active Resource supports the token based authentication provided by Rails through the <tt>ActionController::HttpAuthentication::Token</tt> class using custom headers.
65
+
66
+ class Person < ActiveResource::Base
67
+ self.headers['Authorization'] = 'Token token="abcd"'
68
+ end
69
+
70
+ You can also set any specific HTTP header using the same way.
71
+
72
+ ==== Protocol
73
+
74
+ Active Resource is built on a standard JSON or XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing
75
+ built into Action Controller but will also work with any other REST service that properly implements the protocol.
76
+ REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification:
77
+
78
+ * GET requests are used for finding and retrieving resources.
79
+ * POST requests are used to create new resources.
80
+ * PUT requests are used to update existing resources.
81
+ * DELETE requests are used to delete resources.
82
+
83
+ For more information on how this protocol works with Active Resource, see the ActiveResource::Base documentation;
84
+ for more general information on REST web services, see the article here[http://en.wikipedia.org/wiki/Representational_State_Transfer].
85
+
86
+ ==== Find
87
+
88
+ Find requests use the GET method and expect the JSON form of whatever resource/resources is/are being requested. So,
89
+ for a request for a single element, the JSON of that item is expected in response:
90
+
91
+ # Expects a response of
92
+ #
93
+ # {"id":1,"first":"Tyler","last":"Durden"}
94
+ #
95
+ # for GET http://api.people.com:3000/people/1.json
96
+ #
97
+ tyler = Person.find(1)
98
+
99
+ The JSON document that is received is used to build a new object of type Person, with each
100
+ JSON element becoming an attribute on the object.
101
+
102
+ tyler.is_a? Person # => true
103
+ tyler.last # => 'Durden'
104
+
105
+ Any complex element (one that contains other elements) becomes its own object:
106
+
107
+ # With this response:
108
+ # {"id":1,"first":"Tyler","address":{"street":"Paper St.","state":"CA"}}
109
+ #
110
+ # for GET http://api.people.com:3000/people/1.json
111
+ #
112
+ tyler = Person.find(1)
113
+ tyler.address # => <Person::Address::xxxxx>
114
+ tyler.address.street # => 'Paper St.'
115
+
116
+ Collections can also be requested in a similar fashion
117
+
118
+ # Expects a response of
119
+ #
120
+ # [
121
+ # {"id":1,"first":"Tyler","last":"Durden"},
122
+ # {"id":2,"first":"Tony","last":"Stark",}
123
+ # ]
124
+ #
125
+ # for GET http://api.people.com:3000/people.json
126
+ #
127
+ people = Person.all
128
+ people.first # => <Person::xxx 'first' => 'Tyler' ...>
129
+ people.last # => <Person::xxx 'first' => 'Tony' ...>
130
+
131
+ ==== Create
132
+
133
+ Creating a new resource submits the JSON form of the resource as the body of the request and expects
134
+ a 'Location' header in the response with the RESTful URL location of the newly created resource. The
135
+ id of the newly created resource is parsed out of the Location response header and automatically set
136
+ as the id of the ARes object.
137
+
138
+ # {"first":"Tyler","last":"Durden"}
139
+ #
140
+ # is submitted as the body on
141
+ #
142
+ # if include_root_in_json is not set or set to false => {"first":"Tyler"}
143
+ # if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
144
+ #
145
+ # POST http://api.people.com:3000/people.json
146
+ #
147
+ # when save is called on a new Person object. An empty response is
148
+ # is expected with a 'Location' header value:
149
+ #
150
+ # Response (201): Location: http://api.people.com:3000/people/2
151
+ #
152
+ tyler = Person.new(:first => 'Tyler')
153
+ tyler.new? # => true
154
+ tyler.save # => true
155
+ tyler.new? # => false
156
+ tyler.id # => 2
157
+
158
+ ==== Update
159
+
160
+ 'save' is also used to update an existing resource and follows the same protocol as creating a resource
161
+ with the exception that no response headers are needed -- just an empty response when the update on the
162
+ server side was successful.
163
+
164
+ # {"first":"Tyler"}
165
+ #
166
+ # is submitted as the body on
167
+ #
168
+ # if include_root_in_json is not set or set to false => {"first":"Tyler"}
169
+ # if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
170
+ #
171
+ # PUT http://api.people.com:3000/people/1.json
172
+ #
173
+ # when save is called on an existing Person object. An empty response is
174
+ # is expected with code (204)
175
+ #
176
+ tyler = Person.find(1)
177
+ tyler.first # => 'Tyler'
178
+ tyler.first = 'Tyson'
179
+ tyler.save # => true
180
+
181
+ ==== Delete
182
+
183
+ Destruction of a resource can be invoked as a class and instance method of the resource.
184
+
185
+ # A request is made to
186
+ #
187
+ # DELETE http://api.people.com:3000/people/1.json
188
+ #
189
+ # for both of these forms. An empty response with
190
+ # is expected with response code (200)
191
+ #
192
+ tyler = Person.find(1)
193
+ tyler.destroy # => true
194
+ tyler.exists? # => false
195
+ Person.delete(2) # => true
196
+ Person.exists?(2) # => false
197
+
198
+ ==== Associations
199
+
200
+ Relationships between resources can be declared using the standard association syntax
201
+ that should be familiar to anyone who uses activerecord. For example, using the
202
+ class definition below:
203
+
204
+ class Post < ActiveResource::Base
205
+ self.site = "http://blog.io"
206
+ has_many :comments
207
+ end
208
+
209
+ post = Post.find(1) # issues GET http://blog.io/posts/1.json
210
+ comments = post.comments # issues GET http://blog.io/posts/1/comments.json
211
+
212
+
213
+ If you control the server, you may wish to include nested resources thus avoiding a
214
+ second network request. Given the resource above, if the response includes comments
215
+ in the response, they will be automatically loaded into the activeresource object.
216
+ The server-side model can be adjusted as follows to include comments in the response.
217
+
218
+ class Post < ActiveRecord::Base
219
+ has_many :comments
220
+
221
+ def as_json(options)
222
+ super.merge(:include=>[:comments])
223
+ end
224
+ end
225
+
226
+ == License
227
+
228
+ Active Resource is released under the MIT license:
229
+
230
+ * http://www.opensource.org/licenses/MIT
231
+
232
+ == Contributing to Active Resource
233
+
234
+ Active Resource is work of many contributors. You're encouraged to submit pull requests, propose
235
+ features and discuss issues.
236
+
237
+ See {CONTRIBUTING}[https://github.com/rails/activeresource/blob/master/CONTRIBUTING.md].
238
+
239
+ == Support
240
+
241
+ API documentation is at
242
+
243
+ * http://rubydoc.info/gems/activeresource/4.0.0/frames
244
+
245
+ Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
246
+
247
+ * https://github.com/rails/activeresource/issues
248
+
249
+ You can find more usage information in the ActiveResource::Base documentation.
@@ -0,0 +1,44 @@
1
+ #--
2
+ # Copyright (c) 2006-2012 David Heinemeier Hansson
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'active_support'
25
+ require 'active_model'
26
+ require 'active_resource/exceptions'
27
+ require 'active_resource/version'
28
+
29
+ module ActiveResource
30
+ extend ActiveSupport::Autoload
31
+
32
+ autoload :Base
33
+ autoload :Callbacks
34
+ autoload :Connection
35
+ autoload :CustomMethods
36
+ autoload :Formats
37
+ autoload :HttpMock
38
+ autoload :Schema
39
+ autoload :Singleton
40
+ autoload :Validations
41
+ autoload :Collection
42
+ end
43
+
44
+ require 'active_resource/railtie' if defined?(Rails.application)
@@ -0,0 +1,175 @@
1
+ module ActiveResource::Associations
2
+
3
+ module Builder
4
+ autoload :Association, 'active_resource/associations/builder/association'
5
+ autoload :HasMany, 'active_resource/associations/builder/has_many'
6
+ autoload :HasOne, 'active_resource/associations/builder/has_one'
7
+ autoload :BelongsTo, 'active_resource/associations/builder/belongs_to'
8
+ end
9
+
10
+
11
+
12
+ # Specifies a one-to-many association.
13
+ #
14
+ # === Options
15
+ # [:class_name]
16
+ # Specify the class name of the association. This class name would
17
+ # be used for resolving the association class.
18
+ #
19
+ # ==== Example for [:class_name] - option
20
+ # GET /posts/123.json delivers following response body:
21
+ # {
22
+ # title: "ActiveResource now has associations",
23
+ # body: "Lorem Ipsum"
24
+ # comments: [
25
+ # {
26
+ # content: "..."
27
+ # },
28
+ # {
29
+ # content: "..."
30
+ # }
31
+ # ]
32
+ # }
33
+ # ====
34
+ #
35
+ # <tt>has_many :comments, :class_name => 'myblog/comment'</tt>
36
+ # Would resolve those comments into the <tt>Myblog::Comment</tt> class.
37
+ #
38
+ # If the response body does not contain an attribute matching the association name
39
+ # a request sent to the index action under the current resource.
40
+ # For the example above, if the comments are not present the requested path would be:
41
+ # GET /posts/123/comments.xml
42
+ def has_many(name, options = {})
43
+ Builder::HasMany.build(self, name, options)
44
+ end
45
+
46
+ # Specifies a one-to-one association.
47
+ #
48
+ # === Options
49
+ # [:class_name]
50
+ # Specify the class name of the association. This class name would
51
+ # be used for resolving the association class.
52
+ #
53
+ # ==== Example for [:class_name] - option
54
+ # GET /posts/1.json delivers following response body:
55
+ # {
56
+ # title: "ActiveResource now has associations",
57
+ # body: "Lorem Ipsum",
58
+ # author: {
59
+ # name: "Gabby Blogger",
60
+ # }
61
+ # }
62
+ # ====
63
+ #
64
+ # <tt>has_one :author, :class_name => 'myblog/author'</tt>
65
+ # Would resolve this author into the <tt>Myblog::Author</tt> class.
66
+ #
67
+ # If the response body does not contain an attribute matching the association name
68
+ # a request is sent to a singleton path under the current resource.
69
+ # For example, if a Product class <tt>has_one :inventory</tt> calling <tt>Product#inventory</tt>
70
+ # will generate a request on /products/:product_id/inventory.json.
71
+ #
72
+ def has_one(name, options = {})
73
+ Builder::HasOne.build(self, name, options)
74
+ end
75
+
76
+ # Specifies a one-to-one association with another class. This class should only be used
77
+ # if this class contains the foreign key.
78
+ #
79
+ # Methods will be added for retrieval and query for a single associated object, for which
80
+ # this object holds an id:
81
+ #
82
+ # [association(force_reload = false)]
83
+ # Returns the associated object. +nil+ is returned if the foreign key is +nil+.
84
+ # Throws a ActiveResource::ResourceNotFound exception if the foreign key is not +nil+
85
+ # and the resource is not found.
86
+ #
87
+ # (+association+ is replaced with the symbol passed as the first argument, so
88
+ # <tt>belongs_to :post</tt> would add among others <tt>post.nil?</tt>.
89
+ #
90
+ # === Example
91
+ #
92
+ # A Comment class declares <tt>belongs_to :post</tt>, which will add:
93
+ # * <tt>Comment#post</tt> (similar to <tt>Post.find(post_id)</tt>)
94
+ # The declaration can also include an options hash to specialize the behavior of the association.
95
+ #
96
+ # === Options
97
+ # [:class_name]
98
+ # Specify the class name for the association. Use it only if that name can't be inferred from association name.
99
+ # So <tt>belongs_to :post</tt> will by default be linked to the Post class, but if the real class name is Article,
100
+ # you'll have to specify it with this option.
101
+ # [:foreign_key]
102
+ # Specify the foreign key used for the association. By default this is guessed to be the name
103
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :post</tt>
104
+ # association will use "post_id" as the default <tt>:foreign_key</tt>. Similarly,
105
+ # <tt>belongs_to :article, :class_name => "Post"</tt> will use a foreign key
106
+ # of "article_id".
107
+ #
108
+ # Option examples:
109
+ # <tt>belongs_to :customer, :class_name => 'User'</tt>
110
+ # Creates a belongs_to association called customer which is represented through the <tt>User</tt> class.
111
+ #
112
+ # <tt>belongs_to :customer, :foreign_key => 'user_id'</tt>
113
+ # Creates a belongs_to association called customer which would be resolved by the foreign_key <tt>user_id</tt> instead of <tt>customer_id</tt>
114
+ #
115
+ def belongs_to(name, options={})
116
+ Builder::BelongsTo.build(self, name, options)
117
+ end
118
+
119
+ # Defines the belongs_to association finder method
120
+ def defines_belongs_to_finder_method(reflection)
121
+ method_name = reflection.name
122
+ ivar_name = :"@#{method_name}"
123
+
124
+ if method_defined?(method_name)
125
+ instance_variable_set(ivar_name, nil)
126
+ remove_method(method_name)
127
+ end
128
+
129
+ define_method(method_name) do
130
+ if instance_variable_defined?(ivar_name)
131
+ instance_variable_get(ivar_name)
132
+ elsif attributes.include?(method_name)
133
+ attributes[method_name]
134
+ elsif association_id = send(reflection.foreign_key)
135
+ instance_variable_set(ivar_name, reflection.klass.find(association_id))
136
+ end
137
+ end
138
+ end
139
+
140
+ def defines_has_many_finder_method(reflection)
141
+ method_name = reflection.name
142
+ ivar_name = :"@#{method_name}"
143
+
144
+ define_method(method_name) do
145
+ if instance_variable_defined?(ivar_name)
146
+ instance_variable_get(ivar_name)
147
+ elsif attributes.include?(method_name)
148
+ attributes[method_name]
149
+ elsif !new_record?
150
+ instance_variable_set(ivar_name, reflection.klass.find(:all, :params => {:"#{self.class.element_name}_id" => self.id}))
151
+ else
152
+ instance_variable_set(ivar_name, self.class.collection_parser.new)
153
+ end
154
+ end
155
+ end
156
+
157
+ # Defines the has_one association
158
+ def defines_has_one_finder_method(reflection)
159
+ method_name = reflection.name
160
+ ivar_name = :"@#{method_name}"
161
+
162
+ define_method(method_name) do
163
+ if instance_variable_defined?(ivar_name)
164
+ instance_variable_get(ivar_name)
165
+ elsif attributes.include?(method_name)
166
+ attributes[method_name]
167
+ elsif reflection.klass.respond_to?(:singleton_name)
168
+ instance_variable_set(ivar_name, reflection.klass.find(:params => {:"#{self.class.element_name}_id" => self.id}))
169
+ else
170
+ instance_variable_set(ivar_name, reflection.klass.find(:one, :from => "/#{self.class.collection_name}/#{self.id}/#{method_name}#{self.class.format_extension}"))
171
+ end
172
+ end
173
+ end
174
+
175
+ end