Can we get the pallet and function data from call (type) passed as a parameter to a function in substrate? - blockchain

fn pre_dispatch(
self,
who: &Self::AccountId,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize
) -> Result<Self::Pre, TransactionValidityError> {
let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
Ok((self.0, who.clone(), imbalance))
}
(The above code is copied from txn-payment-pallet)
Here can we get function name and parameters from the call(one of the parameter), And based on the function-name , pallet , parameters passed by user, I want to compute fee.
For example , If it is call from pallet-staking::bond(x : amount_of_tokens_to_be_bonded) and I want to set fee for txn based on x.
Is that possible??
Like wise I want to set fee based on function-call parameters entered by user.

You can, but it requires a bit of type juggling to do so.
First, you need to realize that type Call = T::Call; in ChargeTransactionPayment. Looking at trait Config in pallet_transaction_payment, no type Call can be seen there. Instead, this type is coming from frame_system::Config (which is the super-trait of all pallets).
A brief look at the top level runtime aggregator file unravels that this Call type is essentially the outer-call of the runtime, an enum that encapsulates the call of all pallets.
That being said, the main point here is that from within pallet_transaction_payment, we cannot know of this outer-call contains this particular call from staking or not. To do so, you need to enforce this assumption via a new trait bound, namely IsSubType. This trait is specifically made to convert from wrapping type (like the outer-call) into its inner variants. See an example of this type being implemented for node_runtime's Call type.
Applying the following diff to substrate master should do exactly what you want .
diff --git a/Cargo.lock b/Cargo.lock
index ea54adf99e..df66185163 100644
--- a/Cargo.lock
+++ b/Cargo.lock
## -6074,6 +6074,7 ## dependencies = [
"frame-support",
"frame-system",
"pallet-balances",
+ "pallet-staking",
"parity-scale-codec",
"scale-info",
"serde",
diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml
index 1d3066e39f..0e705514bb 100644
--- a/frame/transaction-payment/Cargo.toml
+++ b/frame/transaction-payment/Cargo.toml
## -27,6 +27,7 ## sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primit
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
+pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../staking" }
[dev-dependencies]
serde_json = "1.0.68"
## -44,5 +45,6 ## std = [
"sp-std/std",
"frame-support/std",
"frame-system/std",
+ "pallet-staking/std",
]
try-runtime = ["frame-support/try-runtime"]
diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs
index 59d94a8237..3b0803663d 100644
--- a/frame/transaction-payment/src/lib.rs
+++ b/frame/transaction-payment/src/lib.rs
## -251,7 +251,7 ## pub mod pallet {
pub struct Pallet<T>(_);
#[pallet::config]
- pub trait Config: frame_system::Config {
+ pub trait Config: frame_system::Config + pallet_staking::Config {
/// Handler for withdrawing, refunding and depositing the transaction fee.
/// Transaction fees are withdrawn before the transaction is executed.
/// After the transaction was executed the transaction weight can be
## -696,7 +696,8 ## impl<T: Config> sp_std::fmt::Debug for ChargeTransactionPayment<T> {
impl<T: Config> SignedExtension for ChargeTransactionPayment<T>
where
BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
- T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
+ T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
+ + frame_support::traits::IsSubType<pallet_staking::Call<T>>,
{
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
type AccountId = T::AccountId;
## -736,8 +737,15 ## where
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
- let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
- Ok((self.0, who.clone(), imbalance))
+ use frame_support::traits::IsSubType;
+ if let Some(pallet_staking::Call::bond_extra { .. }) = call.is_sub_type() {
+ // skip
+ todo!()
+ } else {
+ // default impl
+ let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
+ Ok((self.0, who.clone(), imbalance))
+ }
}
fn post_dispatch(
Note that this approach is implying that the pallet_staking::Config be present in the runtime, which is not aligned with the modularity of Frame, and ergo is not implemented. If you want to have this feature, as of now, the only way is to fork pallet_transaction_payment and customize it a bit for your runtime.

Related

I want to have collections of variables and select which set I choose from, I tried nested variable assignment

I have a module for which I need to pass a set of values via variables.tf, currently, the variables are grouped by the suffix on the name, i.e. -dev or -stg, etc
The module itself doesn't care which set it gets, but I must decide somewhere, so I pass the suffix at terraform invocation time or in a .tfvars file.
How can I get the following code to work, or how else should I do it?
module "alb" {
...
# these work but is ugly and inflexible
# connect_alb_client_id = var.connect_alb_client_id-dev
# connect_alb_client_id = var.connect_alb_client_id-stg
# this doesn't work
connect_alb_client_id = "${var.connect_alb_client_id}${var.suffix}"
# and neither does this
connect_alb_client_id = "${var.connect_alb_client_id${var.suffix}}"
# so what is the correct syntax or alternative way of doing it
...
}
variable "suffix"
type = string
default = "-dev"
# default = "-stg"
}
variable "connect_alb_client_id-dev" {
type = string
default = "abcdef"
}
variable "connect_alb_client_id-stg {
type = string
default = "ghijkl"
}
Your connect_alb_client_id should be a map with keys of dev, stg and so on:
variable "connect_alb_client_id" {
default = {
dev = "abcdef"
stg = "ghijkl"
}
}
then:
module "alb" {
connect_alb_client_id = var.connect_alb_client[var.suffix]
}

How to add value to matrix

I want to add value to matrix.
But, I could not do it.
I made the stepfunction for reinforcement learning and I calculated value which named R.
I want to gather R of value and add to matrix named RR.
However, all the calculated values are not added.
Is this because it's done inside a function? If it's not in a function, it works.
Please someone tell me the way to fix it.
% Define the step function
function [NextObservation, Reward, IsDone, LoggedSignals] =
myStepfunction(Action,LoggedSignals,SimplePendulum)
% get the pre statement
statePre = [-pi/2;0];
statePre(1) = SimplePendulum.Theta;
statePre(2) = SimplePendulum.AngularVelocity;
IsDone = false;
% updating states
SimplePendulum.pstep(Action);
% get the statement after updating
state = [-pi/2;0];
state(1) = SimplePendulum.Theta;
state(2) = SimplePendulum.AngularVelocity;
RR = [];
Ball_Target = 10;
Ball_Distance = Ballfunction(SimplePendulum);
R = -abs(Ball_Distance -Ball_Target); ← this the calculated value
RR = [RR,R]; ← I want to add R to RR
if (state(2) > 0) || (SimplePendulum.Y_Position < 0)
IsDone = true;
[InitialObservation, LoggedSignal] = myResetFunction(SimplePendulum);
LoggedSignal.State = [-pi/2 ; 0];
InitialObservation = LoggedSignal.State;
state = InitialObservation;
SimplePendulum.Theta =-pi/2;
SimplePendulum.AngularVelocity = 0;
end
LoggedSignals.State = state;
NextObservation = LoggedSignals.State;
Reward = +max(R);
end
If I understand you correctly, you are trying update RR with this function, and then use/see it outside the loop. This means you need to make two edits:
a) You need to pass RR in and out of the function to update it. You will need to change the first line of your code as shown here, but you will also need to change how you call the function:
function [NextObservation, Reward, IsDone, LoggedSignals, RR] =
myStepfunction(Action,LoggedSignals,SimplePendulum, RR)
b) When you do this RR = [] you delete the content of RR. To initialise RR you will need to move this line to the script that calls this function, making sure it is called before this function is ever called.

