module Verdict module Storage class BaseStorage # Should store the assignments to allow quick lookups. # - Assignments should be unique on the combination of # `assignment.experiment.handle` and `assignment.subject_identifier`. # - The main property to store is `group.handle` # - Should return true if stored successfully. def store_assignment(assignment) hash = { group: assignment.handle, created_at: assignment.created_at.strftime('%FT%TZ') } set(assignment.experiment.handle.to_s, "assignment_#{assignment.subject_identifier}", JSON.dump(hash)) end # Should do a fast lookup of an assignment of the subject for the given experiment. # - Should return nil if not found in store # - Should return an Assignment instance otherwise. def retrieve_assignment(experiment, subject) subject_identifier = experiment.retrieve_subject_identifier(subject) if value = get(experiment.handle.to_s, "assignment_#{subject_identifier}") hash = JSON.parse(value) experiment.subject_assignment( subject, experiment.group(hash['group']), Time.xmlschema(hash['created_at']) ) end end # Should remove the subject from storage, so it will be reassigned later. def remove_assignment(experiment, subject) subject_identifier = experiment.retrieve_subject_identifier(subject) remove(experiment.handle.to_s, "assignment_#{subject_identifier}") end # Retrieves the start timestamp of the experiment def retrieve_start_timestamp(experiment) if timestamp = get(experiment.handle.to_s, 'started_at') Time.parse(timestamp) end end # Stores the timestamp on which the experiment was started def store_start_timestamp(experiment, timestamp) set(experiment.handle.to_s, 'started_at', timestamp.utc.strftime('%FT%TZ')) end # Deletes all assignments (and any other stored data) for the given experiment def cleanup(experiment_or_scope, options = {}) if experiment_or_scope.is_a?(Symbol) || experiment_or_scope.is_a?(String) Verdict.default_logger.warn( "Passing a scope string/symbol to #{self.class}#cleanup is deprecated, " \ 'pass a Verdict::Experiment instance instead.' ) clear(experiment_or_scope, options) else clear(experiment_or_scope.handle.to_s, options) end end protected # Retrieves a key in a given scope from storage. # - The scope and key are both provided as string. # - Should return a string value if the key is found in the scope, nil otherwise. # - Should raise Verdict::StorageError if anything goes wrong. def get(scope, key) raise NotImplementedError end # Sets the value of a key in a given scope from storage. # - The scope, key, and value are all provided as string. # - Should return true if the item was successfully stored. # - Should raise Verdict::StorageError if anything goes wrong. def set(scope, key, value) raise NotImplementedError end # Removes a key in a given scope from storage. # - The scope and key are both provided as string. # - Should return true if the item was successfully removed from storage. # - Should raise Verdict::StorageError if anything goes wrong. def remove(scope, key) raise NotImplementedError end # Removes all keys in a given scope from storage. # - The scope is provided as string. # - Should return true if all items were successfully removed from storage. # - Should raise Verdict::StorageError if anything goes wrong. def clear(scope, options) raise NotImplementedError end end end end