Setup is quick and painless with handy templates. Simply fill out the TODOs and paste the result in your module!
To consolidate everything within your existing module, fill out SnippetTemplate.bas and paste into your module. Then paste Snippet.bas alongside it.
To outsource the SOb framework to a single external dependency, fill out SObTemplate.bas and paste into your module. Then instruct your end user to import the SOb.bas module, which you may reference as a submodule in your repo.
Warning
By outsourcing, you reduce your security against tampering! The "encryption" is no longer scoped to your module, so others can overwrite1 your "private" fields.
Fill out either template according to these steps:
-
TODO: Replace every*with the ("class") name you desire for your SOb. A simple Find & Replace should suffice.So if you name your SOb something like "
Foo", this should yield aFOO_CLSconstant atop your module…- Private Const *_CLS As String = "*" + Private Const FOO_CLS As String = "Foo"
…along with a
Foo__Fieldenumeration below…- Private Enum *__Field + Private Enum Foo__Field FieldOne ' The 1st field. FieldTwo ' The 2nd field. FieldThree ' The 3rd field. ' ... End Enum
…followed by procedures of the form
Foo_…()…- Public Function *_Format(ByRef * As Object, _ + Public Function Foo_Format(ByRef * As Object, _ Optional ByVal depth = 1, _ Optional ByVal plain As Boolean = False, _ Optional ByVal pointer As Boolean = False, _ Optional ByVal preview As Boolean = False, _ Optional ByVal indent As String = VBA.vbTab, _ Optional ByVal orphan As Boolean = True _ ) As String - *_Format = Obj_Format(*, _ + Foo_Format = Obj_Format(*, _ _ _ _ depth := depth, _ plain := plain, _ pointer := pointer, _ preview := preview, _ indent := indent, _ orphan := orphan _ ) End Function
…with a
fooargument.- Public Function Foo_Format(ByRef * As Object, _ + Public Function Foo_Format(ByRef foo As Object, _ Optional ByVal depth = 1, _ Optional ByVal plain As Boolean = False, _ Optional ByVal pointer As Boolean = False, _ Optional ByVal preview As Boolean = False, _ Optional ByVal indent As String = VBA.vbTab, _ Optional ByVal orphan As Boolean = True _ ) As String - Foo_Format = Obj_Format(* , _ + Foo_Format = Obj_Format(foo, _ _ _ _ depth := depth, _ plain := plain, _ pointer := pointer, _ preview := preview, _ indent := indent, _ orphan := orphan _ ) End Function
-
TODO: Enumerate the fields you desire for your SOb. Currently there are three placeholders for these fields:FieldOneandFieldTwoandFieldThree. Feel free to Find & Replace these, and to append further fields of your own.Private Enum Foo__Field - FieldOne ' The 1st field. + Bar ' The 1st field. - FieldTwo ' The 2nd field. + Baz ' The 2nd field. - FieldThree ' The 3rd field. + Qux ' The 3rd field. End Enum
Public Property Get Foo_FieldOne(ByRef foo As Object) As Boolean - Let Foo_FieldOne = Obj_Field(foo, Foo__Field.FieldOne) + Let Foo_FieldOne = Obj_Field(foo, Foo__Field.Bar ) End Property Public Property Let Foo_FieldOne(ByRef foo As Object, ByVal val As Boolean) - Let Obj_Field(foo, Foo__Field.FieldOne) = val + Let Obj_Field(foo, Foo__Field.Bar ) = val End Property
This way, you can specify a field to (say)
Obj_Field()by simply selecting it from the dropdown forFoo__Field.…. -
TODO: Implement accessors for yourFoo__Field.…fields. Each should be aPropertyof the formFoo_…(ByRef foo As Object), and you may restrict it to internal usage via thePrivatestatement.- Public Property Get Foo_FieldOne(ByRef foo As Object) As Boolean + Public Property Get Foo_Bar (ByRef foo As Object) As Boolean - Let Foo_FieldOne = Obj_Field(foo, Foo__Field.Bar) + Let Foo_Bar = Obj_Field(foo, Foo__Field.Bar) End Property - Public Property Let Foo_FieldOne(ByRef foo As Object, ByVal val As Boolean) + Public Property Let Foo_Bar (ByRef foo As Object, ByVal val As Boolean) Let Obj_Field(foo, Foo__Field.Bar) = val End Property
Private Sub Foo_Initialize(ByRef foo As Object) Const CLS_NAME As String = FOO_CLS Obj_Initialize foo, CLS_NAME If Not Obj_HasField(foo, Foo__Field.Bar) Then Dim f1 As Boolean: ' Let f1 = ... - Let Foo_FieldOne(foo) = f1 + Let Foo_Bar (foo) = f1 End If If Not Obj_HasField(foo, Foo__Field.Baz) Then Dim f2 As Range: ' Set f2 = ... - Set Foo_FieldTwo(foo) = f2 + Set Foo_Baz (foo) = f2 End If If Not Obj_HasField(foo, Foo__Field.Qux) Then Dim f3 As Variant: ' Assign f3, ... - Assign Foo_FieldThree(foo), f3 + Assign Foo_Qux (foo), f3 End If End Sub
Simply wrap
Obj_Get()with aProperty Getto retrieve a field…Property Get Foo_Bar(ByRef foo As Object) As Boolean Obj_Get Foo_Bar, foo, Foo__Field.Bar End Property
…but implement a
Property Letto update a scalar field…Property Let Foo_Bar(ByRef foo As Object, ByVal val As Boolean) Let Foo_Bar = val End Property
…or a
Property Setto update an objective field.Property Set Foo_Baz(ByRef foo As Object, ByRef val As Range) Set Foo_Baz = val End Property
-
TODO: Initialize the values for your fields, withinFoo_Initialize(). UseObj_HasField()to test whether a field exists, and when it does not, use your accessor to set its initial value2.Private Sub Foo_Initialize(ByRef foo As Object) Const CLS_NAME As String = FOO_CLS Obj_Initialize foo, CLS_NAME If Not Obj_HasField(foo, Foo__Field.Bar) Then - Dim f1 As Boolean: ' Let f1 = ... + Dim bar As Boolean: Let bar = False - Let Foo_Bar(foo) = f1 + Let Foo_Bar(foo) = bar End If If Not Obj_HasField(foo, Foo__Field.Baz) Then - Dim f2 As Range: ' Set f2 = ... + Dim baz As Rangę: Set baz = Nothing - Set Foo_Baz(foo) = f2 + Set Foo_Baz(foo) = baz End If If Not Obj_HasField(foo, Foo__Field.Qux) Then - Dim f3 As Variant: ' Assign f3 , ... + Dim qux As Variant: Assign qux, Empty - Assign Foo_Qux(foo), f3 + Assign Foo_Qux(foo), qux End If End Sub
-
TODO: List all yourFoo__Field.…fields in theArray(…)call, withinIsFoo().Public Function IsFoo(ByRef foo As Variant, _ Optional ByVal strict As Boolean = False _ ) As Boolean Const CLS_NAME As String = FOO_CLS ' ### Class and Fields ### ' Ensure an accurate class with its proper set of fields. IsFoo = IsObj(x, class := CLS_NAME, strict := strict, fields := Array( _ + Foo__Field.Bar, _ + Foo__Field.Baz, _ + Foo__Field.Qux _ )) If Not IsFoo Then Exit Function ' Return the result in lieu of errors. Exit Function End Function
This way,
IsFoo()checks that a "Foo" object has all its fields. -
TODO: Using your accessors, assign each field fromobjto its corresponding field inAsFoo, withinAsFoo().Public Function AsFoo(ByRef x As Variant) As Object ' Cast the input to a (generic) simulated object... Dim obj As Object: Set obj = AsObj(x) ' ...and extract its fields into a new "Foo" object. Set AsFoo = New_Foo() + Let Foo_Bar(AsFoo) = Foo_Bar(obj) + Set Foo_Baz(AsFoo) = Foo_Baz(obj) + Assign Foo_Qux(AsFoo), Foo_Qux(obj) End Function
This way,
AsFoo()can cast any input (x) to a "Foo" object, by extracting fields from the former into the latter. -
TODO: Create anysummaryordetailsyou wish, to visually represent your object withinFoo_Format().Public Function Foo_Format(ByRef foo As Object, _ Optional ByVal depth = 1, _ Optional ByVal plain As Boolean = False, _ Optional ByVal pointer As Boolean = False, _ Optional ByVal preview As Boolean = False, _ Optional ByVal indent As String = VBA.vbTab, _ Optional ByVal orphan As Boolean = True _ ) As String + Dim summary As String: summary = "..." + Dim details As String: details = "..." & vbNewLine & "..." & vbNewLine & "..." ' Adjust settings to your satisfaction. Foo_Format = Obj_Format(foo, _ _ _ _ depth := depth, _ plain := plain, _ pointer := pointer, _ preview := preview, _ indent := indent, _ orphan := orphan _ ) End Function
The
SObframework automatically formats these for you: summaries display on a single line…<Foo[...]>…and details display across multiple lines:
<Foo: { ... ... ... }> -
TODO: Pass anysummaryordetailstoObj_Format(), along with all arguments fromFoo_Format().Public Function Foo_Format(ByRef foo As Object, _ Optional ByVal depth = 1, _ Optional ByVal plain As Boolean = False, _ Optional ByVal pointer As Boolean = False, _ Optional ByVal preview As Boolean = False, _ Optional ByVal indent As String = VBA.vbTab, _ Optional ByVal orphan As Boolean = True _ ) As String Dim summary As String: summary = "..." Dim details As String: details = "..." & vbNewLine & "..." & vbNewLine & "..." ' Adjust settings to your satisfaction. Foo_Format = Obj_Format(foo, _ + summary := summary, _ + details := details, _ depth := depth, _ plain := plain, _ pointer := pointer, _ preview := preview, _ indent := indent, _ orphan := orphan _ ) End Function
This way, others can apply various settings when printing your "Foo" object, including developers who wish to build their own SObs upon "Foo".
You may optionally enhance "Foo" with further steps:
-
TODO: Implement any methods you desire, which operate on your "Foo" object. Each should be aFunctionorSubroutine of the formFoo_…(ByRef foo As Object, …)wherefoois followed by any arguments needed by the method. You may restrict it to internal usage via thePrivatestatement.' ################### ' ## Foo | Methods ## ' ################### + Public Function Foo_Corge(ByRef foo As Object, _ + arg1 As Integer, _ + Optional arg2 As Integer = 0 _ + ) As Integer + Let Foo_Corge = arg1 + arg2 + Foo_Bar(foo) + Foo_Baz(foo).Cells.Count + End Function
-
TODO: Call all your field accessors likeFoo_FieldOne(), in theObj_Check …call withinIsFoo().Public Function IsFoo(ByRef x As Variant, _ Optional ByVal strict As Boolean = False _ ) As Boolean Const CLS_NAME As String = FOO_CLS ' ### Class and Fields ### ' Ensure an accurate class with its proper set of fields. IsFoo = IsObj(x, class := CLS_NAME, strict := strict, fields := Array( _ Foo__Field.Bar, _ Foo__Field.Baz, _ Foo__Field.Qux _ )) If Not IsFoo Then Exit Function ' ### Accessors ### ' Treat as an object moving forward. Dim obj As Object: Set obj = x ' Ensure the field accessors all work. On Error GoTo CHECK_ERROR If IsFoo Then Obj_Check _ + Foo_FieldOne(obj), _ + Foo_FieldTwo(obj), _ + Foo_FieldThree(obj) On Error GoTo 0 If Not IsFoo Then Exit Function ' Return the result in lieu of errors. Exit Function ' Handle inaccessibility. CHECK_ERROR: IsFoo = Obj_CheckError() End Function
This way,
IsFoo()also validates that the "Foo" fields are of the expected type, and so forth. -
TODO: Specify which validation errors (like type) you wish to catch, via arguments to theObj_CheckError(…)call withinIsFoo(). SeeObj_CheckError()for details.' Handle inaccessibility. CHECK_ERROR: - IsFoo = Obj_CheckError() + IsFoo = Obj_CheckError(type_ := True) End Function
This way,
IsFoo()returnsFalsewhen errors disqualify the input (x) as a "Foo" object, while "bubbling up" other errors for (say) improper usage. -
TODO: Customize any further validation you wishIsFoo()to perform. Each validation step should assign aBooleanvalue toIsFoo…IsFoo = …
…and finish by short-circuiting when
False.If Not IsFoo Then Exit Function
Now you are ready to work with "Foo" objects, within your module and elsewhere!
Footnotes
-
Others cannot typically overwrite the value of a "private" field in your SOb—though they can remove the field, which effectively resets it to an uninitialized state.
However, if you outsource the framework from your module to the
SObmodule, then others can overwrite it viaSOb.Obj_Field(). ↩ -
You may specify a
Rangewith its.Addressin shortcut notation:[A1:B2]. ↩