Page 1 of 1

Modules for Family Historian

Posted: 04 Oct 2013 12:35
by Jane
Mike Tate posted in IUP Hints And Tips

FYI: I have written some Lua Library Modules that all my Plugins now use, although none have made it to the Plugin Store yet, except for Show Project Statistics that uses most of them.
Here are my SkyDrive links to them:

A Global Functions Module for popular basic functions ( some require "lfs" )

A GUI Library Module relevant to KB IUP tips ( needs all the iup libraries )
It does a lot of the repetitive donkey work, and includes many standard GUI features, but needs a user guide:
Common attribute values such as colour codes, and popular filenames & filepaths, etc.
LoadSettings & SaveSettings for sticky data settings
ShowDialogue for default operations of showxy and popup
NewHelpDialogue & AddHelpButton for a Help & Advice button
FontDialogue for a Set Window Font button
MemoDialogue & WarnDialogue both like iup.Alarm and fhMessageBox
CheckVersionInStore

A HTML XML URI UTF8 CP1252 Module for all those web page & UTF8 cases

A Progress Bar Module requires "iuplua"

A Table Load Save Module

Those look pretty good. I think I might be tempted to split the global one so you have one for extensions to the string library, and make the helper functions table ones so perhaps fhh.PtrDataRef() so that they don't clash. I could then talk to Calico about adding the modules to the load require set on family-historian.co.uk

BTW is it OK to move this thread into plugin discussions?

Re: Modules for Family Historian

Posted: 04 Oct 2013 13:59
by tatewise
Would it be consistent to also put all the A HTML XML URI UTF8 CP1252 Module functions into the fhh helper table?
Then we end up with:
A String Functions Module using the Lua string table.
A Helper Functions Module using the FH fhh helper table.
A HTML XML URI UTF8 CP1252 Module using the FH fhh helper table.
A GUI Library Module using its tblGUI table assigned to user's own table.
A Progress Bar Module using tblProgressBar assigned to user's own table.
A Table Load Save Module using the Lua table table.

Re: Modules for Family Historian

Posted: 04 Oct 2013 15:21
by Jane
Yes those sound good. I might call the string functions module bonus string functions or similar.

A thought on your HTML encodes, I lifted the encode from your plugin for the Evernote one, but needed to use <br /> rather than <br> for the XHTML format Evernote needed, so it might be worth adding a newline style setting which could be set using a function?

I might be tempted to split the loadrequire functions away as well as I might try and get them built in or at least shipped with FH, so plugin store plugins can use them with out having to include the code to do it.

Re: Modules for Family Historian

Posted: 04 Oct 2013 23:41
by tatewise
So we could have:
A Bonus String Functions Module using the Lua string table.
A General Helper Functions Module using the FH fhh helper table.

Alternatively, we could wrap all the General Helper Functions in a Function Prototype that returns its table in the same way the A GUI Library Module uses NewGUI() to return its table.
Likewise, the A HTML XML URI UTF8 CP1252 Module could use a Function Prototype to return its table.
This has several benefits
  1. modules can have private upvalues.
  2. users choose the name of their table.
  3. most modules are used in the same way (exceptions being those that build on existing Lua tables).
Searching via Google suggests that <br /> should work in both XHTML and HTML 5 and probably in HTML 4 too.
So that should be the default and is easily changed.
Functions to set such tags could be easily added if necessary.
Alternatively, simply set the tags as strings in the returned table, just as A GUI Library Module offers colour codes and gap/border/margin values that the user can overwrite.
e.g.
The function prototype could look like:

Code: Select all

function NewEncoder()
	local tblEncode = { }
	tblEncode.Break = "<br />"
	tblEncode.StrEncode = function(strText,strPattern)
		etc
	end
	etc
	return tblEncode
end -- function NewEncoder
Then user could use the following to alter line break tag:

Code: Select all

 tblEnc = NewEncoder()
 tblEnc.Break = "<br>"
If only a few values are affected then they could be supplied as arguments to the function prototype:

Code: Select all

function NewEncoder( strBreak, etc )
	local tblEncode = { }
	local strBreak = strBreak or "<br />"
perhaps with validity checks on the supplied strBreak tag argument.

I have not used the loadrequire function so have no opinion.

Re: Modules for Family Historian

Posted: 05 Oct 2013 08:22
by Jane
That sounds good. There are a few extra functions which might work well in the General Helper module.

Such as the iterator functions for looping records, facts and all items.
I have not used the loadrequire function so have no opinion.
The reason for using loadrequire is that the new modules can be downloaded by the plug-ins rather than having to be included in the source for the plugin when issuing to the store.

Re: Modules for Family Historian

Posted: 05 Oct 2013 10:10
by tatewise
OK, that all seems perfectly reasonable.
I presume a Plugin would simply use a require statement to include a library module? e.g.

Code: Select all

require "A GUI Library Module"
How do you (and Simon) propose the library modules would be managed?
Would the modules somehow be tied in to a Release/Upgrade of FH, and thus only updated in sync with FH installation?
Alternatively, would the modules simply be Downloaded from the Plugin Store, and updated at any time by the author?