scalaFX - Titledpane: how do I get the heigth of the content?

I created a TiteldPane in scalafx
val titled: TitledPane = new TitledPane()
and put some nodes in it for my GUI.
Later I want to read out the heigth of the content of titled.
In javaFX this would be done with:
((Region) titled.getContent()).getHeight()
But if I try to read the height of the content in scala with:
titled.content.height
the height is marked as deprecated and does not compile. I've got a hint to github (scalafx/issue69) that explains why it is deprecated but does not explain how it can be done instead.
Just to clarify: I want to read out the height of the content of the titledpane, not just titled.heigth.
When titled is closed, then titled.height is 0, but I want to know what it would be when it is expanded (to detect when it has finished expanding actually).
So, how can I do this in scalafx?
EDIT:
Here is a example that shows the described error
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane
object TitledPaneEndOfExpansion extends JFXApp {
val expandedHeight = new DoubleProperty()
val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")
stage = new JFXApp.PrimaryStage {
title = "JavaFX: edit after rendering test"
val list: ListView[String] = new ListView[String](data) {
editable = true
cellFactory = TextFieldListCell.forListView()
height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
expandedHeight.value = titled.content.height
println("old height is: " + oldValue.doubleValue() + " new height is: " + newValue.doubleValue())
if (newValue.doubleValue() == expandedHeight.value) {
edit(1)
}
}
}
val titled: TitledPane = new TitledPane {
text = "titled"
content = list
}
scene = new Scene {
root = new BorderPane {
center = titled
bottom = new Button() {
text = "edit cell 1"
onAction = { _: ActionEvent => list.edit(1) }
}
}
}
expandedHeight.value = titled.content.height //set to 400
list.edit(1)
}
}
And here is the buid.sbt file:
name := "JavaFXrenderingProblem"
version := "0.1"
scalaVersion := "2.13.3"
libraryDependencies += "org.scalafx" %% "scalafx" % "15.0.1-R21"
libraryDependencies += "org.controlsfx" % "controlsfx" % "8.40.18"
// Prevent startup bug in JavaFX
fork := true
// Tell Javac and scalac to build for jvm 1.8
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
scalacOptions += "-target:jvm-1.8"
scalacOptions += "-feature"
When I just compile with plain sbt i get the compile error-message:
[info] compiling 1 Scala source to ... JavaFXrenderingProblem\target\scala-2.13\classes ...
[error] ... JavaFXrenderingProblem\src\main\scala\TitledPaneEndOfExpansion.scala:38:47: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error] expandedHeight.value = titled.content.height
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed 03.05.2021 11:09:02
I actually get two errors when I execute sbt run on your code, and I do not get a deprecation error:
[info] compiling 1 Scala source to C:\Users\SomeUser\src\SFC\target\scala-2.13\classes ...
[error] C:\Users\SomeUser\src\SFX\src\main\scala\TitledPaneEndOfExpansion.scala:23:41: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error] expandedHeight.value = titled.content.height
[error] ^
[error] C:\Users\MichaelAllen\src\SOSFX\src\main\scala\TitledPaneEndOfExpansion.scala:45:40: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error] expandedHeight.value = titled.content.height //set to 400
[error] ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed May 3, 2021 9:58:00 AM
From your code, the list value returns the contents of the TitledPane instance, titled, as a ListView[String]. It is this object whose height method you're trying to call. Correct?
The primary problem is that the content method of titled doesn't know enough about the type of the object that titled is storing. All it knows is that it is derived from javafx.scene.Node. Such Node instances do not have a height property, and hence your errors. (It's actually a little more complicated than that, but that's the simplest way to explain the issue.)
However, you already have a reference to the object that is the content of titled: list. So you can replace the second reference to titled.content.height with list.height. The first reference, in list's height's onChanged method, is accessible through the source parameter (it identifies the property that changed value, namely list.height in this case). So you can replace title.content.height with source in this case.
I notice that you're using a DoubleProperty type for expandedHeight in your example, but you need to keep looking at the value of the associated types. That's not very idiomatic. If you don't need this value to be reactive, a simple Double would suffice (but this would require that expandedHeight be declared as a var).
Combined, this produces the following code:
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane
object TitledPaneEndOfExpansion extends JFXApp {
var expandedHeight: Double = _
val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")
stage = new JFXApp.PrimaryStage {
title = "JavaFX: edit after rendering test"
val list: ListView[String] = new ListView[String](data) {
editable = true
cellFactory = TextFieldListCell.forListView()
height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
expandedHeight = source.value
println("old height is: " + oldValue.doubleValue() + " new height is: " + newValue.doubleValue())
if (newValue.doubleValue() == expandedHeight) {
edit(1)
}
}
}
val titled: TitledPane = new TitledPane {
text = "titled"
content = list
}
scene = new Scene {
root = new BorderPane {
center = titled
bottom = new Button() {
text = "edit cell 1"
onAction = { _: ActionEvent => list.edit(1) }
}
}
}
expandedHeight = list.height.value //set to 400
list.edit(1)
}
}
Your code then compiles and runs.
Updated
ScalaFX is simply a wrapper for JavaFX: each JavaFX type has a corresponding ScalaFX type. ScalaFX provides implicit conversion functions to seamlessly convert, say, a JavaFX TitledPane to a ScalaFX TitledPane, and vice versa. However, there's no inheritance relationship between the two sets of objects. That is, a JavaFX TitledPane has no type relationship to a ScalaFX TitledPane. Casting between the two sets of objects is therefore a complicated process.
If you wanted to be able to cast titled.content correctly in order to access the height property of the contents more directly, you would need to get the property's value and explicitly pattern match on the result with the JavaFX version of the object, as follows:
import javafx.scene.control.{ListView => JFXListView}
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane
object TitledPaneEndOfExpansion extends JFXApp {
var expandedHeight: Double = _
val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")
stage = new JFXApp.PrimaryStage {
title = "JavaFX: edit after rendering test"
val list: ListView[String] = new ListView[String](data) {
editable = true
cellFactory = TextFieldListCell.forListView()
height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
expandedHeight = titled.content.value match {
case lv: JFXListView[_] => lv.height.value
case _ => {
throw new RuntimeException(s"Unexpected content type: ${titled.content.getClass.getCanonicalName}")
}
}
println("old height is: " + oldValue.doubleValue() + " new height is: " + newValue.doubleValue())
if (newValue.doubleValue() == expandedHeight) {
edit(1)
}
}
}
val titled: TitledPane = new TitledPane {
text = "titled"
content = list
}
scene = new Scene {
root = new BorderPane {
center = titled
bottom = new Button() {
text = "edit cell 1"
onAction = { _: ActionEvent => list.edit(1) }
}
}
}
expandedHeight = titled.content.value match { //set to 400
case lv: JFXListView[_] => lv.height.value
case _ => throw new RuntimeException(s"Unexpected content type: ${titled.content.getClass.getCanonicalName}")
}
list.edit(1)
}
}
If you didn't have any other means of referencing the list object, that would be your only option.

