Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
Oleksandra Stolyar (Tomilina)
dry-validation vs dry-schema 1.*.*
Harry Potter and…
the Very Important Gem
Update
to the Major Version
“a set of gems that bring solutions
to common problems”
dry-struct
dry-logic
dry-container
dry-validation
dry-transaction
...
Backstory…
ME
dry-validation
Yeah, that feeling…
2017,
February
• Plans for dry-validation + dry-schema (a new gem!) by Piotr Solnica
2019,
January
• dry-schema introduced...
exceptionally about my experience
how I structured knowledge about these gems
features that were crucial for me
workaround...
bugs that were fixed since dry-validation 0.13
all great features that dry-validation or dry-schema can provide
What I won...
The Challenge
Operations gem ‘trailblazer-operation’
Params schema and Validation Macro in operations gem ‘dry-validation’
Error normali...
Trailblazer Operation v2.0 Contract documentation mentions the ability of validation using:
Dry::Schema (dry-validation < ...
For same purposes you can use:
dry-transaction (active v0.13.0, release of v1.0.0 is planned but further development will ...
That is the question… 🤔
Reusing schemas
Rules
Custom Predicates
Custom Error Messages
Schema/Contract Configuration
Features to migrate:
dry-validation <
0.13
dry-validation > 1.0
(business
validations)
dry-schema > 1.0
(shape & type
validations)
Main changes
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schema
Reusing Schemas
{
"sender": {
"first_name": "Draco",
"last_name": "Malfoy"
},
"receiver": {
"first_name": "Lord",
"last_name": "Voldemort"...
Reusing Schemas dry-validation 0.13
optional(:sender).schema(PersonalInfo::FormValidation)
required(:receiver).schema(Pers...
Reusing Schemas dry-validation 1.3.1
params do
optional(:sender).hash(PersonalInfo::FormValidation)
required(:receiver).ha...
Reusing Schemas dry-schema 1.3.4
optional(:sender).hash(PersonalInfo::FormValidation)
required(:receiver).hash(PersonalInf...
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schem...
Rules
{
"hogwarts_student": {
"name": "Sasha",
"age": 18,
"parents_owl_id": "hedwig_was_the_best@owail.com",
"has_owl": true,
"h...
required(:hogwarts_student).schema do
required(:name).filled(:str?)
required(:age).filled(:int?, gt?: 11, lteq?: 18)
requi...
params do
required(:hogwarts_student).schema do
required(:name).filled(:string)
required(:age).filled(:integer)
required(:...
required(:hogwarts_student).schema do
required(:name).filled(:string)
required(:age).filled(:integer, gt?: 11, lteq?: 18)
...
{
"hogwarts_house": {
"name": "Slotherin",
"head": "Snape",
"ghost": "Bloody Baron",
"immutable_info": {
"founder_name": "...
required(:hogwarts_house).schema do
# ...
required(:head).filled(:str?,
size?: 1..255,
format?: SOME_MAGIC_REGEX)
# ...
re...
params do
required(:hogwarts_house).schema do
# ...
required(:head).filled(:string)
# ...
required(:common_room).schema do...
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schema
Custom predicates
it needs to be validated:
{
"hogwarts_student": {
"name": "Sasha",
"age": 18,
"parents_owl_id": "hedwig_was_the_best@owail...
required(:hogwarts_student).schema do
# ...
required(:parents_owl_id).filled(:str?, :owl_id?)
optional(:has_owl).filled(:b...
params do
required(:hogwarts_student).schema do
# ...
required(:parents_owl_id).filled(:string)
required(:has_owl).filled(...
params do
required(:hogwarts_student).schema do
# ...
required(:parents_owl_id).filled(:string)
required(:has_owl).filled(...
## 2
class CommonContract < Dry::Validation::Contract
option :owl_validator
end
class OwlValidator
def self.valid?(value)
...
params do
required(:hogwarts_student).schema do
# ...
required(:parents_owl_id).filled(:string)
required(:has_owl).filled(...
class CommonContract < Dry::Validation::Contract
register_macro(:owl_id_format) do
unless OwlLib.valid?(value)
key.failure...
Custom Predicates dry-schema 1.3.4
required(:hogwarts_student).schema do
# ...
required(:parents_owl_id).filled(:string)
required(:has_owl).filled(:bool)
req...
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schem...
Custom Error Messages
Custom Error Messages dry-validation 0.13
class CommonSchema < Dry::Validation::Schema::Params
configure do
I18n.config.ba...
Custom Error Messages dry-validation 1.3.1
class CommonContract < Dry::Validation::Contract
config.messages.load_paths << ...
Custom Error Messages dry-schema 1.3.4
en:
dry_schema:
errors:
bool?: "must be bla bla bla"
CommonConfig = Dry::Schema.Par...
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schema
Configuration parameters
class CommonSchema < Dry::Validation::Schema::Params
configure do
# custom errors files and I18n configs
end
# predicates
...
Configuration parameters dry-validation 1.3.1
class CommonContract < Dry::Validation::Contract
# custom errors files and I...
Configuration parameters dry-schema 1.3.4
CommonConfig = Dry::Schema.Params do
# custom errors files and I18n configs
# cu...
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dry-schem...
Totals
Reusing
schemas
Rules
Custom
predicates
Custom
Error
Messages
Configuration
Parameters
dry-validation
(Contract)
dr...
This separation will not only make dry-v much simpler internally, but also allow us to have schemas for
processing/validat...
How can we draw a line between dry-validation
and dry-schema?
Use dry-validation or use both.
dry-schema for high-level ht...
About dry-rb gems
https://www.rubyguides.com/2019/01/what-is-dry-rb/
Piotr Solnica about dry-schema 1.0 release
https://so...
Questions?
Thanks for listening 🎉🐱
Dry-validation update. Dry-validation vs Dry-schema 1.0 - Aleksandra Stolyar | Ruby Meditation 29
Nächste SlideShare
Wird geladen in …5
×

Dry-validation update. Dry-validation vs Dry-schema 1.0 - Aleksandra Stolyar | Ruby Meditation 29

573 Aufrufe

Veröffentlicht am

Talk of Aleksandra Stolyar, Ruby developer at DataArt, at Ruby Meditation #29 Kyiv 14.12.2019
Next conference - http://www.rubymeditation.com/

Description:
I will talk about dry-rb ecosystem and it’s major components - dry-validation and dry-schema which are very helpful for validations. This year dry-rb introduced a major update to its’ gems and I faced some problems when decided to marry these updates with a project. This talk will cover some of differences and specifics of dry-validation and dry-schema updates.


Announcements and conference materials https://www.fb.me/RubyMeditation
News https://twitter.com/RubyMeditation
Photos https://www.instagram.com/RubyMeditation
The stream of Ruby conferences (not just ours) https://t.me/RubyMeditation

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

Dry-validation update. Dry-validation vs Dry-schema 1.0 - Aleksandra Stolyar | Ruby Meditation 29

  1. 1. Oleksandra Stolyar (Tomilina) dry-validation vs dry-schema 1.*.*
  2. 2. Harry Potter and… the Very Important Gem Update to the Major Version
  3. 3. “a set of gems that bring solutions to common problems” dry-struct dry-logic dry-container dry-validation dry-transaction dry-schema … dry-rb
  4. 4. Backstory… ME dry-validation
  5. 5. Yeah, that feeling…
  6. 6. 2017, February • Plans for dry-validation + dry-schema (a new gem!) by Piotr Solnica 2019, January • dry-schema introduced 2019, May • dry-schema v1.0.0 released 2019, June • dry-validation v1.0.0 released gems update timeline
  7. 7. exceptionally about my experience how I structured knowledge about these gems features that were crucial for me workarounds successes and failures conclusions, decisions and their consequences What this talk will be about?
  8. 8. bugs that were fixed since dry-validation 0.13 all great features that dry-validation or dry-schema can provide What I won’t cover?
  9. 9. The Challenge
  10. 10. Operations gem ‘trailblazer-operation’ Params schema and Validation Macro in operations gem ‘dry-validation’ Error normalization Macro for errors in operations gem ‘error_normalizer’ I had:
  11. 11. Trailblazer Operation v2.0 Contract documentation mentions the ability of validation using: Dry::Schema (dry-validation < v0.13.3 Schema) Reform Trailblazer Macro Contract has dry-validation v0.11.1 dependency Reform status: Since September 2019 Emanuele Magliozzi started pushing commits for old and new API compatibility which use dry-validation < v0.13.3 and dry-validation > v1.0.0 respectively. In November 2019 he pushed into Trailblazer Reform v2.3.0 with new api which uses Dry::Validation::Contract Yet docs are still not updated and 🤔 Trailblazer usage
  12. 12. For same purposes you can use: dry-transaction (active v0.13.0, release of v1.0.0 is planned but further development will be stopped) dry-monads gem ‘interactor’ Other approaches
  13. 13. That is the question… 🤔
  14. 14. Reusing schemas Rules Custom Predicates Custom Error Messages Schema/Contract Configuration Features to migrate:
  15. 15. dry-validation < 0.13 dry-validation > 1.0 (business validations) dry-schema > 1.0 (shape & type validations) Main changes
  16. 16. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema
  17. 17. Reusing Schemas
  18. 18. { "sender": { "first_name": "Draco", "last_name": "Malfoy" }, "receiver": { "first_name": "Lord", "last_name": "Voldemort" }, "receiver_address": "Castle", "text": "I'm scared", "asap": true } it needs to be validated:
  19. 19. Reusing Schemas dry-validation 0.13 optional(:sender).schema(PersonalInfo::FormValidation) required(:receiver).schema(PersonalInfo::FormValidation) required(:receiver_address).filled(:str?) required(:text).filled(:str?) optional(:asap).filled(:bool?) module PersonalInfo FormValidation = Dry::Validation.Schema required(:first_name).filled(:str?) required(:last_name).filled(:str?) end end
  20. 20. Reusing Schemas dry-validation 1.3.1 params do optional(:sender).hash(PersonalInfo::FormValidation) required(:receiver).hash(PersonalInfo::FormValidation) required(:receiver_address).filled(:string) required(:text).filled(:string) optional(:asap).filled(:bool) end module PersonalInfo FormValidation = Dry::Schema.Params do required(:first_name).filled(:string) required(:last_name).filled(:string) end end
  21. 21. Reusing Schemas dry-schema 1.3.4 optional(:sender).hash(PersonalInfo::FormValidation) required(:receiver).hash(PersonalInfo::FormValidation) required(:receiver_address).filled(:string) required(:text).filled(:string) optional(:asap).filled(:bool) module PersonalInfo FormValidation = Dry::Schema.Params do required(:first_name) { filled? > str? } required(:last_name).filled(:string) end end
  22. 22. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema Achieved with defining Schema
  23. 23. Rules
  24. 24. { "hogwarts_student": { "name": "Sasha", "age": 18, "parents_owl_id": "hedwig_was_the_best@owail.com", "has_owl": true, "has_cat_or_toad": true } } it needs to be validated:
  25. 25. required(:hogwarts_student).schema do required(:name).filled(:str?) required(:age).filled(:int?, gt?: 11, lteq?: 18) required(:parents_owl_id).filled(:str?) optional(:has_owl).filled(:bool?) optional(:has_cat_or_toad).filled(:bool?) rule(inadmissible_animal_quantity: [:has_owl, :has_cat_or_toad]) do |owl, not_owl| owl.true? ^ not_owl.true? end end Rules dry-validation 0.13
  26. 26. params do required(:hogwarts_student).schema do required(:name).filled(:string) required(:age).filled(:integer) required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end end rule('hogwarts_student.age').validate(gteq?: 11) rule('hogwarts_student.age').validate(lteq?: 18) rule(hogwarts_student: [:has_owl, :has_cat_or_toad]) do unless values[:hogwarts_student][:has_owl] ^ values[:hogwarts_student][:has_cat_or_toad] key(:owl_errors).failure(:inadmissible_animal_quantity) end end Rules dry-validation 1.3.1
  27. 27. required(:hogwarts_student).schema do required(:name).filled(:string) required(:age).filled(:integer, gt?: 11, lteq?: 18) required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end step :check_inadmissible_animal_quantity fail AddError('inadmissible_animal_quantity', path: 'has_owl'), fail_fast: true def check_inadmissible_animal_quantity(opts, output:, **) hogwarts_student = output[:hogwarts_student] hogwarts_student[:has_owl] ^ hogwarts_student[:has_cat_or_toad] end Rules dry-schema 1.3.4
  28. 28. { "hogwarts_house": { "name": "Slotherin", "head": "Snape", "ghost": "Bloody Baron", "immutable_info": { "founder_name": "Salazar Slytherin", "element": "Water", "flag": "flag_img", "animal": "Serpent", "traits": [ "Resourcefulness", ... "Lineage" ] }, "common_room": { "name": "Slytherin Dungeon", "location": "hell" } } } it needs to be validated:
  29. 29. required(:hogwarts_house).schema do # ... required(:head).filled(:str?, size?: 1..255, format?: SOME_MAGIC_REGEX) # ... required(:common_room).schema do required(:name).filled(:str?) required(:location).filled(:str?, included_in?: %w[tower underground]) end end Rules dry-validation 0.13
  30. 30. params do required(:hogwarts_house).schema do # ... required(:head).filled(:string) # ... required(:common_room).schema do required(:name).filled(:string) required(:location).filled(:string) end end end rule('hogwarts_house.head') do key.failure(:invalid_format) unless SOME_MAGIC_REGEX.match?(value) key.failure(:invalid_size, range: 1..255) unless (1..255).cover?(value.length) end rule('hogwarts_house.common_room.location') do Rules dry-validation 1.3.1
  31. 31. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema
  32. 32. Custom predicates
  33. 33. it needs to be validated: { "hogwarts_student": { "name": "Sasha", "age": 18, "parents_owl_id": "hedwig_was_the_best@owail.com", "has_owl": true, "has_cat_or_toad": true } }
  34. 34. required(:hogwarts_student).schema do # ... required(:parents_owl_id).filled(:str?, :owl_id?) optional(:has_owl).filled(:bool?) optional(:has_cat_or_toad).filled(:bool?) end class CommonSchema < Dry::Validation::Schema::Params configure do # ... end def owl_id?(value) OwlLib.valid?(value) end end Dry::Validation.Schema(CommonSchema, {}, &block) Custom Predicates dry-validation 0.13
  35. 35. params do required(:hogwarts_student).schema do # ... required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end end ## 1 rule(hogwarts_student: :parents_owl_id) do key.failure(:invalid_owl_id) unless OwlLib.valid?(value) end Custom Predicates dry-validation 1.3.0
  36. 36. params do required(:hogwarts_student).schema do # ... required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end end ## 2 rule('hogwarts_student.parents_owl_id') do unless owl_validator.valid?(value) key.failure('invalid_owl_id') end end Custom Predicates dry-validation 1.3.0
  37. 37. ## 2 class CommonContract < Dry::Validation::Contract option :owl_validator end class OwlValidator def self.valid?(value) OwlLib.valid?(value) end end MyContract.new(owl_validator: OwlValidator) Custom Predicates dry-validation 1.3.0
  38. 38. params do required(:hogwarts_student).schema do # ... required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end end ## 3 rule(hogwarts_student: :parents_owl_id).validate(:owl_id_format) Custom Predicates dry-validation 1.3.0
  39. 39. class CommonContract < Dry::Validation::Contract register_macro(:owl_id_format) do unless OwlLib.valid?(value) key.failure('not a valid owl id format') end end end MyContract.new Custom Predicates dry-validation 1.3.0
  40. 40. Custom Predicates dry-schema 1.3.4
  41. 41. required(:hogwarts_student).schema do # ... required(:parents_owl_id).filled(:string) required(:has_owl).filled(:bool) required(:has_cat_or_toad).filled(:bool) end step Validate() fail NormalizeErrors(), fail_fast: true step ValidateOwl(:hogwarts_student, :parents_owl_id) fail AddError('invalid_parents_owl_id', path: 'parents_owl_id'), fail_fast: true def ValidateOwl(*args) step = lambda do |_input, options| value = options[:output].dig(*args) OwlLib.valid?(value) end end Custom Predicates dry-schema 1.3.4
  42. 42. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema Rules Achieved with Macro
  43. 43. Custom Error Messages
  44. 44. Custom Error Messages dry-validation 0.13 class CommonSchema < Dry::Validation::Schema::Params configure do I18n.config.backend.load_translations('somewhere/custom_error_messages.yml') config.messages = :i18n end end en: errors: bool?: "must be bla bla bla" owl_number?: "must be in owl number international format" rules: inadmissible_animal_quantity: "either owl or something else"
  45. 45. Custom Error Messages dry-validation 1.3.1 class CommonContract < Dry::Validation::Contract config.messages.load_paths << 'somewhere/custom_error_messages.yml' end en: dry_validation: errors: bool?: "must be bla bla bla" rules: hogwarts_house: common_room: location: invalid: "must be somewhere in: %{list}" head: invalid_format: "must not contain magic" invalid_size: "length must be within %{range}" hogwarts_student: parents_owl_number: invalid_owl_number: "must be in owl number international format" owl_errors: inadmissible_animal_quantity: "either owl or something else"
  46. 46. Custom Error Messages dry-schema 1.3.4 en: dry_schema: errors: bool?: "must be bla bla bla" CommonConfig = Dry::Schema.Params do config.messages.load_paths << 'somewhere/custom_error_messages.yml' end
  47. 47. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema
  48. 48. Configuration parameters
  49. 49. class CommonSchema < Dry::Validation::Schema::Params configure do # custom errors files and I18n configs end # predicates # custom validation blocks end Dry::Validation.Schema(CommonSchema, {}, &block) Configuration parameters dry-validation 0.13
  50. 50. Configuration parameters dry-validation 1.3.1 class CommonContract < Dry::Validation::Contract # custom errors files and I18n configs # external dependencies # predicates as macros end MyContract.new( # list of validators )
  51. 51. Configuration parameters dry-schema 1.3.4 CommonConfig = Dry::Schema.Params do # custom errors files and I18n configs # custom types end Dry::Schema.Params( processor: 'Params', config: CommonConfig.config, &block )
  52. 52. Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema
  53. 53. Totals Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema Reusing schemas Rules Custom predicates Custom Error Messages Configuration Parameters dry-validation (Contract) dry-schema
  54. 54. This separation will not only make dry-v much simpler internally, but also allow us to have schemas for processing/validating input at the HTTP boundary, and then having domain validators that can be called in the application layer. Schemas and validators can be composed, it means that you’ll be able to specify schemas and reuse them in validators. This way your application’s domain validation will live in the app layer, and hairy HTTP processing/validation will be in the HTTP layer (ie controllers, roda routes, etc.) and this will be possible with 0 code duplication (ie you won’t have to define same attributes in two places). The main idea behind dry-schema is to be a fast type checker and a coercion mechanism. It does support lots of predicates OOTB through dry-logic but it’s important to understand that for “domain validation” it is not a good fit. Good use cases for dry-schema with additional predicates (as in, other than type checks) may include things like processing and validating application configuration or HTTP params pre-processing (before it is passed down to domain layer where further processing may take place). Proving the idea by Piotr Solnica
  55. 55. How can we draw a line between dry-validation and dry-schema? Use dry-validation or use both. dry-schema for high-level http params validation dry-validation for specific and complex validations, business logic Ex.: dry-schema in Controllers dry-validation in Operations or whatever you use to process data Figuring it out
  56. 56. About dry-rb gems https://www.rubyguides.com/2019/01/what-is-dry-rb/ Piotr Solnica about dry-schema 1.0 release https://solnic.codes/2019/01/31/introducing-dry-schema/ Piotr Solnica about dry-validation 1.0 release https://dry-rb.org/news/2019/06/10/dry-validation-1-0-0-released/ Tim Riley “A tour of dry-schema and dry-validation 1.0” https://speakerdeck.com/timriley/a-tour-of-dry-schema-and-dry-validation-1-dot-0 Igor Morozov upgrading dry-gems https://www.morozov.is/2019/05/31/upgrading-dry-gems.html Helpful links:
  57. 57. Questions?
  58. 58. Thanks for listening 🎉🐱

×