A functional storage interface with HTTP semantics and pluggable adapters.
Methods
public class
public instance
- delete
- get
- head
- http_methods
- implements?
- meta_methods
- methods_for_uri
- options
- post
- put
- resolve_uris
- resolved_resource_collection_methods
- resolved_version_collection_methods
- resource_collection_methods
- resource_methods
- resource_version_methods
- storage_adapter
- version
- version_collection_methods
protected instance
Included modules
Public class methods
Initialize a new Store. All resources in a Store are automatically versioned.
Options
- :collections - Array of resource collections to manage.
Example
store = CloudKit::Store.new(:collections => [:foos, :bars])
See also: Response
# File lib/cloudkit/store.rb, line 19 19: def initialize(options) 20: CloudKit.setup_storage_adapter unless CloudKit.storage_adapter 21: @collections = options[:collections] 22: end
Public instance methods
Delete the resource specified by the URI. Requires the :etag option.
# File lib/cloudkit/store.rb, line 109 109: def delete(uri, options={}) 110: methods = methods_for_uri(uri) 111: return status_405(methods) unless methods.include?('DELETE') 112: return invalid_entity_type unless @collections.include?(uri.collection_type) 113: return etag_required unless options[:etag] 114: resource = CloudKit::Resource.first(options.excluding(:etag).merge(:uri => uri.string)) 115: return status_404 unless (resource && (resource.remote_user == options[:remote_user])) 116: return status_410 if resource.deleted? 117: return status_412 if resource.etag != options[:etag] 118: 119: resource.delete 120: archived_resource = resource.previous_version 121: return json_meta_response(archived_resource.uri.string, archived_resource.etag, resource.last_modified) 122: end
Retrieve a resource or collection of resources based on a URI.
Parameters
- uri - URI of the resource or collection to retrieve.
- options - See below.
Options
- :remote_user - Optional. Scopes the dataset if provided.
- :limit - Optional. Default is unlimited. Limit the number of records returned by a collection request.
- :offset - Optional. Start the list of resources in a collection at offset (0-based).
- :any - Optional. Not a literal “:any”, but any key or keys that are top level JSON keys. This is a starting point for future JSONPath/JSONQuery support.
URI Types
/cloudkit-meta /{collection} /{collection}/_resolved /{collection}/{uuid} /{collection}/{uuid}/versions /{collection}/{uuid}/versions/_resolved /{collection}/{uuid}/versions/{etag}
Examples
get('/cloudkit-meta') get('/foos') get('/foos', :remote_user => 'coltrane') get('/foos', :limit => 100, :offset => 200) get('/foos/123') get('/foos/123/versions') get('/foos/123/versions/abc')
See also: REST API
# File lib/cloudkit/store.rb, line 56 56: def get(uri, options={}) 57: return invalid_entity_type if !valid_collection_type?(uri.collection_type) 58: return meta if uri.meta_uri? 59: return resource_collection(uri, options) if uri.resource_collection_uri? 60: return resolved_resource_collection(uri, options) if uri.resolved_resource_collection_uri? 61: return resource(uri, options) if uri.resource_uri? 62: return version_collection(uri, options) if uri.version_collection_uri? 63: return resolved_version_collection(uri, options) if uri.resolved_version_collection_uri? 64: return resource_version(uri, options) if uri.resource_version_uri? 65: status_404 66: end
Retrieve the same items as the get method, minus the content/body. Using this method on a single resource URI performs a slight optimization due to the way CloudKit stores its ETags and Last-Modified information on write.
# File lib/cloudkit/store.rb, line 72 72: def head(uri, options={}) 73: return invalid_entity_type unless @collections.include?(uri.collection_type) 74: if uri.resource_uri? || uri.resource_version_uri? 75: # ETag and Last-Modified are already stored for single items, so a slight 76: # optimization can be made for HEAD requests. 77: result = CloudKit::Resource.first(options.merge(:uri => uri.string)) 78: return status_404.head unless result 79: return status_410.head if result.deleted? 80: return response(200, '', result.etag, result.last_modified) 81: else 82: get(uri, options).head 83: end 84: end
Return the list of HTTP methods supported by this Store.
# File lib/cloudkit/store.rb, line 182 182: def http_methods 183: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS'] 184: end
Return true if this store implements a given HTTP method.
# File lib/cloudkit/store.rb, line 177 177: def implements?(http_method) 178: http_methods.include?(http_method.upcase) 179: end
Return the list of methods allowed for the cloudkit-meta URI.
# File lib/cloudkit/store.rb, line 142 142: def meta_methods 143: @meta_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') 144: end
Return a list of allowed methods for a given URI.
# File lib/cloudkit/store.rb, line 131 131: def methods_for_uri(uri) 132: return meta_methods if uri.meta_uri? 133: return resource_collection_methods if uri.resource_collection_uri? 134: return resolved_resource_collection_methods if uri.resolved_resource_collection_uri? 135: return resource_methods if uri.resource_uri? 136: return version_collection_methods if uri.version_collection_uri? 137: return resolved_version_collection_methods if uri.resolved_version_collection_uri? 138: return resource_version_methods if uri.resource_version_uri? 139: end
Build a response containing the allowed methods for a given URI.
# File lib/cloudkit/store.rb, line 125 125: def options(uri) 126: methods = methods_for_uri(uri) 127: allow(methods) 128: end
Create a resource in a given collection.
# File lib/cloudkit/store.rb, line 100 100: def post(uri, options={}) 101: methods = methods_for_uri(uri) 102: return status_405(methods) unless methods.include?('POST') 103: return invalid_entity_type unless @collections.include?(uri.collection_type) 104: return data_required unless options[:json] 105: create_resource(uri, options) 106: end
Update or create a resource at the specified URI. If the resource already exists, an :etag option is required.
# File lib/cloudkit/store.rb, line 88 88: def put(uri, options={}) 89: methods = methods_for_uri(uri) 90: return status_405(methods) unless methods.include?('PUT') 91: return invalid_entity_type unless @collections.include?(uri.collection_type) 92: return data_required unless options[:json] 93: current_resource = resource(uri, options.excluding(:json, :etag, :remote_user)) 94: return update_resource(uri, options) if current_resource.status == 200 95: return current_resource if current_resource.status == 410 96: create_resource(uri, options) 97: end
Return an array containing the response for each URI in a list.
# File lib/cloudkit/store.rb, line 187 187: def resolve_uris(uris) # TODO - remove if no longer needed 188: result = [] 189: uris.each do |uri| 190: result << get(uri) 191: end 192: result 193: end
Return the list of methods allowed on a resolved resource collection.
# File lib/cloudkit/store.rb, line 152 152: def resolved_resource_collection_methods 153: @resolved_resource_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') 154: end
Return the list of methods allowed on a resolved version history collection.
# File lib/cloudkit/store.rb, line 167 167: def resolved_version_collection_methods 168: @resolved_version_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') 169: end
Return the list of methods allowed for a resource collection.
# File lib/cloudkit/store.rb, line 147 147: def resource_collection_methods 148: @resource_collection_methods ||= http_methods.excluding('PUT', 'DELETE') 149: end
Return the list of methods allowed on an individual resource.
# File lib/cloudkit/store.rb, line 157 157: def resource_methods 158: @resource_methods ||= http_methods.excluding('POST') 159: end
Return the list of methods allowed on a resource version.
# File lib/cloudkit/store.rb, line 172 172: def resource_version_methods 173: @resource_version_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') 174: end
# File lib/cloudkit/store.rb, line 195 195: def storage_adapter 196: CloudKit.storage_adapter 197: end
Return the version number of this Store.
# File lib/cloudkit/store.rb, line 200 200: def version; 1; end
Return the list of methods allowed on a version history collection.
# File lib/cloudkit/store.rb, line 162 162: def version_collection_methods 163: @version_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') 164: end
Protected instance methods
Build an ETag for a collection. ETags are generated on write as an optimization for GETs. This method is used for collections of resources where the optimization is not practical.
# File lib/cloudkit/store.rb, line 340 340: def build_etag(data) 341: Digest::MD5.hexdigest(data.to_s) 342: end
Bundle a collection of results as a list of URIs for the response.
# File lib/cloudkit/store.rb, line 296 296: def bundle_collection_result(uri, options, result) 297: total = result.size 298: offset = options[:offset].try(:to_i) || 0 299: max = options[:limit] ? offset + options[:limit].to_i : total 300: list = result.to_a[offset...max].map{|r| r.uri} 301: json = uri_list(list, total, offset) 302: last_modified = result.first.try(:last_modified) if result.any? 303: response(200, json, build_etag(json), last_modified) 304: end
Bundle a collection of results as a list of documents and the associated metadata (last_modified, uri, etag) that would have accompanied a response to their singular request.
# File lib/cloudkit/store.rb, line 309 309: def bundle_resolved_collection_result(uri, options, result) 310: total = result.size 311: offset = options[:offset].try(:to_i) || 0 312: max = options[:limit] ? offset + options[:limit].to_i : total 313: list = result.to_a[offset...max] 314: json = resource_list(list, total, offset) 315: last_modified = result.first.last_modified if result.any? 316: response(200, json, build_etag(json), last_modified) 317: end
Create a resource at the specified URI.
# File lib/cloudkit/store.rb, line 277 277: def create_resource(uri, options) 278: JSON.parse(options[:json]) rescue (return status_422) 279: resource = CloudKit::Resource.create(uri, options[:json], options[:remote_user]) 280: json_create_response(resource.uri.string, resource.etag, resource.last_modified) 281: end
Return the list of collections managed by this Store.
# File lib/cloudkit/store.rb, line 205 205: def meta 206: json = JSON.generate(:uris => @collections.map{|t| "/#{t}"}) 207: response(200, json, build_etag(json)) 208: end
Return all documents and their associated metadata for the given collection URI.
# File lib/cloudkit/store.rb, line 223 223: def resolved_resource_collection(uri, options) 224: result = CloudKit::Resource.current( 225: options.excluding(:offset, :limit).merge( 226: :collection_reference => uri.collection_uri_fragment)) 227: bundle_resolved_collection_result(uri, options, result) 228: end
Return all document versions and their associated metadata for a given resource including the current version. Sorted by Last-Modified date in descending order.
# File lib/cloudkit/store.rb, line 257 257: def resolved_version_collection(uri, options) 258: found = CloudKit::Resource.first( 259: options.excluding(:offset, :limit).merge( 260: :uri => uri.current_resource_uri)) 261: return status_404 unless found 262: result = CloudKit::Resource.all( 263: options.excluding(:offset, :limit).merge( 264: :resource_reference => uri.current_resource_uri, 265: :deleted => false)) 266: bundle_resolved_collection_result(uri, options, result) 267: end
Return the resource for the given URI. Return 404 if not found or if protected and unauthorized, 410 if authorized but deleted.
# File lib/cloudkit/store.rb, line 232 232: def resource(uri, options) 233: if resource = CloudKit::Resource.first(options.merge!(:uri => uri.string)) 234: return status_410 if resource.deleted? 235: return response(200, resource.json, resource.etag, resource.last_modified) 236: end 237: status_404 238: end
Return a list of resource URIs for the given collection URI. Sorted by Last-Modified date in descending order.
# File lib/cloudkit/store.rb, line 212 212: def resource_collection(uri, options) 213: filter = options.excluding(:offset, :limit).merge( 214: :deleted => false, 215: :collection_reference => uri.collection_uri_fragment, 216: :archived => false) 217: result = CloudKit::Resource.current(filter) 218: bundle_collection_result(uri.string, options, result) 219: end
Generate a JSON document list.
# File lib/cloudkit/store.rb, line 325 325: def resource_list(list, total, offset) 326: results = [] 327: list.each do |resource| 328: results << { 329: :uri => resource.uri.string, 330: :etag => resource.etag, 331: :last_modified => resource.last_modified, 332: :document => resource.json} 333: end 334: JSON.generate(:total => total, :offset => offset, :documents => results) 335: end
Return a specific version of a resource.
# File lib/cloudkit/store.rb, line 270 270: def resource_version(uri, options) 271: result = CloudKit::Resource.first(options.merge(:uri => uri.string)) 272: return status_404 unless result 273: response(200, result.json, result.etag, result.last_modified) 274: end
Update the resource at the specified URI. Requires the :etag option.
# File lib/cloudkit/store.rb, line 284 284: def update_resource(uri, options) 285: JSON.parse(options[:json]) rescue (return status_422) 286: resource = CloudKit::Resource.first( 287: options.excluding(:json, :etag).merge(:uri => uri.string)) 288: return status_404 unless (resource && (resource.remote_user == options[:remote_user])) 289: return etag_required unless options[:etag] 290: return status_412 unless options[:etag] == resource.etag 291: resource.update(options[:json]) 292: return json_meta_response(uri.string, resource.etag, resource.last_modified) 293: end
Generate a JSON URI list.
# File lib/cloudkit/store.rb, line 320 320: def uri_list(list, total, offset) 321: JSON.generate(:total => total, :offset => offset, :uris => list.map { |u| u.string }) 322: end
Returns true if the collection type is valid for this Store.
# File lib/cloudkit/store.rb, line 345 345: def valid_collection_type?(collection_type) 346: @collections.include?(collection_type) || collection_type.to_s == 'cloudkit-meta' 347: end
Return a collection of URIs for all versions of a resource including the current version. Sorted by Last-Modified date in descending order.
# File lib/cloudkit/store.rb, line 242 242: def version_collection(uri, options) 243: found = CloudKit::Resource.first( 244: options.excluding(:offset, :limit).merge( 245: :uri => uri.current_resource_uri)) 246: return status_404 unless found 247: result = CloudKit::Resource.all( # TODO - just use found.versions 248: options.excluding(:offset, :limit).merge( 249: :resource_reference => uri.current_resource_uri, 250: :deleted => false)) 251: bundle_collection_result(uri.string, options, result) 252: end