Class: Treequel::Model
- Extended by:
- Loggability
- Includes:
- Constants, Constants::Patterns, SchemaValidations, Normalization
- Defined in:
- lib/treequel/model.rb
Overview
An object interface to LDAP entries.
Defined Under Namespace
Modules: ObjectClass, SchemaValidations Classes: Errors
Constant Summary collapse
- SET_HASH =
A prototype Hash that autovivifies its members as Sets, for use in the objectclass_registry and the base_registry
Hash.new {|h,k| h[k] = Set.new }
- BEFORE_HOOKS =
The hooks that are called before an action
[ :before_create, :before_update, :before_save, :before_destroy, :before_validation, ]
- AFTER_HOOKS =
The hooks that are called after an action
[ :after_initialize, :after_create, :after_update, :after_save, :after_destroy, :after_validation, ]
- HOOKS =
Hooks the user can override
BEFORE_HOOKS + AFTER_HOOKS
- DEFAULT_VALIDATION_OPTIONS =
Defaults for #validate options
{ :with_schema => true }
- DEFAULT_SAVE_OPTIONS =
Defaults for #save options
{ :raise_on_failure => true, :validate => true }
- DEFAULT_DESTROY_OPTIONS =
Defaults for #destroy options
{ :raise_on_failure => true, }
Constants included from Constants
Constants::CONTROL_NAMES, Constants::CONTROL_OIDS, Constants::EXTENSION_NAMES, Constants::EXTENSION_OIDS, Constants::FEATURE_NAMES, Constants::FEATURE_OIDS, Constants::MINIMAL_OPERATIONAL_ATTRIBUTES, Constants::SCOPE, Constants::SCOPE_NAME
Constants included from Constants::Patterns
Constants::Patterns::ALPHA, Constants::Patterns::AMPERSAND, Constants::Patterns::ASSERTIONVALUE, Constants::Patterns::ASTERISK, Constants::Patterns::ATTRIBUTE_TYPE, Constants::Patterns::ATTRIBUTE_TYPE_AND_VALUE, Constants::Patterns::ATTRIBUTE_VALUE, Constants::Patterns::BASE64_CHAR, Constants::Patterns::BASE64_STRING, Constants::Patterns::COLON, Constants::Patterns::COMMA, Constants::Patterns::DESCR, Constants::Patterns::DIGIT, Constants::Patterns::DISTINGUISHED_NAME, Constants::Patterns::DN_ESCAPED, Constants::Patterns::DOLLAR, Constants::Patterns::DOT, Constants::Patterns::DQUOTE, Constants::Patterns::DSTRING, Constants::Patterns::EQUALS, Constants::Patterns::ESC, Constants::Patterns::ESCAPED, Constants::Patterns::EXCLAMATION, Constants::Patterns::EXTENSIONS, Constants::Patterns::FILL, Constants::Patterns::FOLD, Constants::Patterns::HEX, Constants::Patterns::HEXPAIR, Constants::Patterns::HEXSTRING, Constants::Patterns::HYPHEN, Constants::Patterns::KEYCHAR, Constants::Patterns::KEYSTRING, Constants::Patterns::KIND, Constants::Patterns::LANGLE, Constants::Patterns::LCURLY, Constants::Patterns::LDAP_ATTRIBUTE_DESCRIPTION, Constants::Patterns::LDAP_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDAP_MATCHING_RULE_DESCRIPTION, Constants::Patterns::LDAP_MATCHING_RULE_USE_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_DESC_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_KIND_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_MISORDERED_SYNTAX_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDAP_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_SUBSTRING_FILTER, Constants::Patterns::LDAP_SUBSTRING_FILTER_VALUE, Constants::Patterns::LDAP_SYNTAX_DESCRIPTION, Constants::Patterns::LDAP_TRAILING_KIND_OBJECTCLASS_DESCRIPTION, Constants::Patterns::LDAP_UNESCAPE_SQUOTE_ATTRIBUTE_TYPE_DESCRIPTION, Constants::Patterns::LDIF_ATTRIBUTE_DESCRIPTION, Constants::Patterns::LDIF_ATTRIBUTE_TYPE, Constants::Patterns::LDIF_ATTRTYPE_OPTION, Constants::Patterns::LDIF_ATTRTYPE_OPTIONS, Constants::Patterns::LDIF_ATTRVAL_SPEC, Constants::Patterns::LDIF_ATTR_TYPE_CHARS, Constants::Patterns::LDIF_OPT_CHAR, Constants::Patterns::LDIF_SAFE_CHAR, Constants::Patterns::LDIF_SAFE_INIT_CHAR, Constants::Patterns::LDIF_SAFE_STRING, Constants::Patterns::LDIF_VALUE_SPEC, Constants::Patterns::LDIGIT, Constants::Patterns::LEADCHAR, Constants::Patterns::LEADKEYCHAR, Constants::Patterns::LEN, Constants::Patterns::LPAREN, Constants::Patterns::LUTF1, Constants::Patterns::MALFORMED_DSTRING, Constants::Patterns::MALFORMED_QDSTRING, Constants::Patterns::NOIDLEN, Constants::Patterns::NORMAL, Constants::Patterns::NUL, Constants::Patterns::NUMBER, Constants::Patterns::NUMERICOID, Constants::Patterns::OCTET, Constants::Patterns::OID, Constants::Patterns::OIDLIST, Constants::Patterns::OIDS, Constants::Patterns::PAIR, Constants::Patterns::PLUS, Constants::Patterns::QDESCR, Constants::Patterns::QDESCRLIST, Constants::Patterns::QDESCRS, Constants::Patterns::QDSTRING, Constants::Patterns::QDSTRINGLIST, Constants::Patterns::QDSTRINGS, Constants::Patterns::QQ, Constants::Patterns::QS, Constants::Patterns::QUOTED_DESCR, Constants::Patterns::QUOTED_NUMERICOID, Constants::Patterns::QUTF1, Constants::Patterns::QUTF8, Constants::Patterns::RANGLE, Constants::Patterns::RCURLY, Constants::Patterns::RELATIVE_DISTINGUISHED_NAME, Constants::Patterns::RPAREN, Constants::Patterns::SEMI, Constants::Patterns::SEP, Constants::Patterns::SHARP, Constants::Patterns::SP, Constants::Patterns::SPACE, Constants::Patterns::SPECIAL, Constants::Patterns::SQUOTE, Constants::Patterns::STRING, Constants::Patterns::STRINGCHAR, Constants::Patterns::SUTF1, Constants::Patterns::TILDE, Constants::Patterns::TRAILCHAR, Constants::Patterns::TUTF1, Constants::Patterns::UNESCAPED, Constants::Patterns::URI_REF, Constants::Patterns::USAGE, Constants::Patterns::USCORE, Constants::Patterns::UTF0, Constants::Patterns::UTF1, Constants::Patterns::UTF1SUBSET, Constants::Patterns::UTF2, Constants::Patterns::UTF3, Constants::Patterns::UTF4, Constants::Patterns::UTF8, Constants::Patterns::UTFMB, Constants::Patterns::VALUEENCODING, Constants::Patterns::VERTBAR, Constants::Patterns::WSP, Constants::Patterns::XSTRING
Constants included from SchemaValidations
SchemaValidations::IGNORED_OPERATIONAL_ATTRS
Constants inherited from Branch
Branch::DEFAULT_LDIF_WIDTH, Branch::LDIF_FOLD_SEPARATOR
Class Attribute Summary collapse
-
.base_registry ⇒ Object
readonly
Returns the value of attribute base_registry.
-
.objectclass_registry ⇒ Object
readonly
Returns the value of attribute objectclass_registry.
Instance Attribute Summary collapse
-
#values ⇒ Object
readonly
Unsaved attribute values hash.
Attributes inherited from Branch
Class Method Summary collapse
-
.directory ⇒ Object
Return the Treequel::Directory the Model will use for searches, creating it if it hasn’t been created already.
-
.directory=(newdirectory) ⇒ Object
Set the Treequel::Directory that should be used for searches.
-
.freeze_converted_values? ⇒ Boolean
Never freeze converted values in Model objects.
-
.inherited(subclass) ⇒ Object
Inheritance callback – add a class-specific objectclass registry to inheriting classes.
-
.mixins_for_dn(dn) ⇒ Object
Return the mixins that should be applied to an entry with the given
dn. -
.mixins_for_objectclasses(*objectclasses) ⇒ Object
Return the mixins that should be applied to an entry with the given
objectclasses. -
.new_from_entry(entry, directory) ⇒ Object
Create a new Treequel::Model object with the given
entryhash from the specifieddirectory. -
.register_mixin(mixin) ⇒ Object
Register the given
mixinfor the specifiedobjectclasses. -
.unregister_mixin(mixin) ⇒ Object
Unregister the given
mixinfor the specifiedobjectclasses.
Instance Method Summary collapse
-
#[]=(attrname, value) ⇒ Object
Index set operator – set attribute
attrnameto a newvalue. -
#delete(*attributes) ⇒ Object
Delete the specified attributes.
-
#destroy(opts = {}) ⇒ Object
Like #delete, but runs destroy hooks before and after deleting.
-
#diff_with_entry(attribute, values) ⇒ Object
Diff the specified
valuesfor the givenattributeagainst those in the directory entry and return LDAP::Mod objects for any differences. -
#errors ⇒ Object
Returns the validation errors associated with this object.
-
#extensions ⇒ Object
Return the Treequel::Model::ObjectClass mixins that have been applied to the receiver.
-
#initialize(directory, dn, entry = nil, from_directory = false) ⇒ Model
constructor
Override the default to extend new instances with applicable mixins if their entry is set.
-
#initialize_copy(other) ⇒ Object
Copy initializer – re-apply mixins to duplicates, too.
-
#inspect ⇒ Object
Return a human-readable representation of the receiving object, suitable for debugging.
-
#merge(attributes) ⇒ Object
Make the changes to the object specified by the given
attributes. -
#modification_ldif ⇒ Object
Return the pending modifications for the object as an LDIF string.
-
#modifications ⇒ Object
Return any pending changes in the model object as an Array of LDAP::Mod objects.
-
#modified? ⇒ Boolean
Tests whether the object has been modified since it was loaded from the directory.
-
#reset_dirty_flag ⇒ Object
Mark the object as unmodified.
-
#respond_to?(sym, include_priv = false) ⇒ Boolean
Returns
trueif the receiver responds to the given method. -
#revert ⇒ Object
Revert to the attributes in the directory, discarding any pending changes.
-
#save(opts = {}) ⇒ Object
Write any pending changes in the model object to the directory.
-
#search(scope = :subtree, filter = '(objectClass=*)', parameters = {}, &block) ⇒ Object
Override Branch#search to inject the ‘objectClass’ attribute to the selected attribute list if there is one.
-
#valid?(opts = {}) ⇒ Boolean
Return
trueif the model object passes all of its validations. -
#validate(options = {}) ⇒ Object
Validate the object with the specified
options, appending validation errors onto the #errors object.
Methods included from Normalization
Methods included from SchemaValidations
#validate_attribute_syntax, #validate_may_attributes, #validate_must_attributes, #validate_structural_objectclass
Methods inherited from Branch
#+, #<=>, #[], #branchset, #children, #copy, #entry, #eql?, #exists?, #get_child, #hash, #include_operational_attrs=, #loaded?, #may_attribute_types, #may_attributes_hash, #may_oids, #move, #must_attribute_types, #must_attributes_hash, #must_oids, #object_classes, #operational_attribute_oids, #operational_attribute_types, #parent, #parent_dn, #rdn, #rdn_attributes, #split_dn, #to_hash, #to_ldif, #to_ufn, #uri, #valid_attribute?, #valid_attribute_oids, #valid_attribute_type, #valid_attribute_types, #valid_attributes_hash, #values_at
Methods included from AttributeDeclarations
Methods included from Delegation
def_ivar_delegators, def_method_delegators
Methods included from HashUtilities
merge_recursively, normalize_attributes, stringify_keys, symbolify_keys
Constructor Details
#initialize(directory, dn, entry = nil, from_directory = false) ⇒ Model
Override the default to extend new instances with applicable mixins if their entry is set.
216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/treequel/model.rb', line 216 def initialize( directory, dn, entry=nil, from_directory=false ) if from_directory super( directory, dn, entry ) @dirty = false else super( directory, dn ) @values = symbolify_keys( entry ? entry : self.rdn_attributes ) @dirty = true end self.apply_applicable_mixins( @dn, @entry ) self.after_initialize end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args) ⇒ Object (protected)
Proxy method – Handle calls to missing methods by searching for an attribute.
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 |
# File 'lib/treequel/model.rb', line 631 def method_missing( sym, *args ) self.log.debug "Dynamic dispatch to %p with args: %p" % [ sym, args ] # First, if the entry hasn't yet been loaded, try loading it to make sure the # object is already extended with any applicable objectClass mixins. If that ends # up defining the method in question, call it. if (( meth = self.entry_method(sym) )) return meth.call( *args ) end # Next, super to rdn-traversal if it looks like a reader but has arguments plainsym, methodtype = attribute_from_method( sym ) return super if methodtype == :reader && !args.empty? # Now make a method body for a new method based on what attributeType it is if # it's a valid attribute attrtype = self.find_attribute_type( plainsym ) or return super methodbody = case methodtype when :writer self.make_writer( attrtype ) when :predicate self.make_predicate( attrtype ) else self.make_reader( attrtype ) end # Define the new method and call it by fetching the corresponding Method object # so we don't loop back through #method_missing if something goes wrong self.class.send( :define_method, sym, &methodbody ) return self.method( sym ).call( *args ) end |
Class Attribute Details
.base_registry ⇒ Object (readonly)
Returns the value of attribute base_registry.
84 85 86 |
# File 'lib/treequel/model.rb', line 84 def base_registry @base_registry end |
.objectclass_registry ⇒ Object (readonly)
Returns the value of attribute objectclass_registry.
83 84 85 |
# File 'lib/treequel/model.rb', line 83 def objectclass_registry @objectclass_registry end |
Instance Attribute Details
#values ⇒ Object (readonly)
Unsaved attribute values hash
246 247 248 |
# File 'lib/treequel/model.rb', line 246 def values @values end |
Class Method Details
.directory ⇒ Object
Return the Treequel::Directory the Model will use for searches, creating it if it hasn’t been created already. The default Directory will be created by calling Treequel.directory_from_config.
91 92 93 94 |
# File 'lib/treequel/model.rb', line 91 def self::directory self.directory = Treequel.directory_from_config unless @directory return @directory end |
.directory=(newdirectory) ⇒ Object
Set the Treequel::Directory that should be used for searches. The receiving class will also be set as the #results_class of the newdirectory.
99 100 101 102 |
# File 'lib/treequel/model.rb', line 99 def self::directory=( newdirectory ) @directory = newdirectory @directory.results_class = self if @directory end |
.freeze_converted_values? ⇒ Boolean
Never freeze converted values in Model objects.
193 |
# File 'lib/treequel/model.rb', line 193 def self::freeze_converted_values?; false; end |
.inherited(subclass) ⇒ Object
Inheritance callback – add a class-specific objectclass registry to inheriting classes.
106 107 108 109 110 |
# File 'lib/treequel/model.rb', line 106 def self::inherited( subclass ) super subclass.instance_variable_set( :@objectclass_registry, SET_HASH.dup ) subclass.instance_variable_set( :@base_registry, SET_HASH.dup ) end |
.mixins_for_dn(dn) ⇒ Object
Return the mixins that should be applied to an entry with the given dn.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/treequel/model.rb', line 176 def self::mixins_for_dn( dn ) dn_tuples = dn.downcase.split( /\s*,\s*/ ) dn_keys = dn_tuples.reverse.inject(['']) do |keys, dnpair| dnpair += ',' + keys.last unless keys.last.empty? keys << dnpair end # Get the union of all of the mixin sets for the DN and all of its parents union = self.base_registry. values_at( *dn_keys ). inject {|set1,set2| set1 | set2 } return union end |
.mixins_for_objectclasses(*objectclasses) ⇒ Object
Return the mixins that should be applied to an entry with the given objectclasses.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/treequel/model.rb', line 158 def self::mixins_for_objectclasses( *objectclasses ) return self.objectclass_registry[:top] if objectclasses.empty? ocsymbols = objectclasses.flatten.collect {|oc| oc.untaint.to_sym } # Get the union of all of the mixin sets for the objectclasses in question mixins = self.objectclass_registry. values_at( *ocsymbols ). inject {|set1,set2| set1 | set2 } # Return the mixins whose objectClass requirements are met by the # specified objectclasses return mixins.delete_if do |mixin| !mixin.model_objectclasses.all? {|oc| ocsymbols.include?(oc) } end end |
.new_from_entry(entry, directory) ⇒ Object
Create a new Treequel::Model object with the given entry hash from the specified directory. Overrides Treequel::Branch.new_from_entry to pass the from_directory flag to mark it as unmodified.
199 200 201 202 203 204 205 206 207 |
# File 'lib/treequel/model.rb', line 199 def self::new_from_entry( entry, directory ) entry = Treequel::HashUtilities.stringify_keys( entry ) dnvals = entry.delete( 'dn' ) or raise ArgumentError, "no 'dn' attribute for entry" Treequel.logger.debug "Creating %p from entry: %p in directory: %s" % [ self, dnvals.first, directory ] return self.new( directory, dnvals.first, entry, true ) end |
.register_mixin(mixin) ⇒ Object
Register the given mixin for the specified objectclasses. Instances that have all the specified objectclasses will be extended with the mixin, which should be a Module extended with Treequel::Model::ObjectClass.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/treequel/model.rb', line 116 def self::register_mixin( mixin ) objectclasses = mixin.model_objectclasses bases = mixin.model_bases bases << '' if bases.empty? Treequel.logger.debug "registering %p [objectClasses: %p, base DNs: %p]" % [ mixin, objectclasses, bases ] # Register it with each of its objectClasses objectclasses.each do |oc| @objectclass_registry[ oc.to_sym ].add( mixin ) end # ...and each of its bases bases.each do |dn| @base_registry[ dn.downcase ].add( mixin ) end end |
.unregister_mixin(mixin) ⇒ Object
Unregister the given mixin for the specified objectclasses.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/treequel/model.rb', line 137 def self::unregister_mixin( mixin ) objectclasses = mixin.model_objectclasses bases = mixin.model_bases bases << '' if bases.empty? Treequel.logger.debug "un-registering %p [objectclasses: %p, base DNs: %p]" % [ mixin, objectclasses, bases ] # Unregister it from each of its bases bases.each do |dn| @base_registry[ dn.downcase ].delete( mixin ) end # ...and each of its objectClasses objectclasses.each do |oc| @objectclass_registry[ oc.to_sym ].delete( mixin ) end end |
Instance Method Details
#[]=(attrname, value) ⇒ Object
Index set operator – set attribute attrname to a new value. Overridden to make Model objects defer writing changes until Treequel::Model#save is called.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/treequel/model.rb', line 274 def []=( attrname, value ) attrtype = self.find_attribute_type( attrname.to_sym ) or raise ArgumentError, "unknown attribute %p" % [ attrname ] value = Array( value ) unless attrtype.single? self.mark_dirty if value.nil? @values.delete( attrtype.name.to_sym ) else @values[ attrtype.name.to_sym ] = value end # If the objectClasses change, we (may) need to re-apply mixins if attrname.to_s.downcase == 'objectclass' self.log.debug " objectClass change -- reapplying mixins" self.apply_applicable_mixins( self.dn ) else self.log.debug " no objectClass changes -- no need to reapply mixins" end return value end |
#delete(*attributes) ⇒ Object
Delete the specified attributes. Overridden to make Model objects defer writing changes until Treequel::Model#save is called.
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/treequel/model.rb', line 311 def delete( *attributes ) return super if attributes.empty? self.log.debug "Deleting attributes: %p" % [ attributes ] self.mark_dirty attributes.flatten.each do |attribute| # With a hash, delete each value for each key if attribute.is_a?( Hash ) self.delete_specific_values( attribute ) # With an array of attributes to delete, replace # MULTIPLE attribute types with an empty array, and SINGLE # attribute types with nil elsif attribute.respond_to?( :to_sym ) attrtype = self.find_attribute_type( attribute.to_sym ) if attrtype.single? @values[ attribute.to_sym ] = nil else @values[ attribute.to_sym ] = [] end else raise ArgumentError, "can't convert a %p to a Symbol or a Hash" % [ attribute.class ] end end return true end |
#destroy(opts = {}) ⇒ Object
Like #delete, but runs destroy hooks before and after deleting.
496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/treequel/model.rb', line 496 def destroy( opts={} ) opts = DEFAULT_DESTROY_OPTIONS.merge( opts ) self.before_destroy or raise Treequel::BeforeHookFailed, :destroy self.delete self.after_destroy return true rescue Treequel::BeforeHookFailed => err self.log.info( err. ) raise if opts[:raise_on_failure] end |
#diff_with_entry(attribute, values) ⇒ Object
Diff the specified values for the given attribute against those in the directory entry and return LDAP::Mod objects for any differences.
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/treequel/model.rb', line 436 def diff_with_entry( attribute, values ) mods = [] attribute = attribute.to_s entry = self.entry || {} entry_values = entry.key?( attribute ) ? entry[attribute] : [] # Workaround for the fact that Time has a #to_ary, causing it to become an # Array of integers when cast via Array(). values = [ values ] if values.is_a?( Time ) values = Array( values ).compact. collect {|val| self.get_converted_attribute(attribute, val) } self.log.debug " comparing %s values to entry: %p vs. %p" % [ attribute, values, entry_values ] # If the attributes on the server are the same as the local ones, # it's a NOOP. if values.sort == entry_values.sort self.log.debug " no change." return nil # If the directory doesn't have this attribute, but the local # object does, it's an ADD elsif entry_values.empty? self.log.debug " ADD %s: %p" % [ attribute, values ] return LDAP::Mod.new( LDAP::LDAP_MOD_ADD, attribute, values ) # ...or if the local value doesn't have anything for this attribute # but the directory does, it's a DEL elsif values.empty? self.log.debug " DELETE %s" % [ attribute ] return LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, attribute ) # ...otherwise it's a REPLACE else self.log.debug " REPLACE %s: %p with %p" % [ attribute, entry_values, values ] return LDAP::Mod.new( LDAP::LDAP_MOD_REPLACE, attribute, values ) end end |
#errors ⇒ Object
Returns the validation errors associated with this object.
344 345 346 |
# File 'lib/treequel/model.rb', line 344 def errors return @errors ||= Treequel::Model::Errors.new end |
#extensions ⇒ Object
Return the Treequel::Model::ObjectClass mixins that have been applied to the receiver.
531 532 533 534 535 536 |
# File 'lib/treequel/model.rb', line 531 def extensions eigenclass = ( class << self; self; end ) return eigenclass.included_modules.find_all do |mod| (class << mod; self; end).include?(Treequel::Model::ObjectClass) end end |
#initialize_copy(other) ⇒ Object
Copy initializer – re-apply mixins to duplicates, too.
232 233 234 235 236 |
# File 'lib/treequel/model.rb', line 232 def initialize_copy( other ) super self.apply_applicable_mixins( @dn, @entry ) self.after_initialize end |
#inspect ⇒ Object
Return a human-readable representation of the receiving object, suitable for debugging.
540 541 542 543 544 545 546 547 548 549 |
# File 'lib/treequel/model.rb', line 540 def inspect return "#<%s:0x%x (%s): %s>" % [ self.class.name, self.object_id * 2, self.loaded? ? self.extensions.map( &:name ).join( ', ' ) : 'not yet loaded', self.dn ] end |
#merge(attributes) ⇒ Object
Make the changes to the object specified by the given attributes. Overridden to make Model objects defer writing changes until Treequel::Model#save is called.
301 302 303 304 305 |
# File 'lib/treequel/model.rb', line 301 def merge( attributes ) attributes.each do |attrname, value| self[ attrname ] = value end end |
#modification_ldif ⇒ Object
Return the pending modifications for the object as an LDIF string.
480 481 482 483 |
# File 'lib/treequel/model.rb', line 480 def modification_ldif mods = self.modifications return LDAP::LDIF.mods_to_ldif( self.dn, mods ) end |
#modifications ⇒ Object
Return any pending changes in the model object as an Array of LDAP::Mod objects.
419 420 421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/treequel/model.rb', line 419 def modifications return unless self.modified? self.log.debug "Gathering modifications..." mods = [] @values.sort_by {|k, _| k.to_s }.each do |attribute, vals| self.log.debug " finding mods for %s" % [ attribute ] mod = self.diff_with_entry( attribute, vals ) or next mods << mod end return mods end |
#modified? ⇒ Boolean
Tests whether the object has been modified since it was loaded from the directory.
260 261 262 |
# File 'lib/treequel/model.rb', line 260 def modified? return @dirty ? true : false end |
#reset_dirty_flag ⇒ Object
Mark the object as unmodified.
266 267 268 |
# File 'lib/treequel/model.rb', line 266 def reset_dirty_flag @dirty = false end |
#respond_to?(sym, include_priv = false) ⇒ Boolean
Returns true if the receiver responds to the given method.
521 522 523 524 525 526 527 |
# File 'lib/treequel/model.rb', line 521 def respond_to?( sym, include_priv=false ) return super if caller(1).first =~ %r{/r?spec/} && caller(1).first !~ /respond_to/ # RSpec workaround return true if super plainsym, _ = attribute_from_method( sym ) return self.find_attribute_type( plainsym ) ? true : false end |
#revert ⇒ Object
Revert to the attributes in the directory, discarding any pending changes.
487 488 489 490 491 492 |
# File 'lib/treequel/model.rb', line 487 def revert self.clear_caches @dirty = false return true end |
#save(opts = {}) ⇒ Object
Write any pending changes in the model object to the directory. The valid opts are:
:raise_on_failure-
raise a Treequel::ValidationFailed or Treequel::BeforeHookFailed if either the validations or before_save,create.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/treequel/model.rb', line 379 def save( opts={} ) opts = DEFAULT_SAVE_OPTIONS.merge( opts ) self.log.debug "Saving %s..." % [ self.dn ] if opts[ :validate ] raise Treequel::ValidationFailed, self.errors unless self.valid?( opts ) self.log.debug " validation succeeded." end unless mods = self.modifications self.log.debug " no modifications... no save necessary." return false end self.log.debug " got %d modifications." % [ mods.length ] self.before_save( mods ) or raise Treequel::BeforeHookFailed, :save if self.exists? self.log.debug " already exists, so updating." self.update( mods ) else self.log.debug " doesn't exist, so creating." self.create( mods ) end self.after_save( mods ) return true rescue Treequel::BeforeHookFailed => err self.log.info( err. ) raise if opts[:raise_on_failure] rescue Treequel::ValidationFailed => err self.log.error( "Save aborted: validation failed." ) self.log.info( err.errors..join(', ') ) raise if opts[:raise_on_failure] end |
#search(scope = :subtree, filter = '(objectClass=*)', parameters = {}, &block) ⇒ Object
Override Branch#search to inject the ‘objectClass’ attribute to the selected attribute list if there is one.
512 513 514 515 516 517 |
# File 'lib/treequel/model.rb', line 512 def search( scope=:subtree, filter='(objectClass=*)', parameters={}, &block ) parameters[:selectattrs] |= ['objectClass'] unless !parameters.key?( :selectattrs ) || parameters[ :selectattrs ].empty? super end |
#valid?(opts = {}) ⇒ Boolean
Return true if the model object passes all of its validations.
350 351 352 353 354 |
# File 'lib/treequel/model.rb', line 350 def valid?( opts={} ) self.errors.clear self.validate( opts ) return self.errors.empty? ? true : false end |
#validate(options = {}) ⇒ Object
Validate the object with the specified options, appending validation errors onto the #errors object.
359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/treequel/model.rb', line 359 def validate( ={} ) = DEFAULT_VALIDATION_OPTIONS.merge( ) self.before_validation or raise Treequel::BeforeHookFailed, :validation self.errors.add( :objectClass, 'must have at least one' ) if self.object_classes.empty? super( ) self.log.debug "Validations failed:\s %s" % [ self.errors..join("\n ") ] if self.errors.count.nonzero? self.after_validation end |