I know Simon was concerned about the risks of library modules being shared by several Plugins.
He was worried that a module update might accidentally cause many Plugins to stop working correctly.
Could each library module update have a new name based on a version number system, rather like Windows dll?
Then existing Plugins would continue to use the same old reliable library modules until the author chose to update.
One snag is that users could accumulate several redundant old module versions that would need garbage collecting.
Answers in a plain brown envelope please :lol:

I presume an associated User Guide will be needed for each library module, and would be provided in the plugins:index#developer_guide|> Plugins > Developer Guides section?

Re: Modules for Family Historian

Posted: 05 Oct 2013 11:43
by Jane
When we discussed it, the thought was to include a version number in the module, so that a plugin would request for example "fhfmodule_1.1" and would be provided with that version, in a similar way to things like jQuery work.

If we can get loadrequire included what you do is use

Code: Select all

loadrequire('module_1.1')
It then checks if the module exists and uses it if it does and failing that prompts the user to download it.

There are more details in plugins:code_snippets:module_require_with_load|Module Require with Load

Re: Modules for Family Historian

Posted: 06 Oct 2013 19:51
by tatewise
I think I have incorporated all the ideas that we have discussed.
I only start all the module names with A ... so they are listed together at the top of the Plugins.
In modules that extend the string and table Lua tables, all the functions have lowercase names.
In modules that utilise the function prototype technique, all the functions have MixedCase names.

A Bonus String Functions Module extends the string Lua table.
To be consistent string.SplitNumbers() has become string.splitnumbers().
I have added some defensive code and tidied up a few functions.

A General Helper Functions Module uses NewHelper() prototype to return its table.
The module now includes slightly reworked versions of the iterator functions as you suggested.
Others could of course be easily added without interfering with existing functions.

A GUI Library Module uses NewGUI() prototype to return its table.
It requires both the above two modules and so needs:

Code: Select all

	require "A Bonus String Functions Module"
	require "A General Helper Functions Module"
	local Helper = NewHelper()
I presume this is the way such modules must incorporate other modules? ( or use loadrequire() later )

A Progress Bar Module uses NewProgressBar() prototype to return its table.
This can be used standalone, or integrated with A GUI Library Module above.

A Table Load Save Module extends the table Lua table - no changes.

A Text Encoder Functions Module uses NewEncoder() prototype to return its table.
This is a slightly reworked version of A HTML XML URI UTF8 CP1252 Module with break tag enhancements, etc.

I have done some superficial testing on all the new and reworked modules.

Re: Modules for Family Historian

Posted: 07 Oct 2013 07:44
by Jane
I have "played" with your string module to make it a little more compliant with some of the suggestions from PIL.
fhhstring.lua
(2.13 KiB) Downloaded 215 times
Rather than overloading the string library directly, it returns a normal module table with an import function, I have also changed the name to fhhstring and the extension to .lua this means it can still live in the plugins directory and be opened using the editor, but won't appear on the installed plugins list.

Code: Select all

require "fhhstring"
fhhstring.import()
a = 'hello,me,me to'
b = a:split(',')
print(b[1],b[2],b[3])

Code: Select all

require "fhhstring"
a = 'hello,me,me to'
b = fhhstring.split(a,',')
print(b[1],b[2],b[3])

Re: Modules for Family Historian

Posted: 07 Oct 2013 09:53
by tatewise
I can follow how the code is working, but leaves me with a number of questions:
  1. Who or what is PIL ?
  2. How do I open fhhstring.lua using the Plugin editor ?
  3. Is the purpose of import to give the user control over whether string is overloaded or not ?
  4. If the statement local tblstring = require "fhhstring" is used, then all the functions appear in both tblstring and fhhstring, so the user could use the functions without even using import. Is that correct?
  5. Or should all the functions be in a table local to import and only overloaded on string when import is run?
  6. I now see how all the other modules could use fhh... tables, retain the benefits of function prototypes, and be shared among modules. Shall I rework them along those lines?

Re: Modules for Family Historian

Posted: 07 Oct 2013 10:39
by Jane
Who or what is PIL ?
Programming in Lua

How do I open fhhstring.lua using the Plugin editor ?
Do Open on the editor and type *.lua in the file name field.


Is the purpose of import to give the user control over whether string is overloaded or not ?
Yes

If the statement local tblstring = require "fhhstring" is used, then all the functions appear in both tblstring and fhhstring, so the user could use the functions without even using import. Is that correct?

As I understand it Yes, the idea is for example if you did not want to type fhhstring you could do

fs = require "fhhstring"
and type
fs.split('my string',' ')

Or should all the functions be in a table local to import and only overloaded on string when import is run?

The overload puts the functions into the string table so you can do, you would not want to do
string = require "fhhstring"


I now see how all the other modules could use fhh... tables, retain the benefits of function prototypes, and be shared among modules. Shall I rework them along those lines?

Sounds good to me. I am adding a few of my string functions to fhhstring at the moment and I will post a newer version in a while.

So far I have added

trim
startswith
endswith
gedsplit
titlecase

