Class: ObjectifiedSessions::Base
- Inherits:
-
Object
- Object
- ObjectifiedSessions::Base
- Defined in:
- lib/objectified_sessions/base.rb
Overview
ObjectifiedSessions::Base is the base class for all objectified sessions – in other words, all classes that actually implement an objectified session must inherit from this class. It therefore contains the methods that allow you to define new fields, set various options (like #unknown_fields and #default_visibility), and so on.
Most functionality here is actually implemented on the class itself (the class << self block below), as most of the functionality has to do with defining which fields exist, how they should behave, and so on. Behavior for an actual instance is smaller, and largely limited to reading and writing data from fields, as most such access comes through dynamically-generated methods via the class.
Constant Summary collapse
- ALLOWED_ALLOWED_VALUE_TYPES =
%w{anything primitive_and_compound primitive}.map { |x| x.to_sym }
Class Method Summary collapse
-
._dynamic_methods_module ⇒ Object
Returns the dynamic-methods module.
-
._ensure_has_field_named(name) ⇒ Object
If this class doesn’t have an active field (not retired or inactive) with the given name, raises ObjectifiedSessions::Errors::NoSuchFieldError.
-
._field_named(name) ⇒ Object
Returns the FieldDefinition object with the given name, if any.
-
._field_with_storage_name(storage_name) ⇒ Object
Returns the FieldDefinition object that stores its data under the given key, if any.
-
.accessible_field_names ⇒ Object
What are the names of all fields that are accessible – that is, whose data can be accessed? This returns an array of field names, not storage names; retired fields and inactive fields don’t allow access to their data, so they won’t be included.
-
.allowed_value_types(allowed = nil) ⇒ Object
Sets the allowed value types on this class, or returns the current setting if no argument is supplied.
-
.default_visibility(new_visibility = nil) ⇒ Object
Sets the default visibility of new fields on this class.
-
.field(name, options = { }) ⇒ Object
Defines a new field.
-
.inactive(name, options = { }) ⇒ Object
Defines an inactive field.
-
.prefix(new_prefix = :__none_specified) ⇒ Object
Sets the prefix.
-
.retired(name, options = { }) ⇒ Object
Defines a retired field.
-
.unknown_fields(what_to_do = nil) ⇒ Object
Sets what to do with unknown fields.
Instance Method Summary collapse
-
#field_names ⇒ Object
A convenient alias for accessible_field_names, so you don’t have to go through the class.
-
#initialize(underlying_session) ⇒ Base
constructor
Creates a new instance.
-
#inspect(abbreviate = true) ⇒ Object
Make #inspect do the same as #to_s, so we also get this in debugging output.
-
#keys ⇒ Object
Returns the (possibly empty) set of all field names that actually have data present.
-
#to_s(abbreviate = true) ⇒ Object
Returns a nice, pretty string of the current set of values for this session.
Constructor Details
#initialize(underlying_session) ⇒ Base
Creates a new instance. underlying_session is the Rails session object – i.e., whatever is returned by calling #session in a controller. (The actual class of this object varies among Rails versions, but its behavior is identical for our purposes.)
This method also takes care of calling #_delete_unknown_fields_if_needed!, which, as its name suggests, is responsible for deleting any data that does not map to any known fields.
21 22 23 24 |
# File 'lib/objectified_sessions/base.rb', line 21 def initialize() @_base_underlying_session = _delete_unknown_fields_if_needed! end |
Class Method Details
._dynamic_methods_module ⇒ Object
Returns the dynamic-methods module. The dynamic-methods module is a new Module that is automatically included into the objectified-sessions class and given a reasonable name; it also has #define_method and #private made into public methods, so that it’s easy to define methods on it.
The dynamic-methods module is where we define all the accessor methods that #field generates. We do this instead of defining them directly on this class so that you can override them, and #super will still work properly.
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/objectified_sessions/base.rb', line 344 def _dynamic_methods_module @_dynamic_methods_module ||= begin out = Module.new do class << self public :define_method, :private end end remove_const(DYNAMIC_METHODS_MODULE_NAME) if const_defined?(DYNAMIC_METHODS_MODULE_NAME) const_set(DYNAMIC_METHODS_MODULE_NAME, out) include out out end end |
._ensure_has_field_named(name) ⇒ Object
If this class doesn’t have an active field (not retired or inactive) with the given name, raises ObjectifiedSessions::Errors::NoSuchFieldError. This is used as a guard to make sure we don’t try to retrieve data that hasn’t been defined as a field.
332 333 334 335 336 |
# File 'lib/objectified_sessions/base.rb', line 332 def _ensure_has_field_named(name) out = _field_named(name) out = nil if out && (! out.allow_access_to_data?) out || (raise ObjectifiedSessions::Errors::NoSuchFieldError.new(self, name)) end |
._field_named(name) ⇒ Object
Returns the FieldDefinition object with the given name, if any.
318 319 320 321 |
# File 'lib/objectified_sessions/base.rb', line 318 def _field_named(name) name = ObjectifiedSessions::FieldDefinition.normalize_name(name) @fields[name] end |
._field_with_storage_name(storage_name) ⇒ Object
Returns the FieldDefinition object that stores its data under the given key, if any.
324 325 326 327 |
# File 'lib/objectified_sessions/base.rb', line 324 def _field_with_storage_name(storage_name) storage_name = ObjectifiedSessions::FieldDefinition.normalize_name(storage_name).to_s @fields_by_storage_name[storage_name] end |
.accessible_field_names ⇒ Object
What are the names of all fields that are accessible – that is, whose data can be accessed? This returns an array of field names, not storage names; retired fields and inactive fields don’t allow access to their data, so they won’t be included.
309 310 311 312 313 314 315 |
# File 'lib/objectified_sessions/base.rb', line 309 def accessible_field_names if @fields @fields.values.select { |f| f.allow_access_to_data? }.map(&:name) else [ ] end end |
.allowed_value_types(allowed = nil) ⇒ Object
Sets the allowed value types on this class, or returns the current setting if no argument is supplied. The valid settings are:
- :anything
-
All values are allowed, including arbitrary Ruby objects.
- :primitive
-
Only primitive, simple scalars are allowed: nil, true, false, Strings, Symbols, Numerics (including both integer and floating-point numbers), and Times. Arrays and Hashes are not allowed.
- :primitive_and_compound
-
All primitive scalars, plus Arrays and Hashes composed entirely of primitive scalars, plus other Arrays and Hashes, are allowed.
258 259 260 261 262 263 264 265 266 267 |
# File 'lib/objectified_sessions/base.rb', line 258 def allowed_value_types(allowed = nil) if allowed allowed = allowed.to_s.strip.downcase.to_sym raise ArgumentError, "Invalid value for allowed_value_types: #{allowed.inspect}; we allow: #{ALLOWED_ALLOWED_VALUE_TYPES.inspect}" unless ALLOWED_ALLOWED_VALUE_TYPES.include?(allowed) @allowed_value_types = allowed end @allowed_value_types ||= :anything end |
.default_visibility(new_visibility = nil) ⇒ Object
Sets the default visibility of new fields on this class. This is ordinarily :public, meaning fields will generate accessor methods (e.g., #foo and #foo=) that are public unless you explicitly say :visibility => :private in the field definition. However, you can change it to :private, meaning fields will be private unless you explicitly specify :visibility => :public.
If called without an argument, returns the current default visibility for fields on this class.
236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/objectified_sessions/base.rb', line 236 def default_visibility(new_visibility = nil) if new_visibility if [ :public, :private ].include?(new_visibility) @default_visibility = new_visibility else raise ArgumentError, "Invalid default visibility: #{new_visibility.inspect}; must be :public or :private" end else @default_visibility ||= :public end end |
.field(name, options = { }) ⇒ Object
Defines a new field. name is the name of the field, specified as either a String or a Symbol. options can contain:
- :visibility
-
If
:private, methods generated for this field will be marked as private, meaning they can only be accessed from inside the objectified-session class itself. If:public, methods will be marked as public, making them accessible from anywhere. If omitted, the class’s #default_visibility will be used (which itself defaults to:public). - :storage
-
If specified, this field will be stored in the session under the given String or Symbol (which will be converted to a String before being used). If not specified, data will be stored under the name of the field (converted to a String), instead.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/objectified_sessions/base.rb', line 177 def field(name, = { }) @fields ||= { } @fields_by_storage_name ||= { } # Compute our effective options. = { :visibility => default_visibility }.merge() [:type] ||= :normal # Create a new FieldDefinition instance. new_field = ObjectifiedSessions::FieldDefinition.new(self, name, ) # Check for a conflict with the field name. if (other_field = @fields[new_field.name]) && (other_field != new_field) raise ObjectifiedSessions::Errors::DuplicateFieldNameError.new(self, new_field.name) end # Check for a conflict with the storage name. if (other_field = @fields_by_storage_name[new_field.storage_name]) && (other_field != new_field) raise ObjectifiedSessions::Errors::DuplicateFieldStorageNameError.new(self, other_field.name, new_field.name, new_field.storage_name) end @fields[new_field.name] = new_field @fields_by_storage_name[new_field.storage_name] = new_field end |
.inactive(name, options = { }) ⇒ Object
Defines an inactive field. An inactive field is identical to a retired field, except that, if you’ve set unknown_fields :delete, data from an inactive field will not be deleted. You can use it as a way of retiring a field that you no longer want to use from code, but whose data you still want preserved. (If you have not set unknown_fields :delete, then it behaves identically to a retired field.)
name is the name of the field; the only valid option for options is :storage. (:visibility is accepted but ignored, since no methods are generated for inactive fields.)
226 227 228 |
# File 'lib/objectified_sessions/base.rb', line 226 def inactive(name, = { }) field(name, .merge(:type => :inactive)) end |
.prefix(new_prefix = :__none_specified) ⇒ Object
Sets the prefix. If a prefix is set, then all field data is taken from (and stored into) a Hash bound to this prefix within the session, rather than directly in the session; this segregates all your ObjectifiedSession data from other usage of the session. This is not generally necessary, but can be useful in certain situations. Note that setting a prefix affects all fields, not just those defined after it’s set; the prefix is global to your objectified session, and you can only have a single prefix at once.
Perhaps obvious, but changing the prefix will effectively cause all your objectified-session data to disappear, as it’ll be stored under a different key. Choose once, at the beginning.
If called with no arguments, returns the current prefix.
279 280 281 282 283 284 285 286 287 |
# File 'lib/objectified_sessions/base.rb', line 279 def prefix(new_prefix = :__none_specified) if new_prefix == :__none_specified @prefix elsif new_prefix.kind_of?(String) || new_prefix.kind_of?(Symbol) || new_prefix == nil @prefix = if new_prefix then new_prefix.to_s.strip else nil end else raise ArgumentError, "Invalid prefix; must be a String or Symbol: #{new_prefix.inspect}" end end |
.retired(name, options = { }) ⇒ Object
Defines a retired field. A retired field is really nothing more than a marker indicating that you used to have a field with a given name (and, potentially, storage alias); you can’t access its data, and, if you’ve set unknown_fields :delete, any data will be deleted.
So, what’s the point? You will still get an error if you try to define another field with the same name, or storage alias. If you re-use a field, then, especially if you’re using Rails’ default CookieStore, you can run into awful problems where data from some previous usage is interpreted as being valid data for the new usage. Instead of simply removing fields when you’re done with them, make them retired (and move them to the bottom of the class, if you want, for better readability); this will have the same effect as removing them, but will keep you from accidentally reusing them in the future.
name is the name of the field; the only valid option for options is :storage. (:visibility is accepted but ignored, since no methods are generated for retired fields.)
215 216 217 |
# File 'lib/objectified_sessions/base.rb', line 215 def retired(name, = { }) field(name, .merge(:type => :retired)) end |
.unknown_fields(what_to_do = nil) ⇒ Object
Sets what to do with unknown fields. With :preserve, the default setting, any data residing under keys that aren’t defined as a field will simply be preserved, even as it’s inaccessible. With :delete, any data residing under keys that aren’t defined as a field will be deleted when your objectified session class is instantiated. Obviously, be careful if you set this to :delete; if you’re using traditional session access anywhere else in code, and you don’t duplicate its use as a field in your objectified session, really bad things will happen as the objectified session removes keys being used by other parts of the code. But it’s a very nice way to keep your session tidy, too.
296 297 298 299 300 301 302 303 304 |
# File 'lib/objectified_sessions/base.rb', line 296 def unknown_fields(what_to_do = nil) if what_to_do == nil @unknown_fields ||= :preserve elsif [ :delete, :preserve ].include?(what_to_do) @unknown_fields = what_to_do else raise ArgumentError, "You must pass :delete or :preserve, not: #{what_to_do.inspect}" end end |
Instance Method Details
#field_names ⇒ Object
A convenient alias for accessible_field_names, so you don’t have to go through the class.
27 28 29 |
# File 'lib/objectified_sessions/base.rb', line 27 def field_names self.class.accessible_field_names end |
#inspect(abbreviate = true) ⇒ Object
Make #inspect do the same as #to_s, so we also get this in debugging output.
52 53 54 |
# File 'lib/objectified_sessions/base.rb', line 52 def inspect(abbreviate = true) to_s(abbreviate) end |
#keys ⇒ Object
Returns the (possibly empty) set of all field names that actually have data present.
32 33 34 |
# File 'lib/objectified_sessions/base.rb', line 32 def keys field_names.select { |f| self[f] != nil } end |
#to_s(abbreviate = true) ⇒ Object
Returns a nice, pretty string of the current set of values for this session. We abbreviate long values by default, so that we don’t return some absurdly-long string.
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/objectified_sessions/base.rb', line 38 def to_s(abbreviate = true) out = "<#{self.class.name}: " out << keys.sort_by(&:to_s).map do |k| s = self[k].inspect s = s[0..36] + "..." if abbreviate && s.length > 40 "#{k}: #{s}" end.join(", ") out << ">" out end |