Easy DSL for adding XEP-0122 validation to fields

Stephen Paul Weber created

Change summary

lib/form_template.rb       | 26 ++++++++++++++-
test/test_form_template.rb | 68 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 2 deletions(-)

Detailed changes

lib/form_template.rb 🔗

@@ -48,8 +48,30 @@ class FormTemplate
 			@__form.instructions = s
 		end
 
-		def field(**kwargs)
-			@__form.fields = @__form.fields + [kwargs]
+		def validate(f, datatype: nil, **kwargs)
+			Nokogiri::XML::Builder.with(f) do |x|
+				x.validate(
+					xmlns: "http://jabber.org/protocol/xdata-validate",
+					datatype: datatype || "xs:string"
+				) do
+					x.basic unless validation_type(x, **kwargs)
+				end
+			end
+		end
+
+		def validation_type(x, open: false, regex: nil, range: nil)
+			x.open if open
+			x.range(min: range.first, max: range.last) if range
+			x.regex(regex.source) if regex
+			open || regex || range
+		end
+
+		def field(datatype: nil, open: false, regex: nil, range: nil, **kwargs)
+			f = Blather::Stanza::X::Field.new(kwargs)
+			if datatype || open || regex || range
+				validate(f, datatype: datatype, open: open, regex: regex, range: range)
+			end
+			@__form.fields += [f]
 		end
 
 		def xml

test/test_form_template.rb 🔗

@@ -46,6 +46,74 @@ class FormTemplateTest < Minitest::Test
 		assert_equal "INSTRUCTIONS", form.instructions
 	end
 
+	def test_form_validate_basic
+		template = FormTemplate.new(<<~TEMPLATE)
+			form!
+			field(var: "thevar", label: "thelabel", datatype: "xs:integer")
+		TEMPLATE
+		form = template.render
+		assert_equal 1, form.fields.length
+		assert_equal "thevar", form.fields[0].var
+		assert_equal "thelabel", form.fields[0].label
+		validate = form.fields[0].find(
+			"ns:validate",
+			ns: "http://jabber.org/protocol/xdata-validate"
+		).first
+		assert_equal "xs:integer", validate[:datatype]
+		assert_equal "basic", validate.children.first.name
+	end
+
+	def test_form_validate_open
+		template = FormTemplate.new(<<~TEMPLATE)
+			form!
+			field(var: "thevar", label: "thelabel", open: true)
+		TEMPLATE
+		form = template.render
+		assert_equal 1, form.fields.length
+		assert_equal "thevar", form.fields[0].var
+		assert_equal "thelabel", form.fields[0].label
+		validate = form.fields[0].find(
+			"ns:validate",
+			ns: "http://jabber.org/protocol/xdata-validate"
+		).first
+		assert_equal ["open"], validate.children.map(&:name)
+	end
+
+	def test_form_validate_regex
+		template = FormTemplate.new(<<~TEMPLATE)
+			form!
+			field(var: "thevar", label: "thelabel", regex: /[A-Z]/)
+		TEMPLATE
+		form = template.render
+		assert_equal 1, form.fields.length
+		assert_equal "thevar", form.fields[0].var
+		assert_equal "thelabel", form.fields[0].label
+		validate = form.fields[0].find(
+			"ns:validate",
+			ns: "http://jabber.org/protocol/xdata-validate"
+		).first
+		assert_equal ["regex"], validate.children.map(&:name)
+		assert_equal "[A-Z]", validate.children.first.content
+	end
+
+	def test_form_validate_range
+		template = FormTemplate.new(<<~TEMPLATE)
+			form!
+			field(var: "thevar", label: "thelabel", range: (10..22))
+		TEMPLATE
+		form = template.render
+		assert_equal 1, form.fields.length
+		assert_equal "thevar", form.fields[0].var
+		assert_equal "thelabel", form.fields[0].label
+		validate = form.fields[0].find(
+			"ns:validate",
+			ns: "http://jabber.org/protocol/xdata-validate"
+		).first
+		assert_equal ["range"], validate.children.map(&:name)
+		assert_equal "10", validate.children.first[:min]
+		assert_equal "22", validate.children.first[:max]
+	end
+
 	def test_no_type
 		template = FormTemplate.new(<<~TEMPLATE)
 			title "TITLE"