I think filenamesplit would be best in here as well.

Re: Modules for Family Historian

Posted: 07 Oct 2013 12:46
by tatewise
I disagree, I think SplitFilename should be with all the other file helpers such as OpenFile, DeleteFile, SaveStringToFile, DirTree, etc.

When would you want to use SplitFilename without any of those others?

Re: Modules for Family Historian

Posted: 07 Oct 2013 13:12
by Jane
That's fine, my thought was it was a string handling function rather than a file one, but it can go in either.

I have a slight improvement to it which returns 4 parameters so you get
path
filename with extension
filename no extension
extension

Re: Modules for Family Historian

Posted: 07 Oct 2013 23:40
by tatewise
I have been looking through the PIL guide-lines and come up with some ideas.

During development it seems that the file can be called fhhstring.fh_lua with direct editor access, and yet behaves exactly as a module package.
It just needs to be renamed to fhhstring.lua for distribution.

Instead of using package.seeall there are other perhaps better ways of making globals visible, and ways to avoid having to prefix the module table name on every public item as illustrated in the following:

Code: Select all

local fhh = {}			-- local table
fhhstring = fhh			-- assign package table name
local module = module	-- only required globals are visible as locals within package
local string = string
local pairs = pairs
local data = 0			-- private local upvalues go here
setfenv(1, fhh)			-- hereon all public names are added to local fhh table
module(...)				-- create the module from filename
etc, etc...

Code: Select all

function convert(strTxt,strOld,strNew,intNum)
	return strTxt:gsub(strOld,function() return strNew end,intNum) -- note use of string.gsub()
end -- function convert

-- import overloads fhh into string table
function import()
	for k,v in pairs(fhh) do
		if k ~= "import" then string[k] = v end
	end
end -- function import

return(fhh)
The only globals that this module can use are module, string & pairs.
Note that public functions convert and import are elements of the fhh table.
i.e. public names cannot pollute the global namespace, and they do not need the fhh. prefix.
The global package table name fhhstring only needs to appear in one place.

Re: Modules for Family Historian

Posted: 08 Oct 2013 07:37
by Jane
There are advantages with using package seeall, as I think the method you suggest is depreciated in 5.1 and removed in 5.2 of Lua. See the recent discussion on Lua mailing list <lua-l@lists.lua.org>

If you are reading PIL online remember that's the older 5.0 version, the printed version is available for 5.1 and 5.2

The other advantage with the prefix is that if you were to load Penlight for example you would not get any clashes. What do you have against the prefix, it's how most lua modules I have come across work?

Re: Modules for Family Historian

Posted: 08 Oct 2013 10:41
by tatewise
Yes, I was reading PIL online.

The stated advantage of the technique was to identify which global resources the package required.
However, I am perfectly comfortable with the package.seeall method:

Code: Select all

local fhh = {}				-- local table
fhhstring = fhh				-- assign package table name
module(...,package.seeall)	-- create the module with filename
setfenv(1, fhh)				-- hereon all public names are added to local fhh table
local upvalue = 0			-- private local upvalues best here
The benefits of this setfenv() approach are:
  • Forgetting to use local on a declaration does not pollute the global namespace.
  • Forgetting to use the table prefix on a declaration does not pollute the global namespace.
  • Existing code is easily turned into a package without having to add lots of table prefixes.
  • A package name clash is resolved by changing just one line fhhstring = fhh.
In the unlikely event of a Penlight or any other package name clash there are several workarounds.
e.g. If utils clashed then say local _utils = utils near the top is a workaround.
As an experiment the following worked fine within a package.

Code: Select all

local _lfs = require "lfs"    -- avoid lfs name clash
-- later after setfenv(1, fhh)
lfs = 0             -- becomes 'fhh.lfs' and also 'local lfs' works OK
print("lfs  = ", lfs)
print("mode = ",_lfs.attributes("C:\\ProgramData","mode"))

Re: Modules for Family Historian

Posted: 10 Oct 2013 10:10
by tatewise
I have experimented with Penlight, and it certainly offers a great many useful functions.
Pity we did not come across it earlier, as it would have saved writing quite a few code snippets!

For the moment I have simply copied all the Penlight .lua modules into C:\ProgramData\Calico Pie\Family Historian\Plugins\pl\ and use local pl = require "pl.import_into"() but is there a better way?

Re: Modules for Family Historian

Posted: 10 Oct 2013 12:23
by Jane
You can require each module directly if you wanted.

I can get pl added to the downloadable modules so it can be retrieved using loadrequire

Personally I find writing or sourcing code Snippets has helped my Lua coding.

Re: Modules for Family Historian

Posted: 10 Oct 2013 14:35
by tatewise
Yes, sometimes just adding a module is useful, but require "pl.import_into"() efficiently only loads functions that are actually used.

Just wondered if Penlight had perhaps already been added, but adding it via loadrequire would be good.

Agreed that writing Lua snippets helps understanding, but Penlight offers reassuring comparisons.

Any further thoughts on setfenv() approach discussed above?

Discussion continues under Modules for FH Library (10857) ...