This page is intended for advanced programmers. See Magik Basic Language for more common language uses.
Compiling on the Fly
Do you need to load Magik code on the fly? Lets say a procedure is stored as a text join on an object and when you update the object, you want to let it run the procedure stored on the object.
The first thing you need is a handle to the string you want to "compile"
Then run this method: magik_rep.load_chunck(string.read_stream())
Stopping an Endless Loop on the Magik Prompt.
Have you started a (Near) endless loop on the Magik prompt? Here is a way to stop the loop.. You will need to open a Magik Input GUI from the opened application. In 3.X it was under Configuration or something like that. Once there type the following.
cli_thread.interrupt(_proc() _throw@wobbly _endproc)
From the Threads Manager, you can error the cli thread and it should stop the loop. Note the Threads Manager may only be available on 4.X releases.
Indirect Access to Globals
Have you wanted to write code that tests for the existence of a global variable without initializing the variable itself? You can use the following. Note the !current_package! could be replaced with packages such as sf_package.
!current_package![:global_symbol_name]
Another way. Good for getting procs too…
get_global_value(:!print_length!)
get_global_value(:startup)
You can "undeclare" a global by doing the following.
!current_package!.remove_key(:global_symbol_name)
Mixins
Adding a Mixin during a session
If you need to create a mixin in a current session and want to add the mixin ancestry to existing exemplars that you can't recreate during the session (record exemplars for example), you can add them using the following magik
def_mixin(:whatever_mixin)
whatever_mixin.add_child(whatever_exemplar)
Mixin Method Table
A defined mixin's method table is retrieved using mtable on the mixin itself. method_table on the mixin return's the core mixin method table. Also note, to find the core mixin methods in method browser, use mixin_mixin as the exemplar name.
Change Mixin Shared Variable
To change a shared variable defined on a mixin, you can do it through method.value.
def_mixin(:my_mixin)
$
my_mixin.define_shared_variable(:a_var,:test,:public)
$
my_mixin.method(:a_var).value << :changed
Getting Current Dynamics
Here is a way to get the available dynamic variables in your current thread.
_for k,v _over _thisthread.dynamic_environment(_true)
_loop
show(k,v)
_endloop
Do you need to make a private method public?
Try this… Note you don't typically want to use this… But it is something to be aware of.
map_trail.method(:append_sectors_to_trail|()|).set_private(_false)
!!! THREAD !!! bpt on thread error
I was getting this when I was trying to do a code coverage analysis. The way to track this down is to add the following show() statement.
_pragma(classify_level=restricted)
_method coverage_analyser.insert_bpts_in(method)
show(method)
method.value.set_breakpoint(0)
_endmethod
$
From the result you can add the problem method to coverage_analyser.excluded_code_vectors shared variable
_block
_local ex << message_handler
_for m _over {:human_string|()|
}.fast_elements()
_loop
coverage_analyser.excluded_code_vectors.add(ex.method(m).value.code_vector)
_endloop
_endblock
Evaluating Strings into Magik results
You can evaluate a string like "_false" into a true Magik boolean.
MagikSF> !a << magik_text.new()
$
MagikSF> !a.add_last("_false")
$
MagiKSF> !a.evaluate() = _false
$
True
Temporarily Remove Method Functionality
Have you tried to use a method like split_by() only to see your leading and trailing spaces disappear? Here is a way you can overwrite the default behavior.. In this case we replace the trim_spaces() method with a simple procedure, run the split_by() and restore the original behavior.
_local data
_local str << " this is, a test , ,,"
_local oProc << charindex_mixin.method(:trim_spaces|()|).value
_protect
charindex_mixin.method(:trim_spaces|()|).value <<
_proc()
>> _self
_endproc
data<< str.split_by(%,,_true)
_protection
charindex_mixin.method(:trim_spaces|()|).value << oProc
_endprotect
Lexicographic Sorting
Lexicographic sorting is sorting strings like in a dictionary. When sorting strings, there can be special characters like ï, é and so on. The special characters do not sort on the base character, but differently as is shown by the following example:
MagikSF> %ï _cf %j
$
False
MagikSF> %i _cf %j
$
True
Here is a simple solution to the Lexicographic sorting problem with special characters.
_block
_local l_special_chars << property_list.new_with(%ï,%i, %ë,%e, %ö,%o)
_local l_normalise << _proc(a)
_import l_special_chars
_local l_result << ""
_for i_char _over a.fast_elementS()
_loop
l_result +<< l_special_chars[i_char].default(i_char)
_endloop
_return l_result
_endproc
_local l_list << {"mals", "maak", "mazk", "maïs"}
_local l_sorted_list << sorted_collection.new_from(l_list,
_proc(a,b)
_import l_normalise
_return l_normalise(a) _cf l_normalise(b)
_endproc)
print(l_sorted_list)
_endblock
$
sorted_collection(1,4):
1 "maak"
2 "maïs"
3 "mals"
4 "mazk"
Alternatively one may use text translators
text_converter.new(:cp1250, _false).translate(text_converter.new(:cp1250, _true).translate("maïs"))
Renaming Methods
You can rename a method using the following. This is great to implement customized code without modifying the original code. This saves lots of time when TSBs or upgrades are installed!
meth << exemplar.method(method_name)
newMeth << :sw!+method_name
exemplar.define_method(newMeth,meth.value,_false)

