Class: CloudKit::Resource

Inherits:
Object show all
Defined in:
lib/cloudkit/store/resource.rb

Overview

A CloudKit::Resource represents a “resource” in the REST/HTTP sense of the word. It encapsulates a JSON document and its metadata such as its URI, ETag, Last-Modified date, remote user, and historical versions.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri, json, remote_user = nil, options = {}) ⇒ Resource

Initialize a new instance of a resource.

Parameters

  • uri - A CloudKit::URI for the resource or its parent collection.

  • json - A string representing a valid JSON object.

  • remote_user - Optional. The URI for the user creating the resource.

  • options - Optional. A hash of other internal properties to set, mostly for internal use.



17
18
19
20
21
22
# File 'lib/cloudkit/store/resource.rb', line 17

def initialize(uri, json, remote_user=nil, options={})
  load_from_options(options.merge(
    :uri         => uri,
    :json        => json,
    :remote_user => remote_user))
end

Instance Attribute Details

#etagObject (readonly)

Returns the value of attribute etag.



8
9
10
# File 'lib/cloudkit/store/resource.rb', line 8

def etag
  @etag
end

#jsonObject (readonly)

Returns the value of attribute json.



8
9
10
# File 'lib/cloudkit/store/resource.rb', line 8

def json
  @json
end

#last_modifiedObject (readonly)

Returns the value of attribute last_modified.



8
9
10
# File 'lib/cloudkit/store/resource.rb', line 8

def last_modified
  @last_modified
end

#remote_userObject (readonly)

Returns the value of attribute remote_user.



8
9
10
# File 'lib/cloudkit/store/resource.rb', line 8

def remote_user
  @remote_user
end

#uriObject (readonly)

Returns the value of attribute uri.



8
9
10
# File 'lib/cloudkit/store/resource.rb', line 8

def uri
  @uri
end

Class Method Details

.all(spec = {}) ⇒ Object

Find all resources with the given properties. Expects a hash specifying the search parameters. Returns an array of Resources.



144
145
146
147
148
149
150
# File 'lib/cloudkit/store/resource.rb', line 144

def self.all(spec={})
  CloudKit.storage_adapter.query { |q|
    spec.keys.each { |k|
      q.add_condition(k.to_s, :eql, escape(spec[k]))
    }
  }.reverse.map { |hash| build_from_hash(hash) }
end

.create(uri, json, remote_user = nil) ⇒ Object

Create a new resource. Intializes and saves in one step.

Parameters

  • uri - A CloudKit::URI for the resource or its parent collection.

  • json - A string representing a valid JSON object.

  • remote_user - Optional. The URI for the user creating the resource.



130
131
132
133
134
# File 'lib/cloudkit/store/resource.rb', line 130

def self.create(uri, json, remote_user=nil)
  resource = new(uri, json, remote_user)
  resource.save
  resource
end

.current(spec = {}) ⇒ Object

Find all current resources with the given properties. Expectes a hash specifying the search parameters. Returns an array of Resources.



138
139
140
# File 'lib/cloudkit/store/resource.rb', line 138

def self.current(spec={})
  all({:deleted => false, :archived => false}.merge(spec))
end

.first(spec) ⇒ Object

Find the first matching resource or nil. Expects a hash specifying the search parameters.



154
155
156
# File 'lib/cloudkit/store/resource.rb', line 154

def self.first(spec)
  all(spec)[0]
end

Instance Method Details

#archived?Boolean

Returns true if the resource is archived i.e. not the current version.

Returns:

  • (Boolean)


110
111
112
# File 'lib/cloudkit/store/resource.rb', line 110

def archived?
  @archived
end

#current?Boolean

Returns true if the resource is not archived and not deleted.

Returns:

  • (Boolean)


115
116
117
# File 'lib/cloudkit/store/resource.rb', line 115

def current?
  !@deleted && !@archived
end

#deleteObject

