HERE'S HOW YOU CAN USE SWITCH CASES IN LUA
IMG iillustration-9.png
Kidding! You can't! Lua doesn't have a `switch'
operator. Instead, it has tables. And tables are actually
much more interesting, and far easier to work into a routine
that requires evaluating statements based on
conditions. Don't believe me? Here's an example of how I've
put tables to use.
The context: I'm building a RSS reader for
KOReader. Sometimes the RSS reader needs to fetch a web
page, which it then parses, deconstructs, and re-builds as
an EPUB. To re-build the web page into an EPUB, I create
objects for each of the website's components, like the HTML
document and any images it contains. I call these
"items". Different types of items have different private
methods, but they all share the same two data points: `path'
and `content'. Because all items share these two data
points, I've decided to make a common interface for their
construction: `ItemFactory'. Supplying the `makeItem'
method within `ItemFactory' with the item's `path' and
`content' will "automatically" construct the item with the
appropriate constructor.
How? First: a file extension is derived from the item's
`path.' This value is matched against the `ITEM_TYPES'
table. The keys of this table represent the supported item
types. The values for each key represent the item type's
supported file formats (given as extensions). When a match
is found, the table key is stored in a variable. Next, (and
this is the exciting part) that variable's value is used as
an index into the `ITEM_CONSTRUCTORS' table, which contains
constructors for each supported item type. The indexed
constructor is returned and then supplied with the `path'
and `content' data that was passed to the factory.
Cool, right? What I love so much about this no-switch
switch-like logic is that the `makeItem' method basically
reads like a unit test. It stays focused on returning errors
when conditions aren't met. The actually divining of what
code gets evaluated, and what that code does/looks like gets
tucked away into a table. Extending the code is simple,
maintaining the code is clear. It's bliss!
,----
| local ItemFactory = {
|
| }
|
| ItemFactory.ITEM_TYPES = {
| xhtml = XHtmlItem.SUPPORTED_FORMATS,
| image = Image.SUPPORTED_FORMATS
| }
|
| ItemFactory.ITEM_CONSTRUCTORS = {
| xhtml = function(path, content)
| return XHtmlItem:new{
| path = path,
| content = content,
| }
| end,
| image = function(path, content)
| return Image:new{
| path = path,
| content = content
| }
| end
| }
|
| function ItemFactory:makeItem(path, content)
| local suffix = util.getFileNameSuffix(
| string.lower(path)
| )
|
| local matched_type = ItemFactory:getItemTypeFromFileNameSuffix(suffix)
| if not matched_type
| then
| return false, EpubError.ITEMFACTORY_UNSUPPORTED_TYPE
| end
|
| local item_constructor = ItemFactory.ITEM_CONSTRUCTORS[matched_type]
| if not item_constructor
| then
| return false, EpubError.ITEMFACTORY_NONEXISTENT_CONSTRUCTOR
| end
|
| return item_constructor(path, content)
| end
|
| function ItemFactory:getItemTypeFromFileNameSuffix(suffix)
| local matched_item_type = nil
| for item_type, supported_formats in pairs(ItemFactory.ITEM_TYPES) do
| if supported_formats[suffix]
| then
| matched_item_type = item_type
| break
| end
| end
| return matched_item_type
| end
|
| return ItemFactory
`----