Handling "does not understand"
You can subclass the method does_not_understand(msg,private?). This allows you to support functionality when you do not necessarily know what is going to be called. A good example of this usage is in the Explorer Add-on for Pseudo Records located here. This passes unknown method calls to an owning object. Another interesting solution would be to create a in-memory object whose fields may be anything. You can intercept the non () methods and create in-memory object representations of those fields and they would act just like a field or slot on the original exemplar…
Pseudo Slots
Pseudo slots (defined on the exemplar with define_pseudo_slot()) allow you to add functionality to an existing exemplar without subclassing it. I use it a lot when trying to add functionality to editors or something that is very difficult or very time consuming to replace the exemplar name in places.
One side affect I recently ran across is that when define_pseudo_slot() is called, it sets up other methods like shallow_copy() on the exemplar the slot is being added to. This can cause conflict methods. For example if you add a pseudo slot to database_view, any subclass of database_view will have shallow_copy() method conflicts. You will need to add the method to the subclass to use the appropriate method.
Pesky Reporting
Have you added debug code and didn't label it with something you can search for and then save the file? Then you see stuff going to the magik prompt and you can't figure where the heck it is coming from? Here is some code to help:
Method Finder/Class Browser
Method Finder Mapping
You can map the location of source files so that the method finder can jump to the appropriate code. This is especially good for when a JAR file is delivered but only certain source files are delivered in a separate directory. You can use method_finder.add_mapping("NAME_OF_ENV","mapped_directory").
An example for the following condition:
- the original location for compiling files was at c:\release\my_modules.
- the installed directory is \\a_server\product\some_modules
- you have an environment called SOME_MODS_DIR that points to \\a_server\product\some_modules
You can run method_finder.add_mapping("SOME_MODS_DIR","c:\release\my_modules").
NOTE : This seems to work ok for SW5, but not sure about 4.3. Could be user error. If anyone has a better example/use please share!
An example without using an evironment variable:
- the orignal location for compiling files was at e:\gis
- the installed directory is r:\
Using F3-j in the Class Browser results in: 'error in process filter: Cannot find file, e:\gis\…'.
Note: Exploring the mf-file with a text-editor resolves that the actual paths used are: 'E:\gis\…' (capital E).
Now the following mapping works:
method_finder.add_mapping("R:\", "E:\gis\", "")
Note the following obvious mapping did not work:
method_finder.add_mapping("r:\", "e:\gis\", "")
Check your mapping by:
print(method_finder)
a sw:method_finder
command method_finder
pid 6752
mapping E:\gis\ => R:\
Don't forget to apply the mappings by:
method_finder.apply_mappings()
Now the F3-j should jump to the right file.
For future use, you now may want to save the method_finder by:
method_finder.save(optional <target path to *.mf>)
To reset the original mappings, the easiest way is to reload the mf-file by:
method_finder.load(<source path to *.mf>)
or you can apply the reverse mapping by:
method_finder.add_mapping("E:\gis\", "R:\", "")
method_finder.apply_mappings()
*Packages
This needs a page of its own as there appears to be many things you can do with packages….