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).
-
.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, comparison_method = '==') ⇒ 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.
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) 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 @read_lock = Mutex.new @write_lock = Mutex.new 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.
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 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 |
# File 'lib/rffdb/document.rb', line 239 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 storage.index(self.class, key).delete(@data[key.to_s], id) if indexed_column?(key) @data[key.to_s] = args.last storage.index(self.class, key).put(args.last, id) if indexed_column?(key) 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
196 197 198 199 200 201 |
# File 'lib/rffdb/document.rb', line 196 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.
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/rffdb/document.rb', line 108 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
190 191 192 |
# File 'lib/rffdb/document.rb', line 190 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.
184 185 186 |
# File 'lib/rffdb/document.rb', line 184 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.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/rffdb/document.rb', line 136 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?
223 224 225 226 |
# File 'lib/rffdb/document.rb', line 223 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)
88 89 90 |
# File 'lib/rffdb/document.rb', line 88 def self.load(id) new(id) end |
.storage ⇒ StorageEngine
Returns a reference to the storage engine singleton of this document class.
159 160 161 162 |
# File 'lib/rffdb/document.rb', line 159 def self.storage @engine ||= StorageEngines::YamlEngine @engine end |
.structure ⇒ Hash
Returns a copy of the schema information for this class.
171 172 173 174 |
# File 'lib/rffdb/document.rb', line 171 def self.structure @structure ||= {} @structure.dup end |
.where(attribute, value, comparison_method = '==') ⇒ Object
Query for Documents based on an attribute
205 206 207 208 209 210 211 212 213 214 |
# File 'lib/rffdb/document.rb', line 205 def self.where(attribute, value, comparison_method = '==') if comparison_method.to_s == '==' && indexed_column?(attribute) DocumentCollection.new( storage.index(self, attribute).get(value).collect { |did| load(did) }, self ) else all.where(attribute, value, comparison_method) end end |
Instance Method Details
#<=>(other) ⇒ Object
Compare two documents
217 218 219 |
# File 'lib/rffdb/document.rb', line 217 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
228 229 230 |
# File 'lib/rffdb/document.rb', line 228 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.
78 79 80 81 82 83 |
# File 'lib/rffdb/document.rb', line 78 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 |
# 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 end |
#respond_to?(method) ⇒ Boolean
286 287 288 289 290 291 292 293 294 |
# File 'lib/rffdb/document.rb', line 286 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.
166 167 168 |
# File 'lib/rffdb/document.rb', line 166 def storage self.class.send(:storage) end |
#structure ⇒ Hash
Returns a copy of the schema information for this class.
177 178 179 |
# File 'lib/rffdb/document.rb', line 177 def structure self.class.send(:structure) end |