How to use shared libraries with JenkinsPipelineUnit from current branch

I'm trying to use jenkinsPipelineUnit to test a JenkinsFile that exists in the same git repository as my shared libraries. This Jenkinsfile references shared libraries located in src. It appears that I must commit my shared library changes before I can test them even if I use localSource from within the retriever.
How can I load my shared libraries and unit test them without committing the code first?
Here is my current code that doesn't work:
def library = library().name('pipeline-utils')
.defaultVersion("master")
.allowOverride(true)
.implicit(false)
.targetPath(sharedLibs)
.retriever(localSource(sharedLibs))
.build()
helper.registerSharedLibrary(library)
try {
def script = runScript("pipelines/test.groovy")
}
I get this error:
file:/Users/<myuserid>/git/pipelines/test.groovy: 2:
Error on loading library pipeline-utils#myteam/pipelineUnitTest :
Directory /Users/<myuserid>/git/out/test/classes/com/company/test/pipeline-utils#myteam/pipelineUnitTest does not exists # line 2, column 1.
#Library("pipeline-utils#myteam/pipelineUnitTest") _
This isn't as easy as it sounds. JenkinsPipelineUnit isn't moving any more since one year while some interesting work is waiting on pull-requests. Here are the steps I had to go through to get this working locally, but also on jenkins where the name of my repository directory can be different each time.
1. Create a custom version of JenkinsPipelineUnit
I started from https://github.com/jenkinsci/JenkinsPipelineUnit/pull/75 but had to add some other changes. These are all the changes:
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
index f4eeb17..dc13b9c 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryConfiguration.groovy
## -18,7 +18,7 ## class LibraryConfiguration {
String targetPath
LibraryConfiguration validate() {
- if (name && defaultVersion && retriever && targetPath)
+ if (name && retriever && targetPath && ((retriever instanceof LocalSource || defaultVersion)))
return this
throw new IllegalStateException("LibraryConfiguration is not properly initialized ${this.toString()}")
}
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
index 120a316..a253f2d 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LibraryLoader.groovy
## -117,11 +117,14 ## class LibraryLoader {
}
private static boolean matches(String libName, String version, LibraryConfiguration libraryDescription) {
+ if (libraryDescription.allowOverride) {
+ return true
+ }
if (libraryDescription.name == libName) {
if (version == null) {
return true
}
- if (libraryDescription.allowOverride || libraryDescription.defaultVersion == version) {
+ if (libraryDescription.defaultVersion == version) {
return true
}
}
diff --git a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
index 61babde..4edca23 100644
--- a/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
+++ b/src/main/groovy/com/lesfurets/jenkins/unit/global/lib/LocalSource.groovy
## -11,7 +11,13 ## class LocalSource implements SourceRetriever {
#Override
List<URL> retrieve(String repository, String branch, String targetPath) {
- def sourceDir = new File(sourceURL).toPath().resolve("$repository#$branch").toFile()
+ def sourceURLPath = new File(sourceURL).toPath()
+ def sourceDir
+ if (branch) {
+ sourceDir = sourceURLPath.resolve("$repository#$branch").toFile()
+ } else {
+ sourceDir = sourceURLPath.resolve(repository).toFile()
+ }
if (sourceDir.exists()) {
return [sourceDir.toURI().toURL()]
}
2. Register your current repository directory as your shared library
Inspired from: https://github.com/jimcroft/jenkinslib-example/blob/master/test/com/example/TestCase1.groovy
in your TestClass.groovy:
void setup() {
String repositoryDirectoryName = FilenameUtils.getName(System.getProperty("user.dir"))
String dirPath = new File( System.getProperty("user.dir") )
.getAbsoluteFile()
.getParentFile()
.getAbsolutePath()
// This next call bypasses registerSharedLibrary; to allow registering a library with a different directory name
helper.libraries.put('my-jenkins-library', library(repositoryDirectoryName)
.allowOverride(true)
.implicit(false)
.targetPath(dirPath)
.retriever(localSource(dirPath))
.build())

Lua: pass context into loadstring?

I'm trying to pass context into a dynamic expression that I evaluate every iteration of a for loop. I understand that the load string only evaluates within a global context meaning local variables are inaccessible. In my case I work around this limitation by converting a local into a global for the purpose of the string evaluation. Here's what I have:
require 'cosmo'
model = { { player = "Cliff", age = 35, gender = "male" }, { player = "Ally", age = 36, gender = "female" }, { player = "Jasmine", age = 13, gender = "female" }, { player = "Lauren", age = 6.5, gender = "female" } }
values = { eval = function(args)
output = ''
condition = assert(loadstring('return ' .. args.condition))
for _, it in ipairs(model) do
each = it
if condition() then
output = output .. each.player .. ' age: ' .. each.age .. ' ' .. '\n'
end
end
return output
end }
template = "$eval{ condition = 'each.age < 30' }"
result = cosmo.fill(template, values)
print (result)
My ultimate goal (other than mastering Lua) is to build out an XSLT like tempting engine where I could do something like:
apply_templates{ match = each.age > 30}[[<parent-player>$each.player</parent-player>]]
apply_templates{ match = each.age > 30}[[<child-player>$each.player</child-player>]]
...And generate different outputs. Currently I'm stuck on my above hawkish means of sharing a local context thru a global. Does anyone here have better insight on how I'd go about doing what I'm attempting to do?
It's worth noting that setfenv was removed from Lua 5.2 and loadstring is deprecated. 5.2 is pretty new so you won't have to worry about it for a while, but it is possible to write a load routine that works for both versions:
local function load_code(code, environment)
if setfenv and loadstring then
local f = assert(loadstring(code))
setfenv(f,environment)
return f
else
return assert(load(code, nil,"t",environment))
end
end
local context = {}
context.string = string
context.table = table
-- etc. add libraries/functions that are safe for your application.
-- see: http://lua-users.org/wiki/SandBoxes
local condition = load_code("return " .. args.condition, context)
Version 5.2's load handles both the old loadstring behavior and sets the environment (context, in your example). Version 5.2 also changes the concept of environments, so loadstring may be the least of your worries. Still, it's something to consider to possibly save yourself some work down the road.
You can change the context of a function with setfenv(). This allows you to basically sandbox the loaded function into its own private environment. Something like the following should work:
local context = {}
local condition = assert(loadstring('return ' .. args.condition))
setfenv(condition, context)
for _, it in ipairs(model) do
context['each'] = it
if condition() then
-- ...
This will also prevent the condition value from being able to access any data you don't want it to, or more crucially, modifying any data you don't want it to. Note, however, that you'll need to expose any top-level bindings into the context table that you want condition to be able to access (e.g. if you want it to have access to the math package then you'll need to stick that into context). Alternatively, if you don't have any problem with condition having global access and you simply want to deal with not making your local a global, you can use a metatable on context to have it pass unknown indexes through to _G:
setmetatable(context, { __index = _G })