Author Archives: DLed
Beginning another quest – scripting business logic in .NET: using dynamiclua
Let’s start with Lua
The current state of things is that the lightweight but mighty Lua has become ubiquitous in the world of computing as a scripting and configuration component. Lua’s strategy of “mechanism, not policies” allows the language to be extremely malleable. It’s worth watching Roberto Ierusalimschy’s talks on the evolution of Lua [1. Video 1, more slides, Video 2, more Roberto].
The latest adoptions of Lua are for example the Redis’ EVAL Query with Lua scripting, and Wikipedia’s template automation using user-written Lua scripts.
dynamiclua
There have been several attempts to bring Lua into the world of .NET, and one of the most recent efforts by Niklas Rother, dynamiclua, combines the previous achievements with the dynamic
type of .NET 4.0, and lays path for an easily reusable business logic scripting component, which is now even available via nuget.
After adding dynamiclua as a reference, here’s your first business logic:
using (dynamic lua = new DynamicLua.DynamicLua()) { Console.WriteLine(lua("return 2+2")); }
note the IDisposable
usage for freeing native resources.
Discovering the features
Static configuration
As an author of a configurable component, In order to reduce my support effort, I want the users of my component to write their static configuration themselves
While for that user story JSON or XML would suffice, we’re on the Lua track, hence this is how it could work:
// comes from some user input or file string user_static_config = @" config = { url = 'https://github.com/nrother/dynamiclua' } "; lua(user_static_config); string url = lua.config.url; Console.WriteLine(url);
… it does look like JSON, but it isn’t. Here, one can observe the harmony resulting from the use of the dynamic
keyword to interact with a dynamic language from a statically typed one. DynamicLua can leverage reflection capabilities of both languages to create an “automagical” interop, compared sometimes tedious writing of bindings for a language, such as native C++.
The power of Lua at the user’s fingertips
As an author of a customizable business software, In order to reduce my own support effort, And create a new business of customizations, I want the administrators of my software to write their business logic scripts themselves
Many systems grow out of static configurability, and business logic definition and evaluation in some programming language becomes inevitable. Whether this is a good thing or a bad one for one’s business [2. User scripts are software, and thus might never terminate] is not in the scope of this article, as it is assumed, we want to make our software customizable via scripting [3. I won’t repeat the meme about great power here].
The static configuration entries can be dynamically evaluated at script run time. Various bindings may be used in the script, as i.e. the math
standard library here:
lua("config.answer = nil or math.floor(131.94689145078/math.pi)"); int answer = (int)lua.config.answer; Console.WriteLine(answer);
Using Lua as an expression evaluator
Lua supports multiple return values, which makes certain scenarios fun:
dynamic result = lua("return 1, 'world'"); Console.WriteLine("{0} {1}", result[0], result[1]);
Binding .NET code
Dynamiclua does not have an explicit class binding syntax [4. as is the case with many C++ bindings i.e. LuaBridge], but it doesn’t matter much, as functions can be bound, and thus, constructors. Via reflection all public [5. In my opinion, access to privates should throw…] members are bound. The domain code is therefore very easy to bind. Take an example:
class Example { int answer = 42; public int Answer { get { return answer; } } public int Add(int a, int b) { return a + b; } }
The binding is quite trivial:
lua.NewExample = new Func<Example>(() => new Example()); // Lua functions may be called via dynamiclua Example my_example = lua.NewExample(); Console.WriteLine(my_example.Answer); lua(@" local example = NewExample() print(example.Answer) print(example:Add(2,3)) ");
Minimal sandboxing
As an author of a scripting component, In order to mitigate the risk of damage by malicious user-supplied code, I want the script executing environment to be sandboxed.
Sandboxing is a very complex issue and perhaps only approximations of an undefined ideal sandbox can be made [6. see wiki: SandBoxes and Daniel Silverstone’s talk at Lua Workshop 2013 on his approach]. A sane attitude towards this security-related issue should go along the lines of trusting nobody, even oneself, and even that distrust should not be enough. Depending on the posed security requirements, the logic execution component might even be physically separated from your business software, i.e. running on another machine or in another process. But that is perhaps another concern and should be independent of your script execution component.
Since Lua is malleable, one can configure or manipulate the Lua virtual machine to make certain actions unlikely, such as accessing the file system. This can be achieved by setting the bound names to nil
as in [7. More …]:
lua("import = nil"); lua("require = nil"); new Action(() => lua("import 'System'")) .ShouldThrow<Exception>();
In this little spec you can also observe what should happen (non-specifically) if there’s a Lua syntax or a runtime error. You can also see how to import assemblies into the Lua state.
Here’s another quote from the test:
lua.import("System"); Func<int> func = () => (int)lua("return Console.BufferHeight")[0]; new Action(() => func()) .ShouldNotThrow(); func().Should().Be(Console.BufferHeight);
Without further ado
Some things should perhaps be further investigated:
- Is dynamiclua prepared for the Mono runtime on *x platforms?
- Is it possible to crash the process uncontrollably, even while catching exceptions?
Fix crash on finalization
Lua can appear simple enough, such that, given a syntactic constraint, even non-programmers could write business logic scripts and configurations. However, there is a company experimenting with alternative ways to define business logic using different approaches to a logic-definition UI. It’s worth checking out: LogicEditor.
Other language and runtime candidates will hopefully be investigated within the next posts of the quest: IronRuby and Boo. The readers of this blog entry are welcome to criticize, point out omitted or wrong information, and interact freely.
Code
ReactiveUI 6 and ViewModel Testing
Testability and ReactiveUI
In the previous articles about ReactiveUI I’ve claimed without bringing any evidence that writing ViewModels using ReactiveObject
s brings about testability. While the aspects of testing Rx and ReactiveUI have been treated at length in the respective authors’ blogs linked herein, this post is intended as a quick glance for the impatient online surfer at the hello-world testing code, which has been written “post-mortem” [1. as in, not within TDD] as a follow-up to the previous articles.
An update to ReactiveUI 6
Paul Betts and contributors have been busy simplifying and extending the library [2. which now also targets Xamarin and Windows Phone 8 and Windows Store Apps]. There are some extension methods now that help creating observables from properties, and transforming observables to properties. In the example ViewModel from previous articles, there’s an observable stream of strings that is simply transformed into a property defined as follows:
ObservableAsPropertyHelper<string> _BackgroundTicker; public string BackgroundTicker { get { return _BackgroundTicker.Value; } }
In the constructor, the helper is now initialized without using strings:
public WordCounterModel(IObservable<string> someBackgroundTicker) { someBackgroundTicker .ToProperty(this, x => x.BackgroundTicker, out _BackgroundTicker); ... }
instead of string-based error-prone
_BackgroundTicker = new ObservableAsPropertyHelper<string>( someBackgroundTicker, _ => raisePropertyChanged("BackgroundTicker") );
For the actual changes in ReactiveUI, consult Paul Betts’ insightful log.
A simple test
Since the tests have been written after writing the example code, I’ve been searching for the “Generate Unit Test” context menu in Visual Studio 2013. The context menu is not there, but luckily some enthusiasts recreated the functionality partly: → Unit Test Generator.
After the initial set-up and
here’s the simple test of the word count property:
[TestMethod] public void WordCounterModelTest() { var mock = new Mock<IObservable<string>>(); var vm = new WordCounterModel(mock.Object); vm.WordCount.Should().Be(0); vm.TextInput = "bla!"; vm.WordCount.Should().Be(1); vm.TextInput = "bla, bla!!"; vm.WordCount.Should().Be(2); }
In it, one can observe the use of MOQ for mocking a dummy and FluentAssertions for beautifully readable Spec/BDD-style assertions [3. I originally intended to use SpecFlow, but the specs refused to flow frictionlessly].
So, there’s no UI involved, and the UI is dogmatically bound from XAML with almost no code-behind.
Testing time series
The hello world example program simulated a dependency on some timed series of strings, ticking every second. While this is not specific to ReactiveUI, let’s make use of the test scheduler [4. see Intro to Rx]. For that, the time series should optionally depend on an injected IScheduler
:
public class BackgroundTicker { IScheduler scheduler = Scheduler.Default; public BackgroundTicker(IScheduler other_scheduler = null) { if (other_scheduler != null) scheduler = other_scheduler; } public IObservable<string> Ticker { get { return Observable .Interval(TimeSpan.FromSeconds(1), scheduler) .Select(_ => DateTime.Now.ToLongTimeString()); } } }
The test instantiates a test scheduler, which is then advanced to make deterministic assertions. The code should speak for itself:
[TestMethod] public void BackgroundTickerTest() { (new TestScheduler()).With(scheduler => { var ticker = new BackgroundTicker(scheduler); int count = 0; ticker.Ticker.Subscribe(_ => count++); count.Should().Be(0); scheduler.AdvanceByMs(1000); count.Should().Be(1); scheduler.AdvanceByMs(2000); count.Should().Be(3); }); }
Summary
Code: https://github.com/d-led/reactiveexamples
Previous article: The WPF + ReactiveUI Refactored Version of the Responsive UI Hello World.
See also: the c++ version.