Skip to content

Commit 204761b

Browse files
authored
Add custom validation class (#184)
1 parent 2e1fa40 commit 204761b

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

bower.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"purescript-heterogeneous": ">=0.3.0 <0.5.0",
4646
"purescript-free": "^5.1.0",
4747
"purescript-colors": "^5.0.0",
48-
"purescript-web-uievents": "^2.0.0"
48+
"purescript-web-uievents": "^2.0.0",
49+
"purescript-foreign-generic": "^10.0.0"
4950
},
5051
"devDependencies": {
5152
"purescript-debug": "^4.0.0",

docs/Examples/Form.example.purs

+8-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Data.Foldable (foldMap)
1111
import Data.Int as Int
1212
import Data.Lens (iso)
1313
import Data.Lens.Iso (mapping)
14+
import Data.Lens.Iso.Newtype (_Newtype)
1415
import Data.Lens.Record (prop)
1516
import Data.Maybe (Maybe(..), isNothing, maybe)
1617
import Data.Monoid as Monoid
@@ -23,15 +24,15 @@ import Effect.Aff (Milliseconds(..), delay, error, throwError)
2324
import Effect.Class (liftEffect)
2425
import Effect.Random (randomRange)
2526
import Effect.Unsafe (unsafePerformEffect)
27+
import Heterogeneous.Mapping as H
2628
import Lumi.Components.Button as Button
2729
import Lumi.Components.Color (colors)
2830
import Lumi.Components.Column (column, column_)
2931
import Lumi.Components.Example (example)
30-
import Lumi.Components.Form (FormBuilder, Validated)
32+
import Lumi.Components.Form (class CustomModifyValidated, FormBuilder, ModifyValidatedProxy, Validated)
3133
import Lumi.Components.Form as F
3234
import Lumi.Components.Form.Defaults (formDefaults)
3335
import Lumi.Components.Form.Table as FT
34-
import Lumi.Components.Form.Validation (ValidatedNewtype, _ValidatedNewtype)
3536
import Lumi.Components.Input as Input
3637
import Lumi.Components.LabeledField (RequiredField(..))
3738
import Lumi.Components.Modal (dialog)
@@ -201,7 +202,7 @@ type User =
201202
, descriptiveCheckbox :: Boolean
202203
, height :: Validated String
203204
, addresses :: Validated (Array Address)
204-
, pets :: Validated (Array (ValidatedNewtype Pet))
205+
, pets :: Validated (Array (ModifyValidatedProxy Pet))
205206
-- ^ validation helpers like `setModified` need a little assistance getting
206207
-- into Newtypes like `Pet`, hence this `ValidatedNewtype` wrapper
207208
, leastFavoriteColors :: Validated (Array String)
@@ -234,6 +235,9 @@ newtype Pet = Pet
234235

235236
derive instance ntPet :: Newtype Pet _
236237

238+
instance cmvPet :: CustomModifyValidated Pet where
239+
customModifyValidated f (Pet a) = Pet (H.mapping f a)
240+
237241
type ValidatedPet =
238242
{ name :: NonEmptyString
239243
, animal :: String
@@ -343,7 +347,7 @@ userForm = ado
343347

344348
F.section "Pets"
345349
pets <-
346-
F.focus (prop (SProxy :: SProxy "pets") <<< mapping (mapping _ValidatedNewtype))
350+
F.focus (prop (SProxy :: SProxy "pets") <<< mapping (mapping (_Newtype <<< _Newtype)))
347351
$ F.warn (\pets ->
348352
Monoid.guard (Array.null pets) (pure "You should adopt a pet.")
349353
)

src/Lumi/Components/Form.purs

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ import Lumi.Components.FetchCache as FetchCache
8383
import Lumi.Components.Form.Defaults (formDefaults) as Defaults
8484
import Lumi.Components.Form.Internal (Forest, FormBuilder'(..), FormBuilder, SeqFormBuilder, Tree(..), formBuilder, formBuilder_, invalidate, pruneTree, sequential)
8585
import Lumi.Components.Form.Internal (Forest, FormBuilder', FormBuilder, SeqFormBuilder', SeqFormBuilder, Tree(..), formBuilder, formBuilder_, invalidate, listen, parallel, revalidate, sequential) as Internal
86-
import Lumi.Components.Form.Validation (ModifyValidated(..), Validated(..), ValidatedNewtype(..), Validator, _Validated, _ValidatedNewtype, fromValidated, mustBe, mustEqual, nonEmpty, nonEmptyArray, nonNull, nonEmpty', nonEmptyArray', nonNull', optional, setFresh, setModified, validDate, validInt, validNumber, validDate', validInt', validNumber', validated, warn) as Validation
86+
import Lumi.Components.Form.Validation (ModifyValidated(..), Validated(..), Validator, ModifyValidatedProxy(..), class CustomModifyValidated, customModifyValidated, _Validated, fromValidated, mustBe, mustEqual, nonEmpty, nonEmptyArray, nonNull, nonEmpty', nonEmptyArray', nonNull', optional, setFresh, setModified, validDate, validInt, validNumber, validDate', validInt', validNumber', validated, warn) as Validation
8787
import Lumi.Components.Input (alignToInput)
8888
import Lumi.Components.Input as Input
8989
import Lumi.Components.LabeledField (RequiredField(..), labeledField, labeledFieldValidationErrorStyles, labeledFieldValidationWarningStyles)

src/Lumi/Components/Form/Validation.purs

+44-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module Lumi.Components.Form.Validation
1010
, _Validated, _Fresh, _Modified
1111
, setFresh, setModified
1212
, ModifyValidated(..)
13-
, ValidatedNewtype(..), _ValidatedNewtype
13+
, ModifyValidatedProxy(..), class CustomModifyValidated, customModifyValidated
1414
, class CanValidate, fresh, modified, fromValidated
1515
, validated
1616
, warn
@@ -26,8 +26,10 @@ import Data.Either (Either(..), either, hush, note)
2626
import Data.Enum (toEnum)
2727
import Data.Eq (class Eq1)
2828
import Data.Foldable (foldMap)
29+
import Data.Function (on)
30+
import Data.Generic.Rep (class Generic, from, to)
2931
import Data.Int as Int
30-
import Data.Lens (Lens, Prism', Iso', lens, over, prism', review, view)
32+
import Data.Lens (Lens, Prism', lens, over, prism', review, view)
3133
import Data.Lens.Iso.Newtype (_Newtype)
3234
import Data.Maybe (Maybe(..))
3335
import Data.Monoid (guard)
@@ -40,6 +42,7 @@ import Data.String.NonEmpty (NonEmptyString)
4042
import Data.String.NonEmpty (fromString) as NES
4143
import Data.String.Pattern (Pattern(..))
4244
import Data.Traversable (traverse)
45+
import Foreign.Generic (class Decode, class Encode, decode, encode)
4346
import Heterogeneous.Mapping (class MapRecordWithIndex, class Mapping, ConstMapping, hmap, mapping)
4447
import Lumi.Components.Column (column)
4548
import Lumi.Components.Form.Internal (Forest, FormBuilder, FormBuilder'(..), Tree(..))
@@ -160,6 +163,18 @@ instance applyValidated :: Apply Validated where
160163
instance applicativeValidated :: Applicative Validated where
161164
pure = Fresh
162165

166+
instance genericValidated :: Generic value rep => Generic (Validated value) rep where
167+
to = Fresh <<< to
168+
from (Fresh value) = from value
169+
from (Modified value) = from value
170+
171+
instance decodeValidated :: Decode value => Decode (Validated value) where
172+
decode value = Fresh <$> decode value
173+
174+
instance encodeValidated :: Encode value => Encode (Validated value) where
175+
encode (Fresh value) = encode value
176+
encode (Modified value) = encode value
177+
163178
-- | Lens for viewing and modifying `Validated` values.
164179
_Validated :: forall a b. Lens (Validated a) (Validated b) a b
165180
_Validated = flip lens ($>) $
@@ -203,12 +218,31 @@ setModified = mapping (ModifyValidated (Modified <<< view _Validated))
203218
-- | records containing `Validated` values.
204219
newtype ModifyValidated = ModifyValidated (Validated ~> Validated)
205220

206-
newtype ValidatedNewtype a = ValidatedNewtype a
221+
newtype ModifyValidatedProxy a = ModifyValidatedProxy a
222+
223+
unModifyValidatedProxy :: forall value. ModifyValidatedProxy value -> value
224+
unModifyValidatedProxy (ModifyValidatedProxy value) = value
225+
226+
derive instance ntMVP :: Newtype (ModifyValidatedProxy a) _
207227

208-
derive instance ntMVP :: Newtype (ValidatedNewtype a) _
228+
instance eqValidatedNewtype :: Eq value => Eq (ModifyValidatedProxy value) where
229+
eq = eq `on` unModifyValidatedProxy
209230

210-
_ValidatedNewtype :: forall s a. Newtype s a => Iso' (ValidatedNewtype s) a
211-
_ValidatedNewtype = _Newtype <<< _Newtype
231+
instance ordValidatedNewtype :: Ord value => Ord (ModifyValidatedProxy value) where
232+
compare = compare `on` unModifyValidatedProxy
233+
234+
instance genericValidatedNewtype :: Generic value rep => Generic (ModifyValidatedProxy value) rep where
235+
to = ModifyValidatedProxy <<< to
236+
from = from <<< unModifyValidatedProxy
237+
238+
instance decodeValidatedNewtype :: Decode value => Decode (ModifyValidatedProxy value) where
239+
decode value = ModifyValidatedProxy <$> decode value
240+
241+
instance encodeValidatedNewtype :: Encode value => Encode (ModifyValidatedProxy value) where
242+
encode (ModifyValidatedProxy value) = encode value
243+
244+
class CustomModifyValidated a where
245+
customModifyValidated :: ModifyValidated -> a -> a
212246

213247
instance modifyValidated :: Mapping ModifyValidated a a => Mapping ModifyValidated (Validated a) (Validated a) where
214248
mapping m@(ModifyValidated f) = over _Validated (mapping m) <<< f
@@ -219,8 +253,10 @@ else instance modifyValidatedRecord ::
219253
mapping d = hmap d
220254
else instance modifyValidatedArray :: Mapping ModifyValidated a a => Mapping ModifyValidated (Array a) (Array a) where
221255
mapping d = map (mapping d)
222-
else instance modifyValidatedNewtype :: (Newtype a b, Mapping ModifyValidated b b) => Mapping ModifyValidated (ValidatedNewtype a) (ValidatedNewtype a) where
223-
mapping d = over (_Newtype <<< _Newtype) (mapping d)
256+
else instance modifyValidatedMaybe :: Mapping ModifyValidated a a => Mapping ModifyValidated (Maybe a) (Maybe a) where
257+
mapping d = map (mapping d)
258+
else instance modifyValidatedProxy :: (CustomModifyValidated a, Mapping ModifyValidated a a) => Mapping ModifyValidated (ModifyValidatedProxy a) (ModifyValidatedProxy a) where
259+
mapping f = over _Newtype (customModifyValidated f)
224260
else instance modifyValidatedIdentity :: Mapping ModifyValidated a a where
225261
mapping _ = identity
226262

0 commit comments

Comments
 (0)