Delete the given resource. This is a soft delete, archiving the previous resource and inserting a deleted resource placeholder at the old URI. Raises HistoricalIntegrityViolation for attempts to delete resources that are not current.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/cloudkit/store/resource.rb', line 66

def delete
  raise HistoricalIntegrityViolation unless current?
  transaction do
    original_uri = @uri
    record = CloudKit.storage_adapter[@id]
    record['uri'] = "#{@uri.string}/versions/#{@etag}"
    record['archived'] = escape(true)
    @uri = wrap_uri(record['uri'])
    @archived = unescape(record['archived'])
    CloudKit.storage_adapter[@id] = record
    self.class.new(original_uri, @json, @remote_user, {:deleted => true}).save
  end
  reload
end

#deleted?Boolean

Returns true if the resource has been deleted.

Returns:

  • (Boolean)


105
106
107
# File 'lib/cloudkit/store/resource.rb', line 105

def deleted?
  @deleted
end

#parsed_jsonObject

Returns and caches the parsed JSON representation for this resource.



120
121
122
# File 'lib/cloudkit/store/resource.rb', line 120

def parsed_json
  @parsed_json ||= JSON.parse(@json)
end

#previous_versionObject

Returns the most recent previous version of the given resource.



100
101
102
# File 'lib/cloudkit/store/resource.rb', line 100

def previous_version
  @previous_version ||= previous_versions[0]
end

#previous_versionsObject

Returns all previous versions of a resource, reverse ordered by Last-Modified date.



95
96
97
# File 'lib/cloudkit/store/resource.rb', line 95

def previous_versions
  @previous_versions ||= versions[1..-1] rescue []
end

#saveObject

Save the resource. If this is a new resource with only a resource collection URI, its full URI will be generated. ETags and Last-Modified dates are also generated upon saving. No manual reloads are required.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/cloudkit/store/resource.rb', line 27

def save
  @id ||= '%064d' % CloudKit.storage_adapter.generate_unique_id
  @etag = UUID.generate unless @deleted
  @last_modified = Time.now.httpdate

  CloudKit.storage_adapter[@id] = {
    'uri'                  => @uri.cannonical_uri_string,
    'etag'                 => escape(@etag),
    'last_modified'        => @last_modified,
    'json'                 => @json,
    'deleted'              => escape(@deleted),
    'archived'             => escape(@archived),
    'remote_user'          => escape(@remote_user),
    'collection_reference' => @collection_reference ||= @uri.collection_uri_fragment,
    'resource_reference'   => @resource_reference ||= @uri.cannonical_uri_string
  }.merge(escape_values(parsed_json))
  reload
end

#update(json, remote_user = nil) ⇒ Object

Update the json and optionally the remote_user for the current resource. Automatically archives the previous version, generating new ETag and Last-Modified dates. Raises HistoricalIntegrityViolation for attempts to modify resources that are not current.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/cloudkit/store/resource.rb', line 50

def update(json, remote_user=nil)
  raise HistoricalIntegrityViolation unless current?
  transaction do
    record = CloudKit.storage_adapter[@id]
    record['uri'] = "#{@uri.string}/versions/#{@etag}"
    record['archived'] = escape(true)
    CloudKit.storage_adapter[@id] = record
    self.class.create(@uri, json, remote_user || @remote_user)
  end
  reload
end

#versionsObject

Returns all versions of the given resource, reverse ordered by Last-Modified date, including the current version of the resource.



83
84
85
86
87
88
89
90
91
# File 'lib/cloudkit/store/resource.rb', line 83

def versions
  # TODO make this a collection proxy, only loading the first, then the
  # rest as needed during iteration (possibly in chunks)
  return nil if @archived
  @versions ||= [self].concat(CloudKit.storage_adapter.query { |q|
    q.add_condition('resource_reference', :eql, @resource_reference)
    q.add_condition('archived', :eql, 'true')
  }.reverse.map { |hash| self.class.build_from_hash(hash) })
end