activeresource-five 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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