Class: CloudKit::Store
- Includes:
- ResponseHelpers
- Defined in:
- lib/cloudkit/store.rb
Overview
A functional storage interface with HTTP semantics and pluggable adapters.
Instance Method Summary collapse
-
#collection_type(uri) ⇒ Object
Return the resource collection referenced by a URI.
-
#collection_uri_fragment(uri) ⇒ Object
Return the resource collection URI fragment.
-
#current_resource_uri(uri) ⇒ Object
Return the URI for the current version of a resource.
-
#delete(uri, options = {}) ⇒ Object
Delete the resource specified by the URI.
-
#get(uri, options = {}) ⇒ Object
Retrieve a resource or collection of resources based on a URI.
-
#head(uri, options = {}) ⇒ Object
Retrieve the same items as the get method, minus the content/body.
-
#http_methods ⇒ Object
Return the list of HTTP methods supported by this Store.
-
#implements?(http_method) ⇒ Boolean
Return true if this store implements a given HTTP method.
-
#initialize(options) ⇒ Store
constructor
Initialize a new Store, creating its schema if needed.
-
#meta_methods ⇒ Object
Return the list of methods allowed for the cloudkit-meta URI.
-
#meta_uri?(uri) ⇒ Boolean
Returns true if URI matches /cloudkit-meta.
-
#methods_for_uri(uri) ⇒ Object
Return a list of allowed methods for a given URI.
-
#options(uri) ⇒ Object
Build a response containing the allowed methods for a given URI.
-
#post(uri, options = {}) ⇒ Object
Create a resource in a given collection.
-
#put(uri, options = {}) ⇒ Object
Update or create a resource at the specified URI.
-
#reset! ⇒ Object
Clear all contents of the store.
-
#resolve_uris(uris) ⇒ Object
Return an array containing the response for each URI in a list.
-
#resolved_resource_collection_methods ⇒ Object
Return the list of methods allowed on a resolved resource collection.
-
#resolved_resource_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/_resolved.
-
#resolved_version_collection_methods ⇒ Object
Return the list of methods allowed on a resolved version history collection.
-
#resolved_version_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions/_resolved.
-
#resource_collection_methods ⇒ Object
Return the list of methods allowed for a resource collection.
-
#resource_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection.
-
#resource_methods ⇒ Object
Return the list of methods allowed on an individual resource.
-
#resource_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid.
-
#resource_version_methods ⇒ Object
Return the list of methods allowed on a resource version.
-
#resource_version_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions/etag.
-
#uri_components(uri) ⇒ Object
Splits a URI into its components.
-
#version ⇒ Object
Return the version number of this Store.
-
#version_collection_methods ⇒ Object
Return the list of methods allowed on a version history collection.
-
#version_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions.
-
#view_uri?(uri) ⇒ Boolean
Returns true if URI matches /#view.
Methods included from ResponseHelpers
#allow, #data_required, #etag_required, #internal_server_error, #invalid_entity_type, #json_error, #json_error_response, #json_meta_response, #response, #status_404, #status_405, #status_410, #status_412, #status_422
Constructor Details
#initialize(options) ⇒ Store
Initialize a new Store, creating its schema if needed. All resources in a Store are automatically versioned.
Options
-
:adapter - Optional. An instance of Adapter. Defaults to in-memory SQLite.
-
:collections - Array of resource collections to manage.
-
:views - Optional. Array of views to be updated based on JSON content.
Example
store = CloudKit::Store.new(:collections => [:foos, :bars])
Example
adapter = CloudKit::SQLAdapter.new('mysql://user:pass@localhost/my_db')
fruit_color_view = CloudKit::ExtractionView.new(
:fruits_by_color_and_season,
:observe => :fruits,
:extract => [:color, :season])
store = CloudKit::Store.new(
:adapter => adapter,
:collections => [:foos, :fruits],
:views => [fruit_color_view])
See also: Adapter, ExtractionView, Response
31 32 33 34 35 36 |
# File 'lib/cloudkit/store.rb', line 31 def initialize() @db = [:adapter] || SQLAdapter.new @collections = [:collections] @views = [:views] @views.each {|view| view.initialize_storage(@db)} if @views end |
Instance Method Details
#collection_type(uri) ⇒ Object
Return the resource collection referenced by a URI. Example: collection_type(‘/foos/123’) => :foos
232 233 234 |
# File 'lib/cloudkit/store.rb', line 232 def collection_type(uri) uri_components(uri)[0].to_sym rescue nil end |
#collection_uri_fragment(uri) ⇒ Object
Return the resource collection URI fragment. Example: collection_uri_fragment(‘/foos/123’) => ‘/foos
226 227 228 |
# File 'lib/cloudkit/store.rb', line 226 def collection_uri_fragment(uri) "/#{uri_components(uri)[0]}" rescue nil end |
#current_resource_uri(uri) ⇒ Object
Return the URI for the current version of a resource. Example: current_resource_uri(‘/foos/123/versions/abc’) => ‘/foos/123’
238 239 240 |
# File 'lib/cloudkit/store.rb', line 238 def current_resource_uri(uri) "/#{uri_components(uri)[0..1].join('/')}" rescue nil end |
#delete(uri, options = {}) ⇒ Object
Delete the resource specified by the URI. Requires the :etag option.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/cloudkit/store.rb', line 132 def delete(uri, ={}) methods = methods_for_uri(uri) return status_405(methods) unless methods.include?('DELETE') return invalid_entity_type unless @collections.include?(collection_type(uri)) return etag_required unless [:etag] original = @db[CLOUDKIT_STORE]. filter(.excluding(:etag).merge(:uri => uri)) if original.any? item = original.first return status_404 unless item[:remote_user] == [:remote_user] return status_410 if item[:deleted] return status_412 if item[:etag] != [:etag] version_uri = '' @db.transaction do version_uri = "#{item[:uri]}/versions/#{item[:etag]}" original.update(:uri => version_uri) @db[CLOUDKIT_STORE].insert( :uri => item[:uri], :collection_reference => item[:collection_reference], :resource_reference => item[:resource_reference], :remote_user => item[:remote_user], :content => item[:content], :deleted => true) unmap(uri) end return (200, version_uri, item[:etag], item[:last_modified]) end status_404 end |
#get(uri, options = {}) ⇒ Object
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 defined as extrations from a view.
URI Types
/cloudkit-meta
/{collection}
/{collection}/_resolved
/{collection}/{uuid}
/{collection}/{uuid}/versions
/{collection}/{uuid}/versions/_resolved
/{collection}/{uuid}/versions/{etag}
/{view}
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')
get('/shiny_foos', :color => 'green')
See also: REST API
72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/cloudkit/store.rb', line 72 def get(uri, ={}) return invalid_entity_type if !valid_collection_type?(collection_type(uri)) return if (uri) return resource_collection(uri, ) if resource_collection_uri?(uri) return resolved_resource_collection(uri, ) if resolved_resource_collection_uri?(uri) return resource(uri, ) if resource_uri?(uri) return version_collection(uri, ) if version_collection_uri?(uri) return resolved_version_collection(uri, ) if resolved_version_collection_uri?(uri) return resource_version(uri, ) if resource_version_uri?(uri) return view(uri, ) if view_uri?(uri) status_404 end |
#head(uri, options = {}) ⇒ Object
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.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/cloudkit/store.rb', line 89 def head(uri, ={}) return invalid_entity_type unless @collections.include?(collection_type(uri)) if resource_uri?(uri) || resource_version_uri?(uri) # ETag and Last-Modified are already stored for single items, so a slight # optimization can be made for HEAD requests. result = @db[CLOUDKIT_STORE]. select(:etag, :last_modified, :deleted). filter(.merge(:uri => uri)) if result.any? result = result.first return status_410.head if result[:deleted] return response(200, '', result[:etag], result[:last_modified]) end status_404.head else get(uri, ).head end end |
#http_methods ⇒ Object
Return the list of HTTP methods supported by this Store.
220 221 222 |
# File 'lib/cloudkit/store.rb', line 220 def http_methods ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS'] end |
#implements?(http_method) ⇒ Boolean
Return true if this store implements a given HTTP method.
215 216 217 |
# File 'lib/cloudkit/store.rb', line 215 def implements?(http_method) http_methods.include?(http_method.upcase) end |
#meta_methods ⇒ Object
Return the list of methods allowed for the cloudkit-meta URI.
180 181 182 |
# File 'lib/cloudkit/store.rb', line 180 def @meta_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') end |
#meta_uri?(uri) ⇒ Boolean
Returns true if URI matches /cloudkit-meta
248 249 250 251 |
# File 'lib/cloudkit/store.rb', line 248 def (uri) c = uri_components(uri) return c.size == 1 && c[0] == 'cloudkit-meta' end |
#methods_for_uri(uri) ⇒ Object
Return a list of allowed methods for a given URI.
169 170 171 172 173 174 175 176 177 |
# File 'lib/cloudkit/store.rb', line 169 def methods_for_uri(uri) return if (uri) return resource_collection_methods if resource_collection_uri?(uri) return resolved_resource_collection_methods if resolved_resource_collection_uri?(uri) return resource_methods if resource_uri?(uri) return version_collection_methods if version_collection_uri?(uri) return resolved_version_collection_methods if resolved_version_collection_uri?(uri) return resource_version_methods if resource_version_uri?(uri) end |
#options(uri) ⇒ Object
Build a response containing the allowed methods for a given URI.
163 164 165 166 |
# File 'lib/cloudkit/store.rb', line 163 def (uri) methods = methods_for_uri(uri) allow(methods) end |
#post(uri, options = {}) ⇒ Object
Create a resource in a given collection.
122 123 124 125 126 127 128 129 |
# File 'lib/cloudkit/store.rb', line 122 def post(uri, ={}) methods = methods_for_uri(uri) return status_405(methods) unless methods.include?('POST') return invalid_entity_type unless @collections.include?(collection_type(uri)) return data_required unless [:json] uri = "#{collection_uri_fragment(uri)}/#{UUID.generate}" create_resource(uri, ) end |
#put(uri, options = {}) ⇒ Object
Update or create a resource at the specified URI. If the resource already exists, an :etag option is required.
110 111 112 113 114 115 116 117 118 119 |
# File 'lib/cloudkit/store.rb', line 110 def put(uri, ={}) methods = methods_for_uri(uri) return status_405(methods) unless methods.include?('PUT') return invalid_entity_type unless @collections.include?(collection_type(uri)) return data_required unless [:json] current_resource = resource(uri, .excluding(:json, :etag, :remote_user)) return update_resource(uri, ) if current_resource.status == 200 return current_resource if current_resource.status == 410 create_resource(uri, ) end |
#reset! ⇒ Object
Clear all contents of the store. Used mostly for testing.
305 306 307 308 309 |
# File 'lib/cloudkit/store.rb', line 305 def reset! @db.schema.keys.each do |table| @db[table.gsub('`','').to_sym].delete end end |
#resolve_uris(uris) ⇒ Object
Return an array containing the response for each URI in a list.
296 297 298 299 300 301 302 |
# File 'lib/cloudkit/store.rb', line 296 def resolve_uris(uris) result = [] uris.each do |uri| result << get(uri) end result end |
#resolved_resource_collection_methods ⇒ Object
Return the list of methods allowed on a resolved resource collection.
190 191 192 |
# File 'lib/cloudkit/store.rb', line 190 def resolved_resource_collection_methods @resolved_resource_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') end |
#resolved_resource_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/_resolved
260 261 262 263 |
# File 'lib/cloudkit/store.rb', line 260 def resolved_resource_collection_uri?(uri) c = uri_components(uri) return c.size == 2 && @collections.include?(c[0].to_sym) && c[1] == '_resolved' end |
#resolved_version_collection_methods ⇒ Object
Return the list of methods allowed on a resolved version history collection.
205 206 207 |
# File 'lib/cloudkit/store.rb', line 205 def resolved_version_collection_methods @resolved_version_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') end |
#resolved_version_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions/_resolved
278 279 280 281 |
# File 'lib/cloudkit/store.rb', line 278 def resolved_version_collection_uri?(uri) c = uri_components(uri) return c.size == 4 && @collections.include?(c[0].to_sym) && c[2] == 'versions' && c[3] == '_resolved' end |
#resource_collection_methods ⇒ Object
Return the list of methods allowed for a resource collection.
185 186 187 |
# File 'lib/cloudkit/store.rb', line 185 def resource_collection_methods @resource_collection_methods ||= http_methods.excluding('PUT', 'DELETE') end |
#resource_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection
254 255 256 257 |
# File 'lib/cloudkit/store.rb', line 254 def resource_collection_uri?(uri) c = uri_components(uri) return c.size == 1 && @collections.include?(c[0].to_sym) end |
#resource_methods ⇒ Object
Return the list of methods allowed on an individual resource.
195 196 197 |
# File 'lib/cloudkit/store.rb', line 195 def resource_methods @resource_methods ||= http_methods.excluding('POST') end |
#resource_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid
266 267 268 269 |
# File 'lib/cloudkit/store.rb', line 266 def resource_uri?(uri) c = uri_components(uri) return c.size == 2 && @collections.include?(c[0].to_sym) && c[1] != '_resolved' end |
#resource_version_methods ⇒ Object
Return the list of methods allowed on a resource version.
210 211 212 |
# File 'lib/cloudkit/store.rb', line 210 def resource_version_methods @resource_version_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') end |
#resource_version_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions/etag
284 285 286 287 |
# File 'lib/cloudkit/store.rb', line 284 def resource_version_uri?(uri) c = uri_components(uri) return c.size == 4 && @collections.include?(c[0].to_sym) && c[2] == 'versions' && c[3] != '_resolved' end |
#uri_components(uri) ⇒ Object
Splits a URI into its components
243 244 245 |
# File 'lib/cloudkit/store.rb', line 243 def uri_components(uri) uri.split('/').reject{|x| x == '' || x == nil} rescue [] end |
#version ⇒ Object
Return the version number of this Store.
312 |
# File 'lib/cloudkit/store.rb', line 312 def version; 1; end |
#version_collection_methods ⇒ Object
Return the list of methods allowed on a version history collection.
200 201 202 |
# File 'lib/cloudkit/store.rb', line 200 def version_collection_methods @version_collection_methods ||= http_methods.excluding('POST', 'PUT', 'DELETE') end |
#version_collection_uri?(uri) ⇒ Boolean
Returns true if URI matches /collection/uuid/versions
272 273 274 275 |
# File 'lib/cloudkit/store.rb', line 272 def version_collection_uri?(uri) c = uri_components(uri) return c.size == 3 && @collections.include?(c[0].to_sym) && c[2] == 'versions' end |
#view_uri?(uri) ⇒ Boolean
Returns true if URI matches /#view
290 291 292 293 |
# File 'lib/cloudkit/store.rb', line 290 def view_uri?(uri) c = uri_components(uri) return c.size == 1 && @views && @views.map{|v| v.name}.include?(c[0].to_sym) end |