Class: RubyFFDB::Document
- Inherits:
-
Object
- Object
- RubyFFDB::Document
- Includes:
- Comparable
- Defined in:
- lib/rffdb/document.rb
Instance Attribute Summary collapse
-
#id ⇒ Object
readonly
Returns the value of attribute id.
Class Method Summary collapse
-
.all ⇒ DocumentCollection
Return all available instances of this type.
-
.attribute(name, options = {}) ⇒ Object
This DSL method is used to define the schema for a document.
-
.cache ⇒ CacheProvider
Allow direct access to the cache instance of this document class.
-
.cache_size(size) ⇒ Object
Sets the maximum number of entries the cache instance for this document will hold.
-
.engine(storage_engine, cache_opts = {}) ⇒ Object
This DSL method is used to setup the backend StorageEngine class and optionally the CacheProvider for this Document type.
-
.indexed_column?(column) ⇒ Boolean
Should this column be indexed?.
-
.load(id) ⇒ Object
Currently an alias for #new, but used as a wrapper in case more work needs to be done before pulling a document from the storage engine (such as sanitizing input, etc).
-
.reindex ⇒ Object
Reindex all documents of this type.
-
.storage ⇒ StorageEngine
A reference to the storage engine singleton of this document class.
-
.structure ⇒ Hash
A copy of the schema information for this class.
-
.where(attribute, value, comp_op = '==') ⇒ Object
Query for Documents based on an attribute.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Compare two documents.
-
#commit ⇒ Boolean
(also: #save)
Commit the document to storage.
-
#committed? ⇒ Boolean
Has this documented been committed to storage?.
-
#file_path ⇒ String
The location of the flat-file.
- #indexed_column?(column) ⇒ Boolean
-
#initialize(existing_id = false, lazy = true) ⇒ Document
constructor
A new instance of Document.
-
#method_missing(method, *args, &block) ⇒ Object
Uses the defined schema to setup getter and setter methods.
-
#refresh ⇒ Object
Overwrites the document’s data, either from disk or from cache.
-
#reload(force = false) ⇒ Object
Retrieve the stored data from disk, never using cache.
- #respond_to?(method) ⇒ Boolean
-
#storage ⇒ StorageEngine
A reference to the storage engine singleton of this document class.
-
#structure ⇒ Hash
A copy of the schema information for this class.
-
#touch ⇒ Object
Allow saving an already saved Document.
Constructor Details
#initialize(existing_id = false, lazy = true) ⇒ Document
Returns a new instance of Document.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/rffdb/document.rb', line 7 def initialize(existing_id = false, lazy = true) @read_lock = Mutex.new @write_lock = Mutex.new if existing_id @id = existing_id fail Exceptions::NoSuchDocument unless File.exist?(file_path) if lazy @lazy = true else reload(true) @lazy = false end @saved = true else @id = storage.next_id(self.class) @data = {} # relative to database root @saved = false end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
refactor and comment better
Uses the defined schema to setup getter and setter methods. Runs validations, format checking, and type checking on setting methods.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/rffdb/document.rb', line 258 def method_missing(method, *args, &block) setter = method.to_s.match(/(.*)=$/) ? true : false key = setter ? $1.to_sym : method.to_s.to_sym if structure.key?(key) && setter if args.last.is_a?(structure[key][:class]) && ( structure[key][:format].nil? || args.last.to_s.match(structure[key][:format]) ) valid = true if structure[key][:unique] == true fail Exceptions::NotUnique unless test_uniqueness(key, args.last) end structure[key][:validations].each do |validation| valid = send(validation.to_sym, args.last) fail Exceptions::FailedValidation unless valid end # here is where the lazy-loading happens refresh if @read_lock.synchronize { @lazy } && @read_lock.synchronize { committed? } @read_lock.synchronize do @write_lock.synchronize do if valid @data[key.to_s] = args.last end end end commit if indexed_column?(key) # indexed columns always cause commits else fail Exceptions::InvalidInput end @saved = false elsif structure.key?(key) # here is where the lazy-loading happens refresh if @read_lock.synchronize { @lazy } && @read_lock.synchronize { committed? } @read_lock.synchronize do @data[key.to_s] end else super end end |
Instance Attribute Details
#id ⇒ Object (readonly)
Returns the value of attribute id.
4 5 6 |
# File 'lib/rffdb/document.rb', line 4 def id @id end |
Class Method Details
.all ⇒ DocumentCollection
Return all available instances of this type
213 214 215 216 217 218 |
# File 'lib/rffdb/document.rb', line 213 def self.all DocumentCollection.new( storage.all(self).collect { |doc_id| load(doc_id) }, self ) end |
.attribute(name, options = {}) ⇒ Object
This DSL method is used to define the schema for a document. It sets up all data access for the class, and allows specifying strict checks on that schema during its use, such as validations, class types, regexp formatting, etc.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/rffdb/document.rb', line 125 def self.attribute(name, = {}) @structure ||= {} @structure[name.to_sym] = {} # setup the schema @structure[name.to_sym][:class] = .key?(:class) ? [:class] : Object @structure[name.to_sym][:format] = .key?(:format) ? [:format] : nil @structure[name.to_sym][:validations] = .key?(:validate) ? [*[:validate]] : [] @structure[name.to_sym][:unique] = .key?(:unique) == true ? true : false @structure[name.to_sym][:index] = .key?(:index) == true ? true : false end |
.cache ⇒ CacheProvider
Allow direct access to the cache instance of this document class
207 208 209 |
# File 'lib/rffdb/document.rb', line 207 def self.cache storage.cache(self) end |
.cache_size(size) ⇒ Object
Sets the maximum number of entries the cache instance for this document will hold. Note: this clears the current contents of the cache.
201 202 203 |
# File 'lib/rffdb/document.rb', line 201 def self.cache_size(size) storage.cache_size(self, size) end |
.engine(storage_engine, cache_opts = {}) ⇒ Object
This DSL method is used to setup the backend StorageEngine class and optionally the CacheProvider for this Document type.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/rffdb/document.rb', line 153 def self.engine(storage_engine, cache_opts = {}) unless storage_engine.instance_of?(Class) && storage_engine.ancestors.include?(StorageEngine) fail Exceptions::InvalidEngine end @engine = storage_engine if cache_opts.key?(:cache_provider) # Make sure the cache provider specified is valid unless cache_opts[:cache_provider].instance_of?(Class) && cache_opts[:cache_provider].ancestors.include?(CacheProvider) fail Exceptions::InvalidCacheProvider end @engine.cache_provider(self, cache_opts[:cache_provider]) end @engine.cache_size( self, cache_opts[:cache_size] ) if cache_opts.key?(:cache_size) end |
.indexed_column?(column) ⇒ Boolean
Should this column be indexed?
242 243 244 245 |
# File 'lib/rffdb/document.rb', line 242 def self.indexed_column?(column) csym = column.to_sym structure.key?(csym) && structure[csym][:index] == true end |
.load(id) ⇒ Object
Currently an alias for #new, but used as a wrapper in case more work needs to be done before pulling a document from the storage engine (such as sanitizing input, etc)
97 98 99 |
# File 'lib/rffdb/document.rb', line 97 def self.load(id) new(id) end |
.reindex ⇒ Object
Reindex all documents of this type. This can take a while on large DBs.
104 105 106 107 108 109 |
# File 'lib/rffdb/document.rb', line 104 def self.reindex storage.all(self).each do |doc_id| new(doc_id, false).touch.commit end return true end |
.storage ⇒ StorageEngine
Returns a reference to the storage engine singleton of this document class.
176 177 178 179 |
# File 'lib/rffdb/document.rb', line 176 def self.storage @engine ||= StorageEngines::YamlEngine @engine end |
.structure ⇒ Hash
Returns a copy of the schema information for this class.
188 189 190 191 |
# File 'lib/rffdb/document.rb', line 188 def self.structure @structure ||= {} @structure.dup end |
.where(attribute, value, comp_op = '==') ⇒ Object
Query for Documents based on an attribute
222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/rffdb/document.rb', line 222 def self.where(attribute, value, comp_op = '==') if indexed_column?(attribute) DocumentCollection.new( storage.index_lookup(self, attribute, value, comp_op).collect do |did| load(did) end, self ) else all.where(attribute, value, comp_op) end end |
Instance Method Details
#<=>(other) ⇒ Object
Compare two documents
236 237 238 |
# File 'lib/rffdb/document.rb', line 236 def <=>(other) id <=> other.id end |
#commit ⇒ Boolean Also known as: save
Commit the document to storage
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/rffdb/document.rb', line 36 def commit @read_lock.synchronize do @write_lock.synchronize do unless @saved storage.store(self.class, @id, @data.dup) end @saved = true end end end |
#committed? ⇒ Boolean
Has this documented been committed to storage?
51 52 53 |
# File 'lib/rffdb/document.rb', line 51 def committed? @saved end |
#file_path ⇒ String
The location of the flat-file
30 31 32 |
# File 'lib/rffdb/document.rb', line 30 def file_path storage.file_path(self.class, @id) end |
#indexed_column?(column) ⇒ Boolean
247 248 249 |
# File 'lib/rffdb/document.rb', line 247 def indexed_column?(column) self.class.send('indexed_column?'.to_sym, column) end |
#refresh ⇒ Object
Overwrites the document’s data, either from disk or from cache. Useful for lazy-loading and not typically used directly. Since data might have been pulled from cache, this can lead to bizarre things if not used carefully and things rely on #committed? or @saved.
79 80 81 82 83 84 |
# File 'lib/rffdb/document.rb', line 79 def refresh @write_lock.synchronize do @data = storage.retrieve(self.class, @id) @saved = true end end |
#reload(force = false) ⇒ Object
Retrieve the stored data from disk, never using cache. Allows forcing to overwrite uncommitted changes.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/rffdb/document.rb', line 59 def reload(force = false) if committed? || force @read_lock.synchronize do @write_lock.synchronize do @data = storage.retrieve(self.class, @id, false) end end else fail Exceptions::PendingChanges end @read_lock.synchronize do @write_lock.synchronize { @saved = true } end return self end |
#respond_to?(method) ⇒ Boolean
303 304 305 306 307 308 309 310 311 |
# File 'lib/rffdb/document.rb', line 303 def respond_to?(method) key = method.to_s.match(/(.*)=$/) ? $1.to_sym : method.to_s.to_sym if structure.key?(key) true else super end end |
#storage ⇒ StorageEngine
Returns a reference to the storage engine singleton of this document class.
183 184 185 |
# File 'lib/rffdb/document.rb', line 183 def storage self.class.send(:storage) end |
#structure ⇒ Hash
Returns a copy of the schema information for this class.
194 195 196 |
# File 'lib/rffdb/document.rb', line 194 def structure self.class.send(:structure) end |
#touch ⇒ Object
Allow saving an already saved Document. Useful for reindexing, maybe more.
87 88 89 90 91 92 |
# File 'lib/rffdb/document.rb', line 87 def touch @read_lock.synchronize do @write_lock.synchronize { @saved = false } end return self end |