ActiveResourceKit  v1.2 (498.0)
 All Classes Files Functions Variables Typedefs Enumerator Properties Macros Pages
ARService.h
Go to the documentation of this file.
1 // ActiveResourceKit ARService.h
2 //
3 // Copyright © 2011, 2012, Roy Ratcliffe, Pioneering Software, United Kingdom
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the “Software”), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EITHER
16 // EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO
18 // EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
19 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22 //
23 //------------------------------------------------------------------------------
24 
25 #import <Foundation/Foundation.h>
27 
28 extern NSString *const ARFromKey;
29 extern NSString *const ARParamsKey;
30 
31 @class ARConnection;
32 @class ARResource;
33 @class ARHTTPResponse;
34 
35 /*!
36  * @brief Defines a completion handler block type where the results of
37  * completion yield a single resource.
38  * @details Successful completion gives a single instance.
39  */
40 typedef void (^ARResourceCompletionHandler)(ARHTTPResponse *response, ARResource *resource, NSError *error);
41 
42 /*!
43  * @brief Defines a type of completion handler block where successful completion
44  * yields an array of resources.
45  * @details The completion handler's @a resources argument equals @c nil when
46  * completion ends unsuccessfully. In this case, the second argument @a error
47  * supplies the reason.
48  */
49 typedef void (^ARResourcesCompletionHandler)(ARHTTPResponse *response, NSArray *resources, NSError *error);
50 
51 /*!
52  * @brief An active resource's service configuration.
53  * @details Defines an active resource's site, schema and known attributes,
54  * etc. Does not however define an active resource @e instance. Active resources
55  * binding to the same remote element at the same remote site associate with a
56  * common base.
57  *
58  * ARService corresponds to the singleton class aspects of @c
59  * ActiveResource::Base. Under Rails ActiveResource, the @c ActiveResource::Base
60  * singleton class carries the following state. See list below.
61  *
62  * - auth_type
63  * - collection_name
64  * - connection
65  * - element_name
66  * - headers
67  * - known_attributes
68  * - nospam
69  * - password
70  * - prefix_parameters
71  * - primary_key
72  * - proxy
73  * - schema
74  * - site
75  * - ssl_options
76  * - test
77  * - timeout
78  * - user
79  *
80  * This might help to define what ARService actually does, its purpose. ARService
81  * implements the anonymous singleton class behaviours belonging to
82  * @c ActiveResource::Base. ARResource defines the class for Active Resource
83  * instances, but ARService defines the class for Active Resource @e classes,
84  * singleton classes that is. Objective-C 2.0 does not provide anything
85  * comparable singleton classes. The Rails singleton class becomes the
86  * Objective-C “service” class.
87  *
88  * Singleton methods for @c ActiveResource::Base include the following.
89  *
90  * - all
91  * - auth_type
92  * - build
93  * - check_prefix_options
94  * - collection_name
95  * - collection_path
96  * - connection
97  * - create
98  * - create_proxy_uri_from
99  * - create_site_uri_from
100  * - delete
101  * - element_name
102  * - element_path
103  * - exists
104  * - find
105  * - find_every
106  * - find_one
107  * - find_single
108  * - first
109  * - format
110  * - headers
111  * - instantiate_collection
112  * - instantiate_record
113  * - known_attributes
114  * - last
115  * - new_element_path
116  * - password
117  * - prefix
118  * - prefix_parameters
119  * - prefix_source
120  * - primary_key
121  * - proxy
122  * - query_string
123  * - schema
124  * - site
125  * - split_options
126  * - ssl_options
127  * - timeout
128  * - user
129  *
130  * @par Lazy Getters
131  * Use a getter-lazily paradigm in place of actual lazy getting. This has
132  * advantages. First, it obviates the need for defining custom getters and
133  * setters. This is a useful thing, since the exact setter and getter
134  * implementations depend on the memory model being deployed. Automatic
135  * Reference Counting (ARC) has one requirement, garbage-collection another, and
136  * manual retain-release (MRR) another. Strategy employed here: Let the compiler
137  * synthesise the correct setters and getters accordingly. If you want to access
138  * the getter but with lazy initialisation, ask for propertyLazily.
139  */
140 @interface ARService : NSObject
141 {
143 }
144 
145 + (Class)defaultConnectionClass;
146 + (void)setDefaultConnectionClass:(Class)aClass;
147 
148 /*!
149  * @brief Initialises a new Active Resource Base instance using a given @a site.
150  * @param site An URL specifying the remote HTTP or HTTPS resource.
151  * @details This is @em not the designated initialiser but rather a shorthand
152  * for sending @c -init followed by @c -setSite:. The element name, if you do
153  * not subsequently assign one, derives from the @ref ARService sub-class
154  * name. This assumes that you derive ARService.
155  */
156 - (id)initWithSite:(NSURL *)site;
157 - (id)initWithSite:(NSURL *)site elementName:(NSString *)elementName;
158 
159 /*!
160  * @brief Derives a service for a subelement based on this service, representing
161  * nested resources.
162  * @details The new service shares the connection, if already
163  * configured. Otherwise, it constructs its own connection on-demand using the
164  * default.
165  */
166 - (ARService *)serviceForSubelementNamed:(NSString *)elementName;
167 
168 // The following properties use copy, in general, rather than retain. Why use
169 // copy? You can pass a mutable URL or string. The Active Resource retains a
170 // non-mutable copy.
171 //
172 // Note also that Rails implements some of the behaviours at class scope. For
173 // example, format is a class-scoped attribute in Rails. The Objective-C
174 // implementation here maps such behaviour to instances rather than classes.
175 
176 //-------------------------------------------------- Schema and Known Attributes
177 
178 @property(copy, NS_NONATOMIC_IOSONLY) NSDictionary *schema;
179 
180 /*!
181  * @brief Answers the known attributes, known because the resource server
182  * publishes them; although the server does not necessarily publish everything.
183  * @details Known attributes depend on schema. The result amounts to the
184  * schema's keys. The schema is a dictionary of attribute name-type pairs.
185  */
186 - (NSArray *)knownAttributes;
187 
188 //------------------------------------------------------------------------- Site
189 
190 /*!
191  * Always set up the site first. Other things depend on this essential
192  * property. You cannot fully operate the resource without the site URL. The
193  * site's path becomes the default prefix.
194  */
195 @property(copy, NS_NONATOMIC_IOSONLY) NSURL *site;
196 
197 /*!
198  * @brief Answers this service's site URL combined with an additional prefix
199  * parameter representing the element attached to this service.
200  * @details Useful for initialising a nested resource service. You can use the
201  * answer to build another service for accessing resources nested below this
202  * service element. You need to supply the super-resource identifier as a prefix
203  * option when accessing sub-resources, using the super-resource's foreign key
204  * as the option key.
205  */
206 - (NSURL *)siteWithPrefixParameter;
207 
208 //----------------------------------------------------------------------- Format
209 
210 @property(strong, NS_NONATOMIC_IOSONLY) id<ARFormat> format;
211 
212 // lazy getter
213 - (id<ARFormat>)formatLazily;
214 
215 //---------------------------------------------------------------------- Timeout
216 
217 @property(assign, NS_NONATOMIC_IOSONLY) NSTimeInterval timeout;
218 
219 //------------------------------------------------------------------- Connection
220 
221 // Lazy Getter
222 // ARService has no non-lazily getter.
223 - (ARConnection *)connectionLazily;
224 
225 /*!
226  * @details Setting the connection overwrites the given connection's site,
227  * format and time-out. You can easily reset any of these attributes afterwards,
228  * even though that defeats the purpose.
229  */
230 - (void)setConnection:(ARConnection *)connection;
231 
232 //---------------------------------------------------------------------- Headers
233 
234 /*!
235  * @note The @ref headers property exposes its implementation. This echoes the
236  * Rails interface where @c ActiveResource::Base subclasses derives a singleton
237  * class with a directly-accessible mutable hash called @c headers. If Person
238  * inherits from @c ActiveResource::Base, then an instance of Person exposes its
239  * mutable headers at @c person.class.headers.
240  */
241 @property(strong, NS_NONATOMIC_IOSONLY) NSMutableDictionary *headers;
242 
243 - (NSMutableDictionary *)headersLazily;
244 
245 //------------------------------------------------- Element and Collection Names
246 
247 // Setters and getters for element and collection name follow Rails
248 // semantics. You can set them but they also have default values which the class
249 // computes whenever you get the property without setting it first. The getter
250 // examines the current value and if not already set, sets up the default which
251 // typically accesses other values and possibly other defaults. This Rails-like
252 // ‘lazy getter’ approach means that you can always override defaults using the
253 // setter, either before accessing the getter or even afterwards, only provided
254 // that the property has remained unrequested.
255 
256 @property(copy, NS_NONATOMIC_IOSONLY) NSString *elementName;
257 @property(copy, NS_NONATOMIC_IOSONLY) NSString *collectionName;
258 
259 // lazy getters
260 - (NSString *)elementNameLazily;
261 - (NSString *)collectionNameLazily;
262 
263 //------------------------------------------------------ Primary and Foreign Key
264 
265 @property(copy, NS_NONATOMIC_IOSONLY) NSString *primaryKey;
266 
267 - (NSString *)primaryKeyLazily;
268 
269 /*!
270  * @brief Answers the element's foreign key.
271  * @details The foreign key equates to the element name followed by an
272  * underscore and finally the "id" string. This key can appear in URL paths as a
273  * prefix parameter, marked by a leading colon.
274  */
275 - (NSString *)foreignKey;
276 
277 //----------------------------------------------------------------------- Prefix
278 
279 /*!
280  * By default, the prefix source equals the site URL's path.
281  */
282 @property(copy, NS_NONATOMIC_IOSONLY) NSString *prefixSource;
283 
284 // lazy getter
285 - (NSString *)prefixSourceLazily;
286 
287 /*!
288  * Answers the prefix after translating the prefix parameters according to the
289  * given prefix-options dictionary. The options dictionary may be nil. In that
290  * case -prefixWithOptions: answers the prefix unmodified. This assumes that the
291  * prefix contains no untranslated prefix parameters. The method quietly fails
292  * if you do not provide mappings for all parameters. The prefix result will
293  * contain parameter placeholders.
294  */
295 - (NSString *)prefixWithOptions:(NSDictionary *)options;
296 
297 //------------------------------------------------------------------------ Paths
298 
299 - (NSString *)elementPathForID:(NSNumber *)ID prefixOptions:(NSDictionary *)prefixOptions queryOptions:(NSDictionary *)queryOptions;
300 - (NSString *)newElementPathWithPrefixOptions:(NSDictionary *)prefixOptions;
301 - (NSString *)collectionPathWithPrefixOptions:(NSDictionary *)prefixOptions queryOptions:(NSDictionary *)queryOptions;
302 
303 //------------------------------------------------------------- RESTful Services
304 
305 // The remote RESTful services offer the following basic actions.
306 //
307 // - build
308 // - create
309 // - find
310 // * every
311 // * single
312 // * one
313 // - delete
314 // - exists?
315 
316 /*!
317  * Asynchronously builds an Active Resource.
318  *
319  * Executes the completion handler on success or upon error. Completion handler
320  * arguments signal the outcome: non-nil attributes indicate successful
321  * completion. In such case, error always equals nil. There is no error.
322  */
323 - (void)buildWithAttributes:(NSDictionary *)attributes completionHandler:(ARResourceCompletionHandler)completionHandler;
324 
325 /*!
326  * @brief Creates a new resource instance, requesting that the remote service
327  * saves the new resource.
328  */
329 - (void)createWithAttributes:(NSDictionary *)attributes completionHandler:(ARResourceCompletionHandler)completionHandler;
330 
331 - (void)findAllWithOptions:(NSDictionary *)options completionHandler:(ARResourcesCompletionHandler)completionHandler;
332 
333 /*!
334  * Answers just the first resource in a collection of resources. Finds all the
335  * resources first, then extracts the first element. Acts as a convenience
336  * wrapper.
337  */
338 - (void)findFirstWithOptions:(NSDictionary *)options completionHandler:(ARResourceCompletionHandler)completionHandler;
339 
340 - (void)findLastWithOptions:(NSDictionary *)options completionHandler:(ARResourceCompletionHandler)completionHandler;
341 
342 /*!
343  * @brief Finds a single resource for a given identifier using the default URL.
344  *
345  * @par Ruby on Rails Comparison
346  * Under the Rails' ActiveResource gem, this method appears as a private
347  * method. Why not here? In Rails, you access the @c find_single(scope, options)
348  * method indirectly as the default @c find(arguments) case when the first scope
349  * argument does not match all, first, last or one (by symbol). Objective-C does
350  * not offer so flexible a syntax. Consequently, this implementation folds the
351  * find-scope interface into distinct methods: find all, find first, find last,
352  * find one and find single. The scope argument resolves to the method @e
353  * name. This approach carries advantages and disadvantages. It eliminates the
354  * @c switch statement necessary to resolve the scope. But at the same stroke
355  * eliminates the flexibility of parameterising the scope in cases where scope
356  * is a dynamic argument.
357  */
358 - (void)findSingleWithID:(NSNumber *)ID options:(NSDictionary *)options completionHandler:(ARResourceCompletionHandler)completionHandler;
359 
360 /*!
361  * @brief Finds a single resource from a one-off URL.
362  * @details This method expects you to provide the one-off URL using the @ref
363  * ARFromKey within the options dictionary. You must also specify query string
364  * parameters using the @ref ARParamsKey within the options.
365  */
366 - (void)findOneWithOptions:(NSDictionary *)options completionHandler:(ARResourceCompletionHandler)completionHandler;
367 
368 /*!
369  * @brief Deletes the resource with the given ID.
370  * @param ID Identifies the resource to delete.
371  * @param options All options specify prefix and query parameters.
372  * @param completionHandler Block to execute on success or failure.
373  */
374 - (void)deleteWithID:(NSNumber *)ID options:(NSDictionary *)options completionHandler:(void (^)(ARHTTPResponse *response, NSError *error))completionHandler;
375 
376 /*!
377  * @brief Asserts the existence of a resource.
378  * @param ID Identifies the resource to assert the existence of.
379  * @param options Specifies prefix and query parameters if any.
380  * @param completionHandler Block to execute on success or failure. The block's
381  * parameters include a response, a boolean equal to YES if a resource with the
382  * given ID really exists along with an error object describing the error if one
383  * occurs.
384  * @details The resource exists when the block receives @a exists argument equal
385  * to YES. This indicates a valid code-200 response from the remote
386  * service. Otherwise, when @a exists equals NO, either the resource does not
387  * exist or there was a communication error. When the remote service fails to
388  * locate the given resource, the completion handler receives an error object
389  * with an error code matching @ref ARResourceNotFoundErrorCode or @ref
390  * ARResourceGoneErrorCode (response codes 404 or 410, respectively).
391  *
392  * @note Asserting existence sends a HEAD request to the remote RESTful
393  * service. The standard Rails Rack converts the HEAD request to a GET but then
394  * strips the body from the response.
395  */
396 - (void)existsWithID:(NSNumber *)ID options:(NSDictionary *)options completionHandler:(void (^)(ARHTTPResponse *response, BOOL exists, NSError *error))completionHandler;
397 
398 @end