Skip to content

Commit

Permalink
Merge pull request #92 from ellykits/issue-91-fiz-a-bug-with-rule-engine
Browse files Browse the repository at this point in the history
Updated forms to recreate the rule engine bug
  • Loading branch information
ellykits authored May 28, 2020
2 parents e8d9f4e + 222824b commit 3f53550
Show file tree
Hide file tree
Showing 86 changed files with 2,540 additions and 1,020 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Add the library as a dependency to your app's `build.gradle` file
```groovy
dependencies {
//....
implementation "com.nerdstone:neat-form-core:1.0.12"
implementation "com.nerdstone:neat-form-core:1.1.0"
//....
}
Expand Down Expand Up @@ -82,7 +82,7 @@ Add the library in the dependency section of your application's `build.gradle` f
```groovy
dependencies {
//consume library - use the latest version available on github packages
implementation "com.nerdstone:neat-form-core:1.0.12"
implementation "com.nerdstone:neat-form-core:1.1.0"
//....
}
Expand Down Expand Up @@ -851,7 +851,7 @@ Custom `sample_one_form_custom_layout.xml` layout used for rendering views
* [Easy Rules](https://github.com/j-easy/easy-rules) - Rules Engine library
* [Mockk](https://mockk.io/) - Testing Framework Kotlin
* [GSON](https://github.com/google/gson) - Parsing JSON files
* [Smart Material Spinner](https://github.com/Chivorns/SmartMaterialSpinner) - Powerful android spinner library
* [Smart Material Spinner](https://github.com/Chivorns/SmartMaterialSpinner) - Powerful android spinner library

## RoadMap

Expand All @@ -862,14 +862,14 @@ Custom `sample_one_form_custom_layout.xml` layout used for rendering views
| ✔️ |️ Form Fields Validation |
| ✔️ |️ Support Multi-Step Forms |
| ✔️ |️ Rules Engine integration - handle form skip logic and calculations |
| ❌ |️ Support image and location picker and barcode reader |
| ❌ |️ Multi language support |
| ❌ |️ Ability to obtain and render `JSON`form from server |

>If you need addition of more attributes on the current widgets or new widget implementations, create an issue. Contributions are also welcome.

## Awesome Contributors

* [ellykits](https://github.com/ellykits)
* [cozej4](https://github.com/cozej4)

## License
Expand Down
21 changes: 15 additions & 6 deletions neat-form-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jacoco {
def properties = new Properties()
properties.load(new FileInputStream(rootProject.file("local.properties")))

version = "1.0.12"
version = "1.1.0"

android {
compileSdkVersion 28
Expand All @@ -31,6 +31,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}

lintOptions {
abortOnError false
Expand Down Expand Up @@ -70,9 +74,9 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.core:core-ktx:1.2.0"
api "androidx.appcompat:appcompat:1.1.0"
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
api("com.nerdstone:neat-android-stepper:1.0.6") {
transitive = true
exclude group: 'androidx.appcompat', module: 'appcompat'
Expand All @@ -87,17 +91,22 @@ dependencies {
api 'org.jeasy:easy-rules-core:3.3.0'
api 'org.jeasy:easy-rules-mvel:3.3.0'
api 'com.google.code.gson:gson:2.8.6'
api 'com.github.chivorns:smartmaterialspinner:1.1.6'
api 'com.github.chivorns:smartmaterialspinner:1.2.1'

testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'
testImplementation 'org.robolectric:robolectric:4.3.1'
testImplementation "org.robolectric:shadows-multidex:4.3.1"
debugImplementation 'androidx.fragment:fragment-testing:1.2.4'

testImplementation 'androidx.test:core:1.2.0'
testImplementation "io.mockk:mockk:$mockKVersion"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'

androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

// debugImplementation because LeakCanary should only run in debug builds.
// debugImplementation "com.squareup.leakcanary:leakcanary-android:2.3"
}

repositories {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output=none
3 changes: 1 addition & 2 deletions neat-form-core/src/main/assets/sample/sample_one_form.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"properties": {
"hint": "Enter adult's phone number",
"type": "name",
"text": "",
"padding": "8"
},
"meta_data": {
Expand All @@ -31,7 +30,7 @@
"error_message": "Not a valid phone number"
}
],
"subjects": "age:number, child:text, dob:number",
"subjects": "age:number, child:text",
"required_status": "True:please add phone number"
},
{
Expand Down
20 changes: 9 additions & 11 deletions neat-form-core/src/main/assets/sample/sample_two_form.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
"name": "adult",
"type": "text_input_edit_text",
"properties": {
"hint": "This is an adult",
"type": "name",
"text": "",
"padding": "8"
"hint": "This is an adult"
},
"meta_data": {
"openmrs_entity": "",
Expand All @@ -22,15 +19,16 @@
"validation": [
{
"validation_name": "length",
"condition": "value.length() < 8",
"error_message": "value should be less than eight digits"
},{
"validation_name": "special characters",
"condition": "!value.contains('@')",
"error_message": "value should not contain special character"
"condition": "value.length() < 11",
"error_message": "value should be less than ten digits"
},
{
"validation_name": "phone number",
"condition": "value.matches(\"\\\\d{10}\")",
"error_message": "Not a valid phone number"
}
],
"subjects": "age:number, child:text, dob:number",
"subjects": "age:number",
"required_status": "True:please add phone number"
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,90 @@
package com.nerdstone.neatformcore.domain.builders

import android.content.Context
import android.view.View
import com.nerdstone.neatandroidstepper.core.widget.NeatStepperLayout
import com.nerdstone.neatformcore.domain.model.JsonFormStepBuilderModel
import com.nerdstone.neatformcore.domain.model.NFormViewData
import com.nerdstone.neatformcore.domain.view.FormValidator
import com.nerdstone.neatformcore.rules.RulesFactory
import com.nerdstone.neatformcore.viewmodel.DataViewModel
import com.nerdstone.neatformcore.viewmodel.FormViewModel
import com.nerdstone.neatformcore.views.handlers.ViewDispatcher
import kotlin.reflect.KClass

/**
* [FormBuilder] contract is implemented by any builder using any preferred Schema.
*
* [formString] is the string representation of the form depending on the schema which can be JSON
* YAML or even XML
*
* The form builder also uses the [formValidator] to validate fields.
*
* Before building views, views are first registered withing the [registeredViews] map. This is a map
* that holds the view type against the actual class type of the view that will used to create the view for example
* for an EditText inside the view type map you will have
* ["edit_text" -> EditTextNFormView::class]
*
* This map can be overridden if you have custom views that you also want rendered. Just remember to register the views
* before building the form.
*
*/
interface FormBuilder {

var jsonString: String?
var formString: String?

val context: Context

var neatStepperLayout: NeatStepperLayout
var dataViewModel: DataViewModel

var viewModel: DataViewModel

var formValidator: FormValidator
val formValidator: FormValidator

var registeredViews: HashMap<String, KClass<*>>

fun buildForm(
jsonFormStepBuilderModel: JsonFormStepBuilderModel? = null, viewList: List<View>? = null
): FormBuilder
var formViewModel: FormViewModel

fun createFormViews(
context: Context, views: List<View>? = null,
jsonFormStepBuilderModel: JsonFormStepBuilderModel? = null
)
val rulesFactory: RulesFactory
val viewDispatcher: ViewDispatcher

/**
* This method reads the rules file available withing the given [context] and returns true when done
* false otherwise. You also setup the [rulesFileType] in order for this to work. The form builder
* uses Rules engine that allows you to write rules either in JSON or YML format.
*/
fun registerFormRulesFromFile(
context: Context, rulesFileType: RulesFactory.RulesFileType
context: Context, rulesFileType: RulesFactory.RulesFileType
): Boolean

/**
* This method returns a map of the form data. Where the key is the field name and the value is of
* type [NFormViewData]
*/
fun getFormData(): HashMap<String, NFormViewData>

/**
* This method returns Form metadata as specified on the form
*/
fun getFormMetaData(): Map<String, Any>

/**
* This method returns a JSON string representation of the form data including the metadata specified on the
* form.
*/
fun getFormDataAsJson(): String

/**
* Use this method to add view types to the [registeredViews] property
*/
fun registerViews()

/**
* Call this method before building the form to supply the fields with data obtained from [formDataJson]. You
* can optionally pass [readOnlyFields] which will be disabled.
*/
fun withFormData(
formDataJson: String, readOnlyFields: MutableSet<String> = mutableSetOf()
): FormBuilder

/**
* This method is called right after the views have been created. The data provided will be delegated to
* the view model which will call the method for setting values on its observers (fields)
*/
fun preFillForm()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.nerdstone.neatformcore.domain.listeners

/**
* @author Elly Nerdstone
*
* Calculation listener. This listener listens for changes in calculations.
* Any views implementing this can make use of the updated value
* Any views implementing this can make use of the updated calculation value
*/
interface CalculationChangeListener {
/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ data class NFormViewData(
@Expose
var value: Any? = null,
@Expose
@SerializedName("meta_data")
var metadata: Map<String, Any>? = null
@SerializedName("meta_data", alternate = ["metadata"])
var metadata: Map<String, Any>? = null,
@Expose
var visible: Boolean = true

) : Serializable
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.nerdstone.neatformcore.domain.view

import com.nerdstone.neatformcore.domain.builders.FormBuilder

interface FormValidator {

var formBuilder: FormBuilder

val invalidFields: HashSet<String>

val requiredFields: HashSet<String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import com.nerdstone.neatformcore.domain.listeners.DataActionListener
import com.nerdstone.neatformcore.domain.listeners.VisibilityChangeListener
import com.nerdstone.neatformcore.domain.model.NFormViewDetails
import com.nerdstone.neatformcore.domain.model.NFormViewProperty
import com.nerdstone.neatformcore.views.handlers.ViewDispatcher

/**
* @author Elly Nerdstone
*
* very view must implement this interface. This contract basically defines all the interactions
* between fields. The form builder needs to be aware of how the fields are going to handle
* validation, required values and how they shall pass their values.
*/
interface NFormView {

var dataActionListener: DataActionListener?
Expand All @@ -21,9 +27,27 @@ interface NFormView {

var formValidator: FormValidator

var initialValue: Any?

/**
* This function is called to reset the value of the field when it is hidden
*/
fun resetValueWhenHidden()

fun validateValue() : Boolean
/**
* This function is to implement field validation and it returns a [Boolean] value.
*/
fun validateValue(): Boolean

/**
* This function is used by fields to handle their required status
*/
fun trackRequiredField()

/**
* This method sets [value] of the field. And it also accepts an optional parameter called
* [enabled] that is used to indicate whether you want the field to be disabled or not. Use the
* option if you want to make the field readonly.
*/
fun setValue(value: Any, enabled: Boolean = true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ package com.nerdstone.neatformcore.domain.view

import com.nerdstone.neatformcore.domain.builders.FormBuilder
import com.nerdstone.neatformcore.domain.model.NFormViewProperty
import com.nerdstone.neatformcore.views.handlers.ViewDispatcher
import java.io.Serializable

interface RootView: Serializable{
interface RootView : Serializable {

var formBuilder: FormBuilder

fun initRootView(formBuilder: FormBuilder): RootView

fun addChild(nFormView: NFormView)

fun addChildren(viewProperties: List<NFormViewProperty>, viewDispatcher: ViewDispatcher,buildFromLayout: Boolean=false)

fun addChildren(
viewProperties: List<NFormViewProperty>, formBuilder: FormBuilder,
buildFromLayout: Boolean = false
)
}
Loading

0 comments on commit 3f53550

Please sign in to comment.